diff options
Diffstat (limited to 'qemu/roms/u-boot/common')
186 files changed, 75026 insertions, 0 deletions
diff --git a/qemu/roms/u-boot/common/Makefile b/qemu/roms/u-boot/common/Makefile new file mode 100644 index 000000000..7c853ae44 --- /dev/null +++ b/qemu/roms/u-boot/common/Makefile @@ -0,0 +1,246 @@ +# +# (C) Copyright 2004-2006 +# Wolfgang Denk, DENX Software Engineering, wd@denx.de. +# +# SPDX-License-Identifier: GPL-2.0+ +# + +# core +ifndef CONFIG_SPL_BUILD +obj-y += main.o +obj-y += command.o +obj-y += exports.o +obj-y += hash.o +obj-$(CONFIG_SYS_HUSH_PARSER) += hush.o +obj-y += s_record.o +obj-y += xyzModem.o +obj-y += cmd_disk.o + +# boards +obj-$(CONFIG_SYS_GENERIC_BOARD) += board_f.o +obj-$(CONFIG_SYS_GENERIC_BOARD) += board_r.o + +# core command +obj-y += cmd_boot.o +obj-$(CONFIG_CMD_BOOTM) += cmd_bootm.o +obj-y += cmd_help.o +obj-y += cmd_version.o + +# environment +obj-y += env_attr.o +obj-y += env_callback.o +obj-y += env_flags.o +obj-$(CONFIG_ENV_IS_IN_DATAFLASH) += env_dataflash.o +obj-$(CONFIG_ENV_IS_IN_EEPROM) += env_eeprom.o +extra-$(CONFIG_ENV_IS_EMBEDDED) += env_embedded.o +obj-$(CONFIG_ENV_IS_IN_EEPROM) += env_embedded.o +extra-$(CONFIG_ENV_IS_IN_FLASH) += env_embedded.o +obj-$(CONFIG_ENV_IS_IN_NVRAM) += env_embedded.o +obj-$(CONFIG_ENV_IS_IN_FLASH) += env_flash.o +obj-$(CONFIG_ENV_IS_IN_MMC) += env_mmc.o +obj-$(CONFIG_ENV_IS_IN_FAT) += env_fat.o +obj-$(CONFIG_ENV_IS_IN_NAND) += env_nand.o +obj-$(CONFIG_ENV_IS_IN_NVRAM) += env_nvram.o +obj-$(CONFIG_ENV_IS_IN_ONENAND) += env_onenand.o +obj-$(CONFIG_ENV_IS_IN_SPI_FLASH) += env_sf.o +obj-$(CONFIG_ENV_IS_IN_REMOTE) += env_remote.o +obj-$(CONFIG_ENV_IS_IN_UBI) += env_ubi.o +obj-$(CONFIG_ENV_IS_NOWHERE) += env_nowhere.o + +# command +obj-$(CONFIG_CMD_AES) += cmd_aes.o +obj-$(CONFIG_CMD_AMBAPP) += cmd_ambapp.o +obj-$(CONFIG_SOURCE) += cmd_source.o +obj-$(CONFIG_CMD_SOURCE) += cmd_source.o +obj-$(CONFIG_CMD_BDI) += cmd_bdinfo.o +obj-$(CONFIG_CMD_BEDBUG) += bedbug.o cmd_bedbug.o +obj-$(CONFIG_CMD_BMP) += cmd_bmp.o +obj-$(CONFIG_CMD_BOOTMENU) += cmd_bootmenu.o +obj-$(CONFIG_CMD_BOOTLDR) += cmd_bootldr.o +obj-$(CONFIG_CMD_BOOTSTAGE) += cmd_bootstage.o +obj-$(CONFIG_CMD_CACHE) += cmd_cache.o +obj-$(CONFIG_CMD_CBFS) += cmd_cbfs.o +obj-$(CONFIG_CMD_CLK) += cmd_clk.o +obj-$(CONFIG_CMD_CONSOLE) += cmd_console.o +obj-$(CONFIG_CMD_CPLBINFO) += cmd_cplbinfo.o +obj-$(CONFIG_DATAFLASH_MMC_SELECT) += cmd_dataflash_mmc_mux.o +obj-$(CONFIG_CMD_DATE) += cmd_date.o +obj-$(CONFIG_CMD_DEMO) += cmd_demo.o +obj-$(CONFIG_CMD_SOUND) += cmd_sound.o +ifdef CONFIG_4xx +obj-$(CONFIG_CMD_SETGETDCR) += cmd_dcr.o +endif +ifdef CONFIG_POST +obj-$(CONFIG_CMD_DIAG) += cmd_diag.o +endif +obj-$(CONFIG_CMD_DISPLAY) += cmd_display.o +obj-$(CONFIG_CMD_DTT) += cmd_dtt.o +obj-$(CONFIG_CMD_ECHO) += cmd_echo.o +obj-$(CONFIG_ENV_IS_IN_EEPROM) += cmd_eeprom.o +obj-$(CONFIG_CMD_EEPROM) += cmd_eeprom.o +obj-$(CONFIG_CMD_ELF) += cmd_elf.o +obj-$(CONFIG_SYS_HUSH_PARSER) += cmd_exit.o +obj-$(CONFIG_CMD_EXT4) += cmd_ext4.o +obj-$(CONFIG_CMD_EXT2) += cmd_ext2.o +obj-$(CONFIG_CMD_FAT) += cmd_fat.o +obj-$(CONFIG_CMD_FDC) += cmd_fdc.o +obj-$(CONFIG_OF_LIBFDT) += cmd_fdt.o fdt_support.o +obj-$(CONFIG_CMD_FITUPD) += cmd_fitupd.o +obj-$(CONFIG_CMD_FLASH) += cmd_flash.o +ifdef CONFIG_FPGA +obj-$(CONFIG_CMD_FPGA) += cmd_fpga.o +endif +obj-$(CONFIG_CMD_FPGAD) += cmd_fpgad.o +obj-$(CONFIG_CMD_FS_GENERIC) += cmd_fs.o +obj-$(CONFIG_CMD_FUSE) += cmd_fuse.o +obj-$(CONFIG_CMD_GETTIME) += cmd_gettime.o +obj-$(CONFIG_CMD_GPIO) += cmd_gpio.o +obj-$(CONFIG_CMD_I2C) += cmd_i2c.o +obj-$(CONFIG_CMD_HASH) += cmd_hash.o +obj-$(CONFIG_CMD_IDE) += cmd_ide.o +obj-$(CONFIG_CMD_IMMAP) += cmd_immap.o +obj-$(CONFIG_CMD_INI) += cmd_ini.o +obj-$(CONFIG_CMD_IRQ) += cmd_irq.o +obj-$(CONFIG_CMD_ITEST) += cmd_itest.o +obj-$(CONFIG_CMD_JFFS2) += cmd_jffs2.o +obj-$(CONFIG_CMD_CRAMFS) += cmd_cramfs.o +obj-$(CONFIG_CMD_LDRINFO) += cmd_ldrinfo.o +obj-$(CONFIG_CMD_LED) += cmd_led.o +obj-$(CONFIG_CMD_LICENSE) += cmd_license.o +obj-y += cmd_load.o +obj-$(CONFIG_LOGBUFFER) += cmd_log.o +obj-$(CONFIG_ID_EEPROM) += cmd_mac.o +obj-$(CONFIG_CMD_MD5SUM) += cmd_md5sum.o +obj-$(CONFIG_CMD_MEMORY) += cmd_mem.o +obj-$(CONFIG_CMD_IO) += cmd_io.o +obj-$(CONFIG_CMD_MFSL) += cmd_mfsl.o +obj-$(CONFIG_MII) += miiphyutil.o +obj-$(CONFIG_CMD_MII) += miiphyutil.o +obj-$(CONFIG_PHYLIB) += miiphyutil.o +obj-$(CONFIG_CMD_MII) += cmd_mii.o +ifdef CONFIG_PHYLIB +obj-$(CONFIG_CMD_MII) += cmd_mdio.o +endif +obj-$(CONFIG_CMD_MISC) += cmd_misc.o +obj-$(CONFIG_CMD_MMC) += cmd_mmc.o +obj-$(CONFIG_CMD_MMC_SPI) += cmd_mmc_spi.o +obj-$(CONFIG_MP) += cmd_mp.o +obj-$(CONFIG_CMD_MTDPARTS) += cmd_mtdparts.o +obj-$(CONFIG_CMD_NAND) += cmd_nand.o +obj-$(CONFIG_CMD_NET) += cmd_net.o +obj-$(CONFIG_CMD_ONENAND) += cmd_onenand.o +obj-$(CONFIG_CMD_OTP) += cmd_otp.o +obj-$(CONFIG_CMD_PART) += cmd_part.o +ifdef CONFIG_PCI +obj-$(CONFIG_CMD_PCI) += cmd_pci.o +endif +obj-y += cmd_pcmcia.o +obj-$(CONFIG_CMD_PORTIO) += cmd_portio.o +obj-$(CONFIG_CMD_PXE) += cmd_pxe.o +obj-$(CONFIG_CMD_READ) += cmd_read.o +obj-$(CONFIG_CMD_REGINFO) += cmd_reginfo.o +obj-$(CONFIG_CMD_REISER) += cmd_reiser.o +obj-$(CONFIG_SANDBOX) += cmd_sandbox.o +obj-$(CONFIG_CMD_SATA) += cmd_sata.o +obj-$(CONFIG_CMD_SF) += cmd_sf.o +obj-$(CONFIG_CMD_SCSI) += cmd_scsi.o +obj-$(CONFIG_CMD_SHA1SUM) += cmd_sha1sum.o +obj-$(CONFIG_CMD_SETEXPR) += cmd_setexpr.o +obj-$(CONFIG_CMD_SOFTSWITCH) += cmd_softswitch.o +obj-$(CONFIG_CMD_SPI) += cmd_spi.o +obj-$(CONFIG_CMD_SPIBOOTLDR) += cmd_spibootldr.o +obj-$(CONFIG_CMD_STRINGS) += cmd_strings.o +obj-$(CONFIG_CMD_TERMINAL) += cmd_terminal.o +obj-$(CONFIG_CMD_TIME) += cmd_time.o +obj-$(CONFIG_CMD_TRACE) += cmd_trace.o +obj-$(CONFIG_SYS_HUSH_PARSER) += cmd_test.o +obj-$(CONFIG_CMD_TPM) += cmd_tpm.o +obj-$(CONFIG_CMD_TSI148) += cmd_tsi148.o +obj-$(CONFIG_CMD_UBI) += cmd_ubi.o +obj-$(CONFIG_CMD_UBIFS) += cmd_ubifs.o +obj-$(CONFIG_CMD_UNIVERSE) += cmd_universe.o +obj-$(CONFIG_CMD_UNZIP) += cmd_unzip.o +ifdef CONFIG_LZMA +obj-$(CONFIG_CMD_LZMADEC) += cmd_lzmadec.o +endif +ifdef CONFIG_CMD_USB +obj-y += cmd_usb.o +obj-y += usb.o usb_hub.o +obj-$(CONFIG_USB_STORAGE) += usb_storage.o +endif +obj-$(CONFIG_CMD_USB_MASS_STORAGE) += cmd_usb_mass_storage.o +obj-$(CONFIG_CMD_THOR_DOWNLOAD) += cmd_thordown.o +obj-$(CONFIG_CMD_XIMG) += cmd_ximg.o +obj-$(CONFIG_YAFFS2) += cmd_yaffs2.o +obj-$(CONFIG_CMD_SPL) += cmd_spl.o +obj-$(CONFIG_CMD_ZIP) += cmd_zip.o +obj-$(CONFIG_CMD_ZFS) += cmd_zfs.o + +# others +obj-$(CONFIG_BOOTSTAGE) += bootstage.o +obj-$(CONFIG_CONSOLE_MUX) += iomux.o +obj-y += flash.o +obj-$(CONFIG_CMD_KGDB) += kgdb.o kgdb_stubs.o +obj-$(CONFIG_I2C_EDID) += edid.o +obj-$(CONFIG_KALLSYMS) += kallsyms.o +obj-y += splash.o +obj-$(CONFIG_LCD) += lcd.o +obj-$(CONFIG_LYNXKDI) += lynxkdi.o +obj-$(CONFIG_MENU) += menu.o +obj-$(CONFIG_MODEM_SUPPORT) += modem.o +obj-$(CONFIG_UPDATE_TFTP) += update.o +obj-$(CONFIG_USB_KEYBOARD) += usb_kbd.o +obj-$(CONFIG_CMD_DFU) += cmd_dfu.o +obj-$(CONFIG_CMD_GPT) += cmd_gpt.o +endif + +ifdef CONFIG_SPL_BUILD +obj-$(CONFIG_ENV_IS_IN_FLASH) += env_flash.o +obj-$(CONFIG_SPL_YMODEM_SUPPORT) += xyzModem.o +obj-$(CONFIG_SPL_NET_SUPPORT) += miiphyutil.o +# environment +obj-$(CONFIG_SPL_ENV_SUPPORT) += env_attr.o +obj-$(CONFIG_SPL_ENV_SUPPORT) += env_flags.o +obj-$(CONFIG_SPL_ENV_SUPPORT) += env_callback.o +ifdef CONFIG_SPL_USB_HOST_SUPPORT +obj-$(CONFIG_SPL_USB_SUPPORT) += usb.o usb_hub.o +obj-$(CONFIG_USB_STORAGE) += usb_storage.o +endif +ifdef CONFIG_SPL_SATA_SUPPORT +obj-$(CONFIG_CMD_SCSI) += cmd_scsi.o +endif +ifneq ($(CONFIG_SPL_NET_SUPPORT),y) +obj-$(CONFIG_ENV_IS_NOWHERE) += env_nowhere.o +obj-$(CONFIG_ENV_IS_IN_MMC) += env_mmc.o +obj-$(CONFIG_ENV_IS_IN_NAND) += env_nand.o +obj-$(CONFIG_ENV_IS_IN_SPI_FLASH) += env_sf.o +obj-$(CONFIG_ENV_IS_IN_FLASH) += env_flash.o +else +obj-y += env_nowhere.o +endif +endif +# core command +obj-y += cmd_nvedit.o +#environment +obj-y += env_common.o +#others +ifdef CONFIG_DDR_SPD +SPD := y +endif +ifdef CONFIG_SPD_EEPROM +SPD := y +endif +obj-$(SPD) += ddr_spd.o +obj-$(CONFIG_HWCONFIG) += hwconfig.o +obj-$(CONFIG_BOUNCE_BUFFER) += bouncebuf.o +obj-y += console.o +obj-$(CONFIG_CROS_EC) += cros_ec.o +obj-y += dlmalloc.o +obj-y += image.o +obj-$(CONFIG_OF_LIBFDT) += image-fdt.o +obj-$(CONFIG_FIT) += image-fit.o +obj-$(CONFIG_FIT_SIGNATURE) += image-sig.o +obj-y += memsize.o +obj-y += stdio.o + +CFLAGS_env_embedded.o := -Wa,--no-warn -DENV_CRC=$(shell tools/envcrc 2>/dev/null) diff --git a/qemu/roms/u-boot/common/bedbug.c b/qemu/roms/u-boot/common/bedbug.c new file mode 100644 index 000000000..42ecf61ef --- /dev/null +++ b/qemu/roms/u-boot/common/bedbug.c @@ -0,0 +1,1253 @@ +/* $Id$ */ + +#include <common.h> + +#include <linux/ctype.h> +#include <bedbug/bedbug.h> +#include <bedbug/ppc.h> +#include <bedbug/regs.h> +#include <bedbug/tables.h> + +#define Elf32_Word unsigned long + +/* USE_SOURCE_CODE enables some symbolic debugging functions of this + code. This is only useful if the program will have access to the + source code for the binary being examined. +*/ + +/* #define USE_SOURCE_CODE 1 */ + +#ifdef USE_SOURCE_CODE +extern int line_info_from_addr __P ((Elf32_Word, char *, char *, int *)); +extern struct symreflist *symByAddr; +extern char *symbol_name_from_addr __P ((Elf32_Word, int, int *)); +#endif /* USE_SOURCE_CODE */ + +int print_operands __P ((struct ppc_ctx *)); +int get_operand_value __P ((struct opcode *, unsigned long, + enum OP_FIELD, unsigned long *)); +struct opcode *find_opcode __P ((unsigned long)); +struct opcode *find_opcode_by_name __P ((char *)); +char *spr_name __P ((int)); +int spr_value __P ((char *)); +char *tbr_name __P ((int)); +int tbr_value __P ((char *)); +int parse_operand __P ((unsigned long, struct opcode *, + struct operand *, char *, int *)); +int get_word __P ((char **, char *)); +long read_number __P ((char *)); +int downstring __P ((char *)); + + +/*====================================================================== + * Entry point for the PPC disassembler. + * + * Arguments: + * memaddr The address to start disassembling from. + * + * virtual If this value is non-zero, then this will be + * used as the base address for the output and + * symbol lookups. If this value is zero then + * memaddr is used as the absolute address. + * + * num_instr The number of instructions to disassemble. Since + * each instruction is 32 bits long, this can be + * computed if you know the total size of the region. + * + * pfunc The address of a function that is called to print + * each line of output. The function should take a + * single character pointer as its parameters a la puts. + * + * flags Sets options for the output. This is a + * bitwise-inclusive-OR of the following + * values. Note that only one of the radix + * options may be set. + * + * F_RADOCTAL - output radix is unsigned base 8. + * F_RADUDECIMAL - output radix is unsigned base 10. + * F_RADSDECIMAL - output radix is signed base 10. + * F_RADHEX - output radix is unsigned base 16. + * F_SIMPLE - use simplified mnemonics. + * F_SYMBOL - lookup symbols for addresses. + * F_INSTR - output raw instruction. + * F_LINENO - show line # info if available. + * + * Returns true if the area was successfully disassembled or false if + * a problem was encountered with accessing the memory. + */ + +int disppc (unsigned char *memaddr, unsigned char *virtual, int num_instr, + int (*pfunc) (const char *), unsigned long flags) +{ + int i; + struct ppc_ctx ctx; + +#ifdef USE_SOURCE_CODE + int line_no = 0; + int last_line_no = 0; + char funcname[128] = { 0 }; + char filename[256] = { 0 }; + char last_funcname[128] = { 0 }; + int symoffset; + char *symname; + char *cursym = (char *) 0; +#endif /* USE_SOURCE_CODE */ + /*------------------------------------------------------------*/ + + ctx.flags = flags; + ctx.virtual = virtual; + + /* Figure out the output radix before we go any further */ + + if (ctx.flags & F_RADOCTAL) { + /* Unsigned octal output */ + strcpy (ctx.radix_fmt, "O%o"); + } else if (ctx.flags & F_RADUDECIMAL) { + /* Unsigned decimal output */ + strcpy (ctx.radix_fmt, "%u"); + } else if (ctx.flags & F_RADSDECIMAL) { + /* Signed decimal output */ + strcpy (ctx.radix_fmt, "%d"); + } else { + /* Unsigned hex output */ + strcpy (ctx.radix_fmt, "0x%x"); + } + + if (ctx.virtual == 0) { + ctx.virtual = memaddr; + } +#ifdef USE_SOURCE_CODE + if (ctx.flags & F_SYMBOL) { + if (symByAddr == 0) /* no symbols loaded */ + ctx.flags &= ~F_SYMBOL; + else { + cursym = (char *) 0; + symoffset = 0; + } + } +#endif /* USE_SOURCE_CODE */ + + /* format each line as "XXXXXXXX: <symbol> IIIIIIII disassembly" where, + XXXXXXXX is the memory address in hex, + <symbol> is the symbolic location if F_SYMBOL is set. + IIIIIIII is the raw machine code in hex if F_INSTR is set, + and disassembly is the disassembled machine code with numbers + formatted according to the 'radix' parameter */ + + for (i = 0; i < num_instr; ++i, memaddr += 4, ctx.virtual += 4) { +#ifdef USE_SOURCE_CODE + if (ctx.flags & F_LINENO) { + if ((line_info_from_addr ((Elf32_Word) ctx.virtual, + filename, funcname, &line_no) == true) && + ((line_no != last_line_no) || + (strcmp (last_funcname, funcname) != 0))) { + print_source_line (filename, funcname, line_no, pfunc); + } + last_line_no = line_no; + strcpy (last_funcname, funcname); + } +#endif /* USE_SOURCE_CODE */ + + sprintf (ctx.data, "%08lx: ", (unsigned long) ctx.virtual); + ctx.datalen = 10; + +#ifdef USE_SOURCE_CODE + if (ctx.flags & F_SYMBOL) { + if ((symname = + symbol_name_from_addr((Elf32_Word) ctx.virtual, + true, 0)) != 0) { + cursym = symname; + symoffset = 0; + } else { + if ((cursym == 0) && + ((symname = + symbol_name_from_addr((Elf32_Word) ctx.virtual, + false, &symoffset)) != 0)) { + cursym = symname; + } else { + symoffset += 4; + } + } + + if (cursym != 0) { + sprintf (&ctx.data[ctx.datalen], "<%s+", cursym); + ctx.datalen = strlen (ctx.data); + sprintf (&ctx.data[ctx.datalen], ctx.radix_fmt, symoffset); + strcat (ctx.data, ">"); + ctx.datalen = strlen (ctx.data); + } + } +#endif /* USE_SOURCE_CODE */ + + ctx.instr = INSTRUCTION (memaddr); + + if (ctx.flags & F_INSTR) { + /* Find the opcode structure for this opcode. If one is not found + then it must be an illegal instruction */ + sprintf (&ctx.data[ctx.datalen], + " %02lx %02lx %02lx %02lx ", + ((ctx.instr >> 24) & 0xff), + ((ctx.instr >> 16) & 0xff), ((ctx.instr >> 8) & 0xff), + (ctx.instr & 0xff)); + ctx.datalen += 18; + } else { + strcat (ctx.data, " "); + ctx.datalen += 3; + } + + if ((ctx.op = find_opcode (ctx.instr)) == 0) { + /* Illegal Opcode */ + sprintf (&ctx.data[ctx.datalen], " .long 0x%08lx", + ctx.instr); + ctx.datalen += 24; + (*pfunc) (ctx.data); + continue; + } + + if (((ctx.flags & F_SIMPLE) == 0) || + (ctx.op->hfunc == 0) || + ((*ctx.op->hfunc) (&ctx) == false)) { + sprintf (&ctx.data[ctx.datalen], "%-7s ", ctx.op->name); + ctx.datalen += 8; + print_operands (&ctx); + } + + (*pfunc) (ctx.data); + } + + return true; +} /* disppc */ + + + +/*====================================================================== + * Called by the disassembler to print the operands for an instruction. + * + * Arguments: + * ctx A pointer to the disassembler context record. + * + * always returns 0. + */ + +int print_operands (struct ppc_ctx *ctx) +{ + int open_parens = 0; + int field; + unsigned long operand; + struct operand *opr; + +#ifdef USE_SOURCE_CODE + char *symname; + int offset; +#endif /* USE_SOURCE_CODE */ + /*------------------------------------------------------------*/ + + /* Walk through the operands and list each in order */ + for (field = 0; ctx->op->fields[field] != 0; ++field) { + if (ctx->op->fields[field] > n_operands) { + continue; /* bad operand ?! */ + } + + opr = &operands[ctx->op->fields[field] - 1]; + + if (opr->hint & OH_SILENT) { + continue; + } + + if ((field > 0) && !open_parens) { + strcat (ctx->data, ","); + ctx->datalen++; + } + + operand = (ctx->instr >> opr->shift) & ((1 << opr->bits) - 1); + + if (opr->hint & OH_ADDR) { + if ((operand & (1 << (opr->bits - 1))) != 0) { + operand = operand - (1 << opr->bits); + } + + if (ctx->op->hint & H_RELATIVE) + operand = (operand << 2) + (unsigned long) ctx->virtual; + else + operand = (operand << 2); + + + sprintf (&ctx->data[ctx->datalen], "0x%lx", operand); + ctx->datalen = strlen (ctx->data); + +#ifdef USE_SOURCE_CODE + if ((ctx->flags & F_SYMBOL) && + ((symname = + symbol_name_from_addr (operand, 0, &offset)) != 0)) { + sprintf (&ctx->data[ctx->datalen], " <%s", symname); + if (offset != 0) { + strcat (ctx->data, "+"); + ctx->datalen = strlen (ctx->data); + sprintf (&ctx->data[ctx->datalen], ctx->radix_fmt, + offset); + } + strcat (ctx->data, ">"); + } +#endif /* USE_SOURCE_CODE */ + } + + else if (opr->hint & OH_REG) { + if ((operand == 0) && + (opr->field == O_rA) && (ctx->op->hint & H_RA0_IS_0)) { + strcat (ctx->data, "0"); + } else { + sprintf (&ctx->data[ctx->datalen], "r%d", (short) operand); + } + + if (open_parens) { + strcat (ctx->data, ")"); + open_parens--; + } + } + + else if (opr->hint & OH_SPR) { + strcat (ctx->data, spr_name (operand)); + } + + else if (opr->hint & OH_TBR) { + strcat (ctx->data, tbr_name (operand)); + } + + else if (opr->hint & OH_LITERAL) { + switch (opr->field) { + case O_cr2: + strcat (ctx->data, "cr2"); + ctx->datalen += 3; + break; + + default: + break; + } + } + + else { + sprintf (&ctx->data[ctx->datalen], ctx->radix_fmt, + (unsigned short) operand); + + if (open_parens) { + strcat (ctx->data, ")"); + open_parens--; + } + + else if (opr->hint & OH_OFFSET) { + strcat (ctx->data, "("); + open_parens++; + } + } + + ctx->datalen = strlen (ctx->data); + } + + return 0; +} /* print_operands */ + + + +/*====================================================================== + * Called to get the value of an arbitrary operand with in an instruction. + * + * Arguments: + * op The pointer to the opcode structure to which + * the operands belong. + * + * instr The instruction (32 bits) containing the opcode + * and the operands to print. By the time that + * this routine is called the operand has already + * been added to the output. + * + * field The field (operand) to get the value of. + * + * value The address of an unsigned long to be filled in + * with the value of the operand if it is found. This + * will only be filled in if the function returns + * true. This may be passed as 0 if the value is + * not required. + * + * Returns true if the operand was found or false if it was not. + */ + +int get_operand_value (struct opcode *op, unsigned long instr, + enum OP_FIELD field, unsigned long *value) +{ + int i; + struct operand *opr; + + /*------------------------------------------------------------*/ + + if (field > n_operands) { + return false; /* bad operand ?! */ + } + + /* Walk through the operands and list each in order */ + for (i = 0; op->fields[i] != 0; ++i) { + if (op->fields[i] != field) { + continue; + } + + opr = &operands[op->fields[i] - 1]; + + if (value) { + *value = (instr >> opr->shift) & ((1 << opr->bits) - 1); + } + return true; + } + + return false; +} /* operand_value */ + + + +/*====================================================================== + * Called by the disassembler to match an opcode value to an opcode structure. + * + * Arguments: + * instr The instruction (32 bits) to match. This value + * may contain operand values as well as the opcode + * since they will be masked out anyway for this + * search. + * + * Returns the address of an opcode struct (from the opcode table) if the + * operand successfully matched an entry, or 0 if no match was found. + */ + +struct opcode *find_opcode (unsigned long instr) +{ + struct opcode *ptr; + int top = 0; + int bottom = n_opcodes - 1; + int idx; + + /*------------------------------------------------------------*/ + + while (top <= bottom) { + idx = (top + bottom) >> 1; + ptr = &opcodes[idx]; + + if ((instr & ptr->mask) < ptr->opcode) { + bottom = idx - 1; + } else if ((instr & ptr->mask) > ptr->opcode) { + top = idx + 1; + } else { + return ptr; + } + } + + return (struct opcode *) 0; +} /* find_opcode */ + + + +/*====================================================================== + * Called by the assembler to match an opcode name to an opcode structure. + * + * Arguments: + * name The text name of the opcode, e.g. "b", "mtspr", etc. + * + * The opcodes are sorted numerically by their instruction binary code + * so a search for the name cannot use the binary search used by the + * other find routine. + * + * Returns the address of an opcode struct (from the opcode table) if the + * name successfully matched an entry, or 0 if no match was found. + */ + +struct opcode *find_opcode_by_name (char *name) +{ + int idx; + + /*------------------------------------------------------------*/ + + downstring (name); + + for (idx = 0; idx < n_opcodes; ++idx) { + if (!strcmp (name, opcodes[idx].name)) + return &opcodes[idx]; + } + + return (struct opcode *) 0; +} /* find_opcode_by_name */ + + + +/*====================================================================== + * Convert the 'spr' operand from its numeric value to its symbolic name. + * + * Arguments: + * value The value of the 'spr' operand. This value should + * be unmodified from its encoding in the instruction. + * the split-field computations will be performed + * here before the switch. + * + * Returns the address of a character array containing the name of the + * special purpose register defined by the 'value' parameter, or the + * address of a character array containing "???" if no match was found. + */ + +char *spr_name (int value) +{ + unsigned short spr; + static char other[10]; + int i; + + /*------------------------------------------------------------*/ + + /* spr is a 10 bit field whose interpretation has the high and low + five-bit fields reversed from their encoding in the operand */ + + spr = ((value >> 5) & 0x1f) | ((value & 0x1f) << 5); + + for (i = 0; i < n_sprs; ++i) { + if (spr == spr_map[i].spr_val) + return spr_map[i].spr_name; + } + + sprintf (other, "%d", spr); + return other; +} /* spr_name */ + + + +/*====================================================================== + * Convert the 'spr' operand from its symbolic name to its numeric value + * + * Arguments: + * name The symbolic name of the 'spr' operand. The + * split-field encoding will be done by this routine. + * NOTE: name can be a number. + * + * Returns the numeric value for the spr appropriate for encoding a machine + * instruction. Returns 0 if unable to find the SPR. + */ + +int spr_value (char *name) +{ + struct spr_info *sprp; + int spr; + int i; + + /*------------------------------------------------------------*/ + + if (!name || !*name) + return 0; + + if (isdigit ((int) name[0])) { + i = htonl (read_number (name)); + spr = ((i >> 5) & 0x1f) | ((i & 0x1f) << 5); + return spr; + } + + downstring (name); + + for (i = 0; i < n_sprs; ++i) { + sprp = &spr_map[i]; + + if (strcmp (name, sprp->spr_name) == 0) { + /* spr is a 10 bit field whose interpretation has the high and low + five-bit fields reversed from their encoding in the operand */ + i = htonl (sprp->spr_val); + spr = ((i >> 5) & 0x1f) | ((i & 0x1f) << 5); + + return spr; + } + } + + return 0; +} /* spr_value */ + + + +/*====================================================================== + * Convert the 'tbr' operand from its numeric value to its symbolic name. + * + * Arguments: + * value The value of the 'tbr' operand. This value should + * be unmodified from its encoding in the instruction. + * the split-field computations will be performed + * here before the switch. + * + * Returns the address of a character array containing the name of the + * time base register defined by the 'value' parameter, or the address + * of a character array containing "???" if no match was found. + */ + +char *tbr_name (int value) +{ + unsigned short tbr; + + /*------------------------------------------------------------*/ + + /* tbr is a 10 bit field whose interpretation has the high and low + five-bit fields reversed from their encoding in the operand */ + + tbr = ((value >> 5) & 0x1f) | ((value & 0x1f) << 5); + + if (tbr == 268) + return "TBL"; + + else if (tbr == 269) + return "TBU"; + + + return "???"; +} /* tbr_name */ + + + +/*====================================================================== + * Convert the 'tbr' operand from its symbolic name to its numeric value. + * + * Arguments: + * name The symbolic name of the 'tbr' operand. The + * split-field encoding will be done by this routine. + * + * Returns the numeric value for the spr appropriate for encoding a machine + * instruction. Returns 0 if unable to find the TBR. + */ + +int tbr_value (char *name) +{ + int tbr; + int val; + + /*------------------------------------------------------------*/ + + if (!name || !*name) + return 0; + + downstring (name); + + if (isdigit ((int) name[0])) { + val = read_number (name); + + if (val != 268 && val != 269) + return 0; + } else if (strcmp (name, "tbl") == 0) + val = 268; + else if (strcmp (name, "tbu") == 0) + val = 269; + else + return 0; + + /* tbr is a 10 bit field whose interpretation has the high and low + five-bit fields reversed from their encoding in the operand */ + + val = htonl (val); + tbr = ((val >> 5) & 0x1f) | ((val & 0x1f) << 5); + return tbr; +} /* tbr_name */ + + + +/*====================================================================== + * The next several functions (handle_xxx) are the routines that handle + * disassembling the opcodes with simplified mnemonics. + * + * Arguments: + * ctx A pointer to the disassembler context record. + * + * Returns true if the simpler form was printed or false if it was not. + */ + +int handle_bc (struct ppc_ctx *ctx) +{ + unsigned long bo; + unsigned long bi; + static struct opcode blt = { B_OPCODE (16, 0, 0), B_MASK, {O_BD, 0}, + 0, "blt", H_RELATIVE + }; + static struct opcode bne = + { B_OPCODE (16, 0, 0), B_MASK, {O_cr2, O_BD, 0}, + 0, "bne", H_RELATIVE + }; + static struct opcode bdnz = { B_OPCODE (16, 0, 0), B_MASK, {O_BD, 0}, + 0, "bdnz", H_RELATIVE + }; + + /*------------------------------------------------------------*/ + + if (get_operand_value(ctx->op, ctx->instr, O_BO, &bo) == false) + return false; + + if (get_operand_value(ctx->op, ctx->instr, O_BI, &bi) == false) + return false; + + if ((bo == 12) && (bi == 0)) { + ctx->op = &blt; + sprintf (&ctx->data[ctx->datalen], "%-7s ", ctx->op->name); + ctx->datalen += 8; + print_operands (ctx); + return true; + } else if ((bo == 4) && (bi == 10)) { + ctx->op = =⃥ + sprintf (&ctx->data[ctx->datalen], "%-7s ", ctx->op->name); + ctx->datalen += 8; + print_operands (ctx); + return true; + } else if ((bo == 16) && (bi == 0)) { + ctx->op = &bdnz; + sprintf (&ctx->data[ctx->datalen], "%-7s ", ctx->op->name); + ctx->datalen += 8; + print_operands (ctx); + return true; + } + + return false; +} /* handle_blt */ + + + +/*====================================================================== + * Outputs source line information for the disassembler. This should + * be modified in the future to lookup the actual line of source code + * from the file, but for now this will do. + * + * Arguments: + * filename The address of a character array containing the + * absolute path and file name of the source file. + * + * funcname The address of a character array containing the + * name of the function (not C++ demangled (yet)) + * to which this code belongs. + * + * line_no An integer specifying the source line number that + * generated this code. + * + * pfunc The address of a function to call to print the output. + * + * + * Returns true if it was able to output the line info, or false if it was + * not. + */ + +int print_source_line (char *filename, char *funcname, + int line_no, int (*pfunc) (const char *)) +{ + char out_buf[256]; + + /*------------------------------------------------------------*/ + + (*pfunc) (""); /* output a newline */ + sprintf (out_buf, "%s %s(): line %d", filename, funcname, line_no); + (*pfunc) (out_buf); + + return true; +} /* print_source_line */ + + + +/*====================================================================== + * Entry point for the PPC assembler. + * + * Arguments: + * asm_buf An array of characters containing the assembly opcode + * and operands to convert to a POWERPC machine + * instruction. + * + * Returns the machine instruction or zero. + */ + +unsigned long asmppc (unsigned long memaddr, char *asm_buf, int *err) +{ + struct opcode *opc; + struct operand *oper[MAX_OPERANDS]; + unsigned long instr; + unsigned long param; + char *ptr = asm_buf; + char scratch[20]; + int i; + int w_operands = 0; /* wanted # of operands */ + int n_operands = 0; /* # of operands read */ + int asm_debug = 0; + + /*------------------------------------------------------------*/ + + if (err) + *err = 0; + + if (get_word (&ptr, scratch) == 0) + return 0; + + /* Lookup the opcode structure based on the opcode name */ + if ((opc = find_opcode_by_name (scratch)) == (struct opcode *) 0) { + if (err) + *err = E_ASM_BAD_OPCODE; + return 0; + } + + if (asm_debug) { + printf ("asmppc: Opcode = \"%s\"\n", opc->name); + } + + for (i = 0; i < 8; ++i) { + if (opc->fields[i] == 0) + break; + ++w_operands; + } + + if (asm_debug) { + printf ("asmppc: Expecting %d operands\n", w_operands); + } + + instr = opc->opcode; + + /* read each operand */ + while (n_operands < w_operands) { + + oper[n_operands] = &operands[opc->fields[n_operands] - 1]; + + if (oper[n_operands]->hint & OH_SILENT) { + /* Skip silent operands, they are covered in opc->opcode */ + + if (asm_debug) { + printf ("asmppc: Operand %d \"%s\" SILENT\n", n_operands, + oper[n_operands]->name); + } + + ++n_operands; + continue; + } + + if (get_word (&ptr, scratch) == 0) + break; + + if (asm_debug) { + printf ("asmppc: Operand %d \"%s\" : \"%s\"\n", n_operands, + oper[n_operands]->name, scratch); + } + + if ((param = parse_operand (memaddr, opc, oper[n_operands], + scratch, err)) == -1) + return 0; + + instr |= param; + ++n_operands; + } + + if (n_operands < w_operands) { + if (err) + *err = E_ASM_NUM_OPERANDS; + return 0; + } + + if (asm_debug) { + printf ("asmppc: Instruction = 0x%08lx\n", instr); + } + + return instr; +} /* asmppc */ + + + +/*====================================================================== + * Called by the assembler to interpret a single operand + * + * Arguments: + * ctx A pointer to the disassembler context record. + * + * Returns 0 if the operand is ok, or -1 if it is bad. + */ + +int parse_operand (unsigned long memaddr, struct opcode *opc, + struct operand *oper, char *txt, int *err) +{ + long data; + long mask; + int is_neg = 0; + + /*------------------------------------------------------------*/ + + mask = (1 << oper->bits) - 1; + + if (oper->hint & OH_ADDR) { + data = read_number (txt); + + if (opc->hint & H_RELATIVE) + data = data - memaddr; + + if (data < 0) + is_neg = 1; + + data >>= 2; + data &= (mask >> 1); + + if (is_neg) + data |= 1 << (oper->bits - 1); + } + + else if (oper->hint & OH_REG) { + if (txt[0] == 'r' || txt[0] == 'R') + txt++; + else if (txt[0] == '%' && (txt[1] == 'r' || txt[1] == 'R')) + txt += 2; + + data = read_number (txt); + if (data > 31) { + if (err) + *err = E_ASM_BAD_REGISTER; + return -1; + } + + data = htonl (data); + } + + else if (oper->hint & OH_SPR) { + if ((data = spr_value (txt)) == 0) { + if (err) + *err = E_ASM_BAD_SPR; + return -1; + } + } + + else if (oper->hint & OH_TBR) { + if ((data = tbr_value (txt)) == 0) { + if (err) + *err = E_ASM_BAD_TBR; + return -1; + } + } + + else { + data = htonl (read_number (txt)); + } + + return (data & mask) << oper->shift; +} /* parse_operand */ + + +char *asm_error_str (int err) +{ + switch (err) { + case E_ASM_BAD_OPCODE: + return "Bad opcode"; + case E_ASM_NUM_OPERANDS: + return "Bad number of operands"; + case E_ASM_BAD_REGISTER: + return "Bad register number"; + case E_ASM_BAD_SPR: + return "Bad SPR name or number"; + case E_ASM_BAD_TBR: + return "Bad TBR name or number"; + } + + return ""; +} /* asm_error_str */ + + + +/*====================================================================== + * Copy a word from one buffer to another, ignores leading white spaces. + * + * Arguments: + * src The address of a character pointer to the + * source buffer. + * dest A pointer to a character buffer to write the word + * into. + * + * Returns the number of non-white space characters copied, or zero. + */ + +int get_word (char **src, char *dest) +{ + char *ptr = *src; + int nchars = 0; + + /*------------------------------------------------------------*/ + + /* Eat white spaces */ + while (*ptr && isblank (*ptr)) + ptr++; + + if (*ptr == 0) { + *src = ptr; + return 0; + } + + /* Find the text of the word */ + while (*ptr && !isblank (*ptr) && (*ptr != ',')) + dest[nchars++] = *ptr++; + ptr = (*ptr == ',') ? ptr + 1 : ptr; + dest[nchars] = 0; + + *src = ptr; + return nchars; +} /* get_word */ + + + +/*====================================================================== + * Convert a numeric string to a number, be aware of base notations. + * + * Arguments: + * txt The numeric string. + * + * Returns the converted numeric value. + */ + +long read_number (char *txt) +{ + long val; + int is_neg = 0; + + /*------------------------------------------------------------*/ + + if (txt == 0 || *txt == 0) + return 0; + + if (*txt == '-') { + is_neg = 1; + ++txt; + } + + if (txt[0] == '0' && (txt[1] == 'x' || txt[1] == 'X')) /* hex */ + val = simple_strtoul (&txt[2], NULL, 16); + else /* decimal */ + val = simple_strtoul (txt, NULL, 10); + + if (is_neg) + val = -val; + + return val; +} /* read_number */ + + +int downstring (char *s) +{ + if (!s || !*s) + return 0; + + while (*s) { + if (isupper (*s)) + *s = tolower (*s); + s++; + } + + return 0; +} /* downstring */ + + + +/*====================================================================== + * Examines the instruction at the current address and determines the + * next address to be executed. This will take into account branches + * of different types so that a "step" and "next" operations can be + * supported. + * + * Arguments: + * nextaddr The address (to be filled in) of the next + * instruction to execute. This will only be a valid + * address if true is returned. + * + * step_over A flag indicating how to compute addresses for + * branch statements: + * true = Step over the branch (next) + * false = step into the branch (step) + * + * Returns true if it was able to compute the address. Returns false if + * it has a problem reading the current instruction or one of the registers. + */ + +int find_next_address (unsigned char *nextaddr, int step_over, + struct pt_regs *regs) +{ + unsigned long pc; /* SRR0 register from PPC */ + unsigned long ctr; /* CTR register from PPC */ + unsigned long cr; /* CR register from PPC */ + unsigned long lr; /* LR register from PPC */ + unsigned long instr; /* instruction at SRR0 */ + unsigned long next; /* computed instruction for 'next' */ + unsigned long step; /* computed instruction for 'step' */ + unsigned long addr = 0; /* target address operand */ + unsigned long aa = 0; /* AA operand */ + unsigned long lk = 0; /* LK operand */ + unsigned long bo = 0; /* BO operand */ + unsigned long bi = 0; /* BI operand */ + struct opcode *op = 0; /* opcode structure for 'instr' */ + int ctr_ok = 0; + int cond_ok = 0; + int conditional = 0; + int branch = 0; + + /*------------------------------------------------------------*/ + + if (nextaddr == 0 || regs == 0) { + printf ("find_next_address: bad args"); + return false; + } + + pc = regs->nip & 0xfffffffc; + instr = INSTRUCTION (pc); + + if ((op = find_opcode (instr)) == (struct opcode *) 0) { + printf ("find_next_address: can't parse opcode 0x%lx", instr); + return false; + } + + ctr = regs->ctr; + cr = regs->ccr; + lr = regs->link; + + switch (op->opcode) { + case B_OPCODE (16, 0, 0): /* bc */ + case B_OPCODE (16, 0, 1): /* bcl */ + case B_OPCODE (16, 1, 0): /* bca */ + case B_OPCODE (16, 1, 1): /* bcla */ + if (!get_operand_value (op, instr, O_BD, &addr) || + !get_operand_value (op, instr, O_BO, &bo) || + !get_operand_value (op, instr, O_BI, &bi) || + !get_operand_value (op, instr, O_AA, &aa) || + !get_operand_value (op, instr, O_LK, &lk)) + return false; + + if ((addr & (1 << 13)) != 0) + addr = addr - (1 << 14); + addr <<= 2; + conditional = 1; + branch = 1; + break; + + case I_OPCODE (18, 0, 0): /* b */ + case I_OPCODE (18, 0, 1): /* bl */ + case I_OPCODE (18, 1, 0): /* ba */ + case I_OPCODE (18, 1, 1): /* bla */ + if (!get_operand_value (op, instr, O_LI, &addr) || + !get_operand_value (op, instr, O_AA, &aa) || + !get_operand_value (op, instr, O_LK, &lk)) + return false; + + if ((addr & (1 << 23)) != 0) + addr = addr - (1 << 24); + addr <<= 2; + conditional = 0; + branch = 1; + break; + + case XL_OPCODE (19, 528, 0): /* bcctr */ + case XL_OPCODE (19, 528, 1): /* bcctrl */ + if (!get_operand_value (op, instr, O_BO, &bo) || + !get_operand_value (op, instr, O_BI, &bi) || + !get_operand_value (op, instr, O_LK, &lk)) + return false; + + addr = ctr; + aa = 1; + conditional = 1; + branch = 1; + break; + + case XL_OPCODE (19, 16, 0): /* bclr */ + case XL_OPCODE (19, 16, 1): /* bclrl */ + if (!get_operand_value (op, instr, O_BO, &bo) || + !get_operand_value (op, instr, O_BI, &bi) || + !get_operand_value (op, instr, O_LK, &lk)) + return false; + + addr = lr; + aa = 1; + conditional = 1; + branch = 1; + break; + + default: + conditional = 0; + branch = 0; + break; + } + + if (conditional) { + switch ((bo & 0x1e) >> 1) { + case 0: /* 0000y */ + if (--ctr != 0) + ctr_ok = 1; + + cond_ok = !(cr & (1 << (31 - bi))); + break; + + case 1: /* 0001y */ + if (--ctr == 0) + ctr_ok = 1; + + cond_ok = !(cr & (1 << (31 - bi))); + break; + + case 2: /* 001zy */ + ctr_ok = 1; + cond_ok = !(cr & (1 << (31 - bi))); + break; + + case 4: /* 0100y */ + if (--ctr != 0) + ctr_ok = 1; + + cond_ok = cr & (1 << (31 - bi)); + break; + + case 5: /* 0101y */ + if (--ctr == 0) + ctr_ok = 1; + + cond_ok = cr & (1 << (31 - bi)); + break; + + case 6: /* 011zy */ + ctr_ok = 1; + cond_ok = cr & (1 << (31 - bi)); + break; + + case 8: /* 1z00y */ + if (--ctr != 0) + ctr_ok = cond_ok = 1; + break; + + case 9: /* 1z01y */ + if (--ctr == 0) + ctr_ok = cond_ok = 1; + break; + + case 10: /* 1z1zz */ + ctr_ok = cond_ok = 1; + break; + } + } + + if (branch && (!conditional || (ctr_ok && cond_ok))) { + if (aa) + step = addr; + else + step = addr + pc; + + if (lk) + next = pc + 4; + else + next = step; + } else { + step = next = pc + 4; + } + + if (step_over == true) + *(unsigned long *) nextaddr = next; + else + *(unsigned long *) nextaddr = step; + + return true; +} /* find_next_address */ + + +/* + * Copyright (c) 2000 William L. Pitts and W. Gerald Hicks + * All rights reserved. + * + * Redistribution and use in source and binary forms are freely + * permitted provided that the above copyright notice and this + * paragraph and the following disclaimer are duplicated in all + * such forms. + * + * This software is provided "AS IS" and without any express or + * implied warranties, including, without limitation, the implied + * warranties of merchantability and fitness for a particular + * purpose. + */ diff --git a/qemu/roms/u-boot/common/board_f.c b/qemu/roms/u-boot/common/board_f.c new file mode 100644 index 000000000..4ea4cb21b --- /dev/null +++ b/qemu/roms/u-boot/common/board_f.c @@ -0,0 +1,1037 @@ +/* + * Copyright (c) 2011 The Chromium OS Authors. + * (C) Copyright 2002-2006 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * (C) Copyright 2002 + * Sysgo Real-Time Solutions, GmbH <www.elinos.com> + * Marius Groeger <mgroeger@sysgo.de> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <linux/compiler.h> +#include <version.h> +#include <environment.h> +#include <fdtdec.h> +#include <fs.h> +#if defined(CONFIG_CMD_IDE) +#include <ide.h> +#endif +#include <i2c.h> +#include <initcall.h> +#include <logbuff.h> + +/* TODO: Can we move these into arch/ headers? */ +#ifdef CONFIG_8xx +#include <mpc8xx.h> +#endif +#ifdef CONFIG_5xx +#include <mpc5xx.h> +#endif +#ifdef CONFIG_MPC5xxx +#include <mpc5xxx.h> +#endif + +#include <os.h> +#include <post.h> +#include <spi.h> +#include <trace.h> +#include <watchdog.h> +#include <asm/errno.h> +#include <asm/io.h> +#ifdef CONFIG_MP +#include <asm/mp.h> +#endif +#include <asm/sections.h> +#ifdef CONFIG_X86 +#include <asm/init_helpers.h> +#include <asm/relocate.h> +#endif +#ifdef CONFIG_SANDBOX +#include <asm/state.h> +#endif +#include <linux/compiler.h> + +/* + * Pointer to initial global data area + * + * Here we initialize it if needed. + */ +#ifdef XTRN_DECLARE_GLOBAL_DATA_PTR +#undef XTRN_DECLARE_GLOBAL_DATA_PTR +#define XTRN_DECLARE_GLOBAL_DATA_PTR /* empty = allocate here */ +DECLARE_GLOBAL_DATA_PTR = (gd_t *) (CONFIG_SYS_INIT_GD_ADDR); +#else +DECLARE_GLOBAL_DATA_PTR; +#endif + +/* + * sjg: IMO this code should be + * refactored to a single function, something like: + * + * void led_set_state(enum led_colour_t colour, int on); + */ +/************************************************************************ + * Coloured LED functionality + ************************************************************************ + * May be supplied by boards if desired + */ +inline void __coloured_LED_init(void) {} +void coloured_LED_init(void) + __attribute__((weak, alias("__coloured_LED_init"))); +inline void __red_led_on(void) {} +void red_led_on(void) __attribute__((weak, alias("__red_led_on"))); +inline void __red_led_off(void) {} +void red_led_off(void) __attribute__((weak, alias("__red_led_off"))); +inline void __green_led_on(void) {} +void green_led_on(void) __attribute__((weak, alias("__green_led_on"))); +inline void __green_led_off(void) {} +void green_led_off(void) __attribute__((weak, alias("__green_led_off"))); +inline void __yellow_led_on(void) {} +void yellow_led_on(void) __attribute__((weak, alias("__yellow_led_on"))); +inline void __yellow_led_off(void) {} +void yellow_led_off(void) __attribute__((weak, alias("__yellow_led_off"))); +inline void __blue_led_on(void) {} +void blue_led_on(void) __attribute__((weak, alias("__blue_led_on"))); +inline void __blue_led_off(void) {} +void blue_led_off(void) __attribute__((weak, alias("__blue_led_off"))); + +/* + * Why is gd allocated a register? Prior to reloc it might be better to + * just pass it around to each function in this file? + * + * After reloc one could argue that it is hardly used and doesn't need + * to be in a register. Or if it is it should perhaps hold pointers to all + * global data for all modules, so that post-reloc we can avoid the massive + * literal pool we get on ARM. Or perhaps just encourage each module to use + * a structure... + */ + +/* + * Could the CONFIG_SPL_BUILD infection become a flag in gd? + */ + +#if defined(CONFIG_WATCHDOG) +static int init_func_watchdog_init(void) +{ + puts(" Watchdog enabled\n"); + WATCHDOG_RESET(); + + return 0; +} + +int init_func_watchdog_reset(void) +{ + WATCHDOG_RESET(); + + return 0; +} +#endif /* CONFIG_WATCHDOG */ + +void __board_add_ram_info(int use_default) +{ + /* please define platform specific board_add_ram_info() */ +} + +void board_add_ram_info(int) + __attribute__ ((weak, alias("__board_add_ram_info"))); + +static int init_baud_rate(void) +{ + gd->baudrate = getenv_ulong("baudrate", 10, CONFIG_BAUDRATE); + return 0; +} + +static int display_text_info(void) +{ +#ifndef CONFIG_SANDBOX + ulong bss_start, bss_end; + + bss_start = (ulong)&__bss_start; + bss_end = (ulong)&__bss_end; + + debug("U-Boot code: %08X -> %08lX BSS: -> %08lX\n", + CONFIG_SYS_TEXT_BASE, bss_start, bss_end); +#endif + +#ifdef CONFIG_MODEM_SUPPORT + debug("Modem Support enabled\n"); +#endif +#ifdef CONFIG_USE_IRQ + debug("IRQ Stack: %08lx\n", IRQ_STACK_START); + debug("FIQ Stack: %08lx\n", FIQ_STACK_START); +#endif + + return 0; +} + +static int announce_dram_init(void) +{ + puts("DRAM: "); + return 0; +} + +#if defined(CONFIG_MIPS) || defined(CONFIG_PPC) +static int init_func_ram(void) +{ +#ifdef CONFIG_BOARD_TYPES + int board_type = gd->board_type; +#else + int board_type = 0; /* use dummy arg */ +#endif + + gd->ram_size = initdram(board_type); + + if (gd->ram_size > 0) + return 0; + + puts("*** failed ***\n"); + return 1; +} +#endif + +static int show_dram_config(void) +{ + unsigned long long size; + +#ifdef CONFIG_NR_DRAM_BANKS + int i; + + debug("\nRAM Configuration:\n"); + for (i = size = 0; i < CONFIG_NR_DRAM_BANKS; i++) { + size += gd->bd->bi_dram[i].size; + debug("Bank #%d: %08lx ", i, gd->bd->bi_dram[i].start); +#ifdef DEBUG + print_size(gd->bd->bi_dram[i].size, "\n"); +#endif + } + debug("\nDRAM: "); +#else + size = gd->ram_size; +#endif + + print_size(size, ""); + board_add_ram_info(0); + putc('\n'); + + return 0; +} + +void __dram_init_banksize(void) +{ +#if defined(CONFIG_NR_DRAM_BANKS) && defined(CONFIG_SYS_SDRAM_BASE) + gd->bd->bi_dram[0].start = CONFIG_SYS_SDRAM_BASE; + gd->bd->bi_dram[0].size = get_effective_memsize(); +#endif +} + +void dram_init_banksize(void) + __attribute__((weak, alias("__dram_init_banksize"))); + +#if defined(CONFIG_HARD_I2C) || defined(CONFIG_SYS_I2C) +static int init_func_i2c(void) +{ + puts("I2C: "); +#ifdef CONFIG_SYS_I2C + i2c_init_all(); +#else + i2c_init(CONFIG_SYS_I2C_SPEED, CONFIG_SYS_I2C_SLAVE); +#endif + puts("ready\n"); + return 0; +} +#endif + +#if defined(CONFIG_HARD_SPI) +static int init_func_spi(void) +{ + puts("SPI: "); + spi_init(); + puts("ready\n"); + return 0; +} +#endif + +__maybe_unused +static int zero_global_data(void) +{ + memset((void *)gd, '\0', sizeof(gd_t)); + + return 0; +} + +static int setup_mon_len(void) +{ +#ifdef __ARM__ + gd->mon_len = (ulong)&__bss_end - (ulong)_start; +#elif defined(CONFIG_SANDBOX) + gd->mon_len = (ulong)&_end - (ulong)_init; +#else + /* TODO: use (ulong)&__bss_end - (ulong)&__text_start; ? */ + gd->mon_len = (ulong)&__bss_end - CONFIG_SYS_MONITOR_BASE; +#endif + return 0; +} + +__weak int arch_cpu_init(void) +{ + return 0; +} + +#ifdef CONFIG_OF_HOSTFILE + +static int read_fdt_from_file(void) +{ + struct sandbox_state *state = state_get_current(); + const char *fname = state->fdt_fname; + void *blob; + ssize_t size; + int err; + int fd; + + blob = map_sysmem(CONFIG_SYS_FDT_LOAD_ADDR, 0); + if (!state->fdt_fname) { + err = fdt_create_empty_tree(blob, 256); + if (!err) + goto done; + printf("Unable to create empty FDT: %s\n", fdt_strerror(err)); + return -EINVAL; + } + + size = os_get_filesize(fname); + if (size < 0) { + printf("Failed to file FDT file '%s'\n", fname); + return -ENOENT; + } + fd = os_open(fname, OS_O_RDONLY); + if (fd < 0) { + printf("Failed to open FDT file '%s'\n", fname); + return -EACCES; + } + if (os_read(fd, blob, size) != size) { + os_close(fd); + return -EIO; + } + os_close(fd); + +done: + gd->fdt_blob = blob; + + return 0; +} +#endif + +#ifdef CONFIG_SANDBOX +static int setup_ram_buf(void) +{ + struct sandbox_state *state = state_get_current(); + + gd->arch.ram_buf = state->ram_buf; + gd->ram_size = state->ram_size; + + return 0; +} +#endif + +static int setup_fdt(void) +{ +#ifdef CONFIG_OF_EMBED + /* Get a pointer to the FDT */ + gd->fdt_blob = __dtb_dt_begin; +#elif defined CONFIG_OF_SEPARATE + /* FDT is at end of image */ + gd->fdt_blob = (ulong *)&_end; +#elif defined(CONFIG_OF_HOSTFILE) + if (read_fdt_from_file()) { + puts("Failed to read control FDT\n"); + return -1; + } +#endif + /* Allow the early environment to override the fdt address */ + gd->fdt_blob = (void *)getenv_ulong("fdtcontroladdr", 16, + (uintptr_t)gd->fdt_blob); + return 0; +} + +/* Get the top of usable RAM */ +__weak ulong board_get_usable_ram_top(ulong total_size) +{ + return gd->ram_top; +} + +static int setup_dest_addr(void) +{ + debug("Monitor len: %08lX\n", gd->mon_len); + /* + * Ram is setup, size stored in gd !! + */ + debug("Ram size: %08lX\n", (ulong)gd->ram_size); +#if defined(CONFIG_SYS_MEM_TOP_HIDE) + /* + * Subtract specified amount of memory to hide so that it won't + * get "touched" at all by U-Boot. By fixing up gd->ram_size + * the Linux kernel should now get passed the now "corrected" + * memory size and won't touch it either. This should work + * for arch/ppc and arch/powerpc. Only Linux board ports in + * arch/powerpc with bootwrapper support, that recalculate the + * memory size from the SDRAM controller setup will have to + * get fixed. + */ + gd->ram_size -= CONFIG_SYS_MEM_TOP_HIDE; +#endif +#ifdef CONFIG_SYS_SDRAM_BASE + gd->ram_top = CONFIG_SYS_SDRAM_BASE; +#endif + gd->ram_top += get_effective_memsize(); + gd->ram_top = board_get_usable_ram_top(gd->mon_len); + gd->relocaddr = gd->ram_top; + debug("Ram top: %08lX\n", (ulong)gd->ram_top); +#if defined(CONFIG_MP) && (defined(CONFIG_MPC86xx) || defined(CONFIG_E500)) + /* + * We need to make sure the location we intend to put secondary core + * boot code is reserved and not used by any part of u-boot + */ + if (gd->relocaddr > determine_mp_bootpg(NULL)) { + gd->relocaddr = determine_mp_bootpg(NULL); + debug("Reserving MP boot page to %08lx\n", gd->relocaddr); + } +#endif + return 0; +} + +#if defined(CONFIG_LOGBUFFER) && !defined(CONFIG_ALT_LB_ADDR) +static int reserve_logbuffer(void) +{ + /* reserve kernel log buffer */ + gd->relocaddr -= LOGBUFF_RESERVE; + debug("Reserving %dk for kernel logbuffer at %08lx\n", LOGBUFF_LEN, + gd->relocaddr); + return 0; +} +#endif + +#ifdef CONFIG_PRAM +/* reserve protected RAM */ +static int reserve_pram(void) +{ + ulong reg; + + reg = getenv_ulong("pram", 10, CONFIG_PRAM); + gd->relocaddr -= (reg << 10); /* size is in kB */ + debug("Reserving %ldk for protected RAM at %08lx\n", reg, + gd->relocaddr); + return 0; +} +#endif /* CONFIG_PRAM */ + +/* Round memory pointer down to next 4 kB limit */ +static int reserve_round_4k(void) +{ + gd->relocaddr &= ~(4096 - 1); + return 0; +} + +#if !(defined(CONFIG_SYS_ICACHE_OFF) && defined(CONFIG_SYS_DCACHE_OFF)) && \ + defined(CONFIG_ARM) +static int reserve_mmu(void) +{ + /* reserve TLB table */ + gd->arch.tlb_size = PGTABLE_SIZE; + gd->relocaddr -= gd->arch.tlb_size; + + /* round down to next 64 kB limit */ + gd->relocaddr &= ~(0x10000 - 1); + + gd->arch.tlb_addr = gd->relocaddr; + debug("TLB table from %08lx to %08lx\n", gd->arch.tlb_addr, + gd->arch.tlb_addr + gd->arch.tlb_size); + return 0; +} +#endif + +#ifdef CONFIG_LCD +static int reserve_lcd(void) +{ +#ifdef CONFIG_FB_ADDR + gd->fb_base = CONFIG_FB_ADDR; +#else + /* reserve memory for LCD display (always full pages) */ + gd->relocaddr = lcd_setmem(gd->relocaddr); + gd->fb_base = gd->relocaddr; +#endif /* CONFIG_FB_ADDR */ + return 0; +} +#endif /* CONFIG_LCD */ + +static int reserve_trace(void) +{ +#ifdef CONFIG_TRACE + gd->relocaddr -= CONFIG_TRACE_BUFFER_SIZE; + gd->trace_buff = map_sysmem(gd->relocaddr, CONFIG_TRACE_BUFFER_SIZE); + debug("Reserving %dk for trace data at: %08lx\n", + CONFIG_TRACE_BUFFER_SIZE >> 10, gd->relocaddr); +#endif + + return 0; +} + +#if defined(CONFIG_VIDEO) && (!defined(CONFIG_PPC) || defined(CONFIG_8xx)) \ + && !defined(CONFIG_ARM) && !defined(CONFIG_X86) +static int reserve_video(void) +{ + /* reserve memory for video display (always full pages) */ + gd->relocaddr = video_setmem(gd->relocaddr); + gd->fb_base = gd->relocaddr; + + return 0; +} +#endif + +static int reserve_uboot(void) +{ + /* + * reserve memory for U-Boot code, data & bss + * round down to next 4 kB limit + */ + gd->relocaddr -= gd->mon_len; + gd->relocaddr &= ~(4096 - 1); +#ifdef CONFIG_E500 + /* round down to next 64 kB limit so that IVPR stays aligned */ + gd->relocaddr &= ~(65536 - 1); +#endif + + debug("Reserving %ldk for U-Boot at: %08lx\n", gd->mon_len >> 10, + gd->relocaddr); + + gd->start_addr_sp = gd->relocaddr; + + return 0; +} + +#ifndef CONFIG_SPL_BUILD +/* reserve memory for malloc() area */ +static int reserve_malloc(void) +{ + gd->start_addr_sp = gd->start_addr_sp - TOTAL_MALLOC_LEN; + debug("Reserving %dk for malloc() at: %08lx\n", + TOTAL_MALLOC_LEN >> 10, gd->start_addr_sp); + return 0; +} + +/* (permanently) allocate a Board Info struct */ +static int reserve_board(void) +{ + gd->start_addr_sp -= sizeof(bd_t); + gd->bd = (bd_t *)map_sysmem(gd->start_addr_sp, sizeof(bd_t)); + memset(gd->bd, '\0', sizeof(bd_t)); + debug("Reserving %zu Bytes for Board Info at: %08lx\n", + sizeof(bd_t), gd->start_addr_sp); + return 0; +} +#endif + +static int setup_machine(void) +{ +#ifdef CONFIG_MACH_TYPE + gd->bd->bi_arch_number = CONFIG_MACH_TYPE; /* board id for Linux */ +#endif + return 0; +} + +static int reserve_global_data(void) +{ + gd->start_addr_sp -= sizeof(gd_t); + gd->new_gd = (gd_t *)map_sysmem(gd->start_addr_sp, sizeof(gd_t)); + debug("Reserving %zu Bytes for Global Data at: %08lx\n", + sizeof(gd_t), gd->start_addr_sp); + return 0; +} + +static int reserve_fdt(void) +{ + /* + * If the device tree is sitting immediate above our image then we + * must relocate it. If it is embedded in the data section, then it + * will be relocated with other data. + */ + if (gd->fdt_blob) { + gd->fdt_size = ALIGN(fdt_totalsize(gd->fdt_blob) + 0x1000, 32); + + gd->start_addr_sp -= gd->fdt_size; + gd->new_fdt = map_sysmem(gd->start_addr_sp, gd->fdt_size); + debug("Reserving %lu Bytes for FDT at: %08lx\n", + gd->fdt_size, gd->start_addr_sp); + } + + return 0; +} + +static int reserve_stacks(void) +{ +#ifdef CONFIG_SPL_BUILD +# ifdef CONFIG_ARM + gd->start_addr_sp -= 128; /* leave 32 words for abort-stack */ + gd->irq_sp = gd->start_addr_sp; +# endif +#else +# ifdef CONFIG_PPC + ulong *s; +# endif + + /* setup stack pointer for exceptions */ + gd->start_addr_sp -= 16; + gd->start_addr_sp &= ~0xf; + gd->irq_sp = gd->start_addr_sp; + + /* + * Handle architecture-specific things here + * TODO(sjg@chromium.org): Perhaps create arch_reserve_stack() + * to handle this and put in arch/xxx/lib/stack.c + */ +# if defined(CONFIG_ARM) && !defined(CONFIG_ARM64) +# ifdef CONFIG_USE_IRQ + gd->start_addr_sp -= (CONFIG_STACKSIZE_IRQ + CONFIG_STACKSIZE_FIQ); + debug("Reserving %zu Bytes for IRQ stack at: %08lx\n", + CONFIG_STACKSIZE_IRQ + CONFIG_STACKSIZE_FIQ, gd->start_addr_sp); + + /* 8-byte alignment for ARM ABI compliance */ + gd->start_addr_sp &= ~0x07; +# endif + /* leave 3 words for abort-stack, plus 1 for alignment */ + gd->start_addr_sp -= 16; +# elif defined(CONFIG_PPC) + /* Clear initial stack frame */ + s = (ulong *) gd->start_addr_sp; + *s = 0; /* Terminate back chain */ + *++s = 0; /* NULL return address */ +# endif /* Architecture specific code */ + + return 0; +#endif +} + +static int display_new_sp(void) +{ + debug("New Stack Pointer is: %08lx\n", gd->start_addr_sp); + + return 0; +} + +#ifdef CONFIG_PPC +static int setup_board_part1(void) +{ + bd_t *bd = gd->bd; + + /* + * Save local variables to board info struct + */ + + bd->bi_memstart = CONFIG_SYS_SDRAM_BASE; /* start of memory */ + bd->bi_memsize = gd->ram_size; /* size in bytes */ + +#ifdef CONFIG_SYS_SRAM_BASE + bd->bi_sramstart = CONFIG_SYS_SRAM_BASE; /* start of SRAM */ + bd->bi_sramsize = CONFIG_SYS_SRAM_SIZE; /* size of SRAM */ +#endif + +#if defined(CONFIG_8xx) || defined(CONFIG_MPC8260) || defined(CONFIG_5xx) || \ + defined(CONFIG_E500) || defined(CONFIG_MPC86xx) + bd->bi_immr_base = CONFIG_SYS_IMMR; /* base of IMMR register */ +#endif +#if defined(CONFIG_MPC5xxx) + bd->bi_mbar_base = CONFIG_SYS_MBAR; /* base of internal registers */ +#endif +#if defined(CONFIG_MPC83xx) + bd->bi_immrbar = CONFIG_SYS_IMMR; +#endif + + return 0; +} + +static int setup_board_part2(void) +{ + bd_t *bd = gd->bd; + + bd->bi_intfreq = gd->cpu_clk; /* Internal Freq, in Hz */ + bd->bi_busfreq = gd->bus_clk; /* Bus Freq, in Hz */ +#if defined(CONFIG_CPM2) + bd->bi_cpmfreq = gd->arch.cpm_clk; + bd->bi_brgfreq = gd->arch.brg_clk; + bd->bi_sccfreq = gd->arch.scc_clk; + bd->bi_vco = gd->arch.vco_out; +#endif /* CONFIG_CPM2 */ +#if defined(CONFIG_MPC512X) + bd->bi_ipsfreq = gd->arch.ips_clk; +#endif /* CONFIG_MPC512X */ +#if defined(CONFIG_MPC5xxx) + bd->bi_ipbfreq = gd->arch.ipb_clk; + bd->bi_pcifreq = gd->pci_clk; +#endif /* CONFIG_MPC5xxx */ + + return 0; +} +#endif + +#ifdef CONFIG_SYS_EXTBDINFO +static int setup_board_extra(void) +{ + bd_t *bd = gd->bd; + + strncpy((char *) bd->bi_s_version, "1.2", sizeof(bd->bi_s_version)); + strncpy((char *) bd->bi_r_version, U_BOOT_VERSION, + sizeof(bd->bi_r_version)); + + bd->bi_procfreq = gd->cpu_clk; /* Processor Speed, In Hz */ + bd->bi_plb_busfreq = gd->bus_clk; +#if defined(CONFIG_405GP) || defined(CONFIG_405EP) || \ + defined(CONFIG_440EP) || defined(CONFIG_440GR) || \ + defined(CONFIG_440EPX) || defined(CONFIG_440GRX) + bd->bi_pci_busfreq = get_PCI_freq(); + bd->bi_opbfreq = get_OPB_freq(); +#elif defined(CONFIG_XILINX_405) + bd->bi_pci_busfreq = get_PCI_freq(); +#endif + + return 0; +} +#endif + +#ifdef CONFIG_POST +static int init_post(void) +{ + post_bootmode_init(); + post_run(NULL, POST_ROM | post_bootmode_get(0)); + + return 0; +} +#endif + +static int setup_dram_config(void) +{ + /* Ram is board specific, so move it to board code ... */ + dram_init_banksize(); + + return 0; +} + +static int reloc_fdt(void) +{ + if (gd->new_fdt) { + memcpy(gd->new_fdt, gd->fdt_blob, gd->fdt_size); + gd->fdt_blob = gd->new_fdt; + } + + return 0; +} + +static int setup_reloc(void) +{ + gd->reloc_off = gd->relocaddr - CONFIG_SYS_TEXT_BASE; + memcpy(gd->new_gd, (char *)gd, sizeof(gd_t)); + + debug("Relocation Offset is: %08lx\n", gd->reloc_off); + debug("Relocating to %08lx, new gd at %08lx, sp at %08lx\n", + gd->relocaddr, (ulong)map_to_sysmem(gd->new_gd), + gd->start_addr_sp); + + return 0; +} + +/* ARM calls relocate_code from its crt0.S */ +#if !defined(CONFIG_ARM) && !defined(CONFIG_SANDBOX) + +static int jump_to_copy(void) +{ + /* + * x86 is special, but in a nice way. It uses a trampoline which + * enables the dcache if possible. + * + * For now, other archs use relocate_code(), which is implemented + * similarly for all archs. When we do generic relocation, hopefully + * we can make all archs enable the dcache prior to relocation. + */ +#ifdef CONFIG_X86 + /* + * SDRAM and console are now initialised. The final stack can now + * be setup in SDRAM. Code execution will continue in Flash, but + * with the stack in SDRAM and Global Data in temporary memory + * (CPU cache) + */ + board_init_f_r_trampoline(gd->start_addr_sp); +#else + relocate_code(gd->start_addr_sp, gd->new_gd, gd->relocaddr); +#endif + + return 0; +} +#endif + +/* Record the board_init_f() bootstage (after arch_cpu_init()) */ +static int mark_bootstage(void) +{ + bootstage_mark_name(BOOTSTAGE_ID_START_UBOOT_F, "board_init_f"); + + return 0; +} + +static init_fnc_t init_sequence_f[] = { +#ifdef CONFIG_SANDBOX + setup_ram_buf, +#endif + setup_mon_len, + setup_fdt, + trace_early_init, +#if defined(CONFIG_MPC85xx) || defined(CONFIG_MPC86xx) + /* TODO: can this go into arch_cpu_init()? */ + probecpu, +#endif + arch_cpu_init, /* basic arch cpu dependent setup */ +#ifdef CONFIG_X86 + cpu_init_f, /* TODO(sjg@chromium.org): remove */ +# ifdef CONFIG_OF_CONTROL + find_fdt, /* TODO(sjg@chromium.org): remove */ +# endif +#endif + mark_bootstage, +#ifdef CONFIG_OF_CONTROL + fdtdec_check_fdt, +#endif +#if defined(CONFIG_BOARD_EARLY_INIT_F) + board_early_init_f, +#endif + /* TODO: can any of this go into arch_cpu_init()? */ +#if defined(CONFIG_PPC) && !defined(CONFIG_8xx_CPUCLK_DEFAULT) + get_clocks, /* get CPU and bus clocks (etc.) */ +#if defined(CONFIG_TQM8xxL) && !defined(CONFIG_TQM866M) \ + && !defined(CONFIG_TQM885D) + adjust_sdram_tbs_8xx, +#endif + /* TODO: can we rename this to timer_init()? */ + init_timebase, +#endif +#if defined(CONFIG_ARM) || defined(CONFIG_MIPS) + timer_init, /* initialize timer */ +#endif +#ifdef CONFIG_SYS_ALLOC_DPRAM +#if !defined(CONFIG_CPM2) + dpram_init, +#endif +#endif +#if defined(CONFIG_BOARD_POSTCLK_INIT) + board_postclk_init, +#endif +#ifdef CONFIG_FSL_ESDHC + get_clocks, +#endif + env_init, /* initialize environment */ +#if defined(CONFIG_8xx_CPUCLK_DEFAULT) + /* get CPU and bus clocks according to the environment variable */ + get_clocks_866, + /* adjust sdram refresh rate according to the new clock */ + sdram_adjust_866, + init_timebase, +#endif + init_baud_rate, /* initialze baudrate settings */ + serial_init, /* serial communications setup */ + console_init_f, /* stage 1 init of console */ +#ifdef CONFIG_SANDBOX + sandbox_early_getopt_check, +#endif +#ifdef CONFIG_OF_CONTROL + fdtdec_prepare_fdt, +#endif + display_options, /* say that we are here */ + display_text_info, /* show debugging info if required */ +#if defined(CONFIG_MPC8260) + prt_8260_rsr, + prt_8260_clks, +#endif /* CONFIG_MPC8260 */ +#if defined(CONFIG_MPC83xx) + prt_83xx_rsr, +#endif +#ifdef CONFIG_PPC + checkcpu, +#endif + print_cpuinfo, /* display cpu info (and speed) */ +#if defined(CONFIG_MPC5xxx) + prt_mpc5xxx_clks, +#endif /* CONFIG_MPC5xxx */ +#if defined(CONFIG_DISPLAY_BOARDINFO) + checkboard, /* display board info */ +#endif + INIT_FUNC_WATCHDOG_INIT +#if defined(CONFIG_MISC_INIT_F) + misc_init_f, +#endif + INIT_FUNC_WATCHDOG_RESET +#if defined(CONFIG_HARD_I2C) || defined(CONFIG_SYS_I2C) + init_func_i2c, +#endif +#if defined(CONFIG_HARD_SPI) + init_func_spi, +#endif +#ifdef CONFIG_X86 + dram_init_f, /* configure available RAM banks */ + calculate_relocation_address, +#endif + announce_dram_init, + /* TODO: unify all these dram functions? */ +#ifdef CONFIG_ARM + dram_init, /* configure available RAM banks */ +#endif +#if defined(CONFIG_MIPS) || defined(CONFIG_PPC) + init_func_ram, +#endif +#ifdef CONFIG_POST + post_init_f, +#endif + INIT_FUNC_WATCHDOG_RESET +#if defined(CONFIG_SYS_DRAM_TEST) + testdram, +#endif /* CONFIG_SYS_DRAM_TEST */ + INIT_FUNC_WATCHDOG_RESET + +#ifdef CONFIG_POST + init_post, +#endif + INIT_FUNC_WATCHDOG_RESET + /* + * Now that we have DRAM mapped and working, we can + * relocate the code and continue running from DRAM. + * + * Reserve memory at end of RAM for (top down in that order): + * - area that won't get touched by U-Boot and Linux (optional) + * - kernel log buffer + * - protected RAM + * - LCD framebuffer + * - monitor code + * - board info struct + */ + setup_dest_addr, +#if defined(CONFIG_LOGBUFFER) && !defined(CONFIG_ALT_LB_ADDR) + reserve_logbuffer, +#endif +#ifdef CONFIG_PRAM + reserve_pram, +#endif + reserve_round_4k, +#if !(defined(CONFIG_SYS_ICACHE_OFF) && defined(CONFIG_SYS_DCACHE_OFF)) && \ + defined(CONFIG_ARM) + reserve_mmu, +#endif +#ifdef CONFIG_LCD + reserve_lcd, +#endif + reserve_trace, + /* TODO: Why the dependency on CONFIG_8xx? */ +#if defined(CONFIG_VIDEO) && (!defined(CONFIG_PPC) || defined(CONFIG_8xx)) \ + && !defined(CONFIG_ARM) && !defined(CONFIG_X86) + reserve_video, +#endif + reserve_uboot, +#ifndef CONFIG_SPL_BUILD + reserve_malloc, + reserve_board, +#endif + setup_machine, + reserve_global_data, + reserve_fdt, + reserve_stacks, + setup_dram_config, + show_dram_config, +#ifdef CONFIG_PPC + setup_board_part1, + INIT_FUNC_WATCHDOG_RESET + setup_board_part2, +#endif + display_new_sp, +#ifdef CONFIG_SYS_EXTBDINFO + setup_board_extra, +#endif + INIT_FUNC_WATCHDOG_RESET + reloc_fdt, + setup_reloc, +#if !defined(CONFIG_ARM) && !defined(CONFIG_SANDBOX) + jump_to_copy, +#endif + NULL, +}; + +void board_init_f(ulong boot_flags) +{ +#ifdef CONFIG_SYS_GENERIC_GLOBAL_DATA + /* + * For some archtectures, global data is initialized and used before + * calling this function. The data should be preserved. For others, + * CONFIG_SYS_GENERIC_GLOBAL_DATA should be defined and use the stack + * here to host global data until relocation. + */ + gd_t data; + + gd = &data; + + /* + * Clear global data before it is accessed at debug print + * in initcall_run_list. Otherwise the debug print probably + * get the wrong vaule of gd->have_console. + */ + zero_global_data(); +#endif + + gd->flags = boot_flags; + gd->have_console = 0; + + if (initcall_run_list(init_sequence_f)) + hang(); + +#if !defined(CONFIG_ARM) && !defined(CONFIG_SANDBOX) + /* NOTREACHED - jump_to_copy() does not return */ + hang(); +#endif +} + +#ifdef CONFIG_X86 +/* + * For now this code is only used on x86. + * + * init_sequence_f_r is the list of init functions which are run when + * U-Boot is executing from Flash with a semi-limited 'C' environment. + * The following limitations must be considered when implementing an + * '_f_r' function: + * - 'static' variables are read-only + * - Global Data (gd->xxx) is read/write + * + * The '_f_r' sequence must, as a minimum, copy U-Boot to RAM (if + * supported). It _should_, if possible, copy global data to RAM and + * initialise the CPU caches (to speed up the relocation process) + * + * NOTE: At present only x86 uses this route, but it is intended that + * all archs will move to this when generic relocation is implemented. + */ +static init_fnc_t init_sequence_f_r[] = { + init_cache_f_r, + copy_uboot_to_ram, + clear_bss, + do_elf_reloc_fixups, + + NULL, +}; + +void board_init_f_r(void) +{ + if (initcall_run_list(init_sequence_f_r)) + hang(); + + /* + * U-Boot has been copied into SDRAM, the BSS has been cleared etc. + * Transfer execution from Flash to RAM by calculating the address + * of the in-RAM copy of board_init_r() and calling it + */ + (board_init_r + gd->reloc_off)(gd, gd->relocaddr); + + /* NOTREACHED - board_init_r() does not return */ + hang(); +} +#endif /* CONFIG_X86 */ diff --git a/qemu/roms/u-boot/common/board_r.c b/qemu/roms/u-boot/common/board_r.c new file mode 100644 index 000000000..d1f0aa9b1 --- /dev/null +++ b/qemu/roms/u-boot/common/board_r.c @@ -0,0 +1,958 @@ +/* + * Copyright (c) 2011 The Chromium OS Authors. + * (C) Copyright 2002-2006 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * (C) Copyright 2002 + * Sysgo Real-Time Solutions, GmbH <www.elinos.com> + * Marius Groeger <mgroeger@sysgo.de> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +/* TODO: can we just include all these headers whether needed or not? */ +#if defined(CONFIG_CMD_BEDBUG) +#include <bedbug/type.h> +#endif +#ifdef CONFIG_HAS_DATAFLASH +#include <dataflash.h> +#endif +#include <dm.h> +#include <environment.h> +#include <fdtdec.h> +#if defined(CONFIG_CMD_IDE) +#include <ide.h> +#endif +#include <initcall.h> +#ifdef CONFIG_PS2KBD +#include <keyboard.h> +#endif +#if defined(CONFIG_CMD_KGDB) +#include <kgdb.h> +#endif +#include <logbuff.h> +#include <malloc.h> +#ifdef CONFIG_BITBANGMII +#include <miiphy.h> +#endif +#include <mmc.h> +#include <nand.h> +#include <onenand_uboot.h> +#include <scsi.h> +#include <serial.h> +#include <spi.h> +#include <stdio_dev.h> +#include <trace.h> +#include <watchdog.h> +#ifdef CONFIG_ADDR_MAP +#include <asm/mmu.h> +#endif +#include <asm/sections.h> +#ifdef CONFIG_X86 +#include <asm/init_helpers.h> +#endif +#include <dm/root.h> +#include <linux/compiler.h> +#include <linux/err.h> + +DECLARE_GLOBAL_DATA_PTR; + +ulong monitor_flash_len; + +int __board_flash_wp_on(void) +{ + /* + * Most flashes can't be detected when write protection is enabled, + * so provide a way to let U-Boot gracefully ignore write protected + * devices. + */ + return 0; +} + +int board_flash_wp_on(void) + __attribute__ ((weak, alias("__board_flash_wp_on"))); + +void __cpu_secondary_init_r(void) +{ +} + +void cpu_secondary_init_r(void) + __attribute__ ((weak, alias("__cpu_secondary_init_r"))); + +static int initr_secondary_cpu(void) +{ + /* + * after non-volatile devices & environment is setup and cpu code have + * another round to deal with any initialization that might require + * full access to the environment or loading of some image (firmware) + * from a non-volatile device + */ + /* TODO: maybe define this for all archs? */ + cpu_secondary_init_r(); + + return 0; +} + +static int initr_trace(void) +{ +#ifdef CONFIG_TRACE + trace_init(gd->trace_buff, CONFIG_TRACE_BUFFER_SIZE); +#endif + + return 0; +} + +static int initr_reloc(void) +{ + gd->flags |= GD_FLG_RELOC; /* tell others: relocation done */ + bootstage_mark_name(BOOTSTAGE_ID_START_UBOOT_R, "board_init_r"); + + return 0; +} + +#ifdef CONFIG_ARM +/* + * Some of these functions are needed purely because the functions they + * call return void. If we change them to return 0, these stubs can go away. + */ +static int initr_caches(void) +{ + /* Enable caches */ + enable_caches(); + return 0; +} +#endif + +__weak int fixup_cpu(void) +{ + return 0; +} + +static int initr_reloc_global_data(void) +{ +#ifdef __ARM__ + monitor_flash_len = _end - __image_copy_start; +#elif !defined(CONFIG_SANDBOX) + monitor_flash_len = (ulong)&__init_end - gd->relocaddr; +#endif +#if defined(CONFIG_MPC85xx) || defined(CONFIG_MPC86xx) + /* + * The gd->cpu pointer is set to an address in flash before relocation. + * We need to update it to point to the same CPU entry in RAM. + * TODO: why not just add gd->reloc_ofs? + */ + gd->arch.cpu += gd->relocaddr - CONFIG_SYS_MONITOR_BASE; + + /* + * If we didn't know the cpu mask & # cores, we can save them of + * now rather than 'computing' them constantly + */ + fixup_cpu(); +#endif +#ifdef CONFIG_SYS_EXTRA_ENV_RELOC + /* + * Some systems need to relocate the env_addr pointer early because the + * location it points to will get invalidated before env_relocate is + * called. One example is on systems that might use a L2 or L3 cache + * in SRAM mode and initialize that cache from SRAM mode back to being + * a cache in cpu_init_r. + */ + gd->env_addr += gd->relocaddr - CONFIG_SYS_MONITOR_BASE; +#endif + return 0; +} + +static int initr_serial(void) +{ + serial_initialize(); + return 0; +} + +#ifdef CONFIG_PPC +static int initr_trap(void) +{ + /* + * Setup trap handlers + */ + trap_init(gd->relocaddr); + + return 0; +} +#endif + +#ifdef CONFIG_ADDR_MAP +static int initr_addr_map(void) +{ + init_addr_map(); + + return 0; +} +#endif + +#ifdef CONFIG_LOGBUFFER +unsigned long logbuffer_base(void) +{ + return gd->ram_top - LOGBUFF_LEN; +} + +static int initr_logbuffer(void) +{ + logbuff_init_ptrs(); + return 0; +} +#endif + +#ifdef CONFIG_POST +static int initr_post_backlog(void) +{ + post_output_backlog(); + return 0; +} +#endif + +#ifdef CONFIG_SYS_DELAYED_ICACHE +static int initr_icache_enable(void) +{ + return 0; +} +#endif + +#if defined(CONFIG_SYS_INIT_RAM_LOCK) && defined(CONFIG_E500) +static int initr_unlock_ram_in_cache(void) +{ + unlock_ram_in_cache(); /* it's time to unlock D-cache in e500 */ + return 0; +} +#endif + +#ifdef CONFIG_PCI +static int initr_pci(void) +{ + pci_init(); + + return 0; +} +#endif + +#ifdef CONFIG_WINBOND_83C553 +static int initr_w83c553f(void) +{ + /* + * Initialise the ISA bridge + */ + initialise_w83c553f(); + return 0; +} +#endif + +static int initr_barrier(void) +{ +#ifdef CONFIG_PPC + /* TODO: Can we not use dmb() macros for this? */ + asm("sync ; isync"); +#endif + return 0; +} + +static int initr_malloc(void) +{ + ulong malloc_start; + + /* The malloc area is immediately below the monitor copy in DRAM */ + malloc_start = gd->relocaddr - TOTAL_MALLOC_LEN; + mem_malloc_init((ulong)map_sysmem(malloc_start, TOTAL_MALLOC_LEN), + TOTAL_MALLOC_LEN); + return 0; +} + +#ifdef CONFIG_DM +static int initr_dm(void) +{ + int ret; + + ret = dm_init(); + if (ret) { + debug("dm_init() failed: %d\n", ret); + return ret; + } + ret = dm_scan_platdata(); + if (ret) { + debug("dm_scan_platdata() failed: %d\n", ret); + return ret; + } +#ifdef CONFIG_OF_CONTROL + ret = dm_scan_fdt(gd->fdt_blob); + if (ret) { + debug("dm_scan_fdt() failed: %d\n", ret); + return ret; + } +#endif + + return 0; +} +#endif + +__weak int power_init_board(void) +{ + return 0; +} + +static int initr_announce(void) +{ + debug("Now running in RAM - U-Boot at: %08lx\n", gd->relocaddr); + return 0; +} + +#if !defined(CONFIG_SYS_NO_FLASH) +static int initr_flash(void) +{ + ulong flash_size = 0; + bd_t *bd = gd->bd; + int ok; + + puts("Flash: "); + + if (board_flash_wp_on()) { + printf("Uninitialized - Write Protect On\n"); + /* Since WP is on, we can't find real size. Set to 0 */ + ok = 1; + } else { + flash_size = flash_init(); + ok = flash_size > 0; + } + if (!ok) { + puts("*** failed ***\n"); +#ifdef CONFIG_PPC + /* Why does PPC do this? */ + hang(); +#endif + return -1; + } + print_size(flash_size, ""); +#ifdef CONFIG_SYS_FLASH_CHECKSUM + /* + * Compute and print flash CRC if flashchecksum is set to 'y' + * + * NOTE: Maybe we should add some WATCHDOG_RESET()? XXX + */ + if (getenv_yesno("flashchecksum") == 1) { + printf(" CRC: %08X", crc32(0, + (const unsigned char *) CONFIG_SYS_FLASH_BASE, + flash_size)); + } +#endif /* CONFIG_SYS_FLASH_CHECKSUM */ + putc('\n'); + + /* update start of FLASH memory */ +#ifdef CONFIG_SYS_FLASH_BASE + bd->bi_flashstart = CONFIG_SYS_FLASH_BASE; +#endif + /* size of FLASH memory (final value) */ + bd->bi_flashsize = flash_size; + +#if defined(CONFIG_SYS_UPDATE_FLASH_SIZE) + /* Make a update of the Memctrl. */ + update_flash_size(flash_size); +#endif + + +#if defined(CONFIG_OXC) || defined(CONFIG_RMU) + /* flash mapped at end of memory map */ + bd->bi_flashoffset = CONFIG_SYS_TEXT_BASE + flash_size; +#elif CONFIG_SYS_MONITOR_BASE == CONFIG_SYS_FLASH_BASE + bd->bi_flashoffset = monitor_flash_len; /* reserved area for monitor */ +#endif + return 0; +} +#endif + +#ifdef CONFIG_PPC +static int initr_spi(void) +{ + /* PPC does this here */ +#ifdef CONFIG_SPI +#if !defined(CONFIG_ENV_IS_IN_EEPROM) + spi_init_f(); +#endif + spi_init_r(); +#endif + return 0; +} +#endif + +#ifdef CONFIG_CMD_NAND +/* go init the NAND */ +int initr_nand(void) +{ + puts("NAND: "); + nand_init(); + return 0; +} +#endif + +#if defined(CONFIG_CMD_ONENAND) +/* go init the NAND */ +int initr_onenand(void) +{ + puts("NAND: "); + onenand_init(); + return 0; +} +#endif + +#ifdef CONFIG_GENERIC_MMC +int initr_mmc(void) +{ + puts("MMC: "); + mmc_initialize(gd->bd); + return 0; +} +#endif + +#ifdef CONFIG_HAS_DATAFLASH +int initr_dataflash(void) +{ + AT91F_DataflashInit(); + dataflash_print_info(); + return 0; +} +#endif + +/* + * Tell if it's OK to load the environment early in boot. + * + * If CONFIG_OF_CONFIG is defined, we'll check with the FDT to see + * if this is OK (defaulting to saying it's OK). + * + * NOTE: Loading the environment early can be a bad idea if security is + * important, since no verification is done on the environment. + * + * @return 0 if environment should not be loaded, !=0 if it is ok to load + */ +static int should_load_env(void) +{ +#ifdef CONFIG_OF_CONTROL + return fdtdec_get_config_int(gd->fdt_blob, "load-environment", 1); +#elif defined CONFIG_DELAY_ENVIRONMENT + return 0; +#else + return 1; +#endif +} + +static int initr_env(void) +{ + /* initialize environment */ + if (should_load_env()) + env_relocate(); + else + set_default_env(NULL); + + /* Initialize from environment */ + load_addr = getenv_ulong("loadaddr", 16, load_addr); +#if defined(CONFIG_SYS_EXTBDINFO) +#if defined(CONFIG_405GP) || defined(CONFIG_405EP) +#if defined(CONFIG_I2CFAST) + /* + * set bi_iic_fast for linux taking environment variable + * "i2cfast" into account + */ + { + char *s = getenv("i2cfast"); + + if (s && ((*s == 'y') || (*s == 'Y'))) { + gd->bd->bi_iic_fast[0] = 1; + gd->bd->bi_iic_fast[1] = 1; + } + } +#endif /* CONFIG_I2CFAST */ +#endif /* CONFIG_405GP, CONFIG_405EP */ +#endif /* CONFIG_SYS_EXTBDINFO */ + return 0; +} + +#ifdef CONFIG_HERMES +static int initr_hermes(void) +{ + if ((gd->board_type >> 16) == 2) + gd->bd->bi_ethspeed = gd->board_type & 0xFFFF; + else + gd->bd->bi_ethspeed = 0xFFFF; + return 0; +} + +static int initr_hermes_start(void) +{ + if (gd->bd->bi_ethspeed != 0xFFFF) + hermes_start_lxt980((int) gd->bd->bi_ethspeed); + return 0; +} +#endif + +#ifdef CONFIG_SC3 +/* TODO: with new initcalls, move this into the driver */ +extern void sc3_read_eeprom(void); + +static int initr_sc3_read_eeprom(void) +{ + sc3_read_eeprom(); + return 0; +} +#endif + +static int initr_jumptable(void) +{ + jumptable_init(); + return 0; +} + +#if defined(CONFIG_API) +static int initr_api(void) +{ + /* Initialize API */ + api_init(); + return 0; +} +#endif + +#ifdef CONFIG_DISPLAY_BOARDINFO_LATE +static int show_model_r(void) +{ + /* Put this here so it appears on the LCD, now it is ready */ +# ifdef CONFIG_OF_CONTROL + const char *model; + + model = (char *)fdt_getprop(gd->fdt_blob, 0, "model", NULL); + printf("Model: %s\n", model ? model : "<unknown>"); +# else + checkboard(); +# endif + return 0; +} +#endif + +/* enable exceptions */ +#ifdef CONFIG_ARM +static int initr_enable_interrupts(void) +{ + enable_interrupts(); + return 0; +} +#endif + +#ifdef CONFIG_CMD_NET +static int initr_ethaddr(void) +{ + bd_t *bd = gd->bd; + + /* kept around for legacy kernels only ... ignore the next section */ + eth_getenv_enetaddr("ethaddr", bd->bi_enetaddr); +#ifdef CONFIG_HAS_ETH1 + eth_getenv_enetaddr("eth1addr", bd->bi_enet1addr); +#endif +#ifdef CONFIG_HAS_ETH2 + eth_getenv_enetaddr("eth2addr", bd->bi_enet2addr); +#endif +#ifdef CONFIG_HAS_ETH3 + eth_getenv_enetaddr("eth3addr", bd->bi_enet3addr); +#endif +#ifdef CONFIG_HAS_ETH4 + eth_getenv_enetaddr("eth4addr", bd->bi_enet4addr); +#endif +#ifdef CONFIG_HAS_ETH5 + eth_getenv_enetaddr("eth5addr", bd->bi_enet5addr); +#endif + return 0; +} +#endif /* CONFIG_CMD_NET */ + +#ifdef CONFIG_CMD_KGDB +static int initr_kgdb(void) +{ + puts("KGDB: "); + kgdb_init(); + return 0; +} +#endif + +#if defined(CONFIG_STATUS_LED) && defined(STATUS_LED_BOOT) +static int initr_status_led(void) +{ + status_led_set(STATUS_LED_BOOT, STATUS_LED_BLINKING); + + return 0; +} +#endif + +#if defined(CONFIG_CMD_SCSI) +static int initr_scsi(void) +{ + /* Not supported properly on ARM yet */ +#ifndef CONFIG_ARM + puts("SCSI: "); + scsi_init(); +#endif + + return 0; +} +#endif /* CONFIG_CMD_NET */ + +#if defined(CONFIG_CMD_DOC) +static int initr_doc(void) +{ + puts("DOC: "); + doc_init(); +} +#endif + +#ifdef CONFIG_BITBANGMII +static int initr_bbmii(void) +{ + bb_miiphy_init(); + return 0; +} +#endif + +#ifdef CONFIG_CMD_NET +static int initr_net(void) +{ + puts("Net: "); + eth_initialize(gd->bd); +#if defined(CONFIG_RESET_PHY_R) + debug("Reset Ethernet PHY\n"); + reset_phy(); +#endif + return 0; +} +#endif + +#ifdef CONFIG_POST +static int initr_post(void) +{ + post_run(NULL, POST_RAM | post_bootmode_get(0)); + return 0; +} +#endif + +#if defined(CONFIG_CMD_PCMCIA) && !defined(CONFIG_CMD_IDE) +static int initr_pcmcia(void) +{ + puts("PCMCIA:"); + pcmcia_init(); + return 0; +} +#endif + +#if defined(CONFIG_CMD_IDE) +static int initr_ide(void) +{ +#ifdef CONFIG_IDE_8xx_PCCARD + puts("PCMCIA:"); +#else + puts("IDE: "); +#endif +#if defined(CONFIG_START_IDE) + if (board_start_ide()) + ide_init(); +#else + ide_init(); +#endif + return 0; +} +#endif + +#if defined(CONFIG_PRAM) || defined(CONFIG_LOGBUFFER) +/* + * Export available size of memory for Linux, taking into account the + * protected RAM at top of memory + */ +int initr_mem(void) +{ + ulong pram = 0; + char memsz[32]; + +# ifdef CONFIG_PRAM + pram = getenv_ulong("pram", 10, CONFIG_PRAM); +# endif +# if defined(CONFIG_LOGBUFFER) && !defined(CONFIG_ALT_LB_ADDR) + /* Also take the logbuffer into account (pram is in kB) */ + pram += (LOGBUFF_LEN + LOGBUFF_OVERHEAD) / 1024; +# endif + sprintf(memsz, "%ldk", (gd->ram_size / 1024) - pram); + setenv("mem", memsz); + + return 0; +} +#endif + +#ifdef CONFIG_CMD_BEDBUG +static int initr_bedbug(void) +{ + bedbug_init(); + + return 0; +} +#endif + +#ifdef CONFIG_PS2KBD +static int initr_kbd(void) +{ + puts("PS/2: "); + kbd_init(); + return 0; +} +#endif + +#ifdef CONFIG_MODEM_SUPPORT +static int initr_modem(void) +{ + /* TODO: with new initcalls, move this into the driver */ + extern int do_mdm_init; + + do_mdm_init = gd->do_mdm_init; + return 0; +} +#endif + +static int run_main_loop(void) +{ +#ifdef CONFIG_SANDBOX + sandbox_main_loop_init(); +#endif + /* main_loop() can return to retry autoboot, if so just run it again */ + for (;;) + main_loop(); + return 0; +} + +/* + * Over time we hope to remove these functions with code fragments and + * stub funtcions, and instead call the relevant function directly. + * + * We also hope to remove most of the driver-related init and do it if/when + * the driver is later used. + * + * TODO: perhaps reset the watchdog in the initcall function after each call? + */ +init_fnc_t init_sequence_r[] = { + initr_trace, + initr_reloc, + /* TODO: could x86/PPC have this also perhaps? */ +#ifdef CONFIG_ARM + initr_caches, + board_init, /* Setup chipselects */ +#endif + /* + * TODO: printing of the clock inforamtion of the board is now + * implemented as part of bdinfo command. Currently only support for + * davinci SOC's is added. Remove this check once all the board + * implement this. + */ +#ifdef CONFIG_CLOCKS + set_cpu_clk_info, /* Setup clock information */ +#endif + initr_reloc_global_data, + initr_serial, + initr_announce, + INIT_FUNC_WATCHDOG_RESET +#ifdef CONFIG_PPC + initr_trap, +#endif +#ifdef CONFIG_ADDR_MAP + initr_addr_map, +#endif +#if defined(CONFIG_BOARD_EARLY_INIT_R) + board_early_init_r, +#endif + INIT_FUNC_WATCHDOG_RESET +#ifdef CONFIG_LOGBUFFER + initr_logbuffer, +#endif +#ifdef CONFIG_POST + initr_post_backlog, +#endif + INIT_FUNC_WATCHDOG_RESET +#ifdef CONFIG_SYS_DELAYED_ICACHE + initr_icache_enable, +#endif +#if defined(CONFIG_SYS_INIT_RAM_LOCK) && defined(CONFIG_E500) + initr_unlock_ram_in_cache, +#endif +#if defined(CONFIG_PCI) && defined(CONFIG_SYS_EARLY_PCI_INIT) + /* + * Do early PCI configuration _before_ the flash gets initialised, + * because PCU ressources are crucial for flash access on some boards. + */ + initr_pci, +#endif +#ifdef CONFIG_WINBOND_83C553 + initr_w83c553f, +#endif + initr_barrier, + initr_malloc, + bootstage_relocate, +#ifdef CONFIG_DM + initr_dm, +#endif +#ifdef CONFIG_ARCH_EARLY_INIT_R + arch_early_init_r, +#endif + power_init_board, +#ifndef CONFIG_SYS_NO_FLASH + initr_flash, +#endif + INIT_FUNC_WATCHDOG_RESET +#if defined(CONFIG_PPC) || defined(CONFIG_X86) + /* initialize higher level parts of CPU like time base and timers */ + cpu_init_r, +#endif +#ifdef CONFIG_PPC + initr_spi, +#endif +#if defined(CONFIG_X86) && defined(CONFIG_SPI) + init_func_spi, +#endif +#ifdef CONFIG_CMD_NAND + initr_nand, +#endif +#ifdef CONFIG_CMD_ONENAND + initr_onenand, +#endif +#ifdef CONFIG_GENERIC_MMC + initr_mmc, +#endif +#ifdef CONFIG_HAS_DATAFLASH + initr_dataflash, +#endif + initr_env, + INIT_FUNC_WATCHDOG_RESET + initr_secondary_cpu, +#ifdef CONFIG_SC3 + initr_sc3_read_eeprom, +#endif +#ifdef CONFIG_HERMES + initr_hermes, +#endif +#if defined(CONFIG_ID_EEPROM) || defined(CONFIG_SYS_I2C_MAC_OFFSET) + mac_read_from_eeprom, +#endif + INIT_FUNC_WATCHDOG_RESET +#if defined(CONFIG_PCI) && !defined(CONFIG_SYS_EARLY_PCI_INIT) + /* + * Do pci configuration + */ + initr_pci, +#endif + stdio_init, + initr_jumptable, +#ifdef CONFIG_API + initr_api, +#endif + console_init_r, /* fully init console as a device */ +#ifdef CONFIG_DISPLAY_BOARDINFO_LATE + show_model_r, +#endif +#ifdef CONFIG_ARCH_MISC_INIT + arch_misc_init, /* miscellaneous arch-dependent init */ +#endif +#ifdef CONFIG_MISC_INIT_R + misc_init_r, /* miscellaneous platform-dependent init */ +#endif +#ifdef CONFIG_HERMES + initr_hermes_start, +#endif + INIT_FUNC_WATCHDOG_RESET +#ifdef CONFIG_CMD_KGDB + initr_kgdb, +#endif +#ifdef CONFIG_X86 + board_early_init_r, +#endif + interrupt_init, +#if defined(CONFIG_ARM) || defined(CONFIG_x86) + initr_enable_interrupts, +#endif +#ifdef CONFIG_X86 + timer_init, /* initialize timer */ +#endif +#if defined(CONFIG_STATUS_LED) && defined(STATUS_LED_BOOT) + initr_status_led, +#endif + /* PPC has a udelay(20) here dating from 2002. Why? */ +#ifdef CONFIG_CMD_NET + initr_ethaddr, +#endif +#ifdef CONFIG_BOARD_LATE_INIT + board_late_init, +#endif +#ifdef CONFIG_CMD_SCSI + INIT_FUNC_WATCHDOG_RESET + initr_scsi, +#endif +#ifdef CONFIG_CMD_DOC + INIT_FUNC_WATCHDOG_RESET + initr_doc, +#endif +#ifdef CONFIG_BITBANGMII + initr_bbmii, +#endif +#ifdef CONFIG_CMD_NET + INIT_FUNC_WATCHDOG_RESET + initr_net, +#endif +#ifdef CONFIG_POST + initr_post, +#endif +#if defined(CONFIG_CMD_PCMCIA) && !defined(CONFIG_CMD_IDE) + initr_pcmcia, +#endif +#if defined(CONFIG_CMD_IDE) + initr_ide, +#endif +#ifdef CONFIG_LAST_STAGE_INIT + INIT_FUNC_WATCHDOG_RESET + /* + * Some parts can be only initialized if all others (like + * Interrupts) are up and running (i.e. the PC-style ISA + * keyboard). + */ + last_stage_init, +#endif +#ifdef CONFIG_CMD_BEDBUG + INIT_FUNC_WATCHDOG_RESET + initr_bedbug, +#endif +#if defined(CONFIG_PRAM) || defined(CONFIG_LOGBUFFER) + initr_mem, +#endif +#ifdef CONFIG_PS2KBD + initr_kbd, +#endif +#ifdef CONFIG_MODEM_SUPPORT + initr_modem, +#endif + run_main_loop, +}; + +void board_init_r(gd_t *new_gd, ulong dest_addr) +{ +#ifdef CONFIG_NEEDS_MANUAL_RELOC + int i; +#endif + +#ifndef CONFIG_X86 + gd = new_gd; +#endif + +#ifdef CONFIG_NEEDS_MANUAL_RELOC + for (i = 0; i < ARRAY_SIZE(init_sequence_r); i++) + init_sequence_r[i] += gd->reloc_off; +#endif + + if (initcall_run_list(init_sequence_r)) + hang(); + + /* NOTREACHED - run_main_loop() does not return */ + hang(); +} diff --git a/qemu/roms/u-boot/common/bootstage.c b/qemu/roms/u-boot/common/bootstage.c new file mode 100644 index 000000000..35bce3d88 --- /dev/null +++ b/qemu/roms/u-boot/common/bootstage.c @@ -0,0 +1,457 @@ +/* + * Copyright (c) 2011, Google Inc. All rights reserved. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + + +/* + * This module records the progress of boot and arbitrary commands, and + * permits accurate timestamping of each. + * + * TBD: Pass timings to kernel in the FDT + */ + +#include <common.h> +#include <libfdt.h> +#include <malloc.h> +#include <linux/compiler.h> + +DECLARE_GLOBAL_DATA_PTR; + +struct bootstage_record { + ulong time_us; + uint32_t start_us; + const char *name; + int flags; /* see enum bootstage_flags */ + enum bootstage_id id; +}; + +static struct bootstage_record record[BOOTSTAGE_ID_COUNT] = { {1} }; +static int next_id = BOOTSTAGE_ID_USER; + +enum { + BOOTSTAGE_VERSION = 0, + BOOTSTAGE_MAGIC = 0xb00757a3, + BOOTSTAGE_DIGITS = 9, +}; + +struct bootstage_hdr { + uint32_t version; /* BOOTSTAGE_VERSION */ + uint32_t count; /* Number of records */ + uint32_t size; /* Total data size (non-zero if valid) */ + uint32_t magic; /* Unused */ +}; + +int bootstage_relocate(void) +{ + int i; + + /* + * Duplicate all strings. They may point to an old location in the + * program .text section that can eventually get trashed. + */ + for (i = 0; i < BOOTSTAGE_ID_COUNT; i++) + if (record[i].name) + record[i].name = strdup(record[i].name); + + return 0; +} + +ulong bootstage_add_record(enum bootstage_id id, const char *name, + int flags, ulong mark) +{ + struct bootstage_record *rec; + + if (flags & BOOTSTAGEF_ALLOC) + id = next_id++; + + if (id < BOOTSTAGE_ID_COUNT) { + rec = &record[id]; + + /* Only record the first event for each */ + if (!rec->time_us) { + rec->time_us = mark; + rec->name = name; + rec->flags = flags; + rec->id = id; + } + } + + /* Tell the board about this progress */ + show_boot_progress(flags & BOOTSTAGEF_ERROR ? -id : id); + return mark; +} + + +ulong bootstage_mark(enum bootstage_id id) +{ + return bootstage_add_record(id, NULL, 0, timer_get_boot_us()); +} + +ulong bootstage_error(enum bootstage_id id) +{ + return bootstage_add_record(id, NULL, BOOTSTAGEF_ERROR, + timer_get_boot_us()); +} + +ulong bootstage_mark_name(enum bootstage_id id, const char *name) +{ + int flags = 0; + + if (id == BOOTSTAGE_ID_ALLOC) + flags = BOOTSTAGEF_ALLOC; + return bootstage_add_record(id, name, flags, timer_get_boot_us()); +} + +ulong bootstage_mark_code(const char *file, const char *func, int linenum) +{ + char *str, *p; + __maybe_unused char *end; + int len = 0; + + /* First work out the length we need to allocate */ + if (linenum != -1) + len = 11; + if (func) + len += strlen(func); + if (file) + len += strlen(file); + + str = malloc(len + 1); + p = str; + end = p + len; + if (file) + p += snprintf(p, end - p, "%s,", file); + if (linenum != -1) + p += snprintf(p, end - p, "%d", linenum); + if (func) + p += snprintf(p, end - p, ": %s", func); + + return bootstage_mark_name(BOOTSTAGE_ID_ALLOC, str); +} + +uint32_t bootstage_start(enum bootstage_id id, const char *name) +{ + struct bootstage_record *rec = &record[id]; + + rec->start_us = timer_get_boot_us(); + rec->name = name; + return rec->start_us; +} + +uint32_t bootstage_accum(enum bootstage_id id) +{ + struct bootstage_record *rec = &record[id]; + uint32_t duration; + + duration = (uint32_t)timer_get_boot_us() - rec->start_us; + rec->time_us += duration; + return duration; +} + +/** + * Get a record name as a printable string + * + * @param buf Buffer to put name if needed + * @param len Length of buffer + * @param rec Boot stage record to get the name from + * @return pointer to name, either from the record or pointing to buf. + */ +static const char *get_record_name(char *buf, int len, + struct bootstage_record *rec) +{ + if (rec->name) + return rec->name; + else if (rec->id >= BOOTSTAGE_ID_USER) + snprintf(buf, len, "user_%d", rec->id - BOOTSTAGE_ID_USER); + else + snprintf(buf, len, "id=%d", rec->id); + + return buf; +} + +static uint32_t print_time_record(enum bootstage_id id, + struct bootstage_record *rec, uint32_t prev) +{ + char buf[20]; + + if (prev == -1U) { + printf("%11s", ""); + print_grouped_ull(rec->time_us, BOOTSTAGE_DIGITS); + } else { + print_grouped_ull(rec->time_us, BOOTSTAGE_DIGITS); + print_grouped_ull(rec->time_us - prev, BOOTSTAGE_DIGITS); + } + printf(" %s\n", get_record_name(buf, sizeof(buf), rec)); + + return rec->time_us; +} + +static int h_compare_record(const void *r1, const void *r2) +{ + const struct bootstage_record *rec1 = r1, *rec2 = r2; + + return rec1->time_us > rec2->time_us ? 1 : -1; +} + +#ifdef CONFIG_OF_LIBFDT +/** + * Add all bootstage timings to a device tree. + * + * @param blob Device tree blob + * @return 0 on success, != 0 on failure. + */ +static int add_bootstages_devicetree(struct fdt_header *blob) +{ + int bootstage; + char buf[20]; + int id; + int i; + + if (!blob) + return 0; + + /* + * Create the node for bootstage. + * The address of flat device tree is set up by the command bootm. + */ + bootstage = fdt_add_subnode(blob, 0, "bootstage"); + if (bootstage < 0) + return -1; + + /* + * Insert the timings to the device tree in the reverse order so + * that they can be printed in the Linux kernel in the right order. + */ + for (id = BOOTSTAGE_ID_COUNT - 1, i = 0; id >= 0; id--, i++) { + struct bootstage_record *rec = &record[id]; + int node; + + if (id != BOOTSTAGE_ID_AWAKE && rec->time_us == 0) + continue; + + node = fdt_add_subnode(blob, bootstage, simple_itoa(i)); + if (node < 0) + break; + + /* add properties to the node. */ + if (fdt_setprop_string(blob, node, "name", + get_record_name(buf, sizeof(buf), rec))) + return -1; + + /* Check if this is a 'mark' or 'accum' record */ + if (fdt_setprop_cell(blob, node, + rec->start_us ? "accum" : "mark", + rec->time_us)) + return -1; + } + + return 0; +} + +int bootstage_fdt_add_report(void) +{ + if (add_bootstages_devicetree(working_fdt)) + puts("bootstage: Failed to add to device tree\n"); + + return 0; +} +#endif + +void bootstage_report(void) +{ + struct bootstage_record *rec = record; + int id; + uint32_t prev; + + puts("Timer summary in microseconds:\n"); + printf("%11s%11s %s\n", "Mark", "Elapsed", "Stage"); + + /* Fake the first record - we could get it from early boot */ + rec->name = "reset"; + rec->time_us = 0; + prev = print_time_record(BOOTSTAGE_ID_AWAKE, rec, 0); + + /* Sort records by increasing time */ + qsort(record, ARRAY_SIZE(record), sizeof(*rec), h_compare_record); + + for (id = 0; id < BOOTSTAGE_ID_COUNT; id++, rec++) { + if (rec->time_us != 0 && !rec->start_us) + prev = print_time_record(rec->id, rec, prev); + } + if (next_id > BOOTSTAGE_ID_COUNT) + printf("(Overflowed internal boot id table by %d entries\n" + "- please increase CONFIG_BOOTSTAGE_USER_COUNT\n", + next_id - BOOTSTAGE_ID_COUNT); + + puts("\nAccumulated time:\n"); + for (id = 0, rec = record; id < BOOTSTAGE_ID_COUNT; id++, rec++) { + if (rec->start_us) + prev = print_time_record(id, rec, -1); + } +} + +ulong __timer_get_boot_us(void) +{ + static ulong base_time; + + /* + * We can't implement this properly. Return 0 on the first call and + * larger values after that. + */ + if (base_time) + return get_timer(base_time) * 1000; + base_time = get_timer(0); + return 0; +} + +ulong timer_get_boot_us(void) + __attribute__((weak, alias("__timer_get_boot_us"))); + +/** + * Append data to a memory buffer + * + * Write data to the buffer if there is space. Whether there is space or not, + * the buffer pointer is incremented. + * + * @param ptrp Pointer to buffer, updated by this function + * @param end Pointer to end of buffer + * @param data Data to write to buffer + * @param size Size of data + */ +static void append_data(char **ptrp, char *end, const void *data, int size) +{ + char *ptr = *ptrp; + + *ptrp += size; + if (*ptrp > end) + return; + + memcpy(ptr, data, size); +} + +int bootstage_stash(void *base, int size) +{ + struct bootstage_hdr *hdr = (struct bootstage_hdr *)base; + struct bootstage_record *rec; + char buf[20]; + char *ptr = base, *end = ptr + size; + uint32_t count; + int id; + + if (hdr + 1 > (struct bootstage_hdr *)end) { + debug("%s: Not enough space for bootstage hdr\n", __func__); + return -1; + } + + /* Write an arbitrary version number */ + hdr->version = BOOTSTAGE_VERSION; + + /* Count the number of records, and write that value first */ + for (rec = record, id = count = 0; id < BOOTSTAGE_ID_COUNT; + id++, rec++) { + if (rec->time_us != 0) + count++; + } + hdr->count = count; + hdr->size = 0; + hdr->magic = BOOTSTAGE_MAGIC; + ptr += sizeof(*hdr); + + /* Write the records, silently stopping when we run out of space */ + for (rec = record, id = 0; id < BOOTSTAGE_ID_COUNT; id++, rec++) { + if (rec->time_us != 0) + append_data(&ptr, end, rec, sizeof(*rec)); + } + + /* Write the name strings */ + for (rec = record, id = 0; id < BOOTSTAGE_ID_COUNT; id++, rec++) { + if (rec->time_us != 0) { + const char *name; + + name = get_record_name(buf, sizeof(buf), rec); + append_data(&ptr, end, name, strlen(name) + 1); + } + } + + /* Check for buffer overflow */ + if (ptr > end) { + debug("%s: Not enough space for bootstage stash\n", __func__); + return -1; + } + + /* Update total data size */ + hdr->size = ptr - (char *)base; + printf("Stashed %d records\n", hdr->count); + + return 0; +} + +int bootstage_unstash(void *base, int size) +{ + struct bootstage_hdr *hdr = (struct bootstage_hdr *)base; + struct bootstage_record *rec; + char *ptr = base, *end = ptr + size; + uint rec_size; + int id; + + if (size == -1) + end = (char *)(~(uintptr_t)0); + + if (hdr + 1 > (struct bootstage_hdr *)end) { + debug("%s: Not enough space for bootstage hdr\n", __func__); + return -1; + } + + if (hdr->magic != BOOTSTAGE_MAGIC) { + debug("%s: Invalid bootstage magic\n", __func__); + return -1; + } + + if (ptr + hdr->size > end) { + debug("%s: Bootstage data runs past buffer end\n", __func__); + return -1; + } + + if (hdr->count * sizeof(*rec) > hdr->size) { + debug("%s: Bootstage has %d records needing %lu bytes, but " + "only %d bytes is available\n", __func__, hdr->count, + (ulong)hdr->count * sizeof(*rec), hdr->size); + return -1; + } + + if (hdr->version != BOOTSTAGE_VERSION) { + debug("%s: Bootstage data version %#0x unrecognised\n", + __func__, hdr->version); + return -1; + } + + if (next_id + hdr->count > BOOTSTAGE_ID_COUNT) { + debug("%s: Bootstage has %d records, we have space for %d\n" + "- please increase CONFIG_BOOTSTAGE_USER_COUNT\n", + __func__, hdr->count, BOOTSTAGE_ID_COUNT - next_id); + return -1; + } + + ptr += sizeof(*hdr); + + /* Read the records */ + rec_size = hdr->count * sizeof(*record); + memcpy(record + next_id, ptr, rec_size); + + /* Read the name strings */ + ptr += rec_size; + for (rec = record + next_id, id = 0; id < hdr->count; id++, rec++) { + rec->name = ptr; + + /* Assume no data corruption here */ + ptr += strlen(ptr) + 1; + } + + /* Mark the records as read */ + next_id += hdr->count; + printf("Unstashed %d records\n", hdr->count); + + return 0; +} diff --git a/qemu/roms/u-boot/common/bouncebuf.c b/qemu/roms/u-boot/common/bouncebuf.c new file mode 100644 index 000000000..9eece6d75 --- /dev/null +++ b/qemu/roms/u-boot/common/bouncebuf.c @@ -0,0 +1,83 @@ +/* + * Generic bounce buffer implementation + * + * Copyright (C) 2012 Marek Vasut <marex@denx.de> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <malloc.h> +#include <errno.h> +#include <bouncebuf.h> + +static int addr_aligned(struct bounce_buffer *state) +{ + const ulong align_mask = ARCH_DMA_MINALIGN - 1; + + /* Check if start is aligned */ + if ((ulong)state->user_buffer & align_mask) { + debug("Unaligned buffer address %p\n", state->user_buffer); + return 0; + } + + /* Check if length is aligned */ + if (state->len != state->len_aligned) { + debug("Unaligned buffer length %d\n", state->len); + return 0; + } + + /* Aligned */ + return 1; +} + +int bounce_buffer_start(struct bounce_buffer *state, void *data, + size_t len, unsigned int flags) +{ + state->user_buffer = data; + state->bounce_buffer = data; + state->len = len; + state->len_aligned = roundup(len, ARCH_DMA_MINALIGN); + state->flags = flags; + + if (!addr_aligned(state)) { + state->bounce_buffer = memalign(ARCH_DMA_MINALIGN, + state->len_aligned); + if (!state->bounce_buffer) + return -ENOMEM; + + if (state->flags & GEN_BB_READ) + memcpy(state->bounce_buffer, state->user_buffer, + state->len); + } + + /* + * Flush data to RAM so DMA reads can pick it up, + * and any CPU writebacks don't race with DMA writes + */ + flush_dcache_range((unsigned long)state->bounce_buffer, + (unsigned long)(state->bounce_buffer) + + state->len_aligned); + + return 0; +} + +int bounce_buffer_stop(struct bounce_buffer *state) +{ + if (state->flags & GEN_BB_WRITE) { + /* Invalidate cache so that CPU can see any newly DMA'd data */ + invalidate_dcache_range((unsigned long)state->bounce_buffer, + (unsigned long)(state->bounce_buffer) + + state->len_aligned); + } + + if (state->bounce_buffer == state->user_buffer) + return 0; + + if (state->flags & GEN_BB_WRITE) + memcpy(state->user_buffer, state->bounce_buffer, state->len); + + free(state->bounce_buffer); + + return 0; +} diff --git a/qemu/roms/u-boot/common/cmd_aes.c b/qemu/roms/u-boot/common/cmd_aes.c new file mode 100644 index 000000000..76da3efff --- /dev/null +++ b/qemu/roms/u-boot/common/cmd_aes.c @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2014 Marek Vasut <marex@denx.de> + * + * Command for en/de-crypting block of memory with AES-128-CBC cipher. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <command.h> +#include <environment.h> +#include <aes.h> +#include <malloc.h> +#include <asm/byteorder.h> +#include <linux/compiler.h> + +DECLARE_GLOBAL_DATA_PTR; + +/** + * do_aes() - Handle the "aes" 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_aes(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[]) +{ + uint32_t key_addr, src_addr, dst_addr, len; + uint8_t *key_ptr, *src_ptr, *dst_ptr; + uint8_t key_exp[AES_EXPAND_KEY_LENGTH]; + uint32_t aes_blocks; + int enc; + + if (argc != 6) + return CMD_RET_USAGE; + + if (!strncmp(argv[1], "enc", 3)) + enc = 1; + else if (!strncmp(argv[1], "dec", 3)) + enc = 0; + else + return CMD_RET_USAGE; + + key_addr = simple_strtoul(argv[2], NULL, 16); + src_addr = simple_strtoul(argv[3], NULL, 16); + dst_addr = simple_strtoul(argv[4], NULL, 16); + len = simple_strtoul(argv[5], NULL, 16); + + key_ptr = (uint8_t *)key_addr; + src_ptr = (uint8_t *)src_addr; + dst_ptr = (uint8_t *)dst_addr; + + /* First we expand the key. */ + aes_expand_key(key_ptr, key_exp); + + /* Calculate the number of AES blocks to encrypt. */ + aes_blocks = DIV_ROUND_UP(len, AES_KEY_LENGTH); + + if (enc) + aes_cbc_encrypt_blocks(key_exp, src_ptr, dst_ptr, aes_blocks); + else + aes_cbc_decrypt_blocks(key_exp, src_ptr, dst_ptr, aes_blocks); + + return 0; +} + +/***************************************************/ +#ifdef CONFIG_SYS_LONGHELP +static char aes_help_text[] = + "enc key src dst len - Encrypt block of data $len bytes long\n" + " at address $src using a key at address\n" + " $key and store the result at address\n" + " $dst. The $len size must be multiple of\n" + " 16 bytes and $key must be 16 bytes long.\n" + "aes dec key src dst len - Decrypt block of data $len bytes long\n" + " at address $src using a key at address\n" + " $key and store the result at address\n" + " $dst. The $len size must be multiple of\n" + " 16 bytes and $key must be 16 bytes long."; +#endif + +U_BOOT_CMD( + aes, 6, 1, do_aes, + "AES 128 CBC encryption", + aes_help_text +); diff --git a/qemu/roms/u-boot/common/cmd_ambapp.c b/qemu/roms/u-boot/common/cmd_ambapp.c new file mode 100644 index 000000000..2a1995a76 --- /dev/null +++ b/qemu/roms/u-boot/common/cmd_ambapp.c @@ -0,0 +1,264 @@ +/* + * (C) Copyright 2007 + * Daniel Hellstrom, Gaisler Research, daniel@gaisler.com. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/* + * AMBA Plug&Play information list command + * + */ +#include <common.h> +#include <command.h> +#include <ambapp.h> + +DECLARE_GLOBAL_DATA_PTR; + +/* We put these variables into .data section so that they are zero + * when entering the AMBA Plug & Play routines (in cpu/cpu/ambapp.c) + * the first time. BSS is not garantueed to be zero since BSS + * hasn't been cleared the first times entering the CPU AMBA functions. + * + * The AMBA PnP routines call these functions if ambapp_???_print is set. + * + */ +int ambapp_apb_print __attribute__ ((section(".data"))) = 0; +int ambapp_ahb_print __attribute__ ((section(".data"))) = 0; + +typedef struct { + int device_id; + char *name; +} ambapp_device_name; + +static ambapp_device_name gaisler_devices[] = { + {GAISLER_LEON3, "GAISLER_LEON3"}, + {GAISLER_LEON3DSU, "GAISLER_LEON3DSU"}, + {GAISLER_ETHAHB, "GAISLER_ETHAHB"}, + {GAISLER_ETHMAC, "GAISLER_ETHMAC"}, + {GAISLER_APBMST, "GAISLER_APBMST"}, + {GAISLER_AHBUART, "GAISLER_AHBUART"}, + {GAISLER_SRCTRL, "GAISLER_SRCTRL"}, + {GAISLER_SDCTRL, "GAISLER_SDCTRL"}, + {GAISLER_APBUART, "GAISLER_APBUART"}, + {GAISLER_IRQMP, "GAISLER_IRQMP"}, + {GAISLER_AHBRAM, "GAISLER_AHBRAM"}, + {GAISLER_GPTIMER, "GAISLER_GPTIMER"}, + {GAISLER_PCITRG, "GAISLER_PCITRG"}, + {GAISLER_PCISBRG, "GAISLER_PCISBRG"}, + {GAISLER_PCIFBRG, "GAISLER_PCIFBRG"}, + {GAISLER_PCITRACE, "GAISLER_PCITRACE"}, + {GAISLER_AHBTRACE, "GAISLER_AHBTRACE"}, + {GAISLER_ETHDSU, "GAISLER_ETHDSU"}, + {GAISLER_PIOPORT, "GAISLER_PIOPORT"}, + {GAISLER_AHBJTAG, "GAISLER_AHBJTAG"}, + {GAISLER_ATACTRL, "GAISLER_ATACTRL"}, + {GAISLER_VGA, "GAISLER_VGA"}, + {GAISLER_KBD, "GAISLER_KBD"}, + {GAISLER_L2TIME, "GAISLER_L2TIME"}, + {GAISLER_L2C, "GAISLER_L2C"}, + {GAISLER_PLUGPLAY, "GAISLER_PLUGPLAY"}, + {GAISLER_SPW, "GAISLER_SPW"}, + {GAISLER_SPW2, "GAISLER_SPW2"}, + {GAISLER_EHCI, "GAISLER_EHCI"}, + {GAISLER_UHCI, "GAISLER_UHCI"}, + {GAISLER_AHBSTAT, "GAISLER_AHBSTAT"}, + {GAISLER_DDR2SPA, "GAISLER_DDR2SPA"}, + {GAISLER_DDRSPA, "GAISLER_DDRSPA"}, + {0, NULL} +}; + +static ambapp_device_name esa_devices[] = { + {ESA_LEON2, "ESA_LEON2"}, + {ESA_MCTRL, "ESA_MCTRL"}, + {0, NULL} +}; + +static ambapp_device_name opencores_devices[] = { + {OPENCORES_PCIBR, "OPENCORES_PCIBR"}, + {OPENCORES_ETHMAC, "OPENCORES_ETHMAC"}, + {0, NULL} +}; + +typedef struct { + unsigned int vendor_id; + char *name; + ambapp_device_name *devices; +} ambapp_vendor_devnames; + +static ambapp_vendor_devnames vendors[] = { + {VENDOR_GAISLER, "VENDOR_GAISLER", gaisler_devices}, + {VENDOR_ESA, "VENDOR_ESA", esa_devices}, + {VENDOR_OPENCORES, "VENDOR_OPENCORES", opencores_devices}, + {0, NULL, 0} +}; + +static char *ambapp_get_devname(ambapp_device_name * devs, int id) +{ + if (!devs) + return NULL; + + while (devs->device_id > 0) { + if (devs->device_id == id) + return devs->name; + devs++; + } + return NULL; +} + +char *ambapp_device_id2str(int vendor, int id) +{ + ambapp_vendor_devnames *ven = &vendors[0]; + + while (ven->vendor_id > 0) { + if (ven->vendor_id == vendor) { + return ambapp_get_devname(ven->devices, id); + } + ven++; + } + return NULL; +} + +char *ambapp_vendor_id2str(int vendor) +{ + ambapp_vendor_devnames *ven = &vendors[0]; + + while (ven->vendor_id > 0) { + if (ven->vendor_id == vendor) { + return ven->name; + } + ven++; + } + return NULL; +} + +static char *unknown = "unknown"; + +/* Print one APB device */ +void ambapp_print_apb(apbctrl_pp_dev * apb, ambapp_ahbdev * apbmst, int index) +{ + char *dev_str, *ven_str; + int irq, ver, vendor, deviceid; + unsigned int address, apbmst_base, mask; + + vendor = amba_vendor(apb->conf); + deviceid = amba_device(apb->conf); + irq = amba_irq(apb->conf); + ver = amba_ver(apb->conf); + apbmst_base = apbmst->address[0] & LEON3_IO_AREA; + address = (apbmst_base | (((apb->bar & 0xfff00000) >> 12))) & + (((apb->bar & 0x0000fff0) << 4) | 0xfff00000); + + mask = amba_membar_mask(apb->bar) << 8; + mask = ((~mask) & 0x000fffff) + 1; + + ven_str = ambapp_vendor_id2str(vendor); + if (!ven_str) { + ven_str = unknown; + dev_str = unknown; + } else { + dev_str = ambapp_device_id2str(vendor, deviceid); + if (!dev_str) + dev_str = unknown; + } + + printf("0x%02x:0x%02x:0x%02x: %s %s\n" + " apb: 0x%08x - 0x%08x\n" + " irq: %-2d (ver: %-2d)\n", + index, vendor, deviceid, ven_str, dev_str, address, + address + mask, irq, ver); +} + +void ambapp_print_ahb(ahbctrl_pp_dev * ahb, int index) +{ + char *dev_str, *ven_str; + int irq, ver, vendor, deviceid; + unsigned int addr, mask; + int j; + + vendor = amba_vendor(ahb->conf); + deviceid = amba_device(ahb->conf); + irq = amba_irq(ahb->conf); + ver = amba_ver(ahb->conf); + + ven_str = ambapp_vendor_id2str(vendor); + if (!ven_str) { + ven_str = unknown; + dev_str = unknown; + } else { + dev_str = ambapp_device_id2str(vendor, deviceid); + if (!dev_str) + dev_str = unknown; + } + + printf("0x%02x:0x%02x:0x%02x: %s %s\n", + index, vendor, deviceid, ven_str, dev_str); + + for (j = 0; j < 4; j++) { + addr = amba_membar_start(ahb->bars[j]); + if (amba_membar_type(ahb->bars[j]) == 0) + continue; + if (amba_membar_type(ahb->bars[j]) == AMBA_TYPE_AHBIO) + addr = AMBA_TYPE_AHBIO_ADDR(addr); + mask = amba_membar_mask(ahb->bars[j]) << 20; + printf(" mem: 0x%08x - 0x%08x\n", addr, addr + ((~mask) + 1)); + } + + printf(" irq: %-2d (ver: %d)\n", irq, ver); +} + +int do_ambapp_print(cmd_tbl_t * cmdtp, int flag, int argc, char * const argv[]) +{ + + /* Print AHB Masters */ + puts("--------- AHB Masters ---------\n"); + ambapp_apb_print = 0; + ambapp_ahb_print = 1; + ambapp_ahbmst_count(99, 99); /* Get vendor&device 99 = nonexistent... */ + + /* Print AHB Slaves */ + puts("--------- AHB Slaves ---------\n"); + ambapp_ahbslv_count(99, 99); /* Get vendor&device 99 = nonexistent... */ + + /* Print APB Slaves */ + puts("--------- APB Slaves ---------\n"); + ambapp_apb_print = 1; + ambapp_ahb_print = 0; + ambapp_apb_count(99, 99); /* Get vendor&device 99 = nonexistent... */ + + /* Reset, no futher printing */ + ambapp_apb_print = 0; + ambapp_ahb_print = 0; + puts("\n"); + return 0; +} + +int ambapp_init_reloc(void) +{ + ambapp_vendor_devnames *vend = vendors; + ambapp_device_name *dev; + + while (vend->vendor_id && vend->name) { + vend->name = (char *)((unsigned int)vend->name + gd->reloc_off); + vend->devices = + (ambapp_device_name *) ((unsigned int)vend->devices + + gd->reloc_off);; + dev = vend->devices; + vend++; + if (!dev) + continue; + while (dev->device_id && dev->name) { + dev->name = + (char *)((unsigned int)dev->name + gd->reloc_off);; + dev++; + } + } + return 0; +} + +U_BOOT_CMD( + ambapp, 1, 1, do_ambapp_print, + "list AMBA Plug&Play information", + "ambapp\n" + " - lists AMBA (AHB & APB) Plug&Play devices present on the system" +); diff --git a/qemu/roms/u-boot/common/cmd_bdinfo.c b/qemu/roms/u-boot/common/cmd_bdinfo.c new file mode 100644 index 000000000..f283a1616 --- /dev/null +++ b/qemu/roms/u-boot/common/cmd_bdinfo.c @@ -0,0 +1,548 @@ +/* + * (C) Copyright 2003 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/* + * Boot support + */ +#include <common.h> +#include <command.h> +#include <linux/compiler.h> + +DECLARE_GLOBAL_DATA_PTR; + +__maybe_unused +static void print_num(const char *name, ulong value) +{ + printf("%-12s= 0x%08lX\n", name, value); +} + +__maybe_unused +static void print_eth(int idx) +{ + char name[10], *val; + if (idx) + sprintf(name, "eth%iaddr", idx); + else + strcpy(name, "ethaddr"); + val = getenv(name); + if (!val) + val = "(not set)"; + printf("%-12s= %s\n", name, val); +} + +__maybe_unused +static void print_eths(void) +{ + struct eth_device *dev; + int i = 0; + + do { + dev = eth_get_dev_by_index(i); + if (dev) { + printf("eth%dname = %s\n", i, dev->name); + print_eth(i); + i++; + } + } while (dev); + + printf("current eth = %s\n", eth_get_name()); + printf("ip_addr = %s\n", getenv("ipaddr")); +} + +__maybe_unused +static void print_lnum(const char *name, unsigned long long value) +{ + printf("%-12s= 0x%.8llX\n", name, value); +} + +__maybe_unused +static void print_mhz(const char *name, unsigned long hz) +{ + char buf[32]; + + printf("%-12s= %6s MHz\n", name, strmhz(buf, hz)); +} + +#if defined(CONFIG_PPC) +void __weak board_detail(void) +{ + /* Please define boot_detail() for your platform */ +} + +int do_bdinfo(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + bd_t *bd = gd->bd; + +#ifdef DEBUG + print_num("bd address", (ulong)bd); +#endif + print_num("memstart", bd->bi_memstart); + print_lnum("memsize", bd->bi_memsize); + print_num("flashstart", bd->bi_flashstart); + print_num("flashsize", bd->bi_flashsize); + print_num("flashoffset", bd->bi_flashoffset); + print_num("sramstart", bd->bi_sramstart); + print_num("sramsize", bd->bi_sramsize); +#if defined(CONFIG_5xx) || defined(CONFIG_8xx) || \ + defined(CONFIG_MPC8260) || defined(CONFIG_E500) + print_num("immr_base", bd->bi_immr_base); +#endif + print_num("bootflags", bd->bi_bootflags); +#if defined(CONFIG_405EP) || \ + defined(CONFIG_405GP) || \ + defined(CONFIG_440EP) || defined(CONFIG_440EPX) || \ + defined(CONFIG_440GR) || defined(CONFIG_440GRX) || \ + defined(CONFIG_440SP) || defined(CONFIG_440SPE) || \ + defined(CONFIG_XILINX_405) + print_mhz("procfreq", bd->bi_procfreq); + print_mhz("plb_busfreq", bd->bi_plb_busfreq); +#if defined(CONFIG_405EP) || defined(CONFIG_405GP) || \ + defined(CONFIG_440EP) || defined(CONFIG_440EPX) || \ + defined(CONFIG_440GR) || defined(CONFIG_440GRX) || \ + defined(CONFIG_440SPE) || defined(CONFIG_XILINX_405) + print_mhz("pci_busfreq", bd->bi_pci_busfreq); +#endif +#else /* ! CONFIG_405GP, CONFIG_405EP, CONFIG_XILINX_405, CONFIG_440EP CONFIG_440GR */ +#if defined(CONFIG_CPM2) + print_mhz("vco", bd->bi_vco); + print_mhz("sccfreq", bd->bi_sccfreq); + print_mhz("brgfreq", bd->bi_brgfreq); +#endif + print_mhz("intfreq", bd->bi_intfreq); +#if defined(CONFIG_CPM2) + print_mhz("cpmfreq", bd->bi_cpmfreq); +#endif + print_mhz("busfreq", bd->bi_busfreq); +#endif /* CONFIG_405GP, CONFIG_405EP, CONFIG_XILINX_405, CONFIG_440EP CONFIG_440GR */ + +#ifdef CONFIG_ENABLE_36BIT_PHYS +#ifdef CONFIG_PHYS_64BIT + puts("addressing = 36-bit\n"); +#else + puts("addressing = 32-bit\n"); +#endif +#endif + + print_eth(0); +#if defined(CONFIG_HAS_ETH1) + print_eth(1); +#endif +#if defined(CONFIG_HAS_ETH2) + print_eth(2); +#endif +#if defined(CONFIG_HAS_ETH3) + print_eth(3); +#endif +#if defined(CONFIG_HAS_ETH4) + print_eth(4); +#endif +#if defined(CONFIG_HAS_ETH5) + print_eth(5); +#endif + +#ifdef CONFIG_HERMES + print_mhz("ethspeed", bd->bi_ethspeed); +#endif + printf("IP addr = %s\n", getenv("ipaddr")); + printf("baudrate = %6u bps\n", gd->baudrate); + print_num("relocaddr", gd->relocaddr); + board_detail(); + return 0; +} + +#elif defined(CONFIG_NIOS2) + +int do_bdinfo(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + bd_t *bd = gd->bd; + + print_num("mem start", (ulong)bd->bi_memstart); + print_lnum("mem size", (u64)bd->bi_memsize); + print_num("flash start", (ulong)bd->bi_flashstart); + print_num("flash size", (ulong)bd->bi_flashsize); + print_num("flash offset", (ulong)bd->bi_flashoffset); + +#if defined(CONFIG_SYS_SRAM_BASE) + print_num ("sram start", (ulong)bd->bi_sramstart); + print_num ("sram size", (ulong)bd->bi_sramsize); +#endif + +#if defined(CONFIG_CMD_NET) + print_eth(0); + printf("ip_addr = %s\n", getenv("ipaddr")); +#endif + + printf("baudrate = %u bps\n", gd->baudrate); + + return 0; +} + +#elif defined(CONFIG_MICROBLAZE) + +int do_bdinfo(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + bd_t *bd = gd->bd; + print_num("mem start ", (ulong)bd->bi_memstart); + print_lnum("mem size ", (u64)bd->bi_memsize); + print_num("flash start ", (ulong)bd->bi_flashstart); + print_num("flash size ", (ulong)bd->bi_flashsize); + print_num("flash offset ", (ulong)bd->bi_flashoffset); +#if defined(CONFIG_SYS_SRAM_BASE) + print_num("sram start ", (ulong)bd->bi_sramstart); + print_num("sram size ", (ulong)bd->bi_sramsize); +#endif +#if defined(CONFIG_CMD_NET) + print_eths(); +#endif + printf("baudrate = %u bps\n", gd->baudrate); + return 0; +} + +#elif defined(CONFIG_SPARC) + +int do_bdinfo(cmd_tbl_t * cmdtp, int flag, int argc, char * const argv[]) +{ + bd_t *bd = gd->bd; + +#ifdef DEBUG + print_num("bd address ", (ulong) bd); +#endif + print_num("memstart ", bd->bi_memstart); + print_lnum("memsize ", bd->bi_memsize); + print_num("flashstart ", bd->bi_flashstart); + print_num("CONFIG_SYS_MONITOR_BASE ", CONFIG_SYS_MONITOR_BASE); + print_num("CONFIG_ENV_ADDR ", CONFIG_ENV_ADDR); + printf("CONFIG_SYS_RELOC_MONITOR_BASE = 0x%x (%d)\n", CONFIG_SYS_RELOC_MONITOR_BASE, + CONFIG_SYS_MONITOR_LEN); + printf("CONFIG_SYS_MALLOC_BASE = 0x%x (%d)\n", CONFIG_SYS_MALLOC_BASE, + CONFIG_SYS_MALLOC_LEN); + printf("CONFIG_SYS_INIT_SP_OFFSET = 0x%x (%d)\n", CONFIG_SYS_INIT_SP_OFFSET, + CONFIG_SYS_STACK_SIZE); + printf("CONFIG_SYS_PROM_OFFSET = 0x%x (%d)\n", CONFIG_SYS_PROM_OFFSET, + CONFIG_SYS_PROM_SIZE); + printf("CONFIG_SYS_GBL_DATA_OFFSET = 0x%x (%d)\n", CONFIG_SYS_GBL_DATA_OFFSET, + GENERATED_GBL_DATA_SIZE); + +#if defined(CONFIG_CMD_NET) + print_eth(0); + printf("ip_addr = %s\n", getenv("ipaddr")); +#endif + printf("baudrate = %6u bps\n", gd->baudrate); + return 0; +} + +#elif defined(CONFIG_M68K) + +int do_bdinfo(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + bd_t *bd = gd->bd; + + print_num("memstart", (ulong)bd->bi_memstart); + print_lnum("memsize", (u64)bd->bi_memsize); + print_num("flashstart", (ulong)bd->bi_flashstart); + print_num("flashsize", (ulong)bd->bi_flashsize); + print_num("flashoffset", (ulong)bd->bi_flashoffset); +#if defined(CONFIG_SYS_INIT_RAM_ADDR) + print_num("sramstart", (ulong)bd->bi_sramstart); + print_num("sramsize", (ulong)bd->bi_sramsize); +#endif +#if defined(CONFIG_SYS_MBAR) + print_num("mbar", bd->bi_mbar_base); +#endif + print_mhz("cpufreq", bd->bi_intfreq); + print_mhz("busfreq", bd->bi_busfreq); +#ifdef CONFIG_PCI + print_mhz("pcifreq", bd->bi_pcifreq); +#endif +#ifdef CONFIG_EXTRA_CLOCK + print_mhz("flbfreq", bd->bi_flbfreq); + print_mhz("inpfreq", bd->bi_inpfreq); + print_mhz("vcofreq", bd->bi_vcofreq); +#endif +#if defined(CONFIG_CMD_NET) + print_eth(0); +#if defined(CONFIG_HAS_ETH1) + print_eth(1); +#endif +#if defined(CONFIG_HAS_ETH2) + print_eth(2); +#endif +#if defined(CONFIG_HAS_ETH3) + print_eth(3); +#endif + + printf("ip_addr = %s\n", getenv("ipaddr")); +#endif + printf("baudrate = %u bps\n", gd->baudrate); + + return 0; +} + +#elif defined(CONFIG_BLACKFIN) + +int do_bdinfo(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + bd_t *bd = gd->bd; + + printf("U-Boot = %s\n", bd->bi_r_version); + printf("CPU = %s\n", bd->bi_cpu); + printf("Board = %s\n", bd->bi_board_name); + print_mhz("VCO", bd->bi_vco); + print_mhz("CCLK", bd->bi_cclk); + print_mhz("SCLK", bd->bi_sclk); + + print_num("boot_params", (ulong)bd->bi_boot_params); + print_num("memstart", (ulong)bd->bi_memstart); + print_lnum("memsize", (u64)bd->bi_memsize); + print_num("flashstart", (ulong)bd->bi_flashstart); + print_num("flashsize", (ulong)bd->bi_flashsize); + print_num("flashoffset", (ulong)bd->bi_flashoffset); + + print_eth(0); + printf("ip_addr = %s\n", getenv("ipaddr")); + printf("baudrate = %u bps\n", gd->baudrate); + + return 0; +} + +#elif defined(CONFIG_MIPS) + +int do_bdinfo(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + bd_t *bd = gd->bd; + + print_num("boot_params", (ulong)bd->bi_boot_params); + print_num("memstart", (ulong)bd->bi_memstart); + print_lnum("memsize", (u64)bd->bi_memsize); + print_num("flashstart", (ulong)bd->bi_flashstart); + print_num("flashsize", (ulong)bd->bi_flashsize); + print_num("flashoffset", (ulong)bd->bi_flashoffset); + + print_eth(0); + printf("ip_addr = %s\n", getenv("ipaddr")); + printf("baudrate = %u bps\n", gd->baudrate); + + return 0; +} + +#elif defined(CONFIG_AVR32) + +int do_bdinfo(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + bd_t *bd = gd->bd; + + print_num("boot_params", (ulong)bd->bi_boot_params); + print_num("memstart", (ulong)bd->bi_memstart); + print_lnum("memsize", (u64)bd->bi_memsize); + print_num("flashstart", (ulong)bd->bi_flashstart); + print_num("flashsize", (ulong)bd->bi_flashsize); + print_num("flashoffset", (ulong)bd->bi_flashoffset); + + print_eth(0); + printf("ip_addr = %s\n", getenv("ipaddr")); + printf("baudrate = %u bps\n", gd->baudrate); + + return 0; +} + +#elif defined(CONFIG_ARM) + +int do_bdinfo(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + int i; + bd_t *bd = gd->bd; + + print_num("arch_number", bd->bi_arch_number); + print_num("boot_params", (ulong)bd->bi_boot_params); + + for (i = 0; i < CONFIG_NR_DRAM_BANKS; ++i) { + print_num("DRAM bank", i); + print_num("-> start", bd->bi_dram[i].start); + print_num("-> size", bd->bi_dram[i].size); + } + +#if defined(CONFIG_CMD_NET) + print_eths(); +#endif + printf("baudrate = %u bps\n", gd->baudrate); +#if !(defined(CONFIG_SYS_ICACHE_OFF) && defined(CONFIG_SYS_DCACHE_OFF)) + print_num("TLB addr", gd->arch.tlb_addr); +#endif + print_num("relocaddr", gd->relocaddr); + print_num("reloc off", gd->reloc_off); + print_num("irq_sp", gd->irq_sp); /* irq stack pointer */ + print_num("sp start ", gd->start_addr_sp); +#if defined(CONFIG_LCD) || defined(CONFIG_VIDEO) + print_num("FB base ", gd->fb_base); +#endif + /* + * TODO: Currently only support for davinci SOC's is added. + * Remove this check once all the board implement this. + */ +#ifdef CONFIG_CLOCKS + printf("ARM frequency = %ld MHz\n", gd->bd->bi_arm_freq); + printf("DSP frequency = %ld MHz\n", gd->bd->bi_dsp_freq); + printf("DDR frequency = %ld MHz\n", gd->bd->bi_ddr_freq); +#endif + return 0; +} + +#elif defined(CONFIG_SH) + +int do_bdinfo(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + bd_t *bd = gd->bd; + print_num("mem start ", (ulong)bd->bi_memstart); + print_lnum("mem size ", (u64)bd->bi_memsize); + print_num("flash start ", (ulong)bd->bi_flashstart); + print_num("flash size ", (ulong)bd->bi_flashsize); + print_num("flash offset ", (ulong)bd->bi_flashoffset); + +#if defined(CONFIG_CMD_NET) + print_eth(0); + printf("ip_addr = %s\n", getenv("ipaddr")); +#endif + printf("baudrate = %u bps\n", gd->baudrate); + return 0; +} + +#elif defined(CONFIG_X86) + +int do_bdinfo(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + int i; + bd_t *bd = gd->bd; + + print_num("boot_params", (ulong)bd->bi_boot_params); + print_num("bi_memstart", bd->bi_memstart); + print_num("bi_memsize", bd->bi_memsize); + print_num("bi_flashstart", bd->bi_flashstart); + print_num("bi_flashsize", bd->bi_flashsize); + print_num("bi_flashoffset", bd->bi_flashoffset); + print_num("bi_sramstart", bd->bi_sramstart); + print_num("bi_sramsize", bd->bi_sramsize); + print_num("bi_bootflags", bd->bi_bootflags); + print_mhz("cpufreq", bd->bi_intfreq); + print_mhz("busfreq", bd->bi_busfreq); + + for (i = 0; i < CONFIG_NR_DRAM_BANKS; ++i) { + print_num("DRAM bank", i); + print_num("-> start", bd->bi_dram[i].start); + print_num("-> size", bd->bi_dram[i].size); + } + +#if defined(CONFIG_CMD_NET) + print_eth(0); + printf("ip_addr = %s\n", getenv("ipaddr")); + print_mhz("ethspeed", bd->bi_ethspeed); +#endif + printf("baudrate = %u bps\n", gd->baudrate); + + return 0; +} + +#elif defined(CONFIG_SANDBOX) + +int do_bdinfo(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + int i; + bd_t *bd = gd->bd; + + print_num("boot_params", (ulong)bd->bi_boot_params); + + for (i = 0; i < CONFIG_NR_DRAM_BANKS; ++i) { + print_num("DRAM bank", i); + print_num("-> start", bd->bi_dram[i].start); + print_num("-> size", bd->bi_dram[i].size); + } + +#if defined(CONFIG_CMD_NET) + print_eth(0); + printf("ip_addr = %s\n", getenv("ipaddr")); +#endif +#if defined(CONFIG_LCD) || defined(CONFIG_VIDEO) + print_num("FB base ", gd->fb_base); +#endif + return 0; +} + +#elif defined(CONFIG_NDS32) + +int do_bdinfo(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + int i; + bd_t *bd = gd->bd; + + print_num("arch_number", bd->bi_arch_number); + print_num("boot_params", (ulong)bd->bi_boot_params); + + for (i = 0; i < CONFIG_NR_DRAM_BANKS; ++i) { + print_num("DRAM bank", i); + print_num("-> start", bd->bi_dram[i].start); + print_num("-> size", bd->bi_dram[i].size); + } + +#if defined(CONFIG_CMD_NET) + print_eth(0); + printf("ip_addr = %s\n", getenv("ipaddr")); +#endif + printf("baudrate = %u bps\n", gd->baudrate); + + return 0; +} + +#elif defined(CONFIG_OPENRISC) + +int do_bdinfo(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + bd_t *bd = gd->bd; + + print_num("mem start", (ulong)bd->bi_memstart); + print_lnum("mem size", (u64)bd->bi_memsize); + print_num("flash start", (ulong)bd->bi_flashstart); + print_num("flash size", (ulong)bd->bi_flashsize); + print_num("flash offset", (ulong)bd->bi_flashoffset); + +#if defined(CONFIG_CMD_NET) + print_eth(0); + printf("ip_addr = %s\n", getenv("ipaddr")); +#endif + + printf("baudrate = %u bps\n", gd->baudrate); + + return 0; +} + +#elif defined(CONFIG_ARC700) + +int do_bdinfo(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + bd_t *bd = gd->bd; + + print_num("mem start", bd->bi_memstart); + print_lnum("mem size", bd->bi_memsize); + +#if defined(CONFIG_CMD_NET) + print_eth(0); + printf("ip_addr = %s\n", getenv("ipaddr")); +#endif + printf("baudrate = %d bps\n", gd->baudrate); + + return 0; +} + +#else + #error "a case for this architecture does not exist!" +#endif + +/* -------------------------------------------------------------------- */ + +U_BOOT_CMD( + bdinfo, 1, 1, do_bdinfo, + "print Board Info structure", + "" +); diff --git a/qemu/roms/u-boot/common/cmd_bedbug.c b/qemu/roms/u-boot/common/cmd_bedbug.c new file mode 100644 index 000000000..77b6e3e88 --- /dev/null +++ b/qemu/roms/u-boot/common/cmd_bedbug.c @@ -0,0 +1,420 @@ +/* + * BedBug Functions + */ + +#include <common.h> +#include <command.h> +#include <linux/ctype.h> +#include <net.h> +#include <bedbug/type.h> +#include <bedbug/bedbug.h> +#include <bedbug/regs.h> +#include <bedbug/ppc.h> + +DECLARE_GLOBAL_DATA_PTR; + +extern void show_regs __P ((struct pt_regs *)); +extern int run_command __P ((const char *, int)); + +ulong dis_last_addr = 0; /* Last address disassembled */ +ulong dis_last_len = 20; /* Default disassembler length */ +CPU_DEBUG_CTX bug_ctx; /* Bedbug context structure */ + + +/* ====================================================================== + * U-Boot's puts function does not append a newline, so the bedbug stuff + * will use this for the output of the dis/assembler. + * ====================================================================== */ + +int bedbug_puts (const char *str) +{ + /* -------------------------------------------------- */ + + printf ("%s\r\n", str); + return 0; +} /* bedbug_puts */ + + + +/* ====================================================================== + * Initialize the bug_ctx structure used by the bedbug debugger. This is + * specific to the CPU since each has different debug registers and + * settings. + * ====================================================================== */ + +void bedbug_init (void) +{ + /* -------------------------------------------------- */ + +#if defined(CONFIG_4xx) + void bedbug405_init (void); + + bedbug405_init (); +#elif defined(CONFIG_8xx) + void bedbug860_init (void); + + bedbug860_init (); +#endif + +#if defined(CONFIG_MPC824X) || defined(CONFIG_MPC8260) + /* Processors that are 603e core based */ + void bedbug603e_init (void); + + bedbug603e_init (); +#endif + + return; +} /* bedbug_init */ + + + +/* ====================================================================== + * Entry point from the interpreter to the disassembler. Repeated calls + * will resume from the last disassembled address. + * ====================================================================== */ +int do_bedbug_dis (cmd_tbl_t * cmdtp, int flag, int argc, char * const argv[]) +{ + ulong addr; /* Address to start disassembly from */ + ulong len; /* # of instructions to disassemble */ + + /* -------------------------------------------------- */ + + /* Setup to go from the last address if none is given */ + addr = dis_last_addr; + len = dis_last_len; + + if (argc < 2) + return CMD_RET_USAGE; + + if ((flag & CMD_FLAG_REPEAT) == 0) { + /* New command */ + addr = simple_strtoul (argv[1], NULL, 16); + + /* If an extra param is given then it is the length */ + if (argc > 2) + len = simple_strtoul (argv[2], NULL, 16); + } + + /* Run the disassembler */ + disppc ((unsigned char *) addr, 0, len, bedbug_puts, F_RADHEX); + + dis_last_addr = addr + (len * 4); + dis_last_len = len; + return 0; +} /* do_bedbug_dis */ + +U_BOOT_CMD (ds, 3, 1, do_bedbug_dis, + "disassemble memory", + "ds <address> [# instructions]"); + +/* ====================================================================== + * Entry point from the interpreter to the assembler. Assembles + * instructions in consecutive memory locations until a '.' (period) is + * entered on a line by itself. + * ====================================================================== */ +int do_bedbug_asm (cmd_tbl_t * cmdtp, int flag, int argc, char * const argv[]) +{ + long mem_addr; /* Address to assemble into */ + unsigned long instr; /* Machine code for text */ + char prompt[15]; /* Prompt string for user input */ + int asm_err; /* Error code from the assembler */ + + /* -------------------------------------------------- */ + int rcode = 0; + + if (argc < 2) + return CMD_RET_USAGE; + + printf ("\nEnter '.' when done\n"); + mem_addr = simple_strtoul (argv[1], NULL, 16); + + while (1) { + putc ('\n'); + disppc ((unsigned char *) mem_addr, 0, 1, bedbug_puts, + F_RADHEX); + + sprintf (prompt, "%08lx: ", mem_addr); + readline (prompt); + + if (console_buffer[0] && strcmp (console_buffer, ".")) { + if ((instr = + asmppc (mem_addr, console_buffer, + &asm_err)) != 0) { + *(unsigned long *) mem_addr = instr; + mem_addr += 4; + } else { + printf ("*** Error: %s ***\n", + asm_error_str (asm_err)); + rcode = 1; + } + } else { + break; + } + } + return rcode; +} /* do_bedbug_asm */ + +U_BOOT_CMD (as, 2, 0, do_bedbug_asm, + "assemble memory", "as <address>"); + +/* ====================================================================== + * Used to set a break point from the interpreter. Simply calls into the + * CPU-specific break point set routine. + * ====================================================================== */ + +int do_bedbug_break (cmd_tbl_t * cmdtp, int flag, int argc, char * const argv[]) +{ + /* -------------------------------------------------- */ + if (bug_ctx.do_break) + (*bug_ctx.do_break) (cmdtp, flag, argc, argv); + return 0; + +} /* do_bedbug_break */ + +U_BOOT_CMD (break, 3, 0, do_bedbug_break, + "set or clear a breakpoint", + " - Set or clear a breakpoint\n" + "break <address> - Break at an address\n" + "break off <bp#> - Disable breakpoint.\n" + "break show - List breakpoints."); + +/* ====================================================================== + * Called from the debug interrupt routine. Simply calls the CPU-specific + * breakpoint handling routine. + * ====================================================================== */ + +void do_bedbug_breakpoint (struct pt_regs *regs) +{ + /* -------------------------------------------------- */ + + if (bug_ctx.break_isr) + (*bug_ctx.break_isr) (regs); + + return; +} /* do_bedbug_breakpoint */ + + + +/* ====================================================================== + * Called from the CPU-specific breakpoint handling routine. Enter a + * mini main loop until the stopped flag is cleared from the breakpoint + * context. + * + * This handles the parts of the debugger that are common to all CPU's. + * ====================================================================== */ + +void bedbug_main_loop (unsigned long addr, struct pt_regs *regs) +{ + int len; /* Length of command line */ + int flag; /* Command flags */ + int rc = 0; /* Result from run_command */ + char prompt_str[20]; /* Prompt string */ + static char lastcommand[CONFIG_SYS_CBSIZE] = { 0 }; /* previous command */ + /* -------------------------------------------------- */ + + if (bug_ctx.clear) + (*bug_ctx.clear) (bug_ctx.current_bp); + + printf ("Breakpoint %d: ", bug_ctx.current_bp); + disppc ((unsigned char *) addr, 0, 1, bedbug_puts, F_RADHEX); + + bug_ctx.stopped = 1; + bug_ctx.regs = regs; + + sprintf (prompt_str, "BEDBUG.%d =>", bug_ctx.current_bp); + + /* A miniature main loop */ + while (bug_ctx.stopped) { + len = readline (prompt_str); + + flag = 0; /* assume no special flags for now */ + + if (len > 0) + strcpy (lastcommand, console_buffer); + else if (len == 0) + flag |= CMD_FLAG_REPEAT; + + if (len == -1) + printf ("<INTERRUPT>\n"); + else + rc = run_command(lastcommand, flag); + + if (rc <= 0) { + /* invalid command or not repeatable, forget it */ + lastcommand[0] = 0; + } + } + + bug_ctx.regs = NULL; + bug_ctx.current_bp = 0; + + return; +} /* bedbug_main_loop */ + + + +/* ====================================================================== + * Interpreter command to continue from a breakpoint. Just clears the + * stopped flag in the context so that the breakpoint routine will + * return. + * ====================================================================== */ +int do_bedbug_continue (cmd_tbl_t * cmdtp, int flag, int argc, char * const argv[]) +{ + /* -------------------------------------------------- */ + + if (!bug_ctx.stopped) { + printf ("Not at a breakpoint\n"); + return 1; + } + + bug_ctx.stopped = 0; + return 0; +} /* do_bedbug_continue */ + +U_BOOT_CMD (continue, 1, 0, do_bedbug_continue, + "continue from a breakpoint", + ""); + +/* ====================================================================== + * Interpreter command to continue to the next instruction, stepping into + * subroutines. Works by calling the find_next_addr() routine to compute + * the address passes control to the CPU-specific set breakpoint routine + * for the current breakpoint number. + * ====================================================================== */ +int do_bedbug_step (cmd_tbl_t * cmdtp, int flag, int argc, char * const argv[]) +{ + unsigned long addr; /* Address to stop at */ + + /* -------------------------------------------------- */ + + if (!bug_ctx.stopped) { + printf ("Not at a breakpoint\n"); + return 1; + } + + if (!find_next_address((unsigned char *) &addr, false, bug_ctx.regs)) + return 1; + + if (bug_ctx.set) + (*bug_ctx.set) (bug_ctx.current_bp, addr); + + bug_ctx.stopped = 0; + return 0; +} /* do_bedbug_step */ + +U_BOOT_CMD (step, 1, 1, do_bedbug_step, + "single step execution.", + ""); + +/* ====================================================================== + * Interpreter command to continue to the next instruction, stepping over + * subroutines. Works by calling the find_next_addr() routine to compute + * the address passes control to the CPU-specific set breakpoint routine + * for the current breakpoint number. + * ====================================================================== */ +int do_bedbug_next (cmd_tbl_t * cmdtp, int flag, int argc, char * const argv[]) +{ + unsigned long addr; /* Address to stop at */ + + /* -------------------------------------------------- */ + + if (!bug_ctx.stopped) { + printf ("Not at a breakpoint\n"); + return 1; + } + + if (!find_next_address((unsigned char *) &addr, true, bug_ctx.regs)) + return 1; + + if (bug_ctx.set) + (*bug_ctx.set) (bug_ctx.current_bp, addr); + + bug_ctx.stopped = 0; + return 0; +} /* do_bedbug_next */ + +U_BOOT_CMD (next, 1, 1, do_bedbug_next, + "single step execution, stepping over subroutines.", + ""); + +/* ====================================================================== + * Interpreter command to print the current stack. This assumes an EABI + * architecture, so it starts with GPR R1 and works back up the stack. + * ====================================================================== */ +int do_bedbug_stack (cmd_tbl_t * cmdtp, int flag, int argc, char * const argv[]) +{ + unsigned long sp; /* Stack pointer */ + unsigned long func; /* LR from stack */ + int depth; /* Stack iteration level */ + int skip = 1; /* Flag to skip the first entry */ + unsigned long top; /* Top of memory address */ + + /* -------------------------------------------------- */ + + if (!bug_ctx.stopped) { + printf ("Not at a breakpoint\n"); + return 1; + } + + top = gd->bd->bi_memstart + gd->bd->bi_memsize; + depth = 0; + + printf ("Depth PC\n"); + printf ("----- --------\n"); + printf ("%5d %08lx\n", depth++, bug_ctx.regs->nip); + + sp = bug_ctx.regs->gpr[1]; + func = *(unsigned long *) (sp + 4); + + while ((func < top) && (sp < top)) { + if (!skip) + printf ("%5d %08lx\n", depth++, func); + else + --skip; + + sp = *(unsigned long *) sp; + func = *(unsigned long *) (sp + 4); + } + return 0; +} /* do_bedbug_stack */ + +U_BOOT_CMD (where, 1, 1, do_bedbug_stack, + "Print the running stack.", + ""); + +/* ====================================================================== + * Interpreter command to dump the registers. Calls the CPU-specific + * show registers routine. + * ====================================================================== */ +int do_bedbug_rdump (cmd_tbl_t * cmdtp, int flag, int argc, char * const argv[]) +{ + /* -------------------------------------------------- */ + + if (!bug_ctx.stopped) { + printf ("Not at a breakpoint\n"); + return 1; + } + + show_regs (bug_ctx.regs); + return 0; +} /* do_bedbug_rdump */ + +U_BOOT_CMD (rdump, 1, 1, do_bedbug_rdump, + "Show registers.", ""); +/* ====================================================================== */ + + +/* + * Copyright (c) 2001 William L. Pitts + * All rights reserved. + * + * Redistribution and use in source and binary forms are freely + * permitted provided that the above copyright notice and this + * paragraph and the following disclaimer are duplicated in all + * such forms. + * + * This software is provided "AS IS" and without any express or + * implied warranties, including, without limitation, the implied + * warranties of merchantability and fitness for a particular + * purpose. + */ diff --git a/qemu/roms/u-boot/common/cmd_bmp.c b/qemu/roms/u-boot/common/cmd_bmp.c new file mode 100644 index 000000000..cc904c253 --- /dev/null +++ b/qemu/roms/u-boot/common/cmd_bmp.c @@ -0,0 +1,252 @@ +/* + * (C) Copyright 2002 + * Detlev Zundel, DENX Software Engineering, dzu@denx.de. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/* + * BMP handling routines + */ + +#include <common.h> +#include <lcd.h> +#include <bmp_layout.h> +#include <command.h> +#include <asm/byteorder.h> +#include <malloc.h> +#include <splash.h> +#include <video.h> + +static int bmp_info (ulong addr); + +/* + * Allocate and decompress a BMP image using gunzip(). + * + * Returns a pointer to the decompressed image data. This pointer is + * aligned to 32-bit-aligned-address + 2. + * See doc/README.displaying-bmps for explanation. + * + * The allocation address is passed to 'alloc_addr' and must be freed + * by the caller after use. + * + * Returns NULL if decompression failed, or if the decompressed data + * didn't contain a valid BMP signature. + */ +#ifdef CONFIG_VIDEO_BMP_GZIP +bmp_image_t *gunzip_bmp(unsigned long addr, unsigned long *lenp, + void **alloc_addr) +{ + void *dst; + unsigned long len; + bmp_image_t *bmp; + + /* + * Decompress bmp image + */ + len = CONFIG_SYS_VIDEO_LOGO_MAX_SIZE; + /* allocate extra 3 bytes for 32-bit-aligned-address + 2 alignment */ + dst = malloc(CONFIG_SYS_VIDEO_LOGO_MAX_SIZE + 3); + if (dst == NULL) { + puts("Error: malloc in gunzip failed!\n"); + return NULL; + } + + bmp = dst; + + /* align to 32-bit-aligned-address + 2 */ + bmp = (bmp_image_t *)((((unsigned int)dst + 1) & ~3) + 2); + + if (gunzip(bmp, CONFIG_SYS_VIDEO_LOGO_MAX_SIZE, (uchar *)addr, &len) != 0) { + free(dst); + return NULL; + } + if (len == CONFIG_SYS_VIDEO_LOGO_MAX_SIZE) + puts("Image could be truncated" + " (increase CONFIG_SYS_VIDEO_LOGO_MAX_SIZE)!\n"); + + /* + * Check for bmp mark 'BM' + */ + if (!((bmp->header.signature[0] == 'B') && + (bmp->header.signature[1] == 'M'))) { + free(dst); + return NULL; + } + + debug("Gzipped BMP image detected!\n"); + + *alloc_addr = dst; + return bmp; +} +#else +bmp_image_t *gunzip_bmp(unsigned long addr, unsigned long *lenp, + void **alloc_addr) +{ + return NULL; +} +#endif + +static int do_bmp_info(cmd_tbl_t * cmdtp, int flag, int argc, char * const argv[]) +{ + ulong addr; + + switch (argc) { + case 1: /* use load_addr as default address */ + addr = load_addr; + break; + case 2: /* use argument */ + addr = simple_strtoul(argv[1], NULL, 16); + break; + default: + return CMD_RET_USAGE; + } + + return (bmp_info(addr)); +} + +static int do_bmp_display(cmd_tbl_t * cmdtp, int flag, int argc, char * const argv[]) +{ + ulong addr; + int x = 0, y = 0; + + splash_get_pos(&x, &y); + + switch (argc) { + case 1: /* use load_addr as default address */ + addr = load_addr; + break; + case 2: /* use argument */ + addr = simple_strtoul(argv[1], NULL, 16); + break; + case 4: + addr = simple_strtoul(argv[1], NULL, 16); + x = simple_strtoul(argv[2], NULL, 10); + y = simple_strtoul(argv[3], NULL, 10); + break; + default: + return CMD_RET_USAGE; + } + + return (bmp_display(addr, x, y)); +} + +static cmd_tbl_t cmd_bmp_sub[] = { + U_BOOT_CMD_MKENT(info, 3, 0, do_bmp_info, "", ""), + U_BOOT_CMD_MKENT(display, 5, 0, do_bmp_display, "", ""), +}; + +#ifdef CONFIG_NEEDS_MANUAL_RELOC +void bmp_reloc(void) { + fixup_cmdtable(cmd_bmp_sub, ARRAY_SIZE(cmd_bmp_sub)); +} +#endif + +/* + * Subroutine: do_bmp + * + * Description: Handler for 'bmp' command.. + * + * Inputs: argv[1] contains the subcommand + * + * Return: None + * + */ +static int do_bmp(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + cmd_tbl_t *c; + + /* Strip off leading 'bmp' command argument */ + argc--; + argv++; + + c = find_cmd_tbl(argv[0], &cmd_bmp_sub[0], ARRAY_SIZE(cmd_bmp_sub)); + + if (c) + return c->cmd(cmdtp, flag, argc, argv); + else + return CMD_RET_USAGE; +} + +U_BOOT_CMD( + bmp, 5, 1, do_bmp, + "manipulate BMP image data", + "info <imageAddr> - display image info\n" + "bmp display <imageAddr> [x y] - display image at x,y" +); + +/* + * Subroutine: bmp_info + * + * Description: Show information about bmp file in memory + * + * Inputs: addr address of the bmp file + * + * Return: None + * + */ +static int bmp_info(ulong addr) +{ + bmp_image_t *bmp=(bmp_image_t *)addr; + void *bmp_alloc_addr = NULL; + unsigned long len; + + if (!((bmp->header.signature[0]=='B') && + (bmp->header.signature[1]=='M'))) + bmp = gunzip_bmp(addr, &len, &bmp_alloc_addr); + + if (bmp == NULL) { + printf("There is no valid bmp file at the given address\n"); + return 1; + } + + printf("Image size : %d x %d\n", le32_to_cpu(bmp->header.width), + le32_to_cpu(bmp->header.height)); + printf("Bits per pixel: %d\n", le16_to_cpu(bmp->header.bit_count)); + printf("Compression : %d\n", le32_to_cpu(bmp->header.compression)); + + if (bmp_alloc_addr) + free(bmp_alloc_addr); + + return(0); +} + +/* + * Subroutine: bmp_display + * + * Description: Display bmp file located in memory + * + * Inputs: addr address of the bmp file + * + * Return: None + * + */ +int bmp_display(ulong addr, int x, int y) +{ + int ret; + bmp_image_t *bmp = (bmp_image_t *)addr; + void *bmp_alloc_addr = NULL; + unsigned long len; + + if (!((bmp->header.signature[0]=='B') && + (bmp->header.signature[1]=='M'))) + bmp = gunzip_bmp(addr, &len, &bmp_alloc_addr); + + if (!bmp) { + printf("There is no valid bmp file at the given address\n"); + return 1; + } + +#if defined(CONFIG_LCD) + ret = lcd_display_bitmap((ulong)bmp, x, y); +#elif defined(CONFIG_VIDEO) + ret = video_display_bitmap((unsigned long)bmp, x, y); +#else +# error bmp_display() requires CONFIG_LCD or CONFIG_VIDEO +#endif + + if (bmp_alloc_addr) + free(bmp_alloc_addr); + + return ret; +} diff --git a/qemu/roms/u-boot/common/cmd_boot.c b/qemu/roms/u-boot/common/cmd_boot.c new file mode 100644 index 000000000..8f2e0701b --- /dev/null +++ b/qemu/roms/u-boot/common/cmd_boot.c @@ -0,0 +1,63 @@ +/* + * (C) Copyright 2000-2003 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/* + * Misc boot support + */ +#include <common.h> +#include <command.h> +#include <net.h> + +#ifdef CONFIG_CMD_GO + +/* Allow ports to override the default behavior */ +__attribute__((weak)) +unsigned long do_go_exec(ulong (*entry)(int, char * const []), int argc, + char * const argv[]) +{ + return entry (argc, argv); +} + +static int do_go(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + ulong addr, rc; + int rcode = 0; + + if (argc < 2) + return CMD_RET_USAGE; + + addr = simple_strtoul(argv[1], NULL, 16); + + printf ("## Starting application at 0x%08lX ...\n", addr); + + /* + * pass address parameter as argv[0] (aka command name), + * and all remaining args + */ + rc = do_go_exec ((void *)addr, argc - 1, argv + 1); + if (rc != 0) rcode = 1; + + printf ("## Application terminated, rc = 0x%lX\n", rc); + return rcode; +} + +/* -------------------------------------------------------------------- */ + +U_BOOT_CMD( + go, CONFIG_SYS_MAXARGS, 1, do_go, + "start application at address 'addr'", + "addr [arg ...]\n - start application at address 'addr'\n" + " passing 'arg' as arguments" +); + +#endif + +U_BOOT_CMD( + reset, 1, 0, do_reset, + "Perform RESET of the CPU", + "" +); diff --git a/qemu/roms/u-boot/common/cmd_bootldr.c b/qemu/roms/u-boot/common/cmd_bootldr.c new file mode 100644 index 000000000..bc5c1f95e --- /dev/null +++ b/qemu/roms/u-boot/common/cmd_bootldr.c @@ -0,0 +1,170 @@ +/* + * U-boot - bootldr.c + * + * Copyright (c) 2005-2008 Analog Devices Inc. + * + * See file CREDITS for list of people who contributed to this + * project. + * + * Licensed under the GPL-2 or later. + */ + +#include <config.h> +#include <common.h> +#include <command.h> + +#include <asm/blackfin.h> +#include <asm/mach-common/bits/bootrom.h> + +/* Simple sanity check on the specified address to make sure it contains + * an LDR image of some sort. + */ +static bool ldr_valid_signature(uint8_t *data) +{ +#if defined(__ADSPBF561__) + + /* BF56x has a 4 byte global header */ + if (data[3] == (GFLAG_56X_SIGN_MAGIC << (GFLAG_56X_SIGN_SHIFT - 24))) + return true; + +#elif defined(__ADSPBF531__) || defined(__ADSPBF532__) || defined(__ADSPBF533__) || \ + defined(__ADSPBF534__) || defined(__ADSPBF536__) || defined(__ADSPBF537__) || \ + defined(__ADSPBF538__) || defined(__ADSPBF539__) + + /* all the BF53x should start at this address mask */ + uint32_t addr; + memmove(&addr, data, sizeof(addr)); + if ((addr & 0xFF0FFF0F) == 0xFF000000) + return true; +#else + + /* everything newer has a magic byte */ + uint32_t count; + memmove(&count, data + 8, sizeof(count)); + if (data[3] == 0xAD && count == 0) + return true; + +#endif + + return false; +} + +/* If the Blackfin is new enough, the Blackfin on-chip ROM supports loading + * LDRs from random memory addresses. So whenever possible, use that. In + * the older cases (BF53x/BF561), parse the LDR format ourselves. + */ +static void ldr_load(uint8_t *base_addr) +{ +#if defined(__ADSPBF531__) || defined(__ADSPBF532__) || defined(__ADSPBF533__) || \ + /*defined(__ADSPBF534__) || defined(__ADSPBF536__) || defined(__ADSPBF537__) ||*/\ + defined(__ADSPBF538__) || defined(__ADSPBF539__) || defined(__ADSPBF561__) + + uint32_t addr; + uint32_t count; + uint16_t flags; + + /* the bf56x has a 4 byte global header ... but it is useless to + * us when booting an LDR from a memory address, so skip it + */ +# ifdef __ADSPBF561__ + base_addr += 4; +# endif + + memmove(&flags, base_addr + 8, sizeof(flags)); + bfin_write_EVT1(flags & BFLAG_53X_RESVECT ? 0xFFA00000 : 0xFFA08000); + + do { + /* block header may not be aligned */ + memmove(&addr, base_addr, sizeof(addr)); + memmove(&count, base_addr+4, sizeof(count)); + memmove(&flags, base_addr+8, sizeof(flags)); + base_addr += sizeof(addr) + sizeof(count) + sizeof(flags); + + printf("loading to 0x%08x (%#x bytes) flags: 0x%04x\n", + addr, count, flags); + + if (!(flags & BFLAG_53X_IGNORE)) { + if (flags & BFLAG_53X_ZEROFILL) + memset((void *)addr, 0x00, count); + else + memcpy((void *)addr, base_addr, count); + + if (flags & BFLAG_53X_INIT) { + void (*init)(void) = (void *)addr; + init(); + } + } + + if (!(flags & BFLAG_53X_ZEROFILL)) + base_addr += count; + } while (!(flags & BFLAG_53X_FINAL)); + +#endif +} + +/* For BF537, we use the _BOOTROM_BOOT_DXE_FLASH funky ROM function. + * For all other BF53x/BF56x, we just call the entry point. + * For everything else (newer), we use _BOOTROM_MEMBOOT ROM function. + */ +static void ldr_exec(void *addr) +{ +#if defined(__ADSPBF534__) || defined(__ADSPBF536__) || defined(__ADSPBF537__) + + /* restore EVT1 to reset value as this is what the bootrom uses as + * the default entry point when booting the final block of LDRs + */ + bfin_write_EVT1(L1_INST_SRAM); + __asm__("call (%0);" : : "a"(_BOOTROM_MEMBOOT), "q7"(addr) : "RETS", "memory"); + +#elif defined(__ADSPBF531__) || defined(__ADSPBF532__) || defined(__ADSPBF533__) || \ + defined(__ADSPBF538__) || defined(__ADSPBF539__) || defined(__ADSPBF561__) + + void (*ldr_entry)(void) = (void *)bfin_read_EVT1(); + ldr_entry(); + +#else + + int32_t (*BOOTROM_MEM)(void *, int32_t, int32_t, void *) = (void *)_BOOTROM_MEMBOOT; + BOOTROM_MEM(addr, 0, 0, NULL); + +#endif +} + +/* + * the bootldr command loads an address, checks to see if there + * is a Boot stream that the on-chip BOOTROM can understand, + * and loads it via the BOOTROM Callback. It is possible + * to also add booting from SPI, or TWI, but this function does + * not currently support that. + */ +int do_bootldr(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + void *addr; + + /* Get the address */ + if (argc < 2) + addr = (void *)load_addr; + else + addr = (void *)simple_strtoul(argv[1], NULL, 16); + + /* Check if it is a LDR file */ + if (ldr_valid_signature(addr)) { + printf("## Booting ldr image at 0x%p ...\n", addr); + ldr_load(addr); + + icache_disable(); + dcache_disable(); + + ldr_exec(addr); + } else + printf("## No ldr image at address 0x%p\n", addr); + + return 0; +} + +U_BOOT_CMD( + bootldr, 2, 0, do_bootldr, + "boot ldr image from memory", + "[addr]\n" + "" +); diff --git a/qemu/roms/u-boot/common/cmd_bootm.c b/qemu/roms/u-boot/common/cmd_bootm.c new file mode 100644 index 000000000..e683af369 --- /dev/null +++ b/qemu/roms/u-boot/common/cmd_bootm.c @@ -0,0 +1,1931 @@ +/* + * (C) Copyright 2000-2009 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + + +/* + * Boot support + */ +#include <common.h> +#include <watchdog.h> +#include <command.h> +#include <image.h> +#include <malloc.h> +#include <u-boot/zlib.h> +#include <bzlib.h> +#include <environment.h> +#include <lmb.h> +#include <linux/ctype.h> +#include <asm/byteorder.h> +#include <asm/io.h> +#include <linux/compiler.h> + +#if defined(CONFIG_BOOTM_VXWORKS) && \ + (defined(CONFIG_PPC) || defined(CONFIG_ARM)) +#include <vxworks.h> +#endif + +#if defined(CONFIG_CMD_USB) +#include <usb.h> +#endif + +#ifdef CONFIG_SYS_HUSH_PARSER +#include <hush.h> +#endif + +#if defined(CONFIG_OF_LIBFDT) +#include <libfdt.h> +#include <fdt_support.h> +#endif + +#ifdef CONFIG_LZMA +#include <lzma/LzmaTypes.h> +#include <lzma/LzmaDec.h> +#include <lzma/LzmaTools.h> +#endif /* CONFIG_LZMA */ + +#ifdef CONFIG_LZO +#include <linux/lzo.h> +#endif /* CONFIG_LZO */ + +DECLARE_GLOBAL_DATA_PTR; + +#ifndef CONFIG_SYS_BOOTM_LEN +#define CONFIG_SYS_BOOTM_LEN 0x800000 /* use 8MByte as default max gunzip size */ +#endif + +#ifdef CONFIG_BZIP2 +extern void bz_internal_error(int); +#endif + +#if defined(CONFIG_CMD_IMI) +static int image_info(unsigned long addr); +#endif + +#if defined(CONFIG_CMD_IMLS) +#include <flash.h> +#include <mtd/cfi_flash.h> +extern flash_info_t flash_info[]; /* info for FLASH chips */ +#endif + +#if defined(CONFIG_CMD_IMLS) || defined(CONFIG_CMD_IMLS_NAND) +static int do_imls(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]); +#endif + +#include <linux/err.h> +#include <nand.h> + +#if defined(CONFIG_SILENT_CONSOLE) && !defined(CONFIG_SILENT_U_BOOT_ONLY) +static void fixup_silent_linux(void); +#endif + +static int do_bootm_standalone(int flag, int argc, char * const argv[], + bootm_headers_t *images); + +static const void *boot_get_kernel(cmd_tbl_t *cmdtp, int flag, int argc, + char * const argv[], bootm_headers_t *images, + ulong *os_data, ulong *os_len); + +/* + * Continue booting an OS image; caller already has: + * - copied image header to global variable `header' + * - checked header magic number, checksums (both header & image), + * - verified image architecture (PPC) and type (KERNEL or MULTI), + * - loaded (first part of) image to header load address, + * - disabled interrupts. + * + * @flag: Flags indicating what to do (BOOTM_STATE_...) + * @argc: Number of arguments. Note that the arguments are shifted down + * so that 0 is the first argument not processed by U-Boot, and + * argc is adjusted accordingly. This avoids confusion as to how + * many arguments are available for the OS. + * @images: Pointers to os/initrd/fdt + * @return 1 on error. On success the OS boots so this function does + * not return. + */ +typedef int boot_os_fn(int flag, int argc, char * const argv[], + bootm_headers_t *images); + +#ifdef CONFIG_BOOTM_LINUX +extern boot_os_fn do_bootm_linux; +#endif +#ifdef CONFIG_BOOTM_NETBSD +static boot_os_fn do_bootm_netbsd; +#endif +#if defined(CONFIG_LYNXKDI) +static boot_os_fn do_bootm_lynxkdi; +extern void lynxkdi_boot(image_header_t *); +#endif +#ifdef CONFIG_BOOTM_RTEMS +static boot_os_fn do_bootm_rtems; +#endif +#if defined(CONFIG_BOOTM_OSE) +static boot_os_fn do_bootm_ose; +#endif +#if defined(CONFIG_BOOTM_PLAN9) +static boot_os_fn do_bootm_plan9; +#endif +#if defined(CONFIG_BOOTM_VXWORKS) && \ + (defined(CONFIG_PPC) || defined(CONFIG_ARM)) +static boot_os_fn do_bootm_vxworks; +#endif +#if defined(CONFIG_CMD_ELF) +static boot_os_fn do_bootm_qnxelf; +int do_bootvx(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]); +int do_bootelf(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]); +#endif +#if defined(CONFIG_INTEGRITY) +static boot_os_fn do_bootm_integrity; +#endif + +static boot_os_fn *boot_os[] = { + [IH_OS_U_BOOT] = do_bootm_standalone, +#ifdef CONFIG_BOOTM_LINUX + [IH_OS_LINUX] = do_bootm_linux, +#endif +#ifdef CONFIG_BOOTM_NETBSD + [IH_OS_NETBSD] = do_bootm_netbsd, +#endif +#ifdef CONFIG_LYNXKDI + [IH_OS_LYNXOS] = do_bootm_lynxkdi, +#endif +#ifdef CONFIG_BOOTM_RTEMS + [IH_OS_RTEMS] = do_bootm_rtems, +#endif +#if defined(CONFIG_BOOTM_OSE) + [IH_OS_OSE] = do_bootm_ose, +#endif +#if defined(CONFIG_BOOTM_PLAN9) + [IH_OS_PLAN9] = do_bootm_plan9, +#endif +#if defined(CONFIG_BOOTM_VXWORKS) && \ + (defined(CONFIG_PPC) || defined(CONFIG_ARM)) + [IH_OS_VXWORKS] = do_bootm_vxworks, +#endif +#if defined(CONFIG_CMD_ELF) + [IH_OS_QNX] = do_bootm_qnxelf, +#endif +#ifdef CONFIG_INTEGRITY + [IH_OS_INTEGRITY] = do_bootm_integrity, +#endif +}; + +bootm_headers_t images; /* pointers to os/initrd/fdt images */ + +/* Allow for arch specific config before we boot */ +static void __arch_preboot_os(void) +{ + /* please define platform specific arch_preboot_os() */ +} +void arch_preboot_os(void) __attribute__((weak, alias("__arch_preboot_os"))); + +#define IH_INITRD_ARCH IH_ARCH_DEFAULT + +#ifdef CONFIG_LMB +static void boot_start_lmb(bootm_headers_t *images) +{ + ulong mem_start; + phys_size_t mem_size; + + lmb_init(&images->lmb); + + mem_start = getenv_bootm_low(); + mem_size = getenv_bootm_size(); + + lmb_add(&images->lmb, (phys_addr_t)mem_start, mem_size); + + arch_lmb_reserve(&images->lmb); + board_lmb_reserve(&images->lmb); +} +#else +#define lmb_reserve(lmb, base, size) +static inline void boot_start_lmb(bootm_headers_t *images) { } +#endif + +static int bootm_start(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + memset((void *)&images, 0, sizeof(images)); + images.verify = getenv_yesno("verify"); + + boot_start_lmb(&images); + + bootstage_mark_name(BOOTSTAGE_ID_BOOTM_START, "bootm_start"); + images.state = BOOTM_STATE_START; + + return 0; +} + +static int bootm_find_os(cmd_tbl_t *cmdtp, int flag, int argc, + char * const argv[]) +{ + const void *os_hdr; + + /* get kernel image header, start address and length */ + os_hdr = boot_get_kernel(cmdtp, flag, argc, argv, + &images, &images.os.image_start, &images.os.image_len); + if (images.os.image_len == 0) { + puts("ERROR: can't get kernel image!\n"); + return 1; + } + + /* get image parameters */ + switch (genimg_get_format(os_hdr)) { + case IMAGE_FORMAT_LEGACY: + images.os.type = image_get_type(os_hdr); + images.os.comp = image_get_comp(os_hdr); + images.os.os = image_get_os(os_hdr); + + images.os.end = image_get_image_end(os_hdr); + images.os.load = image_get_load(os_hdr); + break; +#if defined(CONFIG_FIT) + case IMAGE_FORMAT_FIT: + if (fit_image_get_type(images.fit_hdr_os, + images.fit_noffset_os, &images.os.type)) { + puts("Can't get image type!\n"); + bootstage_error(BOOTSTAGE_ID_FIT_TYPE); + return 1; + } + + if (fit_image_get_comp(images.fit_hdr_os, + images.fit_noffset_os, &images.os.comp)) { + puts("Can't get image compression!\n"); + bootstage_error(BOOTSTAGE_ID_FIT_COMPRESSION); + return 1; + } + + if (fit_image_get_os(images.fit_hdr_os, + images.fit_noffset_os, &images.os.os)) { + puts("Can't get image OS!\n"); + bootstage_error(BOOTSTAGE_ID_FIT_OS); + return 1; + } + + images.os.end = fit_get_end(images.fit_hdr_os); + + if (fit_image_get_load(images.fit_hdr_os, images.fit_noffset_os, + &images.os.load)) { + puts("Can't get image load address!\n"); + bootstage_error(BOOTSTAGE_ID_FIT_LOADADDR); + return 1; + } + break; +#endif + default: + puts("ERROR: unknown image format type!\n"); + return 1; + } + + /* find kernel entry point */ + if (images.legacy_hdr_valid) { + images.ep = image_get_ep(&images.legacy_hdr_os_copy); +#if defined(CONFIG_FIT) + } else if (images.fit_uname_os) { + int ret; + + ret = fit_image_get_entry(images.fit_hdr_os, + images.fit_noffset_os, &images.ep); + if (ret) { + puts("Can't get entry point property!\n"); + return 1; + } +#endif + } else { + puts("Could not find kernel entry point!\n"); + return 1; + } + + if (images.os.type == IH_TYPE_KERNEL_NOLOAD) { + images.os.load = images.os.image_start; + images.ep += images.os.load; + } + + images.os.start = (ulong)os_hdr; + + return 0; +} + +static int bootm_find_ramdisk(int flag, int argc, char * const argv[]) +{ + int ret; + + /* find ramdisk */ + ret = boot_get_ramdisk(argc, argv, &images, IH_INITRD_ARCH, + &images.rd_start, &images.rd_end); + if (ret) { + puts("Ramdisk image is corrupt or invalid\n"); + return 1; + } + + return 0; +} + +#if defined(CONFIG_OF_LIBFDT) +static int bootm_find_fdt(int flag, int argc, char * const argv[]) +{ + int ret; + + /* find flattened device tree */ + ret = boot_get_fdt(flag, argc, argv, IH_ARCH_DEFAULT, &images, + &images.ft_addr, &images.ft_len); + if (ret) { + puts("Could not find a valid device tree\n"); + return 1; + } + + set_working_fdt_addr(images.ft_addr); + + return 0; +} +#endif + +static int bootm_find_other(cmd_tbl_t *cmdtp, int flag, int argc, + char * const argv[]) +{ + if (((images.os.type == IH_TYPE_KERNEL) || + (images.os.type == IH_TYPE_KERNEL_NOLOAD) || + (images.os.type == IH_TYPE_MULTI)) && + (images.os.os == IH_OS_LINUX || + images.os.os == IH_OS_VXWORKS)) { + if (bootm_find_ramdisk(flag, argc, argv)) + return 1; + +#if defined(CONFIG_OF_LIBFDT) + if (bootm_find_fdt(flag, argc, argv)) + return 1; +#endif + } + + return 0; +} + +#define BOOTM_ERR_RESET -1 +#define BOOTM_ERR_OVERLAP -2 +#define BOOTM_ERR_UNIMPLEMENTED -3 +static int bootm_load_os(bootm_headers_t *images, unsigned long *load_end, + int boot_progress) +{ + image_info_t os = images->os; + uint8_t comp = os.comp; + ulong load = os.load; + ulong blob_start = os.start; + ulong blob_end = os.end; + ulong image_start = os.image_start; + ulong image_len = os.image_len; + __maybe_unused uint unc_len = CONFIG_SYS_BOOTM_LEN; + int no_overlap = 0; + void *load_buf, *image_buf; +#if defined(CONFIG_LZMA) || defined(CONFIG_LZO) + int ret; +#endif /* defined(CONFIG_LZMA) || defined(CONFIG_LZO) */ + + const char *type_name = genimg_get_type_name(os.type); + + load_buf = map_sysmem(load, unc_len); + image_buf = map_sysmem(image_start, image_len); + switch (comp) { + case IH_COMP_NONE: + if (load == image_start) { + printf(" XIP %s ... ", type_name); + no_overlap = 1; + } else { + printf(" Loading %s ... ", type_name); + memmove_wd(load_buf, image_buf, image_len, CHUNKSZ); + } + *load_end = load + image_len; + break; +#ifdef CONFIG_GZIP + case IH_COMP_GZIP: + printf(" Uncompressing %s ... ", type_name); + if (gunzip(load_buf, unc_len, image_buf, &image_len) != 0) { + puts("GUNZIP: uncompress, out-of-mem or overwrite " + "error - must RESET board to recover\n"); + if (boot_progress) + bootstage_error(BOOTSTAGE_ID_DECOMP_IMAGE); + return BOOTM_ERR_RESET; + } + + *load_end = load + image_len; + break; +#endif /* CONFIG_GZIP */ +#ifdef CONFIG_BZIP2 + case IH_COMP_BZIP2: + printf(" Uncompressing %s ... ", type_name); + /* + * If we've got less than 4 MB of malloc() space, + * use slower decompression algorithm which requires + * at most 2300 KB of memory. + */ + int i = BZ2_bzBuffToBuffDecompress(load_buf, &unc_len, + image_buf, image_len, + CONFIG_SYS_MALLOC_LEN < (4096 * 1024), 0); + if (i != BZ_OK) { + printf("BUNZIP2: uncompress or overwrite error %d " + "- must RESET board to recover\n", i); + if (boot_progress) + bootstage_error(BOOTSTAGE_ID_DECOMP_IMAGE); + return BOOTM_ERR_RESET; + } + + *load_end = load + unc_len; + break; +#endif /* CONFIG_BZIP2 */ +#ifdef CONFIG_LZMA + case IH_COMP_LZMA: { + SizeT lzma_len = unc_len; + printf(" Uncompressing %s ... ", type_name); + + ret = lzmaBuffToBuffDecompress(load_buf, &lzma_len, + image_buf, image_len); + unc_len = lzma_len; + if (ret != SZ_OK) { + printf("LZMA: uncompress or overwrite error %d " + "- must RESET board to recover\n", ret); + bootstage_error(BOOTSTAGE_ID_DECOMP_IMAGE); + return BOOTM_ERR_RESET; + } + *load_end = load + unc_len; + break; + } +#endif /* CONFIG_LZMA */ +#ifdef CONFIG_LZO + case IH_COMP_LZO: { + size_t size = unc_len; + + printf(" Uncompressing %s ... ", type_name); + + ret = lzop_decompress(image_buf, image_len, load_buf, &size); + if (ret != LZO_E_OK) { + printf("LZO: uncompress or overwrite error %d " + "- must RESET board to recover\n", ret); + if (boot_progress) + bootstage_error(BOOTSTAGE_ID_DECOMP_IMAGE); + return BOOTM_ERR_RESET; + } + + *load_end = load + size; + break; + } +#endif /* CONFIG_LZO */ + default: + printf("Unimplemented compression type %d\n", comp); + return BOOTM_ERR_UNIMPLEMENTED; + } + + flush_cache(load, (*load_end - load) * sizeof(ulong)); + + puts("OK\n"); + debug(" kernel loaded at 0x%08lx, end = 0x%08lx\n", load, *load_end); + bootstage_mark(BOOTSTAGE_ID_KERNEL_LOADED); + + if (!no_overlap && (load < blob_end) && (*load_end > blob_start)) { + debug("images.os.start = 0x%lX, images.os.end = 0x%lx\n", + blob_start, blob_end); + debug("images.os.load = 0x%lx, load_end = 0x%lx\n", load, + *load_end); + + /* Check what type of image this is. */ + if (images->legacy_hdr_valid) { + if (image_get_type(&images->legacy_hdr_os_copy) + == IH_TYPE_MULTI) + puts("WARNING: legacy format multi component image overwritten\n"); + return BOOTM_ERR_OVERLAP; + } else { + puts("ERROR: new format image overwritten - must RESET the board to recover\n"); + bootstage_error(BOOTSTAGE_ID_OVERWRITTEN); + return BOOTM_ERR_RESET; + } + } + + return 0; +} + +static int do_bootm_standalone(int flag, int argc, char * const argv[], + bootm_headers_t *images) +{ + char *s; + int (*appl)(int, char * const []); + + /* Don't start if "autostart" is set to "no" */ + if (((s = getenv("autostart")) != NULL) && (strcmp(s, "no") == 0)) { + setenv_hex("filesize", images->os.image_len); + return 0; + } + appl = (int (*)(int, char * const []))images->ep; + appl(argc, argv); + return 0; +} + +/* we overload the cmd field with our state machine info instead of a + * function pointer */ +static cmd_tbl_t cmd_bootm_sub[] = { + U_BOOT_CMD_MKENT(start, 0, 1, (void *)BOOTM_STATE_START, "", ""), + U_BOOT_CMD_MKENT(loados, 0, 1, (void *)BOOTM_STATE_LOADOS, "", ""), +#ifdef CONFIG_SYS_BOOT_RAMDISK_HIGH + U_BOOT_CMD_MKENT(ramdisk, 0, 1, (void *)BOOTM_STATE_RAMDISK, "", ""), +#endif +#ifdef CONFIG_OF_LIBFDT + U_BOOT_CMD_MKENT(fdt, 0, 1, (void *)BOOTM_STATE_FDT, "", ""), +#endif + U_BOOT_CMD_MKENT(cmdline, 0, 1, (void *)BOOTM_STATE_OS_CMDLINE, "", ""), + U_BOOT_CMD_MKENT(bdt, 0, 1, (void *)BOOTM_STATE_OS_BD_T, "", ""), + U_BOOT_CMD_MKENT(prep, 0, 1, (void *)BOOTM_STATE_OS_PREP, "", ""), + U_BOOT_CMD_MKENT(fake, 0, 1, (void *)BOOTM_STATE_OS_FAKE_GO, "", ""), + U_BOOT_CMD_MKENT(go, 0, 1, (void *)BOOTM_STATE_OS_GO, "", ""), +}; + +static int boot_selected_os(int argc, char * const argv[], int state, + bootm_headers_t *images, boot_os_fn *boot_fn) +{ + arch_preboot_os(); + boot_fn(state, argc, argv, images); + + /* Stand-alone may return when 'autostart' is 'no' */ + if (images->os.type == IH_TYPE_STANDALONE || + state == BOOTM_STATE_OS_FAKE_GO) /* We expect to return */ + return 0; + bootstage_error(BOOTSTAGE_ID_BOOT_OS_RETURNED); +#ifdef DEBUG + puts("\n## Control returned to monitor - resetting...\n"); +#endif + return BOOTM_ERR_RESET; +} + +/** + * bootm_disable_interrupts() - Disable interrupts in preparation for load/boot + * + * @return interrupt flag (0 if interrupts were disabled, non-zero if they were + * enabled) + */ +static ulong bootm_disable_interrupts(void) +{ + ulong iflag; + + /* + * We have reached the point of no return: we are going to + * overwrite all exception vector code, so we cannot easily + * recover from any failures any more... + */ + iflag = disable_interrupts(); +#ifdef CONFIG_NETCONSOLE + /* Stop the ethernet stack if NetConsole could have left it up */ + eth_halt(); + eth_unregister(eth_get_dev()); +#endif + +#if defined(CONFIG_CMD_USB) + /* + * turn off USB to prevent the host controller from writing to the + * SDRAM while Linux is booting. This could happen (at least for OHCI + * controller), because the HCCA (Host Controller Communication Area) + * lies within the SDRAM and the host controller writes continously to + * this area (as busmaster!). The HccaFrameNumber is for example + * updated every 1 ms within the HCCA structure in SDRAM! For more + * details see the OpenHCI specification. + */ + usb_stop(); +#endif + return iflag; +} + +/** + * Execute selected states of the bootm command. + * + * Note the arguments to this state must be the first argument, Any 'bootm' + * or sub-command arguments must have already been taken. + * + * Note that if states contains more than one flag it MUST contain + * BOOTM_STATE_START, since this handles and consumes the command line args. + * + * Also note that aside from boot_os_fn functions and bootm_load_os no other + * functions we store the return value of in 'ret' may use a negative return + * value, without special handling. + * + * @param cmdtp Pointer to bootm command table entry + * @param flag Command flags (CMD_FLAG_...) + * @param argc Number of subcommand arguments (0 = no arguments) + * @param argv Arguments + * @param states Mask containing states to run (BOOTM_STATE_...) + * @param images Image header information + * @param boot_progress 1 to show boot progress, 0 to not do this + * @return 0 if ok, something else on error. Some errors will cause this + * function to perform a reboot! If states contains BOOTM_STATE_OS_GO + * then the intent is to boot an OS, so this function will not return + * unless the image type is standalone. + */ +static int do_bootm_states(cmd_tbl_t *cmdtp, int flag, int argc, + char * const argv[], int states, bootm_headers_t *images, + int boot_progress) +{ + boot_os_fn *boot_fn; + ulong iflag = 0; + int ret = 0, need_boot_fn; + + images->state |= states; + + /* + * Work through the states and see how far we get. We stop on + * any error. + */ + if (states & BOOTM_STATE_START) + ret = bootm_start(cmdtp, flag, argc, argv); + + if (!ret && (states & BOOTM_STATE_FINDOS)) + ret = bootm_find_os(cmdtp, flag, argc, argv); + + if (!ret && (states & BOOTM_STATE_FINDOTHER)) { + ret = bootm_find_other(cmdtp, flag, argc, argv); + argc = 0; /* consume the args */ + } + + /* Load the OS */ + if (!ret && (states & BOOTM_STATE_LOADOS)) { + ulong load_end; + + iflag = bootm_disable_interrupts(); + ret = bootm_load_os(images, &load_end, 0); + if (ret == 0) + lmb_reserve(&images->lmb, images->os.load, + (load_end - images->os.load)); + else if (ret && ret != BOOTM_ERR_OVERLAP) + goto err; + else if (ret == BOOTM_ERR_OVERLAP) + ret = 0; +#if defined(CONFIG_SILENT_CONSOLE) && !defined(CONFIG_SILENT_U_BOOT_ONLY) + if (images->os.os == IH_OS_LINUX) + fixup_silent_linux(); +#endif + } + + /* Relocate the ramdisk */ +#ifdef CONFIG_SYS_BOOT_RAMDISK_HIGH + if (!ret && (states & BOOTM_STATE_RAMDISK)) { + ulong rd_len = images->rd_end - images->rd_start; + + ret = boot_ramdisk_high(&images->lmb, images->rd_start, + rd_len, &images->initrd_start, &images->initrd_end); + if (!ret) { + setenv_hex("initrd_start", images->initrd_start); + setenv_hex("initrd_end", images->initrd_end); + } + } +#endif +#if defined(CONFIG_OF_LIBFDT) && defined(CONFIG_LMB) + if (!ret && (states & BOOTM_STATE_FDT)) { + boot_fdt_add_mem_rsv_regions(&images->lmb, images->ft_addr); + ret = boot_relocate_fdt(&images->lmb, &images->ft_addr, + &images->ft_len); + } +#endif + + /* From now on, we need the OS boot function */ + if (ret) + return ret; + boot_fn = boot_os[images->os.os]; + need_boot_fn = states & (BOOTM_STATE_OS_CMDLINE | + BOOTM_STATE_OS_BD_T | BOOTM_STATE_OS_PREP | + BOOTM_STATE_OS_FAKE_GO | BOOTM_STATE_OS_GO); + if (boot_fn == NULL && need_boot_fn) { + if (iflag) + enable_interrupts(); + printf("ERROR: booting os '%s' (%d) is not supported\n", + genimg_get_os_name(images->os.os), images->os.os); + bootstage_error(BOOTSTAGE_ID_CHECK_BOOT_OS); + return 1; + } + + /* Call various other states that are not generally used */ + if (!ret && (states & BOOTM_STATE_OS_CMDLINE)) + ret = boot_fn(BOOTM_STATE_OS_CMDLINE, argc, argv, images); + if (!ret && (states & BOOTM_STATE_OS_BD_T)) + ret = boot_fn(BOOTM_STATE_OS_BD_T, argc, argv, images); + if (!ret && (states & BOOTM_STATE_OS_PREP)) + ret = boot_fn(BOOTM_STATE_OS_PREP, argc, argv, images); + +#ifdef CONFIG_TRACE + /* Pretend to run the OS, then run a user command */ + if (!ret && (states & BOOTM_STATE_OS_FAKE_GO)) { + char *cmd_list = getenv("fakegocmd"); + + ret = boot_selected_os(argc, argv, BOOTM_STATE_OS_FAKE_GO, + images, boot_fn); + if (!ret && cmd_list) + ret = run_command_list(cmd_list, -1, flag); + } +#endif + + /* Check for unsupported subcommand. */ + if (ret) { + puts("subcommand not supported\n"); + return ret; + } + + /* Now run the OS! We hope this doesn't return */ + if (!ret && (states & BOOTM_STATE_OS_GO)) + ret = boot_selected_os(argc, argv, BOOTM_STATE_OS_GO, + images, boot_fn); + + /* Deal with any fallout */ +err: + if (iflag) + enable_interrupts(); + + if (ret == BOOTM_ERR_UNIMPLEMENTED) + bootstage_error(BOOTSTAGE_ID_DECOMP_UNIMPL); + else if (ret == BOOTM_ERR_RESET) + do_reset(cmdtp, flag, argc, argv); + + return ret; +} + +static int do_bootm_subcommand(cmd_tbl_t *cmdtp, int flag, int argc, + char * const argv[]) +{ + int ret = 0; + long state; + cmd_tbl_t *c; + + c = find_cmd_tbl(argv[0], &cmd_bootm_sub[0], ARRAY_SIZE(cmd_bootm_sub)); + argc--; argv++; + + if (c) { + state = (long)c->cmd; + if (state == BOOTM_STATE_START) + state |= BOOTM_STATE_FINDOS | BOOTM_STATE_FINDOTHER; + } else { + /* Unrecognized command */ + return CMD_RET_USAGE; + } + + if (state != BOOTM_STATE_START && images.state >= state) { + printf("Trying to execute a command out of order\n"); + return CMD_RET_USAGE; + } + + ret = do_bootm_states(cmdtp, flag, argc, argv, state, &images, 0); + + return ret; +} + +/*******************************************************************/ +/* bootm - boot application image from image in memory */ +/*******************************************************************/ + +int do_bootm(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ +#ifdef CONFIG_NEEDS_MANUAL_RELOC + static int relocated = 0; + + if (!relocated) { + int i; + + /* relocate boot function table */ + for (i = 0; i < ARRAY_SIZE(boot_os); i++) + if (boot_os[i] != NULL) + boot_os[i] += gd->reloc_off; + + /* relocate names of sub-command table */ + for (i = 0; i < ARRAY_SIZE(cmd_bootm_sub); i++) + cmd_bootm_sub[i].name += gd->reloc_off; + + relocated = 1; + } +#endif + + /* determine if we have a sub command */ + argc--; argv++; + if (argc > 0) { + char *endp; + + simple_strtoul(argv[0], &endp, 16); + /* endp pointing to NULL means that argv[0] was just a + * valid number, pass it along to the normal bootm processing + * + * If endp is ':' or '#' assume a FIT identifier so pass + * along for normal processing. + * + * Right now we assume the first arg should never be '-' + */ + if ((*endp != 0) && (*endp != ':') && (*endp != '#')) + return do_bootm_subcommand(cmdtp, flag, argc, argv); + } + + return do_bootm_states(cmdtp, flag, argc, argv, BOOTM_STATE_START | + BOOTM_STATE_FINDOS | BOOTM_STATE_FINDOTHER | + BOOTM_STATE_LOADOS | +#if defined(CONFIG_PPC) || defined(CONFIG_MIPS) + BOOTM_STATE_OS_CMDLINE | +#endif + BOOTM_STATE_OS_PREP | BOOTM_STATE_OS_FAKE_GO | + BOOTM_STATE_OS_GO, &images, 1); +} + +int bootm_maybe_autostart(cmd_tbl_t *cmdtp, const char *cmd) +{ + const char *ep = getenv("autostart"); + + if (ep && !strcmp(ep, "yes")) { + char *local_args[2]; + local_args[0] = (char *)cmd; + local_args[1] = NULL; + printf("Automatic boot of image at addr 0x%08lX ...\n", load_addr); + return do_bootm(cmdtp, 0, 1, local_args); + } + + return 0; +} + +/** + * image_get_kernel - verify legacy format kernel image + * @img_addr: in RAM address of the legacy format image to be verified + * @verify: data CRC verification flag + * + * image_get_kernel() verifies legacy image integrity and returns pointer to + * legacy image header if image verification was completed successfully. + * + * returns: + * pointer to a legacy image header if valid image was found + * otherwise return NULL + */ +static image_header_t *image_get_kernel(ulong img_addr, int verify) +{ + image_header_t *hdr = (image_header_t *)img_addr; + + if (!image_check_magic(hdr)) { + puts("Bad Magic Number\n"); + bootstage_error(BOOTSTAGE_ID_CHECK_MAGIC); + return NULL; + } + bootstage_mark(BOOTSTAGE_ID_CHECK_HEADER); + + if (!image_check_hcrc(hdr)) { + puts("Bad Header Checksum\n"); + bootstage_error(BOOTSTAGE_ID_CHECK_HEADER); + return NULL; + } + + bootstage_mark(BOOTSTAGE_ID_CHECK_CHECKSUM); + image_print_contents(hdr); + + if (verify) { + puts(" Verifying Checksum ... "); + if (!image_check_dcrc(hdr)) { + printf("Bad Data CRC\n"); + bootstage_error(BOOTSTAGE_ID_CHECK_CHECKSUM); + return NULL; + } + puts("OK\n"); + } + bootstage_mark(BOOTSTAGE_ID_CHECK_ARCH); + + if (!image_check_target_arch(hdr)) { + printf("Unsupported Architecture 0x%x\n", image_get_arch(hdr)); + bootstage_error(BOOTSTAGE_ID_CHECK_ARCH); + return NULL; + } + return hdr; +} + +/** + * boot_get_kernel - find kernel image + * @os_data: pointer to a ulong variable, will hold os data start address + * @os_len: pointer to a ulong variable, will hold os data length + * + * boot_get_kernel() tries to find a kernel image, verifies its integrity + * and locates kernel data. + * + * returns: + * pointer to image header if valid image was found, plus kernel start + * address and length, otherwise NULL + */ +static const void *boot_get_kernel(cmd_tbl_t *cmdtp, int flag, int argc, + char * const argv[], bootm_headers_t *images, ulong *os_data, + ulong *os_len) +{ + image_header_t *hdr; + ulong img_addr; + const void *buf; +#if defined(CONFIG_FIT) + const char *fit_uname_config = NULL; + const char *fit_uname_kernel = NULL; + int os_noffset; +#endif + + /* find out kernel image address */ + if (argc < 1) { + img_addr = load_addr; + debug("* kernel: default image load address = 0x%08lx\n", + load_addr); +#if defined(CONFIG_FIT) + } else if (fit_parse_conf(argv[0], load_addr, &img_addr, + &fit_uname_config)) { + debug("* kernel: config '%s' from image at 0x%08lx\n", + fit_uname_config, img_addr); + } else if (fit_parse_subimage(argv[0], load_addr, &img_addr, + &fit_uname_kernel)) { + debug("* kernel: subimage '%s' from image at 0x%08lx\n", + fit_uname_kernel, img_addr); +#endif + } else { + img_addr = simple_strtoul(argv[0], NULL, 16); + debug("* kernel: cmdline image address = 0x%08lx\n", img_addr); + } + + bootstage_mark(BOOTSTAGE_ID_CHECK_MAGIC); + + /* copy from dataflash if needed */ + img_addr = genimg_get_image(img_addr); + + /* check image type, for FIT images get FIT kernel node */ + *os_data = *os_len = 0; + buf = map_sysmem(img_addr, 0); + switch (genimg_get_format(buf)) { + case IMAGE_FORMAT_LEGACY: + printf("## Booting kernel from Legacy Image at %08lx ...\n", + img_addr); + hdr = image_get_kernel(img_addr, images->verify); + if (!hdr) + return NULL; + bootstage_mark(BOOTSTAGE_ID_CHECK_IMAGETYPE); + + /* get os_data and os_len */ + switch (image_get_type(hdr)) { + case IH_TYPE_KERNEL: + case IH_TYPE_KERNEL_NOLOAD: + *os_data = image_get_data(hdr); + *os_len = image_get_data_size(hdr); + break; + case IH_TYPE_MULTI: + image_multi_getimg(hdr, 0, os_data, os_len); + break; + case IH_TYPE_STANDALONE: + *os_data = image_get_data(hdr); + *os_len = image_get_data_size(hdr); + break; + default: + printf("Wrong Image Type for %s command\n", + cmdtp->name); + bootstage_error(BOOTSTAGE_ID_CHECK_IMAGETYPE); + return NULL; + } + + /* + * copy image header to allow for image overwrites during + * kernel decompression. + */ + memmove(&images->legacy_hdr_os_copy, hdr, + sizeof(image_header_t)); + + /* save pointer to image header */ + images->legacy_hdr_os = hdr; + + images->legacy_hdr_valid = 1; + bootstage_mark(BOOTSTAGE_ID_DECOMP_IMAGE); + break; +#if defined(CONFIG_FIT) + case IMAGE_FORMAT_FIT: + os_noffset = fit_image_load(images, FIT_KERNEL_PROP, + img_addr, + &fit_uname_kernel, &fit_uname_config, + IH_ARCH_DEFAULT, IH_TYPE_KERNEL, + BOOTSTAGE_ID_FIT_KERNEL_START, + FIT_LOAD_IGNORED, os_data, os_len); + if (os_noffset < 0) + return NULL; + + images->fit_hdr_os = map_sysmem(img_addr, 0); + images->fit_uname_os = fit_uname_kernel; + images->fit_uname_cfg = fit_uname_config; + images->fit_noffset_os = os_noffset; + break; +#endif + default: + printf("Wrong Image Format for %s command\n", cmdtp->name); + bootstage_error(BOOTSTAGE_ID_FIT_KERNEL_INFO); + return NULL; + } + + debug(" kernel data at 0x%08lx, len = 0x%08lx (%ld)\n", + *os_data, *os_len, *os_len); + + return buf; +} + +#ifdef CONFIG_SYS_LONGHELP +static char bootm_help_text[] = + "[addr [arg ...]]\n - boot application image stored in memory\n" + "\tpassing arguments 'arg ...'; when booting a Linux kernel,\n" + "\t'arg' can be the address of an initrd image\n" +#if defined(CONFIG_OF_LIBFDT) + "\tWhen booting a Linux kernel which requires a flat device-tree\n" + "\ta third argument is required which is the address of the\n" + "\tdevice-tree blob. To boot that kernel without an initrd image,\n" + "\tuse a '-' for the second argument. If you do not pass a third\n" + "\ta bd_info struct will be passed instead\n" +#endif +#if defined(CONFIG_FIT) + "\t\nFor the new multi component uImage format (FIT) addresses\n" + "\tmust be extened to include component or configuration unit name:\n" + "\taddr:<subimg_uname> - direct component image specification\n" + "\taddr#<conf_uname> - configuration specification\n" + "\tUse iminfo command to get the list of existing component\n" + "\timages and configurations.\n" +#endif + "\nSub-commands to do part of the bootm sequence. The sub-commands " + "must be\n" + "issued in the order below (it's ok to not issue all sub-commands):\n" + "\tstart [addr [arg ...]]\n" + "\tloados - load OS image\n" +#if defined(CONFIG_SYS_BOOT_RAMDISK_HIGH) + "\tramdisk - relocate initrd, set env initrd_start/initrd_end\n" +#endif +#if defined(CONFIG_OF_LIBFDT) + "\tfdt - relocate flat device tree\n" +#endif + "\tcmdline - OS specific command line processing/setup\n" + "\tbdt - OS specific bd_t processing\n" + "\tprep - OS specific prep before relocation or go\n" + "\tgo - start OS"; +#endif + +U_BOOT_CMD( + bootm, CONFIG_SYS_MAXARGS, 1, do_bootm, + "boot application image from memory", bootm_help_text +); + +/*******************************************************************/ +/* bootd - boot default image */ +/*******************************************************************/ +#if defined(CONFIG_CMD_BOOTD) +int do_bootd(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + int rcode = 0; + + if (run_command(getenv("bootcmd"), flag) < 0) + rcode = 1; + return rcode; +} + +U_BOOT_CMD( + boot, 1, 1, do_bootd, + "boot default, i.e., run 'bootcmd'", + "" +); + +/* keep old command name "bootd" for backward compatibility */ +U_BOOT_CMD( + bootd, 1, 1, do_bootd, + "boot default, i.e., run 'bootcmd'", + "" +); + +#endif + + +/*******************************************************************/ +/* iminfo - print header info for a requested image */ +/*******************************************************************/ +#if defined(CONFIG_CMD_IMI) +static int do_iminfo(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + int arg; + ulong addr; + int rcode = 0; + + if (argc < 2) { + return image_info(load_addr); + } + + for (arg = 1; arg < argc; ++arg) { + addr = simple_strtoul(argv[arg], NULL, 16); + if (image_info(addr) != 0) + rcode = 1; + } + return rcode; +} + +static int image_info(ulong addr) +{ + void *hdr = (void *)addr; + + printf("\n## Checking Image at %08lx ...\n", addr); + + switch (genimg_get_format(hdr)) { + case IMAGE_FORMAT_LEGACY: + puts(" Legacy image found\n"); + if (!image_check_magic(hdr)) { + puts(" Bad Magic Number\n"); + return 1; + } + + if (!image_check_hcrc(hdr)) { + puts(" Bad Header Checksum\n"); + return 1; + } + + image_print_contents(hdr); + + puts(" Verifying Checksum ... "); + if (!image_check_dcrc(hdr)) { + puts(" Bad Data CRC\n"); + return 1; + } + puts("OK\n"); + return 0; +#if defined(CONFIG_FIT) + case IMAGE_FORMAT_FIT: + puts(" FIT image found\n"); + + if (!fit_check_format(hdr)) { + puts("Bad FIT image format!\n"); + return 1; + } + + fit_print_contents(hdr); + + if (!fit_all_image_verify(hdr)) { + puts("Bad hash in FIT image!\n"); + return 1; + } + + return 0; +#endif + default: + puts("Unknown image format!\n"); + break; + } + + return 1; +} + +U_BOOT_CMD( + iminfo, CONFIG_SYS_MAXARGS, 1, do_iminfo, + "print header information for application image", + "addr [addr ...]\n" + " - print header information for application image starting at\n" + " address 'addr' in memory; this includes verification of the\n" + " image contents (magic number, header and payload checksums)" +); +#endif + + +/*******************************************************************/ +/* imls - list all images found in flash */ +/*******************************************************************/ +#if defined(CONFIG_CMD_IMLS) +static int do_imls_nor(void) +{ + flash_info_t *info; + int i, j; + void *hdr; + + for (i = 0, info = &flash_info[0]; + i < CONFIG_SYS_MAX_FLASH_BANKS; ++i, ++info) { + + if (info->flash_id == FLASH_UNKNOWN) + goto next_bank; + for (j = 0; j < info->sector_count; ++j) { + + hdr = (void *)info->start[j]; + if (!hdr) + goto next_sector; + + switch (genimg_get_format(hdr)) { + case IMAGE_FORMAT_LEGACY: + if (!image_check_hcrc(hdr)) + goto next_sector; + + printf("Legacy Image at %08lX:\n", (ulong)hdr); + image_print_contents(hdr); + + puts(" Verifying Checksum ... "); + if (!image_check_dcrc(hdr)) { + puts("Bad Data CRC\n"); + } else { + puts("OK\n"); + } + break; +#if defined(CONFIG_FIT) + case IMAGE_FORMAT_FIT: + if (!fit_check_format(hdr)) + goto next_sector; + + printf("FIT Image at %08lX:\n", (ulong)hdr); + fit_print_contents(hdr); + break; +#endif + default: + goto next_sector; + } + +next_sector: ; + } +next_bank: ; + } + return 0; +} +#endif + +#if defined(CONFIG_CMD_IMLS_NAND) +static int nand_imls_legacyimage(nand_info_t *nand, int nand_dev, loff_t off, + size_t len) +{ + void *imgdata; + int ret; + + imgdata = malloc(len); + if (!imgdata) { + printf("May be a Legacy Image at NAND device %d offset %08llX:\n", + nand_dev, off); + printf(" Low memory(cannot allocate memory for image)\n"); + return -ENOMEM; + } + + ret = nand_read_skip_bad(nand, off, &len, + imgdata); + if (ret < 0 && ret != -EUCLEAN) { + free(imgdata); + return ret; + } + + if (!image_check_hcrc(imgdata)) { + free(imgdata); + return 0; + } + + printf("Legacy Image at NAND device %d offset %08llX:\n", + nand_dev, off); + image_print_contents(imgdata); + + puts(" Verifying Checksum ... "); + if (!image_check_dcrc(imgdata)) + puts("Bad Data CRC\n"); + else + puts("OK\n"); + + free(imgdata); + + return 0; +} + +static int nand_imls_fitimage(nand_info_t *nand, int nand_dev, loff_t off, + size_t len) +{ + void *imgdata; + int ret; + + imgdata = malloc(len); + if (!imgdata) { + printf("May be a FIT Image at NAND device %d offset %08llX:\n", + nand_dev, off); + printf(" Low memory(cannot allocate memory for image)\n"); + return -ENOMEM; + } + + ret = nand_read_skip_bad(nand, off, &len, + imgdata); + if (ret < 0 && ret != -EUCLEAN) { + free(imgdata); + return ret; + } + + if (!fit_check_format(imgdata)) { + free(imgdata); + return 0; + } + + printf("FIT Image at NAND device %d offset %08llX:\n", nand_dev, off); + + fit_print_contents(imgdata); + free(imgdata); + + return 0; +} + +static int do_imls_nand(void) +{ + nand_info_t *nand; + int nand_dev = nand_curr_device; + size_t len; + loff_t off; + u32 buffer[16]; + + if (nand_dev < 0 || nand_dev >= CONFIG_SYS_MAX_NAND_DEVICE) { + puts("\nNo NAND devices available\n"); + return -ENODEV; + } + + printf("\n"); + + for (nand_dev = 0; nand_dev < CONFIG_SYS_MAX_NAND_DEVICE; nand_dev++) { + nand = &nand_info[nand_dev]; + if (!nand->name || !nand->size) + continue; + + for (off = 0; off < nand->size; off += nand->erasesize) { + const image_header_t *header; + int ret; + + if (nand_block_isbad(nand, off)) + continue; + + len = sizeof(buffer); + + ret = nand_read(nand, off, &len, (u8 *)buffer); + if (ret < 0 && ret != -EUCLEAN) { + printf("NAND read error %d at offset %08llX\n", + ret, off); + continue; + } + + switch (genimg_get_format(buffer)) { + case IMAGE_FORMAT_LEGACY: + header = (const image_header_t *)buffer; + + len = image_get_image_size(header); + nand_imls_legacyimage(nand, nand_dev, off, len); + break; +#if defined(CONFIG_FIT) + case IMAGE_FORMAT_FIT: + len = fit_get_size(buffer); + nand_imls_fitimage(nand, nand_dev, off, len); + break; +#endif + } + } + } + + return 0; +} +#endif + +#if defined(CONFIG_CMD_IMLS) || defined(CONFIG_CMD_IMLS_NAND) +static int do_imls(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + int ret_nor = 0, ret_nand = 0; + +#if defined(CONFIG_CMD_IMLS) + ret_nor = do_imls_nor(); +#endif + +#if defined(CONFIG_CMD_IMLS_NAND) + ret_nand = do_imls_nand(); +#endif + + if (ret_nor) + return ret_nor; + + if (ret_nand) + return ret_nand; + + return (0); +} + +U_BOOT_CMD( + imls, 1, 1, do_imls, + "list all images found in flash", + "\n" + " - Prints information about all images found at sector/block\n" + " boundaries in nor/nand flash." +); +#endif + +/*******************************************************************/ +/* helper routines */ +/*******************************************************************/ +#if defined(CONFIG_SILENT_CONSOLE) && !defined(CONFIG_SILENT_U_BOOT_ONLY) + +#define CONSOLE_ARG "console=" +#define CONSOLE_ARG_LEN (sizeof(CONSOLE_ARG) - 1) + +static void fixup_silent_linux(void) +{ + char *buf; + const char *env_val; + char *cmdline = getenv("bootargs"); + int want_silent; + + /* + * Only fix cmdline when requested. The environment variable can be: + * + * no - we never fixup + * yes - we always fixup + * unset - we rely on the console silent flag + */ + want_silent = getenv_yesno("silent_linux"); + if (want_silent == 0) + return; + else if (want_silent == -1 && !(gd->flags & GD_FLG_SILENT)) + return; + + debug("before silent fix-up: %s\n", cmdline); + if (cmdline && (cmdline[0] != '\0')) { + char *start = strstr(cmdline, CONSOLE_ARG); + + /* Allocate space for maximum possible new command line */ + buf = malloc(strlen(cmdline) + 1 + CONSOLE_ARG_LEN + 1); + if (!buf) { + debug("%s: out of memory\n", __func__); + return; + } + + if (start) { + char *end = strchr(start, ' '); + int num_start_bytes = start - cmdline + CONSOLE_ARG_LEN; + + strncpy(buf, cmdline, num_start_bytes); + if (end) + strcpy(buf + num_start_bytes, end); + else + buf[num_start_bytes] = '\0'; + } else { + sprintf(buf, "%s %s", cmdline, CONSOLE_ARG); + } + env_val = buf; + } else { + buf = NULL; + env_val = CONSOLE_ARG; + } + + setenv("bootargs", env_val); + debug("after silent fix-up: %s\n", env_val); + free(buf); +} +#endif /* CONFIG_SILENT_CONSOLE */ + +#if defined(CONFIG_BOOTM_NETBSD) || defined(CONFIG_BOOTM_PLAN9) +static void copy_args(char *dest, int argc, char * const argv[], char delim) +{ + int i; + + for (i = 0; i < argc; i++) { + if (i > 0) + *dest++ = delim; + strcpy(dest, argv[i]); + dest += strlen(argv[i]); + } +} +#endif + +/*******************************************************************/ +/* OS booting routines */ +/*******************************************************************/ + +#ifdef CONFIG_BOOTM_NETBSD +static int do_bootm_netbsd(int flag, int argc, char * const argv[], + bootm_headers_t *images) +{ + void (*loader)(bd_t *, image_header_t *, char *, char *); + image_header_t *os_hdr, *hdr; + ulong kernel_data, kernel_len; + char *consdev; + char *cmdline; + + if (flag != BOOTM_STATE_OS_GO) + return 0; + +#if defined(CONFIG_FIT) + if (!images->legacy_hdr_valid) { + fit_unsupported_reset("NetBSD"); + return 1; + } +#endif + hdr = images->legacy_hdr_os; + + /* + * Booting a (NetBSD) kernel image + * + * This process is pretty similar to a standalone application: + * The (first part of an multi-) image must be a stage-2 loader, + * which in turn is responsible for loading & invoking the actual + * kernel. The only differences are the parameters being passed: + * besides the board info strucure, the loader expects a command + * line, the name of the console device, and (optionally) the + * address of the original image header. + */ + os_hdr = NULL; + if (image_check_type(&images->legacy_hdr_os_copy, IH_TYPE_MULTI)) { + image_multi_getimg(hdr, 1, &kernel_data, &kernel_len); + if (kernel_len) + os_hdr = hdr; + } + + consdev = ""; +#if defined(CONFIG_8xx_CONS_SMC1) + consdev = "smc1"; +#elif defined(CONFIG_8xx_CONS_SMC2) + consdev = "smc2"; +#elif defined(CONFIG_8xx_CONS_SCC2) + consdev = "scc2"; +#elif defined(CONFIG_8xx_CONS_SCC3) + consdev = "scc3"; +#endif + + if (argc > 0) { + ulong len; + int i; + + for (i = 0, len = 0; i < argc; i += 1) + len += strlen(argv[i]) + 1; + cmdline = malloc(len); + copy_args(cmdline, argc, argv, ' '); + } else if ((cmdline = getenv("bootargs")) == NULL) { + cmdline = ""; + } + + loader = (void (*)(bd_t *, image_header_t *, char *, char *))images->ep; + + printf("## Transferring control to NetBSD stage-2 loader " + "(at address %08lx) ...\n", + (ulong)loader); + + bootstage_mark(BOOTSTAGE_ID_RUN_OS); + + /* + * NetBSD Stage-2 Loader Parameters: + * arg[0]: pointer to board info data + * arg[1]: image load address + * arg[2]: char pointer to the console device to use + * arg[3]: char pointer to the boot arguments + */ + (*loader)(gd->bd, os_hdr, consdev, cmdline); + + return 1; +} +#endif /* CONFIG_BOOTM_NETBSD*/ + +#ifdef CONFIG_LYNXKDI +static int do_bootm_lynxkdi(int flag, int argc, char * const argv[], + bootm_headers_t *images) +{ + image_header_t *hdr = &images->legacy_hdr_os_copy; + + if (flag != BOOTM_STATE_OS_GO) + return 0; + +#if defined(CONFIG_FIT) + if (!images->legacy_hdr_valid) { + fit_unsupported_reset("Lynx"); + return 1; + } +#endif + + lynxkdi_boot((image_header_t *)hdr); + + return 1; +} +#endif /* CONFIG_LYNXKDI */ + +#ifdef CONFIG_BOOTM_RTEMS +static int do_bootm_rtems(int flag, int argc, char * const argv[], + bootm_headers_t *images) +{ + void (*entry_point)(bd_t *); + + if (flag != BOOTM_STATE_OS_GO) + return 0; + +#if defined(CONFIG_FIT) + if (!images->legacy_hdr_valid) { + fit_unsupported_reset("RTEMS"); + return 1; + } +#endif + + entry_point = (void (*)(bd_t *))images->ep; + + printf("## Transferring control to RTEMS (at address %08lx) ...\n", + (ulong)entry_point); + + bootstage_mark(BOOTSTAGE_ID_RUN_OS); + + /* + * RTEMS Parameters: + * r3: ptr to board info data + */ + (*entry_point)(gd->bd); + + return 1; +} +#endif /* CONFIG_BOOTM_RTEMS */ + +#if defined(CONFIG_BOOTM_OSE) +static int do_bootm_ose(int flag, int argc, char * const argv[], + bootm_headers_t *images) +{ + void (*entry_point)(void); + + if (flag != BOOTM_STATE_OS_GO) + return 0; + +#if defined(CONFIG_FIT) + if (!images->legacy_hdr_valid) { + fit_unsupported_reset("OSE"); + return 1; + } +#endif + + entry_point = (void (*)(void))images->ep; + + printf("## Transferring control to OSE (at address %08lx) ...\n", + (ulong)entry_point); + + bootstage_mark(BOOTSTAGE_ID_RUN_OS); + + /* + * OSE Parameters: + * None + */ + (*entry_point)(); + + return 1; +} +#endif /* CONFIG_BOOTM_OSE */ + +#if defined(CONFIG_BOOTM_PLAN9) +static int do_bootm_plan9(int flag, int argc, char * const argv[], + bootm_headers_t *images) +{ + void (*entry_point)(void); + char *s; + + if (flag != BOOTM_STATE_OS_GO) + return 0; + +#if defined(CONFIG_FIT) + if (!images->legacy_hdr_valid) { + fit_unsupported_reset("Plan 9"); + return 1; + } +#endif + + /* See README.plan9 */ + s = getenv("confaddr"); + if (s != NULL) { + char *confaddr = (char *)simple_strtoul(s, NULL, 16); + + if (argc > 0) { + copy_args(confaddr, argc, argv, '\n'); + } else { + s = getenv("bootargs"); + if (s != NULL) + strcpy(confaddr, s); + } + } + + entry_point = (void (*)(void))images->ep; + + printf("## Transferring control to Plan 9 (at address %08lx) ...\n", + (ulong)entry_point); + + bootstage_mark(BOOTSTAGE_ID_RUN_OS); + + /* + * Plan 9 Parameters: + * None + */ + (*entry_point)(); + + return 1; +} +#endif /* CONFIG_BOOTM_PLAN9 */ + +#if defined(CONFIG_BOOTM_VXWORKS) && \ + (defined(CONFIG_PPC) || defined(CONFIG_ARM)) + +void do_bootvx_fdt(bootm_headers_t *images) +{ +#if defined(CONFIG_OF_LIBFDT) + int ret; + char *bootline; + ulong of_size = images->ft_len; + char **of_flat_tree = &images->ft_addr; + struct lmb *lmb = &images->lmb; + + if (*of_flat_tree) { + boot_fdt_add_mem_rsv_regions(lmb, *of_flat_tree); + + ret = boot_relocate_fdt(lmb, of_flat_tree, &of_size); + if (ret) + return; + + ret = fdt_add_subnode(*of_flat_tree, 0, "chosen"); + if ((ret >= 0 || ret == -FDT_ERR_EXISTS)) { + bootline = getenv("bootargs"); + if (bootline) { + ret = fdt_find_and_setprop(*of_flat_tree, + "/chosen", "bootargs", + bootline, + strlen(bootline) + 1, 1); + if (ret < 0) { + printf("## ERROR: %s : %s\n", __func__, + fdt_strerror(ret)); + return; + } + } + } else { + printf("## ERROR: %s : %s\n", __func__, + fdt_strerror(ret)); + return; + } + } +#endif + + boot_prep_vxworks(images); + + bootstage_mark(BOOTSTAGE_ID_RUN_OS); + +#if defined(CONFIG_OF_LIBFDT) + printf("## Starting vxWorks at 0x%08lx, device tree at 0x%08lx ...\n", + (ulong)images->ep, (ulong)*of_flat_tree); +#else + printf("## Starting vxWorks at 0x%08lx\n", (ulong)images->ep); +#endif + + boot_jump_vxworks(images); + + puts("## vxWorks terminated\n"); +} + +static int do_bootm_vxworks(int flag, int argc, char * const argv[], + bootm_headers_t *images) +{ + if (flag != BOOTM_STATE_OS_GO) + return 0; + +#if defined(CONFIG_FIT) + if (!images->legacy_hdr_valid) { + fit_unsupported_reset("VxWorks"); + return 1; + } +#endif + + do_bootvx_fdt(images); + + return 1; +} +#endif + +#if defined(CONFIG_CMD_ELF) +static int do_bootm_qnxelf(int flag, int argc, char * const argv[], + bootm_headers_t *images) +{ + char *local_args[2]; + char str[16]; + + if (flag != BOOTM_STATE_OS_GO) + return 0; + +#if defined(CONFIG_FIT) + if (!images->legacy_hdr_valid) { + fit_unsupported_reset("QNX"); + return 1; + } +#endif + + sprintf(str, "%lx", images->ep); /* write entry-point into string */ + local_args[0] = argv[0]; + local_args[1] = str; /* and provide it via the arguments */ + do_bootelf(NULL, 0, 2, local_args); + + return 1; +} +#endif + +#ifdef CONFIG_INTEGRITY +static int do_bootm_integrity(int flag, int argc, char * const argv[], + bootm_headers_t *images) +{ + void (*entry_point)(void); + + if (flag != BOOTM_STATE_OS_GO) + return 0; + +#if defined(CONFIG_FIT) + if (!images->legacy_hdr_valid) { + fit_unsupported_reset("INTEGRITY"); + return 1; + } +#endif + + entry_point = (void (*)(void))images->ep; + + printf("## Transferring control to INTEGRITY (at address %08lx) ...\n", + (ulong)entry_point); + + bootstage_mark(BOOTSTAGE_ID_RUN_OS); + + /* + * INTEGRITY Parameters: + * None + */ + (*entry_point)(); + + return 1; +} +#endif + +#ifdef CONFIG_CMD_BOOTZ + +int __weak bootz_setup(ulong image, ulong *start, ulong *end) +{ + /* Please define bootz_setup() for your platform */ + + puts("Your platform's zImage format isn't supported yet!\n"); + return -1; +} + +/* + * zImage booting support + */ +static int bootz_start(cmd_tbl_t *cmdtp, int flag, int argc, + char * const argv[], bootm_headers_t *images) +{ + int ret; + ulong zi_start, zi_end; + + ret = do_bootm_states(cmdtp, flag, argc, argv, BOOTM_STATE_START, + images, 1); + + /* Setup Linux kernel zImage entry point */ + if (!argc) { + images->ep = load_addr; + debug("* kernel: default image load address = 0x%08lx\n", + load_addr); + } else { + images->ep = simple_strtoul(argv[0], NULL, 16); + debug("* kernel: cmdline image address = 0x%08lx\n", + images->ep); + } + + ret = bootz_setup(images->ep, &zi_start, &zi_end); + if (ret != 0) + return 1; + + lmb_reserve(&images->lmb, images->ep, zi_end - zi_start); + + /* + * Handle the BOOTM_STATE_FINDOTHER state ourselves as we do not + * have a header that provide this informaiton. + */ + if (bootm_find_ramdisk(flag, argc, argv)) + return 1; + +#if defined(CONFIG_OF_LIBFDT) + if (bootm_find_fdt(flag, argc, argv)) + return 1; +#endif + + return 0; +} + +int do_bootz(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + int ret; + + /* Consume 'bootz' */ + argc--; argv++; + + if (bootz_start(cmdtp, flag, argc, argv, &images)) + return 1; + + /* + * We are doing the BOOTM_STATE_LOADOS state ourselves, so must + * disable interrupts ourselves + */ + bootm_disable_interrupts(); + + images.os.os = IH_OS_LINUX; + ret = do_bootm_states(cmdtp, flag, argc, argv, + BOOTM_STATE_OS_PREP | BOOTM_STATE_OS_FAKE_GO | + BOOTM_STATE_OS_GO, + &images, 1); + + return ret; +} + +#ifdef CONFIG_SYS_LONGHELP +static char bootz_help_text[] = + "[addr [initrd[:size]] [fdt]]\n" + " - boot Linux zImage stored in memory\n" + "\tThe argument 'initrd' is optional and specifies the address\n" + "\tof the initrd in memory. The optional argument ':size' allows\n" + "\tspecifying the size of RAW initrd.\n" +#if defined(CONFIG_OF_LIBFDT) + "\tWhen booting a Linux kernel which requires a flat device-tree\n" + "\ta third argument is required which is the address of the\n" + "\tdevice-tree blob. To boot that kernel without an initrd image,\n" + "\tuse a '-' for the second argument. If you do not pass a third\n" + "\ta bd_info struct will be passed instead\n" +#endif + ""; +#endif + +U_BOOT_CMD( + bootz, CONFIG_SYS_MAXARGS, 1, do_bootz, + "boot Linux zImage image from memory", bootz_help_text +); +#endif /* CONFIG_CMD_BOOTZ */ diff --git a/qemu/roms/u-boot/common/cmd_bootmenu.c b/qemu/roms/u-boot/common/cmd_bootmenu.c new file mode 100644 index 000000000..163d5b2e2 --- /dev/null +++ b/qemu/roms/u-boot/common/cmd_bootmenu.c @@ -0,0 +1,501 @@ +/* + * (C) Copyright 2011-2013 Pali Rohár <pali.rohar@gmail.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <command.h> +#include <ansi.h> +#include <menu.h> +#include <hush.h> +#include <watchdog.h> +#include <malloc.h> +#include <linux/string.h> + +/* maximum bootmenu entries */ +#define MAX_COUNT 99 + +/* maximal size of bootmenu env + * 9 = strlen("bootmenu_") + * 2 = strlen(MAX_COUNT) + * 1 = NULL term + */ +#define MAX_ENV_SIZE (9 + 2 + 1) + +struct bootmenu_entry { + unsigned short int num; /* unique number 0 .. MAX_COUNT */ + char key[3]; /* key identifier of number */ + char *title; /* title of entry */ + char *command; /* hush command of entry */ + struct bootmenu_data *menu; /* this bootmenu */ + struct bootmenu_entry *next; /* next menu entry (num+1) */ +}; + +struct bootmenu_data { + int delay; /* delay for autoboot */ + int active; /* active menu entry */ + int count; /* total count of menu entries */ + struct bootmenu_entry *first; /* first menu entry */ +}; + +enum bootmenu_key { + KEY_NONE = 0, + KEY_UP, + KEY_DOWN, + KEY_SELECT, +}; + +static char *bootmenu_getoption(unsigned short int n) +{ + char name[MAX_ENV_SIZE]; + + if (n > MAX_COUNT) + return NULL; + + sprintf(name, "bootmenu_%d", n); + return getenv(name); +} + +static void bootmenu_print_entry(void *data) +{ + struct bootmenu_entry *entry = data; + int reverse = (entry->menu->active == entry->num); + + /* + * Move cursor to line where the entry will be drown (entry->num) + * First 3 lines contain bootmenu header + 1 empty line + */ + printf(ANSI_CURSOR_POSITION, entry->num + 4, 1); + + puts(" "); + + if (reverse) + puts(ANSI_COLOR_REVERSE); + + puts(entry->title); + + if (reverse) + puts(ANSI_COLOR_RESET); +} + +static void bootmenu_autoboot_loop(struct bootmenu_data *menu, + enum bootmenu_key *key, int *esc) +{ + int i, c; + + if (menu->delay > 0) { + printf(ANSI_CURSOR_POSITION, menu->count + 5, 1); + printf(" Hit any key to stop autoboot: %2d ", menu->delay); + } + + while (menu->delay > 0) { + for (i = 0; i < 100; ++i) { + if (!tstc()) { + WATCHDOG_RESET(); + mdelay(10); + continue; + } + + menu->delay = -1; + c = getc(); + + switch (c) { + case '\e': + *esc = 1; + *key = KEY_NONE; + break; + case '\r': + *key = KEY_SELECT; + break; + default: + *key = KEY_NONE; + break; + } + + break; + } + + if (menu->delay < 0) + break; + + --menu->delay; + printf("\b\b\b%2d ", menu->delay); + } + + printf(ANSI_CURSOR_POSITION, menu->count + 5, 1); + puts(ANSI_CLEAR_LINE); + + if (menu->delay == 0) + *key = KEY_SELECT; +} + +static void bootmenu_loop(struct bootmenu_data *menu, + enum bootmenu_key *key, int *esc) +{ + int c; + + while (!tstc()) { + WATCHDOG_RESET(); + mdelay(10); + } + + c = getc(); + + switch (*esc) { + case 0: + /* First char of ANSI escape sequence '\e' */ + if (c == '\e') { + *esc = 1; + *key = KEY_NONE; + } + break; + case 1: + /* Second char of ANSI '[' */ + if (c == '[') { + *esc = 2; + *key = KEY_NONE; + } else { + *esc = 0; + } + break; + case 2: + case 3: + /* Third char of ANSI (number '1') - optional */ + if (*esc == 2 && c == '1') { + *esc = 3; + *key = KEY_NONE; + break; + } + + *esc = 0; + + /* ANSI 'A' - key up was pressed */ + if (c == 'A') + *key = KEY_UP; + /* ANSI 'B' - key down was pressed */ + else if (c == 'B') + *key = KEY_DOWN; + /* other key was pressed */ + else + *key = KEY_NONE; + + break; + } + + /* enter key was pressed */ + if (c == '\r') + *key = KEY_SELECT; +} + +static char *bootmenu_choice_entry(void *data) +{ + struct bootmenu_data *menu = data; + struct bootmenu_entry *iter; + enum bootmenu_key key = KEY_NONE; + int esc = 0; + int i; + + while (1) { + if (menu->delay >= 0) { + /* Autoboot was not stopped */ + bootmenu_autoboot_loop(menu, &key, &esc); + } else { + /* Some key was pressed, so autoboot was stopped */ + bootmenu_loop(menu, &key, &esc); + } + + switch (key) { + case KEY_UP: + if (menu->active > 0) + --menu->active; + /* no menu key selected, regenerate menu */ + return NULL; + case KEY_DOWN: + if (menu->active < menu->count - 1) + ++menu->active; + /* no menu key selected, regenerate menu */ + return NULL; + case KEY_SELECT: + iter = menu->first; + for (i = 0; i < menu->active; ++i) + iter = iter->next; + return iter->key; + default: + break; + } + } + + /* never happens */ + debug("bootmenu: this should not happen"); + return NULL; +} + +static void bootmenu_destroy(struct bootmenu_data *menu) +{ + struct bootmenu_entry *iter = menu->first; + struct bootmenu_entry *next; + + while (iter) { + next = iter->next; + free(iter->title); + free(iter->command); + free(iter); + iter = next; + } + free(menu); +} + +static struct bootmenu_data *bootmenu_create(int delay) +{ + unsigned short int i = 0; + const char *option; + struct bootmenu_data *menu; + struct bootmenu_entry *iter = NULL; + + int len; + char *sep; + struct bootmenu_entry *entry; + + menu = malloc(sizeof(struct bootmenu_data)); + if (!menu) + return NULL; + + menu->delay = delay; + menu->active = 0; + menu->first = NULL; + + while ((option = bootmenu_getoption(i))) { + sep = strchr(option, '='); + if (!sep) { + printf("Invalid bootmenu entry: %s\n", option); + break; + } + + entry = malloc(sizeof(struct bootmenu_entry)); + if (!entry) + goto cleanup; + + len = sep-option; + entry->title = malloc(len + 1); + if (!entry->title) { + free(entry); + goto cleanup; + } + memcpy(entry->title, option, len); + entry->title[len] = 0; + + len = strlen(sep + 1); + entry->command = malloc(len + 1); + if (!entry->command) { + free(entry->title); + free(entry); + goto cleanup; + } + memcpy(entry->command, sep + 1, len); + entry->command[len] = 0; + + sprintf(entry->key, "%d", i); + + entry->num = i; + entry->menu = menu; + entry->next = NULL; + + if (!iter) + menu->first = entry; + else + iter->next = entry; + + iter = entry; + ++i; + + if (i == MAX_COUNT - 1) + break; + } + + /* Add U-Boot console entry at the end */ + if (i <= MAX_COUNT - 1) { + entry = malloc(sizeof(struct bootmenu_entry)); + if (!entry) + goto cleanup; + + entry->title = strdup("U-Boot console"); + if (!entry->title) { + free(entry); + goto cleanup; + } + + entry->command = strdup(""); + if (!entry->command) { + free(entry->title); + free(entry); + goto cleanup; + } + + sprintf(entry->key, "%d", i); + + entry->num = i; + entry->menu = menu; + entry->next = NULL; + + if (!iter) + menu->first = entry; + else + iter->next = entry; + + iter = entry; + ++i; + } + + menu->count = i; + return menu; + +cleanup: + bootmenu_destroy(menu); + return NULL; +} + +static void bootmenu_show(int delay) +{ + int init = 0; + void *choice = NULL; + char *title = NULL; + char *command = NULL; + struct menu *menu; + struct bootmenu_data *bootmenu; + struct bootmenu_entry *iter; + char *option, *sep; + + /* If delay is 0 do not create menu, just run first entry */ + if (delay == 0) { + option = bootmenu_getoption(0); + if (!option) { + puts("bootmenu option 0 was not found\n"); + return; + } + sep = strchr(option, '='); + if (!sep) { + puts("bootmenu option 0 is invalid\n"); + return; + } + run_command(sep+1, 0); + return; + } + + bootmenu = bootmenu_create(delay); + if (!bootmenu) + return; + + menu = menu_create(NULL, bootmenu->delay, 1, bootmenu_print_entry, + bootmenu_choice_entry, bootmenu); + if (!menu) { + bootmenu_destroy(bootmenu); + return; + } + + for (iter = bootmenu->first; iter; iter = iter->next) { + if (!menu_item_add(menu, iter->key, iter)) + goto cleanup; + } + + /* Default menu entry is always first */ + menu_default_set(menu, "0"); + + puts(ANSI_CURSOR_HIDE); + puts(ANSI_CLEAR_CONSOLE); + printf(ANSI_CURSOR_POSITION, 1, 1); + + init = 1; + + if (menu_get_choice(menu, &choice)) { + iter = choice; + title = strdup(iter->title); + command = strdup(iter->command); + } + +cleanup: + menu_destroy(menu); + bootmenu_destroy(bootmenu); + + if (init) { + puts(ANSI_CURSOR_SHOW); + puts(ANSI_CLEAR_CONSOLE); + printf(ANSI_CURSOR_POSITION, 1, 1); + } + + if (title && command) { + debug("Starting entry '%s'\n", title); + free(title); + run_command(command, 0); + free(command); + } + +#ifdef CONFIG_POSTBOOTMENU + run_command(CONFIG_POSTBOOTMENU, 0); +#endif +} + +void menu_display_statusline(struct menu *m) +{ + struct bootmenu_entry *entry; + struct bootmenu_data *menu; + + if (menu_default_choice(m, (void *)&entry) < 0) + return; + + menu = entry->menu; + + printf(ANSI_CURSOR_POSITION, 1, 1); + puts(ANSI_CLEAR_LINE); + printf(ANSI_CURSOR_POSITION, 2, 1); + puts(" *** U-Boot Boot Menu ***"); + puts(ANSI_CLEAR_LINE_TO_END); + printf(ANSI_CURSOR_POSITION, 3, 1); + puts(ANSI_CLEAR_LINE); + + /* First 3 lines are bootmenu header + 2 empty lines between entries */ + printf(ANSI_CURSOR_POSITION, menu->count + 5, 1); + puts(ANSI_CLEAR_LINE); + printf(ANSI_CURSOR_POSITION, menu->count + 6, 1); + puts(" Press UP/DOWN to move, ENTER to select"); + puts(ANSI_CLEAR_LINE_TO_END); + printf(ANSI_CURSOR_POSITION, menu->count + 7, 1); + puts(ANSI_CLEAR_LINE); +} + +#ifdef CONFIG_MENU_SHOW +int menu_show(int bootdelay) +{ + bootmenu_show(bootdelay); + return -1; /* -1 - abort boot and run monitor code */ +} +#endif + +int do_bootmenu(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[]) +{ + char *delay_str = NULL; + int delay = 10; + +#if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0) + delay = CONFIG_BOOTDELAY; +#endif + + if (argc >= 2) + delay_str = argv[1]; + + if (!delay_str) + delay_str = getenv("bootmenu_delay"); + + if (delay_str) + delay = (int)simple_strtol(delay_str, NULL, 10); + + bootmenu_show(delay); + return 0; +} + +U_BOOT_CMD( + bootmenu, 2, 1, do_bootmenu, + "ANSI terminal bootmenu", + "[delay]\n" + " - show ANSI terminal bootmenu with autoboot delay" +); diff --git a/qemu/roms/u-boot/common/cmd_bootstage.c b/qemu/roms/u-boot/common/cmd_bootstage.c new file mode 100644 index 000000000..106894aa5 --- /dev/null +++ b/qemu/roms/u-boot/common/cmd_bootstage.c @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2012, Google Inc. All rights reserved. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> + +#ifndef CONFIG_BOOTSTAGE_STASH +#define CONFIG_BOOTSTAGE_STASH -1UL +#define CONFIG_BOOTSTAGE_STASH_SIZE -1 +#endif + +static int do_bootstage_report(cmd_tbl_t *cmdtp, int flag, int argc, + char * const argv[]) +{ + bootstage_report(); + + return 0; +} + +static int get_base_size(int argc, char * const argv[], ulong *basep, + ulong *sizep) +{ + char *endp; + + *basep = CONFIG_BOOTSTAGE_STASH; + *sizep = CONFIG_BOOTSTAGE_STASH_SIZE; + if (argc < 2) + return 0; + *basep = simple_strtoul(argv[1], &endp, 16); + if (*argv[1] == 0 || *endp != 0) + return -1; + if (argc == 2) + return 0; + *sizep = simple_strtoul(argv[2], &endp, 16); + if (*argv[2] == 0 || *endp != 0) + return -1; + + return 0; +} + +static int do_bootstage_stash(cmd_tbl_t *cmdtp, int flag, int argc, + char * const argv[]) +{ + ulong base, size; + int ret; + + if (get_base_size(argc, argv, &base, &size)) + return CMD_RET_USAGE; + if (base == -1UL) { + printf("No bootstage stash area defined\n"); + return 1; + } + + if (0 == strcmp(argv[0], "stash")) + ret = bootstage_stash((void *)base, size); + else + ret = bootstage_unstash((void *)base, size); + if (ret) + return 1; + + return 0; +} + +static cmd_tbl_t cmd_bootstage_sub[] = { + U_BOOT_CMD_MKENT(report, 2, 1, do_bootstage_report, "", ""), + U_BOOT_CMD_MKENT(stash, 4, 0, do_bootstage_stash, "", ""), + U_BOOT_CMD_MKENT(unstash, 4, 0, do_bootstage_stash, "", ""), +}; + +/* + * Process a bootstage sub-command + */ +static int do_boostage(cmd_tbl_t *cmdtp, int flag, int argc, + char * const argv[]) +{ + cmd_tbl_t *c; + + /* Strip off leading 'bootstage' command argument */ + argc--; + argv++; + + c = find_cmd_tbl(argv[0], cmd_bootstage_sub, + ARRAY_SIZE(cmd_bootstage_sub)); + + if (c) + return c->cmd(cmdtp, flag, argc, argv); + else + return CMD_RET_USAGE; +} + + +U_BOOT_CMD(bootstage, 4, 1, do_boostage, + "Boot stage command", + " - check boot progress and timing\n" + "report - Print a report\n" + "stash [<start> [<size>]] - Stash data into memory\n" + "unstash [<start> [<size>]] - Unstash data from memory" +); diff --git a/qemu/roms/u-boot/common/cmd_cache.c b/qemu/roms/u-boot/common/cmd_cache.c new file mode 100644 index 000000000..602fecaba --- /dev/null +++ b/qemu/roms/u-boot/common/cmd_cache.c @@ -0,0 +1,106 @@ +/* + * (C) Copyright 2000 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/* + * Cache support: switch on or off, get status + */ +#include <common.h> +#include <command.h> +#include <linux/compiler.h> + +static int parse_argv(const char *); + +void __weak invalidate_icache_all(void) +{ + /* please define arch specific invalidate_icache_all */ + puts("No arch specific invalidate_icache_all available!\n"); +} + +int do_icache(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + switch (argc) { + case 2: /* on / off */ + switch (parse_argv(argv[1])) { + case 0: + icache_disable(); + break; + case 1: + icache_enable(); + break; + case 2: + invalidate_icache_all(); + break; + } + break; + case 1: /* get status */ + printf("Instruction Cache is %s\n", + icache_status() ? "ON" : "OFF"); + return 0; + default: + return CMD_RET_USAGE; + } + return 0; +} + +void __weak flush_dcache_all(void) +{ + puts("No arch specific flush_dcache_all available!\n"); + /* please define arch specific flush_dcache_all */ +} + +int do_dcache(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + switch (argc) { + case 2: /* on / off */ + switch (parse_argv(argv[1])) { + case 0: + dcache_disable(); + break; + case 1: + dcache_enable(); + break; + case 2: + flush_dcache_all(); + break; + } + break; + case 1: /* get status */ + printf("Data (writethrough) Cache is %s\n", + dcache_status() ? "ON" : "OFF"); + return 0; + default: + return CMD_RET_USAGE; + } + return 0; +} + +static int parse_argv(const char *s) +{ + if (strcmp(s, "flush") == 0) + return 2; + else if (strcmp(s, "on") == 0) + return 1; + else if (strcmp(s, "off") == 0) + return 0; + + return -1; +} + + +U_BOOT_CMD( + icache, 2, 1, do_icache, + "enable or disable instruction cache", + "[on, off, flush]\n" + " - enable, disable, or flush instruction cache" +); + +U_BOOT_CMD( + dcache, 2, 1, do_dcache, + "enable or disable data cache", + "[on, off, flush]\n" + " - enable, disable, or flush data (writethrough) cache" +); diff --git a/qemu/roms/u-boot/common/cmd_cbfs.c b/qemu/roms/u-boot/common/cmd_cbfs.c new file mode 100644 index 000000000..35d8a7a89 --- /dev/null +++ b/qemu/roms/u-boot/common/cmd_cbfs.c @@ -0,0 +1,196 @@ +/* + * Copyright (c) 2011 The Chromium OS Authors. All rights reserved. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/* + * CBFS commands + */ +#include <common.h> +#include <command.h> +#include <cbfs.h> + +int do_cbfs_init(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[]) +{ + uintptr_t end_of_rom = 0xffffffff; + char *ep; + + if (argc > 2) { + printf("usage: cbfsls [end of rom]>\n"); + return 0; + } + if (argc == 2) { + end_of_rom = (int)simple_strtoul(argv[1], &ep, 16); + if (*ep) { + puts("\n** Invalid end of ROM **\n"); + return 1; + } + } + file_cbfs_init(end_of_rom); + if (file_cbfs_result != CBFS_SUCCESS) { + printf("%s.\n", file_cbfs_error()); + return 1; + } + return 0; +} + +U_BOOT_CMD( + cbfsinit, 2, 0, do_cbfs_init, + "initialize the cbfs driver", + "[end of rom]\n" + " - Initialize the cbfs driver. The optional 'end of rom'\n" + " parameter specifies where the end of the ROM is that the\n" + " CBFS is in. It defaults to 0xFFFFFFFF\n" +); + +int do_cbfs_fsload(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[]) +{ + const struct cbfs_cachenode *file; + unsigned long offset; + unsigned long count; + long size; + + if (argc < 3) { + printf("usage: cbfsload <addr> <filename> [bytes]\n"); + return 1; + } + + /* parse offset and count */ + offset = simple_strtoul(argv[1], NULL, 16); + if (argc == 4) + count = simple_strtoul(argv[3], NULL, 16); + else + count = 0; + + file = file_cbfs_find(argv[2]); + if (!file) { + if (file_cbfs_result == CBFS_FILE_NOT_FOUND) + printf("%s: %s\n", file_cbfs_error(), argv[2]); + else + printf("%s.\n", file_cbfs_error()); + return 1; + } + + printf("reading %s\n", file_cbfs_name(file)); + + size = file_cbfs_read(file, (void *)offset, count); + + printf("\n%ld bytes read\n", size); + + setenv_hex("filesize", size); + + return 0; +} + +U_BOOT_CMD( + cbfsload, 4, 0, do_cbfs_fsload, + "load binary file from a cbfs filesystem", + "<addr> <filename> [bytes]\n" + " - load binary file 'filename' from the cbfs to address 'addr'\n" +); + +int do_cbfs_ls(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[]) +{ + const struct cbfs_cachenode *file = file_cbfs_get_first(); + int files = 0; + + if (!file) { + printf("%s.\n", file_cbfs_error()); + return 1; + } + + printf(" size type name\n"); + printf("------------------------------------------\n"); + while (file) { + u32 type = file_cbfs_type(file); + char *type_name = NULL; + const char *filename = file_cbfs_name(file); + + printf(" %8d", file_cbfs_size(file)); + + switch (type) { + case CBFS_TYPE_STAGE: + type_name = "stage"; + break; + case CBFS_TYPE_PAYLOAD: + type_name = "payload"; + break; + case CBFS_TYPE_OPTIONROM: + type_name = "option rom"; + break; + case CBFS_TYPE_BOOTSPLASH: + type_name = "boot splash"; + break; + case CBFS_TYPE_RAW: + type_name = "raw"; + break; + case CBFS_TYPE_VSA: + type_name = "vsa"; + break; + case CBFS_TYPE_MBI: + type_name = "mbi"; + break; + case CBFS_TYPE_MICROCODE: + type_name = "microcode"; + break; + case CBFS_COMPONENT_CMOS_DEFAULT: + type_name = "cmos default"; + break; + case CBFS_COMPONENT_CMOS_LAYOUT: + type_name = "cmos layout"; + break; + case -1UL: + type_name = "null"; + break; + } + if (type_name) + printf(" %16s", type_name); + else + printf(" %16d", type); + + if (filename[0]) + printf(" %s\n", filename); + else + printf(" %s\n", "(empty)"); + file_cbfs_get_next(&file); + files++; + } + + printf("\n%d file(s)\n\n", files); + return 0; +} + +U_BOOT_CMD( + cbfsls, 1, 1, do_cbfs_ls, + "list files", + " - list the files in the cbfs\n" +); + +int do_cbfs_fsinfo(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[]) +{ + const struct cbfs_header *header = file_cbfs_get_header(); + + if (!header) { + printf("%s.\n", file_cbfs_error()); + return 1; + } + + printf("\n"); + printf("CBFS version: %#x\n", header->version); + printf("ROM size: %#x\n", header->rom_size); + printf("Boot block size: %#x\n", header->boot_block_size); + printf("CBFS size: %#x\n", + header->rom_size - header->boot_block_size - header->offset); + printf("Alignment: %d\n", header->align); + printf("Offset: %#x\n", header->offset); + printf("\n"); + + return 0; +} + +U_BOOT_CMD( + cbfsinfo, 1, 1, do_cbfs_fsinfo, + "print information about filesystem", + " - print information about the cbfs filesystem\n" +); diff --git a/qemu/roms/u-boot/common/cmd_clk.c b/qemu/roms/u-boot/common/cmd_clk.c new file mode 100644 index 000000000..6d3d46a18 --- /dev/null +++ b/qemu/roms/u-boot/common/cmd_clk.c @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2013 Xilinx, Inc. + * + * SPDX-License-Identifier: GPL-2.0+ + */ +#include <common.h> +#include <command.h> +#include <clk.h> + +int __weak soc_clk_dump(void) +{ + puts("Not implemented\n"); + return 1; +} + +static int do_clk_dump(cmd_tbl_t *cmdtp, int flag, int argc, + char *const argv[]) +{ + return soc_clk_dump(); +} + +static cmd_tbl_t cmd_clk_sub[] = { + U_BOOT_CMD_MKENT(dump, 1, 1, do_clk_dump, "", ""), +}; + +static int do_clk(cmd_tbl_t *cmdtp, int flag, int argc, + char *const argv[]) +{ + cmd_tbl_t *c; + + if (argc < 2) + return CMD_RET_USAGE; + + /* Strip off leading 'clk' command argument */ + argc--; + argv++; + + c = find_cmd_tbl(argv[0], &cmd_clk_sub[0], ARRAY_SIZE(cmd_clk_sub)); + + if (c) + return c->cmd(cmdtp, flag, argc, argv); + else + return CMD_RET_USAGE; +} + +#ifdef CONFIG_SYS_LONGHELP +static char clk_help_text[] = + "dump - Print clock frequencies"; +#endif + +U_BOOT_CMD(clk, 2, 1, do_clk, "CLK sub-system", clk_help_text); diff --git a/qemu/roms/u-boot/common/cmd_console.c b/qemu/roms/u-boot/common/cmd_console.c new file mode 100644 index 000000000..93dc50505 --- /dev/null +++ b/qemu/roms/u-boot/common/cmd_console.c @@ -0,0 +1,54 @@ +/* + * (C) Copyright 2000 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/* + * Boot support + */ +#include <common.h> +#include <command.h> +#include <stdio_dev.h> + +extern void _do_coninfo (void); +static int do_coninfo(cmd_tbl_t *cmd, int flag, int argc, char * const argv[]) +{ + int l; + struct list_head *list = stdio_get_list(); + struct list_head *pos; + struct stdio_dev *dev; + + /* Scan for valid output and input devices */ + + puts ("List of available devices:\n"); + + list_for_each(pos, list) { + dev = list_entry(pos, struct stdio_dev, list); + + printf ("%-8s %08x %c%c%c ", + dev->name, + dev->flags, + (dev->flags & DEV_FLAGS_SYSTEM) ? 'S' : '.', + (dev->flags & DEV_FLAGS_INPUT) ? 'I' : '.', + (dev->flags & DEV_FLAGS_OUTPUT) ? 'O' : '.'); + + for (l = 0; l < MAX_FILES; l++) { + if (stdio_devices[l] == dev) { + printf ("%s ", stdio_names[l]); + } + } + putc ('\n'); + } + return 0; +} + + +/***************************************************/ + +U_BOOT_CMD( + coninfo, 3, 1, do_coninfo, + "print console devices and information", + "" +); diff --git a/qemu/roms/u-boot/common/cmd_cplbinfo.c b/qemu/roms/u-boot/common/cmd_cplbinfo.c new file mode 100644 index 000000000..ab5b3b587 --- /dev/null +++ b/qemu/roms/u-boot/common/cmd_cplbinfo.c @@ -0,0 +1,60 @@ +/* + * cmd_cplbinfo.c - dump the instruction/data cplb tables + * + * Copyright (c) 2007-2008 Analog Devices Inc. + * + * Licensed under the GPL-2 or later. + */ + +#include <common.h> +#include <command.h> +#include <asm/blackfin.h> +#include <asm/cplb.h> +#include <asm/mach-common/bits/mpu.h> + +/* + * Translate the PAGE_SIZE bits into a human string + */ +static const char *cplb_page_size(uint32_t data) +{ + static const char page_size_string_table[][4] = { "1K", "4K", "1M", "4M" }; + return page_size_string_table[(data & PAGE_SIZE_MASK) >> PAGE_SIZE_SHIFT]; +} + +/* + * show a hardware cplb table + */ +static void show_cplb_table(uint32_t *addr, uint32_t *data) +{ + int i; + printf(" Address Data Size Valid Locked\n"); + for (i = 1; i <= 16; ++i) { + printf(" %2i 0x%p 0x%05X %s %c %c\n", + i, (void *)*addr, *data, + cplb_page_size(*data), + (*data & CPLB_VALID ? 'Y' : 'N'), + (*data & CPLB_LOCK ? 'Y' : 'N')); + ++addr; + ++data; + } +} + +/* + * display current instruction and data cplb tables + */ +int do_cplbinfo(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + printf("%s CPLB table [%08x]:\n", "Instruction", *(uint32_t *)DMEM_CONTROL); + show_cplb_table((uint32_t *)ICPLB_ADDR0, (uint32_t *)ICPLB_DATA0); + + printf("%s CPLB table [%08x]:\n", "Data", *(uint32_t *)IMEM_CONTROL); + show_cplb_table((uint32_t *)DCPLB_ADDR0, (uint32_t *)DCPLB_DATA0); + + return 0; +} + +U_BOOT_CMD( + cplbinfo, 1, 0, do_cplbinfo, + "display current CPLB tables", + "" +); diff --git a/qemu/roms/u-boot/common/cmd_cramfs.c b/qemu/roms/u-boot/common/cmd_cramfs.c new file mode 100644 index 000000000..1d31326bd --- /dev/null +++ b/qemu/roms/u-boot/common/cmd_cramfs.c @@ -0,0 +1,207 @@ +/* + * SPDX-License-Identifier: GPL-2.0+ + * + * based on: cmd_jffs2.c + * + * Add support for a CRAMFS located in RAM + */ + + +/* + * CRAMFS support + */ +#include <common.h> +#include <command.h> +#include <malloc.h> +#include <linux/list.h> +#include <linux/ctype.h> +#include <jffs2/jffs2.h> +#include <jffs2/load_kernel.h> +#include <cramfs/cramfs_fs.h> + +/* enable/disable debugging messages */ +#define DEBUG_CRAMFS +#undef DEBUG_CRAMFS + +#ifdef DEBUG_CRAMFS +# define DEBUGF(fmt, args...) printf(fmt ,##args) +#else +# define DEBUGF(fmt, args...) +#endif + +#ifdef CONFIG_CRAMFS_CMDLINE +#include <flash.h> + +#ifdef CONFIG_SYS_NO_FLASH +# define OFFSET_ADJUSTMENT 0 +#else +# define OFFSET_ADJUSTMENT (flash_info[id.num].start[0]) +#endif + +#ifndef CONFIG_CMD_JFFS2 +#include <linux/stat.h> +char *mkmodestr(unsigned long mode, char *str) +{ + static const char *l = "xwr"; + int mask = 1, i; + char c; + + switch (mode & S_IFMT) { + case S_IFDIR: str[0] = 'd'; break; + case S_IFBLK: str[0] = 'b'; break; + case S_IFCHR: str[0] = 'c'; break; + case S_IFIFO: str[0] = 'f'; break; + case S_IFLNK: str[0] = 'l'; break; + case S_IFSOCK: str[0] = 's'; break; + case S_IFREG: str[0] = '-'; break; + default: str[0] = '?'; + } + + for(i = 0; i < 9; i++) { + c = l[i%3]; + str[9-i] = (mode & mask)?c:'-'; + mask = mask<<1; + } + + if(mode & S_ISUID) str[3] = (mode & S_IXUSR)?'s':'S'; + if(mode & S_ISGID) str[6] = (mode & S_IXGRP)?'s':'S'; + if(mode & S_ISVTX) str[9] = (mode & S_IXOTH)?'t':'T'; + str[10] = '\0'; + return str; +} +#endif /* CONFIG_CMD_JFFS2 */ + +extern int cramfs_check (struct part_info *info); +extern int cramfs_load (char *loadoffset, struct part_info *info, char *filename); +extern int cramfs_ls (struct part_info *info, char *filename); +extern int cramfs_info (struct part_info *info); + +/***************************************************/ +/* U-boot commands */ +/***************************************************/ + +/** + * Routine implementing fsload u-boot command. This routine tries to load + * a requested file from cramfs filesystem at location 'cramfsaddr'. + * cramfsaddr is an evironment variable. + * + * @param cmdtp command internal data + * @param flag command flag + * @param argc number of arguments supplied to the command + * @param argv arguments list + * @return 0 on success, 1 otherwise + */ +int do_cramfs_load(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + char *filename; + int size; + ulong offset = load_addr; + + struct part_info part; + struct mtd_device dev; + struct mtdids id; + + ulong addr; + addr = simple_strtoul(getenv("cramfsaddr"), NULL, 16); + + /* hack! */ + /* cramfs_* only supports NOR flash chips */ + /* fake the device type */ + id.type = MTD_DEV_TYPE_NOR; + id.num = 0; + dev.id = &id; + part.dev = &dev; + /* fake the address offset */ + part.offset = addr - OFFSET_ADJUSTMENT; + + /* pre-set Boot file name */ + if ((filename = getenv("bootfile")) == NULL) { + filename = "uImage"; + } + + if (argc == 2) { + filename = argv[1]; + } + if (argc == 3) { + offset = simple_strtoul(argv[1], NULL, 0); + load_addr = offset; + filename = argv[2]; + } + + size = 0; + if (cramfs_check(&part)) + size = cramfs_load ((char *) offset, &part, filename); + + if (size > 0) { + printf("### CRAMFS load complete: %d bytes loaded to 0x%lx\n", + size, offset); + setenv_hex("filesize", size); + } else { + printf("### CRAMFS LOAD ERROR<%x> for %s!\n", size, filename); + } + + return !(size > 0); +} + +/** + * Routine implementing u-boot ls command which lists content of a given + * directory at location 'cramfsaddr'. + * cramfsaddr is an evironment variable. + * + * @param cmdtp command internal data + * @param flag command flag + * @param argc number of arguments supplied to the command + * @param argv arguments list + * @return 0 on success, 1 otherwise + */ +int do_cramfs_ls(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + char *filename = "/"; + int ret; + struct part_info part; + struct mtd_device dev; + struct mtdids id; + + ulong addr; + addr = simple_strtoul(getenv("cramfsaddr"), NULL, 16); + + /* hack! */ + /* cramfs_* only supports NOR flash chips */ + /* fake the device type */ + id.type = MTD_DEV_TYPE_NOR; + id.num = 0; + dev.id = &id; + part.dev = &dev; + /* fake the address offset */ + part.offset = addr - OFFSET_ADJUSTMENT; + + if (argc == 2) + filename = argv[1]; + + ret = 0; + if (cramfs_check(&part)) + ret = cramfs_ls (&part, filename); + + return ret ? 0 : 1; +} + +/* command line only */ + +/***************************************************/ +U_BOOT_CMD( + cramfsload, 3, 0, do_cramfs_load, + "load binary file from a filesystem image", + "[ off ] [ filename ]\n" + " - load binary file from address 'cramfsaddr'\n" + " with offset 'off'\n" +); +U_BOOT_CMD( + cramfsls, 2, 1, do_cramfs_ls, + "list files in a directory (default /)", + "[ directory ]\n" + " - list files in a directory.\n" +); + +#endif /* #ifdef CONFIG_CRAMFS_CMDLINE */ + +/***************************************************/ diff --git a/qemu/roms/u-boot/common/cmd_dataflash_mmc_mux.c b/qemu/roms/u-boot/common/cmd_dataflash_mmc_mux.c new file mode 100644 index 000000000..3832248cb --- /dev/null +++ b/qemu/roms/u-boot/common/cmd_dataflash_mmc_mux.c @@ -0,0 +1,48 @@ +/* + * (C) Copyright 2000 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <command.h> + +static int mmc_nspi (const char *); + +int do_dataflash_mmc_mux (cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + switch (argc) { + case 2: /* on / off */ + switch (mmc_nspi (argv[1])) { + case 0: AT91F_SelectSPI (); + break; + case 1: AT91F_SelectMMC (); + break; + } + case 1: /* get status */ + printf ("Mux is configured to be %s\n", + AT91F_GetMuxStatus () ? "MMC" : "SPI"); + return 0; + default: + return CMD_RET_USAGE; + } + return 0; +} + +static int mmc_nspi (const char *s) +{ + if (strcmp (s, "mmc") == 0) { + return 1; + } else if (strcmp (s, "spi") == 0) { + return 0; + } + return -1; +} + +U_BOOT_CMD( + dataflash_mmc_mux, 2, 1, do_dataflash_mmc_mux, + "enable or disable MMC or SPI\n", + "[mmc, spi]\n" + " - enable or disable MMC or SPI" +); diff --git a/qemu/roms/u-boot/common/cmd_date.c b/qemu/roms/u-boot/common/cmd_date.c new file mode 100644 index 000000000..e3491662b --- /dev/null +++ b/qemu/roms/u-boot/common/cmd_date.c @@ -0,0 +1,218 @@ +/* + * (C) Copyright 2001 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/* + * RTC, Date & Time support: get and set date & time + */ +#include <common.h> +#include <command.h> +#include <rtc.h> +#include <i2c.h> + +DECLARE_GLOBAL_DATA_PTR; + +static const char * const weekdays[] = { + "Sun", "Mon", "Tues", "Wednes", "Thurs", "Fri", "Satur", +}; + +#ifdef CONFIG_NEEDS_MANUAL_RELOC +#define RELOC(a) ((typeof(a))((unsigned long)(a) + gd->reloc_off)) +#else +#define RELOC(a) a +#endif + +int mk_date (const char *, struct rtc_time *); + +static int do_date(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + struct rtc_time tm; + int rcode = 0; + int old_bus; + + /* switch to correct I2C bus */ +#ifdef CONFIG_SYS_I2C + old_bus = i2c_get_bus_num(); + i2c_set_bus_num(CONFIG_SYS_RTC_BUS_NUM); +#else + old_bus = I2C_GET_BUS(); + I2C_SET_BUS(CONFIG_SYS_RTC_BUS_NUM); +#endif + + switch (argc) { + case 2: /* set date & time */ + if (strcmp(argv[1],"reset") == 0) { + puts ("Reset RTC...\n"); + rtc_reset (); + } else { + /* initialize tm with current time */ + rcode = rtc_get (&tm); + + if(!rcode) { + /* insert new date & time */ + if (mk_date (argv[1], &tm) != 0) { + puts ("## Bad date format\n"); + break; + } + /* and write to RTC */ + rcode = rtc_set (&tm); + if(rcode) + puts("## Set date failed\n"); + } else { + puts("## Get date failed\n"); + } + } + /* FALL TROUGH */ + case 1: /* get date & time */ + rcode = rtc_get (&tm); + + if (rcode) { + puts("## Get date failed\n"); + break; + } + + printf ("Date: %4d-%02d-%02d (%sday) Time: %2d:%02d:%02d\n", + tm.tm_year, tm.tm_mon, tm.tm_mday, + (tm.tm_wday<0 || tm.tm_wday>6) ? + "unknown " : RELOC(weekdays[tm.tm_wday]), + tm.tm_hour, tm.tm_min, tm.tm_sec); + + break; + default: + rcode = CMD_RET_USAGE; + } + + /* switch back to original I2C bus */ +#ifdef CONFIG_SYS_I2C + i2c_set_bus_num(old_bus); +#else + I2C_SET_BUS(old_bus); +#endif + + return rcode; +} + +/* + * simple conversion of two-digit string with error checking + */ +static int cnvrt2 (const char *str, int *valp) +{ + int val; + + if ((*str < '0') || (*str > '9')) + return (-1); + + val = *str - '0'; + + ++str; + + if ((*str < '0') || (*str > '9')) + return (-1); + + *valp = 10 * val + (*str - '0'); + + return (0); +} + +/* + * Convert date string: MMDDhhmm[[CC]YY][.ss] + * + * Some basic checking for valid values is done, but this will not catch + * all possible error conditions. + */ +int mk_date (const char *datestr, struct rtc_time *tmp) +{ + int len, val; + char *ptr; + + ptr = strchr (datestr,'.'); + len = strlen (datestr); + + /* Set seconds */ + if (ptr) { + int sec; + + *ptr++ = '\0'; + if ((len - (ptr - datestr)) != 2) + return (-1); + + len = strlen (datestr); + + if (cnvrt2 (ptr, &sec)) + return (-1); + + tmp->tm_sec = sec; + } else { + tmp->tm_sec = 0; + } + + if (len == 12) { /* MMDDhhmmCCYY */ + int year, century; + + if (cnvrt2 (datestr+ 8, ¢ury) || + cnvrt2 (datestr+10, &year) ) { + return (-1); + } + tmp->tm_year = 100 * century + year; + } else if (len == 10) { /* MMDDhhmmYY */ + int year, century; + + century = tmp->tm_year / 100; + if (cnvrt2 (datestr+ 8, &year)) + return (-1); + tmp->tm_year = 100 * century + year; + } + + switch (len) { + case 8: /* MMDDhhmm */ + /* fall thru */ + case 10: /* MMDDhhmmYY */ + /* fall thru */ + case 12: /* MMDDhhmmCCYY */ + if (cnvrt2 (datestr+0, &val) || + val > 12) { + break; + } + tmp->tm_mon = val; + if (cnvrt2 (datestr+2, &val) || + val > ((tmp->tm_mon==2) ? 29 : 31)) { + break; + } + tmp->tm_mday = val; + + if (cnvrt2 (datestr+4, &val) || + val > 23) { + break; + } + tmp->tm_hour = val; + + if (cnvrt2 (datestr+6, &val) || + val > 59) { + break; + } + tmp->tm_min = val; + + /* calculate day of week */ + GregorianDay (tmp); + + return (0); + default: + break; + } + + return (-1); +} + +/***************************************************/ + +U_BOOT_CMD( + date, 2, 1, do_date, + "get/set/reset date & time", + "[MMDDhhmm[[CC]YY][.ss]]\ndate reset\n" + " - without arguments: print date & time\n" + " - with numeric argument: set the system date & time\n" + " - with 'reset' argument: reset the RTC" +); diff --git a/qemu/roms/u-boot/common/cmd_dcr.c b/qemu/roms/u-boot/common/cmd_dcr.c new file mode 100644 index 000000000..896f79f41 --- /dev/null +++ b/qemu/roms/u-boot/common/cmd_dcr.c @@ -0,0 +1,220 @@ +/* + * (C) Copyright 2001 + * Erik Theisen, Wave 7 Optics, etheisen@mindspring.com. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/* + * AMCC 4XX DCR Functions + */ + +#include <common.h> +#include <config.h> +#include <command.h> + +unsigned long get_dcr (unsigned short); +unsigned long set_dcr (unsigned short, unsigned long); + +/* ======================================================================= + * Interpreter command to retrieve an AMCC PPC 4xx Device Control Register + * ======================================================================= + */ +int do_getdcr ( cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[] ) +{ + unsigned short dcrn; /* Device Control Register Num */ + unsigned long value; /* DCR's value */ + + unsigned long get_dcr (unsigned short); + + /* Validate arguments */ + if (argc < 2) + return CMD_RET_USAGE; + + /* Get a DCR */ + dcrn = (unsigned short) simple_strtoul (argv[1], NULL, 16); + value = get_dcr (dcrn); + + printf ("%04x: %08lx\n", dcrn, value); + + return 0; +} + + +/* ====================================================================== + * Interpreter command to set an AMCC PPC 4xx Device Control Register + * ====================================================================== +*/ +int do_setdcr (cmd_tbl_t * cmdtp, int flag, int argc, char * const argv[]) +{ + unsigned short dcrn; /* Device Control Register Num */ + unsigned long value; + + /* DCR's value */ + int nbytes; + + /* Validate arguments */ + if (argc < 2) + return CMD_RET_USAGE; + + /* Set a DCR */ + dcrn = (unsigned short) simple_strtoul (argv[1], NULL, 16); + do { + value = get_dcr (dcrn); + printf ("%04x: %08lx", dcrn, value); + nbytes = readline (" ? "); + if (nbytes == 0) { + /* + * <CR> pressed as only input, don't modify current + * location and exit command. + */ + nbytes = 1; + return 0; + } else { + unsigned long i; + char *endp; + + i = simple_strtoul (console_buffer, &endp, 16); + nbytes = endp - console_buffer; + if (nbytes) + set_dcr (dcrn, i); + } + } while (nbytes); + + return 0; +} + +/* ======================================================================= + * Interpreter command to retrieve an register value through AMCC PPC 4xx + * Device Control Register inderect addressing. + * ======================================================================= + */ +int do_getidcr (cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + unsigned short adr_dcrn; /* Device Control Register Num for Address */ + unsigned short dat_dcrn; /* Device Control Register Num for Data */ + unsigned short offset; /* Register's offset */ + unsigned long value; /* Register's value */ + char *ptr = NULL; + char buf[80]; + + /* Validate arguments */ + if (argc < 3) + return CMD_RET_USAGE; + + /* Find out whether ther is '.' (dot) symbol in the first parameter. */ + strncpy (buf, argv[1], sizeof(buf)-1); + buf[sizeof(buf)-1] = 0; /* will guarantee zero-end string */ + ptr = strchr (buf, '.'); + + if (ptr != NULL) { + /* First parameter has format adr_dcrn.dat_dcrn */ + *ptr++ = 0; /* erase '.', create zero-end string */ + adr_dcrn = (unsigned short) simple_strtoul (buf, NULL, 16); + dat_dcrn = (unsigned short) simple_strtoul (ptr, NULL, 16); + } else { + /* + * First parameter has format adr_dcrn; dat_dcrn will be + * calculated as adr_dcrn+1. + */ + adr_dcrn = (unsigned short) simple_strtoul (buf, NULL, 16); + dat_dcrn = adr_dcrn+1; + } + + /* Register's offset */ + offset = (unsigned short) simple_strtoul (argv[2], NULL, 16); + + /* Disable interrupts */ + disable_interrupts (); + /* Set offset */ + set_dcr (adr_dcrn, offset); + /* get data */ + value = get_dcr (dat_dcrn); + /* Enable interrupts */ + enable_interrupts (); + + printf ("%04x.%04x-%04x Read %08lx\n", adr_dcrn, dat_dcrn, offset, value); + + return 0; +} + +/* ======================================================================= + * Interpreter command to update an register value through AMCC PPC 4xx + * Device Control Register inderect addressing. + * ======================================================================= + */ +int do_setidcr (cmd_tbl_t * cmdtp, int flag, int argc, char * const argv[]) +{ + unsigned short adr_dcrn; /* Device Control Register Num for Address */ + unsigned short dat_dcrn; /* Device Control Register Num for Data */ + unsigned short offset; /* Register's offset */ + unsigned long value; /* Register's value */ + char *ptr = NULL; + char buf[80]; + + /* Validate arguments */ + if (argc < 4) + return CMD_RET_USAGE; + + /* Find out whether ther is '.' (dot) symbol in the first parameter. */ + strncpy (buf, argv[1], sizeof(buf)-1); + buf[sizeof(buf)-1] = 0; /* will guarantee zero-end string */ + ptr = strchr (buf, '.'); + + if (ptr != NULL) { + /* First parameter has format adr_dcrn.dat_dcrn */ + *ptr++ = 0; /* erase '.', create zero-end string */ + adr_dcrn = (unsigned short) simple_strtoul (buf, NULL, 16); + dat_dcrn = (unsigned short) simple_strtoul (ptr, NULL, 16); + } else { + /* + * First parameter has format adr_dcrn; dat_dcrn will be + * calculated as adr_dcrn+1. + */ + adr_dcrn = (unsigned short) simple_strtoul (buf, NULL, 16); + dat_dcrn = adr_dcrn+1; + } + + /* Register's offset */ + offset = (unsigned short) simple_strtoul (argv[2], NULL, 16); + /* New value */ + value = (unsigned long) simple_strtoul (argv[3], NULL, 16); + + /* Disable interrupts */ + disable_interrupts (); + /* Set offset */ + set_dcr (adr_dcrn, offset); + /* set data */ + set_dcr (dat_dcrn, value); + /* Enable interrupts */ + enable_interrupts (); + + printf ("%04x.%04x-%04x Write %08lx\n", adr_dcrn, dat_dcrn, offset, value); + + return 0; +} + +/***************************************************/ + +U_BOOT_CMD( + getdcr, 2, 1, do_getdcr, + "Get an AMCC PPC 4xx DCR's value", + "dcrn - return a DCR's value." +); +U_BOOT_CMD( + setdcr, 2, 1, do_setdcr, + "Set an AMCC PPC 4xx DCR's value", + "dcrn - set a DCR's value." +); + +U_BOOT_CMD( + getidcr, 3, 1, do_getidcr, + "Get a register value via indirect DCR addressing", + "adr_dcrn[.dat_dcrn] offset - write offset to adr_dcrn, read value from dat_dcrn." +); + +U_BOOT_CMD( + setidcr, 4, 1, do_setidcr, + "Set a register value via indirect DCR addressing", + "adr_dcrn[.dat_dcrn] offset value - write offset to adr_dcrn, write value to dat_dcrn." +); diff --git a/qemu/roms/u-boot/common/cmd_demo.c b/qemu/roms/u-boot/common/cmd_demo.c new file mode 100644 index 000000000..a3bba7fdf --- /dev/null +++ b/qemu/roms/u-boot/common/cmd_demo.c @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2013 Google, Inc + * + * (C) Copyright 2012 + * Pavel Herrmann <morpheus.ibis@gmail.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <dm-demo.h> +#include <asm/io.h> + +struct device *demo_dev; + +static int do_demo_hello(cmd_tbl_t *cmdtp, int flag, int argc, + char * const argv[]) +{ + int ch = 0; + + if (argc) + ch = *argv[0]; + + return demo_hello(demo_dev, ch); +} + +static int do_demo_status(cmd_tbl_t *cmdtp, int flag, int argc, + char * const argv[]) +{ + int status; + int ret; + + ret = demo_status(demo_dev, &status); + if (ret) + return ret; + + printf("Status: %d\n", status); + + return 0; +} + +int do_demo_list(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + struct device *dev; + int i, ret; + + puts("Demo uclass entries:\n"); + + for (i = 0, ret = uclass_first_device(UCLASS_DEMO, &dev); + dev; + ret = uclass_next_device(&dev)) { + printf("entry %d - instance %08x, ops %08x, platdata %08x\n", + i++, map_to_sysmem(dev), + map_to_sysmem(dev->driver->ops), + map_to_sysmem(dev_get_platdata(dev))); + } + + return cmd_process_error(cmdtp, ret); +} + +static cmd_tbl_t demo_commands[] = { + U_BOOT_CMD_MKENT(list, 0, 1, do_demo_list, "", ""), + U_BOOT_CMD_MKENT(hello, 2, 1, do_demo_hello, "", ""), + U_BOOT_CMD_MKENT(status, 1, 1, do_demo_status, "", ""), +}; + +static int do_demo(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + cmd_tbl_t *demo_cmd; + int devnum = 0; + int ret; + + if (argc < 2) + return CMD_RET_USAGE; + demo_cmd = find_cmd_tbl(argv[1], demo_commands, + ARRAY_SIZE(demo_commands)); + argc -= 2; + argv += 2; + if (!demo_cmd || argc > demo_cmd->maxargs) + return CMD_RET_USAGE; + + if (argc) { + devnum = simple_strtoul(argv[0], NULL, 10); + ret = uclass_get_device(UCLASS_DEMO, devnum, &demo_dev); + if (ret) + return cmd_process_error(cmdtp, ret); + argc--; + argv++; + } + + ret = demo_cmd->cmd(demo_cmd, flag, argc, argv); + + return cmd_process_error(demo_cmd, ret); +} + +U_BOOT_CMD( + demo, 4, 1, do_demo, + "Driver model (dm) demo operations", + "list List available demo devices\n" + "demo hello <num> [<char>] Say hello\n" + "demo status <num> Get demo device status" +); diff --git a/qemu/roms/u-boot/common/cmd_dfu.c b/qemu/roms/u-boot/common/cmd_dfu.c new file mode 100644 index 000000000..a03538dab --- /dev/null +++ b/qemu/roms/u-boot/common/cmd_dfu.c @@ -0,0 +1,74 @@ +/* + * cmd_dfu.c -- dfu command + * + * Copyright (C) 2012 Samsung Electronics + * authors: Andrzej Pietrasiewicz <andrzej.p@samsung.com> + * Lukasz Majewski <l.majewski@samsung.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <dfu.h> +#include <g_dnl.h> +#include <usb.h> + +static int do_dfu(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + if (argc < 4) + return CMD_RET_USAGE; + + char *usb_controller = argv[1]; + char *interface = argv[2]; + char *devstring = argv[3]; + + int ret, i = 0; + + ret = dfu_init_env_entities(interface, simple_strtoul(devstring, + NULL, 10)); + if (ret) + return ret; + + if (argc > 4 && strcmp(argv[4], "list") == 0) { + dfu_show_entities(); + goto done; + } + + int controller_index = simple_strtoul(usb_controller, NULL, 0); + board_usb_init(controller_index, USB_INIT_DEVICE); + + g_dnl_register("usb_dnl_dfu"); + while (1) { + if (dfu_reset()) + /* + * This extra number of usb_gadget_handle_interrupts() + * calls is necessary to assure correct transmission + * completion with dfu-util + */ + if (++i == 10) + goto exit; + + if (ctrlc()) + goto exit; + + usb_gadget_handle_interrupts(); + } +exit: + g_dnl_unregister(); +done: + dfu_free_entities(); + + if (dfu_reset()) + run_command("reset", 0); + + return CMD_RET_SUCCESS; +} + +U_BOOT_CMD(dfu, CONFIG_SYS_MAXARGS, 1, do_dfu, + "Device Firmware Upgrade", + "<USB_controller> <interface> <dev> [list]\n" + " - device firmware upgrade via <USB_controller>\n" + " on device <dev>, attached to interface\n" + " <interface>\n" + " [list] - list available alt settings\n" +); diff --git a/qemu/roms/u-boot/common/cmd_diag.c b/qemu/roms/u-boot/common/cmd_diag.c new file mode 100644 index 000000000..14ae04f2a --- /dev/null +++ b/qemu/roms/u-boot/common/cmd_diag.c @@ -0,0 +1,60 @@ +/* + * (C) Copyright 2002 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/* + * Diagnostics support + */ +#include <common.h> +#include <command.h> +#include <post.h> + +int do_diag (cmd_tbl_t * cmdtp, int flag, int argc, char * const argv[]) +{ + unsigned int i; + + if (argc == 1 || strcmp (argv[1], "run") != 0) { + /* List test info */ + if (argc == 1) { + puts ("Available hardware tests:\n"); + post_info (NULL); + puts ("Use 'diag [<test1> [<test2> ...]]'" + " to get more info.\n"); + puts ("Use 'diag run [<test1> [<test2> ...]]'" + " to run tests.\n"); + } else { + for (i = 1; i < argc; i++) { + if (post_info (argv[i]) != 0) + printf ("%s - no such test\n", argv[i]); + } + } + } else { + /* Run tests */ + if (argc == 2) { + post_run (NULL, POST_RAM | POST_MANUAL); + } else { + for (i = 2; i < argc; i++) { + if (post_run (argv[i], POST_RAM | POST_MANUAL) != 0) + printf ("%s - unable to execute the test\n", + argv[i]); + } + } + } + + return 0; +} +/***************************************************/ + +U_BOOT_CMD( + diag, CONFIG_SYS_MAXARGS, 0, do_diag, + "perform board diagnostics", + " - print list of available tests\n" + "diag [test1 [test2]]\n" + " - print information about specified tests\n" + "diag run - run all available tests\n" + "diag run [test1 [test2]]\n" + " - run specified tests" +); diff --git a/qemu/roms/u-boot/common/cmd_disk.c b/qemu/roms/u-boot/common/cmd_disk.c new file mode 100644 index 000000000..3e457f672 --- /dev/null +++ b/qemu/roms/u-boot/common/cmd_disk.c @@ -0,0 +1,128 @@ +/* + * (C) Copyright 2000-2011 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * SPDX-License-Identifier: GPL-2.0+ + */ +#include <common.h> +#include <command.h> +#include <part.h> + +#if defined(CONFIG_CMD_IDE) || defined(CONFIG_CMD_SCSI) || \ + defined(CONFIG_USB_STORAGE) +int common_diskboot(cmd_tbl_t *cmdtp, const char *intf, int argc, + char *const argv[]) +{ + int dev, part; + ulong addr = CONFIG_SYS_LOAD_ADDR; + ulong cnt; + disk_partition_t info; + image_header_t *hdr; + block_dev_desc_t *dev_desc; + +#if defined(CONFIG_FIT) + const void *fit_hdr = NULL; +#endif + + bootstage_mark(BOOTSTAGE_ID_IDE_START); + if (argc > 3) { + bootstage_error(BOOTSTAGE_ID_IDE_ADDR); + return CMD_RET_USAGE; + } + bootstage_mark(BOOTSTAGE_ID_IDE_ADDR); + + if (argc > 1) + addr = simple_strtoul(argv[1], NULL, 16); + + bootstage_mark(BOOTSTAGE_ID_IDE_BOOT_DEVICE); + + part = get_device_and_partition(intf, (argc == 3) ? argv[2] : NULL, + &dev_desc, &info, 1); + if (part < 0) { + bootstage_error(BOOTSTAGE_ID_IDE_TYPE); + return 1; + } + + dev = dev_desc->dev; + bootstage_mark(BOOTSTAGE_ID_IDE_TYPE); + + printf("\nLoading from %s device %d, partition %d: " + "Name: %.32s Type: %.32s\n", intf, dev, part, info.name, + info.type); + + debug("First Block: " LBAFU ", # of blocks: " LBAFU + ", Block Size: %ld\n", + info.start, info.size, info.blksz); + + if (dev_desc->block_read(dev, info.start, 1, (ulong *) addr) != 1) { + printf("** Read error on %d:%d\n", dev, part); + bootstage_error(BOOTSTAGE_ID_IDE_PART_READ); + return 1; + } + bootstage_mark(BOOTSTAGE_ID_IDE_PART_READ); + + switch (genimg_get_format((void *) addr)) { + case IMAGE_FORMAT_LEGACY: + hdr = (image_header_t *) addr; + + bootstage_mark(BOOTSTAGE_ID_IDE_FORMAT); + + if (!image_check_hcrc(hdr)) { + puts("\n** Bad Header Checksum **\n"); + bootstage_error(BOOTSTAGE_ID_IDE_CHECKSUM); + return 1; + } + bootstage_mark(BOOTSTAGE_ID_IDE_CHECKSUM); + + image_print_contents(hdr); + + cnt = image_get_image_size(hdr); + break; +#if defined(CONFIG_FIT) + case IMAGE_FORMAT_FIT: + fit_hdr = (const void *) addr; + puts("Fit image detected...\n"); + + cnt = fit_get_size(fit_hdr); + break; +#endif + default: + bootstage_error(BOOTSTAGE_ID_IDE_FORMAT); + puts("** Unknown image type\n"); + return 1; + } + + cnt += info.blksz - 1; + cnt /= info.blksz; + cnt -= 1; + + if (dev_desc->block_read(dev, info.start + 1, cnt, + (ulong *)(addr + info.blksz)) != cnt) { + printf("** Read error on %d:%d\n", dev, part); + bootstage_error(BOOTSTAGE_ID_IDE_READ); + return 1; + } + bootstage_mark(BOOTSTAGE_ID_IDE_READ); + +#if defined(CONFIG_FIT) + /* This cannot be done earlier, + * we need complete FIT image in RAM first */ + if (genimg_get_format((void *) addr) == IMAGE_FORMAT_FIT) { + if (!fit_check_format(fit_hdr)) { + bootstage_error(BOOTSTAGE_ID_IDE_FIT_READ); + puts("** Bad FIT image format\n"); + return 1; + } + bootstage_mark(BOOTSTAGE_ID_IDE_FIT_READ_OK); + fit_print_contents(fit_hdr); + } +#endif + + flush_cache(addr, (cnt+1)*info.blksz); + + /* Loading ok, update default load address */ + load_addr = addr; + + return bootm_maybe_autostart(cmdtp, argv[0]); +} +#endif diff --git a/qemu/roms/u-boot/common/cmd_display.c b/qemu/roms/u-boot/common/cmd_display.c new file mode 100644 index 000000000..bc1b1ebb8 --- /dev/null +++ b/qemu/roms/u-boot/common/cmd_display.c @@ -0,0 +1,54 @@ +/* + * (C) Copyright 2005 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <command.h> +#include <led-display.h> + +#undef DEBUG_DISP + +int do_display (cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + int i; + + /* Clear display */ + display_set(DISPLAY_CLEAR | DISPLAY_HOME); + + if (argc < 2) + return (0); + + for (i = 1; i < argc; i++) { + char *p = argv[i]; + + if (i > 1) { /* Insert a space between strings */ + display_putc(' '); + } + + while ((*p)) { +#ifdef DEBUG_DISP + putc(*p); +#endif + display_putc(*p++); + } + } + +#ifdef DEBUG_DISP + putc('\n'); +#endif + + return (0); +} + +/***************************************************/ + +U_BOOT_CMD( + display, CONFIG_SYS_MAXARGS, 1, do_display, + "display string on dot matrix display", + "[<string>]\n" + " - with <string> argument: display <string> on dot matrix display\n" + " - without arguments: clear dot matrix display" +); diff --git a/qemu/roms/u-boot/common/cmd_dtt.c b/qemu/roms/u-boot/common/cmd_dtt.c new file mode 100644 index 000000000..f2e750f2f --- /dev/null +++ b/qemu/roms/u-boot/common/cmd_dtt.c @@ -0,0 +1,119 @@ +/* + * (C) Copyright 2001 + * Erik Theisen, Wave 7 Optics, etheisen@mindspring.com + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <config.h> +#include <command.h> + +#include <dtt.h> +#include <i2c.h> +#include <tmu.h> + +#if defined CONFIG_DTT_SENSORS +static unsigned long sensor_initialized; + +static void _initialize_dtt(void) +{ + int i; + unsigned char sensors[] = CONFIG_DTT_SENSORS; + + for (i = 0; i < sizeof(sensors); i++) { + if ((sensor_initialized & (1 << i)) == 0) { + if (dtt_init_one(sensors[i]) != 0) { + printf("DTT%d: Failed init!\n", i); + continue; + } + sensor_initialized |= (1 << i); + } + } +} + +void dtt_init(void) +{ + int old_bus; + + /* switch to correct I2C bus */ + old_bus = I2C_GET_BUS(); + I2C_SET_BUS(CONFIG_SYS_DTT_BUS_NUM); + + _initialize_dtt(); + + /* switch back to original I2C bus */ + I2C_SET_BUS(old_bus); +} +#endif + +int dtt_i2c(void) +{ +#if defined CONFIG_DTT_SENSORS + int i; + unsigned char sensors[] = CONFIG_DTT_SENSORS; + int old_bus; + + /* Force a compilation error, if there are more then 32 sensors */ + BUILD_BUG_ON(sizeof(sensors) > 32); + /* switch to correct I2C bus */ +#ifdef CONFIG_SYS_I2C + old_bus = i2c_get_bus_num(); + i2c_set_bus_num(CONFIG_SYS_DTT_BUS_NUM); +#else + old_bus = I2C_GET_BUS(); + I2C_SET_BUS(CONFIG_SYS_DTT_BUS_NUM); +#endif + + _initialize_dtt(); + + /* + * Loop through sensors, read + * temperature, and output it. + */ + for (i = 0; i < sizeof(sensors); i++) + printf("DTT%d: %i C\n", i + 1, dtt_get_temp(sensors[i])); + + /* switch back to original I2C bus */ +#ifdef CONFIG_SYS_I2C + i2c_set_bus_num(old_bus); +#else + I2C_SET_BUS(old_bus); +#endif +#endif + + return 0; +} + +int dtt_tmu(void) +{ +#if defined CONFIG_TMU_CMD_DTT + int cur_temp; + + /* Sense and return latest thermal info */ + if (tmu_monitor(&cur_temp) == TMU_STATUS_INIT) { + puts("TMU is in unknown state, temperature is invalid\n"); + return -1; + } + printf("Current temperature: %u degrees Celsius\n", cur_temp); +#endif + return 0; +} + +int do_dtt(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[]) +{ + int err = 0; + + err |= dtt_i2c(); + err |= dtt_tmu(); + + return err; +} /* do_dtt() */ + +/***************************************************/ + +U_BOOT_CMD( + dtt, 1, 1, do_dtt, + "Read temperature from Digital Thermometer and Thermostat", + "" +); diff --git a/qemu/roms/u-boot/common/cmd_echo.c b/qemu/roms/u-boot/common/cmd_echo.c new file mode 100644 index 000000000..3dc3a63f6 --- /dev/null +++ b/qemu/roms/u-boot/common/cmd_echo.c @@ -0,0 +1,56 @@ +/* + * Copyright 2000-2009 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <command.h> + +static int do_echo(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + int i; + int putnl = 1; + + for (i = 1; i < argc; i++) { + char *p = argv[i]; + char *nls; /* new-line suppression */ + + if (i > 1) + putc(' '); + + nls = strstr(p, "\\c"); + if (nls) { + char *prenls = p; + + putnl = 0; + /* + * be paranoid and guess that someone might + * say \c more than once + */ + while (nls) { + *nls = '\0'; + puts(prenls); + *nls = '\\'; + prenls = nls + 2; + nls = strstr(prenls, "\\c"); + } + puts(prenls); + } else { + puts(p); + } + } + + if (putnl) + putc('\n'); + + return 0; +} + +U_BOOT_CMD( + echo, CONFIG_SYS_MAXARGS, 1, do_echo, + "echo args to console", + "[args..]\n" + " - echo args to console; \\c suppresses newline" +); diff --git a/qemu/roms/u-boot/common/cmd_eeprom.c b/qemu/roms/u-boot/common/cmd_eeprom.c new file mode 100644 index 000000000..fad462fb3 --- /dev/null +++ b/qemu/roms/u-boot/common/cmd_eeprom.c @@ -0,0 +1,427 @@ +/* + * (C) Copyright 2000, 2001 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/* + * Support for read and write access to EEPROM like memory devices. This + * includes regular EEPROM as well as FRAM (ferroelectic nonvolaile RAM). + * FRAM devices read and write data at bus speed. In particular, there is no + * write delay. Also, there is no limit imposed on the number of bytes that can + * be transferred with a single read or write. + * + * Use the following configuration options to ensure no unneeded performance + * degradation (typical for EEPROM) is incured for FRAM memory: + * + * #define CONFIG_SYS_I2C_FRAM + * #undef CONFIG_SYS_EEPROM_PAGE_WRITE_DELAY_MS + * + */ + +#include <common.h> +#include <config.h> +#include <command.h> +#include <i2c.h> + +extern void eeprom_init (void); +extern int eeprom_read (unsigned dev_addr, unsigned offset, + uchar *buffer, unsigned cnt); +extern int eeprom_write (unsigned dev_addr, unsigned offset, + uchar *buffer, unsigned cnt); +#if defined(CONFIG_SYS_EEPROM_WREN) +extern int eeprom_write_enable (unsigned dev_addr, int state); +#endif + + +#if defined(CONFIG_SYS_EEPROM_X40430) + /* Maximum number of times to poll for acknowledge after write */ +#define MAX_ACKNOWLEDGE_POLLS 10 +#endif + +/* ------------------------------------------------------------------------- */ + +#if defined(CONFIG_CMD_EEPROM) +int do_eeprom ( cmd_tbl_t * cmdtp, int flag, int argc, char * const argv[]) +{ + const char *const fmt = + "\nEEPROM @0x%lX %s: addr %08lx off %04lx count %ld ... "; + +#if defined(CONFIG_SYS_I2C_MULTI_EEPROMS) + if (argc == 6) { + ulong dev_addr = simple_strtoul (argv[2], NULL, 16); + ulong addr = simple_strtoul (argv[3], NULL, 16); + ulong off = simple_strtoul (argv[4], NULL, 16); + ulong cnt = simple_strtoul (argv[5], NULL, 16); +#else + if (argc == 5) { + ulong dev_addr = CONFIG_SYS_DEF_EEPROM_ADDR; + ulong addr = simple_strtoul (argv[2], NULL, 16); + ulong off = simple_strtoul (argv[3], NULL, 16); + ulong cnt = simple_strtoul (argv[4], NULL, 16); +#endif /* CONFIG_SYS_I2C_MULTI_EEPROMS */ + +# if !defined(CONFIG_SPI) || defined(CONFIG_ENV_EEPROM_IS_ON_I2C) + eeprom_init (); +# endif /* !CONFIG_SPI */ + + if (strcmp (argv[1], "read") == 0) { + int rcode; + + printf (fmt, dev_addr, argv[1], addr, off, cnt); + + rcode = eeprom_read (dev_addr, off, (uchar *) addr, cnt); + + puts ("done\n"); + return rcode; + } else if (strcmp (argv[1], "write") == 0) { + int rcode; + + printf (fmt, dev_addr, argv[1], addr, off, cnt); + + rcode = eeprom_write (dev_addr, off, (uchar *) addr, cnt); + + puts ("done\n"); + return rcode; + } + } + + return CMD_RET_USAGE; +} +#endif + +/*----------------------------------------------------------------------- + * + * for CONFIG_SYS_I2C_EEPROM_ADDR_LEN == 2 (16-bit EEPROM address) offset is + * 0x000nxxxx for EEPROM address selectors at n, offset xxxx in EEPROM. + * + * for CONFIG_SYS_I2C_EEPROM_ADDR_LEN == 1 (8-bit EEPROM page address) offset is + * 0x00000nxx for EEPROM address selectors and page number at n. + */ + +#if !defined(CONFIG_SPI) || defined(CONFIG_ENV_EEPROM_IS_ON_I2C) +#if !defined(CONFIG_SYS_I2C_EEPROM_ADDR_LEN) || CONFIG_SYS_I2C_EEPROM_ADDR_LEN < 1 || CONFIG_SYS_I2C_EEPROM_ADDR_LEN > 2 +#error CONFIG_SYS_I2C_EEPROM_ADDR_LEN must be 1 or 2 +#endif +#endif + +int eeprom_read (unsigned dev_addr, unsigned offset, uchar *buffer, unsigned cnt) +{ + unsigned end = offset + cnt; + unsigned blk_off; + int rcode = 0; + + /* Read data until done or would cross a page boundary. + * We must write the address again when changing pages + * because the next page may be in a different device. + */ + while (offset < end) { + unsigned alen, len; +#if !defined(CONFIG_SYS_I2C_FRAM) + unsigned maxlen; +#endif + +#if CONFIG_SYS_I2C_EEPROM_ADDR_LEN == 1 && !defined(CONFIG_SPI_X) + uchar addr[2]; + + blk_off = offset & 0xFF; /* block offset */ + + addr[0] = offset >> 8; /* block number */ + addr[1] = blk_off; /* block offset */ + alen = 2; +#else + uchar addr[3]; + + blk_off = offset & 0xFF; /* block offset */ + + addr[0] = offset >> 16; /* block number */ + addr[1] = offset >> 8; /* upper address octet */ + addr[2] = blk_off; /* lower address octet */ + alen = 3; +#endif /* CONFIG_SYS_I2C_EEPROM_ADDR_LEN, CONFIG_SPI_X */ + + addr[0] |= dev_addr; /* insert device address */ + + len = end - offset; + + /* + * For a FRAM device there is no limit on the number of the + * bytes that can be ccessed with the single read or write + * operation. + */ +#if !defined(CONFIG_SYS_I2C_FRAM) + maxlen = 0x100 - blk_off; + if (maxlen > I2C_RXTX_LEN) + maxlen = I2C_RXTX_LEN; + if (len > maxlen) + len = maxlen; +#endif + +#if defined(CONFIG_SPI) && !defined(CONFIG_ENV_EEPROM_IS_ON_I2C) + spi_read (addr, alen, buffer, len); +#else + if (i2c_read(addr[0], offset, alen - 1, buffer, len)) + rcode = 1; +#endif + buffer += len; + offset += len; + } + + return rcode; +} + +/*----------------------------------------------------------------------- + * + * for CONFIG_SYS_I2C_EEPROM_ADDR_LEN == 2 (16-bit EEPROM address) offset is + * 0x000nxxxx for EEPROM address selectors at n, offset xxxx in EEPROM. + * + * for CONFIG_SYS_I2C_EEPROM_ADDR_LEN == 1 (8-bit EEPROM page address) offset is + * 0x00000nxx for EEPROM address selectors and page number at n. + */ + +int eeprom_write (unsigned dev_addr, unsigned offset, uchar *buffer, unsigned cnt) +{ + unsigned end = offset + cnt; + unsigned blk_off; + int rcode = 0; + +#if defined(CONFIG_SYS_EEPROM_X40430) + uchar contr_r_addr[2]; + uchar addr_void[2]; + uchar contr_reg[2]; + uchar ctrl_reg_v; + int i; +#endif + +#if defined(CONFIG_SYS_EEPROM_WREN) + eeprom_write_enable (dev_addr,1); +#endif + /* Write data until done or would cross a write page boundary. + * We must write the address again when changing pages + * because the address counter only increments within a page. + */ + + while (offset < end) { + unsigned alen, len; +#if !defined(CONFIG_SYS_I2C_FRAM) + unsigned maxlen; +#endif + +#if CONFIG_SYS_I2C_EEPROM_ADDR_LEN == 1 && !defined(CONFIG_SPI_X) + uchar addr[2]; + + blk_off = offset & 0xFF; /* block offset */ + + addr[0] = offset >> 8; /* block number */ + addr[1] = blk_off; /* block offset */ + alen = 2; +#else + uchar addr[3]; + + blk_off = offset & 0xFF; /* block offset */ + + addr[0] = offset >> 16; /* block number */ + addr[1] = offset >> 8; /* upper address octet */ + addr[2] = blk_off; /* lower address octet */ + alen = 3; +#endif /* CONFIG_SYS_I2C_EEPROM_ADDR_LEN, CONFIG_SPI_X */ + + addr[0] |= dev_addr; /* insert device address */ + + len = end - offset; + + /* + * For a FRAM device there is no limit on the number of the + * bytes that can be accessed with the single read or write + * operation. + */ +#if !defined(CONFIG_SYS_I2C_FRAM) + +#if defined(CONFIG_SYS_EEPROM_PAGE_WRITE_BITS) + +#define EEPROM_PAGE_SIZE (1 << CONFIG_SYS_EEPROM_PAGE_WRITE_BITS) +#define EEPROM_PAGE_OFFSET(x) ((x) & (EEPROM_PAGE_SIZE - 1)) + + maxlen = EEPROM_PAGE_SIZE - EEPROM_PAGE_OFFSET(blk_off); +#else + maxlen = 0x100 - blk_off; +#endif + if (maxlen > I2C_RXTX_LEN) + maxlen = I2C_RXTX_LEN; + + if (len > maxlen) + len = maxlen; +#endif + +#if defined(CONFIG_SPI) && !defined(CONFIG_ENV_EEPROM_IS_ON_I2C) + spi_write (addr, alen, buffer, len); +#else +#if defined(CONFIG_SYS_EEPROM_X40430) + /* Get the value of the control register. + * Set current address (internal pointer in the x40430) + * to 0x1ff. + */ + contr_r_addr[0] = 9; + contr_r_addr[1] = 0xff; + addr_void[0] = 0; + addr_void[1] = addr[1]; +#ifdef CONFIG_SYS_I2C_EEPROM_ADDR + contr_r_addr[0] |= CONFIG_SYS_I2C_EEPROM_ADDR; + addr_void[0] |= CONFIG_SYS_I2C_EEPROM_ADDR; +#endif + contr_reg[0] = 0xff; + if (i2c_read (contr_r_addr[0], contr_r_addr[1], 1, contr_reg, 1) != 0) { + rcode = 1; + } + ctrl_reg_v = contr_reg[0]; + + /* Are any of the eeprom blocks write protected? + */ + if (ctrl_reg_v & 0x18) { + ctrl_reg_v &= ~0x18; /* reset block protect bits */ + ctrl_reg_v |= 0x02; /* set write enable latch */ + ctrl_reg_v &= ~0x04; /* clear RWEL */ + + /* Set write enable latch. + */ + contr_reg[0] = 0x02; + if (i2c_write (contr_r_addr[0], 0xff, 1, contr_reg, 1) != 0) { + rcode = 1; + } + + /* Set register write enable latch. + */ + contr_reg[0] = 0x06; + if (i2c_write (contr_r_addr[0], 0xFF, 1, contr_reg, 1) != 0) { + rcode = 1; + } + + /* Modify ctrl register. + */ + contr_reg[0] = ctrl_reg_v; + if (i2c_write (contr_r_addr[0], 0xFF, 1, contr_reg, 1) != 0) { + rcode = 1; + } + + /* The write (above) is an operation on NV memory. + * These can take some time (~5ms), and the device + * will not respond to further I2C messages till + * it's completed the write. + * So poll device for an I2C acknowledge. + * When we get one we know we can continue with other + * operations. + */ + contr_reg[0] = 0; + for (i = 0; i < MAX_ACKNOWLEDGE_POLLS; i++) { + if (i2c_read (addr_void[0], addr_void[1], 1, contr_reg, 1) == 0) + break; /* got ack */ +#if defined(CONFIG_SYS_EEPROM_PAGE_WRITE_DELAY_MS) + udelay(CONFIG_SYS_EEPROM_PAGE_WRITE_DELAY_MS * 1000); +#endif + } + if (i == MAX_ACKNOWLEDGE_POLLS) { + puts ("EEPROM poll acknowledge failed\n"); + rcode = 1; + } + } + + /* Is the write enable latch on?. + */ + else if (!(ctrl_reg_v & 0x02)) { + /* Set write enable latch. + */ + contr_reg[0] = 0x02; + if (i2c_write (contr_r_addr[0], 0xFF, 1, contr_reg, 1) != 0) { + rcode = 1; + } + } + /* Write is enabled ... now write eeprom value. + */ +#endif + if (i2c_write(addr[0], offset, alen - 1, buffer, len)) + rcode = 1; + +#endif + buffer += len; + offset += len; + +#if defined(CONFIG_SYS_EEPROM_PAGE_WRITE_DELAY_MS) + udelay(CONFIG_SYS_EEPROM_PAGE_WRITE_DELAY_MS * 1000); +#endif + } +#if defined(CONFIG_SYS_EEPROM_WREN) + eeprom_write_enable (dev_addr,0); +#endif + return rcode; +} + +#if !defined(CONFIG_SPI) || defined(CONFIG_ENV_EEPROM_IS_ON_I2C) +int +eeprom_probe (unsigned dev_addr, unsigned offset) +{ + unsigned char chip; + + /* Probe the chip address + */ +#if CONFIG_SYS_I2C_EEPROM_ADDR_LEN == 1 && !defined(CONFIG_SPI_X) + chip = offset >> 8; /* block number */ +#else + chip = offset >> 16; /* block number */ +#endif /* CONFIG_SYS_I2C_EEPROM_ADDR_LEN, CONFIG_SPI_X */ + + chip |= dev_addr; /* insert device address */ + + return (i2c_probe (chip)); +} +#endif + +/*----------------------------------------------------------------------- + * Set default values + */ +#ifndef CONFIG_SYS_I2C_SPEED +#define CONFIG_SYS_I2C_SPEED 50000 +#endif + +void eeprom_init (void) +{ + +#if defined(CONFIG_SPI) && !defined(CONFIG_ENV_EEPROM_IS_ON_I2C) + spi_init_f (); +#endif +#if defined(CONFIG_HARD_I2C) || defined(CONFIG_SYS_I2C_SOFT) || \ + defined(CONFIG_SYS_I2C) +#ifdef CONFIG_SYS_I2C + i2c_init_all(); +#else + i2c_init(CONFIG_SYS_I2C_SPEED, CONFIG_SYS_I2C_SLAVE); +#endif +#endif +} + +/*----------------------------------------------------------------------- + */ + +/***************************************************/ + +#if defined(CONFIG_CMD_EEPROM) + +#ifdef CONFIG_SYS_I2C_MULTI_EEPROMS +U_BOOT_CMD( + eeprom, 6, 1, do_eeprom, + "EEPROM sub-system", + "read devaddr addr off cnt\n" + "eeprom write devaddr addr off cnt\n" + " - read/write `cnt' bytes from `devaddr` EEPROM at offset `off'" +); +#else /* One EEPROM */ +U_BOOT_CMD( + eeprom, 5, 1, do_eeprom, + "EEPROM sub-system", + "read addr off cnt\n" + "eeprom write addr off cnt\n" + " - read/write `cnt' bytes at EEPROM offset `off'" +); +#endif /* CONFIG_SYS_I2C_MULTI_EEPROMS */ + +#endif diff --git a/qemu/roms/u-boot/common/cmd_elf.c b/qemu/roms/u-boot/common/cmd_elf.c new file mode 100644 index 000000000..ab9c7e332 --- /dev/null +++ b/qemu/roms/u-boot/common/cmd_elf.c @@ -0,0 +1,369 @@ +/* + * Copyright (c) 2001 William L. Pitts + * All rights reserved. + * + * Redistribution and use in source and binary forms are freely + * permitted provided that the above copyright notice and this + * paragraph and the following disclaimer are duplicated in all + * such forms. + * + * This software is provided "AS IS" and without any express or + * implied warranties, including, without limitation, the implied + * warranties of merchantability and fitness for a particular + * purpose. + */ + +#include <common.h> +#include <command.h> +#include <linux/ctype.h> +#include <net.h> +#include <elf.h> +#include <vxworks.h> + +#if defined(CONFIG_WALNUT) || defined(CONFIG_SYS_VXWORKS_MAC_PTR) +DECLARE_GLOBAL_DATA_PTR; +#endif + +static unsigned long load_elf_image_phdr(unsigned long addr); +static unsigned long load_elf_image_shdr(unsigned long addr); + +/* Allow ports to override the default behavior */ +__attribute__((weak)) +unsigned long do_bootelf_exec(ulong (*entry)(int, char * const[]), + int argc, char * const argv[]) +{ + unsigned long ret; + + /* + * QNX images require the data cache is disabled. + * Data cache is already flushed, so just turn it off. + */ + int dcache = dcache_status(); + if (dcache) + dcache_disable(); + + /* + * pass address parameter as argv[0] (aka command name), + * and all remaining args + */ + ret = entry(argc, argv); + + if (dcache) + dcache_enable(); + + return ret; +} + +/* ====================================================================== + * Determine if a valid ELF image exists at the given memory location. + * First looks at the ELF header magic field, the makes sure that it is + * executable and makes sure that it is for a PowerPC. + * ====================================================================== */ +int valid_elf_image(unsigned long addr) +{ + Elf32_Ehdr *ehdr; /* Elf header structure pointer */ + + /* -------------------------------------------------- */ + + ehdr = (Elf32_Ehdr *) addr; + + if (!IS_ELF(*ehdr)) { + printf("## No elf image at address 0x%08lx\n", addr); + return 0; + } + + if (ehdr->e_type != ET_EXEC) { + printf("## Not a 32-bit elf image at address 0x%08lx\n", addr); + return 0; + } + +#if 0 + if (ehdr->e_machine != EM_PPC) { + printf("## Not a PowerPC elf image at address 0x%08lx\n", addr); + return 0; + } +#endif + + return 1; +} + +/* ====================================================================== + * Interpreter command to boot an arbitrary ELF image from memory. + * ====================================================================== */ +int do_bootelf(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + unsigned long addr; /* Address of the ELF image */ + unsigned long rc; /* Return value from user code */ + char *sload, *saddr; + + /* -------------------------------------------------- */ + int rcode = 0; + + sload = saddr = NULL; + if (argc == 3) { + sload = argv[1]; + saddr = argv[2]; + } else if (argc == 2) { + if (argv[1][0] == '-') + sload = argv[1]; + else + saddr = argv[1]; + } + + if (saddr) + addr = simple_strtoul(saddr, NULL, 16); + else + addr = load_addr; + + if (!valid_elf_image(addr)) + return 1; + + if (sload && sload[1] == 'p') + addr = load_elf_image_phdr(addr); + else + addr = load_elf_image_shdr(addr); + + printf("## Starting application at 0x%08lx ...\n", addr); + + /* + * pass address parameter as argv[0] (aka command name), + * and all remaining args + */ + rc = do_bootelf_exec((void *)addr, argc - 1, argv + 1); + if (rc != 0) + rcode = 1; + + printf("## Application terminated, rc = 0x%lx\n", rc); + return rcode; +} + +/* ====================================================================== + * Interpreter command to boot VxWorks from a memory image. The image can + * be either an ELF image or a raw binary. Will attempt to setup the + * bootline and other parameters correctly. + * ====================================================================== */ +int do_bootvx(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + unsigned long addr; /* Address of image */ + unsigned long bootaddr; /* Address to put the bootline */ + char *bootline; /* Text of the bootline */ + char *tmp; /* Temporary char pointer */ + char build_buf[128]; /* Buffer for building the bootline */ + + /* --------------------------------------------------- + * + * Check the loadaddr variable. + * If we don't know where the image is then we're done. + */ + + if (argc < 2) + addr = load_addr; + else + addr = simple_strtoul(argv[1], NULL, 16); + +#if defined(CONFIG_CMD_NET) + /* + * Check to see if we need to tftp the image ourselves before starting + */ + if ((argc == 2) && (strcmp(argv[1], "tftp") == 0)) { + if (NetLoop(TFTPGET) <= 0) + return 1; + printf("Automatic boot of VxWorks image at address 0x%08lx ...\n", + addr); + } +#endif + + /* This should equate + * to NV_RAM_ADRS + NV_BOOT_OFFSET + NV_ENET_OFFSET + * from the VxWorks BSP header files. + * This will vary from board to board + */ + +#if defined(CONFIG_WALNUT) + tmp = (char *) CONFIG_SYS_NVRAM_BASE_ADDR + 0x500; + eth_getenv_enetaddr("ethaddr", (uchar *)build_buf); + memcpy(tmp, &build_buf[3], 3); +#elif defined(CONFIG_SYS_VXWORKS_MAC_PTR) + tmp = (char *) CONFIG_SYS_VXWORKS_MAC_PTR; + eth_getenv_enetaddr("ethaddr", (uchar *)build_buf); + memcpy(tmp, build_buf, 6); +#else + puts("## Ethernet MAC address not copied to NV RAM\n"); +#endif + + /* + * Use bootaddr to find the location in memory that VxWorks + * will look for the bootline string. The default value for + * PowerPC is LOCAL_MEM_LOCAL_ADRS + BOOT_LINE_OFFSET which + * defaults to 0x4200 + */ + tmp = getenv("bootaddr"); + if (!tmp) + bootaddr = CONFIG_SYS_VXWORKS_BOOT_ADDR; + else + bootaddr = simple_strtoul(tmp, NULL, 16); + + /* + * Check to see if the bootline is defined in the 'bootargs' + * parameter. If it is not defined, we may be able to + * construct the info + */ + bootline = getenv("bootargs"); + if (bootline) { + memcpy((void *) bootaddr, bootline, + max(strlen(bootline), 255)); + flush_cache(bootaddr, max(strlen(bootline), 255)); + } else { + sprintf(build_buf, CONFIG_SYS_VXWORKS_BOOT_DEVICE); + tmp = getenv("bootfile"); + if (tmp) + sprintf(&build_buf[strlen(build_buf)], + "%s:%s ", CONFIG_SYS_VXWORKS_SERVERNAME, tmp); + else + sprintf(&build_buf[strlen(build_buf)], + "%s:file ", CONFIG_SYS_VXWORKS_SERVERNAME); + + tmp = getenv("ipaddr"); + if (tmp) + sprintf(&build_buf[strlen(build_buf)], "e=%s ", tmp); + + tmp = getenv("serverip"); + if (tmp) + sprintf(&build_buf[strlen(build_buf)], "h=%s ", tmp); + + tmp = getenv("hostname"); + if (tmp) + sprintf(&build_buf[strlen(build_buf)], "tn=%s ", tmp); + +#ifdef CONFIG_SYS_VXWORKS_ADD_PARAMS + sprintf(&build_buf[strlen(build_buf)], + CONFIG_SYS_VXWORKS_ADD_PARAMS); +#endif + + memcpy((void *) bootaddr, build_buf, + max(strlen(build_buf), 255)); + flush_cache(bootaddr, max(strlen(build_buf), 255)); + } + + /* + * If the data at the load address is an elf image, then + * treat it like an elf image. Otherwise, assume that it is a + * binary image + */ + + if (valid_elf_image(addr)) { + addr = load_elf_image_shdr(addr); + } else { + puts("## Not an ELF image, assuming binary\n"); + /* leave addr as load_addr */ + } + + printf("## Using bootline (@ 0x%lx): %s\n", bootaddr, + (char *) bootaddr); + printf("## Starting vxWorks at 0x%08lx ...\n", addr); + + dcache_disable(); + ((void (*)(int)) addr) (0); + + puts("## vxWorks terminated\n"); + return 1; +} + +/* ====================================================================== + * A very simple elf loader, assumes the image is valid, returns the + * entry point address. + * ====================================================================== */ +static unsigned long load_elf_image_phdr(unsigned long addr) +{ + Elf32_Ehdr *ehdr; /* Elf header structure pointer */ + Elf32_Phdr *phdr; /* Program header structure pointer */ + int i; + + ehdr = (Elf32_Ehdr *) addr; + phdr = (Elf32_Phdr *) (addr + ehdr->e_phoff); + + /* Load each program header */ + for (i = 0; i < ehdr->e_phnum; ++i) { + void *dst = (void *)(uintptr_t) phdr->p_paddr; + void *src = (void *) addr + phdr->p_offset; + debug("Loading phdr %i to 0x%p (%i bytes)\n", + i, dst, phdr->p_filesz); + if (phdr->p_filesz) + memcpy(dst, src, phdr->p_filesz); + if (phdr->p_filesz != phdr->p_memsz) + memset(dst + phdr->p_filesz, 0x00, + phdr->p_memsz - phdr->p_filesz); + flush_cache((unsigned long)dst, phdr->p_filesz); + ++phdr; + } + + return ehdr->e_entry; +} + +static unsigned long load_elf_image_shdr(unsigned long addr) +{ + Elf32_Ehdr *ehdr; /* Elf header structure pointer */ + Elf32_Shdr *shdr; /* Section header structure pointer */ + unsigned char *strtab = 0; /* String table pointer */ + unsigned char *image; /* Binary image pointer */ + int i; /* Loop counter */ + + /* -------------------------------------------------- */ + + ehdr = (Elf32_Ehdr *) addr; + + /* Find the section header string table for output info */ + shdr = (Elf32_Shdr *) (addr + ehdr->e_shoff + + (ehdr->e_shstrndx * sizeof(Elf32_Shdr))); + + if (shdr->sh_type == SHT_STRTAB) + strtab = (unsigned char *) (addr + shdr->sh_offset); + + /* Load each appropriate section */ + for (i = 0; i < ehdr->e_shnum; ++i) { + shdr = (Elf32_Shdr *) (addr + ehdr->e_shoff + + (i * sizeof(Elf32_Shdr))); + + if (!(shdr->sh_flags & SHF_ALLOC) + || shdr->sh_addr == 0 || shdr->sh_size == 0) { + continue; + } + + if (strtab) { + debug("%sing %s @ 0x%08lx (%ld bytes)\n", + (shdr->sh_type == SHT_NOBITS) ? + "Clear" : "Load", + &strtab[shdr->sh_name], + (unsigned long) shdr->sh_addr, + (long) shdr->sh_size); + } + + if (shdr->sh_type == SHT_NOBITS) { + memset((void *)(uintptr_t) shdr->sh_addr, 0, + shdr->sh_size); + } else { + image = (unsigned char *) addr + shdr->sh_offset; + memcpy((void *)(uintptr_t) shdr->sh_addr, + (const void *) image, + shdr->sh_size); + } + flush_cache(shdr->sh_addr, shdr->sh_size); + } + + return ehdr->e_entry; +} + +/* ====================================================================== */ +U_BOOT_CMD( + bootelf, 3, 0, do_bootelf, + "Boot from an ELF image in memory", + "[-p|-s] [address]\n" + "\t- load ELF image at [address] via program headers (-p)\n" + "\t or via section headers (-s)" +); + +U_BOOT_CMD( + bootvx, 2, 0, do_bootvx, + "Boot vxWorks from an ELF image", + " [address] - load address of vxWorks ELF image." +); diff --git a/qemu/roms/u-boot/common/cmd_exit.c b/qemu/roms/u-boot/common/cmd_exit.c new file mode 100644 index 000000000..c7892333b --- /dev/null +++ b/qemu/roms/u-boot/common/cmd_exit.c @@ -0,0 +1,26 @@ +/* + * Copyright 2000-2009 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <command.h> + +static int do_exit(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + int r; + + r = 0; + if (argc > 1) + r = simple_strtoul(argv[1], NULL, 10); + + return -r - 2; +} + +U_BOOT_CMD( + exit, 2, 1, do_exit, + "exit script", + "" +); diff --git a/qemu/roms/u-boot/common/cmd_ext2.c b/qemu/roms/u-boot/common/cmd_ext2.c new file mode 100644 index 000000000..5a4bcc1a3 --- /dev/null +++ b/qemu/roms/u-boot/common/cmd_ext2.c @@ -0,0 +1,51 @@ +/* + * (C) Copyright 2011 - 2012 Samsung Electronics + * EXT4 filesystem implementation in Uboot by + * Uma Shankar <uma.shankar@samsung.com> + * Manjunatha C Achar <a.manjunatha@samsung.com> + + * (C) Copyright 2004 + * esd gmbh <www.esd-electronics.com> + * Reinhard Arlt <reinhard.arlt@esd-electronics.com> + * + * made from cmd_reiserfs by + * + * (C) Copyright 2003 - 2004 + * Sysgo Real-Time Solutions, AG <www.elinos.com> + * Pavel Bartusek <pba@sysgo.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/* + * Ext2fs support + */ +#include <fs.h> + +int do_ext2ls (cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + return do_ls(cmdtp, flag, argc, argv, FS_TYPE_EXT); +} + +/****************************************************************************** + * Ext2fs boot command intepreter. Derived from diskboot + */ +int do_ext2load (cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + return do_load(cmdtp, flag, argc, argv, FS_TYPE_EXT); +} + +U_BOOT_CMD( + ext2ls, 4, 1, do_ext2ls, + "list files in a directory (default /)", + "<interface> <dev[:part]> [directory]\n" + " - list files from 'dev' on 'interface' in a 'directory'" +); + +U_BOOT_CMD( + ext2load, 6, 0, do_ext2load, + "load binary file from a Ext2 filesystem", + "<interface> <dev[:part]> [addr] [filename] [bytes]\n" + " - load binary file 'filename' from 'dev' on 'interface'\n" + " to address 'addr' from ext2 filesystem." +); diff --git a/qemu/roms/u-boot/common/cmd_ext4.c b/qemu/roms/u-boot/common/cmd_ext4.c new file mode 100644 index 000000000..68b047ba6 --- /dev/null +++ b/qemu/roms/u-boot/common/cmd_ext4.c @@ -0,0 +1,125 @@ +/* + * (C) Copyright 2011 - 2012 Samsung Electronics + * EXT4 filesystem implementation in Uboot by + * Uma Shankar <uma.shankar@samsung.com> + * Manjunatha C Achar <a.manjunatha@samsung.com> + * + * Ext4fs support + * made from existing cmd_ext2.c file of Uboot + * + * (C) Copyright 2004 + * esd gmbh <www.esd-electronics.com> + * Reinhard Arlt <reinhard.arlt@esd-electronics.com> + * + * made from cmd_reiserfs by + * + * (C) Copyright 2003 - 2004 + * Sysgo Real-Time Solutions, AG <www.elinos.com> + * Pavel Bartusek <pba@sysgo.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/* + * Changelog: + * 0.1 - Newly created file for ext4fs support. Taken from cmd_ext2.c + * file in uboot. Added ext4fs ls load and write support. + */ + +#include <common.h> +#include <part.h> +#include <config.h> +#include <command.h> +#include <image.h> +#include <linux/ctype.h> +#include <asm/byteorder.h> +#include <ext4fs.h> +#include <linux/stat.h> +#include <malloc.h> +#include <fs.h> + +#if defined(CONFIG_CMD_USB) && defined(CONFIG_USB_STORAGE) +#include <usb.h> +#endif + +int do_ext4_load(cmd_tbl_t *cmdtp, int flag, int argc, + char *const argv[]) +{ + return do_load(cmdtp, flag, argc, argv, FS_TYPE_EXT); +} + +int do_ext4_ls(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[]) +{ + return do_ls(cmdtp, flag, argc, argv, FS_TYPE_EXT); +} + +#if defined(CONFIG_CMD_EXT4_WRITE) +int do_ext4_write(cmd_tbl_t *cmdtp, int flag, int argc, + char *const argv[]) +{ + const char *filename = "/"; + int dev, part; + unsigned long ram_address; + unsigned long file_size; + disk_partition_t info; + block_dev_desc_t *dev_desc; + + if (argc < 6) + return cmd_usage(cmdtp); + + part = get_device_and_partition(argv[1], argv[2], &dev_desc, &info, 1); + if (part < 0) + return 1; + + dev = dev_desc->dev; + + /* get the filename */ + filename = argv[4]; + + /* get the address in hexadecimal format (string to int) */ + ram_address = simple_strtoul(argv[3], NULL, 16); + + /* get the filesize in hexadecimal format */ + file_size = simple_strtoul(argv[5], NULL, 16); + + /* set the device as block device */ + ext4fs_set_blk_dev(dev_desc, &info); + + /* mount the filesystem */ + if (!ext4fs_mount(info.size)) { + printf("Bad ext4 partition %s %d:%d\n", argv[1], dev, part); + goto fail; + } + + /* start write */ + if (ext4fs_write(filename, (unsigned char *)ram_address, file_size)) { + printf("** Error ext4fs_write() **\n"); + goto fail; + } + ext4fs_close(); + + return 0; + +fail: + ext4fs_close(); + + return 1; +} + +U_BOOT_CMD(ext4write, 6, 1, do_ext4_write, + "create a file in the root directory", + "<interface> <dev[:part]> <addr> <absolute filename path> [sizebytes]\n" + " - create a file in / directory"); + +#endif + +U_BOOT_CMD(ext4ls, 4, 1, do_ext4_ls, + "list files in a directory (default /)", + "<interface> <dev[:part]> [directory]\n" + " - list files from 'dev' on 'interface' in a 'directory'"); + +U_BOOT_CMD(ext4load, 6, 0, do_ext4_load, + "load binary file from a Ext4 filesystem", + "<interface> <dev[:part]> [addr] [filename] [bytes]\n" + " - load binary file 'filename' from 'dev' on 'interface'\n" + " to address 'addr' from ext4 filesystem"); diff --git a/qemu/roms/u-boot/common/cmd_fat.c b/qemu/roms/u-boot/common/cmd_fat.c new file mode 100644 index 000000000..a12d8fa09 --- /dev/null +++ b/qemu/roms/u-boot/common/cmd_fat.c @@ -0,0 +1,133 @@ +/* + * (C) Copyright 2002 + * Richard Jones, rjones@nexus-tech.net + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/* + * Boot support + */ +#include <common.h> +#include <command.h> +#include <s_record.h> +#include <net.h> +#include <ata.h> +#include <part.h> +#include <fat.h> +#include <fs.h> + +int do_fat_fsload (cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + return do_load(cmdtp, flag, argc, argv, FS_TYPE_FAT); +} + + +U_BOOT_CMD( + fatload, 7, 0, do_fat_fsload, + "load binary file from a dos filesystem", + "<interface> [<dev[:part]>] <addr> <filename> [bytes [pos]]\n" + " - Load binary file 'filename' from 'dev' on 'interface'\n" + " to address 'addr' from dos filesystem.\n" + " 'pos' gives the file position to start loading from.\n" + " If 'pos' is omitted, 0 is used. 'pos' requires 'bytes'.\n" + " 'bytes' gives the size to load. If 'bytes' is 0 or omitted,\n" + " the load stops on end of file.\n" + " If either 'pos' or 'bytes' are not aligned to\n" + " ARCH_DMA_MINALIGN then a misaligned buffer warning will\n" + " be printed and performance will suffer for the load." +); + +static int do_fat_ls(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + return do_ls(cmdtp, flag, argc, argv, FS_TYPE_FAT); +} + +U_BOOT_CMD( + fatls, 4, 1, do_fat_ls, + "list files in a directory (default /)", + "<interface> [<dev[:part]>] [directory]\n" + " - list files from 'dev' on 'interface' in a 'directory'" +); + +static int do_fat_fsinfo(cmd_tbl_t *cmdtp, int flag, int argc, + char * const argv[]) +{ + int dev, part; + block_dev_desc_t *dev_desc; + disk_partition_t info; + + if (argc < 2) { + printf("usage: fatinfo <interface> [<dev[:part]>]\n"); + return 0; + } + + part = get_device_and_partition(argv[1], argv[2], &dev_desc, &info, 1); + if (part < 0) + return 1; + + dev = dev_desc->dev; + if (fat_set_blk_dev(dev_desc, &info) != 0) { + printf("\n** Unable to use %s %d:%d for fatinfo **\n", + argv[1], dev, part); + return 1; + } + return file_fat_detectfs(); +} + +U_BOOT_CMD( + fatinfo, 3, 1, do_fat_fsinfo, + "print information about filesystem", + "<interface> [<dev[:part]>]\n" + " - print information about filesystem from 'dev' on 'interface'" +); + +#ifdef CONFIG_FAT_WRITE +static int do_fat_fswrite(cmd_tbl_t *cmdtp, int flag, + int argc, char * const argv[]) +{ + long size; + unsigned long addr; + unsigned long count; + block_dev_desc_t *dev_desc = NULL; + disk_partition_t info; + int dev = 0; + int part = 1; + + if (argc < 5) + return cmd_usage(cmdtp); + + part = get_device_and_partition(argv[1], argv[2], &dev_desc, &info, 1); + if (part < 0) + return 1; + + dev = dev_desc->dev; + + if (fat_set_blk_dev(dev_desc, &info) != 0) { + printf("\n** Unable to use %s %d:%d for fatwrite **\n", + argv[1], dev, part); + return 1; + } + addr = simple_strtoul(argv[3], NULL, 16); + count = simple_strtoul(argv[5], NULL, 16); + + size = file_fat_write(argv[4], (void *)addr, count); + if (size == -1) { + printf("\n** Unable to write \"%s\" from %s %d:%d **\n", + argv[4], argv[1], dev, part); + return 1; + } + + printf("%ld bytes written\n", size); + + return 0; +} + +U_BOOT_CMD( + fatwrite, 6, 0, do_fat_fswrite, + "write file into a dos filesystem", + "<interface> <dev[:part]> <addr> <filename> <bytes>\n" + " - write file 'filename' from the address 'addr' in RAM\n" + " to 'dev' on 'interface'" +); +#endif diff --git a/qemu/roms/u-boot/common/cmd_fdc.c b/qemu/roms/u-boot/common/cmd_fdc.c new file mode 100644 index 000000000..1cfb656bc --- /dev/null +++ b/qemu/roms/u-boot/common/cmd_fdc.c @@ -0,0 +1,748 @@ +/* + * (C) Copyright 2001 + * Denis Peter, MPL AG, d.peter@mpl.ch. + * + * SPDX-License-Identifier: GPL-2.0+ + */ +/* + * Floppy Disk support + */ + +#include <common.h> +#include <config.h> +#include <command.h> +#include <image.h> + + +#undef FDC_DEBUG + +#ifdef FDC_DEBUG +#define PRINTF(fmt,args...) printf (fmt ,##args) +#else +#define PRINTF(fmt,args...) +#endif + +/*#if defined(CONFIG_CMD_DATE) */ +/*#include <rtc.h> */ +/*#endif */ + +typedef struct { + int flags; /* connected drives ect */ + unsigned long blnr; /* Logical block nr */ + uchar drive; /* drive no */ + uchar cmdlen; /* cmd length */ + uchar cmd[16]; /* cmd desc */ + uchar dma; /* if > 0 dma enabled */ + uchar result[11]; /* status information */ + uchar resultlen; /* lenght of result */ +} FDC_COMMAND_STRUCT; + +/* flags: only the lower 8bit used: + * bit 0 if set drive 0 is present + * bit 1 if set drive 1 is present + * bit 2 if set drive 2 is present + * bit 3 if set drive 3 is present + * bit 4 if set disk in drive 0 is inserted + * bit 5 if set disk in drive 1 is inserted + * bit 6 if set disk in drive 2 is inserted + * bit 7 if set disk in drive 4 is inserted + */ + +/* cmd indexes */ +#define COMMAND 0 +#define DRIVE 1 +#define CONFIG0 1 +#define SPEC_HUTSRT 1 +#define TRACK 2 +#define CONFIG1 2 +#define SPEC_HLT 2 +#define HEAD 3 +#define CONFIG2 3 +#define SECTOR 4 +#define SECTOR_SIZE 5 +#define LAST_TRACK 6 +#define GAP 7 +#define DTL 8 +/* result indexes */ +#define STATUS_0 0 +#define STATUS_PCN 1 +#define STATUS_1 1 +#define STATUS_2 2 +#define STATUS_TRACK 3 +#define STATUS_HEAD 4 +#define STATUS_SECT 5 +#define STATUS_SECT_SIZE 6 + + +/* Register addresses */ +#define FDC_BASE 0x3F0 +#define FDC_SRA FDC_BASE + 0 /* Status Register A */ +#define FDC_SRB FDC_BASE + 1 /* Status Register B */ +#define FDC_DOR FDC_BASE + 2 /* Digital Output Register */ +#define FDC_TDR FDC_BASE + 3 /* Tape Drive Register */ +#define FDC_DSR FDC_BASE + 4 /* Data rate Register */ +#define FDC_MSR FDC_BASE + 4 /* Main Status Register */ +#define FDC_FIFO FDC_BASE + 5 /* FIFO */ +#define FDC_DIR FDC_BASE + 6 /* Digital Input Register */ +#define FDC_CCR FDC_BASE + 7 /* Configuration Control */ +/* Commands */ +#define FDC_CMD_SENSE_INT 0x08 +#define FDC_CMD_CONFIGURE 0x13 +#define FDC_CMD_SPECIFY 0x03 +#define FDC_CMD_RECALIBRATE 0x07 +#define FDC_CMD_READ 0x06 +#define FDC_CMD_READ_TRACK 0x02 +#define FDC_CMD_READ_ID 0x0A +#define FDC_CMD_DUMP_REG 0x0E +#define FDC_CMD_SEEK 0x0F + +#define FDC_CMD_SENSE_INT_LEN 0x01 +#define FDC_CMD_CONFIGURE_LEN 0x04 +#define FDC_CMD_SPECIFY_LEN 0x03 +#define FDC_CMD_RECALIBRATE_LEN 0x02 +#define FDC_CMD_READ_LEN 0x09 +#define FDC_CMD_READ_TRACK_LEN 0x09 +#define FDC_CMD_READ_ID_LEN 0x02 +#define FDC_CMD_DUMP_REG_LEN 0x01 +#define FDC_CMD_SEEK_LEN 0x03 + +#define FDC_FIFO_THR 0x0C +#define FDC_FIFO_DIS 0x00 +#define FDC_IMPLIED_SEEK 0x01 +#define FDC_POLL_DIS 0x00 +#define FDC_PRE_TRK 0x00 +#define FDC_CONFIGURE FDC_FIFO_THR | (FDC_POLL_DIS<<4) | (FDC_FIFO_DIS<<5) | (FDC_IMPLIED_SEEK << 6) +#define FDC_MFM_MODE 0x01 /* MFM enable */ +#define FDC_SKIP_MODE 0x00 /* skip enable */ + +#define FDC_TIME_OUT 100000 /* time out */ +#define FDC_RW_RETRIES 3 /* read write retries */ +#define FDC_CAL_RETRIES 3 /* calibration and seek retries */ + + +/* Disk structure */ +typedef struct { + unsigned int size; /* nr of sectors total */ + unsigned int sect; /* sectors per track */ + unsigned int head; /* nr of heads */ + unsigned int track; /* nr of tracks */ + unsigned int stretch; /* !=0 means double track steps */ + unsigned char gap; /* gap1 size */ + unsigned char rate; /* data rate. |= 0x40 for perpendicular */ + unsigned char spec1; /* stepping rate, head unload time */ + unsigned char fmt_gap;/* gap2 size */ + unsigned char hlt; /* head load time */ + unsigned char sect_code;/* Sector Size code */ + const char * name; /* used only for predefined formats */ +} FD_GEO_STRUCT; + + +/* supported Floppy types (currently only one) */ +const static FD_GEO_STRUCT floppy_type[2] = { + { 2880,18,2,80,0,0x1B,0x00,0xCF,0x6C,16,2,"H1440" }, /* 7 1.44MB 3.5" */ + { 0, 0,0, 0,0,0x00,0x00,0x00,0x00, 0,0,NULL }, /* end of table */ +}; + +static FDC_COMMAND_STRUCT cmd; /* global command struct */ + +/* If the boot drive number is undefined, we assume it's drive 0 */ +#ifndef CONFIG_SYS_FDC_DRIVE_NUMBER +#define CONFIG_SYS_FDC_DRIVE_NUMBER 0 +#endif + +/* Hardware access */ +#ifndef CONFIG_SYS_ISA_IO_STRIDE +#define CONFIG_SYS_ISA_IO_STRIDE 1 +#endif + +#ifndef CONFIG_SYS_ISA_IO_OFFSET +#define CONFIG_SYS_ISA_IO_OFFSET 0 +#endif + +/* Supporting Functions */ +/* reads a Register of the FDC */ +unsigned char read_fdc_reg(unsigned int addr) +{ + volatile unsigned char *val = + (volatile unsigned char *)(CONFIG_SYS_ISA_IO_BASE_ADDRESS + + (addr * CONFIG_SYS_ISA_IO_STRIDE) + + CONFIG_SYS_ISA_IO_OFFSET); + + return val [0]; +} + +/* writes a Register of the FDC */ +void write_fdc_reg(unsigned int addr, unsigned char val) +{ + volatile unsigned char *tmp = + (volatile unsigned char *)(CONFIG_SYS_ISA_IO_BASE_ADDRESS + + (addr * CONFIG_SYS_ISA_IO_STRIDE) + + CONFIG_SYS_ISA_IO_OFFSET); + tmp[0]=val; +} + +/* waits for an interrupt (polling) */ +int wait_for_fdc_int(void) +{ + unsigned long timeout; + timeout = FDC_TIME_OUT; + while((read_fdc_reg(FDC_SRA)&0x80)==0) { + timeout--; + udelay(10); + if(timeout==0) /* timeout occured */ + return false; + } + return true; +} + +/* reads a byte from the FIFO of the FDC and checks direction and RQM bit + of the MSR. returns -1 if timeout, or byte if ok */ +int read_fdc_byte(void) +{ + unsigned long timeout; + timeout = FDC_TIME_OUT; + while((read_fdc_reg(FDC_MSR)&0xC0)!=0xC0) { + /* direction out and ready */ + udelay(10); + timeout--; + if(timeout==0) /* timeout occured */ + return -1; + } + return read_fdc_reg(FDC_FIFO); +} + +/* if the direction of the FIFO is wrong, this routine is used to + empty the FIFO. Should _not_ be used */ +int fdc_need_more_output(void) +{ + unsigned char c; + while((read_fdc_reg(FDC_MSR)&0xC0)==0xC0) { + c=(unsigned char)read_fdc_byte(); + printf("Error: more output: %x\n",c); + } + return true; +} + + +/* writes a byte to the FIFO of the FDC and checks direction and RQM bit + of the MSR */ +int write_fdc_byte(unsigned char val) +{ + unsigned long timeout; + timeout = FDC_TIME_OUT; + while((read_fdc_reg(FDC_MSR)&0xC0)!=0x80) { + /* direction in and ready for byte */ + timeout--; + udelay(10); + fdc_need_more_output(); + if(timeout==0) /* timeout occured */ + return false; + } + write_fdc_reg(FDC_FIFO,val); + return true; +} + +/* sets up all FDC commands and issues it to the FDC. If + the command causes direct results (no Execution Phase) + the result is be read as well. */ + +int fdc_issue_cmd(FDC_COMMAND_STRUCT *pCMD,FD_GEO_STRUCT *pFG) +{ + int i; + unsigned long head,track,sect,timeout; + track = pCMD->blnr / (pFG->sect * pFG->head); /* track nr */ + sect = pCMD->blnr % (pFG->sect * pFG->head); /* remaining blocks */ + head = sect / pFG->sect; /* head nr */ + sect = sect % pFG->sect; /* remaining blocks */ + sect++; /* sectors are 1 based */ + PRINTF("Cmd 0x%02x Track %ld, Head %ld, Sector %ld, Drive %d (blnr %ld)\n", + pCMD->cmd[0],track,head,sect,pCMD->drive,pCMD->blnr); + + if(head|=0) { /* max heads = 2 */ + pCMD->cmd[DRIVE]=pCMD->drive | 0x04; /* head 1 */ + pCMD->cmd[HEAD]=(unsigned char) head; /* head register */ + } + else { + pCMD->cmd[DRIVE]=pCMD->drive; /* head 0 */ + pCMD->cmd[HEAD]=(unsigned char) head; /* head register */ + } + pCMD->cmd[TRACK]=(unsigned char) track; /* track */ + switch (pCMD->cmd[COMMAND]) { + case FDC_CMD_READ: + pCMD->cmd[SECTOR]=(unsigned char) sect; /* sector */ + pCMD->cmd[SECTOR_SIZE]=pFG->sect_code; /* sector size code */ + pCMD->cmd[LAST_TRACK]=pFG->sect; /* End of track */ + pCMD->cmd[GAP]=pFG->gap; /* gap */ + pCMD->cmd[DTL]=0xFF; /* DTL */ + pCMD->cmdlen=FDC_CMD_READ_LEN; + pCMD->cmd[COMMAND]|=(FDC_MFM_MODE<<6); /* set MFM bit */ + pCMD->cmd[COMMAND]|=(FDC_SKIP_MODE<<5); /* set Skip bit */ + pCMD->resultlen=0; /* result only after execution */ + break; + case FDC_CMD_SEEK: + pCMD->cmdlen=FDC_CMD_SEEK_LEN; + pCMD->resultlen=0; /* no result */ + break; + case FDC_CMD_CONFIGURE: + pCMD->cmd[CONFIG0]=0; + pCMD->cmd[CONFIG1]=FDC_CONFIGURE; /* FIFO Threshold, Poll, Enable FIFO */ + pCMD->cmd[CONFIG2]=FDC_PRE_TRK; /* Precompensation Track */ + pCMD->cmdlen=FDC_CMD_CONFIGURE_LEN; + pCMD->resultlen=0; /* no result */ + break; + case FDC_CMD_SPECIFY: + pCMD->cmd[SPEC_HUTSRT]=pFG->spec1; + pCMD->cmd[SPEC_HLT]=(pFG->hlt)<<1; /* head load time */ + if(pCMD->dma==0) + pCMD->cmd[SPEC_HLT]|=0x1; /* no dma */ + pCMD->cmdlen=FDC_CMD_SPECIFY_LEN; + pCMD->resultlen=0; /* no result */ + break; + case FDC_CMD_DUMP_REG: + pCMD->cmdlen=FDC_CMD_DUMP_REG_LEN; + pCMD->resultlen=10; /* 10 byte result */ + break; + case FDC_CMD_READ_ID: + pCMD->cmd[COMMAND]|=(FDC_MFM_MODE<<6); /* set MFM bit */ + pCMD->cmdlen=FDC_CMD_READ_ID_LEN; + pCMD->resultlen=7; /* 7 byte result */ + break; + case FDC_CMD_RECALIBRATE: + pCMD->cmd[DRIVE]&=0x03; /* don't set the head bit */ + pCMD->cmdlen=FDC_CMD_RECALIBRATE_LEN; + pCMD->resultlen=0; /* no result */ + break; + break; + case FDC_CMD_SENSE_INT: + pCMD->cmdlen=FDC_CMD_SENSE_INT_LEN; + pCMD->resultlen=2; + break; + } + for(i=0;i<pCMD->cmdlen;i++) { + /* PRINTF("write cmd%d = 0x%02X\n",i,pCMD->cmd[i]); */ + if (write_fdc_byte(pCMD->cmd[i]) == false) { + PRINTF("Error: timeout while issue cmd%d\n",i); + return false; + } + } + timeout=FDC_TIME_OUT; + for(i=0;i<pCMD->resultlen;i++) { + while((read_fdc_reg(FDC_MSR)&0xC0)!=0xC0) { + timeout--; + if(timeout==0) { + PRINTF(" timeout while reading result%d MSR=0x%02X\n",i,read_fdc_reg(FDC_MSR)); + return false; + } + } + pCMD->result[i]=(unsigned char)read_fdc_byte(); + } + return true; +} + +/* selects the drive assigned in the cmd structur and + switches on the Motor */ +void select_fdc_drive(FDC_COMMAND_STRUCT *pCMD) +{ + unsigned char val; + + val=(1<<(4+pCMD->drive))|pCMD->drive|0xC; /* set reset, dma gate and motor bits */ + if((read_fdc_reg(FDC_DOR)&val)!=val) { + write_fdc_reg(FDC_DOR,val); + for(val=0;val<255;val++) + udelay(500); /* wait some time to start motor */ + } +} + +/* switches off the Motor of the specified drive */ +void stop_fdc_drive(FDC_COMMAND_STRUCT *pCMD) +{ + unsigned char val; + + val=(1<<(4+pCMD->drive))|pCMD->drive; /* sets motor bits */ + write_fdc_reg(FDC_DOR,(read_fdc_reg(FDC_DOR)&~val)); +} + +/* issues a recalibrate command, waits for interrupt and + * issues a sense_interrupt */ +int fdc_recalibrate(FDC_COMMAND_STRUCT *pCMD,FD_GEO_STRUCT *pFG) +{ + pCMD->cmd[COMMAND]=FDC_CMD_RECALIBRATE; + if (fdc_issue_cmd(pCMD, pFG) == false) + return false; + while (wait_for_fdc_int() != true); + + pCMD->cmd[COMMAND]=FDC_CMD_SENSE_INT; + return(fdc_issue_cmd(pCMD,pFG)); +} + +/* issues a recalibrate command, waits for interrupt and + * issues a sense_interrupt */ +int fdc_seek(FDC_COMMAND_STRUCT *pCMD,FD_GEO_STRUCT *pFG) +{ + pCMD->cmd[COMMAND]=FDC_CMD_SEEK; + if (fdc_issue_cmd(pCMD, pFG) == false) + return false; + while (wait_for_fdc_int() != true); + + pCMD->cmd[COMMAND]=FDC_CMD_SENSE_INT; + return(fdc_issue_cmd(pCMD,pFG)); +} + +/* terminates current command, by not servicing the FIFO + * waits for interrupt and fills in the result bytes */ +int fdc_terminate(FDC_COMMAND_STRUCT *pCMD) +{ + int i; + for(i=0;i<100;i++) + udelay(500); /* wait 500usec for fifo overrun */ + while((read_fdc_reg(FDC_SRA)&0x80)==0x00); /* wait as long as no int has occured */ + for(i=0;i<7;i++) { + pCMD->result[i]=(unsigned char)read_fdc_byte(); + } + return true; +} + +/* reads data from FDC, seek commands are issued automatic */ +int fdc_read_data(unsigned char *buffer, unsigned long blocks,FDC_COMMAND_STRUCT *pCMD, FD_GEO_STRUCT *pFG) +{ + /* first seek to start address */ + unsigned long len,readblk,i,timeout,ii,offset; + unsigned char c,retriesrw,retriescal; + unsigned char *bufferw; /* working buffer */ + int sect_size; + int flags; + + flags=disable_interrupts(); /* switch off all Interrupts */ + select_fdc_drive(pCMD); /* switch on drive */ + sect_size=0x080<<pFG->sect_code; + retriesrw=0; + retriescal=0; + offset=0; + if (fdc_seek(pCMD, pFG) == false) { + stop_fdc_drive(pCMD); + if (flags) + enable_interrupts(); + return false; + } + if((pCMD->result[STATUS_0]&0x20)!=0x20) { + printf("Seek error Status: %02X\n",pCMD->result[STATUS_0]); + stop_fdc_drive(pCMD); + if (flags) + enable_interrupts(); + return false; + } + /* now determine the next seek point */ + /* lastblk=pCMD->blnr + blocks; */ + /* readblk=(pFG->head*pFG->sect)-(pCMD->blnr%(pFG->head*pFG->sect)); */ + readblk=pFG->sect-(pCMD->blnr%pFG->sect); + PRINTF("1st nr of block possible read %ld start %ld\n",readblk,pCMD->blnr); + if(readblk>blocks) /* is end within 1st track */ + readblk=blocks; /* yes, correct it */ + PRINTF("we read %ld blocks start %ld\n",readblk,pCMD->blnr); + bufferw = &buffer[0]; /* setup working buffer */ + do { +retryrw: + len=sect_size * readblk; + pCMD->cmd[COMMAND]=FDC_CMD_READ; + if (fdc_issue_cmd(pCMD, pFG) == false) { + stop_fdc_drive(pCMD); + if (flags) + enable_interrupts(); + return false; + } + for (i=0;i<len;i++) { + timeout=FDC_TIME_OUT; + do { + c=read_fdc_reg(FDC_MSR); + if((c&0xC0)==0xC0) { + bufferw[i]=read_fdc_reg(FDC_FIFO); + break; + } + if((c&0xC0)==0x80) { /* output */ + PRINTF("Transfer error transfered: at %ld, MSR=%02X\n",i,c); + if(i>6) { + for(ii=0;ii<7;ii++) { + pCMD->result[ii]=bufferw[(i-7+ii)]; + } /* for */ + } + if(retriesrw++>FDC_RW_RETRIES) { + if (retriescal++>FDC_CAL_RETRIES) { + stop_fdc_drive(pCMD); + if (flags) + enable_interrupts(); + return false; + } + else { + PRINTF(" trying to recalibrate Try %d\n",retriescal); + if (fdc_recalibrate(pCMD, pFG) == false) { + stop_fdc_drive(pCMD); + if (flags) + enable_interrupts(); + return false; + } + retriesrw=0; + goto retrycal; + } /* else >FDC_CAL_RETRIES */ + } + else { + PRINTF("Read retry %d\n",retriesrw); + goto retryrw; + } /* else >FDC_RW_RETRIES */ + }/* if output */ + timeout--; + } while (true); + } /* for len */ + /* the last sector of a track or all data has been read, + * we need to get the results */ + fdc_terminate(pCMD); + offset+=(sect_size*readblk); /* set up buffer pointer */ + bufferw = &buffer[offset]; + pCMD->blnr+=readblk; /* update current block nr */ + blocks-=readblk; /* update blocks */ + if(blocks==0) + break; /* we are finish */ + /* setup new read blocks */ + /* readblk=pFG->head*pFG->sect; */ + readblk=pFG->sect; + if(readblk>blocks) + readblk=blocks; +retrycal: + /* a seek is necessary */ + if (fdc_seek(pCMD, pFG) == false) { + stop_fdc_drive(pCMD); + if (flags) + enable_interrupts(); + return false; + } + if((pCMD->result[STATUS_0]&0x20)!=0x20) { + PRINTF("Seek error Status: %02X\n",pCMD->result[STATUS_0]); + stop_fdc_drive(pCMD); + return false; + } + } while (true); /* start over */ + stop_fdc_drive(pCMD); /* switch off drive */ + if (flags) + enable_interrupts(); + return true; +} + +/* Scan all drives and check if drive is present and disk is inserted */ +int fdc_check_drive(FDC_COMMAND_STRUCT *pCMD, FD_GEO_STRUCT *pFG) +{ + int i,drives,state; + /* OK procedure of data book is satisfied. + * trying to get some information over the drives */ + state=0; /* no drives, no disks */ + for(drives=0;drives<4;drives++) { + pCMD->drive=drives; + select_fdc_drive(pCMD); + pCMD->blnr=0; /* set to the 1st block */ + if (fdc_recalibrate(pCMD, pFG) == false) + continue; + if((pCMD->result[STATUS_0]&0x10)==0x10) + continue; + /* ok drive connected check for disk */ + state|=(1<<drives); + pCMD->blnr=pFG->size; /* set to the last block */ + if (fdc_seek(pCMD, pFG) == false) + continue; + pCMD->blnr=0; /* set to the 1st block */ + if (fdc_recalibrate(pCMD, pFG) == false) + continue; + pCMD->cmd[COMMAND]=FDC_CMD_READ_ID; + if (fdc_issue_cmd(pCMD, pFG) == false) + continue; + state|=(0x10<<drives); + } + stop_fdc_drive(pCMD); + for(i=0;i<4;i++) { + PRINTF("Floppy Drive %d %sconnected %sDisk inserted %s\n",i, + ((state&(1<<i))==(1<<i)) ? "":"not ", + ((state&(0x10<<i))==(0x10<<i)) ? "":"no ", + ((state&(0x10<<i))==(0x10<<i)) ? pFG->name : ""); + } + pCMD->flags=state; + return true; +} + + +/************************************************************************** +* int fdc_setup +* setup the fdc according the datasheet +* assuming in PS2 Mode +*/ +int fdc_setup(int drive, FDC_COMMAND_STRUCT *pCMD, FD_GEO_STRUCT *pFG) +{ + int i; + +#ifdef CONFIG_SYS_FDC_HW_INIT + fdc_hw_init (); +#endif + /* first, we reset the FDC via the DOR */ + write_fdc_reg(FDC_DOR,0x00); + for(i=0; i<255; i++) /* then we wait some time */ + udelay(500); + /* then, we clear the reset in the DOR */ + pCMD->drive=drive; + select_fdc_drive(pCMD); + /* initialize the CCR */ + write_fdc_reg(FDC_CCR,pFG->rate); + /* then initialize the DSR */ + write_fdc_reg(FDC_DSR,pFG->rate); + if (wait_for_fdc_int() == false) { + PRINTF("Time Out after writing CCR\n"); + return false; + } + /* now issue sense Interrupt and status command + * assuming only one drive present (drive 0) */ + pCMD->dma=0; /* we don't use any dma at all */ + for(i=0;i<4;i++) { + /* issue sense interrupt for all 4 possible drives */ + pCMD->cmd[COMMAND]=FDC_CMD_SENSE_INT; + if (fdc_issue_cmd(pCMD, pFG) == false) { + PRINTF("Sense Interrupt for drive %d failed\n",i); + } + } + /* issue the configure command */ + pCMD->drive=drive; + select_fdc_drive(pCMD); + pCMD->cmd[COMMAND]=FDC_CMD_CONFIGURE; + if (fdc_issue_cmd(pCMD, pFG) == false) { + PRINTF(" configure timeout\n"); + stop_fdc_drive(pCMD); + return false; + } + /* issue specify command */ + pCMD->cmd[COMMAND]=FDC_CMD_SPECIFY; + if (fdc_issue_cmd(pCMD, pFG) == false) { + PRINTF(" specify timeout\n"); + stop_fdc_drive(pCMD); + return false; + + } + /* then, we clear the reset in the DOR */ + /* fdc_check_drive(pCMD,pFG); */ + /* write_fdc_reg(FDC_DOR,0x04); */ + + return true; +} + +/**************************************************************************** + * main routine do_fdcboot + */ +int do_fdcboot (cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + FD_GEO_STRUCT *pFG = (FD_GEO_STRUCT *)floppy_type; + FDC_COMMAND_STRUCT *pCMD = &cmd; + unsigned long addr,imsize; + image_header_t *hdr; /* used for fdc boot */ + unsigned char boot_drive; + int i,nrofblk; +#if defined(CONFIG_FIT) + const void *fit_hdr = NULL; +#endif + + switch (argc) { + case 1: + addr = CONFIG_SYS_LOAD_ADDR; + boot_drive=CONFIG_SYS_FDC_DRIVE_NUMBER; + break; + case 2: + addr = simple_strtoul(argv[1], NULL, 16); + boot_drive=CONFIG_SYS_FDC_DRIVE_NUMBER; + break; + case 3: + addr = simple_strtoul(argv[1], NULL, 16); + boot_drive=simple_strtoul(argv[2], NULL, 10); + break; + default: + return CMD_RET_USAGE; + } + /* setup FDC and scan for drives */ + if (fdc_setup(boot_drive, pCMD, pFG) == false) { + printf("\n** Error in setup FDC **\n"); + return 1; + } + if (fdc_check_drive(pCMD, pFG) == false) { + printf("\n** Error in check_drives **\n"); + return 1; + } + if((pCMD->flags&(1<<boot_drive))==0) { + /* drive not available */ + printf("\n** Drive %d not availabe **\n",boot_drive); + return 1; + } + if((pCMD->flags&(0x10<<boot_drive))==0) { + /* no disk inserted */ + printf("\n** No disk inserted in drive %d **\n",boot_drive); + return 1; + } + /* ok, we have a valid source */ + pCMD->drive=boot_drive; + /* read first block */ + pCMD->blnr=0; + if (fdc_read_data((unsigned char *)addr, 1, pCMD, pFG) == false) { + printf("\nRead error:"); + for(i=0;i<7;i++) + printf("result%d: 0x%02X\n",i,pCMD->result[i]); + return 1; + } + + switch (genimg_get_format ((void *)addr)) { + case IMAGE_FORMAT_LEGACY: + hdr = (image_header_t *)addr; + image_print_contents (hdr); + + imsize = image_get_image_size (hdr); + break; +#if defined(CONFIG_FIT) + case IMAGE_FORMAT_FIT: + fit_hdr = (const void *)addr; + puts ("Fit image detected...\n"); + + imsize = fit_get_size (fit_hdr); + break; +#endif + default: + puts ("** Unknown image type\n"); + return 1; + } + + nrofblk=imsize/512; + if((imsize%512)>0) + nrofblk++; + printf("Loading %ld Bytes (%d blocks) at 0x%08lx..\n",imsize,nrofblk,addr); + pCMD->blnr=0; + if (fdc_read_data((unsigned char *)addr, nrofblk, pCMD, pFG) == false) { + /* read image block */ + printf("\nRead error:"); + for(i=0;i<7;i++) + printf("result%d: 0x%02X\n",i,pCMD->result[i]); + return 1; + } + printf("OK %ld Bytes loaded.\n",imsize); + + flush_cache (addr, imsize); + +#if defined(CONFIG_FIT) + /* This cannot be done earlier, we need complete FIT image in RAM first */ + if (genimg_get_format ((void *)addr) == IMAGE_FORMAT_FIT) { + if (!fit_check_format (fit_hdr)) { + puts ("** Bad FIT image format\n"); + return 1; + } + fit_print_contents (fit_hdr); + } +#endif + + /* Loading ok, update default load address */ + load_addr = addr; + + return bootm_maybe_autostart(cmdtp, argv[0]); +} + +U_BOOT_CMD( + fdcboot, 3, 1, do_fdcboot, + "boot from floppy device", + "loadAddr drive" +); diff --git a/qemu/roms/u-boot/common/cmd_fdt.c b/qemu/roms/u-boot/common/cmd_fdt.c new file mode 100644 index 000000000..a6744ed9c --- /dev/null +++ b/qemu/roms/u-boot/common/cmd_fdt.c @@ -0,0 +1,1042 @@ +/* + * (C) Copyright 2007 + * Gerald Van Baren, Custom IDEAS, vanbaren@cideas.com + * Based on code written by: + * Pantelis Antoniou <pantelis.antoniou@gmail.com> and + * Matthew McClintock <msm@freescale.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <command.h> +#include <linux/ctype.h> +#include <linux/types.h> +#include <asm/global_data.h> +#include <libfdt.h> +#include <fdt_support.h> +#include <asm/io.h> + +#define MAX_LEVEL 32 /* how deeply nested we will go */ +#define SCRATCHPAD 1024 /* bytes of scratchpad memory */ +#ifndef CONFIG_CMD_FDT_MAX_DUMP +#define CONFIG_CMD_FDT_MAX_DUMP 64 +#endif + +/* + * Global data (for the gd->bd) + */ +DECLARE_GLOBAL_DATA_PTR; + +static int fdt_valid(struct fdt_header **blobp); +static int fdt_parse_prop(char *const*newval, int count, char *data, int *len); +static int fdt_print(const char *pathp, char *prop, int depth); +static int is_printable_string(const void *data, int len); + +/* + * The working_fdt points to our working flattened device tree. + */ +struct fdt_header *working_fdt; + +void set_working_fdt_addr(void *addr) +{ + void *buf; + + buf = map_sysmem((ulong)addr, 0); + working_fdt = buf; + setenv_addr("fdtaddr", addr); +} + +/* + * Get a value from the fdt and format it to be set in the environment + */ +static int fdt_value_setenv(const void *nodep, int len, const char *var) +{ + if (is_printable_string(nodep, len)) + setenv(var, (void *)nodep); + else if (len == 4) { + char buf[11]; + + sprintf(buf, "0x%08X", *(uint32_t *)nodep); + setenv(var, buf); + } else if (len%4 == 0 && len <= 20) { + /* Needed to print things like sha1 hashes. */ + char buf[41]; + int i; + + for (i = 0; i < len; i += sizeof(unsigned int)) + sprintf(buf + (i * 2), "%08x", + *(unsigned int *)(nodep + i)); + setenv(var, buf); + } else { + printf("error: unprintable value\n"); + return 1; + } + return 0; +} + +/* + * Flattened Device Tree command, see the help for parameter definitions. + */ +static int do_fdt(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + if (argc < 2) + return CMD_RET_USAGE; + + /* + * Set the address of the fdt + */ + if (argv[1][0] == 'a') { + unsigned long addr; + int control = 0; + struct fdt_header *blob; + /* + * Set the address [and length] of the fdt. + */ + argc -= 2; + argv += 2; +/* Temporary #ifdef - some archs don't have fdt_blob yet */ +#ifdef CONFIG_OF_CONTROL + if (argc && !strcmp(*argv, "-c")) { + control = 1; + argc--; + argv++; + } +#endif + if (argc == 0) { + if (control) + blob = (struct fdt_header *)gd->fdt_blob; + else + blob = working_fdt; + if (!blob || !fdt_valid(&blob)) + return 1; + printf("The address of the fdt is %#08lx\n", + control ? (ulong)blob : + getenv_hex("fdtaddr", 0)); + return 0; + } + + addr = simple_strtoul(argv[0], NULL, 16); + blob = map_sysmem(addr, 0); + if (!fdt_valid(&blob)) + return 1; + if (control) + gd->fdt_blob = blob; + else + set_working_fdt_addr(blob); + + if (argc >= 2) { + int len; + int err; + /* + * Optional new length + */ + len = simple_strtoul(argv[1], NULL, 16); + if (len < fdt_totalsize(blob)) { + printf ("New length %d < existing length %d, " + "ignoring.\n", + len, fdt_totalsize(blob)); + } else { + /* + * Open in place with a new length. + */ + err = fdt_open_into(blob, blob, len); + if (err != 0) { + printf ("libfdt fdt_open_into(): %s\n", + fdt_strerror(err)); + } + } + } + + return CMD_RET_SUCCESS; + } + + if (!working_fdt) { + puts( + "No FDT memory address configured. Please configure\n" + "the FDT address via \"fdt addr <address>\" command.\n" + "Aborting!\n"); + return CMD_RET_FAILURE; + } + + /* + * Move the working_fdt + */ + if (strncmp(argv[1], "mo", 2) == 0) { + struct fdt_header *newaddr; + int len; + int err; + + if (argc < 4) + return CMD_RET_USAGE; + + /* + * Set the address and length of the fdt. + */ + working_fdt = (struct fdt_header *)simple_strtoul(argv[2], NULL, 16); + if (!fdt_valid(&working_fdt)) + return 1; + + newaddr = (struct fdt_header *)simple_strtoul(argv[3],NULL,16); + + /* + * If the user specifies a length, use that. Otherwise use the + * current length. + */ + if (argc <= 4) { + len = fdt_totalsize(working_fdt); + } else { + len = simple_strtoul(argv[4], NULL, 16); + if (len < fdt_totalsize(working_fdt)) { + printf ("New length 0x%X < existing length " + "0x%X, aborting.\n", + len, fdt_totalsize(working_fdt)); + return 1; + } + } + + /* + * Copy to the new location. + */ + err = fdt_open_into(working_fdt, newaddr, len); + if (err != 0) { + printf ("libfdt fdt_open_into(): %s\n", + fdt_strerror(err)); + return 1; + } + working_fdt = newaddr; + + /* + * Make a new node + */ + } else if (strncmp(argv[1], "mk", 2) == 0) { + char *pathp; /* path */ + char *nodep; /* new node to add */ + int nodeoffset; /* node offset from libfdt */ + int err; + + /* + * Parameters: Node path, new node to be appended to the path. + */ + if (argc < 4) + return CMD_RET_USAGE; + + pathp = argv[2]; + nodep = argv[3]; + + nodeoffset = fdt_path_offset (working_fdt, pathp); + if (nodeoffset < 0) { + /* + * Not found or something else bad happened. + */ + printf ("libfdt fdt_path_offset() returned %s\n", + fdt_strerror(nodeoffset)); + return 1; + } + err = fdt_add_subnode(working_fdt, nodeoffset, nodep); + if (err < 0) { + printf ("libfdt fdt_add_subnode(): %s\n", + fdt_strerror(err)); + return 1; + } + + /* + * Set the value of a property in the working_fdt. + */ + } else if (argv[1][0] == 's') { + char *pathp; /* path */ + char *prop; /* property */ + int nodeoffset; /* node offset from libfdt */ + static char data[SCRATCHPAD]; /* storage for the property */ + int len; /* new length of the property */ + int ret; /* return value */ + + /* + * Parameters: Node path, property, optional value. + */ + if (argc < 4) + return CMD_RET_USAGE; + + pathp = argv[2]; + prop = argv[3]; + if (argc == 4) { + len = 0; + } else { + ret = fdt_parse_prop(&argv[4], argc - 4, data, &len); + if (ret != 0) + return ret; + } + + nodeoffset = fdt_path_offset (working_fdt, pathp); + if (nodeoffset < 0) { + /* + * Not found or something else bad happened. + */ + printf ("libfdt fdt_path_offset() returned %s\n", + fdt_strerror(nodeoffset)); + return 1; + } + + ret = fdt_setprop(working_fdt, nodeoffset, prop, data, len); + if (ret < 0) { + printf ("libfdt fdt_setprop(): %s\n", fdt_strerror(ret)); + return 1; + } + + /******************************************************************** + * Get the value of a property in the working_fdt. + ********************************************************************/ + } else if (argv[1][0] == 'g') { + char *subcmd; /* sub-command */ + char *pathp; /* path */ + char *prop; /* property */ + char *var; /* variable to store result */ + int nodeoffset; /* node offset from libfdt */ + const void *nodep; /* property node pointer */ + int len = 0; /* new length of the property */ + + /* + * Parameters: Node path, property, optional value. + */ + if (argc < 5) + return CMD_RET_USAGE; + + subcmd = argv[2]; + + if (argc < 6 && subcmd[0] != 's') + return CMD_RET_USAGE; + + var = argv[3]; + pathp = argv[4]; + prop = argv[5]; + + nodeoffset = fdt_path_offset(working_fdt, pathp); + if (nodeoffset < 0) { + /* + * Not found or something else bad happened. + */ + printf("libfdt fdt_path_offset() returned %s\n", + fdt_strerror(nodeoffset)); + return 1; + } + + if (subcmd[0] == 'n' || (subcmd[0] == 's' && argc == 5)) { + int reqIndex = -1; + int startDepth = fdt_node_depth( + working_fdt, nodeoffset); + int curDepth = startDepth; + int curIndex = -1; + int nextNodeOffset = fdt_next_node( + working_fdt, nodeoffset, &curDepth); + + if (subcmd[0] == 'n') + reqIndex = simple_strtoul(argv[5], NULL, 16); + + while (curDepth > startDepth) { + if (curDepth == startDepth + 1) + curIndex++; + if (subcmd[0] == 'n' && curIndex == reqIndex) { + const char *nodeName = fdt_get_name( + working_fdt, nextNodeOffset, NULL); + + setenv(var, (char *)nodeName); + return 0; + } + nextNodeOffset = fdt_next_node( + working_fdt, nextNodeOffset, &curDepth); + if (nextNodeOffset < 0) + break; + } + if (subcmd[0] == 's') { + /* get the num nodes at this level */ + setenv_ulong(var, curIndex + 1); + } else { + /* node index not found */ + printf("libfdt node not found\n"); + return 1; + } + } else { + nodep = fdt_getprop( + working_fdt, nodeoffset, prop, &len); + if (len == 0) { + /* no property value */ + setenv(var, ""); + return 0; + } else if (len > 0) { + if (subcmd[0] == 'v') { + int ret; + + ret = fdt_value_setenv(nodep, len, var); + if (ret != 0) + return ret; + } else if (subcmd[0] == 'a') { + /* Get address */ + char buf[11]; + + sprintf(buf, "0x%p", nodep); + setenv(var, buf); + } else if (subcmd[0] == 's') { + /* Get size */ + char buf[11]; + + sprintf(buf, "0x%08X", len); + setenv(var, buf); + } else + return CMD_RET_USAGE; + return 0; + } else { + printf("libfdt fdt_getprop(): %s\n", + fdt_strerror(len)); + return 1; + } + } + + /* + * Print (recursive) / List (single level) + */ + } else if ((argv[1][0] == 'p') || (argv[1][0] == 'l')) { + int depth = MAX_LEVEL; /* how deep to print */ + char *pathp; /* path */ + char *prop; /* property */ + int ret; /* return value */ + static char root[2] = "/"; + + /* + * list is an alias for print, but limited to 1 level + */ + if (argv[1][0] == 'l') { + depth = 1; + } + + /* + * Get the starting path. The root node is an oddball, + * the offset is zero and has no name. + */ + if (argc == 2) + pathp = root; + else + pathp = argv[2]; + if (argc > 3) + prop = argv[3]; + else + prop = NULL; + + ret = fdt_print(pathp, prop, depth); + if (ret != 0) + return ret; + + /* + * Remove a property/node + */ + } else if (strncmp(argv[1], "rm", 2) == 0) { + int nodeoffset; /* node offset from libfdt */ + int err; + + /* + * Get the path. The root node is an oddball, the offset + * is zero and has no name. + */ + nodeoffset = fdt_path_offset (working_fdt, argv[2]); + if (nodeoffset < 0) { + /* + * Not found or something else bad happened. + */ + printf ("libfdt fdt_path_offset() returned %s\n", + fdt_strerror(nodeoffset)); + return 1; + } + /* + * Do the delete. A fourth parameter means delete a property, + * otherwise delete the node. + */ + if (argc > 3) { + err = fdt_delprop(working_fdt, nodeoffset, argv[3]); + if (err < 0) { + printf("libfdt fdt_delprop(): %s\n", + fdt_strerror(err)); + return err; + } + } else { + err = fdt_del_node(working_fdt, nodeoffset); + if (err < 0) { + printf("libfdt fdt_del_node(): %s\n", + fdt_strerror(err)); + return err; + } + } + + /* + * Display header info + */ + } else if (argv[1][0] == 'h') { + u32 version = fdt_version(working_fdt); + printf("magic:\t\t\t0x%x\n", fdt_magic(working_fdt)); + printf("totalsize:\t\t0x%x (%d)\n", fdt_totalsize(working_fdt), + fdt_totalsize(working_fdt)); + printf("off_dt_struct:\t\t0x%x\n", + fdt_off_dt_struct(working_fdt)); + printf("off_dt_strings:\t\t0x%x\n", + fdt_off_dt_strings(working_fdt)); + printf("off_mem_rsvmap:\t\t0x%x\n", + fdt_off_mem_rsvmap(working_fdt)); + printf("version:\t\t%d\n", version); + printf("last_comp_version:\t%d\n", + fdt_last_comp_version(working_fdt)); + if (version >= 2) + printf("boot_cpuid_phys:\t0x%x\n", + fdt_boot_cpuid_phys(working_fdt)); + if (version >= 3) + printf("size_dt_strings:\t0x%x\n", + fdt_size_dt_strings(working_fdt)); + if (version >= 17) + printf("size_dt_struct:\t\t0x%x\n", + fdt_size_dt_struct(working_fdt)); + printf("number mem_rsv:\t\t0x%x\n", + fdt_num_mem_rsv(working_fdt)); + printf("\n"); + + /* + * Set boot cpu id + */ + } else if (strncmp(argv[1], "boo", 3) == 0) { + unsigned long tmp = simple_strtoul(argv[2], NULL, 16); + fdt_set_boot_cpuid_phys(working_fdt, tmp); + + /* + * memory command + */ + } else if (strncmp(argv[1], "me", 2) == 0) { + uint64_t addr, size; + int err; + addr = simple_strtoull(argv[2], NULL, 16); + size = simple_strtoull(argv[3], NULL, 16); + err = fdt_fixup_memory(working_fdt, addr, size); + if (err < 0) + return err; + + /* + * mem reserve commands + */ + } else if (strncmp(argv[1], "rs", 2) == 0) { + if (argv[2][0] == 'p') { + uint64_t addr, size; + int total = fdt_num_mem_rsv(working_fdt); + int j, err; + printf("index\t\t start\t\t size\n"); + printf("-------------------------------" + "-----------------\n"); + for (j = 0; j < total; j++) { + err = fdt_get_mem_rsv(working_fdt, j, &addr, &size); + if (err < 0) { + printf("libfdt fdt_get_mem_rsv(): %s\n", + fdt_strerror(err)); + return err; + } + printf(" %x\t%08x%08x\t%08x%08x\n", j, + (u32)(addr >> 32), + (u32)(addr & 0xffffffff), + (u32)(size >> 32), + (u32)(size & 0xffffffff)); + } + } else if (argv[2][0] == 'a') { + uint64_t addr, size; + int err; + addr = simple_strtoull(argv[3], NULL, 16); + size = simple_strtoull(argv[4], NULL, 16); + err = fdt_add_mem_rsv(working_fdt, addr, size); + + if (err < 0) { + printf("libfdt fdt_add_mem_rsv(): %s\n", + fdt_strerror(err)); + return err; + } + } else if (argv[2][0] == 'd') { + unsigned long idx = simple_strtoul(argv[3], NULL, 16); + int err = fdt_del_mem_rsv(working_fdt, idx); + + if (err < 0) { + printf("libfdt fdt_del_mem_rsv(): %s\n", + fdt_strerror(err)); + return err; + } + } else { + /* Unrecognized command */ + return CMD_RET_USAGE; + } + } +#ifdef CONFIG_OF_BOARD_SETUP + /* Call the board-specific fixup routine */ + else if (strncmp(argv[1], "boa", 3) == 0) + ft_board_setup(working_fdt, gd->bd); +#endif + /* Create a chosen node */ + else if (strncmp(argv[1], "cho", 3) == 0) { + unsigned long initrd_start = 0, initrd_end = 0; + + if ((argc != 2) && (argc != 4)) + return CMD_RET_USAGE; + + if (argc == 4) { + initrd_start = simple_strtoul(argv[2], NULL, 16); + initrd_end = simple_strtoul(argv[3], NULL, 16); + } + + fdt_chosen(working_fdt, 1); + fdt_initrd(working_fdt, initrd_start, initrd_end, 1); + +#if defined(CONFIG_FIT_SIGNATURE) + } else if (strncmp(argv[1], "che", 3) == 0) { + int cfg_noffset; + int ret; + unsigned long addr; + struct fdt_header *blob; + + if (!working_fdt) + return CMD_RET_FAILURE; + + if (argc > 2) { + addr = simple_strtoul(argv[2], NULL, 16); + blob = map_sysmem(addr, 0); + } else { + blob = (struct fdt_header *)gd->fdt_blob; + } + if (!fdt_valid(&blob)) + return 1; + + gd->fdt_blob = blob; + cfg_noffset = fit_conf_get_node(working_fdt, NULL); + if (!cfg_noffset) { + printf("Could not find configuration node: %s\n", + fdt_strerror(cfg_noffset)); + return CMD_RET_FAILURE; + } + + ret = fit_config_verify(working_fdt, cfg_noffset); + if (ret == 1) + return CMD_RET_SUCCESS; + else + return CMD_RET_FAILURE; +#endif + + } + /* resize the fdt */ + else if (strncmp(argv[1], "re", 2) == 0) { + fdt_resize(working_fdt); + } + else { + /* Unrecognized command */ + return CMD_RET_USAGE; + } + + return 0; +} + +/****************************************************************************/ + +/** + * fdt_valid() - Check if an FDT is valid. If not, change it to NULL + * + * @blobp: Pointer to FDT pointer + * @return 1 if OK, 0 if bad (in which case *blobp is set to NULL) + */ +static int fdt_valid(struct fdt_header **blobp) +{ + const void *blob = *blobp; + int err; + + if (blob == NULL) { + printf ("The address of the fdt is invalid (NULL).\n"); + return 0; + } + + err = fdt_check_header(blob); + if (err == 0) + return 1; /* valid */ + + if (err < 0) { + printf("libfdt fdt_check_header(): %s", fdt_strerror(err)); + /* + * Be more informative on bad version. + */ + if (err == -FDT_ERR_BADVERSION) { + if (fdt_version(blob) < + FDT_FIRST_SUPPORTED_VERSION) { + printf (" - too old, fdt %d < %d", + fdt_version(blob), + FDT_FIRST_SUPPORTED_VERSION); + } + if (fdt_last_comp_version(blob) > + FDT_LAST_SUPPORTED_VERSION) { + printf (" - too new, fdt %d > %d", + fdt_version(blob), + FDT_LAST_SUPPORTED_VERSION); + } + } + printf("\n"); + *blobp = NULL; + return 0; + } + return 1; +} + +/****************************************************************************/ + +/* + * Parse the user's input, partially heuristic. Valid formats: + * <0x00112233 4 05> - an array of cells. Numbers follow standard + * C conventions. + * [00 11 22 .. nn] - byte stream + * "string" - If the the value doesn't start with "<" or "[", it is + * treated as a string. Note that the quotes are + * stripped by the parser before we get the string. + * newval: An array of strings containing the new property as specified + * on the command line + * count: The number of strings in the array + * data: A bytestream to be placed in the property + * len: The length of the resulting bytestream + */ +static int fdt_parse_prop(char * const *newval, int count, char *data, int *len) +{ + char *cp; /* temporary char pointer */ + char *newp; /* temporary newval char pointer */ + unsigned long tmp; /* holds converted values */ + int stridx = 0; + + *len = 0; + newp = newval[0]; + + /* An array of cells */ + if (*newp == '<') { + newp++; + while ((*newp != '>') && (stridx < count)) { + /* + * Keep searching until we find that last ">" + * That way users don't have to escape the spaces + */ + if (*newp == '\0') { + newp = newval[++stridx]; + continue; + } + + cp = newp; + tmp = simple_strtoul(cp, &newp, 0); + *(__be32 *)data = __cpu_to_be32(tmp); + data += 4; + *len += 4; + + /* If the ptr didn't advance, something went wrong */ + if ((newp - cp) <= 0) { + printf("Sorry, I could not convert \"%s\"\n", + cp); + return 1; + } + + while (*newp == ' ') + newp++; + } + + if (*newp != '>') { + printf("Unexpected character '%c'\n", *newp); + return 1; + } + } else if (*newp == '[') { + /* + * Byte stream. Convert the values. + */ + newp++; + while ((stridx < count) && (*newp != ']')) { + while (*newp == ' ') + newp++; + if (*newp == '\0') { + newp = newval[++stridx]; + continue; + } + if (!isxdigit(*newp)) + break; + tmp = simple_strtoul(newp, &newp, 16); + *data++ = tmp & 0xFF; + *len = *len + 1; + } + if (*newp != ']') { + printf("Unexpected character '%c'\n", *newp); + return 1; + } + } else { + /* + * Assume it is one or more strings. Copy it into our + * data area for convenience (including the + * terminating '\0's). + */ + while (stridx < count) { + size_t length = strlen(newp) + 1; + strcpy(data, newp); + data += length; + *len += length; + newp = newval[++stridx]; + } + } + return 0; +} + +/****************************************************************************/ + +/* + * Heuristic to guess if this is a string or concatenated strings. + */ + +static int is_printable_string(const void *data, int len) +{ + const char *s = data; + + /* zero length is not */ + if (len == 0) + return 0; + + /* must terminate with zero or '\n' */ + if (s[len - 1] != '\0' && s[len - 1] != '\n') + return 0; + + /* printable or a null byte (concatenated strings) */ + while (((*s == '\0') || isprint(*s) || isspace(*s)) && (len > 0)) { + /* + * If we see a null, there are three possibilities: + * 1) If len == 1, it is the end of the string, printable + * 2) Next character also a null, not printable. + * 3) Next character not a null, continue to check. + */ + if (s[0] == '\0') { + if (len == 1) + return 1; + if (s[1] == '\0') + return 0; + } + s++; + len--; + } + + /* Not the null termination, or not done yet: not printable */ + if (*s != '\0' || (len != 0)) + return 0; + + return 1; +} + + +/* + * Print the property in the best format, a heuristic guess. Print as + * a string, concatenated strings, a byte, word, double word, or (if all + * else fails) it is printed as a stream of bytes. + */ +static void print_data(const void *data, int len) +{ + int j; + + /* no data, don't print */ + if (len == 0) + return; + + /* + * It is a string, but it may have multiple strings (embedded '\0's). + */ + if (is_printable_string(data, len)) { + puts("\""); + j = 0; + while (j < len) { + if (j > 0) + puts("\", \""); + puts(data); + j += strlen(data) + 1; + data += strlen(data) + 1; + } + puts("\""); + return; + } + + if ((len %4) == 0) { + if (len > CONFIG_CMD_FDT_MAX_DUMP) + printf("* 0x%p [0x%08x]", data, len); + else { + const __be32 *p; + + printf("<"); + for (j = 0, p = data; j < len/4; j++) + printf("0x%08x%s", fdt32_to_cpu(p[j]), + j < (len/4 - 1) ? " " : ""); + printf(">"); + } + } else { /* anything else... hexdump */ + if (len > CONFIG_CMD_FDT_MAX_DUMP) + printf("* 0x%p [0x%08x]", data, len); + else { + const u8 *s; + + printf("["); + for (j = 0, s = data; j < len; j++) + printf("%02x%s", s[j], j < len - 1 ? " " : ""); + printf("]"); + } + } +} + +/****************************************************************************/ + +/* + * Recursively print (a portion of) the working_fdt. The depth parameter + * determines how deeply nested the fdt is printed. + */ +static int fdt_print(const char *pathp, char *prop, int depth) +{ + static char tabs[MAX_LEVEL+1] = + "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t" + "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t"; + const void *nodep; /* property node pointer */ + int nodeoffset; /* node offset from libfdt */ + int nextoffset; /* next node offset from libfdt */ + uint32_t tag; /* tag */ + int len; /* length of the property */ + int level = 0; /* keep track of nesting level */ + const struct fdt_property *fdt_prop; + + nodeoffset = fdt_path_offset (working_fdt, pathp); + if (nodeoffset < 0) { + /* + * Not found or something else bad happened. + */ + printf ("libfdt fdt_path_offset() returned %s\n", + fdt_strerror(nodeoffset)); + return 1; + } + /* + * The user passed in a property as well as node path. + * Print only the given property and then return. + */ + if (prop) { + nodep = fdt_getprop (working_fdt, nodeoffset, prop, &len); + if (len == 0) { + /* no property value */ + printf("%s %s\n", pathp, prop); + return 0; + } else if (len > 0) { + printf("%s = ", prop); + print_data (nodep, len); + printf("\n"); + return 0; + } else { + printf ("libfdt fdt_getprop(): %s\n", + fdt_strerror(len)); + return 1; + } + } + + /* + * The user passed in a node path and no property, + * print the node and all subnodes. + */ + while(level >= 0) { + tag = fdt_next_tag(working_fdt, nodeoffset, &nextoffset); + switch(tag) { + case FDT_BEGIN_NODE: + pathp = fdt_get_name(working_fdt, nodeoffset, NULL); + if (level <= depth) { + if (pathp == NULL) + pathp = "/* NULL pointer error */"; + if (*pathp == '\0') + pathp = "/"; /* root is nameless */ + printf("%s%s {\n", + &tabs[MAX_LEVEL - level], pathp); + } + level++; + if (level >= MAX_LEVEL) { + printf("Nested too deep, aborting.\n"); + return 1; + } + break; + case FDT_END_NODE: + level--; + if (level <= depth) + printf("%s};\n", &tabs[MAX_LEVEL - level]); + if (level == 0) { + level = -1; /* exit the loop */ + } + break; + case FDT_PROP: + fdt_prop = fdt_offset_ptr(working_fdt, nodeoffset, + sizeof(*fdt_prop)); + pathp = fdt_string(working_fdt, + fdt32_to_cpu(fdt_prop->nameoff)); + len = fdt32_to_cpu(fdt_prop->len); + nodep = fdt_prop->data; + if (len < 0) { + printf ("libfdt fdt_getprop(): %s\n", + fdt_strerror(len)); + return 1; + } else if (len == 0) { + /* the property has no value */ + if (level <= depth) + printf("%s%s;\n", + &tabs[MAX_LEVEL - level], + pathp); + } else { + if (level <= depth) { + printf("%s%s = ", + &tabs[MAX_LEVEL - level], + pathp); + print_data (nodep, len); + printf(";\n"); + } + } + break; + case FDT_NOP: + printf("%s/* NOP */\n", &tabs[MAX_LEVEL - level]); + break; + case FDT_END: + return 1; + default: + if (level <= depth) + printf("Unknown tag 0x%08X\n", tag); + return 1; + } + nodeoffset = nextoffset; + } + return 0; +} + +/********************************************************************/ +#ifdef CONFIG_SYS_LONGHELP +static char fdt_help_text[] = + "addr [-c] <addr> [<length>] - Set the [control] fdt location to <addr>\n" +#ifdef CONFIG_OF_BOARD_SETUP + "fdt boardsetup - Do board-specific set up\n" +#endif + "fdt move <fdt> <newaddr> <length> - Copy the fdt to <addr> and make it active\n" + "fdt resize - Resize fdt to size + padding to 4k addr\n" + "fdt print <path> [<prop>] - Recursive print starting at <path>\n" + "fdt list <path> [<prop>] - Print one level starting at <path>\n" + "fdt get value <var> <path> <prop> - Get <property> and store in <var>\n" + "fdt get name <var> <path> <index> - Get name of node <index> and store in <var>\n" + "fdt get addr <var> <path> <prop> - Get start address of <property> and store in <var>\n" + "fdt get size <var> <path> [<prop>] - Get size of [<property>] or num nodes and store in <var>\n" + "fdt set <path> <prop> [<val>] - Set <property> [to <val>]\n" + "fdt mknode <path> <node> - Create a new node after <path>\n" + "fdt rm <path> [<prop>] - Delete the node or <property>\n" + "fdt header - Display header info\n" + "fdt bootcpu <id> - Set boot cpuid\n" + "fdt memory <addr> <size> - Add/Update memory node\n" + "fdt rsvmem print - Show current mem reserves\n" + "fdt rsvmem add <addr> <size> - Add a mem reserve\n" + "fdt rsvmem delete <index> - Delete a mem reserves\n" + "fdt chosen [<start> <end>] - Add/update the /chosen branch in the tree\n" + " <start>/<end> - initrd start/end addr\n" +#if defined(CONFIG_FIT_SIGNATURE) + "fdt checksign [<addr>] - check FIT signature\n" + " <start> - addr of key blob\n" + " default gd->fdt_blob\n" +#endif + "NOTE: Dereference aliases by omiting the leading '/', " + "e.g. fdt print ethernet0."; +#endif + +U_BOOT_CMD( + fdt, 255, 0, do_fdt, + "flattened device tree utility commands", fdt_help_text +); diff --git a/qemu/roms/u-boot/common/cmd_fitupd.c b/qemu/roms/u-boot/common/cmd_fitupd.c new file mode 100644 index 000000000..e811473b4 --- /dev/null +++ b/qemu/roms/u-boot/common/cmd_fitupd.c @@ -0,0 +1,38 @@ +de <net.h> + +#if !defined(CONFIG_UPDATE_TFTP) +#error "CONFIG_UPDATE_TFTP required" +#endif + +static int do_fitupd(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + ulong addr = 0Un the root directory of the source tree for details. + */ + +#include <common.h> +#include <command.h> +#include <net.h> + +#if !defined(CONFIG_UPDATE_TFTP) +#error "CONFIG_UPDATE_TFTP required" +#endif + +static int do_fitupd(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + ulong addr = 0UL; + + if (argc > 2) + return CMD_RET_USAGE; + + if (argc == 2) + addr = simple_strtoul(argv[1], NULL, 16); + + return update_tftp(addr); +} + +U_BOOT_CMD(fitupd, 2, 0, do_fitupd, + "update from FIT image", + "[addr]\n" + "\t- run update from FIT image at addr\n" + "\t or from tftp 'updatefile'" +); diff --git a/qemu/roms/u-boot/common/cmd_flash.c b/qemu/roms/u-boot/common/cmd_flash.c new file mode 100644 index 000000000..85d18bb2c --- /dev/null +++ b/qemu/roms/u-boot/common/cmd_flash.c @@ -0,0 +1,729 @@ +/* + * (C) Copyright 2000 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/* + * FLASH support + */ +#include <common.h> +#include <command.h> + +#ifdef CONFIG_HAS_DATAFLASH +#include <dataflash.h> +#endif + +#if defined(CONFIG_CMD_MTDPARTS) +#include <jffs2/jffs2.h> + +/* partition handling routines */ +int mtdparts_init(void); +int mtd_id_parse(const char *id, const char **ret_id, u8 *dev_type, u8 *dev_num); +int find_dev_and_part(const char *id, struct mtd_device **dev, + u8 *part_num, struct part_info **part); +#endif + +#ifndef CONFIG_SYS_NO_FLASH +#include <flash.h> +#include <mtd/cfi_flash.h> +extern flash_info_t flash_info[]; /* info for FLASH chips */ + +/* + * The user interface starts numbering for Flash banks with 1 + * for historical reasons. + */ + +/* + * this routine looks for an abbreviated flash range specification. + * the syntax is B:SF[-SL], where B is the bank number, SF is the first + * sector to erase, and SL is the last sector to erase (defaults to SF). + * bank numbers start at 1 to be consistent with other specs, sector numbers + * start at zero. + * + * returns: 1 - correct spec; *pinfo, *psf and *psl are + * set appropriately + * 0 - doesn't look like an abbreviated spec + * -1 - looks like an abbreviated spec, but got + * a parsing error, a number out of range, + * or an invalid flash bank. + */ +static int +abbrev_spec (char *str, flash_info_t ** pinfo, int *psf, int *psl) +{ + flash_info_t *fp; + int bank, first, last; + char *p, *ep; + + if ((p = strchr (str, ':')) == NULL) + return 0; + *p++ = '\0'; + + bank = simple_strtoul (str, &ep, 10); + if (ep == str || *ep != '\0' || + bank < 1 || bank > CONFIG_SYS_MAX_FLASH_BANKS || + (fp = &flash_info[bank - 1])->flash_id == FLASH_UNKNOWN) + return -1; + + str = p; + if ((p = strchr (str, '-')) != NULL) + *p++ = '\0'; + + first = simple_strtoul (str, &ep, 10); + if (ep == str || *ep != '\0' || first >= fp->sector_count) + return -1; + + if (p != NULL) { + last = simple_strtoul (p, &ep, 10); + if (ep == p || *ep != '\0' || + last < first || last >= fp->sector_count) + return -1; + } else { + last = first; + } + + *pinfo = fp; + *psf = first; + *psl = last; + + return 1; +} + +/* + * Take *addr in Flash and adjust it to fall on the end of its sector + */ +int flash_sect_roundb (ulong *addr) +{ + flash_info_t *info; + ulong bank, sector_end_addr; + char found; + int i; + + /* find the end addr of the sector where the *addr is */ + found = 0; + for (bank = 0; bank < CONFIG_SYS_MAX_FLASH_BANKS && !found; ++bank) { + info = &flash_info[bank]; + for (i = 0; i < info->sector_count && !found; ++i) { + /* get the end address of the sector */ + if (i == info->sector_count - 1) { + sector_end_addr = info->start[0] + + info->size - 1; + } else { + sector_end_addr = info->start[i+1] - 1; + } + + if (*addr <= sector_end_addr && + *addr >= info->start[i]) { + found = 1; + /* adjust *addr if necessary */ + if (*addr < sector_end_addr) + *addr = sector_end_addr; + } /* sector */ + } /* bank */ + } + if (!found) { + /* error, address not in flash */ + printf("Error: end address (0x%08lx) not in flash!\n", *addr); + return 1; + } + + return 0; +} + +/* + * This function computes the start and end addresses for both + * erase and protect commands. The range of the addresses on which + * either of the commands is to operate can be given in two forms: + * 1. <cmd> start end - operate on <'start', 'end') + * 2. <cmd> start +length - operate on <'start', start + length) + * If the second form is used and the end address doesn't fall on the + * sector boundary, than it will be adjusted to the next sector boundary. + * If it isn't in the flash, the function will fail (return -1). + * Input: + * arg1, arg2: address specification (i.e. both command arguments) + * Output: + * addr_first, addr_last: computed address range + * Return: + * 1: success + * -1: failure (bad format, bad address). +*/ +static int +addr_spec(char *arg1, char *arg2, ulong *addr_first, ulong *addr_last) +{ + char *ep; + char len_used; /* indicates if the "start +length" form used */ + + *addr_first = simple_strtoul(arg1, &ep, 16); + if (ep == arg1 || *ep != '\0') + return -1; + + len_used = 0; + if (arg2 && *arg2 == '+'){ + len_used = 1; + ++arg2; + } + + *addr_last = simple_strtoul(arg2, &ep, 16); + if (ep == arg2 || *ep != '\0') + return -1; + + if (len_used){ + /* + * *addr_last has the length, compute correct *addr_last + * XXX watch out for the integer overflow! Right now it is + * checked for in both the callers. + */ + *addr_last = *addr_first + *addr_last - 1; + + /* + * It may happen that *addr_last doesn't fall on the sector + * boundary. We want to round such an address to the next + * sector boundary, so that the commands don't fail later on. + */ + + if (flash_sect_roundb(addr_last) > 0) + return -1; + } /* "start +length" from used */ + + return 1; +} + +static int +flash_fill_sect_ranges (ulong addr_first, ulong addr_last, + int *s_first, int *s_last, + int *s_count ) +{ + flash_info_t *info; + ulong bank; + int rcode = 0; + + *s_count = 0; + + for (bank=0; bank < CONFIG_SYS_MAX_FLASH_BANKS; ++bank) { + s_first[bank] = -1; /* first sector to erase */ + s_last [bank] = -1; /* last sector to erase */ + } + + for (bank=0,info = &flash_info[0]; + (bank < CONFIG_SYS_MAX_FLASH_BANKS) && (addr_first <= addr_last); + ++bank, ++info) { + ulong b_end; + int sect; + short s_end; + + if (info->flash_id == FLASH_UNKNOWN) { + continue; + } + + b_end = info->start[0] + info->size - 1; /* bank end addr */ + s_end = info->sector_count - 1; /* last sector */ + + + for (sect=0; sect < info->sector_count; ++sect) { + ulong end; /* last address in current sect */ + + end = (sect == s_end) ? b_end : info->start[sect + 1] - 1; + + if (addr_first > end) + continue; + if (addr_last < info->start[sect]) + continue; + + if (addr_first == info->start[sect]) { + s_first[bank] = sect; + } + if (addr_last == end) { + s_last[bank] = sect; + } + } + if (s_first[bank] >= 0) { + if (s_last[bank] < 0) { + if (addr_last > b_end) { + s_last[bank] = s_end; + } else { + puts ("Error: end address" + " not on sector boundary\n"); + rcode = 1; + break; + } + } + if (s_last[bank] < s_first[bank]) { + puts ("Error: end sector" + " precedes start sector\n"); + rcode = 1; + break; + } + sect = s_last[bank]; + addr_first = (sect == s_end) ? b_end + 1: info->start[sect + 1]; + (*s_count) += s_last[bank] - s_first[bank] + 1; + } else if (addr_first >= info->start[0] && addr_first < b_end) { + puts ("Error: start address not on sector boundary\n"); + rcode = 1; + break; + } else if (s_last[bank] >= 0) { + puts ("Error: cannot span across banks when they are" + " mapped in reverse order\n"); + rcode = 1; + break; + } + } + + return rcode; +} +#endif /* CONFIG_SYS_NO_FLASH */ + +static int do_flinfo(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ +#ifndef CONFIG_SYS_NO_FLASH + ulong bank; +#endif + +#ifdef CONFIG_HAS_DATAFLASH + dataflash_print_info(); +#endif + +#ifndef CONFIG_SYS_NO_FLASH + if (argc == 1) { /* print info for all FLASH banks */ + for (bank=0; bank <CONFIG_SYS_MAX_FLASH_BANKS; ++bank) { + printf ("\nBank # %ld: ", bank+1); + + flash_print_info (&flash_info[bank]); + } + return 0; + } + + bank = simple_strtoul(argv[1], NULL, 16); + if ((bank < 1) || (bank > CONFIG_SYS_MAX_FLASH_BANKS)) { + printf ("Only FLASH Banks # 1 ... # %d supported\n", + CONFIG_SYS_MAX_FLASH_BANKS); + return 1; + } + printf ("\nBank # %ld: ", bank); + flash_print_info (&flash_info[bank-1]); +#endif /* CONFIG_SYS_NO_FLASH */ + return 0; +} + +static int do_flerase(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ +#ifndef CONFIG_SYS_NO_FLASH + flash_info_t *info = NULL; + ulong bank, addr_first, addr_last; + int n, sect_first = 0, sect_last = 0; +#if defined(CONFIG_CMD_MTDPARTS) + struct mtd_device *dev; + struct part_info *part; + u8 dev_type, dev_num, pnum; +#endif + int rcode = 0; + + if (argc < 2) + return CMD_RET_USAGE; + + if (strcmp(argv[1], "all") == 0) { + for (bank=1; bank<=CONFIG_SYS_MAX_FLASH_BANKS; ++bank) { + printf ("Erase Flash Bank # %ld ", bank); + info = &flash_info[bank-1]; + rcode = flash_erase (info, 0, info->sector_count-1); + } + return rcode; + } + + if ((n = abbrev_spec(argv[1], &info, §_first, §_last)) != 0) { + if (n < 0) { + puts ("Bad sector specification\n"); + return 1; + } + printf ("Erase Flash Sectors %d-%d in Bank # %zu ", + sect_first, sect_last, (info-flash_info)+1); + rcode = flash_erase(info, sect_first, sect_last); + return rcode; + } + +#if defined(CONFIG_CMD_MTDPARTS) + /* erase <part-id> - erase partition */ + if ((argc == 2) && (mtd_id_parse(argv[1], NULL, &dev_type, &dev_num) == 0)) { + mtdparts_init(); + if (find_dev_and_part(argv[1], &dev, &pnum, &part) == 0) { + if (dev->id->type == MTD_DEV_TYPE_NOR) { + bank = dev->id->num; + info = &flash_info[bank]; + addr_first = part->offset + info->start[0]; + addr_last = addr_first + part->size - 1; + + printf ("Erase Flash Partition %s, " + "bank %ld, 0x%08lx - 0x%08lx ", + argv[1], bank, addr_first, + addr_last); + + rcode = flash_sect_erase(addr_first, addr_last); + return rcode; + } + + printf("cannot erase, not a NOR device\n"); + return 1; + } + } +#endif + + if (argc != 3) + return CMD_RET_USAGE; + + if (strcmp(argv[1], "bank") == 0) { + bank = simple_strtoul(argv[2], NULL, 16); + if ((bank < 1) || (bank > CONFIG_SYS_MAX_FLASH_BANKS)) { + printf ("Only FLASH Banks # 1 ... # %d supported\n", + CONFIG_SYS_MAX_FLASH_BANKS); + return 1; + } + printf ("Erase Flash Bank # %ld ", bank); + info = &flash_info[bank-1]; + rcode = flash_erase (info, 0, info->sector_count-1); + return rcode; + } + + if (addr_spec(argv[1], argv[2], &addr_first, &addr_last) < 0){ + printf ("Bad address format\n"); + return 1; + } + + if (addr_first >= addr_last) + return CMD_RET_USAGE; + + rcode = flash_sect_erase(addr_first, addr_last); + return rcode; +#else + return 0; +#endif /* CONFIG_SYS_NO_FLASH */ +} + +#ifndef CONFIG_SYS_NO_FLASH +int flash_sect_erase (ulong addr_first, ulong addr_last) +{ + flash_info_t *info; + ulong bank; + int s_first[CONFIG_SYS_MAX_FLASH_BANKS], s_last[CONFIG_SYS_MAX_FLASH_BANKS]; + int erased = 0; + int planned; + int rcode = 0; + + rcode = flash_fill_sect_ranges (addr_first, addr_last, + s_first, s_last, &planned ); + + if (planned && (rcode == 0)) { + for (bank=0,info = &flash_info[0]; + (bank < CONFIG_SYS_MAX_FLASH_BANKS) && (rcode == 0); + ++bank, ++info) { + if (s_first[bank]>=0) { + erased += s_last[bank] - s_first[bank] + 1; + debug ("Erase Flash from 0x%08lx to 0x%08lx " + "in Bank # %ld ", + info->start[s_first[bank]], + (s_last[bank] == info->sector_count) ? + info->start[0] + info->size - 1: + info->start[s_last[bank]+1] - 1, + bank+1); + rcode = flash_erase (info, s_first[bank], s_last[bank]); + } + } + if (rcode == 0) + printf("Erased %d sectors\n", erased); + } else if (rcode == 0) { + puts ("Error: start and/or end address" + " not on sector boundary\n"); + rcode = 1; + } + return rcode; +} +#endif /* CONFIG_SYS_NO_FLASH */ + +static int do_protect(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + int rcode = 0; +#ifndef CONFIG_SYS_NO_FLASH + flash_info_t *info = NULL; + ulong bank; + int i, n, sect_first = 0, sect_last = 0; +#if defined(CONFIG_CMD_MTDPARTS) + struct mtd_device *dev; + struct part_info *part; + u8 dev_type, dev_num, pnum; +#endif +#endif /* CONFIG_SYS_NO_FLASH */ +#ifdef CONFIG_HAS_DATAFLASH + int status; +#endif +#if !defined(CONFIG_SYS_NO_FLASH) || defined(CONFIG_HAS_DATAFLASH) + int p; + ulong addr_first, addr_last; +#endif + + if (argc < 3) + return CMD_RET_USAGE; + +#if !defined(CONFIG_SYS_NO_FLASH) || defined(CONFIG_HAS_DATAFLASH) + if (strcmp(argv[1], "off") == 0) + p = 0; + else if (strcmp(argv[1], "on") == 0) + p = 1; + else + return CMD_RET_USAGE; +#endif + +#ifdef CONFIG_HAS_DATAFLASH + if ((strcmp(argv[2], "all") != 0) && (strcmp(argv[2], "bank") != 0)) { + addr_first = simple_strtoul(argv[2], NULL, 16); + addr_last = simple_strtoul(argv[3], NULL, 16); + + if (addr_dataflash(addr_first) && addr_dataflash(addr_last)) { + status = dataflash_real_protect(p,addr_first,addr_last); + if (status < 0){ + puts ("Bad DataFlash sector specification\n"); + return 1; + } + printf("%sProtect %d DataFlash Sectors\n", + p ? "" : "Un-", status); + return 0; + } + } +#endif + +#ifndef CONFIG_SYS_NO_FLASH + if (strcmp(argv[2], "all") == 0) { + for (bank=1; bank<=CONFIG_SYS_MAX_FLASH_BANKS; ++bank) { + info = &flash_info[bank-1]; + if (info->flash_id == FLASH_UNKNOWN) { + continue; + } + printf ("%sProtect Flash Bank # %ld\n", + p ? "" : "Un-", bank); + + for (i=0; i<info->sector_count; ++i) { +#if defined(CONFIG_SYS_FLASH_PROTECTION) + if (flash_real_protect(info, i, p)) + rcode = 1; + putc ('.'); +#else + info->protect[i] = p; +#endif /* CONFIG_SYS_FLASH_PROTECTION */ + } +#if defined(CONFIG_SYS_FLASH_PROTECTION) + if (!rcode) puts (" done\n"); +#endif /* CONFIG_SYS_FLASH_PROTECTION */ + } + return rcode; + } + + if ((n = abbrev_spec(argv[2], &info, §_first, §_last)) != 0) { + if (n < 0) { + puts ("Bad sector specification\n"); + return 1; + } + printf("%sProtect Flash Sectors %d-%d in Bank # %zu\n", + p ? "" : "Un-", sect_first, sect_last, + (info-flash_info)+1); + for (i = sect_first; i <= sect_last; i++) { +#if defined(CONFIG_SYS_FLASH_PROTECTION) + if (flash_real_protect(info, i, p)) + rcode = 1; + putc ('.'); +#else + info->protect[i] = p; +#endif /* CONFIG_SYS_FLASH_PROTECTION */ + } + +#if defined(CONFIG_SYS_FLASH_PROTECTION) + if (!rcode) puts (" done\n"); +#endif /* CONFIG_SYS_FLASH_PROTECTION */ + + return rcode; + } + +#if defined(CONFIG_CMD_MTDPARTS) + /* protect on/off <part-id> */ + if ((argc == 3) && (mtd_id_parse(argv[2], NULL, &dev_type, &dev_num) == 0)) { + mtdparts_init(); + if (find_dev_and_part(argv[2], &dev, &pnum, &part) == 0) { + if (dev->id->type == MTD_DEV_TYPE_NOR) { + bank = dev->id->num; + info = &flash_info[bank]; + addr_first = part->offset + info->start[0]; + addr_last = addr_first + part->size - 1; + + printf ("%sProtect Flash Partition %s, " + "bank %ld, 0x%08lx - 0x%08lx\n", + p ? "" : "Un", argv[1], + bank, addr_first, addr_last); + + rcode = flash_sect_protect (p, addr_first, addr_last); + return rcode; + } + + printf("cannot %sprotect, not a NOR device\n", + p ? "" : "un"); + return 1; + } + } +#endif + + if (argc != 4) + return CMD_RET_USAGE; + + if (strcmp(argv[2], "bank") == 0) { + bank = simple_strtoul(argv[3], NULL, 16); + if ((bank < 1) || (bank > CONFIG_SYS_MAX_FLASH_BANKS)) { + printf ("Only FLASH Banks # 1 ... # %d supported\n", + CONFIG_SYS_MAX_FLASH_BANKS); + return 1; + } + printf ("%sProtect Flash Bank # %ld\n", + p ? "" : "Un-", bank); + info = &flash_info[bank-1]; + + if (info->flash_id == FLASH_UNKNOWN) { + puts ("missing or unknown FLASH type\n"); + return 1; + } + for (i=0; i<info->sector_count; ++i) { +#if defined(CONFIG_SYS_FLASH_PROTECTION) + if (flash_real_protect(info, i, p)) + rcode = 1; + putc ('.'); +#else + info->protect[i] = p; +#endif /* CONFIG_SYS_FLASH_PROTECTION */ + } + +#if defined(CONFIG_SYS_FLASH_PROTECTION) + if (!rcode) puts (" done\n"); +#endif /* CONFIG_SYS_FLASH_PROTECTION */ + + return rcode; + } + + if (addr_spec(argv[2], argv[3], &addr_first, &addr_last) < 0){ + printf("Bad address format\n"); + return 1; + } + + if (addr_first >= addr_last) + return CMD_RET_USAGE; + + rcode = flash_sect_protect (p, addr_first, addr_last); +#endif /* CONFIG_SYS_NO_FLASH */ + return rcode; +} + +#ifndef CONFIG_SYS_NO_FLASH +int flash_sect_protect (int p, ulong addr_first, ulong addr_last) +{ + flash_info_t *info; + ulong bank; + int s_first[CONFIG_SYS_MAX_FLASH_BANKS], s_last[CONFIG_SYS_MAX_FLASH_BANKS]; + int protected, i; + int planned; + int rcode; + + rcode = flash_fill_sect_ranges( addr_first, addr_last, s_first, s_last, &planned ); + + protected = 0; + + if (planned && (rcode == 0)) { + for (bank=0,info = &flash_info[0]; bank < CONFIG_SYS_MAX_FLASH_BANKS; ++bank, ++info) { + if (info->flash_id == FLASH_UNKNOWN) { + continue; + } + + if (s_first[bank]>=0 && s_first[bank]<=s_last[bank]) { + debug ("%sProtecting sectors %d..%d in bank %ld\n", + p ? "" : "Un-", + s_first[bank], s_last[bank], bank+1); + protected += s_last[bank] - s_first[bank] + 1; + for (i=s_first[bank]; i<=s_last[bank]; ++i) { +#if defined(CONFIG_SYS_FLASH_PROTECTION) + if (flash_real_protect(info, i, p)) + rcode = 1; + putc ('.'); +#else + info->protect[i] = p; +#endif /* CONFIG_SYS_FLASH_PROTECTION */ + } + } + } +#if defined(CONFIG_SYS_FLASH_PROTECTION) + puts (" done\n"); +#endif /* CONFIG_SYS_FLASH_PROTECTION */ + + printf ("%sProtected %d sectors\n", + p ? "" : "Un-", protected); + } else if (rcode == 0) { + puts ("Error: start and/or end address" + " not on sector boundary\n"); + rcode = 1; + } + return rcode; +} +#endif /* CONFIG_SYS_NO_FLASH */ + + +/**************************************************/ +#if defined(CONFIG_CMD_MTDPARTS) +# define TMP_ERASE "erase <part-id>\n - erase partition\n" +# define TMP_PROT_ON "protect on <part-id>\n - protect partition\n" +# define TMP_PROT_OFF "protect off <part-id>\n - make partition writable\n" +#else +# define TMP_ERASE /* empty */ +# define TMP_PROT_ON /* empty */ +# define TMP_PROT_OFF /* empty */ +#endif + +U_BOOT_CMD( + flinfo, 2, 1, do_flinfo, + "print FLASH memory information", + "\n - print information for all FLASH memory banks\n" + "flinfo N\n - print information for FLASH memory bank # N" +); + +U_BOOT_CMD( + erase, 3, 0, do_flerase, + "erase FLASH memory", + "start end\n" + " - erase FLASH from addr 'start' to addr 'end'\n" + "erase start +len\n" + " - erase FLASH from addr 'start' to the end of sect " + "w/addr 'start'+'len'-1\n" + "erase N:SF[-SL]\n - erase sectors SF-SL in FLASH bank # N\n" + "erase bank N\n - erase FLASH bank # N\n" + TMP_ERASE + "erase all\n - erase all FLASH banks" +); + +U_BOOT_CMD( + protect, 4, 0, do_protect, + "enable or disable FLASH write protection", + "on start end\n" + " - protect FLASH from addr 'start' to addr 'end'\n" + "protect on start +len\n" + " - protect FLASH from addr 'start' to end of sect " + "w/addr 'start'+'len'-1\n" + "protect on N:SF[-SL]\n" + " - protect sectors SF-SL in FLASH bank # N\n" + "protect on bank N\n - protect FLASH bank # N\n" + TMP_PROT_ON + "protect on all\n - protect all FLASH banks\n" + "protect off start end\n" + " - make FLASH from addr 'start' to addr 'end' writable\n" + "protect off start +len\n" + " - make FLASH from addr 'start' to end of sect " + "w/addr 'start'+'len'-1 wrtable\n" + "protect off N:SF[-SL]\n" + " - make sectors SF-SL writable in FLASH bank # N\n" + "protect off bank N\n - make FLASH bank # N writable\n" + TMP_PROT_OFF + "protect off all\n - make all FLASH banks writable" +); + +#undef TMP_ERASE +#undef TMP_PROT_ON +#undef TMP_PROT_OFF diff --git a/qemu/roms/u-boot/common/cmd_fpga.c b/qemu/roms/u-boot/common/cmd_fpga.c new file mode 100644 index 000000000..010cd24e6 --- /dev/null +++ b/qemu/roms/u-boot/common/cmd_fpga.c @@ -0,0 +1,286 @@ +/* + * (C) Copyright 2000, 2001 + * Rich Ireland, Enterasys Networks, rireland@enterasys.com. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/* + * FPGA support + */ +#include <common.h> +#include <command.h> +#include <fpga.h> +#include <malloc.h> + +/* Local functions */ +static int fpga_get_op(char *opstr); + +/* Local defines */ +#define FPGA_NONE -1 +#define FPGA_INFO 0 +#define FPGA_LOAD 1 +#define FPGA_LOADB 2 +#define FPGA_DUMP 3 +#define FPGA_LOADMK 4 + +/* ------------------------------------------------------------------------- */ +/* command form: + * fpga <op> <device number> <data addr> <datasize> + * where op is 'load', 'dump', or 'info' + * If there is no device number field, the fpga environment variable is used. + * If there is no data addr field, the fpgadata environment variable is used. + * The info command requires no data address field. + */ +int do_fpga(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[]) +{ + int op, dev = FPGA_INVALID_DEVICE; + size_t data_size = 0; + void *fpga_data = NULL; + char *devstr = getenv("fpga"); + char *datastr = getenv("fpgadata"); + int rc = FPGA_FAIL; + int wrong_parms = 0; +#if defined(CONFIG_FIT) + const char *fit_uname = NULL; + ulong fit_addr; +#endif + + if (devstr) + dev = (int) simple_strtoul(devstr, NULL, 16); + if (datastr) + fpga_data = (void *)simple_strtoul(datastr, NULL, 16); + + switch (argc) { + case 5: /* fpga <op> <dev> <data> <datasize> */ + data_size = simple_strtoul(argv[4], NULL, 16); + + case 4: /* fpga <op> <dev> <data> */ +#if defined(CONFIG_FIT) + if (fit_parse_subimage(argv[3], (ulong)fpga_data, + &fit_addr, &fit_uname)) { + fpga_data = (void *)fit_addr; + debug("* fpga: subimage '%s' from FIT image ", + fit_uname); + debug("at 0x%08lx\n", fit_addr); + } else +#endif + { + fpga_data = (void *)simple_strtoul(argv[3], NULL, 16); + debug("* fpga: cmdline image address = 0x%08lx\n", + (ulong)fpga_data); + } + debug("%s: fpga_data = 0x%x\n", __func__, (uint)fpga_data); + + case 3: /* fpga <op> <dev | data addr> */ + dev = (int)simple_strtoul(argv[2], NULL, 16); + debug("%s: device = %d\n", __func__, dev); + /* FIXME - this is a really weak test */ + if ((argc == 3) && (dev > fpga_count())) { + /* must be buffer ptr */ + debug("%s: Assuming buffer pointer in arg 3\n", + __func__); + +#if defined(CONFIG_FIT) + if (fit_parse_subimage(argv[2], (ulong)fpga_data, + &fit_addr, &fit_uname)) { + fpga_data = (void *)fit_addr; + debug("* fpga: subimage '%s' from FIT image ", + fit_uname); + debug("at 0x%08lx\n", fit_addr); + } else +#endif + { + fpga_data = (void *)dev; + debug("* fpga: cmdline image addr = 0x%08lx\n", + (ulong)fpga_data); + } + + debug("%s: fpga_data = 0x%x\n", + __func__, (uint)fpga_data); + dev = FPGA_INVALID_DEVICE; /* reset device num */ + } + + case 2: /* fpga <op> */ + op = (int)fpga_get_op(argv[1]); + break; + + default: + debug("%s: Too many or too few args (%d)\n", __func__, argc); + op = FPGA_NONE; /* force usage display */ + break; + } + + if (dev == FPGA_INVALID_DEVICE) { + puts("FPGA device not specified\n"); + op = FPGA_NONE; + } + + switch (op) { + case FPGA_NONE: + case FPGA_INFO: + break; + case FPGA_LOAD: + case FPGA_LOADB: + case FPGA_DUMP: + if (!fpga_data || !data_size) + wrong_parms = 1; + break; + case FPGA_LOADMK: + if (!fpga_data) + wrong_parms = 1; + break; + } + + if (wrong_parms) { + puts("Wrong parameters for FPGA request\n"); + op = FPGA_NONE; + } + + switch (op) { + case FPGA_NONE: + return CMD_RET_USAGE; + + case FPGA_INFO: + rc = fpga_info(dev); + break; + + case FPGA_LOAD: + rc = fpga_load(dev, fpga_data, data_size); + break; + + case FPGA_LOADB: + rc = fpga_loadbitstream(dev, fpga_data, data_size); + break; + + case FPGA_LOADMK: + switch (genimg_get_format(fpga_data)) { + case IMAGE_FORMAT_LEGACY: + { + image_header_t *hdr = + (image_header_t *)fpga_data; + ulong data; + uint8_t comp; + + comp = image_get_comp(hdr); + if (comp == IH_COMP_GZIP) { + ulong image_buf = image_get_data(hdr); + data = image_get_load(hdr); + ulong image_size = ~0UL; + + if (gunzip((void *)data, ~0UL, + (void *)image_buf, + &image_size) != 0) { + puts("GUNZIP: error\n"); + return 1; + } + data_size = image_size; + } else { + data = (ulong)image_get_data(hdr); + data_size = image_get_data_size(hdr); + } + rc = fpga_load(dev, (void *)data, data_size); + } + break; +#if defined(CONFIG_FIT) + case IMAGE_FORMAT_FIT: + { + const void *fit_hdr = (const void *)fpga_data; + int noffset; + const void *fit_data; + + if (fit_uname == NULL) { + puts("No FIT subimage unit name\n"); + return 1; + } + + if (!fit_check_format(fit_hdr)) { + puts("Bad FIT image format\n"); + return 1; + } + + /* get fpga component image node offset */ + noffset = fit_image_get_node(fit_hdr, + fit_uname); + if (noffset < 0) { + printf("Can't find '%s' FIT subimage\n", + fit_uname); + return 1; + } + + /* verify integrity */ + if (!fit_image_verify(fit_hdr, noffset)) { + puts ("Bad Data Hash\n"); + return 1; + } + + /* get fpga subimage data address and length */ + if (fit_image_get_data(fit_hdr, noffset, + &fit_data, &data_size)) { + puts("Fpga subimage data not found\n"); + return 1; + } + + rc = fpga_load(dev, fit_data, data_size); + } + break; +#endif + default: + puts("** Unknown image type\n"); + rc = FPGA_FAIL; + break; + } + break; + + case FPGA_DUMP: + rc = fpga_dump(dev, fpga_data, data_size); + break; + + default: + printf("Unknown operation\n"); + return CMD_RET_USAGE; + } + return rc; +} + +/* + * Map op to supported operations. We don't use a table since we + * would just have to relocate it from flash anyway. + */ +static int fpga_get_op(char *opstr) +{ + int op = FPGA_NONE; + + if (!strcmp("info", opstr)) + op = FPGA_INFO; + else if (!strcmp("loadb", opstr)) + op = FPGA_LOADB; + else if (!strcmp("load", opstr)) + op = FPGA_LOAD; + else if (!strcmp("loadmk", opstr)) + op = FPGA_LOADMK; + else if (!strcmp("dump", opstr)) + op = FPGA_DUMP; + + if (op == FPGA_NONE) + printf("Unknown fpga operation \"%s\"\n", opstr); + + return op; +} + +U_BOOT_CMD(fpga, 6, 1, do_fpga, + "loadable FPGA image support", + "[operation type] [device number] [image address] [image size]\n" + "fpga operations:\n" + " dump\t[dev]\t\t\tLoad device to memory buffer\n" + " info\t[dev]\t\t\tlist known device information\n" + " load\t[dev] [address] [size]\tLoad device from memory buffer\n" + " loadb\t[dev] [address] [size]\t" + "Load device from bitstream buffer (Xilinx only)\n" + " loadmk [dev] [address]\tLoad device generated with mkimage" +#if defined(CONFIG_FIT) + "\n" + "\tFor loadmk operating on FIT format uImage address must include\n" + "\tsubimage unit name in the form of addr:<subimg_uname>" +#endif +); diff --git a/qemu/roms/u-boot/common/cmd_fpgad.c b/qemu/roms/u-boot/common/cmd_fpgad.c new file mode 100644 index 000000000..1b25ed874 --- /dev/null +++ b/qemu/roms/u-boot/common/cmd_fpgad.c @@ -0,0 +1,84 @@ +/* + * (C) Copyright 2013 + * Dirk Eibach, Guntermann & Drunck GmbH, dirk.eibach@gdsys.cc + * + * based on cmd_mem.c + * (C) Copyright 2000 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <command.h> + +#include <gdsys_fpga.h> + +static uint dp_last_fpga; +static uint dp_last_addr; +static uint dp_last_length = 0x40; + +/* + * FPGA Memory Display + * + * Syntax: + * fpgad {fpga} {addr} {len} + */ +#define DISP_LINE_LEN 16 +int do_fpga_md(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + unsigned int k; + unsigned int fpga; + ulong addr, length; + int rc = 0; + u16 linebuf[DISP_LINE_LEN/sizeof(u16)]; + + /* + * We use the last specified parameters, unless new ones are + * entered. + */ + fpga = dp_last_fpga; + addr = dp_last_addr; + length = dp_last_length; + + if (argc < 3) + return CMD_RET_USAGE; + + if ((flag & CMD_FLAG_REPEAT) == 0) { + /* + * FPGA is specified since argc > 2 + */ + fpga = simple_strtoul(argv[1], NULL, 16); + + /* + * Address is specified since argc > 2 + */ + addr = simple_strtoul(argv[2], NULL, 16); + + /* + * If another parameter, it is the length to display. + * Length is the number of objects, not number of bytes. + */ + if (argc > 3) + length = simple_strtoul(argv[3], NULL, 16); + } + + /* Print the lines. */ + for (k = 0; k < DISP_LINE_LEN / sizeof(u16); ++k) + fpga_get_reg(fpga, (u16 *)fpga_ptr[fpga] + k, k * sizeof(u16), + &linebuf[k]); + print_buffer(addr, (void *)linebuf, sizeof(u16), + length, DISP_LINE_LEN / sizeof(u16)); + addr += sizeof(u16)*length; + + dp_last_fpga = fpga; + dp_last_addr = addr; + dp_last_length = length; + return rc; +} + +U_BOOT_CMD( + fpgad, 4, 1, do_fpga_md, + "fpga register display", + "fpga address [# of objects]" +); diff --git a/qemu/roms/u-boot/common/cmd_fs.c b/qemu/roms/u-boot/common/cmd_fs.c new file mode 100644 index 000000000..91a205ac1 --- /dev/null +++ b/qemu/roms/u-boot/common/cmd_fs.c @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved. + * + * Inspired by cmd_ext_common.c, cmd_fat.c. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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, see <http://www.gnu.org/licenses/>. + */ + +#include <common.h> +#include <command.h> +#include <fs.h> + +int do_load_wrapper(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + return do_load(cmdtp, flag, argc, argv, FS_TYPE_ANY); +} + +U_BOOT_CMD( + load, 7, 0, do_load_wrapper, + "load binary file from a filesystem", + "<interface> [<dev[:part]> [<addr> [<filename> [bytes [pos]]]]]\n" + " - Load binary file 'filename' from partition 'part' on device\n" + " type 'interface' instance 'dev' to address 'addr' in memory.\n" + " 'bytes' gives the size to load in bytes.\n" + " If 'bytes' is 0 or omitted, the file is read until the end.\n" + " 'pos' gives the file byte position to start reading from.\n" + " If 'pos' is 0 or omitted, the file is read from the start." +); + +int do_ls_wrapper(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + return do_ls(cmdtp, flag, argc, argv, FS_TYPE_ANY); +} + +U_BOOT_CMD( + ls, 4, 1, do_ls_wrapper, + "list files in a directory (default /)", + "<interface> [<dev[:part]> [directory]]\n" + " - List files in directory 'directory' of partition 'part' on\n" + " device type 'interface' instance 'dev'." +); diff --git a/qemu/roms/u-boot/common/cmd_fuse.c b/qemu/roms/u-boot/common/cmd_fuse.c new file mode 100644 index 000000000..0df57dbc8 --- /dev/null +++ b/qemu/roms/u-boot/common/cmd_fuse.c @@ -0,0 +1,152 @@ +/* + * (C) Copyright 2009-2013 ADVANSEE + * Benoît Thébaudeau <benoit.thebaudeau@advansee.com> + * + * Based on the mpc512x iim code: + * Copyright 2008 Silicon Turnkey Express, Inc. + * Martha Marx <mmarx@silicontkx.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <command.h> +#include <fuse.h> +#include <asm/errno.h> + +static int strtou32(const char *str, unsigned int base, u32 *result) +{ + char *ep; + + *result = simple_strtoul(str, &ep, base); + if (ep == str || *ep != '\0') + return -EINVAL; + + return 0; +} + +static int confirm_prog(void) +{ + puts("Warning: Programming fuses is an irreversible operation!\n" + " This may brick your system.\n" + " Use this command only if you are sure of " + "what you are doing!\n" + "\nReally perform this fuse programming? <y/N>\n"); + + if (getc() == 'y') { + int c; + + putc('y'); + c = getc(); + putc('\n'); + if (c == '\r') + return 1; + } + + puts("Fuse programming aborted\n"); + return 0; +} + +static int do_fuse(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[]) +{ + const char *op = argc >= 2 ? argv[1] : NULL; + int confirmed = argc >= 3 && !strcmp(argv[2], "-y"); + u32 bank, word, cnt, val; + int ret, i; + + argc -= 2 + confirmed; + argv += 2 + confirmed; + + if (argc < 2 || strtou32(argv[0], 0, &bank) || + strtou32(argv[1], 0, &word)) + return CMD_RET_USAGE; + + if (!strcmp(op, "read")) { + if (argc == 2) + cnt = 1; + else if (argc != 3 || strtou32(argv[2], 0, &cnt)) + return CMD_RET_USAGE; + + printf("Reading bank %u:\n", bank); + for (i = 0; i < cnt; i++, word++) { + if (!(i % 4)) + printf("\nWord 0x%.8x:", word); + + ret = fuse_read(bank, word, &val); + if (ret) + goto err; + + printf(" %.8x", val); + } + putc('\n'); + } else if (!strcmp(op, "sense")) { + if (argc == 2) + cnt = 1; + else if (argc != 3 || strtou32(argv[2], 0, &cnt)) + return CMD_RET_USAGE; + + printf("Sensing bank %u:\n", bank); + for (i = 0; i < cnt; i++, word++) { + if (!(i % 4)) + printf("\nWord 0x%.8x:", word); + + ret = fuse_sense(bank, word, &val); + if (ret) + goto err; + + printf(" %.8x", val); + } + putc('\n'); + } else if (!strcmp(op, "prog")) { + if (argc < 3) + return CMD_RET_USAGE; + + for (i = 2; i < argc; i++, word++) { + if (strtou32(argv[i], 16, &val)) + return CMD_RET_USAGE; + + printf("Programming bank %u word 0x%.8x to 0x%.8x...\n", + bank, word, val); + if (!confirmed && !confirm_prog()) + return CMD_RET_FAILURE; + ret = fuse_prog(bank, word, val); + if (ret) + goto err; + } + } else if (!strcmp(op, "override")) { + if (argc < 3) + return CMD_RET_USAGE; + + for (i = 2; i < argc; i++, word++) { + if (strtou32(argv[i], 16, &val)) + return CMD_RET_USAGE; + + printf("Overriding bank %u word 0x%.8x with " + "0x%.8x...\n", bank, word, val); + ret = fuse_override(bank, word, val); + if (ret) + goto err; + } + } else { + return CMD_RET_USAGE; + } + + return 0; + +err: + puts("ERROR\n"); + return ret; +} + +U_BOOT_CMD( + fuse, CONFIG_SYS_MAXARGS, 0, do_fuse, + "Fuse sub-system", + "read <bank> <word> [<cnt>] - read 1 or 'cnt' fuse words,\n" + " starting at 'word'\n" + "fuse sense <bank> <word> [<cnt>] - sense 1 or 'cnt' fuse words,\n" + " starting at 'word'\n" + "fuse prog [-y] <bank> <word> <hexval> [<hexval>...] - program 1 or\n" + " several fuse words, starting at 'word' (PERMANENT)\n" + "fuse override <bank> <word> <hexval> [<hexval>...] - override 1 or\n" + " several fuse words, starting at 'word'" +); diff --git a/qemu/roms/u-boot/common/cmd_gettime.c b/qemu/roms/u-boot/common/cmd_gettime.c new file mode 100644 index 000000000..320ff709f --- /dev/null +++ b/qemu/roms/u-boot/common/cmd_gettime.c @@ -0,0 +1,40 @@ +/* + * 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. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/* + * Get Timer overflows after 2^32 / CONFIG_SYS_HZ (32Khz) = 131072 sec + */ +#include <common.h> +#include <command.h> + +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/qemu/roms/u-boot/common/cmd_gpio.c b/qemu/roms/u-boot/common/cmd_gpio.c new file mode 100644 index 000000000..aff044518 --- /dev/null +++ b/qemu/roms/u-boot/common/cmd_gpio.c @@ -0,0 +1,193 @@ +/* + * Control GPIO pins on the fly + * + * Copyright (c) 2008-2011 Analog Devices Inc. + * + * Licensed under the GPL-2 or later. + */ + +#include <common.h> +#include <command.h> +#include <dm.h> +#include <asm/gpio.h> + +int __weak name_to_gpio(const char *name) +{ + return simple_strtoul(name, NULL, 10); +} + +enum gpio_cmd { + GPIO_INPUT, + GPIO_SET, + GPIO_CLEAR, + GPIO_TOGGLE, +}; + +#if defined(CONFIG_DM_GPIO) && !defined(gpio_status) +static const char * const gpio_function[] = { + "input", + "output", + "unknown", +}; + +static void show_gpio(struct device *dev, const char *bank_name, int offset) +{ + struct dm_gpio_ops *ops = gpio_get_ops(dev); + char buf[80]; + int ret; + + *buf = '\0'; + if (ops->get_state) { + ret = ops->get_state(dev, offset, buf, sizeof(buf)); + if (ret) { + puts("<unknown>"); + return; + } + } else { + int func = GPIOF_UNKNOWN; + int ret; + + if (ops->get_function) { + ret = ops->get_function(dev, offset); + if (ret >= 0 && ret < ARRAY_SIZE(gpio_function)) + func = ret; + } + sprintf(buf, "%s%u: %8s %d", bank_name, offset, + gpio_function[func], ops->get_value(dev, offset)); + } + + puts(buf); + puts("\n"); +} + +static int do_gpio_status(const char *gpio_name) +{ + struct device *dev; + int newline = 0; + int ret; + + if (gpio_name && !*gpio_name) + gpio_name = NULL; + for (ret = uclass_first_device(UCLASS_GPIO, &dev); + dev; + ret = uclass_next_device(&dev)) { + const char *bank_name; + int num_bits; + + bank_name = gpio_get_bank_info(dev, &num_bits); + + if (!gpio_name || !bank_name || + !strncmp(gpio_name, bank_name, strlen(bank_name))) { + const char *p = NULL; + int offset; + + if (bank_name) { + if (newline) + putc('\n'); + printf("Bank %s:\n", bank_name); + } + newline = 1; + if (gpio_name && bank_name) { + p = gpio_name + strlen(bank_name); + offset = simple_strtoul(p, NULL, 10); + show_gpio(dev, bank_name, offset); + } else { + for (offset = 0; offset < num_bits; offset++) + show_gpio(dev, bank_name, offset); + } + } + } + + return ret; +} +#endif + +static int do_gpio(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + unsigned int gpio; + enum gpio_cmd sub_cmd; + ulong value; + const char *str_cmd, *str_gpio = NULL; +#ifdef CONFIG_DM_GPIO + int ret; +#endif + + if (argc < 2) + show_usage: + return CMD_RET_USAGE; + str_cmd = argv[1]; + if (argc > 2) + str_gpio = argv[2]; + if (!strcmp(str_cmd, "status")) { + /* Support deprecated gpio_status() */ +#ifdef gpio_status + gpio_status(); + return 0; +#elif defined(CONFIG_DM_GPIO) + return cmd_process_error(cmdtp, do_gpio_status(str_gpio)); +#else + goto show_usage; +#endif + } + + if (!str_gpio) + goto show_usage; + + /* parse the behavior */ + switch (*str_cmd) { + case 'i': sub_cmd = GPIO_INPUT; break; + case 's': sub_cmd = GPIO_SET; break; + case 'c': sub_cmd = GPIO_CLEAR; break; + case 't': sub_cmd = GPIO_TOGGLE; break; + default: goto show_usage; + } + +#if defined(CONFIG_DM_GPIO) + /* + * TODO(sjg@chromium.org): For now we must fit into the existing GPIO + * framework, so we look up the name here and convert it to a GPIO number. + * Once all GPIO drivers are converted to driver model, we can change the + * code here to use the GPIO uclass interface instead of the numbered + * GPIO compatibility layer. + */ + ret = gpio_lookup_name(str_gpio, NULL, NULL, &gpio); + if (ret) + return cmd_process_error(cmdtp, ret); +#else + /* turn the gpio name into a gpio number */ + gpio = name_to_gpio(str_gpio); + if (gpio < 0) + goto show_usage; +#endif + /* grab the pin before we tweak it */ + if (gpio_request(gpio, "cmd_gpio")) { + printf("gpio: requesting pin %u failed\n", gpio); + return -1; + } + + /* finally, let's do it: set direction and exec command */ + if (sub_cmd == GPIO_INPUT) { + gpio_direction_input(gpio); + value = gpio_get_value(gpio); + } else { + switch (sub_cmd) { + case GPIO_SET: value = 1; break; + case GPIO_CLEAR: value = 0; break; + case GPIO_TOGGLE: value = !gpio_get_value(gpio); break; + default: goto show_usage; + } + gpio_direction_output(gpio, value); + } + printf("gpio: pin %s (gpio %i) value is %lu\n", + str_gpio, gpio, value); + + gpio_free(gpio); + + return value; +} + +U_BOOT_CMD(gpio, 3, 0, do_gpio, + "query and control gpio pins", + "<input|set|clear|toggle> <pin>\n" + " - input/set/clear/toggle the specified pin\n" + "gpio status [<bank> | <pin>]"); diff --git a/qemu/roms/u-boot/common/cmd_gpt.c b/qemu/roms/u-boot/common/cmd_gpt.c new file mode 100644 index 000000000..e38422d79 --- /dev/null +++ b/qemu/roms/u-boot/common/cmd_gpt.c @@ -0,0 +1,347 @@ +/* + * cmd_gpt.c -- GPT (GUID Partition Table) handling command + * + * Copyright (C) 2012 Samsung Electronics + * author: Lukasz Majewski <l.majewski@samsung.com> + * author: Piotr Wilczek <p.wilczek@samsung.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <malloc.h> +#include <command.h> +#include <part_efi.h> +#include <exports.h> +#include <linux/ctype.h> +#include <div64.h> + +#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 int extract_env(const char *str, char **env) +{ + int ret = -1; + char *e, *s; +#ifdef CONFIG_RANDOM_UUID + char uuid_str[UUID_STR_LEN + 1]; +#endif + + if (!str || strlen(str) < 4) + return -1; + + if (!((strncmp(str, "${", 2) == 0) && (str[strlen(str) - 1] == '}'))) + return -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); + if (e == NULL) { +#ifdef CONFIG_RANDOM_UUID + debug("%s unset. ", str); + gen_rand_uuid_str(uuid_str, UUID_STR_FORMAT_STD); + setenv(s, uuid_str); + + e = getenv(s); + if (e) { + debug("Set to random.\n"); + ret = 0; + } else { + debug("Can't get random UUID.\n"); + } +#else + debug("%s unset.\n", str); +#endif + } else { + debug("%s get from environment.\n", str); + ret = 0; + } + + *env = e; + free(s); + + return ret; +} + +/** + * 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; + uint64_t size_ll, start_ll; + + debug("%s: 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); + + /* retrieve partitions 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; + size_ll = ustrtoull(p, &p, 0); + parts[i].size = lldiv(size_ll, 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; + start_ll = ustrtoull(p, &p, 0); + parts[i].start = lldiv(start_ll, 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_default(block_dev_desc_t *blk_dev_desc, const char *str_part) +{ + int ret; + char *str_disk_guid; + u8 part_count = 0; + disk_partition_t *partitions = NULL; + + if (!str_part) + return -1; + + /* fill partitions */ + ret = set_gpt_info(blk_dev_desc, 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(blk_dev_desc, 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 *ep; + block_dev_desc_t *blk_dev_desc; + + if (argc < 5) + return CMD_RET_USAGE; + + /* command: 'write' */ + if ((strcmp(argv[1], "write") == 0) && (argc == 5)) { + dev = (int)simple_strtoul(argv[3], &ep, 10); + if (!ep || ep[0] != '\0') { + printf("'%s' is not a number\n", argv[3]); + return CMD_RET_USAGE; + } + blk_dev_desc = get_dev(argv[2], dev); + if (!blk_dev_desc) { + printf("%s: %s dev %d NOT available\n", + __func__, argv[2], dev); + return CMD_RET_FAILURE; + } + + puts("Writing GPT: "); + + ret = gpt_default(blk_dev_desc, argv[4]); + if (!ret) { + puts("success!\n"); + return CMD_RET_SUCCESS; + } else { + puts("error!\n"); + return CMD_RET_FAILURE; + } + } else { + return CMD_RET_USAGE; + } + return ret; +} + +U_BOOT_CMD(gpt, CONFIG_SYS_MAXARGS, 1, do_gpt, + "GUID Partition Table", + "<command> <interface> <dev> <partitions_list>\n" + " - GUID partition table restoration\n" + " Restore GPT information on a device connected\n" + " to interface\n" +); diff --git a/qemu/roms/u-boot/common/cmd_hash.c b/qemu/roms/u-boot/common/cmd_hash.c new file mode 100644 index 000000000..90facbbe1 --- /dev/null +++ b/qemu/roms/u-boot/common/cmd_hash.c @@ -0,0 +1,58 @@ +/* + * 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. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <command.h> +#include <hash.h> +#include <linux/ctype.h> + +static int do_hash(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + char *s; +#ifdef CONFIG_HASH_VERIFY + int flags = HASH_FLAG_ENV; + + if (argc < 4) + return CMD_RET_USAGE; + if (!strcmp(argv[1], "-v")) { + flags |= HASH_FLAG_VERIFY; + argc--; + argv++; + } +#else + const int flags = HASH_FLAG_ENV; +#endif + /* Move forward to 'algorithm' parameter */ + argc--; + argv++; + for (s = *argv; *s; s++) + *s = tolower(*s); + return hash_command(*argv, flags, 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/qemu/roms/u-boot/common/cmd_help.c b/qemu/roms/u-boot/common/cmd_help.c new file mode 100644 index 000000000..6ff494d79 --- /dev/null +++ b/qemu/roms/u-boot/common/cmd_help.c @@ -0,0 +1,34 @@ +/* + * Copyright 2000-2009 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <command.h> + +static int do_help(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + cmd_tbl_t *start = ll_entry_start(cmd_tbl_t, cmd); + const int len = ll_entry_count(cmd_tbl_t, cmd); + return _do_help(start, len, cmdtp, flag, argc, argv); +} + +U_BOOT_CMD( + help, CONFIG_SYS_MAXARGS, 1, do_help, + "print command description/usage", + "\n" + " - print brief description of all commands\n" + "help command ...\n" + " - print detailed usage of 'command'" +); + +/* This does not use the U_BOOT_CMD macro as ? can't be used in symbol names */ +ll_entry_declare(cmd_tbl_t, question_mark, cmd) = { + "?", CONFIG_SYS_MAXARGS, 1, do_help, + "alias for 'help'", +#ifdef CONFIG_SYS_LONGHELP + "" +#endif /* CONFIG_SYS_LONGHELP */ +}; diff --git a/qemu/roms/u-boot/common/cmd_i2c.c b/qemu/roms/u-boot/common/cmd_i2c.c new file mode 100644 index 000000000..ebce7d4c3 --- /dev/null +++ b/qemu/roms/u-boot/common/cmd_i2c.c @@ -0,0 +1,1627 @@ +/* + * (C) Copyright 2009 + * Sergey Kubushyn, himself, ksi@koi8.net + * + * Changes for unified multibus/multiadapter I2C support. + * + * (C) Copyright 2001 + * Gerald Van Baren, Custom IDEAS, vanbaren@cideas.com. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/* + * I2C Functions similar to the standard memory functions. + * + * There are several parameters in many of the commands that bear further + * explanations: + * + * {i2c_chip} is the I2C chip address (the first byte sent on the bus). + * Each I2C chip on the bus has a unique address. On the I2C data bus, + * the address is the upper seven bits and the LSB is the "read/write" + * bit. Note that the {i2c_chip} address specified on the command + * line is not shifted up: e.g. a typical EEPROM memory chip may have + * an I2C address of 0x50, but the data put on the bus will be 0xA0 + * for write and 0xA1 for read. This "non shifted" address notation + * matches at least half of the data sheets :-/. + * + * {addr} is the address (or offset) within the chip. Small memory + * chips have 8 bit addresses. Large memory chips have 16 bit + * addresses. Other memory chips have 9, 10, or 11 bit addresses. + * Many non-memory chips have multiple registers and {addr} is used + * as the register index. Some non-memory chips have only one register + * and therefore don't need any {addr} parameter. + * + * The default {addr} parameter is one byte (.1) which works well for + * memories and registers with 8 bits of address space. + * + * You can specify the length of the {addr} field with the optional .0, + * .1, or .2 modifier (similar to the .b, .w, .l modifier). If you are + * manipulating a single register device which doesn't use an address + * field, use "0.0" for the address and the ".0" length field will + * suppress the address in the I2C data stream. This also works for + * successive reads using the I2C auto-incrementing memory pointer. + * + * If you are manipulating a large memory with 2-byte addresses, use + * the .2 address modifier, e.g. 210.2 addresses location 528 (decimal). + * + * Then there are the unfortunate memory chips that spill the most + * significant 1, 2, or 3 bits of address into the chip address byte. + * This effectively makes one chip (logically) look like 2, 4, or + * 8 chips. This is handled (awkwardly) by #defining + * CONFIG_SYS_I2C_EEPROM_ADDR_OVERFLOW and using the .1 modifier on the + * {addr} field (since .1 is the default, it doesn't actually have to + * be specified). Examples: given a memory chip at I2C chip address + * 0x50, the following would happen... + * i2c md 50 0 10 display 16 bytes starting at 0x000 + * On the bus: <S> A0 00 <E> <S> A1 <rd> ... <rd> + * i2c md 50 100 10 display 16 bytes starting at 0x100 + * On the bus: <S> A2 00 <E> <S> A3 <rd> ... <rd> + * i2c md 50 210 10 display 16 bytes starting at 0x210 + * On the bus: <S> A4 10 <E> <S> A5 <rd> ... <rd> + * This is awfully ugly. It would be nice if someone would think up + * a better way of handling this. + * + * Adapted from cmd_mem.c which is copyright Wolfgang Denk (wd@denx.de). + */ + +#include <common.h> +#include <command.h> +#include <edid.h> +#include <environment.h> +#include <i2c.h> +#include <malloc.h> +#include <asm/byteorder.h> +#include <linux/compiler.h> + +DECLARE_GLOBAL_DATA_PTR; + +/* Display values from last command. + * Memory modify remembered values are different from display memory. + */ +static uchar i2c_dp_last_chip; +static uint i2c_dp_last_addr; +static uint i2c_dp_last_alen; +static uint i2c_dp_last_length = 0x10; + +static uchar i2c_mm_last_chip; +static uint i2c_mm_last_addr; +static uint i2c_mm_last_alen; + +/* If only one I2C bus is present, the list of devices to ignore when + * the probe command is issued is represented by a 1D array of addresses. + * When multiple buses are present, the list is an array of bus-address + * pairs. The following macros take care of this */ + +#if defined(CONFIG_SYS_I2C_NOPROBES) +#if defined(CONFIG_SYS_I2C) || defined(CONFIG_I2C_MULTI_BUS) +static struct +{ + uchar bus; + uchar addr; +} i2c_no_probes[] = CONFIG_SYS_I2C_NOPROBES; +#define GET_BUS_NUM i2c_get_bus_num() +#define COMPARE_BUS(b,i) (i2c_no_probes[(i)].bus == (b)) +#define COMPARE_ADDR(a,i) (i2c_no_probes[(i)].addr == (a)) +#define NO_PROBE_ADDR(i) i2c_no_probes[(i)].addr +#else /* single bus */ +static uchar i2c_no_probes[] = CONFIG_SYS_I2C_NOPROBES; +#define GET_BUS_NUM 0 +#define COMPARE_BUS(b,i) ((b) == 0) /* Make compiler happy */ +#define COMPARE_ADDR(a,i) (i2c_no_probes[(i)] == (a)) +#define NO_PROBE_ADDR(i) i2c_no_probes[(i)] +#endif /* defined(CONFIG_SYS_I2C) */ +#endif + +#define DISP_LINE_LEN 16 + +/** + * 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) +{ +} + +/* 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. + */ +#if !defined(CONFIG_SYS_I2C) +/* + * TODO: Implement architecture-specific get/set functions + * Should go away, if we switched completely to new multibus support + */ +__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) +{ + if (speed != CONFIG_SYS_I2C_SPEED) + return -1; + + return 0; +} +#endif + +/** + * get_alen() - Small parser helper function to get address length + * + * Returns the address length. + */ +static uint get_alen(char *arg) +{ + int j; + int alen; + + alen = 1; + for (j = 0; j < 8; j++) { + if (arg[j] == '.') { + alen = arg[j+1] - '0'; + break; + } else if (arg[j] == '\0') + break; + } + 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; + uint devaddr, alen, length; + u_char *memaddr; + + if (argc != 5) + return CMD_RET_USAGE; + + /* + * I2C chip address + */ + chip = simple_strtoul(argv[1], NULL, 16); + + /* + * I2C data address within the chip. This can be 1 or + * 2 bytes long. Some day it might be 3 bytes long :-). + */ + devaddr = simple_strtoul(argv[2], NULL, 16); + alen = get_alen(argv[2]); + if (alen > 3) + return CMD_RET_USAGE; + + /* + * Length is the number of objects, not number of bytes. + */ + length = simple_strtoul(argv[3], NULL, 16); + + /* + * memaddr is the address where to store things in memory + */ + memaddr = (u_char *)simple_strtoul(argv[4], NULL, 16); + + if (i2c_read(chip, devaddr, alen, memaddr, length) != 0) { + puts ("Error reading the chip.\n"); + return 1; + } + return 0; +} + +static int do_i2c_write(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + u_char chip; + uint devaddr, alen, length; + u_char *memaddr; + + if (argc != 5) + return cmd_usage(cmdtp); + + /* + * memaddr is the address where to store things in memory + */ + memaddr = (u_char *)simple_strtoul(argv[1], NULL, 16); + + /* + * I2C chip address + */ + chip = simple_strtoul(argv[2], NULL, 16); + + /* + * I2C data address within the chip. This can be 1 or + * 2 bytes long. Some day it might be 3 bytes long :-). + */ + devaddr = simple_strtoul(argv[3], NULL, 16); + alen = get_alen(argv[3]); + if (alen > 3) + return cmd_usage(cmdtp); + + /* + * Length is the number of objects, not number of bytes. + */ + length = simple_strtoul(argv[4], NULL, 16); + + while (length-- > 0) { + if (i2c_write(chip, devaddr++, alen, memaddr++, 1) != 0) { + puts("Error writing to the chip.\n"); + return 1; + } +/* + * No write delay with FRAM devices. + */ +#if !defined(CONFIG_SYS_I2C_FRAM) + udelay(11000); +#endif + } + 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} + */ +static int do_i2c_md ( cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + u_char chip; + uint addr, alen, length; + int j, nbytes, linebytes; + + /* We use the last specified parameters, unless new ones are + * entered. + */ + chip = i2c_dp_last_chip; + addr = i2c_dp_last_addr; + alen = i2c_dp_last_alen; + length = i2c_dp_last_length; + + if (argc < 3) + return CMD_RET_USAGE; + + if ((flag & CMD_FLAG_REPEAT) == 0) { + /* + * New command specified. + */ + + /* + * I2C chip address + */ + chip = simple_strtoul(argv[1], NULL, 16); + + /* + * I2C data address within the chip. This can be 1 or + * 2 bytes long. Some day it might be 3 bytes long :-). + */ + addr = simple_strtoul(argv[2], NULL, 16); + alen = get_alen(argv[2]); + if (alen > 3) + return CMD_RET_USAGE; + + /* + * If another parameter, it is the length to display. + * Length is the number of objects, not number of bytes. + */ + if (argc > 3) + length = simple_strtoul(argv[3], NULL, 16); + } + + /* + * Print the lines. + * + * We buffer all read data, so we can make sure data is read only + * once. + */ + nbytes = length; + do { + unsigned char linebuf[DISP_LINE_LEN]; + unsigned char *cp; + + linebytes = (nbytes > DISP_LINE_LEN) ? DISP_LINE_LEN : nbytes; + + if (i2c_read(chip, addr, alen, linebuf, linebytes) != 0) + puts ("Error reading the chip.\n"); + else { + printf("%04x:", addr); + cp = linebuf; + for (j=0; j<linebytes; j++) { + printf(" %02x", *cp++); + addr++; + } + puts (" "); + cp = linebuf; + for (j=0; j<linebytes; j++) { + if ((*cp < 0x20) || (*cp > 0x7e)) + puts ("."); + else + printf("%c", *cp); + cp++; + } + putc ('\n'); + } + nbytes -= linebytes; + } while (nbytes > 0); + + i2c_dp_last_chip = chip; + i2c_dp_last_addr = addr; + i2c_dp_last_alen = alen; + i2c_dp_last_length = length; + + return 0; +} + +/** + * 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}] + */ +static int do_i2c_mw ( cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + uchar chip; + ulong addr; + uint alen; + uchar byte; + int count; + + if ((argc < 4) || (argc > 5)) + return CMD_RET_USAGE; + + /* + * Chip is always specified. + */ + chip = simple_strtoul(argv[1], NULL, 16); + + /* + * Address is always specified. + */ + addr = simple_strtoul(argv[2], NULL, 16); + alen = get_alen(argv[2]); + if (alen > 3) + return CMD_RET_USAGE; + + /* + * Value to write is always specified. + */ + byte = simple_strtoul(argv[3], NULL, 16); + + /* + * Optional count + */ + if (argc == 5) + count = simple_strtoul(argv[4], NULL, 16); + else + count = 1; + + while (count-- > 0) { + if (i2c_write(chip, addr++, alen, &byte, 1) != 0) + puts ("Error writing the chip.\n"); + /* + * Wait for the write to complete. The write can take + * up to 10mSec (we allow a little more time). + */ +/* + * No write delay with FRAM devices. + */ +#if !defined(CONFIG_SYS_I2C_FRAM) + udelay(11000); +#endif + } + + return 0; +} + +/** + * 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} + */ +static int do_i2c_crc (cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + uchar chip; + ulong addr; + uint alen; + int count; + uchar byte; + ulong crc; + ulong err; + + if (argc < 4) + return CMD_RET_USAGE; + + /* + * Chip is always specified. + */ + chip = simple_strtoul(argv[1], NULL, 16); + + /* + * Address is always specified. + */ + addr = simple_strtoul(argv[2], NULL, 16); + alen = get_alen(argv[2]); + if (alen > 3) + return CMD_RET_USAGE; + + /* + * Count is always specified + */ + count = simple_strtoul(argv[3], NULL, 16); + + printf ("CRC32 for %08lx ... %08lx ==> ", addr, addr + count - 1); + /* + * CRC a byte at a time. This is going to be slooow, but hey, the + * memories are small and slow too so hopefully nobody notices. + */ + crc = 0; + err = 0; + while (count-- > 0) { + if (i2c_read(chip, addr, alen, &byte, 1) != 0) + err++; + crc = crc32 (crc, &byte, 1); + addr++; + } + if (err > 0) + puts ("Error reading the chip,\n"); + else + printf ("%08lx\n", crc); + + return 0; +} + +/** + * 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[]) +{ + uchar chip; + ulong addr; + uint alen; + ulong data; + int size = 1; + int nbytes; + + if (argc != 3) + return CMD_RET_USAGE; + +#ifdef CONFIG_BOOT_RETRY_TIME + reset_cmd_timeout(); /* got a good command to get here */ +#endif + /* + * We use the last specified parameters, unless new ones are + * entered. + */ + chip = i2c_mm_last_chip; + addr = i2c_mm_last_addr; + alen = i2c_mm_last_alen; + + if ((flag & CMD_FLAG_REPEAT) == 0) { + /* + * New command specified. Check for a size specification. + * Defaults to byte if no or incorrect specification. + */ + size = cmd_get_data_size(argv[0], 1); + + /* + * Chip is always specified. + */ + chip = simple_strtoul(argv[1], NULL, 16); + + /* + * Address is always specified. + */ + addr = simple_strtoul(argv[2], NULL, 16); + alen = get_alen(argv[2]); + if (alen > 3) + return CMD_RET_USAGE; + } + + /* + * Print the address, followed by value. Then accept input for + * the next value. A non-converted value exits. + */ + do { + printf("%08lx:", addr); + if (i2c_read(chip, addr, alen, (uchar *)&data, size) != 0) + puts ("\nError reading the chip,\n"); + else { + data = cpu_to_be32(data); + if (size == 1) + printf(" %02lx", (data >> 24) & 0x000000FF); + else if (size == 2) + printf(" %04lx", (data >> 16) & 0x0000FFFF); + else + printf(" %08lx", data); + } + + nbytes = readline (" ? "); + if (nbytes == 0) { + /* + * <CR> pressed as only input, don't modify current + * location and move to next. + */ + if (incrflag) + addr += size; + nbytes = size; +#ifdef CONFIG_BOOT_RETRY_TIME + reset_cmd_timeout(); /* good enough to not time out */ +#endif + } +#ifdef CONFIG_BOOT_RETRY_TIME + else if (nbytes == -2) + break; /* timed out, exit the command */ +#endif + else { + char *endp; + + data = simple_strtoul(console_buffer, &endp, 16); + if (size == 1) + data = data << 24; + else if (size == 2) + data = data << 16; + data = be32_to_cpu(data); + nbytes = endp - console_buffer; + if (nbytes) { +#ifdef CONFIG_BOOT_RETRY_TIME + /* + * good enough to not time out + */ + reset_cmd_timeout(); +#endif + if (i2c_write(chip, addr, alen, (uchar *)&data, size) != 0) + puts ("Error writing the chip.\n"); +#ifdef CONFIG_SYS_EEPROM_PAGE_WRITE_DELAY_MS + udelay(CONFIG_SYS_EEPROM_PAGE_WRITE_DELAY_MS * 1000); +#endif + if (incrflag) + addr += size; + } + } + } while (nbytes); + + i2c_mm_last_chip = chip; + i2c_mm_last_addr = addr; + i2c_mm_last_alen = alen; + + 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} + * + * Returns zero (success) if one or more I2C devices was found + */ +static int do_i2c_probe (cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + int j; + int addr = -1; + int found = 0; +#if defined(CONFIG_SYS_I2C_NOPROBES) + int k, skip; + unsigned int bus = GET_BUS_NUM; +#endif /* NOPROBES */ + + if (argc == 2) + addr = simple_strtol(argv[1], 0, 16); + + puts ("Valid chip addresses:"); + for (j = 0; j < 128; j++) { + if ((0 <= addr) && (j != addr)) + continue; + +#if defined(CONFIG_SYS_I2C_NOPROBES) + skip = 0; + for (k = 0; k < ARRAY_SIZE(i2c_no_probes); k++) { + if (COMPARE_BUS(bus, k) && COMPARE_ADDR(j, k)) { + skip = 1; + break; + } + } + if (skip) + continue; +#endif + if (i2c_probe(j) == 0) { + printf(" %02X", j); + found++; + } + } + putc ('\n'); + +#if defined(CONFIG_SYS_I2C_NOPROBES) + puts ("Excluded chip addresses:"); + for (k = 0; k < ARRAY_SIZE(i2c_no_probes); k++) { + if (COMPARE_BUS(bus,k)) + printf(" %02X", NO_PROBE_ADDR(k)); + } + putc ('\n'); +#endif + + 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 + * {delay} - A DECIMAL number and defaults to 1000 uSec + */ +static int do_i2c_loop(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + u_char chip; + ulong alen; + uint addr; + uint length; + u_char bytes[16]; + int delay; + + if (argc < 3) + return CMD_RET_USAGE; + + /* + * Chip is always specified. + */ + chip = simple_strtoul(argv[1], NULL, 16); + + /* + * Address is always specified. + */ + addr = simple_strtoul(argv[2], NULL, 16); + alen = get_alen(argv[2]); + if (alen > 3) + return CMD_RET_USAGE; + + /* + * Length is the number of objects, not number of bytes. + */ + length = 1; + length = simple_strtoul(argv[3], NULL, 16); + if (length > sizeof(bytes)) + length = sizeof(bytes); + + /* + * The delay time (uSec) is optional. + */ + delay = 1000; + if (argc > 3) + delay = simple_strtoul(argv[4], NULL, 10); + /* + * Run the loop... + */ + while (1) { + if (i2c_read(chip, addr, alen, bytes, length) != 0) + puts ("Error reading the chip.\n"); + udelay(delay); + } + + /* NOTREACHED */ + return 0; +} + +/* + * 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) +{ + printf ("%d.", (b >> 4) & 0x0F); + switch (b & 0x0F) { + case 0x0: + case 0x1: + case 0x2: + case 0x3: + case 0x4: + case 0x5: + case 0x6: + case 0x7: + case 0x8: + case 0x9: + printf ("%d ns\n", b & 0x0F); + break; + case 0xA: + puts ("25 ns\n"); + break; + case 0xB: + puts ("33 ns\n"); + break; + case 0xC: + puts ("66 ns\n"); + break; + case 0xD: + puts ("75 ns\n"); + break; + default: + puts ("?? ns\n"); + break; + } +} + +static void decode_bits (u_char const b, char const *str[], int const do_once) +{ + u_char mask; + + for (mask = 0x80; mask != 0x00; mask >>= 1, ++str) { + if (b & mask) { + puts (*str); + if (do_once) + return; + } + } +} + +/* + * Syntax: + * i2c sdram {i2c_chip} + */ +static int do_sdram (cmd_tbl_t * cmdtp, int flag, int argc, char * const argv[]) +{ + enum { unknown, EDO, SDRAM, DDR2 } type; + + u_char chip; + u_char data[128]; + u_char cksum; + int j; + + static const char *decode_CAS_DDR2[] = { + " TBD", " 6", " 5", " 4", " 3", " 2", " TBD", " TBD" + }; + + static const char *decode_CAS_default[] = { + " TBD", " 7", " 6", " 5", " 4", " 3", " 2", " 1" + }; + + static const char *decode_CS_WE_default[] = { + " TBD", " 6", " 5", " 4", " 3", " 2", " 1", " 0" + }; + + static const char *decode_byte21_default[] = { + " TBD (bit 7)\n", + " Redundant row address\n", + " Differential clock input\n", + " Registerd DQMB inputs\n", + " Buffered DQMB inputs\n", + " On-card PLL\n", + " Registered address/control lines\n", + " Buffered address/control lines\n" + }; + + static const char *decode_byte22_DDR2[] = { + " TBD (bit 7)\n", + " TBD (bit 6)\n", + " TBD (bit 5)\n", + " TBD (bit 4)\n", + " TBD (bit 3)\n", + " Supports partial array self refresh\n", + " Supports 50 ohm ODT\n", + " Supports weak driver\n" + }; + + static const char *decode_row_density_DDR2[] = { + "512 MiB", "256 MiB", "128 MiB", "16 GiB", + "8 GiB", "4 GiB", "2 GiB", "1 GiB" + }; + + static const char *decode_row_density_default[] = { + "512 MiB", "256 MiB", "128 MiB", "64 MiB", + "32 MiB", "16 MiB", "8 MiB", "4 MiB" + }; + + if (argc < 2) + return CMD_RET_USAGE; + + /* + * Chip is always specified. + */ + chip = simple_strtoul (argv[1], NULL, 16); + + if (i2c_read (chip, 0, 1, data, sizeof (data)) != 0) { + puts ("No SDRAM Serial Presence Detect found.\n"); + return 1; + } + + cksum = 0; + for (j = 0; j < 63; j++) { + cksum += data[j]; + } + if (cksum != data[63]) { + printf ("WARNING: Configuration data checksum failure:\n" + " is 0x%02x, calculated 0x%02x\n", data[63], cksum); + } + printf ("SPD data revision %d.%d\n", + (data[62] >> 4) & 0x0F, data[62] & 0x0F); + printf ("Bytes used 0x%02X\n", data[0]); + printf ("Serial memory size 0x%02X\n", 1 << data[1]); + + puts ("Memory type "); + switch (data[2]) { + case 2: + type = EDO; + puts ("EDO\n"); + break; + case 4: + type = SDRAM; + puts ("SDRAM\n"); + break; + case 8: + type = DDR2; + puts ("DDR2\n"); + break; + default: + type = unknown; + puts ("unknown\n"); + break; + } + + puts ("Row address bits "); + if ((data[3] & 0x00F0) == 0) + printf ("%d\n", data[3] & 0x0F); + else + printf ("%d/%d\n", data[3] & 0x0F, (data[3] >> 4) & 0x0F); + + puts ("Column address bits "); + if ((data[4] & 0x00F0) == 0) + printf ("%d\n", data[4] & 0x0F); + else + printf ("%d/%d\n", data[4] & 0x0F, (data[4] >> 4) & 0x0F); + + switch (type) { + case DDR2: + printf ("Number of ranks %d\n", + (data[5] & 0x07) + 1); + break; + default: + printf ("Module rows %d\n", data[5]); + break; + } + + switch (type) { + case DDR2: + printf ("Module data width %d bits\n", data[6]); + break; + default: + printf ("Module data width %d bits\n", + (data[7] << 8) | data[6]); + break; + } + + puts ("Interface signal levels "); + switch(data[8]) { + case 0: puts ("TTL 5.0 V\n"); break; + case 1: puts ("LVTTL\n"); break; + case 2: puts ("HSTL 1.5 V\n"); break; + case 3: puts ("SSTL 3.3 V\n"); break; + case 4: puts ("SSTL 2.5 V\n"); break; + case 5: puts ("SSTL 1.8 V\n"); break; + default: puts ("unknown\n"); break; + } + + switch (type) { + case DDR2: + printf ("SDRAM cycle time "); + print_ddr2_tcyc (data[9]); + break; + default: + printf ("SDRAM cycle time %d.%d ns\n", + (data[9] >> 4) & 0x0F, data[9] & 0x0F); + break; + } + + switch (type) { + case DDR2: + printf ("SDRAM access time 0.%d%d ns\n", + (data[10] >> 4) & 0x0F, data[10] & 0x0F); + break; + default: + printf ("SDRAM access time %d.%d ns\n", + (data[10] >> 4) & 0x0F, data[10] & 0x0F); + break; + } + + puts ("EDC configuration "); + switch (data[11]) { + case 0: puts ("None\n"); break; + case 1: puts ("Parity\n"); break; + case 2: puts ("ECC\n"); break; + default: puts ("unknown\n"); break; + } + + if ((data[12] & 0x80) == 0) + puts ("No self refresh, rate "); + else + puts ("Self refresh, rate "); + + switch(data[12] & 0x7F) { + case 0: puts ("15.625 us\n"); break; + case 1: puts ("3.9 us\n"); break; + case 2: puts ("7.8 us\n"); break; + case 3: puts ("31.3 us\n"); break; + case 4: puts ("62.5 us\n"); break; + case 5: puts ("125 us\n"); break; + default: puts ("unknown\n"); break; + } + + switch (type) { + case DDR2: + printf ("SDRAM width (primary) %d\n", data[13]); + break; + default: + printf ("SDRAM width (primary) %d\n", data[13] & 0x7F); + if ((data[13] & 0x80) != 0) { + printf (" (second bank) %d\n", + 2 * (data[13] & 0x7F)); + } + break; + } + + switch (type) { + case DDR2: + if (data[14] != 0) + printf ("EDC width %d\n", data[14]); + break; + default: + if (data[14] != 0) { + printf ("EDC width %d\n", + data[14] & 0x7F); + + if ((data[14] & 0x80) != 0) { + printf (" (second bank) %d\n", + 2 * (data[14] & 0x7F)); + } + } + break; + } + + if (DDR2 != type) { + printf ("Min clock delay, back-to-back random column addresses " + "%d\n", data[15]); + } + + puts ("Burst length(s) "); + if (data[16] & 0x80) puts (" Page"); + if (data[16] & 0x08) puts (" 8"); + if (data[16] & 0x04) puts (" 4"); + if (data[16] & 0x02) puts (" 2"); + if (data[16] & 0x01) puts (" 1"); + putc ('\n'); + printf ("Number of banks %d\n", data[17]); + + switch (type) { + case DDR2: + puts ("CAS latency(s) "); + decode_bits (data[18], decode_CAS_DDR2, 0); + putc ('\n'); + break; + default: + puts ("CAS latency(s) "); + decode_bits (data[18], decode_CAS_default, 0); + putc ('\n'); + break; + } + + if (DDR2 != type) { + puts ("CS latency(s) "); + decode_bits (data[19], decode_CS_WE_default, 0); + putc ('\n'); + } + + if (DDR2 != type) { + puts ("WE latency(s) "); + decode_bits (data[20], decode_CS_WE_default, 0); + putc ('\n'); + } + + switch (type) { + case DDR2: + puts ("Module attributes:\n"); + if (data[21] & 0x80) + puts (" TBD (bit 7)\n"); + if (data[21] & 0x40) + puts (" Analysis probe installed\n"); + if (data[21] & 0x20) + puts (" TBD (bit 5)\n"); + if (data[21] & 0x10) + puts (" FET switch external enable\n"); + printf (" %d PLLs on DIMM\n", (data[21] >> 2) & 0x03); + if (data[20] & 0x11) { + printf (" %d active registers on DIMM\n", + (data[21] & 0x03) + 1); + } + break; + default: + puts ("Module attributes:\n"); + if (!data[21]) + puts (" (none)\n"); + else + decode_bits (data[21], decode_byte21_default, 0); + break; + } + + switch (type) { + case DDR2: + decode_bits (data[22], decode_byte22_DDR2, 0); + break; + default: + puts ("Device attributes:\n"); + if (data[22] & 0x80) puts (" TBD (bit 7)\n"); + if (data[22] & 0x40) puts (" TBD (bit 6)\n"); + if (data[22] & 0x20) puts (" Upper Vcc tolerance 5%\n"); + else puts (" Upper Vcc tolerance 10%\n"); + if (data[22] & 0x10) puts (" Lower Vcc tolerance 5%\n"); + else puts (" Lower Vcc tolerance 10%\n"); + if (data[22] & 0x08) puts (" Supports write1/read burst\n"); + if (data[22] & 0x04) puts (" Supports precharge all\n"); + if (data[22] & 0x02) puts (" Supports auto precharge\n"); + if (data[22] & 0x01) puts (" Supports early RAS# precharge\n"); + break; + } + + switch (type) { + case DDR2: + printf ("SDRAM cycle time (2nd highest CAS latency) "); + print_ddr2_tcyc (data[23]); + break; + default: + printf ("SDRAM cycle time (2nd highest CAS latency) %d." + "%d ns\n", (data[23] >> 4) & 0x0F, data[23] & 0x0F); + break; + } + + switch (type) { + case DDR2: + printf ("SDRAM access from clock (2nd highest CAS latency) 0." + "%d%d ns\n", (data[24] >> 4) & 0x0F, data[24] & 0x0F); + break; + default: + printf ("SDRAM access from clock (2nd highest CAS latency) %d." + "%d ns\n", (data[24] >> 4) & 0x0F, data[24] & 0x0F); + break; + } + + switch (type) { + case DDR2: + printf ("SDRAM cycle time (3rd highest CAS latency) "); + print_ddr2_tcyc (data[25]); + break; + default: + printf ("SDRAM cycle time (3rd highest CAS latency) %d." + "%d ns\n", (data[25] >> 4) & 0x0F, data[25] & 0x0F); + break; + } + + switch (type) { + case DDR2: + printf ("SDRAM access from clock (3rd highest CAS latency) 0." + "%d%d ns\n", (data[26] >> 4) & 0x0F, data[26] & 0x0F); + break; + default: + printf ("SDRAM access from clock (3rd highest CAS latency) %d." + "%d ns\n", (data[26] >> 4) & 0x0F, data[26] & 0x0F); + break; + } + + switch (type) { + case DDR2: + printf ("Minimum row precharge %d.%02d ns\n", + (data[27] >> 2) & 0x3F, 25 * (data[27] & 0x03)); + break; + default: + printf ("Minimum row precharge %d ns\n", data[27]); + break; + } + + switch (type) { + case DDR2: + printf ("Row active to row active min %d.%02d ns\n", + (data[28] >> 2) & 0x3F, 25 * (data[28] & 0x03)); + break; + default: + printf ("Row active to row active min %d ns\n", data[28]); + break; + } + + switch (type) { + case DDR2: + printf ("RAS to CAS delay min %d.%02d ns\n", + (data[29] >> 2) & 0x3F, 25 * (data[29] & 0x03)); + break; + default: + printf ("RAS to CAS delay min %d ns\n", data[29]); + break; + } + + printf ("Minimum RAS pulse width %d ns\n", data[30]); + + switch (type) { + case DDR2: + puts ("Density of each row "); + decode_bits (data[31], decode_row_density_DDR2, 1); + putc ('\n'); + break; + default: + puts ("Density of each row "); + decode_bits (data[31], decode_row_density_default, 1); + putc ('\n'); + break; + } + + switch (type) { + case DDR2: + puts ("Command and Address setup "); + if (data[32] >= 0xA0) { + printf ("1.%d%d ns\n", + ((data[32] >> 4) & 0x0F) - 10, data[32] & 0x0F); + } else { + printf ("0.%d%d ns\n", + ((data[32] >> 4) & 0x0F), data[32] & 0x0F); + } + break; + default: + printf ("Command and Address setup %c%d.%d ns\n", + (data[32] & 0x80) ? '-' : '+', + (data[32] >> 4) & 0x07, data[32] & 0x0F); + break; + } + + switch (type) { + case DDR2: + puts ("Command and Address hold "); + if (data[33] >= 0xA0) { + printf ("1.%d%d ns\n", + ((data[33] >> 4) & 0x0F) - 10, data[33] & 0x0F); + } else { + printf ("0.%d%d ns\n", + ((data[33] >> 4) & 0x0F), data[33] & 0x0F); + } + break; + default: + printf ("Command and Address hold %c%d.%d ns\n", + (data[33] & 0x80) ? '-' : '+', + (data[33] >> 4) & 0x07, data[33] & 0x0F); + break; + } + + switch (type) { + case DDR2: + printf ("Data signal input setup 0.%d%d ns\n", + (data[34] >> 4) & 0x0F, data[34] & 0x0F); + break; + default: + printf ("Data signal input setup %c%d.%d ns\n", + (data[34] & 0x80) ? '-' : '+', + (data[34] >> 4) & 0x07, data[34] & 0x0F); + break; + } + + switch (type) { + case DDR2: + printf ("Data signal input hold 0.%d%d ns\n", + (data[35] >> 4) & 0x0F, data[35] & 0x0F); + break; + default: + printf ("Data signal input hold %c%d.%d ns\n", + (data[35] & 0x80) ? '-' : '+', + (data[35] >> 4) & 0x07, data[35] & 0x0F); + break; + } + + puts ("Manufacturer's JEDEC ID "); + for (j = 64; j <= 71; j++) + printf ("%02X ", data[j]); + putc ('\n'); + printf ("Manufacturing Location %02X\n", data[72]); + puts ("Manufacturer's Part Number "); + for (j = 73; j <= 90; j++) + printf ("%02X ", data[j]); + putc ('\n'); + printf ("Revision Code %02X %02X\n", data[91], data[92]); + printf ("Manufacturing Date %02X %02X\n", data[93], data[94]); + puts ("Assembly Serial Number "); + for (j = 95; j <= 98; j++) + printf ("%02X ", data[j]); + putc ('\n'); + + if (DDR2 != type) { + printf ("Speed rating PC%d\n", + data[126] == 0x66 ? 66 : data[126]); + } + return 0; +} +#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 */ + +/** + * do_i2c_show_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. + */ +#if defined(CONFIG_SYS_I2C) +int do_i2c_show_bus(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + int i; +#ifndef CONFIG_SYS_I2C_DIRECT_BUS + int j; +#endif + + if (argc == 1) { + /* show all busses */ + for (i = 0; i < CONFIG_SYS_NUM_I2C_BUSES; i++) { + printf("Bus %d:\t%s", i, I2C_ADAP_NR(i)->name); +#ifndef CONFIG_SYS_I2C_DIRECT_BUS + for (j = 0; j < CONFIG_SYS_I2C_MAX_HOPS; j++) { + if (i2c_bus[i].next_hop[j].chip == 0) + break; + printf("->%s@0x%2x:%d", + i2c_bus[i].next_hop[j].mux.name, + i2c_bus[i].next_hop[j].chip, + i2c_bus[i].next_hop[j].channel); + } +#endif + printf("\n"); + } + } else { + /* show specific bus */ + i = simple_strtoul(argv[1], NULL, 10); + if (i >= CONFIG_SYS_NUM_I2C_BUSES) { + printf("Invalid bus %d\n", i); + return -1; + } + printf("Bus %d:\t%s", i, I2C_ADAP_NR(i)->name); +#ifndef CONFIG_SYS_I2C_DIRECT_BUS + for (j = 0; j < CONFIG_SYS_I2C_MAX_HOPS; j++) { + if (i2c_bus[i].next_hop[j].chip == 0) + break; + printf("->%s@0x%2x:%d", + i2c_bus[i].next_hop[j].mux.name, + i2c_bus[i].next_hop[j].chip, + i2c_bus[i].next_hop[j].channel); + } +#endif + printf("\n"); + } + + return 0; +} +#endif + +/** + * 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. + */ +#if defined(CONFIG_SYS_I2C) || defined(CONFIG_I2C_MULTI_BUS) +int do_i2c_bus_num(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + int ret = 0; + unsigned int bus_no; + + if (argc == 1) + /* querying current setting */ + printf("Current bus is %d\n", i2c_get_bus_num()); + else { + bus_no = simple_strtoul(argv[1], NULL, 10); +#if defined(CONFIG_SYS_I2C) + if (bus_no >= CONFIG_SYS_NUM_I2C_BUSES) { + printf("Invalid bus %d\n", bus_no); + return -1; + } +#endif + printf("Setting bus to %d\n", bus_no); + ret = i2c_set_bus_num(bus_no); + if (ret) + printf("Failure changing bus number (%d)\n", ret); + } + return ret; +} +#endif /* defined(CONFIG_SYS_I2C) */ + +/** + * 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; + + if (argc == 1) + /* querying current speed */ + printf("Current bus speed=%d\n", i2c_get_bus_speed()); + else { + speed = simple_strtoul(argv[1], NULL, 10); + printf("Setting bus speed to %d Hz\n", speed); + ret = i2c_set_bus_speed(speed); + if (ret) + printf("Failure changing bus speed (%d)\n", ret); + } + 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[]) +{ +#if defined(CONFIG_SYS_I2C) + i2c_init(I2C_ADAP->speed, I2C_ADAP->slaveaddr); +#else + i2c_init(CONFIG_SYS_I2C_SPEED, CONFIG_SYS_I2C_SLAVE); +#endif + return 0; +} + +static cmd_tbl_t cmd_i2c_sub[] = { +#if defined(CONFIG_SYS_I2C) + U_BOOT_CMD_MKENT(bus, 1, 1, do_i2c_show_bus, "", ""), +#endif + U_BOOT_CMD_MKENT(crc32, 3, 1, do_i2c_crc, "", ""), +#if defined(CONFIG_SYS_I2C) || \ + 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, "", ""), + U_BOOT_CMD_MKENT(mw, 3, 1, do_i2c_mw, "", ""), + U_BOOT_CMD_MKENT(nm, 2, 1, do_i2c_nm, "", ""), + U_BOOT_CMD_MKENT(probe, 0, 1, do_i2c_probe, "", ""), + U_BOOT_CMD_MKENT(read, 5, 1, do_i2c_read, "", ""), + U_BOOT_CMD_MKENT(write, 5, 0, do_i2c_write, "", ""), + U_BOOT_CMD_MKENT(reset, 0, 1, do_i2c_reset, "", ""), +#if defined(CONFIG_CMD_SDRAM) + U_BOOT_CMD_MKENT(sdram, 1, 1, do_sdram, "", ""), +#endif + U_BOOT_CMD_MKENT(speed, 1, 1, do_i2c_bus_speed, "", ""), +}; + +#ifdef CONFIG_NEEDS_MANUAL_RELOC +void i2c_reloc(void) { + fixup_cmdtable(cmd_i2c_sub, ARRAY_SIZE(cmd_i2c_sub)); +} +#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; + + if (argc < 2) + return CMD_RET_USAGE; + + /* Strip off leading 'i2c' command argument */ + argc--; + argv++; + + c = find_cmd_tbl(argv[0], &cmd_i2c_sub[0], ARRAY_SIZE(cmd_i2c_sub)); + + if (c) + return c->cmd(cmdtp, flag, argc, argv); + else + return CMD_RET_USAGE; +} + +/***************************************************/ +#ifdef CONFIG_SYS_LONGHELP +static char i2c_help_text[] = +#if defined(CONFIG_SYS_I2C) + "bus [muxtype:muxaddr:muxchannel] - show I2C bus info\n" +#endif + "crc32 chip address[.0, .1, .2] count - compute CRC32 checksum\n" +#if defined(CONFIG_SYS_I2C) || \ + 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" + "i2c mw chip address[.0, .1, .2] value [count] - write to I2C device (fill)\n" + "i2c nm chip address[.0, .1, .2] - write to I2C device (constant address)\n" + "i2c probe [address] - test for and show device(s) on the I2C bus\n" + "i2c read chip address[.0, .1, .2] length memaddress - read to memory \n" + "i2c write memaddress chip address[.0, .1, .2] length - write memory to i2c\n" + "i2c reset - re-init the I2C Controller\n" +#if defined(CONFIG_CMD_SDRAM) + "i2c sdram chip - print SDRAM configuration information\n" +#endif + "i2c speed [speed] - show or set I2C bus speed"; +#endif + +U_BOOT_CMD( + i2c, 6, 1, do_i2c, + "I2C sub-system", + i2c_help_text +); diff --git a/qemu/roms/u-boot/common/cmd_ide.c b/qemu/roms/u-boot/common/cmd_ide.c new file mode 100644 index 000000000..c375ef209 --- /dev/null +++ b/qemu/roms/u-boot/common/cmd_ide.c @@ -0,0 +1,1558 @@ +/* + * (C) Copyright 2000-2011 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/* + * IDE support + */ + +#include <common.h> +#include <config.h> +#include <watchdog.h> +#include <command.h> +#include <image.h> +#include <asm/byteorder.h> +#include <asm/io.h> + +#if defined(CONFIG_IDE_8xx_DIRECT) || defined(CONFIG_IDE_PCMCIA) +# include <pcmcia.h> +#endif + +#include <ide.h> +#include <ata.h> + +#ifdef CONFIG_STATUS_LED +# include <status_led.h> +#endif + +#ifdef __PPC__ +# define EIEIO __asm__ volatile ("eieio") +# define SYNC __asm__ volatile ("sync") +#else +# define EIEIO /* nothing */ +# define SYNC /* nothing */ +#endif + +/* ------------------------------------------------------------------------- */ + +/* Current I/O Device */ +static int curr_device = -1; + +/* Current offset for IDE0 / IDE1 bus access */ +ulong ide_bus_offset[CONFIG_SYS_IDE_MAXBUS] = { +#if defined(CONFIG_SYS_ATA_IDE0_OFFSET) + CONFIG_SYS_ATA_IDE0_OFFSET, +#endif +#if defined(CONFIG_SYS_ATA_IDE1_OFFSET) && (CONFIG_SYS_IDE_MAXBUS > 1) + CONFIG_SYS_ATA_IDE1_OFFSET, +#endif +}; + +static int ide_bus_ok[CONFIG_SYS_IDE_MAXBUS]; + +block_dev_desc_t ide_dev_desc[CONFIG_SYS_IDE_MAXDEVICE]; +/* ------------------------------------------------------------------------- */ + +#ifdef CONFIG_IDE_RESET +static void ide_reset (void); +#else +#define ide_reset() /* dummy */ +#endif + +static void ide_ident (block_dev_desc_t *dev_desc); +static uchar ide_wait (int dev, ulong t); + +#define IDE_TIME_OUT 2000 /* 2 sec timeout */ + +#define ATAPI_TIME_OUT 7000 /* 7 sec timeout (5 sec seems to work...) */ + +#define IDE_SPIN_UP_TIME_OUT 5000 /* 5 sec spin-up timeout */ + +static void ident_cpy (unsigned char *dest, unsigned char *src, unsigned int len); + +#ifndef CONFIG_SYS_ATA_PORT_ADDR +#define CONFIG_SYS_ATA_PORT_ADDR(port) (port) +#endif + +#ifdef CONFIG_ATAPI +static void atapi_inquiry(block_dev_desc_t *dev_desc); +static ulong atapi_read(int device, ulong blknr, lbaint_t blkcnt, + void *buffer); +#endif + + +/* ------------------------------------------------------------------------- */ + +int do_ide(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[]) +{ + int rcode = 0; + + switch (argc) { + case 0: + case 1: + return CMD_RET_USAGE; + case 2: + if (strncmp(argv[1], "res", 3) == 0) { + puts("\nReset IDE" +#ifdef CONFIG_IDE_8xx_DIRECT + " on PCMCIA " PCMCIA_SLOT_MSG +#endif + ": "); + + ide_init(); + return 0; + } else if (strncmp(argv[1], "inf", 3) == 0) { + int i; + + putc('\n'); + + for (i = 0; i < CONFIG_SYS_IDE_MAXDEVICE; ++i) { + if (ide_dev_desc[i].type == DEV_TYPE_UNKNOWN) + continue; /* list only known devices */ + printf("IDE device %d: ", i); + dev_print(&ide_dev_desc[i]); + } + return 0; + + } else if (strncmp(argv[1], "dev", 3) == 0) { + if ((curr_device < 0) + || (curr_device >= CONFIG_SYS_IDE_MAXDEVICE)) { + puts("\nno IDE devices available\n"); + return 1; + } + printf("\nIDE device %d: ", curr_device); + dev_print(&ide_dev_desc[curr_device]); + return 0; + } else if (strncmp(argv[1], "part", 4) == 0) { + int dev, ok; + + for (ok = 0, dev = 0; + dev < CONFIG_SYS_IDE_MAXDEVICE; + ++dev) { + if (ide_dev_desc[dev].part_type != + PART_TYPE_UNKNOWN) { + ++ok; + if (dev) + putc('\n'); + print_part(&ide_dev_desc[dev]); + } + } + if (!ok) { + puts("\nno IDE devices available\n"); + rcode++; + } + return rcode; + } + return CMD_RET_USAGE; + case 3: + if (strncmp(argv[1], "dev", 3) == 0) { + int dev = (int) simple_strtoul(argv[2], NULL, 10); + + printf("\nIDE device %d: ", dev); + if (dev >= CONFIG_SYS_IDE_MAXDEVICE) { + puts("unknown device\n"); + return 1; + } + dev_print(&ide_dev_desc[dev]); + /*ide_print (dev); */ + + if (ide_dev_desc[dev].type == DEV_TYPE_UNKNOWN) + return 1; + + curr_device = dev; + + puts("... is now current device\n"); + + return 0; + } else if (strncmp(argv[1], "part", 4) == 0) { + int dev = (int) simple_strtoul(argv[2], NULL, 10); + + if (ide_dev_desc[dev].part_type != PART_TYPE_UNKNOWN) { + print_part(&ide_dev_desc[dev]); + } else { + printf("\nIDE device %d not available\n", + dev); + rcode = 1; + } + return rcode; + } + + return CMD_RET_USAGE; + default: + /* at least 4 args */ + + if (strcmp(argv[1], "read") == 0) { + ulong addr = simple_strtoul(argv[2], NULL, 16); + ulong cnt = simple_strtoul(argv[4], NULL, 16); + ulong n; + +#ifdef CONFIG_SYS_64BIT_LBA + lbaint_t blk = simple_strtoull(argv[3], NULL, 16); + + printf("\nIDE read: device %d block # %lld, count %ld ... ", + curr_device, blk, cnt); +#else + lbaint_t blk = simple_strtoul(argv[3], NULL, 16); + + printf("\nIDE read: device %d block # %ld, count %ld ... ", + curr_device, blk, cnt); +#endif + + n = ide_dev_desc[curr_device].block_read(curr_device, + blk, cnt, + (ulong *)addr); + /* flush cache after read */ + flush_cache(addr, + cnt * ide_dev_desc[curr_device].blksz); + + printf("%ld blocks read: %s\n", + n, (n == cnt) ? "OK" : "ERROR"); + if (n == cnt) + return 0; + else + return 1; + } else if (strcmp(argv[1], "write") == 0) { + ulong addr = simple_strtoul(argv[2], NULL, 16); + ulong cnt = simple_strtoul(argv[4], NULL, 16); + ulong n; + +#ifdef CONFIG_SYS_64BIT_LBA + lbaint_t blk = simple_strtoull(argv[3], NULL, 16); + + printf("\nIDE write: device %d block # %lld, count %ld ... ", + curr_device, blk, cnt); +#else + lbaint_t blk = simple_strtoul(argv[3], NULL, 16); + + printf("\nIDE write: device %d block # %ld, count %ld ... ", + curr_device, blk, cnt); +#endif + n = ide_write(curr_device, blk, cnt, (ulong *) addr); + + printf("%ld blocks written: %s\n", + n, (n == cnt) ? "OK" : "ERROR"); + if (n == cnt) + return 0; + else + return 1; + } else { + return CMD_RET_USAGE; + } + + return rcode; + } +} + +int do_diskboot(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[]) +{ + return common_diskboot(cmdtp, "ide", argc, argv); +} + +/* ------------------------------------------------------------------------- */ + +void __ide_led(uchar led, uchar status) +{ +#if defined(CONFIG_IDE_LED) && defined(PER8_BASE) /* required by LED_PORT */ + static uchar led_buffer; /* Buffer for current LED status */ + + uchar *led_port = LED_PORT; + + if (status) /* switch LED on */ + led_buffer |= led; + else /* switch LED off */ + led_buffer &= ~led; + + *led_port = led_buffer; +#endif +} + +void ide_led(uchar led, uchar status) + __attribute__ ((weak, alias("__ide_led"))); + +#ifndef CONFIG_IDE_LED /* define LED macros, they are not used anyways */ +# define DEVICE_LED(x) 0 +# define LED_IDE1 1 +# define LED_IDE2 2 +#endif + +/* ------------------------------------------------------------------------- */ + +inline void __ide_outb(int dev, int port, unsigned char val) +{ + debug("ide_outb (dev= %d, port= 0x%x, val= 0x%02x) : @ 0x%08lx\n", + dev, port, val, + (ATA_CURR_BASE(dev) + CONFIG_SYS_ATA_PORT_ADDR(port))); + +#if defined(CONFIG_IDE_AHB) + if (port) { + /* write command */ + ide_write_register(dev, port, val); + } else { + /* write data */ + outb(val, (ATA_CURR_BASE(dev))); + } +#else + outb(val, (ATA_CURR_BASE(dev) + CONFIG_SYS_ATA_PORT_ADDR(port))); +#endif +} + +void ide_outb(int dev, int port, unsigned char val) + __attribute__ ((weak, alias("__ide_outb"))); + +inline unsigned char __ide_inb(int dev, int port) +{ + uchar val; + +#if defined(CONFIG_IDE_AHB) + val = ide_read_register(dev, port); +#else + val = inb((ATA_CURR_BASE(dev) + CONFIG_SYS_ATA_PORT_ADDR(port))); +#endif + + debug("ide_inb (dev= %d, port= 0x%x) : @ 0x%08lx -> 0x%02x\n", + dev, port, + (ATA_CURR_BASE(dev) + CONFIG_SYS_ATA_PORT_ADDR(port)), val); + return val; +} + +unsigned char ide_inb(int dev, int port) + __attribute__ ((weak, alias("__ide_inb"))); + +#ifdef CONFIG_TUNE_PIO +inline int __ide_set_piomode(int pio_mode) +{ + return 0; +} + +inline int ide_set_piomode(int pio_mode) + __attribute__ ((weak, alias("__ide_set_piomode"))); +#endif + +void ide_init(void) +{ + unsigned char c; + int i, bus; + +#ifdef CONFIG_IDE_8xx_PCCARD + extern int ide_devices_found; /* Initialized in check_ide_device() */ +#endif /* CONFIG_IDE_8xx_PCCARD */ + +#ifdef CONFIG_IDE_PREINIT + WATCHDOG_RESET(); + + if (ide_preinit()) { + puts("ide_preinit failed\n"); + return; + } +#endif /* CONFIG_IDE_PREINIT */ + + WATCHDOG_RESET(); + + /* + * Reset the IDE just to be sure. + * Light LED's to show + */ + ide_led((LED_IDE1 | LED_IDE2), 1); /* LED's on */ + + /* ATAPI Drives seems to need a proper IDE Reset */ + ide_reset(); + +#ifdef CONFIG_IDE_INIT_POSTRESET + WATCHDOG_RESET(); + + if (ide_init_postreset()) { + puts("ide_preinit_postreset failed\n"); + return; + } +#endif /* CONFIG_IDE_INIT_POSTRESET */ + + /* + * Wait for IDE to get ready. + * According to spec, this can take up to 31 seconds! + */ + for (bus = 0; bus < CONFIG_SYS_IDE_MAXBUS; ++bus) { + int dev = + bus * (CONFIG_SYS_IDE_MAXDEVICE / + CONFIG_SYS_IDE_MAXBUS); + +#ifdef CONFIG_IDE_8xx_PCCARD + /* Skip non-ide devices from probing */ + if ((ide_devices_found & (1 << bus)) == 0) { + ide_led((LED_IDE1 | LED_IDE2), 0); /* LED's off */ + continue; + } +#endif + printf("Bus %d: ", bus); + + ide_bus_ok[bus] = 0; + + /* Select device + */ + udelay(100000); /* 100 ms */ + ide_outb(dev, ATA_DEV_HD, ATA_LBA | ATA_DEVICE(dev)); + udelay(100000); /* 100 ms */ + i = 0; + do { + udelay(10000); /* 10 ms */ + + c = ide_inb(dev, ATA_STATUS); + i++; + if (i > (ATA_RESET_TIME * 100)) { + puts("** Timeout **\n"); + /* LED's off */ + ide_led((LED_IDE1 | LED_IDE2), 0); + return; + } + if ((i >= 100) && ((i % 100) == 0)) + putc('.'); + + } while (c & ATA_STAT_BUSY); + + if (c & (ATA_STAT_BUSY | ATA_STAT_FAULT)) { + puts("not available "); + debug("Status = 0x%02X ", c); +#ifndef CONFIG_ATAPI /* ATAPI Devices do not set DRDY */ + } else if ((c & ATA_STAT_READY) == 0) { + puts("not available "); + debug("Status = 0x%02X ", c); +#endif + } else { + puts("OK "); + ide_bus_ok[bus] = 1; + } + WATCHDOG_RESET(); + } + + putc('\n'); + + ide_led((LED_IDE1 | LED_IDE2), 0); /* LED's off */ + + curr_device = -1; + for (i = 0; i < CONFIG_SYS_IDE_MAXDEVICE; ++i) { + int led = (IDE_BUS(i) == 0) ? LED_IDE1 : LED_IDE2; + ide_dev_desc[i].type = DEV_TYPE_UNKNOWN; + ide_dev_desc[i].if_type = IF_TYPE_IDE; + ide_dev_desc[i].dev = i; + ide_dev_desc[i].part_type = PART_TYPE_UNKNOWN; + ide_dev_desc[i].blksz = 0; + ide_dev_desc[i].log2blksz = + LOG2_INVALID(typeof(ide_dev_desc[i].log2blksz)); + ide_dev_desc[i].lba = 0; + ide_dev_desc[i].block_read = ide_read; + ide_dev_desc[i].block_write = ide_write; + if (!ide_bus_ok[IDE_BUS(i)]) + continue; + ide_led(led, 1); /* LED on */ + ide_ident(&ide_dev_desc[i]); + ide_led(led, 0); /* LED off */ + dev_print(&ide_dev_desc[i]); + + if ((ide_dev_desc[i].lba > 0) && (ide_dev_desc[i].blksz > 0)) { + /* initialize partition type */ + init_part(&ide_dev_desc[i]); + if (curr_device < 0) + curr_device = i; + } + } + WATCHDOG_RESET(); +} + +/* ------------------------------------------------------------------------- */ + +#ifdef CONFIG_PARTITIONS +block_dev_desc_t *ide_get_dev(int dev) +{ + return (dev < CONFIG_SYS_IDE_MAXDEVICE) ? &ide_dev_desc[dev] : NULL; +} +#endif + +/* ------------------------------------------------------------------------- */ + +void ide_input_swap_data(int dev, ulong *sect_buf, int words) + __attribute__ ((weak, alias("__ide_input_swap_data"))); + +void ide_input_data(int dev, ulong *sect_buf, int words) + __attribute__ ((weak, alias("__ide_input_data"))); + +void ide_output_data(int dev, const ulong *sect_buf, int words) + __attribute__ ((weak, alias("__ide_output_data"))); + +/* We only need to swap data if we are running on a big endian cpu. */ +#if defined(__LITTLE_ENDIAN) +void __ide_input_swap_data(int dev, ulong *sect_buf, int words) +{ + ide_input_data(dev, sect_buf, words); +} +#else +void __ide_input_swap_data(int dev, ulong *sect_buf, int words) +{ + volatile ushort *pbuf = + (ushort *) (ATA_CURR_BASE(dev) + ATA_DATA_REG); + ushort *dbuf = (ushort *) sect_buf; + + debug("in input swap data base for read is %lx\n", + (unsigned long) pbuf); + + while (words--) { +#ifdef __MIPS__ + *dbuf++ = swab16p((u16 *) pbuf); + *dbuf++ = swab16p((u16 *) pbuf); +#else + *dbuf++ = ld_le16(pbuf); + *dbuf++ = ld_le16(pbuf); +#endif /* !MIPS */ + } +} +#endif /* __LITTLE_ENDIAN */ + + +#if defined(CONFIG_IDE_SWAP_IO) +void __ide_output_data(int dev, const ulong *sect_buf, int words) +{ + ushort *dbuf; + volatile ushort *pbuf; + + pbuf = (ushort *) (ATA_CURR_BASE(dev) + ATA_DATA_REG); + dbuf = (ushort *) sect_buf; + while (words--) { + EIEIO; + *pbuf = *dbuf++; + EIEIO; + *pbuf = *dbuf++; + } +} +#else /* ! CONFIG_IDE_SWAP_IO */ +void __ide_output_data(int dev, const ulong *sect_buf, int words) +{ +#if defined(CONFIG_IDE_AHB) + ide_write_data(dev, sect_buf, words); +#else + outsw(ATA_CURR_BASE(dev) + ATA_DATA_REG, sect_buf, words << 1); +#endif +} +#endif /* CONFIG_IDE_SWAP_IO */ + +#if defined(CONFIG_IDE_SWAP_IO) +void __ide_input_data(int dev, ulong *sect_buf, int words) +{ + ushort *dbuf; + volatile ushort *pbuf; + + pbuf = (ushort *) (ATA_CURR_BASE(dev) + ATA_DATA_REG); + dbuf = (ushort *) sect_buf; + + debug("in input data base for read is %lx\n", (unsigned long) pbuf); + + while (words--) { + EIEIO; + *dbuf++ = *pbuf; + EIEIO; + *dbuf++ = *pbuf; + } +} +#else /* ! CONFIG_IDE_SWAP_IO */ +void __ide_input_data(int dev, ulong *sect_buf, int words) +{ +#if defined(CONFIG_IDE_AHB) + ide_read_data(dev, sect_buf, words); +#else + insw(ATA_CURR_BASE(dev) + ATA_DATA_REG, sect_buf, words << 1); +#endif +} + +#endif /* CONFIG_IDE_SWAP_IO */ + +/* ------------------------------------------------------------------------- + */ +static void ide_ident(block_dev_desc_t *dev_desc) +{ + unsigned char c; + hd_driveid_t iop; + +#ifdef CONFIG_ATAPI + int retries = 0; +#endif + +#ifdef CONFIG_TUNE_PIO + int pio_mode; +#endif + +#if 0 + int mode, cycle_time; +#endif + int device; + + device = dev_desc->dev; + printf(" Device %d: ", device); + + ide_led(DEVICE_LED(device), 1); /* LED on */ + /* Select device + */ + ide_outb(device, ATA_DEV_HD, ATA_LBA | ATA_DEVICE(device)); + dev_desc->if_type = IF_TYPE_IDE; +#ifdef CONFIG_ATAPI + + retries = 0; + + /* Warning: This will be tricky to read */ + while (retries <= 1) { + /* check signature */ + if ((ide_inb(device, ATA_SECT_CNT) == 0x01) && + (ide_inb(device, ATA_SECT_NUM) == 0x01) && + (ide_inb(device, ATA_CYL_LOW) == 0x14) && + (ide_inb(device, ATA_CYL_HIGH) == 0xEB)) { + /* ATAPI Signature found */ + dev_desc->if_type = IF_TYPE_ATAPI; + /* + * Start Ident Command + */ + ide_outb(device, ATA_COMMAND, ATAPI_CMD_IDENT); + /* + * Wait for completion - ATAPI devices need more time + * to become ready + */ + c = ide_wait(device, ATAPI_TIME_OUT); + } else +#endif + { + /* + * Start Ident Command + */ + ide_outb(device, ATA_COMMAND, ATA_CMD_IDENT); + + /* + * Wait for completion + */ + c = ide_wait(device, IDE_TIME_OUT); + } + ide_led(DEVICE_LED(device), 0); /* LED off */ + + if (((c & ATA_STAT_DRQ) == 0) || + ((c & (ATA_STAT_FAULT | ATA_STAT_ERR)) != 0)) { +#ifdef CONFIG_ATAPI + { + /* + * Need to soft reset the device + * in case it's an ATAPI... + */ + debug("Retrying...\n"); + ide_outb(device, ATA_DEV_HD, + ATA_LBA | ATA_DEVICE(device)); + udelay(100000); + ide_outb(device, ATA_COMMAND, 0x08); + udelay(500000); /* 500 ms */ + } + /* + * Select device + */ + ide_outb(device, ATA_DEV_HD, + ATA_LBA | ATA_DEVICE(device)); + retries++; +#else + return; +#endif + } +#ifdef CONFIG_ATAPI + else + break; + } /* see above - ugly to read */ + + if (retries == 2) /* Not found */ + return; +#endif + + ide_input_swap_data(device, (ulong *)&iop, ATA_SECTORWORDS); + + ident_cpy((unsigned char *) dev_desc->revision, iop.fw_rev, + sizeof(dev_desc->revision)); + ident_cpy((unsigned char *) dev_desc->vendor, iop.model, + sizeof(dev_desc->vendor)); + ident_cpy((unsigned char *) dev_desc->product, iop.serial_no, + sizeof(dev_desc->product)); +#ifdef __LITTLE_ENDIAN + /* + * firmware revision, model, and serial number have Big Endian Byte + * order in Word. Convert all three to little endian. + * + * See CF+ and CompactFlash Specification Revision 2.0: + * 6.2.1.6: Identify Drive, Table 39 for more details + */ + + strswab(dev_desc->revision); + strswab(dev_desc->vendor); + strswab(dev_desc->product); +#endif /* __LITTLE_ENDIAN */ + + if ((iop.config & 0x0080) == 0x0080) + dev_desc->removable = 1; + else + dev_desc->removable = 0; + +#ifdef CONFIG_TUNE_PIO + /* Mode 0 - 2 only, are directly determined by word 51. */ + pio_mode = iop.tPIO; + if (pio_mode > 2) { + printf("WARNING: Invalid PIO (word 51 = %d).\n", pio_mode); + /* Force it to dead slow, and hope for the best... */ + pio_mode = 0; + } + + /* Any CompactFlash Storage Card that supports PIO mode 3 or above + * shall set bit 1 of word 53 to one and support the fields contained + * in words 64 through 70. + */ + if (iop.field_valid & 0x02) { + /* + * Mode 3 and above are possible. Check in order from slow + * to fast, so we wind up with the highest mode allowed. + */ + if (iop.eide_pio_modes & 0x01) + pio_mode = 3; + if (iop.eide_pio_modes & 0x02) + pio_mode = 4; + if (ata_id_is_cfa((u16 *)&iop)) { + if ((iop.cf_advanced_caps & 0x07) == 0x01) + pio_mode = 5; + if ((iop.cf_advanced_caps & 0x07) == 0x02) + pio_mode = 6; + } + } + + /* System-specific, depends on bus speeds, etc. */ + ide_set_piomode(pio_mode); +#endif /* CONFIG_TUNE_PIO */ + +#if 0 + /* + * Drive PIO mode autoselection + */ + mode = iop.tPIO; + + printf("tPIO = 0x%02x = %d\n", mode, mode); + if (mode > 2) { /* 2 is maximum allowed tPIO value */ + mode = 2; + debug("Override tPIO -> 2\n"); + } + if (iop.field_valid & 2) { /* drive implements ATA2? */ + debug("Drive implements ATA2\n"); + if (iop.capability & 8) { /* drive supports use_iordy? */ + cycle_time = iop.eide_pio_iordy; + } else { + cycle_time = iop.eide_pio; + } + debug("cycle time = %d\n", cycle_time); + mode = 4; + if (cycle_time > 120) + mode = 3; /* 120 ns for PIO mode 4 */ + if (cycle_time > 180) + mode = 2; /* 180 ns for PIO mode 3 */ + if (cycle_time > 240) + mode = 1; /* 240 ns for PIO mode 4 */ + if (cycle_time > 383) + mode = 0; /* 383 ns for PIO mode 4 */ + } + printf("PIO mode to use: PIO %d\n", mode); +#endif /* 0 */ + +#ifdef CONFIG_ATAPI + if (dev_desc->if_type == IF_TYPE_ATAPI) { + atapi_inquiry(dev_desc); + return; + } +#endif /* CONFIG_ATAPI */ + +#ifdef __BIG_ENDIAN + /* swap shorts */ + dev_desc->lba = (iop.lba_capacity << 16) | (iop.lba_capacity >> 16); +#else /* ! __BIG_ENDIAN */ + /* + * do not swap shorts on little endian + * + * See CF+ and CompactFlash Specification Revision 2.0: + * 6.2.1.6: Identfy Drive, Table 39, Word Address 57-58 for details. + */ + dev_desc->lba = iop.lba_capacity; +#endif /* __BIG_ENDIAN */ + +#ifdef CONFIG_LBA48 + if (iop.command_set_2 & 0x0400) { /* LBA 48 support */ + dev_desc->lba48 = 1; + dev_desc->lba = (unsigned long long) iop.lba48_capacity[0] | + ((unsigned long long) iop.lba48_capacity[1] << 16) | + ((unsigned long long) iop.lba48_capacity[2] << 32) | + ((unsigned long long) iop.lba48_capacity[3] << 48); + } else { + dev_desc->lba48 = 0; + } +#endif /* CONFIG_LBA48 */ + /* assuming HD */ + dev_desc->type = DEV_TYPE_HARDDISK; + dev_desc->blksz = ATA_BLOCKSIZE; + dev_desc->log2blksz = LOG2(dev_desc->blksz); + dev_desc->lun = 0; /* just to fill something in... */ + +#if 0 /* only used to test the powersaving mode, + * if enabled, the drive goes after 5 sec + * in standby mode */ + ide_outb(device, ATA_DEV_HD, ATA_LBA | ATA_DEVICE(device)); + c = ide_wait(device, IDE_TIME_OUT); + ide_outb(device, ATA_SECT_CNT, 1); + ide_outb(device, ATA_LBA_LOW, 0); + ide_outb(device, ATA_LBA_MID, 0); + ide_outb(device, ATA_LBA_HIGH, 0); + ide_outb(device, ATA_DEV_HD, ATA_LBA | ATA_DEVICE(device)); + ide_outb(device, ATA_COMMAND, 0xe3); + udelay(50); + c = ide_wait(device, IDE_TIME_OUT); /* can't take over 500 ms */ +#endif +} + + +/* ------------------------------------------------------------------------- */ + +ulong ide_read(int device, lbaint_t blknr, lbaint_t blkcnt, void *buffer) +{ + ulong n = 0; + unsigned char c; + unsigned char pwrsave = 0; /* power save */ + +#ifdef CONFIG_LBA48 + unsigned char lba48 = 0; + + if (blknr & 0x0000fffff0000000ULL) { + /* more than 28 bits used, use 48bit mode */ + lba48 = 1; + } +#endif + debug("ide_read dev %d start " LBAF ", blocks " LBAF " buffer at %lX\n", + device, blknr, blkcnt, (ulong) buffer); + + ide_led(DEVICE_LED(device), 1); /* LED on */ + + /* Select device + */ + ide_outb(device, ATA_DEV_HD, ATA_LBA | ATA_DEVICE(device)); + c = ide_wait(device, IDE_TIME_OUT); + + if (c & ATA_STAT_BUSY) { + printf("IDE read: device %d not ready\n", device); + goto IDE_READ_E; + } + + /* first check if the drive is in Powersaving mode, if yes, + * increase the timeout value */ + ide_outb(device, ATA_COMMAND, ATA_CMD_CHK_PWR); + udelay(50); + + c = ide_wait(device, IDE_TIME_OUT); /* can't take over 500 ms */ + + if (c & ATA_STAT_BUSY) { + printf("IDE read: device %d not ready\n", device); + goto IDE_READ_E; + } + if ((c & ATA_STAT_ERR) == ATA_STAT_ERR) { + printf("No Powersaving mode %X\n", c); + } else { + c = ide_inb(device, ATA_SECT_CNT); + debug("Powersaving %02X\n", c); + if (c == 0) + pwrsave = 1; + } + + + while (blkcnt-- > 0) { + + c = ide_wait(device, IDE_TIME_OUT); + + if (c & ATA_STAT_BUSY) { + printf("IDE read: device %d not ready\n", device); + break; + } +#ifdef CONFIG_LBA48 + if (lba48) { + /* write high bits */ + ide_outb(device, ATA_SECT_CNT, 0); + ide_outb(device, ATA_LBA_LOW, (blknr >> 24) & 0xFF); +#ifdef CONFIG_SYS_64BIT_LBA + ide_outb(device, ATA_LBA_MID, (blknr >> 32) & 0xFF); + ide_outb(device, ATA_LBA_HIGH, (blknr >> 40) & 0xFF); +#else + ide_outb(device, ATA_LBA_MID, 0); + ide_outb(device, ATA_LBA_HIGH, 0); +#endif + } +#endif + ide_outb(device, ATA_SECT_CNT, 1); + ide_outb(device, ATA_LBA_LOW, (blknr >> 0) & 0xFF); + ide_outb(device, ATA_LBA_MID, (blknr >> 8) & 0xFF); + ide_outb(device, ATA_LBA_HIGH, (blknr >> 16) & 0xFF); + +#ifdef CONFIG_LBA48 + if (lba48) { + ide_outb(device, ATA_DEV_HD, + ATA_LBA | ATA_DEVICE(device)); + ide_outb(device, ATA_COMMAND, ATA_CMD_READ_EXT); + + } else +#endif + { + ide_outb(device, ATA_DEV_HD, ATA_LBA | + ATA_DEVICE(device) | ((blknr >> 24) & 0xF)); + ide_outb(device, ATA_COMMAND, ATA_CMD_READ); + } + + udelay(50); + + if (pwrsave) { + /* may take up to 4 sec */ + c = ide_wait(device, IDE_SPIN_UP_TIME_OUT); + pwrsave = 0; + } else { + /* can't take over 500 ms */ + c = ide_wait(device, IDE_TIME_OUT); + } + + if ((c & (ATA_STAT_DRQ | ATA_STAT_BUSY | ATA_STAT_ERR)) != + ATA_STAT_DRQ) { + printf("Error (no IRQ) dev %d blk " LBAF ": status " + "%#02x\n", device, blknr, c); + break; + } + + ide_input_data(device, buffer, ATA_SECTORWORDS); + (void) ide_inb(device, ATA_STATUS); /* clear IRQ */ + + ++n; + ++blknr; + buffer += ATA_BLOCKSIZE; + } +IDE_READ_E: + ide_led(DEVICE_LED(device), 0); /* LED off */ + return (n); +} + +/* ------------------------------------------------------------------------- */ + + +ulong ide_write(int device, lbaint_t blknr, lbaint_t blkcnt, const void *buffer) +{ + ulong n = 0; + unsigned char c; + +#ifdef CONFIG_LBA48 + unsigned char lba48 = 0; + + if (blknr & 0x0000fffff0000000ULL) { + /* more than 28 bits used, use 48bit mode */ + lba48 = 1; + } +#endif + + ide_led(DEVICE_LED(device), 1); /* LED on */ + + /* Select device + */ + ide_outb(device, ATA_DEV_HD, ATA_LBA | ATA_DEVICE(device)); + + while (blkcnt-- > 0) { + + c = ide_wait(device, IDE_TIME_OUT); + + if (c & ATA_STAT_BUSY) { + printf("IDE read: device %d not ready\n", device); + goto WR_OUT; + } +#ifdef CONFIG_LBA48 + if (lba48) { + /* write high bits */ + ide_outb(device, ATA_SECT_CNT, 0); + ide_outb(device, ATA_LBA_LOW, (blknr >> 24) & 0xFF); +#ifdef CONFIG_SYS_64BIT_LBA + ide_outb(device, ATA_LBA_MID, (blknr >> 32) & 0xFF); + ide_outb(device, ATA_LBA_HIGH, (blknr >> 40) & 0xFF); +#else + ide_outb(device, ATA_LBA_MID, 0); + ide_outb(device, ATA_LBA_HIGH, 0); +#endif + } +#endif + ide_outb(device, ATA_SECT_CNT, 1); + ide_outb(device, ATA_LBA_LOW, (blknr >> 0) & 0xFF); + ide_outb(device, ATA_LBA_MID, (blknr >> 8) & 0xFF); + ide_outb(device, ATA_LBA_HIGH, (blknr >> 16) & 0xFF); + +#ifdef CONFIG_LBA48 + if (lba48) { + ide_outb(device, ATA_DEV_HD, + ATA_LBA | ATA_DEVICE(device)); + ide_outb(device, ATA_COMMAND, ATA_CMD_WRITE_EXT); + + } else +#endif + { + ide_outb(device, ATA_DEV_HD, ATA_LBA | + ATA_DEVICE(device) | ((blknr >> 24) & 0xF)); + ide_outb(device, ATA_COMMAND, ATA_CMD_WRITE); + } + + udelay(50); + + /* can't take over 500 ms */ + c = ide_wait(device, IDE_TIME_OUT); + + if ((c & (ATA_STAT_DRQ | ATA_STAT_BUSY | ATA_STAT_ERR)) != + ATA_STAT_DRQ) { + printf("Error (no IRQ) dev %d blk " LBAF ": status " + "%#02x\n", device, blknr, c); + goto WR_OUT; + } + + ide_output_data(device, buffer, ATA_SECTORWORDS); + c = ide_inb(device, ATA_STATUS); /* clear IRQ */ + ++n; + ++blknr; + buffer += ATA_BLOCKSIZE; + } +WR_OUT: + ide_led(DEVICE_LED(device), 0); /* LED off */ + return (n); +} + +/* ------------------------------------------------------------------------- */ + +/* + * copy src to dest, skipping leading and trailing blanks and null + * terminate the string + * "len" is the size of available memory including the terminating '\0' + */ +static void ident_cpy(unsigned char *dst, unsigned char *src, + unsigned int len) +{ + unsigned char *end, *last; + + last = dst; + end = src + len - 1; + + /* reserve space for '\0' */ + if (len < 2) + goto OUT; + + /* skip leading white space */ + while ((*src) && (src < end) && (*src == ' ')) + ++src; + + /* copy string, omitting trailing white space */ + while ((*src) && (src < end)) { + *dst++ = *src; + if (*src++ != ' ') + last = dst; + } +OUT: + *last = '\0'; +} + +/* ------------------------------------------------------------------------- */ + +/* + * Wait until Busy bit is off, or timeout (in ms) + * Return last status + */ +static uchar ide_wait(int dev, ulong t) +{ + ulong delay = 10 * t; /* poll every 100 us */ + uchar c; + + while ((c = ide_inb(dev, ATA_STATUS)) & ATA_STAT_BUSY) { + udelay(100); + if (delay-- == 0) + break; + } + return (c); +} + +/* ------------------------------------------------------------------------- */ + +#ifdef CONFIG_IDE_RESET +extern void ide_set_reset(int idereset); + +static void ide_reset(void) +{ + int i; + + curr_device = -1; + for (i = 0; i < CONFIG_SYS_IDE_MAXBUS; ++i) + ide_bus_ok[i] = 0; + for (i = 0; i < CONFIG_SYS_IDE_MAXDEVICE; ++i) + ide_dev_desc[i].type = DEV_TYPE_UNKNOWN; + + ide_set_reset(1); /* assert reset */ + + /* the reset signal shall be asserted for et least 25 us */ + udelay(25); + + WATCHDOG_RESET(); + + /* de-assert RESET signal */ + ide_set_reset(0); + + /* wait 250 ms */ + for (i = 0; i < 250; ++i) + udelay(1000); +} + +#endif /* CONFIG_IDE_RESET */ + +/* ------------------------------------------------------------------------- */ + +#if defined(CONFIG_OF_IDE_FIXUP) +int ide_device_present(int dev) +{ + if (dev >= CONFIG_SYS_IDE_MAXBUS) + return 0; + return (ide_dev_desc[dev].type == DEV_TYPE_UNKNOWN ? 0 : 1); +} +#endif +/* ------------------------------------------------------------------------- */ + +#ifdef CONFIG_ATAPI +/**************************************************************************** + * ATAPI Support + */ + +void ide_input_data_shorts(int dev, ushort *sect_buf, int shorts) + __attribute__ ((weak, alias("__ide_input_data_shorts"))); + +void ide_output_data_shorts(int dev, ushort *sect_buf, int shorts) + __attribute__ ((weak, alias("__ide_output_data_shorts"))); + + +#if defined(CONFIG_IDE_SWAP_IO) +/* since ATAPI may use commands with not 4 bytes alligned length + * we have our own transfer functions, 2 bytes alligned */ +void __ide_output_data_shorts(int dev, ushort *sect_buf, int shorts) +{ + ushort *dbuf; + volatile ushort *pbuf; + + pbuf = (ushort *) (ATA_CURR_BASE(dev) + ATA_DATA_REG); + dbuf = (ushort *) sect_buf; + + debug("in output data shorts base for read is %lx\n", + (unsigned long) pbuf); + + while (shorts--) { + EIEIO; + *pbuf = *dbuf++; + } +} + +void __ide_input_data_shorts(int dev, ushort *sect_buf, int shorts) +{ + ushort *dbuf; + volatile ushort *pbuf; + + pbuf = (ushort *) (ATA_CURR_BASE(dev) + ATA_DATA_REG); + dbuf = (ushort *) sect_buf; + + debug("in input data shorts base for read is %lx\n", + (unsigned long) pbuf); + + while (shorts--) { + EIEIO; + *dbuf++ = *pbuf; + } +} + +#else /* ! CONFIG_IDE_SWAP_IO */ +void __ide_output_data_shorts(int dev, ushort *sect_buf, int shorts) +{ + outsw(ATA_CURR_BASE(dev) + ATA_DATA_REG, sect_buf, shorts); +} + +void __ide_input_data_shorts(int dev, ushort *sect_buf, int shorts) +{ + insw(ATA_CURR_BASE(dev) + ATA_DATA_REG, sect_buf, shorts); +} + +#endif /* CONFIG_IDE_SWAP_IO */ + +/* + * Wait until (Status & mask) == res, or timeout (in ms) + * Return last status + * This is used since some ATAPI CD ROMs clears their Busy Bit first + * and then they set their DRQ Bit + */ +static uchar atapi_wait_mask(int dev, ulong t, uchar mask, uchar res) +{ + ulong delay = 10 * t; /* poll every 100 us */ + uchar c; + + /* prevents to read the status before valid */ + c = ide_inb(dev, ATA_DEV_CTL); + + while (((c = ide_inb(dev, ATA_STATUS)) & mask) != res) { + /* break if error occurs (doesn't make sense to wait more) */ + if ((c & ATA_STAT_ERR) == ATA_STAT_ERR) + break; + udelay(100); + if (delay-- == 0) + break; + } + return (c); +} + +/* + * issue an atapi command + */ +unsigned char atapi_issue(int device, unsigned char *ccb, int ccblen, + unsigned char *buffer, int buflen) +{ + unsigned char c, err, mask, res; + int n; + + ide_led(DEVICE_LED(device), 1); /* LED on */ + + /* Select device + */ + mask = ATA_STAT_BUSY | ATA_STAT_DRQ; + res = 0; + ide_outb(device, ATA_DEV_HD, ATA_LBA | ATA_DEVICE(device)); + c = atapi_wait_mask(device, ATAPI_TIME_OUT, mask, res); + if ((c & mask) != res) { + printf("ATAPI_ISSUE: device %d not ready status %X\n", device, + c); + err = 0xFF; + goto AI_OUT; + } + /* write taskfile */ + ide_outb(device, ATA_ERROR_REG, 0); /* no DMA, no overlaped */ + ide_outb(device, ATA_SECT_CNT, 0); + ide_outb(device, ATA_SECT_NUM, 0); + ide_outb(device, ATA_CYL_LOW, (unsigned char) (buflen & 0xFF)); + ide_outb(device, ATA_CYL_HIGH, + (unsigned char) ((buflen >> 8) & 0xFF)); + ide_outb(device, ATA_DEV_HD, ATA_LBA | ATA_DEVICE(device)); + + ide_outb(device, ATA_COMMAND, ATAPI_CMD_PACKET); + udelay(50); + + mask = ATA_STAT_DRQ | ATA_STAT_BUSY | ATA_STAT_ERR; + res = ATA_STAT_DRQ; + c = atapi_wait_mask(device, ATAPI_TIME_OUT, mask, res); + + if ((c & mask) != res) { /* DRQ must be 1, BSY 0 */ + printf("ATAPI_ISSUE: Error (no IRQ) before sending ccb dev %d status 0x%02x\n", + device, c); + err = 0xFF; + goto AI_OUT; + } + + /* write command block */ + ide_output_data_shorts(device, (unsigned short *) ccb, ccblen / 2); + + /* ATAPI Command written wait for completition */ + udelay(5000); /* device must set bsy */ + + mask = ATA_STAT_DRQ | ATA_STAT_BUSY | ATA_STAT_ERR; + /* + * if no data wait for DRQ = 0 BSY = 0 + * if data wait for DRQ = 1 BSY = 0 + */ + res = 0; + if (buflen) + res = ATA_STAT_DRQ; + c = atapi_wait_mask(device, ATAPI_TIME_OUT, mask, res); + if ((c & mask) != res) { + if (c & ATA_STAT_ERR) { + err = (ide_inb(device, ATA_ERROR_REG)) >> 4; + debug("atapi_issue 1 returned sense key %X status %02X\n", + err, c); + } else { + printf("ATAPI_ISSUE: (no DRQ) after sending ccb (%x) status 0x%02x\n", + ccb[0], c); + err = 0xFF; + } + goto AI_OUT; + } + n = ide_inb(device, ATA_CYL_HIGH); + n <<= 8; + n += ide_inb(device, ATA_CYL_LOW); + if (n > buflen) { + printf("ERROR, transfer bytes %d requested only %d\n", n, + buflen); + err = 0xff; + goto AI_OUT; + } + if ((n == 0) && (buflen < 0)) { + printf("ERROR, transfer bytes %d requested %d\n", n, buflen); + err = 0xff; + goto AI_OUT; + } + if (n != buflen) { + debug("WARNING, transfer bytes %d not equal with requested %d\n", + n, buflen); + } + if (n != 0) { /* data transfer */ + debug("ATAPI_ISSUE: %d Bytes to transfer\n", n); + /* we transfer shorts */ + n >>= 1; + /* ok now decide if it is an in or output */ + if ((ide_inb(device, ATA_SECT_CNT) & 0x02) == 0) { + debug("Write to device\n"); + ide_output_data_shorts(device, + (unsigned short *) buffer, n); + } else { + debug("Read from device @ %p shorts %d\n", buffer, n); + ide_input_data_shorts(device, + (unsigned short *) buffer, n); + } + } + udelay(5000); /* seems that some CD ROMs need this... */ + mask = ATA_STAT_BUSY | ATA_STAT_ERR; + res = 0; + c = atapi_wait_mask(device, ATAPI_TIME_OUT, mask, res); + if ((c & ATA_STAT_ERR) == ATA_STAT_ERR) { + err = (ide_inb(device, ATA_ERROR_REG) >> 4); + debug("atapi_issue 2 returned sense key %X status %X\n", err, + c); + } else { + err = 0; + } +AI_OUT: + ide_led(DEVICE_LED(device), 0); /* LED off */ + return (err); +} + +/* + * sending the command to atapi_issue. If an status other than good + * returns, an request_sense will be issued + */ + +#define ATAPI_DRIVE_NOT_READY 100 +#define ATAPI_UNIT_ATTN 10 + +unsigned char atapi_issue_autoreq(int device, + unsigned char *ccb, + int ccblen, + unsigned char *buffer, int buflen) +{ + unsigned char sense_data[18], sense_ccb[12]; + unsigned char res, key, asc, ascq; + int notready, unitattn; + + unitattn = ATAPI_UNIT_ATTN; + notready = ATAPI_DRIVE_NOT_READY; + +retry: + res = atapi_issue(device, ccb, ccblen, buffer, buflen); + if (res == 0) + return 0; /* Ok */ + + if (res == 0xFF) + return 0xFF; /* error */ + + debug("(auto_req)atapi_issue returned sense key %X\n", res); + + memset(sense_ccb, 0, sizeof(sense_ccb)); + memset(sense_data, 0, sizeof(sense_data)); + sense_ccb[0] = ATAPI_CMD_REQ_SENSE; + sense_ccb[4] = 18; /* allocation Length */ + + res = atapi_issue(device, sense_ccb, 12, sense_data, 18); + key = (sense_data[2] & 0xF); + asc = (sense_data[12]); + ascq = (sense_data[13]); + + debug("ATAPI_CMD_REQ_SENSE returned %x\n", res); + debug(" Sense page: %02X key %02X ASC %02X ASCQ %02X\n", + sense_data[0], key, asc, ascq); + + if ((key == 0)) + return 0; /* ok device ready */ + + if ((key == 6) || (asc == 0x29) || (asc == 0x28)) { /* Unit Attention */ + if (unitattn-- > 0) { + udelay(200 * 1000); + goto retry; + } + printf("Unit Attention, tried %d\n", ATAPI_UNIT_ATTN); + goto error; + } + if ((asc == 0x4) && (ascq == 0x1)) { + /* not ready, but will be ready soon */ + if (notready-- > 0) { + udelay(200 * 1000); + goto retry; + } + printf("Drive not ready, tried %d times\n", + ATAPI_DRIVE_NOT_READY); + goto error; + } + if (asc == 0x3a) { + debug("Media not present\n"); + goto error; + } + + printf("ERROR: Unknown Sense key %02X ASC %02X ASCQ %02X\n", key, asc, + ascq); +error: + debug("ERROR Sense key %02X ASC %02X ASCQ %02X\n", key, asc, ascq); + return (0xFF); +} + + +static void atapi_inquiry(block_dev_desc_t *dev_desc) +{ + unsigned char ccb[12]; /* Command descriptor block */ + unsigned char iobuf[64]; /* temp buf */ + unsigned char c; + int device; + + device = dev_desc->dev; + dev_desc->type = DEV_TYPE_UNKNOWN; /* not yet valid */ + dev_desc->block_read = atapi_read; + + memset(ccb, 0, sizeof(ccb)); + memset(iobuf, 0, sizeof(iobuf)); + + ccb[0] = ATAPI_CMD_INQUIRY; + ccb[4] = 40; /* allocation Legnth */ + c = atapi_issue_autoreq(device, ccb, 12, (unsigned char *) iobuf, 40); + + debug("ATAPI_CMD_INQUIRY returned %x\n", c); + if (c != 0) + return; + + /* copy device ident strings */ + ident_cpy((unsigned char *) dev_desc->vendor, &iobuf[8], 8); + ident_cpy((unsigned char *) dev_desc->product, &iobuf[16], 16); + ident_cpy((unsigned char *) dev_desc->revision, &iobuf[32], 5); + + dev_desc->lun = 0; + dev_desc->lba = 0; + dev_desc->blksz = 0; + dev_desc->log2blksz = LOG2_INVALID(typeof(dev_desc->log2blksz)); + dev_desc->type = iobuf[0] & 0x1f; + + if ((iobuf[1] & 0x80) == 0x80) + dev_desc->removable = 1; + else + dev_desc->removable = 0; + + memset(ccb, 0, sizeof(ccb)); + memset(iobuf, 0, sizeof(iobuf)); + ccb[0] = ATAPI_CMD_START_STOP; + ccb[4] = 0x03; /* start */ + + c = atapi_issue_autoreq(device, ccb, 12, (unsigned char *) iobuf, 0); + + debug("ATAPI_CMD_START_STOP returned %x\n", c); + if (c != 0) + return; + + memset(ccb, 0, sizeof(ccb)); + memset(iobuf, 0, sizeof(iobuf)); + c = atapi_issue_autoreq(device, ccb, 12, (unsigned char *) iobuf, 0); + + debug("ATAPI_CMD_UNIT_TEST_READY returned %x\n", c); + if (c != 0) + return; + + memset(ccb, 0, sizeof(ccb)); + memset(iobuf, 0, sizeof(iobuf)); + ccb[0] = ATAPI_CMD_READ_CAP; + c = atapi_issue_autoreq(device, ccb, 12, (unsigned char *) iobuf, 8); + debug("ATAPI_CMD_READ_CAP returned %x\n", c); + if (c != 0) + return; + + debug("Read Cap: LBA %02X%02X%02X%02X blksize %02X%02X%02X%02X\n", + iobuf[0], iobuf[1], iobuf[2], iobuf[3], + iobuf[4], iobuf[5], iobuf[6], iobuf[7]); + + dev_desc->lba = ((unsigned long) iobuf[0] << 24) + + ((unsigned long) iobuf[1] << 16) + + ((unsigned long) iobuf[2] << 8) + ((unsigned long) iobuf[3]); + dev_desc->blksz = ((unsigned long) iobuf[4] << 24) + + ((unsigned long) iobuf[5] << 16) + + ((unsigned long) iobuf[6] << 8) + ((unsigned long) iobuf[7]); + dev_desc->log2blksz = LOG2(dev_desc->blksz); +#ifdef CONFIG_LBA48 + /* ATAPI devices cannot use 48bit addressing (ATA/ATAPI v7) */ + dev_desc->lba48 = 0; +#endif + return; +} + + +/* + * atapi_read: + * we transfer only one block per command, since the multiple DRQ per + * command is not yet implemented + */ +#define ATAPI_READ_MAX_BYTES 2048 /* we read max 2kbytes */ +#define ATAPI_READ_BLOCK_SIZE 2048 /* assuming CD part */ +#define ATAPI_READ_MAX_BLOCK (ATAPI_READ_MAX_BYTES/ATAPI_READ_BLOCK_SIZE) + +ulong atapi_read(int device, ulong blknr, lbaint_t blkcnt, void *buffer) +{ + ulong n = 0; + unsigned char ccb[12]; /* Command descriptor block */ + ulong cnt; + + debug("atapi_read dev %d start %lX, blocks " LBAF " buffer at %lX\n", + device, blknr, blkcnt, (ulong) buffer); + + do { + if (blkcnt > ATAPI_READ_MAX_BLOCK) + cnt = ATAPI_READ_MAX_BLOCK; + else + cnt = blkcnt; + + ccb[0] = ATAPI_CMD_READ_12; + ccb[1] = 0; /* reserved */ + ccb[2] = (unsigned char) (blknr >> 24) & 0xFF; /* MSB Block */ + ccb[3] = (unsigned char) (blknr >> 16) & 0xFF; /* */ + ccb[4] = (unsigned char) (blknr >> 8) & 0xFF; + ccb[5] = (unsigned char) blknr & 0xFF; /* LSB Block */ + ccb[6] = (unsigned char) (cnt >> 24) & 0xFF; /* MSB Block cnt */ + ccb[7] = (unsigned char) (cnt >> 16) & 0xFF; + ccb[8] = (unsigned char) (cnt >> 8) & 0xFF; + ccb[9] = (unsigned char) cnt & 0xFF; /* LSB Block */ + ccb[10] = 0; /* reserved */ + ccb[11] = 0; /* reserved */ + + if (atapi_issue_autoreq(device, ccb, 12, + (unsigned char *) buffer, + cnt * ATAPI_READ_BLOCK_SIZE) + == 0xFF) { + return (n); + } + n += cnt; + blkcnt -= cnt; + blknr += cnt; + buffer += (cnt * ATAPI_READ_BLOCK_SIZE); + } while (blkcnt > 0); + return (n); +} + +/* ------------------------------------------------------------------------- */ + +#endif /* CONFIG_ATAPI */ + +U_BOOT_CMD(ide, 5, 1, do_ide, + "IDE sub-system", + "reset - reset IDE controller\n" + "ide info - show available IDE devices\n" + "ide device [dev] - show or set current device\n" + "ide part [dev] - print partition table of one or all IDE devices\n" + "ide read addr blk# cnt\n" + "ide write addr blk# cnt - read/write `cnt'" + " blocks starting at block `blk#'\n" + " to/from memory address `addr'"); + +U_BOOT_CMD(diskboot, 3, 1, do_diskboot, + "boot from IDE device", "loadAddr dev:part"); diff --git a/qemu/roms/u-boot/common/cmd_immap.c b/qemu/roms/u-boot/common/cmd_immap.c new file mode 100644 index 000000000..1414f9ad5 --- /dev/null +++ b/qemu/roms/u-boot/common/cmd_immap.c @@ -0,0 +1,703 @@ +/* + * (C) Copyright 2000-2003 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/* + * MPC8xx/MPC8260 Internal Memory Map Functions + */ + +#include <common.h> +#include <command.h> + +#if defined(CONFIG_8xx) || defined(CONFIG_MPC8260) + +#if defined(CONFIG_8xx) +#include <asm/8xx_immap.h> +#include <commproc.h> +#include <asm/iopin_8xx.h> +#elif defined(CONFIG_MPC8260) +#include <asm/immap_8260.h> +#include <asm/cpm_8260.h> +#include <asm/iopin_8260.h> +#endif + +DECLARE_GLOBAL_DATA_PTR; + +static void +unimplemented ( cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + printf ("Sorry, but the '%s' command has not been implemented\n", + cmdtp->name); +} + +int +do_siuinfo (cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + volatile immap_t *immap = (immap_t *) CONFIG_SYS_IMMR; + +#if defined(CONFIG_8xx) + volatile sysconf8xx_t *sc = &immap->im_siu_conf; +#elif defined(CONFIG_MPC8260) + volatile sysconf8260_t *sc = &immap->im_siu_conf; +#endif + + printf ("SIUMCR= %08x SYPCR = %08x\n", sc->sc_siumcr, sc->sc_sypcr); +#if defined(CONFIG_8xx) + printf ("SWT = %08x\n", sc->sc_swt); + printf ("SIPEND= %08x SIMASK= %08x\n", sc->sc_sipend, sc->sc_simask); + printf ("SIEL = %08x SIVEC = %08x\n", sc->sc_siel, sc->sc_sivec); + printf ("TESR = %08x SDCR = %08x\n", sc->sc_tesr, sc->sc_sdcr); +#elif defined(CONFIG_MPC8260) + printf ("BCR = %08x\n", sc->sc_bcr); + printf ("P_ACR = %02x P_ALRH= %08x P_ALRL= %08x\n", + sc->sc_ppc_acr, sc->sc_ppc_alrh, sc->sc_ppc_alrl); + printf ("L_ACR = %02x L_ALRH= %08x L_ALRL= %08x\n", + sc->sc_lcl_acr, sc->sc_lcl_alrh, sc->sc_lcl_alrl); + printf ("PTESR1= %08x PTESR2= %08x\n", sc->sc_tescr1, sc->sc_tescr2); + printf ("LTESR1= %08x LTESR2= %08x\n", sc->sc_ltescr1, sc->sc_ltescr2); + printf ("PDTEA = %08x PDTEM = %02x\n", sc->sc_pdtea, sc->sc_pdtem); + printf ("LDTEA = %08x LDTEM = %02x\n", sc->sc_ldtea, sc->sc_ldtem); +#endif + return 0; +} + +int +do_memcinfo (cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + volatile immap_t *immap = (immap_t *) CONFIG_SYS_IMMR; + +#if defined(CONFIG_8xx) + volatile memctl8xx_t *memctl = &immap->im_memctl; + int nbanks = 8; +#elif defined(CONFIG_MPC8260) + volatile memctl8260_t *memctl = &immap->im_memctl; + int nbanks = 12; +#endif + volatile uint *p = &memctl->memc_br0; + int i; + + for (i = 0; i < nbanks; i++, p += 2) { + if (i < 10) { + printf ("BR%d = %08x OR%d = %08x\n", + i, p[0], i, p[1]); + } else { + printf ("BR%d = %08x OR%d = %08x\n", + i, p[0], i, p[1]); + } + } + + printf ("MAR = %08x", memctl->memc_mar); +#if defined(CONFIG_8xx) + printf (" MCR = %08x\n", memctl->memc_mcr); +#elif defined(CONFIG_MPC8260) + putc ('\n'); +#endif + printf ("MAMR = %08x MBMR = %08x", + memctl->memc_mamr, memctl->memc_mbmr); +#if defined(CONFIG_8xx) + printf ("\nMSTAT = %04x\n", memctl->memc_mstat); +#elif defined(CONFIG_MPC8260) + printf (" MCMR = %08x\n", memctl->memc_mcmr); +#endif + printf ("MPTPR = %04x MDR = %08x\n", + memctl->memc_mptpr, memctl->memc_mdr); +#if defined(CONFIG_MPC8260) + printf ("PSDMR = %08x LSDMR = %08x\n", + memctl->memc_psdmr, memctl->memc_lsdmr); + printf ("PURT = %02x PSRT = %02x\n", + memctl->memc_purt, memctl->memc_psrt); + printf ("LURT = %02x LSRT = %02x\n", + memctl->memc_lurt, memctl->memc_lsrt); + printf ("IMMR = %08x\n", memctl->memc_immr); +#endif + return 0; +} + +int +do_sitinfo (cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + unimplemented (cmdtp, flag, argc, argv); + return 0; +} + +#ifdef CONFIG_MPC8260 +int +do_icinfo (cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + unimplemented (cmdtp, flag, argc, argv); + return 0; +} +#endif + +int +do_carinfo (cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + volatile immap_t *immap = (immap_t *) CONFIG_SYS_IMMR; + +#if defined(CONFIG_8xx) + volatile car8xx_t *car = &immap->im_clkrst; +#elif defined(CONFIG_MPC8260) + volatile car8260_t *car = &immap->im_clkrst; +#endif + +#if defined(CONFIG_8xx) + printf ("SCCR = %08x\n", car->car_sccr); + printf ("PLPRCR= %08x\n", car->car_plprcr); + printf ("RSR = %08x\n", car->car_rsr); +#elif defined(CONFIG_MPC8260) + printf ("SCCR = %08x\n", car->car_sccr); + printf ("SCMR = %08x\n", car->car_scmr); + printf ("RSR = %08x\n", car->car_rsr); + printf ("RMR = %08x\n", car->car_rmr); +#endif + return 0; +} + +static int counter; + +static void +header(void) +{ + char *data = "\ + -------------------------------- --------------------------------\ + 00000000001111111111222222222233 00000000001111111111222222222233\ + 01234567890123456789012345678901 01234567890123456789012345678901\ + -------------------------------- --------------------------------\ + "; + int i; + + if (counter % 2) + putc('\n'); + counter = 0; + + for (i = 0; i < 4; i++, data += 79) + printf("%.79s\n", data); +} + +static void binary (char *label, uint value, int nbits) +{ + uint mask = 1 << (nbits - 1); + int i, second = (counter++ % 2); + + if (second) + putc (' '); + puts (label); + for (i = 32 + 1; i != nbits; i--) + putc (' '); + + while (mask != 0) { + if (value & mask) + putc ('1'); + else + putc ('0'); + mask >>= 1; + } + + if (second) + putc ('\n'); +} + +#if defined(CONFIG_8xx) +#define PA_NBITS 16 +#define PA_NB_ODR 8 +#define PB_NBITS 18 +#define PB_NB_ODR 16 +#define PC_NBITS 12 +#define PD_NBITS 13 +#elif defined(CONFIG_MPC8260) +#define PA_NBITS 32 +#define PA_NB_ODR 32 +#define PB_NBITS 28 +#define PB_NB_ODR 28 +#define PC_NBITS 32 +#define PD_NBITS 28 +#endif + +int +do_iopinfo (cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + volatile immap_t *immap = (immap_t *) CONFIG_SYS_IMMR; + +#if defined(CONFIG_8xx) + volatile iop8xx_t *iop = &immap->im_ioport; + volatile ushort *l, *r; +#elif defined(CONFIG_MPC8260) + volatile iop8260_t *iop = &immap->im_ioport; + volatile uint *l, *r; +#endif + volatile uint *R; + + counter = 0; + header (); + + /* + * Ports A & B + */ + +#if defined(CONFIG_8xx) + l = &iop->iop_padir; + R = &immap->im_cpm.cp_pbdir; +#elif defined(CONFIG_MPC8260) + l = &iop->iop_pdira; + R = &iop->iop_pdirb; +#endif + binary ("PA_DIR", *l++, PA_NBITS); + binary ("PB_DIR", *R++, PB_NBITS); + binary ("PA_PAR", *l++, PA_NBITS); + binary ("PB_PAR", *R++, PB_NBITS); +#if defined(CONFIG_MPC8260) + binary ("PA_SOR", *l++, PA_NBITS); + binary ("PB_SOR", *R++, PB_NBITS); +#endif + binary ("PA_ODR", *l++, PA_NB_ODR); + binary ("PB_ODR", *R++, PB_NB_ODR); + binary ("PA_DAT", *l++, PA_NBITS); + binary ("PB_DAT", *R++, PB_NBITS); + + header (); + + /* + * Ports C & D + */ + +#if defined(CONFIG_8xx) + l = &iop->iop_pcdir; + r = &iop->iop_pddir; +#elif defined(CONFIG_MPC8260) + l = &iop->iop_pdirc; + r = &iop->iop_pdird; +#endif + binary ("PC_DIR", *l++, PC_NBITS); + binary ("PD_DIR", *r++, PD_NBITS); + binary ("PC_PAR", *l++, PC_NBITS); + binary ("PD_PAR", *r++, PD_NBITS); +#if defined(CONFIG_8xx) + binary ("PC_SO ", *l++, PC_NBITS); + binary (" ", 0, 0); + r++; +#elif defined(CONFIG_MPC8260) + binary ("PC_SOR", *l++, PC_NBITS); + binary ("PD_SOR", *r++, PD_NBITS); + binary ("PC_ODR", *l++, PC_NBITS); + binary ("PD_ODR", *r++, PD_NBITS); +#endif + binary ("PC_DAT", *l++, PC_NBITS); + binary ("PD_DAT", *r++, PD_NBITS); +#if defined(CONFIG_8xx) + binary ("PC_INT", *l++, PC_NBITS); +#endif + + header (); + return 0; +} + +/* + * set the io pins + * this needs a clean up for smaller tighter code + * use *uint and set the address based on cmd + port + */ +int +do_iopset (cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + uint rcode = 0; + iopin_t iopin; + static uint port = 0; + static uint pin = 0; + static uint value = 0; + static enum { + DIR, + PAR, + SOR, + ODR, + DAT, +#if defined(CONFIG_8xx) + INT +#endif + } cmd = DAT; + + if (argc != 5) { + puts ("iopset PORT PIN CMD VALUE\n"); + return 1; + } + port = argv[1][0] - 'A'; + if (port > 3) + port -= 0x20; + if (port > 3) + rcode = 1; + pin = simple_strtol (argv[2], NULL, 10); + if (pin > 31) + rcode = 1; + + + switch (argv[3][0]) { + case 'd': + if (argv[3][1] == 'a') + cmd = DAT; + else if (argv[3][1] == 'i') + cmd = DIR; + else + rcode = 1; + break; + case 'p': + cmd = PAR; + break; + case 'o': + cmd = ODR; + break; + case 's': + cmd = SOR; + break; +#if defined(CONFIG_8xx) + case 'i': + cmd = INT; + break; +#endif + default: + printf ("iopset: unknown command %s\n", argv[3]); + rcode = 1; + } + if (argv[4][0] == '1') + value = 1; + else if (argv[4][0] == '0') + value = 0; + else + rcode = 1; + if (rcode == 0) { + iopin.port = port; + iopin.pin = pin; + iopin.flag = 0; + switch (cmd) { + case DIR: + if (value) + iopin_set_out (&iopin); + else + iopin_set_in (&iopin); + break; + case PAR: + if (value) + iopin_set_ded (&iopin); + else + iopin_set_gen (&iopin); + break; + case SOR: + if (value) + iopin_set_opt2 (&iopin); + else + iopin_set_opt1 (&iopin); + break; + case ODR: + if (value) + iopin_set_odr (&iopin); + else + iopin_set_act (&iopin); + break; + case DAT: + if (value) + iopin_set_high (&iopin); + else + iopin_set_low (&iopin); + break; +#if defined(CONFIG_8xx) + case INT: + if (value) + iopin_set_falledge (&iopin); + else + iopin_set_anyedge (&iopin); + break; +#endif + } + + } + return rcode; +} + +int +do_dmainfo (cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + unimplemented (cmdtp, flag, argc, argv); + return 0; +} + +int +do_fccinfo (cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + unimplemented (cmdtp, flag, argc, argv); + return 0; +} + +static void prbrg (int n, uint val) +{ + uint extc = (val >> 14) & 3; + uint cd = (val & CPM_BRG_CD_MASK) >> 1; + uint div16 = (val & CPM_BRG_DIV16) != 0; + +#if defined(CONFIG_8xx) + ulong clock = gd->cpu_clk; +#elif defined(CONFIG_MPC8260) + ulong clock = gd->arch.brg_clk; +#endif + + printf ("BRG%d:", n); + + if (val & CPM_BRG_RST) + puts (" RESET"); + else + puts (" "); + + if (val & CPM_BRG_EN) + puts (" ENABLED"); + else + puts (" DISABLED"); + + printf (" EXTC=%d", extc); + + if (val & CPM_BRG_ATB) + puts (" ATB"); + else + puts (" "); + + printf (" DIVIDER=%4d", cd); + if (extc == 0 && cd != 0) { + uint baudrate; + + if (div16) + baudrate = (clock / 16) / (cd + 1); + else + baudrate = clock / (cd + 1); + + printf ("=%6d bps", baudrate); + } else { + puts (" "); + } + + if (val & CPM_BRG_DIV16) + puts (" DIV16"); + else + puts (" "); + + putc ('\n'); +} + +int +do_brginfo (cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + volatile immap_t *immap = (immap_t *) CONFIG_SYS_IMMR; + +#if defined(CONFIG_8xx) + volatile cpm8xx_t *cp = &immap->im_cpm; + volatile uint *p = &cp->cp_brgc1; +#elif defined(CONFIG_MPC8260) + volatile uint *p = &immap->im_brgc1; +#endif + int i = 1; + + while (i <= 4) + prbrg (i++, *p++); + +#if defined(CONFIG_MPC8260) + p = &immap->im_brgc5; + while (i <= 8) + prbrg (i++, *p++); +#endif + return 0; +} + +int +do_i2cinfo (cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + volatile immap_t *immap = (immap_t *) CONFIG_SYS_IMMR; + +#if defined(CONFIG_8xx) + volatile i2c8xx_t *i2c = &immap->im_i2c; + volatile cpm8xx_t *cp = &immap->im_cpm; + volatile iic_t *iip = (iic_t *) & cp->cp_dparam[PROFF_IIC]; +#elif defined(CONFIG_MPC8260) + volatile i2c8260_t *i2c = &immap->im_i2c; + volatile iic_t *iip; + uint dpaddr; + + dpaddr = immap->im_dprambase16[PROFF_I2C_BASE / sizeof(u16)]; + if (dpaddr == 0) + iip = NULL; + else + iip = (iic_t *) & immap->im_dprambase[dpaddr]; +#endif + + printf ("I2MOD = %02x I2ADD = %02x\n", i2c->i2c_i2mod, i2c->i2c_i2add); + printf ("I2BRG = %02x I2COM = %02x\n", i2c->i2c_i2brg, i2c->i2c_i2com); + printf ("I2CER = %02x I2CMR = %02x\n", i2c->i2c_i2cer, i2c->i2c_i2cmr); + + if (iip == NULL) + puts ("i2c parameter ram not allocated\n"); + else { + printf ("RBASE = %08x TBASE = %08x\n", + iip->iic_rbase, iip->iic_tbase); + printf ("RFCR = %02x TFCR = %02x\n", + iip->iic_rfcr, iip->iic_tfcr); + printf ("MRBLR = %04x\n", iip->iic_mrblr); + printf ("RSTATE= %08x RDP = %08x\n", + iip->iic_rstate, iip->iic_rdp); + printf ("RBPTR = %04x RBC = %04x\n", + iip->iic_rbptr, iip->iic_rbc); + printf ("RXTMP = %08x\n", iip->iic_rxtmp); + printf ("TSTATE= %08x TDP = %08x\n", + iip->iic_tstate, iip->iic_tdp); + printf ("TBPTR = %04x TBC = %04x\n", + iip->iic_tbptr, iip->iic_tbc); + printf ("TXTMP = %08x\n", iip->iic_txtmp); + } + return 0; +} + +int +do_sccinfo (cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + unimplemented (cmdtp, flag, argc, argv); + return 0; +} + +int +do_smcinfo (cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + unimplemented (cmdtp, flag, argc, argv); + return 0; +} + +int +do_spiinfo (cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + unimplemented (cmdtp, flag, argc, argv); + return 0; +} + +int +do_muxinfo (cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + unimplemented (cmdtp, flag, argc, argv); + return 0; +} + +int +do_siinfo (cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + unimplemented (cmdtp, flag, argc, argv); + return 0; +} + +int +do_mccinfo (cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + unimplemented (cmdtp, flag, argc, argv); + return 0; +} + +/***************************************************/ + +U_BOOT_CMD( + siuinfo, 1, 1, do_siuinfo, + "print System Interface Unit (SIU) registers", + "" +); + +U_BOOT_CMD( + memcinfo, 1, 1, do_memcinfo, + "print Memory Controller registers", + "" +); + +U_BOOT_CMD( + sitinfo, 1, 1, do_sitinfo, + "print System Integration Timers (SIT) registers", + "" +); + +#ifdef CONFIG_MPC8260 +U_BOOT_CMD( + icinfo, 1, 1, do_icinfo, + "print Interrupt Controller registers", + "" +); +#endif + +U_BOOT_CMD( + carinfo, 1, 1, do_carinfo, + "print Clocks and Reset registers", + "" +); + +U_BOOT_CMD( + iopinfo, 1, 1, do_iopinfo, + "print I/O Port registers", + "" +); + +U_BOOT_CMD( + iopset, 5, 0, do_iopset, + "set I/O Port registers", + "PORT PIN CMD VALUE\nPORT: A-D, PIN: 0-31, CMD: [dat|dir|odr|sor], VALUE: 0|1" +); + +U_BOOT_CMD( + dmainfo, 1, 1, do_dmainfo, + "print SDMA/IDMA registers", + "" +); + +U_BOOT_CMD( + fccinfo, 1, 1, do_fccinfo, + "print FCC registers", + "" +); + +U_BOOT_CMD( + brginfo, 1, 1, do_brginfo, + "print Baud Rate Generator (BRG) registers", + "" +); + +U_BOOT_CMD( + i2cinfo, 1, 1, do_i2cinfo, + "print I2C registers", + "" +); + +U_BOOT_CMD( + sccinfo, 1, 1, do_sccinfo, + "print SCC registers", + "" +); + +U_BOOT_CMD( + smcinfo, 1, 1, do_smcinfo, + "print SMC registers", + "" +); + +U_BOOT_CMD( + spiinfo, 1, 1, do_spiinfo, + "print Serial Peripheral Interface (SPI) registers", + "" +); + +U_BOOT_CMD( + muxinfo, 1, 1, do_muxinfo, + "print CPM Multiplexing registers", + "" +); + +U_BOOT_CMD( + siinfo, 1, 1, do_siinfo, + "print Serial Interface (SI) registers", + "" +); + +U_BOOT_CMD( + mccinfo, 1, 1, do_mccinfo, + "print MCC registers", + "" +); + +#endif diff --git a/qemu/roms/u-boot/common/cmd_ini.c b/qemu/roms/u-boot/common/cmd_ini.c new file mode 100644 index 000000000..727fd1c66 --- /dev/null +++ b/qemu/roms/u-boot/common/cmd_ini.c @@ -0,0 +1,252 @@ +/* + * inih -- simple .INI file parser + * + * Copyright (c) 2009, Brush Technology + * Copyright (c) 2012: + * Joe Hershberger, National Instruments, joe.hershberger@ni.com + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + * + * Go to the project home page for more info: + * http://code.google.com/p/inih/ + */ + +#include <common.h> +#include <command.h> +#include <environment.h> +#include <linux/ctype.h> +#include <linux/string.h> + +#ifdef CONFIG_INI_MAX_LINE +#define MAX_LINE CONFIG_INI_MAX_LINE +#else +#define MAX_LINE 200 +#endif + +#ifdef CONFIG_INI_MAX_SECTION +#define MAX_SECTION CONFIG_INI_MAX_SECTION +#else +#define MAX_SECTION 50 +#endif + +#ifdef CONFIG_INI_MAX_NAME +#define MAX_NAME CONFIG_INI_MAX_NAME +#else +#define MAX_NAME 50 +#endif + +/* Strip whitespace chars off end of given string, in place. Return s. */ +static char *rstrip(char *s) +{ + char *p = s + strlen(s); + + while (p > s && isspace(*--p)) + *p = '\0'; + return s; +} + +/* Return pointer to first non-whitespace char in given string. */ +static char *lskip(const char *s) +{ + while (*s && isspace(*s)) + s++; + return (char *)s; +} + +/* Return pointer to first char c or ';' comment in given string, or pointer to + null at end of string if neither found. ';' must be prefixed by a whitespace + character to register as a comment. */ +static char *find_char_or_comment(const char *s, char c) +{ + int was_whitespace = 0; + + while (*s && *s != c && !(was_whitespace && *s == ';')) { + was_whitespace = isspace(*s); + s++; + } + return (char *)s; +} + +/* Version of strncpy that ensures dest (size bytes) is null-terminated. */ +static char *strncpy0(char *dest, const char *src, size_t size) +{ + strncpy(dest, src, size); + dest[size - 1] = '\0'; + return dest; +} + +/* Emulate the behavior of fgets but on memory */ +static char *memgets(char *str, int num, char **mem, size_t *memsize) +{ + char *end; + int len; + int newline = 1; + + end = memchr(*mem, '\n', *memsize); + if (end == NULL) { + if (*memsize == 0) + return NULL; + end = *mem + *memsize; + newline = 0; + } + len = min((end - *mem) + newline, num); + memcpy(str, *mem, len); + if (len < num) + str[len] = '\0'; + + /* prepare the mem vars for the next call */ + *memsize -= (end - *mem) + newline; + *mem += (end - *mem) + newline; + + return str; +} + +/* Parse given INI-style file. May have [section]s, name=value pairs + (whitespace stripped), and comments starting with ';' (semicolon). Section + is "" if name=value pair parsed before any section heading. name:value + pairs are also supported as a concession to Python's ConfigParser. + + For each name=value pair parsed, call handler function with given user + pointer as well as section, name, and value (data only valid for duration + of handler call). Handler should return nonzero on success, zero on error. + + Returns 0 on success, line number of first error on parse error (doesn't + stop on first error). +*/ +static int ini_parse(char *filestart, size_t filelen, + int (*handler)(void *, char *, char *, char *), void *user) +{ + /* Uses a fair bit of stack (use heap instead if you need to) */ + char line[MAX_LINE]; + char section[MAX_SECTION] = ""; + char prev_name[MAX_NAME] = ""; + + char *curmem = filestart; + char *start; + char *end; + char *name; + char *value; + size_t memleft = filelen; + int lineno = 0; + int error = 0; + + /* Scan through file line by line */ + while (memgets(line, sizeof(line), &curmem, &memleft) != NULL) { + lineno++; + start = lskip(rstrip(line)); + + if (*start == ';' || *start == '#') { + /* + * Per Python ConfigParser, allow '#' comments at start + * of line + */ + } +#if CONFIG_INI_ALLOW_MULTILINE + else if (*prev_name && *start && start > line) { + /* + * Non-blank line with leading whitespace, treat as + * continuation of previous name's value (as per Python + * ConfigParser). + */ + if (!handler(user, section, prev_name, start) && !error) + error = lineno; + } +#endif + else if (*start == '[') { + /* A "[section]" line */ + end = find_char_or_comment(start + 1, ']'); + if (*end == ']') { + *end = '\0'; + strncpy0(section, start + 1, sizeof(section)); + *prev_name = '\0'; + } else if (!error) { + /* No ']' found on section line */ + error = lineno; + } + } else if (*start && *start != ';') { + /* Not a comment, must be a name[=:]value pair */ + end = find_char_or_comment(start, '='); + if (*end != '=') + end = find_char_or_comment(start, ':'); + if (*end == '=' || *end == ':') { + *end = '\0'; + name = rstrip(start); + value = lskip(end + 1); + end = find_char_or_comment(value, '\0'); + if (*end == ';') + *end = '\0'; + rstrip(value); + /* Strip double-quotes */ + if (value[0] == '"' && + value[strlen(value)-1] == '"') { + value[strlen(value)-1] = '\0'; + value += 1; + } + + /* + * Valid name[=:]value pair found, call handler + */ + strncpy0(prev_name, name, sizeof(prev_name)); + if (!handler(user, section, name, value) && + !error) + error = lineno; + } else if (!error) + /* No '=' or ':' found on name[=:]value line */ + error = lineno; + } + } + + return error; +} + +static int ini_handler(void *user, char *section, char *name, char *value) +{ + char *requested_section = (char *)user; +#ifdef CONFIG_INI_CASE_INSENSITIVE + int i; + + for (i = 0; i < strlen(requested_section); i++) + requested_section[i] = tolower(requested_section[i]); + for (i = 0; i < strlen(section); i++) + section[i] = tolower(section[i]); +#endif + + if (!strcmp(section, requested_section)) { +#ifdef CONFIG_INI_CASE_INSENSITIVE + for (i = 0; i < strlen(name); i++) + name[i] = tolower(name[i]); + for (i = 0; i < strlen(value); i++) + value[i] = tolower(value[i]); +#endif + setenv(name, value); + printf("ini: Imported %s as %s\n", name, value); + } + + /* success */ + return 1; +} + +static int do_ini(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + const char *section; + char *file_address; + size_t file_size; + + if (argc == 1) + return CMD_RET_USAGE; + + section = argv[1]; + file_address = (char *)simple_strtoul( + argc < 3 ? getenv("loadaddr") : argv[2], NULL, 16); + file_size = (size_t)simple_strtoul( + argc < 4 ? getenv("filesize") : argv[3], NULL, 16); + + return ini_parse(file_address, file_size, ini_handler, (void *)section); +} + +U_BOOT_CMD( + ini, 4, 0, do_ini, + "parse an ini file in memory and merge the specified section into the env", + "section [[file-address] file-size]" +); diff --git a/qemu/roms/u-boot/common/cmd_io.c b/qemu/roms/u-boot/common/cmd_io.c new file mode 100644 index 000000000..eefac36f8 --- /dev/null +++ b/qemu/roms/u-boot/common/cmd_io.c @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2012 The Chromium OS Authors. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/* + * IO space access commands. + */ + +#include <common.h> +#include <command.h> +#include <asm/io.h> + +/* + * 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/qemu/roms/u-boot/common/cmd_irq.c b/qemu/roms/u-boot/common/cmd_irq.c new file mode 100644 index 000000000..02da450f6 --- /dev/null +++ b/qemu/roms/u-boot/common/cmd_irq.c @@ -0,0 +1,40 @@ +/* + * Copyright 2008 Freescale Semiconductor, Inc. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <config.h> +#include <command.h> + +static int do_interrupts(cmd_tbl_t *cmdtp, int flag, int argc, + char * const argv[]) +{ + + if (argc != 2) + return CMD_RET_USAGE; + + /* on */ + if (strncmp(argv[1], "on", 2) == 0) + enable_interrupts(); + else + disable_interrupts(); + + return 0; +} + +U_BOOT_CMD( + interrupts, 5, 0, do_interrupts, + "enable or disable interrupts", + "[on, off]" +); + +/* Implemented in $(CPU)/interrupts.c */ +int do_irqinfo (cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]); + +U_BOOT_CMD( + irqinfo, 1, 1, do_irqinfo, + "print information about IRQs", + "" +); diff --git a/qemu/roms/u-boot/common/cmd_itest.c b/qemu/roms/u-boot/common/cmd_itest.c new file mode 100644 index 000000000..ae2527bfe --- /dev/null +++ b/qemu/roms/u-boot/common/cmd_itest.c @@ -0,0 +1,190 @@ +/* + * (C) Copyright 2003 + * Tait Electronics Limited, Christchurch, New Zealand + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/* + * This file provides a shell like 'test' function to return + * true/false from an integer or string compare of two memory + * locations or a location and a scalar/literal. + * A few parts were lifted from bash 'test' command + */ + +#include <common.h> +#include <config.h> +#include <command.h> + +#define EQ 0 +#define NE 1 +#define LT 2 +#define GT 3 +#define LE 4 +#define GE 5 + +struct op_tbl_s { + char *op; /* operator string */ + int opcode; /* internal representation of opcode */ +}; + +typedef struct op_tbl_s op_tbl_t; + +static const op_tbl_t op_table [] = { + { "-lt", LT }, + { "<" , LT }, + { "-gt", GT }, + { ">" , GT }, + { "-eq", EQ }, + { "==" , EQ }, + { "-ne", NE }, + { "!=" , NE }, + { "<>" , NE }, + { "-ge", GE }, + { ">=" , GE }, + { "-le", LE }, + { "<=" , LE }, +}; + +static long evalexp(char *s, int w) +{ + long l = 0; + long *p; + + /* if the parameter starts with a * then assume is a pointer to the value we want */ + if (s[0] == '*') { + p = (long *)simple_strtoul(&s[1], NULL, 16); + switch (w) { + case 1: return((long)(*(unsigned char *)p)); + case 2: return((long)(*(unsigned short *)p)); + case 4: return(*p); + } + } else { + l = simple_strtoul(s, NULL, 16); + } + + return (l & ((1 << (w * 8)) - 1)); +} + +static char * evalstr(char *s) +{ + /* if the parameter starts with a * then assume a string pointer else its a literal */ + if (s[0] == '*') { + return (char *)simple_strtoul(&s[1], NULL, 16); + } else if (s[0] == '$') { + int i = 2; + + if (s[1] != '{') + return NULL; + + while (s[i] != '}') { + if (s[i] == 0) + return NULL; + i++; + } + s[i] = 0; + return getenv((const char *)&s[2]); + } else { + return s; + } +} + +static int stringcomp(char *s, char *t, int op) +{ + int p; + char *l, *r; + + l = evalstr(s); + r = evalstr(t); + + p = strcmp(l, r); + switch (op) { + case EQ: return (p == 0); + case NE: return (p != 0); + case LT: return (p < 0); + case GT: return (p > 0); + case LE: return (p <= 0); + case GE: return (p >= 0); + } + return (0); +} + +static int arithcomp (char *s, char *t, int op, int w) +{ + long l, r; + + l = evalexp (s, w); + r = evalexp (t, w); + + switch (op) { + case EQ: return (l == r); + case NE: return (l != r); + case LT: return (l < r); + case GT: return (l > r); + case LE: return (l <= r); + case GE: return (l >= r); + } + return (0); +} + +static int binary_test(char *op, char *arg1, char *arg2, int w) +{ + int len, i; + const op_tbl_t *optp; + + len = strlen(op); + + for (optp = (op_tbl_t *)&op_table, i = 0; + i < ARRAY_SIZE(op_table); + optp++, i++) { + + if ((strncmp (op, optp->op, len) == 0) && (len == strlen (optp->op))) { + if (w == 0) { + return (stringcomp(arg1, arg2, optp->opcode)); + } else { + return (arithcomp (arg1, arg2, optp->opcode, w)); + } + } + } + + printf("Unknown operator '%s'\n", op); + return 0; /* op code not found */ +} + +/* command line interface to the shell test */ +static int do_itest(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + int value, w; + + /* Validate arguments */ + if ((argc != 4)) + return CMD_RET_USAGE; + + /* Check for a data width specification. + * Defaults to long (4) if no specification. + * Uses -2 as 'width' for .s (string) so as not to upset existing code + */ + switch (w = cmd_get_data_size(argv[0], 4)) { + case 1: + case 2: + case 4: + value = binary_test (argv[2], argv[1], argv[3], w); + break; + case -2: + value = binary_test (argv[2], argv[1], argv[3], 0); + break; + case -1: + default: + puts("Invalid data width specifier\n"); + value = 0; + break; + } + + return !value; +} + +U_BOOT_CMD( + itest, 4, 0, do_itest, + "return true/false on integer compare", + "[.b, .w, .l, .s] [*]value1 <op> [*]value2" +); diff --git a/qemu/roms/u-boot/common/cmd_jffs2.c b/qemu/roms/u-boot/common/cmd_jffs2.c new file mode 100644 index 000000000..bce098315 --- /dev/null +++ b/qemu/roms/u-boot/common/cmd_jffs2.c @@ -0,0 +1,619 @@ +/* + * (C) Copyright 2002 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * (C) Copyright 2002 + * Robert Schwebel, Pengutronix, <r.schwebel@pengutronix.de> + * + * (C) Copyright 2003 + * Kai-Uwe Bloem, Auerswald GmbH & Co KG, <linux-development@auerswald.de> + * + * (C) Copyright 2005 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * Added support for reading flash partition table from environment. + * Parsing routines are based on driver/mtd/cmdline.c from the linux 2.4 + * kernel tree. + * + * $Id: cmdlinepart.c,v 1.17 2004/11/26 11:18:47 lavinen Exp $ + * Copyright 2002 SYSGO Real-Time Solutions GmbH + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/* + * Three environment variables are used by the parsing routines: + * + * 'partition' - keeps current partition identifier + * + * partition := <part-id> + * <part-id> := <dev-id>,part_num + * + * + * 'mtdids' - linux kernel mtd device id <-> u-boot device id mapping + * + * mtdids=<idmap>[,<idmap>,...] + * + * <idmap> := <dev-id>=<mtd-id> + * <dev-id> := 'nand'|'nor'|'onenand'<dev-num> + * <dev-num> := mtd device number, 0... + * <mtd-id> := unique device tag used by linux kernel to find mtd device (mtd->name) + * + * + * 'mtdparts' - partition list + * + * mtdparts=mtdparts=<mtd-def>[;<mtd-def>...] + * + * <mtd-def> := <mtd-id>:<part-def>[,<part-def>...] + * <mtd-id> := unique device tag used by linux kernel to find mtd device (mtd->name) + * <part-def> := <size>[@<offset>][<name>][<ro-flag>] + * <size> := standard linux memsize OR '-' to denote all remaining space + * <offset> := partition start offset within the device + * <name> := '(' NAME ')' + * <ro-flag> := when set to 'ro' makes partition read-only (not used, passed to kernel) + * + * Notes: + * - each <mtd-id> used in mtdparts must albo exist in 'mtddis' mapping + * - if the above variables are not set defaults for a given target are used + * + * Examples: + * + * 1 NOR Flash, with 1 single writable partition: + * mtdids=nor0=edb7312-nor + * mtdparts=mtdparts=edb7312-nor:- + * + * 1 NOR Flash with 2 partitions, 1 NAND with one + * mtdids=nor0=edb7312-nor,nand0=edb7312-nand + * mtdparts=mtdparts=edb7312-nor:256k(ARMboot)ro,-(root);edb7312-nand:-(home) + * + */ + +/* + * JFFS2/CRAMFS support + */ +#include <common.h> +#include <command.h> +#include <malloc.h> +#include <jffs2/jffs2.h> +#include <linux/list.h> +#include <linux/ctype.h> +#include <cramfs/cramfs_fs.h> + +#if defined(CONFIG_CMD_NAND) +#include <linux/mtd/nand.h> +#include <nand.h> +#endif + +#if defined(CONFIG_CMD_ONENAND) +#include <linux/mtd/mtd.h> +#include <linux/mtd/onenand.h> +#include <onenand_uboot.h> +#endif + +/* enable/disable debugging messages */ +#define DEBUG_JFFS +#undef DEBUG_JFFS + +#ifdef DEBUG_JFFS +# define DEBUGF(fmt, args...) printf(fmt ,##args) +#else +# define DEBUGF(fmt, args...) +#endif + +/* special size referring to all the remaining space in a partition */ +#define SIZE_REMAINING 0xFFFFFFFF + +/* special offset value, it is used when not provided by user + * + * this value is used temporarily during parsing, later such offests + * are recalculated */ +#define OFFSET_NOT_SPECIFIED 0xFFFFFFFF + +/* minimum partition size */ +#define MIN_PART_SIZE 4096 + +/* this flag needs to be set in part_info struct mask_flags + * field for read-only partitions */ +#define MTD_WRITEABLE_CMD 1 + +/* current active device and partition number */ +#ifdef CONFIG_CMD_MTDPARTS +/* Use the ones declared in cmd_mtdparts.c */ +extern struct mtd_device *current_mtd_dev; +extern u8 current_mtd_partnum; +#else +/* Use local ones */ +struct mtd_device *current_mtd_dev = NULL; +u8 current_mtd_partnum = 0; +#endif + +#if defined(CONFIG_CMD_CRAMFS) +extern int cramfs_check (struct part_info *info); +extern int cramfs_load (char *loadoffset, struct part_info *info, char *filename); +extern int cramfs_ls (struct part_info *info, char *filename); +extern int cramfs_info (struct part_info *info); +#else +/* defining empty macros for function names is ugly but avoids ifdef clutter + * all over the code */ +#define cramfs_check(x) (0) +#define cramfs_load(x,y,z) (-1) +#define cramfs_ls(x,y) (0) +#define cramfs_info(x) (0) +#endif + +#ifndef CONFIG_CMD_MTDPARTS +/** + * Check device number to be within valid range for given device type. + * + * @param dev device to validate + * @return 0 if device is valid, 1 otherwise + */ +static int mtd_device_validate(u8 type, u8 num, u32 *size) +{ + if (type == MTD_DEV_TYPE_NOR) { +#if defined(CONFIG_CMD_FLASH) + if (num < CONFIG_SYS_MAX_FLASH_BANKS) { + extern flash_info_t flash_info[]; + *size = flash_info[num].size; + + return 0; + } + + printf("no such FLASH device: %s%d (valid range 0 ... %d\n", + MTD_DEV_TYPE(type), num, CONFIG_SYS_MAX_FLASH_BANKS - 1); +#else + printf("support for FLASH devices not present\n"); +#endif + } else if (type == MTD_DEV_TYPE_NAND) { +#if defined(CONFIG_JFFS2_NAND) && defined(CONFIG_CMD_NAND) + if (num < CONFIG_SYS_MAX_NAND_DEVICE) { + *size = nand_info[num].size; + return 0; + } + + printf("no such NAND device: %s%d (valid range 0 ... %d)\n", + MTD_DEV_TYPE(type), num, CONFIG_SYS_MAX_NAND_DEVICE - 1); +#else + printf("support for NAND devices not present\n"); +#endif + } else if (type == MTD_DEV_TYPE_ONENAND) { +#if defined(CONFIG_CMD_ONENAND) + *size = onenand_mtd.size; + return 0; +#else + printf("support for OneNAND devices not present\n"); +#endif + } else + printf("Unknown defice type %d\n", type); + + return 1; +} + +/** + * Parse device id string <dev-id> := 'nand'|'nor'|'onenand'<dev-num>, + * return device type and number. + * + * @param id string describing device id + * @param ret_id output pointer to next char after parse completes (output) + * @param dev_type parsed device type (output) + * @param dev_num parsed device number (output) + * @return 0 on success, 1 otherwise + */ +static int mtd_id_parse(const char *id, const char **ret_id, u8 *dev_type, u8 *dev_num) +{ + const char *p = id; + + *dev_type = 0; + if (strncmp(p, "nand", 4) == 0) { + *dev_type = MTD_DEV_TYPE_NAND; + p += 4; + } else if (strncmp(p, "nor", 3) == 0) { + *dev_type = MTD_DEV_TYPE_NOR; + p += 3; + } else if (strncmp(p, "onenand", 7) == 0) { + *dev_type = MTD_DEV_TYPE_ONENAND; + p += 7; + } else { + printf("incorrect device type in %s\n", id); + return 1; + } + + if (!isdigit(*p)) { + printf("incorrect device number in %s\n", id); + return 1; + } + + *dev_num = simple_strtoul(p, (char **)&p, 0); + if (ret_id) + *ret_id = p; + return 0; +} + +/* + * 'Static' version of command line mtdparts_init() routine. Single partition on + * a single device configuration. + */ + +/** + * Calculate sector size. + * + * @return sector size + */ +static inline u32 get_part_sector_size_nand(struct mtdids *id) +{ +#if defined(CONFIG_JFFS2_NAND) && defined(CONFIG_CMD_NAND) + nand_info_t *nand; + + nand = &nand_info[id->num]; + + return nand->erasesize; +#else + BUG(); + return 0; +#endif +} + +static inline u32 get_part_sector_size_nor(struct mtdids *id, struct part_info *part) +{ +#if defined(CONFIG_CMD_FLASH) + extern flash_info_t flash_info[]; + + u32 end_phys, start_phys, sector_size = 0, size = 0; + int i; + flash_info_t *flash; + + flash = &flash_info[id->num]; + + start_phys = flash->start[0] + part->offset; + end_phys = start_phys + part->size - 1; + + for (i = 0; i < flash->sector_count; i++) { + if (flash->start[i] >= end_phys) + break; + + if (flash->start[i] >= start_phys) { + if (i == flash->sector_count - 1) { + size = flash->start[0] + flash->size - flash->start[i]; + } else { + size = flash->start[i+1] - flash->start[i]; + } + + if (sector_size < size) + sector_size = size; + } + } + + return sector_size; +#else + BUG(); + return 0; +#endif +} + +static inline u32 get_part_sector_size_onenand(void) +{ +#if defined(CONFIG_CMD_ONENAND) + struct mtd_info *mtd; + + mtd = &onenand_mtd; + + return mtd->erasesize; +#else + BUG(); + return 0; +#endif +} + +static inline u32 get_part_sector_size(struct mtdids *id, struct part_info *part) +{ + if (id->type == MTD_DEV_TYPE_NAND) + return get_part_sector_size_nand(id); + else if (id->type == MTD_DEV_TYPE_NOR) + return get_part_sector_size_nor(id, part); + else if (id->type == MTD_DEV_TYPE_ONENAND) + return get_part_sector_size_onenand(); + else + DEBUGF("Error: Unknown device type.\n"); + + return 0; +} + +/** + * Parse and initialize global mtdids mapping and create global + * device/partition list. + * + * 'Static' version of command line mtdparts_init() routine. Single partition on + * a single device configuration. + * + * @return 0 on success, 1 otherwise + */ +int mtdparts_init(void) +{ + static int initialized = 0; + u32 size; + char *dev_name; + + DEBUGF("\n---mtdparts_init---\n"); + if (!initialized) { + struct mtdids *id; + struct part_info *part; + + initialized = 1; + current_mtd_dev = (struct mtd_device *) + malloc(sizeof(struct mtd_device) + + sizeof(struct part_info) + + sizeof(struct mtdids)); + if (!current_mtd_dev) { + printf("out of memory\n"); + return 1; + } + memset(current_mtd_dev, 0, sizeof(struct mtd_device) + + sizeof(struct part_info) + sizeof(struct mtdids)); + + id = (struct mtdids *)(current_mtd_dev + 1); + part = (struct part_info *)(id + 1); + + /* id */ + id->mtd_id = "single part"; + +#if defined(CONFIG_JFFS2_DEV) + dev_name = CONFIG_JFFS2_DEV; +#else + dev_name = "nor0"; +#endif + + if ((mtd_id_parse(dev_name, NULL, &id->type, &id->num) != 0) || + (mtd_device_validate(id->type, id->num, &size) != 0)) { + printf("incorrect device: %s%d\n", MTD_DEV_TYPE(id->type), id->num); + free(current_mtd_dev); + return 1; + } + id->size = size; + INIT_LIST_HEAD(&id->link); + + DEBUGF("dev id: type = %d, num = %d, size = 0x%08lx, mtd_id = %s\n", + id->type, id->num, id->size, id->mtd_id); + + /* partition */ + part->name = "static"; + part->auto_name = 0; + +#if defined(CONFIG_JFFS2_PART_SIZE) + part->size = CONFIG_JFFS2_PART_SIZE; +#else + part->size = SIZE_REMAINING; +#endif + +#if defined(CONFIG_JFFS2_PART_OFFSET) + part->offset = CONFIG_JFFS2_PART_OFFSET; +#else + part->offset = 0x00000000; +#endif + + part->dev = current_mtd_dev; + INIT_LIST_HEAD(&part->link); + + /* recalculate size if needed */ + if (part->size == SIZE_REMAINING) + part->size = id->size - part->offset; + + part->sector_size = get_part_sector_size(id, part); + + DEBUGF("part : name = %s, size = 0x%08lx, offset = 0x%08lx\n", + part->name, part->size, part->offset); + + /* device */ + current_mtd_dev->id = id; + INIT_LIST_HEAD(¤t_mtd_dev->link); + current_mtd_dev->num_parts = 1; + INIT_LIST_HEAD(¤t_mtd_dev->parts); + list_add(&part->link, ¤t_mtd_dev->parts); + } + + return 0; +} +#endif /* #ifndef CONFIG_CMD_MTDPARTS */ + +/** + * Return pointer to the partition of a requested number from a requested + * device. + * + * @param dev device that is to be searched for a partition + * @param part_num requested partition number + * @return pointer to the part_info, NULL otherwise + */ +static struct part_info* jffs2_part_info(struct mtd_device *dev, unsigned int part_num) +{ + struct list_head *entry; + struct part_info *part; + int num; + + if (!dev) + return NULL; + + DEBUGF("\n--- jffs2_part_info: partition number %d for device %s%d (%s)\n", + part_num, MTD_DEV_TYPE(dev->id->type), + dev->id->num, dev->id->mtd_id); + + if (part_num >= dev->num_parts) { + printf("invalid partition number %d for device %s%d (%s)\n", + part_num, MTD_DEV_TYPE(dev->id->type), + dev->id->num, dev->id->mtd_id); + return NULL; + } + + /* locate partition number, return it */ + num = 0; + list_for_each(entry, &dev->parts) { + part = list_entry(entry, struct part_info, link); + + if (part_num == num++) { + return part; + } + } + + return NULL; +} + +/***************************************************/ +/* U-boot commands */ +/***************************************************/ + +/** + * Routine implementing fsload u-boot command. This routine tries to load + * a requested file from jffs2/cramfs filesystem on a current partition. + * + * @param cmdtp command internal data + * @param flag command flag + * @param argc number of arguments supplied to the command + * @param argv arguments list + * @return 0 on success, 1 otherwise + */ +int do_jffs2_fsload(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + char *fsname; + char *filename; + int size; + struct part_info *part; + ulong offset = load_addr; + + /* pre-set Boot file name */ + if ((filename = getenv("bootfile")) == NULL) { + filename = "uImage"; + } + + if (argc == 2) { + filename = argv[1]; + } + if (argc == 3) { + offset = simple_strtoul(argv[1], NULL, 16); + load_addr = offset; + filename = argv[2]; + } + + /* make sure we are in sync with env variables */ + if (mtdparts_init() !=0) + return 1; + + if ((part = jffs2_part_info(current_mtd_dev, current_mtd_partnum))){ + + /* check partition type for cramfs */ + fsname = (cramfs_check(part) ? "CRAMFS" : "JFFS2"); + printf("### %s loading '%s' to 0x%lx\n", fsname, filename, offset); + + if (cramfs_check(part)) { + size = cramfs_load ((char *) offset, part, filename); + } else { + /* if this is not cramfs assume jffs2 */ + size = jffs2_1pass_load((char *)offset, part, filename); + } + + if (size > 0) { + printf("### %s load complete: %d bytes loaded to 0x%lx\n", + fsname, size, offset); + setenv_hex("filesize", size); + } else { + printf("### %s LOAD ERROR<%x> for %s!\n", fsname, size, filename); + } + + return !(size > 0); + } + return 1; +} + +/** + * Routine implementing u-boot ls command which lists content of a given + * directory on a current partition. + * + * @param cmdtp command internal data + * @param flag command flag + * @param argc number of arguments supplied to the command + * @param argv arguments list + * @return 0 on success, 1 otherwise + */ +int do_jffs2_ls(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + char *filename = "/"; + int ret; + struct part_info *part; + + if (argc == 2) + filename = argv[1]; + + /* make sure we are in sync with env variables */ + if (mtdparts_init() !=0) + return 1; + + if ((part = jffs2_part_info(current_mtd_dev, current_mtd_partnum))){ + + /* check partition type for cramfs */ + if (cramfs_check(part)) { + ret = cramfs_ls (part, filename); + } else { + /* if this is not cramfs assume jffs2 */ + ret = jffs2_1pass_ls(part, filename); + } + + return ret ? 0 : 1; + } + return 1; +} + +/** + * Routine implementing u-boot fsinfo command. This routine prints out + * miscellaneous filesystem informations/statistics. + * + * @param cmdtp command internal data + * @param flag command flag + * @param argc number of arguments supplied to the command + * @param argv arguments list + * @return 0 on success, 1 otherwise + */ +int do_jffs2_fsinfo(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + struct part_info *part; + char *fsname; + int ret; + + /* make sure we are in sync with env variables */ + if (mtdparts_init() !=0) + return 1; + + if ((part = jffs2_part_info(current_mtd_dev, current_mtd_partnum))){ + + /* check partition type for cramfs */ + fsname = (cramfs_check(part) ? "CRAMFS" : "JFFS2"); + printf("### filesystem type is %s\n", fsname); + + if (cramfs_check(part)) { + ret = cramfs_info (part); + } else { + /* if this is not cramfs assume jffs2 */ + ret = jffs2_1pass_info(part); + } + + return ret ? 0 : 1; + } + return 1; +} + +/***************************************************/ +U_BOOT_CMD( + fsload, 3, 0, do_jffs2_fsload, + "load binary file from a filesystem image", + "[ off ] [ filename ]\n" + " - load binary file from flash bank\n" + " with offset 'off'" +); +U_BOOT_CMD( + ls, 2, 1, do_jffs2_ls, + "list files in a directory (default /)", + "[ directory ]" +); + +U_BOOT_CMD( + fsinfo, 1, 1, do_jffs2_fsinfo, + "print information about filesystems", + "" +); +/***************************************************/ diff --git a/qemu/roms/u-boot/common/cmd_ldrinfo.c b/qemu/roms/u-boot/common/cmd_ldrinfo.c new file mode 100644 index 000000000..2aa56bdb6 --- /dev/null +++ b/qemu/roms/u-boot/common/cmd_ldrinfo.c @@ -0,0 +1,192 @@ +/* + * U-boot - ldrinfo + * + * Copyright (c) 2010 Analog Devices Inc. + * + * See file CREDITS for list of people who contributed to this + * project. + * + * Licensed under the GPL-2 or later. + */ + +#include <config.h> +#include <common.h> +#include <command.h> + +#include <asm/blackfin.h> +#include <asm/mach-common/bits/bootrom.h> + +static uint32_t ldrinfo_header(const void *addr) +{ + uint32_t skip = 0; + +#if defined(__ADSPBF561__) + /* BF56x has a 4 byte global header */ + uint32_t header, sign; + static const char * const spi_speed[] = { + "500K", "1M", "2M", "??", + }; + + memcpy(&header, addr, sizeof(header)); + + sign = (header & GFLAG_56X_SIGN_MASK) >> GFLAG_56X_SIGN_SHIFT; + printf("Header: %08X ( %s-bit-flash wait:%i hold:%i spi:%s %s)\n", + header, + (header & GFLAG_56X_16BIT_FLASH) ? "16" : "8", + (header & GFLAG_56X_WAIT_MASK) >> GFLAG_56X_WAIT_SHIFT, + (header & GFLAG_56X_HOLD_MASK) >> GFLAG_56X_HOLD_SHIFT, + spi_speed[(header & GFLAG_56X_SPI_MASK) >> GFLAG_56X_SPI_SHIFT], + sign == GFLAG_56X_SIGN_MAGIC ? "" : "!!hdrsign!! "); + + skip = 4; +#endif + + /* |Block @ 12345678: 12345678 12345678 12345678 12345678 | */ +#if defined(__ADSPBF531__) || defined(__ADSPBF532__) || defined(__ADSPBF533__) || \ + defined(__ADSPBF534__) || defined(__ADSPBF536__) || defined(__ADSPBF537__) || \ + defined(__ADSPBF538__) || defined(__ADSPBF539__) || defined(__ADSPBF561__) + printf(" Address Count Flags\n"); +#else + printf(" BCode Address Count Argument\n"); +#endif + + return skip; +} + +struct ldr_flag { + uint16_t flag; + const char *desc; +}; + +static uint32_t ldrinfo_block(const void *base_addr) +{ + uint32_t count; + + printf("Block @ %08X: ", (uint32_t)base_addr); + +#if defined(__ADSPBF531__) || defined(__ADSPBF532__) || defined(__ADSPBF533__) || \ + defined(__ADSPBF534__) || defined(__ADSPBF536__) || defined(__ADSPBF537__) || \ + defined(__ADSPBF538__) || defined(__ADSPBF539__) || defined(__ADSPBF561__) + + uint32_t addr, pval; + uint16_t flags; + int i; + static const struct ldr_flag ldr_flags[] = { + { BFLAG_53X_ZEROFILL, "zerofill" }, + { BFLAG_53X_RESVECT, "resvect" }, + { BFLAG_53X_INIT, "init" }, + { BFLAG_53X_IGNORE, "ignore" }, + { BFLAG_53X_COMPRESSED, "compressed"}, + { BFLAG_53X_FINAL, "final" }, + }; + + memcpy(&addr, base_addr, sizeof(addr)); + memcpy(&count, base_addr+4, sizeof(count)); + memcpy(&flags, base_addr+8, sizeof(flags)); + + printf("%08X %08X %04X ( ", addr, count, flags); + + for (i = 0; i < ARRAY_SIZE(ldr_flags); ++i) + if (flags & ldr_flags[i].flag) + printf("%s ", ldr_flags[i].desc); + + pval = (flags & BFLAG_53X_PFLAG_MASK) >> BFLAG_53X_PFLAG_SHIFT; + if (pval) + printf("gpio%i ", pval); + pval = (flags & BFLAG_53X_PPORT_MASK) >> BFLAG_53X_PPORT_SHIFT; + if (pval) + printf("port%c ", 'e' + pval); + + if (flags & BFLAG_53X_ZEROFILL) + count = 0; + if (flags & BFLAG_53X_FINAL) + count = 0; + else + count += sizeof(addr) + sizeof(count) + sizeof(flags); + +#else + + const uint8_t *raw8 = base_addr; + uint32_t bcode, addr, arg, sign, chk; + int i; + static const struct ldr_flag ldr_flags[] = { + { BFLAG_SAFE, "safe" }, + { BFLAG_AUX, "aux" }, + { BFLAG_FILL, "fill" }, + { BFLAG_QUICKBOOT, "quickboot" }, + { BFLAG_CALLBACK, "callback" }, + { BFLAG_INIT, "init" }, + { BFLAG_IGNORE, "ignore" }, + { BFLAG_INDIRECT, "indirect" }, + { BFLAG_FIRST, "first" }, + { BFLAG_FINAL, "final" }, + }; + + memcpy(&bcode, base_addr, sizeof(bcode)); + memcpy(&addr, base_addr+4, sizeof(addr)); + memcpy(&count, base_addr+8, sizeof(count)); + memcpy(&arg, base_addr+12, sizeof(arg)); + + printf("%08X %08X %08X %08X ( ", bcode, addr, count, arg); + + if (addr % 4) + printf("!!addralgn!! "); + if (count % 4) + printf("!!cntalgn!! "); + + sign = (bcode & BFLAG_HDRSIGN_MASK) >> BFLAG_HDRSIGN_SHIFT; + if (sign != BFLAG_HDRSIGN_MAGIC) + printf("!!hdrsign!! "); + + chk = 0; + for (i = 0; i < 16; ++i) + chk ^= raw8[i]; + if (chk) + printf("!!hdrchk!! "); + + printf("dma:%i ", bcode & BFLAG_DMACODE_MASK); + + for (i = 0; i < ARRAY_SIZE(ldr_flags); ++i) + if (bcode & ldr_flags[i].flag) + printf("%s ", ldr_flags[i].desc); + + if (bcode & BFLAG_FILL) + count = 0; + if (bcode & BFLAG_FINAL) + count = 0; + else + count += sizeof(bcode) + sizeof(addr) + sizeof(count) + sizeof(arg); + +#endif + + printf(")\n"); + + return count; +} + +static int do_ldrinfo(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + const void *addr; + uint32_t skip; + + /* Get the address */ + if (argc < 2) + addr = (void *)load_addr; + else + addr = (void *)simple_strtoul(argv[1], NULL, 16); + + /* Walk the LDR */ + addr += ldrinfo_header(addr); + do { + skip = ldrinfo_block(addr); + addr += skip; + } while (skip); + + return 0; +} + +U_BOOT_CMD( + ldrinfo, 2, 0, do_ldrinfo, + "validate ldr image in memory", + "[addr]\n" +); diff --git a/qemu/roms/u-boot/common/cmd_led.c b/qemu/roms/u-boot/common/cmd_led.c new file mode 100644 index 000000000..172bc30bd --- /dev/null +++ b/qemu/roms/u-boot/common/cmd_led.c @@ -0,0 +1,158 @@ +/* + * (C) Copyright 2010 + * Jason Kridner <jkridner@beagleboard.org> + * + * Based on cmd_led.c patch from: + * http://www.mail-archive.com/u-boot@lists.denx.de/msg06873.html + * (C) Copyright 2008 + * Ulf Samuelsson <ulf.samuelsson@atmel.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <config.h> +#include <command.h> +#include <status_led.h> + +struct led_tbl_s { + char *string; /* String for use in the command */ + led_id_t mask; /* Mask used for calling __led_set() */ + void (*off)(void); /* Optional function for turning LED off */ + void (*on)(void); /* Optional function for turning LED on */ + void (*toggle)(void);/* Optional function for toggling LED */ +}; + +typedef struct led_tbl_s led_tbl_t; + +static const led_tbl_t led_commands[] = { +#ifdef CONFIG_BOARD_SPECIFIC_LED +#ifdef STATUS_LED_BIT + { "0", STATUS_LED_BIT, NULL, NULL, NULL }, +#endif +#ifdef STATUS_LED_BIT1 + { "1", STATUS_LED_BIT1, NULL, NULL, NULL }, +#endif +#ifdef STATUS_LED_BIT2 + { "2", STATUS_LED_BIT2, NULL, NULL, NULL }, +#endif +#ifdef STATUS_LED_BIT3 + { "3", STATUS_LED_BIT3, NULL, NULL, NULL }, +#endif +#endif +#ifdef STATUS_LED_GREEN + { "green", STATUS_LED_GREEN, green_led_off, green_led_on, NULL }, +#endif +#ifdef STATUS_LED_YELLOW + { "yellow", STATUS_LED_YELLOW, yellow_led_off, yellow_led_on, NULL }, +#endif +#ifdef STATUS_LED_RED + { "red", STATUS_LED_RED, red_led_off, red_led_on, NULL }, +#endif +#ifdef STATUS_LED_BLUE + { "blue", STATUS_LED_BLUE, blue_led_off, blue_led_on, NULL }, +#endif + { NULL, 0, NULL, NULL, NULL } +}; + +enum led_cmd { LED_ON, LED_OFF, LED_TOGGLE }; + +enum led_cmd get_led_cmd(char *var) +{ + if (strcmp(var, "off") == 0) { + return LED_OFF; + } + if (strcmp(var, "on") == 0) { + return LED_ON; + } + if (strcmp(var, "toggle") == 0) + return LED_TOGGLE; + return -1; +} + +int do_led (cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + int i, match = 0; + enum led_cmd cmd; + + /* Validate arguments */ + if ((argc != 3)) { + return CMD_RET_USAGE; + } + + cmd = get_led_cmd(argv[2]); + if (cmd < 0) { + return CMD_RET_USAGE; + } + + for (i = 0; led_commands[i].string; i++) { + if ((strcmp("all", argv[1]) == 0) || + (strcmp(led_commands[i].string, argv[1]) == 0)) { + match = 1; + switch (cmd) { + case LED_ON: + if (led_commands[i].on) + led_commands[i].on(); + else + __led_set(led_commands[i].mask, + STATUS_LED_ON); + break; + case LED_OFF: + if (led_commands[i].off) + led_commands[i].off(); + else + __led_set(led_commands[i].mask, + STATUS_LED_OFF); + break; + case LED_TOGGLE: + if (led_commands[i].toggle) + led_commands[i].toggle(); + else + __led_toggle(led_commands[i].mask); + } + /* Need to set only 1 led if led_name wasn't 'all' */ + if (strcmp("all", argv[1]) != 0) + break; + } + } + + /* If we ran out of matches, print Usage */ + if (!match) { + return CMD_RET_USAGE; + } + + return 0; +} + +U_BOOT_CMD( + led, 3, 1, do_led, + "[" +#ifdef CONFIG_BOARD_SPECIFIC_LED +#ifdef STATUS_LED_BIT + "0|" +#endif +#ifdef STATUS_LED_BIT1 + "1|" +#endif +#ifdef STATUS_LED_BIT2 + "2|" +#endif +#ifdef STATUS_LED_BIT3 + "3|" +#endif +#endif +#ifdef STATUS_LED_GREEN + "green|" +#endif +#ifdef STATUS_LED_YELLOW + "yellow|" +#endif +#ifdef STATUS_LED_RED + "red|" +#endif +#ifdef STATUS_LED_BLUE + "blue|" +#endif + "all] [on|off|toggle]", + "[led_name] [on|off|toggle] sets or clears led(s)" +); diff --git a/qemu/roms/u-boot/common/cmd_license.c b/qemu/roms/u-boot/common/cmd_license.c new file mode 100644 index 000000000..b07de72d3 --- /dev/null +++ b/qemu/roms/u-boot/common/cmd_license.c @@ -0,0 +1,40 @@ +/* + * (C) Copyright 2007 by OpenMoko, Inc. + * Author: Harald Welte <laforge@openmoko.org> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> + +/* COPYING is currently 15951 bytes in size */ +#define LICENSE_MAX 20480 + +#include <command.h> +#include <malloc.h> +#include <license.h> + +int do_license(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + char *tok, *dst = malloc(LICENSE_MAX); + unsigned long len = LICENSE_MAX; + + if (!dst) + return -1; + + if (gunzip(dst, LICENSE_MAX, license_gz, &len) != 0) { + printf("Error uncompressing license text\n"); + free(dst); + return -1; + } + puts(dst); + free(dst); + + return 0; +} + +U_BOOT_CMD( + license, 1, 1, do_license, + "print GPL license text", + "" +); diff --git a/qemu/roms/u-boot/common/cmd_load.c b/qemu/roms/u-boot/common/cmd_load.c new file mode 100644 index 000000000..f6e522cbb --- /dev/null +++ b/qemu/roms/u-boot/common/cmd_load.c @@ -0,0 +1,1112 @@ +/* + * (C) Copyright 2000-2004 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/* + * Serial up- and download support + */ +#include <common.h> +#include <command.h> +#include <s_record.h> +#include <net.h> +#include <exports.h> +#include <xyzModem.h> + +DECLARE_GLOBAL_DATA_PTR; + +#if defined(CONFIG_CMD_LOADB) +static ulong load_serial_ymodem(ulong offset, int mode); +#endif + +#if defined(CONFIG_CMD_LOADS) +static ulong load_serial(long offset); +static int read_record(char *buf, ulong len); +# if defined(CONFIG_CMD_SAVES) +static int save_serial(ulong offset, ulong size); +static int write_record(char *buf); +#endif + +static int do_echo = 1; +#endif + +/* -------------------------------------------------------------------- */ + +#if defined(CONFIG_CMD_LOADS) +static int do_load_serial(cmd_tbl_t *cmdtp, int flag, int argc, + char * const argv[]) +{ + long offset = 0; + ulong addr; + int i; + char *env_echo; + int rcode = 0; +#ifdef CONFIG_SYS_LOADS_BAUD_CHANGE + int load_baudrate, current_baudrate; + + load_baudrate = current_baudrate = gd->baudrate; +#endif + + if (((env_echo = getenv("loads_echo")) != NULL) && (*env_echo == '1')) { + do_echo = 1; + } else { + do_echo = 0; + } + +#ifdef CONFIG_SYS_LOADS_BAUD_CHANGE + if (argc >= 2) { + offset = simple_strtol(argv[1], NULL, 16); + } + if (argc == 3) { + load_baudrate = (int)simple_strtoul(argv[2], NULL, 10); + + /* default to current baudrate */ + if (load_baudrate == 0) + load_baudrate = current_baudrate; + } + if (load_baudrate != current_baudrate) { + printf("## Switch baudrate to %d bps and press ENTER ...\n", + load_baudrate); + udelay(50000); + gd->baudrate = load_baudrate; + serial_setbrg(); + udelay(50000); + for (;;) { + if (getc() == '\r') + break; + } + } +#else /* ! CONFIG_SYS_LOADS_BAUD_CHANGE */ + if (argc == 2) { + offset = simple_strtol(argv[1], NULL, 16); + } +#endif /* CONFIG_SYS_LOADS_BAUD_CHANGE */ + + printf("## Ready for S-Record download ...\n"); + + addr = load_serial(offset); + + /* + * Gather any trailing characters (for instance, the ^D which + * is sent by 'cu' after sending a file), and give the + * box some time (100 * 1 ms) + */ + for (i=0; i<100; ++i) { + if (tstc()) { + (void) getc(); + } + udelay(1000); + } + + if (addr == ~0) { + printf("## S-Record download aborted\n"); + rcode = 1; + } else { + printf("## Start Addr = 0x%08lX\n", addr); + load_addr = addr; + } + +#ifdef CONFIG_SYS_LOADS_BAUD_CHANGE + if (load_baudrate != current_baudrate) { + printf("## Switch baudrate to %d bps and press ESC ...\n", + current_baudrate); + udelay(50000); + gd->baudrate = current_baudrate; + serial_setbrg(); + udelay(50000); + for (;;) { + if (getc() == 0x1B) /* ESC */ + break; + } + } +#endif + return rcode; +} + +static ulong load_serial(long offset) +{ + char record[SREC_MAXRECLEN + 1]; /* buffer for one S-Record */ + char binbuf[SREC_MAXBINLEN]; /* buffer for binary data */ + int binlen; /* no. of data bytes in S-Rec. */ + int type; /* return code for record type */ + ulong addr; /* load address from S-Record */ + ulong size; /* number of bytes transferred */ + ulong store_addr; + ulong start_addr = ~0; + ulong end_addr = 0; + int line_count = 0; + + while (read_record(record, SREC_MAXRECLEN + 1) >= 0) { + type = srec_decode(record, &binlen, &addr, binbuf); + + if (type < 0) { + return (~0); /* Invalid S-Record */ + } + + switch (type) { + case SREC_DATA2: + case SREC_DATA3: + case SREC_DATA4: + store_addr = addr + offset; +#ifndef CONFIG_SYS_NO_FLASH + if (addr2info(store_addr)) { + int rc; + + rc = flash_write((char *)binbuf,store_addr,binlen); + if (rc != 0) { + flash_perror(rc); + return (~0); + } + } else +#endif + { + memcpy((char *)(store_addr), binbuf, binlen); + } + if ((store_addr) < start_addr) + start_addr = store_addr; + if ((store_addr + binlen - 1) > end_addr) + end_addr = store_addr + binlen - 1; + break; + case SREC_END2: + case SREC_END3: + case SREC_END4: + udelay(10000); + size = end_addr - start_addr + 1; + printf("\n" + "## First Load Addr = 0x%08lX\n" + "## Last Load Addr = 0x%08lX\n" + "## Total Size = 0x%08lX = %ld Bytes\n", + start_addr, end_addr, size, size + ); + flush_cache(start_addr, size); + setenv_hex("filesize", size); + return (addr); + case SREC_START: + break; + default: + break; + } + if (!do_echo) { /* print a '.' every 100 lines */ + if ((++line_count % 100) == 0) + putc('.'); + } + } + + return (~0); /* Download aborted */ +} + +static int read_record(char *buf, ulong len) +{ + char *p; + char c; + + --len; /* always leave room for terminating '\0' byte */ + + for (p=buf; p < buf+len; ++p) { + c = getc(); /* read character */ + if (do_echo) + putc(c); /* ... and echo it */ + + switch (c) { + case '\r': + case '\n': + *p = '\0'; + return (p - buf); + case '\0': + case 0x03: /* ^C - Control C */ + return (-1); + default: + *p = c; + } + + /* Check for the console hangup (if any different from serial) */ + if (gd->jt[XF_getc] != getc) { + if (ctrlc()) { + return (-1); + } + } + } + + /* line too long - truncate */ + *p = '\0'; + return (p - buf); +} + +#if defined(CONFIG_CMD_SAVES) + +int do_save_serial (cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + ulong offset = 0; + ulong size = 0; +#ifdef CONFIG_SYS_LOADS_BAUD_CHANGE + int save_baudrate, current_baudrate; + + save_baudrate = current_baudrate = gd->baudrate; +#endif + + if (argc >= 2) { + offset = simple_strtoul(argv[1], NULL, 16); + } +#ifdef CONFIG_SYS_LOADS_BAUD_CHANGE + if (argc >= 3) { + size = simple_strtoul(argv[2], NULL, 16); + } + if (argc == 4) { + save_baudrate = (int)simple_strtoul(argv[3], NULL, 10); + + /* default to current baudrate */ + if (save_baudrate == 0) + save_baudrate = current_baudrate; + } + if (save_baudrate != current_baudrate) { + printf("## Switch baudrate to %d bps and press ENTER ...\n", + save_baudrate); + udelay(50000); + gd->baudrate = save_baudrate; + serial_setbrg(); + udelay(50000); + for (;;) { + if (getc() == '\r') + break; + } + } +#else /* ! CONFIG_SYS_LOADS_BAUD_CHANGE */ + if (argc == 3) { + size = simple_strtoul(argv[2], NULL, 16); + } +#endif /* CONFIG_SYS_LOADS_BAUD_CHANGE */ + + printf("## Ready for S-Record upload, press ENTER to proceed ...\n"); + for (;;) { + if (getc() == '\r') + break; + } + if (save_serial(offset, size)) { + printf("## S-Record upload aborted\n"); + } else { + printf("## S-Record upload complete\n"); + } +#ifdef CONFIG_SYS_LOADS_BAUD_CHANGE + if (save_baudrate != current_baudrate) { + printf("## Switch baudrate to %d bps and press ESC ...\n", + (int)current_baudrate); + udelay(50000); + gd->baudrate = current_baudrate; + serial_setbrg(); + udelay(50000); + for (;;) { + if (getc() == 0x1B) /* ESC */ + break; + } + } +#endif + return 0; +} + +#define SREC3_START "S0030000FC\n" +#define SREC3_FORMAT "S3%02X%08lX%s%02X\n" +#define SREC3_END "S70500000000FA\n" +#define SREC_BYTES_PER_RECORD 16 + +static int save_serial(ulong address, ulong count) +{ + int i, c, reclen, checksum, length; + char *hex = "0123456789ABCDEF"; + char record[2*SREC_BYTES_PER_RECORD+16]; /* buffer for one S-Record */ + char data[2*SREC_BYTES_PER_RECORD+1]; /* buffer for hex data */ + + reclen = 0; + checksum = 0; + + if(write_record(SREC3_START)) /* write the header */ + return (-1); + do { + if(count) { /* collect hex data in the buffer */ + c = *(volatile uchar*)(address + reclen); /* get one byte */ + checksum += c; /* accumulate checksum */ + data[2*reclen] = hex[(c>>4)&0x0f]; + data[2*reclen+1] = hex[c & 0x0f]; + data[2*reclen+2] = '\0'; + ++reclen; + --count; + } + if(reclen == SREC_BYTES_PER_RECORD || count == 0) { + /* enough data collected for one record: dump it */ + if(reclen) { /* build & write a data record: */ + /* address + data + checksum */ + length = 4 + reclen + 1; + + /* accumulate length bytes into checksum */ + for(i = 0; i < 2; i++) + checksum += (length >> (8*i)) & 0xff; + + /* accumulate address bytes into checksum: */ + for(i = 0; i < 4; i++) + checksum += (address >> (8*i)) & 0xff; + + /* make proper checksum byte: */ + checksum = ~checksum & 0xff; + + /* output one record: */ + sprintf(record, SREC3_FORMAT, length, address, data, checksum); + if(write_record(record)) + return (-1); + } + address += reclen; /* increment address */ + checksum = 0; + reclen = 0; + } + } + while(count); + if(write_record(SREC3_END)) /* write the final record */ + return (-1); + return(0); +} + +static int write_record(char *buf) +{ + char c; + + while((c = *buf++)) + putc(c); + + /* Check for the console hangup (if any different from serial) */ + + if (ctrlc()) { + return (-1); + } + return (0); +} +# endif + +#endif + + +#if defined(CONFIG_CMD_LOADB) +/* + * loadb command (load binary) included + */ +#define XON_CHAR 17 +#define XOFF_CHAR 19 +#define START_CHAR 0x01 +#define ETX_CHAR 0x03 +#define END_CHAR 0x0D +#define SPACE 0x20 +#define K_ESCAPE 0x23 +#define SEND_TYPE 'S' +#define DATA_TYPE 'D' +#define ACK_TYPE 'Y' +#define NACK_TYPE 'N' +#define BREAK_TYPE 'B' +#define tochar(x) ((char) (((x) + SPACE) & 0xff)) +#define untochar(x) ((int) (((x) - SPACE) & 0xff)) + +static void set_kerm_bin_mode(unsigned long *); +static int k_recv(void); +static ulong load_serial_bin(ulong offset); + + +static char his_eol; /* character he needs at end of packet */ +static int his_pad_count; /* number of pad chars he needs */ +static char his_pad_char; /* pad chars he needs */ +static char his_quote; /* quote chars he'll use */ + +static int do_load_serial_bin(cmd_tbl_t *cmdtp, int flag, int argc, + char * const argv[]) +{ + ulong offset = 0; + ulong addr; + int load_baudrate, current_baudrate; + int rcode = 0; + char *s; + + /* pre-set offset from CONFIG_SYS_LOAD_ADDR */ + offset = CONFIG_SYS_LOAD_ADDR; + + /* pre-set offset from $loadaddr */ + if ((s = getenv("loadaddr")) != NULL) { + offset = simple_strtoul(s, NULL, 16); + } + + load_baudrate = current_baudrate = gd->baudrate; + + if (argc >= 2) { + offset = simple_strtoul(argv[1], NULL, 16); + } + if (argc == 3) { + load_baudrate = (int)simple_strtoul(argv[2], NULL, 10); + + /* default to current baudrate */ + if (load_baudrate == 0) + load_baudrate = current_baudrate; + } + + if (load_baudrate != current_baudrate) { + printf("## Switch baudrate to %d bps and press ENTER ...\n", + load_baudrate); + udelay(50000); + gd->baudrate = load_baudrate; + serial_setbrg(); + udelay(50000); + for (;;) { + if (getc() == '\r') + break; + } + } + + if (strcmp(argv[0],"loady")==0) { + printf("## Ready for binary (ymodem) download " + "to 0x%08lX at %d bps...\n", + offset, + load_baudrate); + + addr = load_serial_ymodem(offset, xyzModem_ymodem); + + } else if (strcmp(argv[0],"loadx")==0) { + printf("## Ready for binary (xmodem) download " + "to 0x%08lX at %d bps...\n", + offset, + load_baudrate); + + addr = load_serial_ymodem(offset, xyzModem_xmodem); + + } else { + + printf("## Ready for binary (kermit) download " + "to 0x%08lX at %d bps...\n", + offset, + load_baudrate); + addr = load_serial_bin(offset); + + if (addr == ~0) { + load_addr = 0; + printf("## Binary (kermit) download aborted\n"); + rcode = 1; + } else { + printf("## Start Addr = 0x%08lX\n", addr); + load_addr = addr; + } + } + if (load_baudrate != current_baudrate) { + printf("## Switch baudrate to %d bps and press ESC ...\n", + current_baudrate); + udelay(50000); + gd->baudrate = current_baudrate; + serial_setbrg(); + udelay(50000); + for (;;) { + if (getc() == 0x1B) /* ESC */ + break; + } + } + + return rcode; +} + + +static ulong load_serial_bin(ulong offset) +{ + int size, i; + + set_kerm_bin_mode((ulong *) offset); + size = k_recv(); + + /* + * Gather any trailing characters (for instance, the ^D which + * is sent by 'cu' after sending a file), and give the + * box some time (100 * 1 ms) + */ + for (i=0; i<100; ++i) { + if (tstc()) { + (void) getc(); + } + udelay(1000); + } + + flush_cache(offset, size); + + printf("## Total Size = 0x%08x = %d Bytes\n", size, size); + setenv_hex("filesize", size); + + return offset; +} + +static void send_pad(void) +{ + int count = his_pad_count; + + while (count-- > 0) + putc(his_pad_char); +} + +/* converts escaped kermit char to binary char */ +static char ktrans(char in) +{ + if ((in & 0x60) == 0x40) { + return (char) (in & ~0x40); + } else if ((in & 0x7f) == 0x3f) { + return (char) (in | 0x40); + } else + return in; +} + +static int chk1(char *buffer) +{ + int total = 0; + + while (*buffer) { + total += *buffer++; + } + return (int) ((total + ((total >> 6) & 0x03)) & 0x3f); +} + +static void s1_sendpacket(char *packet) +{ + send_pad(); + while (*packet) { + putc(*packet++); + } +} + +static char a_b[24]; +static void send_ack(int n) +{ + a_b[0] = START_CHAR; + a_b[1] = tochar(3); + a_b[2] = tochar(n); + a_b[3] = ACK_TYPE; + a_b[4] = '\0'; + a_b[4] = tochar(chk1(&a_b[1])); + a_b[5] = his_eol; + a_b[6] = '\0'; + s1_sendpacket(a_b); +} + +static void send_nack(int n) +{ + a_b[0] = START_CHAR; + a_b[1] = tochar(3); + a_b[2] = tochar(n); + a_b[3] = NACK_TYPE; + a_b[4] = '\0'; + a_b[4] = tochar(chk1(&a_b[1])); + a_b[5] = his_eol; + a_b[6] = '\0'; + s1_sendpacket(a_b); +} + + +static void (*os_data_init)(void); +static void (*os_data_char)(char new_char); +static int os_data_state, os_data_state_saved; +static char *os_data_addr, *os_data_addr_saved; +static char *bin_start_address; + +static void bin_data_init(void) +{ + os_data_state = 0; + os_data_addr = bin_start_address; +} + +static void os_data_save(void) +{ + os_data_state_saved = os_data_state; + os_data_addr_saved = os_data_addr; +} + +static void os_data_restore(void) +{ + os_data_state = os_data_state_saved; + os_data_addr = os_data_addr_saved; +} + +static void bin_data_char(char new_char) +{ + switch (os_data_state) { + case 0: /* data */ + *os_data_addr++ = new_char; + break; + } +} + +static void set_kerm_bin_mode(unsigned long *addr) +{ + bin_start_address = (char *) addr; + os_data_init = bin_data_init; + os_data_char = bin_data_char; +} + + +/* k_data_* simply handles the kermit escape translations */ +static int k_data_escape, k_data_escape_saved; +static void k_data_init(void) +{ + k_data_escape = 0; + os_data_init(); +} + +static void k_data_save(void) +{ + k_data_escape_saved = k_data_escape; + os_data_save(); +} + +static void k_data_restore(void) +{ + k_data_escape = k_data_escape_saved; + os_data_restore(); +} + +static void k_data_char(char new_char) +{ + if (k_data_escape) { + /* last char was escape - translate this character */ + os_data_char(ktrans(new_char)); + k_data_escape = 0; + } else { + if (new_char == his_quote) { + /* this char is escape - remember */ + k_data_escape = 1; + } else { + /* otherwise send this char as-is */ + os_data_char(new_char); + } + } +} + +#define SEND_DATA_SIZE 20 +static char send_parms[SEND_DATA_SIZE]; +static char *send_ptr; + +/* handle_send_packet interprits the protocol info and builds and + sends an appropriate ack for what we can do */ +static void handle_send_packet(int n) +{ + int length = 3; + int bytes; + + /* initialize some protocol parameters */ + his_eol = END_CHAR; /* default end of line character */ + his_pad_count = 0; + his_pad_char = '\0'; + his_quote = K_ESCAPE; + + /* ignore last character if it filled the buffer */ + if (send_ptr == &send_parms[SEND_DATA_SIZE - 1]) + --send_ptr; + bytes = send_ptr - send_parms; /* how many bytes we'll process */ + do { + if (bytes-- <= 0) + break; + /* handle MAXL - max length */ + /* ignore what he says - most I'll take (here) is 94 */ + a_b[++length] = tochar(94); + if (bytes-- <= 0) + break; + /* handle TIME - time you should wait for my packets */ + /* ignore what he says - don't wait for my ack longer than 1 second */ + a_b[++length] = tochar(1); + if (bytes-- <= 0) + break; + /* handle NPAD - number of pad chars I need */ + /* remember what he says - I need none */ + his_pad_count = untochar(send_parms[2]); + a_b[++length] = tochar(0); + if (bytes-- <= 0) + break; + /* handle PADC - pad chars I need */ + /* remember what he says - I need none */ + his_pad_char = ktrans(send_parms[3]); + a_b[++length] = 0x40; /* He should ignore this */ + if (bytes-- <= 0) + break; + /* handle EOL - end of line he needs */ + /* remember what he says - I need CR */ + his_eol = untochar(send_parms[4]); + a_b[++length] = tochar(END_CHAR); + if (bytes-- <= 0) + break; + /* handle QCTL - quote control char he'll use */ + /* remember what he says - I'll use '#' */ + his_quote = send_parms[5]; + a_b[++length] = '#'; + if (bytes-- <= 0) + break; + /* handle QBIN - 8-th bit prefixing */ + /* ignore what he says - I refuse */ + a_b[++length] = 'N'; + if (bytes-- <= 0) + break; + /* handle CHKT - the clock check type */ + /* ignore what he says - I do type 1 (for now) */ + a_b[++length] = '1'; + if (bytes-- <= 0) + break; + /* handle REPT - the repeat prefix */ + /* ignore what he says - I refuse (for now) */ + a_b[++length] = 'N'; + if (bytes-- <= 0) + break; + /* handle CAPAS - the capabilities mask */ + /* ignore what he says - I only do long packets - I don't do windows */ + a_b[++length] = tochar(2); /* only long packets */ + a_b[++length] = tochar(0); /* no windows */ + a_b[++length] = tochar(94); /* large packet msb */ + a_b[++length] = tochar(94); /* large packet lsb */ + } while (0); + + a_b[0] = START_CHAR; + a_b[1] = tochar(length); + a_b[2] = tochar(n); + a_b[3] = ACK_TYPE; + a_b[++length] = '\0'; + a_b[length] = tochar(chk1(&a_b[1])); + a_b[++length] = his_eol; + a_b[++length] = '\0'; + s1_sendpacket(a_b); +} + +/* k_recv receives a OS Open image file over kermit line */ +static int k_recv(void) +{ + char new_char; + char k_state, k_state_saved; + int sum; + int done; + int length; + int n, last_n; + int len_lo, len_hi; + + /* initialize some protocol parameters */ + his_eol = END_CHAR; /* default end of line character */ + his_pad_count = 0; + his_pad_char = '\0'; + his_quote = K_ESCAPE; + + /* initialize the k_recv and k_data state machine */ + done = 0; + k_state = 0; + k_data_init(); + k_state_saved = k_state; + k_data_save(); + n = 0; /* just to get rid of a warning */ + last_n = -1; + + /* expect this "type" sequence (but don't check): + S: send initiate + F: file header + D: data (multiple) + Z: end of file + B: break transmission + */ + + /* enter main loop */ + while (!done) { + /* set the send packet pointer to begining of send packet parms */ + send_ptr = send_parms; + + /* With each packet, start summing the bytes starting with the length. + Save the current sequence number. + Note the type of the packet. + If a character less than SPACE (0x20) is received - error. + */ + +#if 0 + /* OLD CODE, Prior to checking sequence numbers */ + /* first have all state machines save current states */ + k_state_saved = k_state; + k_data_save (); +#endif + + /* get a packet */ + /* wait for the starting character or ^C */ + for (;;) { + switch (getc ()) { + case START_CHAR: /* start packet */ + goto START; + case ETX_CHAR: /* ^C waiting for packet */ + return (0); + default: + ; + } + } +START: + /* get length of packet */ + sum = 0; + new_char = getc(); + if ((new_char & 0xE0) == 0) + goto packet_error; + sum += new_char & 0xff; + length = untochar(new_char); + /* get sequence number */ + new_char = getc(); + if ((new_char & 0xE0) == 0) + goto packet_error; + sum += new_char & 0xff; + n = untochar(new_char); + --length; + + /* NEW CODE - check sequence numbers for retried packets */ + /* Note - this new code assumes that the sequence number is correctly + * received. Handling an invalid sequence number adds another layer + * of complexity that may not be needed - yet! At this time, I'm hoping + * that I don't need to buffer the incoming data packets and can write + * the data into memory in real time. + */ + if (n == last_n) { + /* same sequence number, restore the previous state */ + k_state = k_state_saved; + k_data_restore(); + } else { + /* new sequence number, checkpoint the download */ + last_n = n; + k_state_saved = k_state; + k_data_save(); + } + /* END NEW CODE */ + + /* get packet type */ + new_char = getc(); + if ((new_char & 0xE0) == 0) + goto packet_error; + sum += new_char & 0xff; + k_state = new_char; + --length; + /* check for extended length */ + if (length == -2) { + /* (length byte was 0, decremented twice) */ + /* get the two length bytes */ + new_char = getc(); + if ((new_char & 0xE0) == 0) + goto packet_error; + sum += new_char & 0xff; + len_hi = untochar(new_char); + new_char = getc(); + if ((new_char & 0xE0) == 0) + goto packet_error; + sum += new_char & 0xff; + len_lo = untochar(new_char); + length = len_hi * 95 + len_lo; + /* check header checksum */ + new_char = getc(); + if ((new_char & 0xE0) == 0) + goto packet_error; + if (new_char != tochar((sum + ((sum >> 6) & 0x03)) & 0x3f)) + goto packet_error; + sum += new_char & 0xff; +/* --length; */ /* new length includes only data and block check to come */ + } + /* bring in rest of packet */ + while (length > 1) { + new_char = getc(); + if ((new_char & 0xE0) == 0) + goto packet_error; + sum += new_char & 0xff; + --length; + if (k_state == DATA_TYPE) { + /* pass on the data if this is a data packet */ + k_data_char (new_char); + } else if (k_state == SEND_TYPE) { + /* save send pack in buffer as is */ + *send_ptr++ = new_char; + /* if too much data, back off the pointer */ + if (send_ptr >= &send_parms[SEND_DATA_SIZE]) + --send_ptr; + } + } + /* get and validate checksum character */ + new_char = getc(); + if ((new_char & 0xE0) == 0) + goto packet_error; + if (new_char != tochar((sum + ((sum >> 6) & 0x03)) & 0x3f)) + goto packet_error; + /* get END_CHAR */ + new_char = getc(); + if (new_char != END_CHAR) { + packet_error: + /* restore state machines */ + k_state = k_state_saved; + k_data_restore(); + /* send a negative acknowledge packet in */ + send_nack(n); + } else if (k_state == SEND_TYPE) { + /* crack the protocol parms, build an appropriate ack packet */ + handle_send_packet(n); + } else { + /* send simple acknowledge packet in */ + send_ack(n); + /* quit if end of transmission */ + if (k_state == BREAK_TYPE) + done = 1; + } + } + return ((ulong) os_data_addr - (ulong) bin_start_address); +} + +static int getcxmodem(void) { + if (tstc()) + return (getc()); + return -1; +} +static ulong load_serial_ymodem(ulong offset, int mode) +{ + int size; + int err; + int res; + connection_info_t info; + char ymodemBuf[1024]; + ulong store_addr = ~0; + ulong addr = 0; + + size = 0; + info.mode = mode; + res = xyzModem_stream_open(&info, &err); + if (!res) { + + while ((res = + xyzModem_stream_read(ymodemBuf, 1024, &err)) > 0) { + store_addr = addr + offset; + size += res; + addr += res; +#ifndef CONFIG_SYS_NO_FLASH + if (addr2info(store_addr)) { + int rc; + + rc = flash_write((char *) ymodemBuf, + store_addr, res); + if (rc != 0) { + flash_perror (rc); + return (~0); + } + } else +#endif + { + memcpy((char *)(store_addr), ymodemBuf, + res); + } + + } + } else { + printf("%s\n", xyzModem_error(err)); + } + + xyzModem_stream_close(&err); + xyzModem_stream_terminate(false, &getcxmodem); + + + flush_cache(offset, size); + + printf("## Total Size = 0x%08x = %d Bytes\n", size, size); + setenv_hex("filesize", size); + + return offset; +} + +#endif + +/* -------------------------------------------------------------------- */ + +#if defined(CONFIG_CMD_LOADS) + +#ifdef CONFIG_SYS_LOADS_BAUD_CHANGE +U_BOOT_CMD( + loads, 3, 0, do_load_serial, + "load S-Record file over serial line", + "[ off ] [ baud ]\n" + " - load S-Record file over serial line" + " with offset 'off' and baudrate 'baud'" +); + +#else /* ! CONFIG_SYS_LOADS_BAUD_CHANGE */ +U_BOOT_CMD( + loads, 2, 0, do_load_serial, + "load S-Record file over serial line", + "[ off ]\n" + " - load S-Record file over serial line with offset 'off'" +); +#endif /* CONFIG_SYS_LOADS_BAUD_CHANGE */ + +/* + * SAVES always requires LOADS support, but not vice versa + */ + + +#if defined(CONFIG_CMD_SAVES) +#ifdef CONFIG_SYS_LOADS_BAUD_CHANGE +U_BOOT_CMD( + saves, 4, 0, do_save_serial, + "save S-Record file over serial line", + "[ off ] [size] [ baud ]\n" + " - save S-Record file over serial line" + " with offset 'off', size 'size' and baudrate 'baud'" +); +#else /* ! CONFIG_SYS_LOADS_BAUD_CHANGE */ +U_BOOT_CMD( + saves, 3, 0, do_save_serial, + "save S-Record file over serial line", + "[ off ] [size]\n" + " - save S-Record file over serial line with offset 'off' and size 'size'" +); +#endif /* CONFIG_SYS_LOADS_BAUD_CHANGE */ +#endif /* CONFIG_CMD_SAVES */ +#endif /* CONFIG_CMD_LOADS */ + + +#if defined(CONFIG_CMD_LOADB) +U_BOOT_CMD( + loadb, 3, 0, do_load_serial_bin, + "load binary file over serial line (kermit mode)", + "[ off ] [ baud ]\n" + " - load binary file over serial line" + " with offset 'off' and baudrate 'baud'" +); + +U_BOOT_CMD( + loadx, 3, 0, do_load_serial_bin, + "load binary file over serial line (xmodem mode)", + "[ off ] [ baud ]\n" + " - load binary file over serial line" + " with offset 'off' and baudrate 'baud'" +); + +U_BOOT_CMD( + loady, 3, 0, do_load_serial_bin, + "load binary file over serial line (ymodem mode)", + "[ off ] [ baud ]\n" + " - load binary file over serial line" + " with offset 'off' and baudrate 'baud'" +); + +#endif /* CONFIG_CMD_LOADB */ + +/* -------------------------------------------------------------------- */ + +#if defined(CONFIG_CMD_HWFLOW) +int do_hwflow(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + extern int hwflow_onoff(int); + + if (argc == 2) { + if (strcmp(argv[1], "off") == 0) + hwflow_onoff(-1); + else + if (strcmp(argv[1], "on") == 0) + hwflow_onoff(1); + else + return CMD_RET_USAGE; + } + printf("RTS/CTS hardware flow control: %s\n", hwflow_onoff(0) ? "on" : "off"); + return 0; +} + +/* -------------------------------------------------------------------- */ + +U_BOOT_CMD( + hwflow, 2, 0, do_hwflow, + "turn RTS/CTS hardware flow control in serial line on/off", + "[on|off]" +); + +#endif /* CONFIG_CMD_HWFLOW */ diff --git a/qemu/roms/u-boot/common/cmd_log.c b/qemu/roms/u-boot/common/cmd_log.c new file mode 100644 index 000000000..38d0f5edf --- /dev/null +++ b/qemu/roms/u-boot/common/cmd_log.c @@ -0,0 +1,310 @@ +/* + * (C) Copyright 2002-2007 + * Detlev Zundel, DENX Software Engineering, dzu@denx.de. + * + * Code used from linux/kernel/printk.c + * Copyright (C) 1991, 1992 Linus Torvalds + * + * SPDX-License-Identifier: GPL-2.0+ + * + * Comments: + * + * After relocating the code, the environment variable "loglevel" is + * copied to console_loglevel. The functionality is similar to the + * handling in the Linux kernel, i.e. messages logged with a priority + * less than console_loglevel are also output to stdout. + * + * If you want messages with the default level (e.g. POST messages) to + * appear on stdout also, make sure the environment variable + * "loglevel" is set at boot time to a number higher than + * default_message_loglevel below. + */ + +/* + * Logbuffer handling routines + */ + +#include <common.h> +#include <command.h> +#include <stdio_dev.h> +#include <post.h> +#include <logbuff.h> + +DECLARE_GLOBAL_DATA_PTR; + +/* Local prototypes */ +static void logbuff_putc(const char c); +static void logbuff_puts(const char *s); +static int logbuff_printk(const char *line); + +static char buf[1024]; + +/* This combination will not print messages with the default loglevel */ +static unsigned console_loglevel = 3; +static unsigned default_message_loglevel = 4; +static unsigned log_version = 1; +#ifdef CONFIG_ALT_LB_ADDR +static volatile logbuff_t *log; +#else +static logbuff_t *log; +#endif +static char *lbuf; + +unsigned long __logbuffer_base(void) +{ + return CONFIG_SYS_SDRAM_BASE + get_effective_memsize() - LOGBUFF_LEN; +} +unsigned long logbuffer_base(void) +__attribute__((weak, alias("__logbuffer_base"))); + +void logbuff_init_ptrs(void) +{ + unsigned long tag, post_word; + char *s; + +#ifdef CONFIG_ALT_LB_ADDR + log = (logbuff_t *)CONFIG_ALT_LH_ADDR; + lbuf = (char *)CONFIG_ALT_LB_ADDR; +#else + log = (logbuff_t *)(logbuffer_base()) - 1; + lbuf = (char *)log->buf; +#endif + + /* Set up log version */ + if ((s = getenv ("logversion")) != NULL) + log_version = (int)simple_strtoul(s, NULL, 10); + + if (log_version == 2) + tag = log->v2.tag; + else + tag = log->v1.tag; + post_word = post_word_load(); +#ifdef CONFIG_POST + /* The post routines have setup the word so we can simply test it */ + if (tag != LOGBUFF_MAGIC || (post_word & POST_COLDBOOT)) + logbuff_reset(); +#else + /* No post routines, so we do our own checking */ + if (tag != LOGBUFF_MAGIC || post_word != LOGBUFF_MAGIC) { + logbuff_reset (); + post_word_store (LOGBUFF_MAGIC); + } +#endif + if (log_version == 2 && (long)log->v2.start > (long)log->v2.con) + log->v2.start = log->v2.con; + + /* Initialize default loglevel if present */ + if ((s = getenv ("loglevel")) != NULL) + console_loglevel = (int)simple_strtoul(s, NULL, 10); + + gd->flags |= GD_FLG_LOGINIT; +} + +void logbuff_reset(void) +{ +#ifndef CONFIG_ALT_LB_ADDR + memset(log, 0, sizeof(logbuff_t)); +#endif + if (log_version == 2) { + log->v2.tag = LOGBUFF_MAGIC; +#ifdef CONFIG_ALT_LB_ADDR + log->v2.start = 0; + log->v2.con = 0; + log->v2.end = 0; + log->v2.chars = 0; +#endif + } else { + log->v1.tag = LOGBUFF_MAGIC; +#ifdef CONFIG_ALT_LB_ADDR + log->v1.dummy = 0; + log->v1.start = 0; + log->v1.size = 0; + log->v1.chars = 0; +#endif + } +} + +int drv_logbuff_init(void) +{ + struct stdio_dev logdev; + int rc; + + /* Device initialization */ + memset (&logdev, 0, sizeof (logdev)); + + strcpy (logdev.name, "logbuff"); + logdev.ext = 0; /* No extensions */ + logdev.flags = DEV_FLAGS_OUTPUT; /* Output only */ + logdev.putc = logbuff_putc; /* 'putc' function */ + logdev.puts = logbuff_puts; /* 'puts' function */ + + rc = stdio_register(&logdev); + + return (rc == 0) ? 1 : rc; +} + +static void logbuff_putc(const char c) +{ + char buf[2]; + buf[0] = c; + buf[1] = '\0'; + logbuff_printk(buf); +} + +static void logbuff_puts(const char *s) +{ + logbuff_printk (s); +} + +void logbuff_log(char *msg) +{ + if ((gd->flags & GD_FLG_LOGINIT)) { + logbuff_printk(msg); + } else { + /* + * Can happen only for pre-relocated errors as logging + * at that stage should be disabled + */ + puts (msg); + } +} + +/* + * Subroutine: do_log + * + * Description: Handler for 'log' command.. + * + * Inputs: argv[1] contains the subcommand + * + * Return: None + * + */ +int do_log(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + char *s; + unsigned long i, start, size; + + if (strcmp(argv[1], "append") == 0) { + /* Log concatenation of all arguments separated by spaces */ + for (i = 2; i < argc; i++) { + logbuff_printk(argv[i]); + logbuff_putc((i < argc - 1) ? ' ' : '\n'); + } + return 0; + } + + switch (argc) { + + case 2: + if (strcmp(argv[1], "show") == 0) { + if (log_version == 2) { + start = log->v2.start; + size = log->v2.end - log->v2.start; + } else { + start = log->v1.start; + size = log->v1.size; + } + if (size > LOGBUFF_LEN) + size = LOGBUFF_LEN; + for (i = 0; i < size; i++) { + s = lbuf + ((start + i) & LOGBUFF_MASK); + putc(*s); + } + return 0; + } else if (strcmp(argv[1], "reset") == 0) { + logbuff_reset(); + return 0; + } else if (strcmp(argv[1], "info") == 0) { + printf("Logbuffer at %08lx\n", (unsigned long)lbuf); + if (log_version == 2) { + printf("log_start = %08lx\n", + log->v2.start); + printf("log_end = %08lx\n", log->v2.end); + printf("log_con = %08lx\n", log->v2.con); + printf("logged_chars = %08lx\n", + log->v2.chars); + } + else { + printf("log_start = %08lx\n", + log->v1.start); + printf("log_size = %08lx\n", + log->v1.size); + printf("logged_chars = %08lx\n", + log->v1.chars); + } + return 0; + } + return CMD_RET_USAGE; + + default: + return CMD_RET_USAGE; + } +} + +U_BOOT_CMD( + log, 255, 1, do_log, + "manipulate logbuffer", + "info - show pointer details\n" + "log reset - clear contents\n" + "log show - show contents\n" + "log append <msg> - append <msg> to the logbuffer" +); + +static int logbuff_printk(const char *line) +{ + int i; + char *msg, *p, *buf_end; + int line_feed; + static signed char msg_level = -1; + + strcpy(buf + 3, line); + i = strlen(line); + buf_end = buf + 3 + i; + for (p = buf + 3; p < buf_end; p++) { + msg = p; + if (msg_level < 0) { + if ( + p[0] != '<' || + p[1] < '0' || + p[1] > '7' || + p[2] != '>' + ) { + p -= 3; + p[0] = '<'; + p[1] = default_message_loglevel + '0'; + p[2] = '>'; + } else { + msg += 3; + } + msg_level = p[1] - '0'; + } + line_feed = 0; + for (; p < buf_end; p++) { + if (log_version == 2) { + lbuf[log->v2.end & LOGBUFF_MASK] = *p; + log->v2.end++; + if (log->v2.end - log->v2.start > LOGBUFF_LEN) + log->v2.start++; + log->v2.chars++; + } else { + lbuf[(log->v1.start + log->v1.size) & + LOGBUFF_MASK] = *p; + if (log->v1.size < LOGBUFF_LEN) + log->v1.size++; + else + log->v1.start++; + log->v1.chars++; + } + if (*p == '\n') { + line_feed = 1; + break; + } + } + if (msg_level < console_loglevel) { + printf("%s", msg); + } + if (line_feed) + msg_level = -1; + } + return i; +} diff --git a/qemu/roms/u-boot/common/cmd_lzmadec.c b/qemu/roms/u-boot/common/cmd_lzmadec.c new file mode 100644 index 000000000..7b0b3fdd9 --- /dev/null +++ b/qemu/roms/u-boot/common/cmd_lzmadec.c @@ -0,0 +1,52 @@ +/* + * (C) Copyright 2013 Patrice Bouchand <pbfwdlist_gmail_com> + * lzma uncompress command in Uboot + * + * made from existing cmd_unzip.c file of Uboot + * + * (C) Copyright 2000 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <command.h> +#include <asm/io.h> + +#include <lzma/LzmaTools.h> + +static int do_lzmadec(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[]) +{ + unsigned long src, dst; + unsigned long src_len = ~0UL, dst_len = ~0UL; + int ret; + + switch (argc) { + case 4: + dst_len = simple_strtoul(argv[3], NULL, 16); + /* fall through */ + case 3: + src = simple_strtoul(argv[1], NULL, 16); + dst = simple_strtoul(argv[2], NULL, 16); + break; + default: + return CMD_RET_USAGE; + } + + ret = lzmaBuffToBuffDecompress(map_sysmem(dst, dst_len), &src_len, + map_sysmem(src, 0), dst_len); + + if (ret != SZ_OK) + return 1; + printf("Uncompressed size: %ld = 0x%lX\n", src_len, src_len); + setenv_hex("filesize", src_len); + + return 0; +} + +U_BOOT_CMD( + lzmadec, 4, 1, do_lzmadec, + "lzma uncompress a memory region", + "srcaddr dstaddr [dstsize]" +); diff --git a/qemu/roms/u-boot/common/cmd_mac.c b/qemu/roms/u-boot/common/cmd_mac.c new file mode 100644 index 000000000..52d3ba0f2 --- /dev/null +++ b/qemu/roms/u-boot/common/cmd_mac.c @@ -0,0 +1,33 @@ +/* + * Copyright 2006 Freescale Semiconductor + * York Sun (yorksun@freescale.com) + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <command.h> + +extern int do_mac(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]); + +U_BOOT_CMD( + mac, 3, 1, do_mac, + "display and program the system ID and MAC addresses in EEPROM", + "[read|save|id|num|errata|date|ports|0|1|2|3|4|5|6|7]\n" + "mac read\n" + " - read EEPROM content into memory\n" + "mac save\n" + " - save to the EEPROM\n" + "mac id\n" + " - program system id\n" + "mac num\n" + " - program system serial number\n" + "mac errata\n" + " - program errata data\n" + "mac date\n" + " - program date\n" + "mac ports\n" + " - program the number of ports\n" + "mac X\n" + " - program the MAC address for port X [X=0...7]" +); diff --git a/qemu/roms/u-boot/common/cmd_md5sum.c b/qemu/roms/u-boot/common/cmd_md5sum.c new file mode 100644 index 000000000..ae0f62e26 --- /dev/null +++ b/qemu/roms/u-boot/common/cmd_md5sum.c @@ -0,0 +1,175 @@ +/* + * (C) Copyright 2011 + * Joe Hershberger, National Instruments, joe.hershberger@ni.com + * + * (C) Copyright 2000 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <command.h> +#include <u-boot/md5.h> + +/* + * 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 < 16; i++) + *ptr++ = sum[i]; + } else { + char str_output[33]; + char *str_ptr = str_output; + + for (i = 0; i < 16; i++) { + sprintf(str_ptr, "%02x", sum[i]); + str_ptr += 2; + } + str_ptr = '\0'; + setenv(dest, str_output); + } +} + +#ifdef CONFIG_MD5SUM_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, 16); + } else { + unsigned int i; + char *vsum_str; + + if (strlen(verify_str) == 32) + vsum_str = verify_str; + else { + vsum_str = getenv(verify_str); + if (vsum_str == NULL || strlen(vsum_str) != 32) + return 1; + } + + for (i = 0; i < 16; 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_md5sum(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + ulong addr, len; + unsigned int i; + u8 output[16]; + u8 vsum[16]; + int verify = 0; + int ac; + char * const *av; + + if (argc < 3) + return CMD_RET_USAGE; + + av = argv + 1; + ac = argc - 1; + if (strcmp(*av, "-v") == 0) { + verify = 1; + av++; + ac--; + if (ac < 3) + return CMD_RET_USAGE; + } + + addr = simple_strtoul(*av++, NULL, 16); + len = simple_strtoul(*av++, NULL, 16); + + md5_wd((unsigned char *) addr, len, output, CHUNKSZ_MD5); + + if (!verify) { + printf("md5 for %08lx ... %08lx ==> ", addr, addr + len - 1); + for (i = 0; i < 16; 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 md5 sum\n", + verify_str); + return 1; + } + if (memcmp(output, vsum, 16) != 0) { + printf("md5 for %08lx ... %08lx ==> ", addr, + addr + len - 1); + for (i = 0; i < 16; i++) + printf("%02x", output[i]); + printf(" != "); + for (i = 0; i < 16; i++) + printf("%02x", vsum[i]); + printf(" ** ERROR **\n"); + return 1; + } + } + + return 0; +} +#else +static int do_md5sum(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + unsigned long addr, len; + unsigned int i; + u8 output[16]; + + if (argc < 3) + return CMD_RET_USAGE; + + addr = simple_strtoul(argv[1], NULL, 16); + len = simple_strtoul(argv[2], NULL, 16); + + md5_wd((unsigned char *) addr, len, output, CHUNKSZ_MD5); + printf("md5 for %08lx ... %08lx ==> ", addr, addr + len - 1); + for (i = 0; i < 16; i++) + printf("%02x", output[i]); + printf("\n"); + + if (argc > 3) + store_result(output, argv[3]); + + return 0; +} +#endif + +#ifdef CONFIG_MD5SUM_VERIFY +U_BOOT_CMD( + md5sum, 5, 1, do_md5sum, + "compute MD5 message digest", + "address count [[*]sum]\n" + " - compute MD5 message digest [save to sum]\n" + "md5sum -v address count [*]sum\n" + " - verify md5sum of memory area" +); +#else +U_BOOT_CMD( + md5sum, 4, 1, do_md5sum, + "compute MD5 message digest", + "address count [[*]sum]\n" + " - compute MD5 message digest [save to sum]" +); +#endif diff --git a/qemu/roms/u-boot/common/cmd_mdio.c b/qemu/roms/u-boot/common/cmd_mdio.c new file mode 100644 index 000000000..fb13d0507 --- /dev/null +++ b/qemu/roms/u-boot/common/cmd_mdio.c @@ -0,0 +1,313 @@ +/* + * (C) Copyright 2011 Freescale Semiconductor, Inc + * Andy Fleming + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/* + * MDIO Commands + */ + +#include <common.h> +#include <command.h> +#include <miiphy.h> +#include <phy.h> + + +static char last_op[2]; +static uint last_data; +static uint last_addr_lo; +static uint last_addr_hi; +static uint last_devad_lo; +static uint last_devad_hi; +static uint last_reg_lo; +static uint last_reg_hi; + +static int extract_range(char *input, int *plo, int *phi) +{ + char *end; + *plo = simple_strtol(input, &end, 0); + if (end == input) + return -1; + + if ((*end == '-') && *(++end)) + *phi = simple_strtol(end, NULL, 0); + else if (*end == '\0') + *phi = *plo; + else + return -1; + + return 0; +} + +static int mdio_write_ranges(struct phy_device *phydev, struct mii_dev *bus, + int addrlo, + int addrhi, int devadlo, int devadhi, + int reglo, int reghi, unsigned short data, + int extended) +{ + int addr, devad, reg; + int err = 0; + + for (addr = addrlo; addr <= addrhi; addr++) { + for (devad = devadlo; devad <= devadhi; devad++) { + for (reg = reglo; reg <= reghi; reg++) { + if (!extended) + err = bus->write(bus, addr, devad, + reg, data); + else + err = phydev->drv->writeext(phydev, + addr, devad, reg, data); + + if (err) + goto err_out; + } + } + } + +err_out: + return err; +} + +static int mdio_read_ranges(struct phy_device *phydev, struct mii_dev *bus, + int addrlo, + int addrhi, int devadlo, int devadhi, + int reglo, int reghi, int extended) +{ + int addr, devad, reg; + + printf("Reading from bus %s\n", bus->name); + for (addr = addrlo; addr <= addrhi; addr++) { + printf("PHY at address %d:\n", addr); + + for (devad = devadlo; devad <= devadhi; devad++) { + for (reg = reglo; reg <= reghi; reg++) { + int val; + + if (!extended) + val = bus->read(bus, addr, devad, reg); + else + val = phydev->drv->readext(phydev, addr, + devad, reg); + + if (val < 0) { + printf("Error\n"); + + return val; + } + + if (devad >= 0) + printf("%d.", devad); + + printf("%d - 0x%x\n", reg, val & 0xffff); + } + } + } + + return 0; +} + +/* The register will be in the form [a[-b].]x[-y] */ +static int extract_reg_range(char *input, int *devadlo, int *devadhi, + int *reglo, int *reghi) +{ + char *regstr; + + /* use strrchr to find the last string after a '.' */ + regstr = strrchr(input, '.'); + + /* If it exists, extract the devad(s) */ + if (regstr) { + char devadstr[32]; + + strncpy(devadstr, input, regstr - input); + devadstr[regstr - input] = '\0'; + + if (extract_range(devadstr, devadlo, devadhi)) + return -1; + + regstr++; + } else { + /* Otherwise, we have no devad, and we just got regs */ + *devadlo = *devadhi = MDIO_DEVAD_NONE; + + regstr = input; + } + + return extract_range(regstr, reglo, reghi); +} + +static int extract_phy_range(char *const argv[], int argc, struct mii_dev **bus, + struct phy_device **phydev, + int *addrlo, int *addrhi) +{ + struct phy_device *dev = *phydev; + + if ((argc < 1) || (argc > 2)) + return -1; + + /* If there are two arguments, it's busname addr */ + if (argc == 2) { + *bus = miiphy_get_dev_by_name(argv[0]); + + if (!*bus) + return -1; + + return extract_range(argv[1], addrlo, addrhi); + } + + /* It must be one argument, here */ + + /* + * This argument can be one of two things: + * 1) Ethernet device name + * 2) Just an address (use the previously-used bus) + * + * We check all buses for a PHY which is connected to an ethernet + * device by the given name. If none are found, we call + * extract_range() on the string, and see if it's an address range. + */ + dev = mdio_phydev_for_ethname(argv[0]); + + if (dev) { + *addrlo = *addrhi = dev->addr; + *bus = dev->bus; + + return 0; + } + + /* It's an address or nothing useful */ + return extract_range(argv[0], addrlo, addrhi); +} + +/* ---------------------------------------------------------------- */ +static int do_mdio(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + char op[2]; + int addrlo, addrhi, reglo, reghi, devadlo, devadhi; + unsigned short data; + int pos = argc - 1; + struct mii_dev *bus; + struct phy_device *phydev = NULL; + int extended = 0; + + if (argc < 2) + return CMD_RET_USAGE; + + /* + * We use the last specified parameters, unless new ones are + * entered. + */ + op[0] = argv[1][0]; + addrlo = last_addr_lo; + addrhi = last_addr_hi; + devadlo = last_devad_lo; + devadhi = last_devad_hi; + reglo = last_reg_lo; + reghi = last_reg_hi; + data = last_data; + + bus = mdio_get_current_dev(); + + if (flag & CMD_FLAG_REPEAT) + op[0] = last_op[0]; + + if (strlen(argv[1]) > 1) { + op[1] = argv[1][1]; + if (op[1] == 'x') { + phydev = mdio_phydev_for_ethname(argv[2]); + + if (phydev) { + addrlo = phydev->addr; + addrhi = addrlo; + bus = phydev->bus; + extended = 1; + } else { + return -1; + } + + if (!phydev->drv || + (!phydev->drv->writeext && (op[0] == 'w')) || + (!phydev->drv->readext && (op[0] == 'r'))) { + puts("PHY does not have extended functions\n"); + return -1; + } + } + } + + switch (op[0]) { + case 'w': + if (pos > 1) + data = simple_strtoul(argv[pos--], NULL, 16); + case 'r': + if (pos > 1) + if (extract_reg_range(argv[pos--], &devadlo, &devadhi, + ®lo, ®hi)) + return -1; + + default: + if (pos > 1) + if (extract_phy_range(&(argv[2]), pos - 1, &bus, + &phydev, &addrlo, &addrhi)) + return -1; + + break; + } + + if (op[0] == 'l') { + mdio_list_devices(); + + return 0; + } + + /* Save the chosen bus */ + miiphy_set_current_dev(bus->name); + + switch (op[0]) { + case 'w': + mdio_write_ranges(phydev, bus, addrlo, addrhi, devadlo, devadhi, + reglo, reghi, data, extended); + break; + + case 'r': + mdio_read_ranges(phydev, bus, addrlo, addrhi, devadlo, devadhi, + reglo, reghi, extended); + break; + } + + /* + * Save the parameters for repeats. + */ + last_op[0] = op[0]; + last_addr_lo = addrlo; + last_addr_hi = addrhi; + last_devad_lo = devadlo; + last_devad_hi = devadhi; + last_reg_lo = reglo; + last_reg_hi = reghi; + last_data = data; + + return 0; +} + +/***************************************************/ + +U_BOOT_CMD( + mdio, 6, 1, do_mdio, + "MDIO utility commands", + "list - List MDIO buses\n" + "mdio read <phydev> [<devad>.]<reg> - " + "read PHY's register at <devad>.<reg>\n" + "mdio write <phydev> [<devad>.]<reg> <data> - " + "write PHY's register at <devad>.<reg>\n" + "mdio rx <phydev> [<devad>.]<reg> - " + "read PHY's extended register at <devad>.<reg>\n" + "mdio wx <phydev> [<devad>.]<reg> <data> - " + "write PHY's extended register at <devad>.<reg>\n" + "<phydev> may be:\n" + " <busname> <addr>\n" + " <addr>\n" + " <eth name>\n" + "<addr> <devad>, and <reg> may be ranges, e.g. 1-5.4-0x1f.\n" +); diff --git a/qemu/roms/u-boot/common/cmd_mem.c b/qemu/roms/u-boot/common/cmd_mem.c new file mode 100644 index 000000000..5b03c2d5b --- /dev/null +++ b/qemu/roms/u-boot/common/cmd_mem.c @@ -0,0 +1,1399 @@ +/* + * (C) Copyright 2000 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/* + * Memory Functions + * + * Copied from FADS ROM, Dan Malek (dmalek@jlc.net) + */ + +#include <common.h> +#include <command.h> +#ifdef CONFIG_HAS_DATAFLASH +#include <dataflash.h> +#endif +#include <hash.h> +#include <watchdog.h> +#include <asm/io.h> +#include <linux/compiler.h> + +DECLARE_GLOBAL_DATA_PTR; + +#ifndef CONFIG_SYS_MEMTEST_SCRATCH +#define CONFIG_SYS_MEMTEST_SCRATCH 0 +#endif + +static int mod_mem(cmd_tbl_t *, int, int, int, char * const []); + +/* Display values from last command. + * Memory modify remembered values are different from display memory. + */ +static uint dp_last_addr, dp_last_size; +static uint dp_last_length = 0x40; +static uint mm_last_addr, mm_last_size; + +static ulong base_address = 0; + +/* Memory Display + * + * Syntax: + * md{.b, .w, .l, .q} {addr} {len} + */ +#define DISP_LINE_LEN 16 +static int do_mem_md(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + ulong addr, length; +#if defined(CONFIG_HAS_DATAFLASH) + ulong nbytes, linebytes; +#endif + int size; + int rc = 0; + + /* We use the last specified parameters, unless new ones are + * entered. + */ + addr = dp_last_addr; + size = dp_last_size; + length = dp_last_length; + + if (argc < 2) + return CMD_RET_USAGE; + + if ((flag & CMD_FLAG_REPEAT) == 0) { + /* New command specified. Check for a size specification. + * Defaults to long if no or incorrect specification. + */ + if ((size = cmd_get_data_size(argv[0], 4)) < 0) + return 1; + + /* Address is specified since argc > 1 + */ + addr = simple_strtoul(argv[1], NULL, 16); + addr += base_address; + + /* If another parameter, it is the length to display. + * Length is the number of objects, not number of bytes. + */ + if (argc > 2) + length = simple_strtoul(argv[2], NULL, 16); + } + +#if defined(CONFIG_HAS_DATAFLASH) + /* Print the lines. + * + * We buffer all read data, so we can make sure data is read only + * once, and all accesses are with the specified bus width. + */ + nbytes = length * size; + do { + char linebuf[DISP_LINE_LEN]; + void* p; + linebytes = (nbytes>DISP_LINE_LEN)?DISP_LINE_LEN:nbytes; + + rc = read_dataflash(addr, (linebytes/size)*size, linebuf); + p = (rc == DATAFLASH_OK) ? linebuf : (void*)addr; + print_buffer(addr, p, size, linebytes/size, DISP_LINE_LEN/size); + + nbytes -= linebytes; + addr += linebytes; + if (ctrlc()) { + rc = 1; + break; + } + } while (nbytes > 0); +#else + +# if defined(CONFIG_BLACKFIN) + /* See if we're trying to display L1 inst */ + if (addr_bfin_on_chip_mem(addr)) { + char linebuf[DISP_LINE_LEN]; + ulong linebytes, nbytes = length * size; + do { + linebytes = (nbytes > DISP_LINE_LEN) ? DISP_LINE_LEN : nbytes; + memcpy(linebuf, (void *)addr, linebytes); + print_buffer(addr, linebuf, size, linebytes/size, DISP_LINE_LEN/size); + + nbytes -= linebytes; + addr += linebytes; + if (ctrlc()) { + rc = 1; + break; + } + } while (nbytes > 0); + } else +# endif + + { + ulong bytes = size * length; + const void *buf = map_sysmem(addr, bytes); + + /* Print the lines. */ + print_buffer(addr, buf, size, length, DISP_LINE_LEN / size); + addr += bytes; + unmap_sysmem(buf); + } +#endif + + dp_last_addr = addr; + dp_last_length = length; + dp_last_size = size; + return (rc); +} + +static int do_mem_mm(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + return mod_mem (cmdtp, 1, flag, argc, argv); +} +static int do_mem_nm(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + return mod_mem (cmdtp, 0, flag, argc, argv); +} + +static int do_mem_mw(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ +#ifdef CONFIG_SYS_SUPPORT_64BIT_DATA + u64 writeval; +#else + ulong writeval; +#endif + ulong addr, count; + int size; + void *buf; + ulong bytes; + + if ((argc < 3) || (argc > 4)) + return CMD_RET_USAGE; + + /* Check for size specification. + */ + if ((size = cmd_get_data_size(argv[0], 4)) < 1) + return 1; + + /* Address is specified since argc > 1 + */ + addr = simple_strtoul(argv[1], NULL, 16); + addr += base_address; + + /* Get the value to write. + */ +#ifdef CONFIG_SYS_SUPPORT_64BIT_DATA + writeval = simple_strtoull(argv[2], NULL, 16); +#else + writeval = simple_strtoul(argv[2], NULL, 16); +#endif + + /* Count ? */ + if (argc == 4) { + count = simple_strtoul(argv[3], NULL, 16); + } else { + count = 1; + } + + bytes = size * count; + buf = map_sysmem(addr, bytes); + while (count-- > 0) { + if (size == 4) + *((u32 *)buf) = (u32)writeval; +#ifdef CONFIG_SYS_SUPPORT_64BIT_DATA + else if (size == 8) + *((u64 *)buf) = (u64)writeval; +#endif + else if (size == 2) + *((u16 *)buf) = (u16)writeval; + else + *((u8 *)buf) = (u8)writeval; + buf += size; + } + unmap_sysmem(buf); + return 0; +} + +#ifdef CONFIG_MX_CYCLIC +int do_mem_mdc ( cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + int i; + ulong count; + + if (argc < 4) + return CMD_RET_USAGE; + + count = simple_strtoul(argv[3], NULL, 10); + + for (;;) { + do_mem_md (NULL, 0, 3, argv); + + /* delay for <count> ms... */ + for (i=0; i<count; i++) + udelay (1000); + + /* check for ctrl-c to abort... */ + if (ctrlc()) { + puts("Abort\n"); + return 0; + } + } + + return 0; +} + +int do_mem_mwc ( cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + int i; + ulong count; + + if (argc < 4) + return CMD_RET_USAGE; + + count = simple_strtoul(argv[3], NULL, 10); + + for (;;) { + do_mem_mw (NULL, 0, 3, argv); + + /* delay for <count> ms... */ + for (i=0; i<count; i++) + udelay (1000); + + /* check for ctrl-c to abort... */ + if (ctrlc()) { + puts("Abort\n"); + return 0; + } + } + + return 0; +} +#endif /* CONFIG_MX_CYCLIC */ + +static int do_mem_cmp(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + ulong addr1, addr2, count, ngood, bytes; + int size; + int rcode = 0; + const char *type; + const void *buf1, *buf2, *base; +#ifdef CONFIG_SYS_SUPPORT_64BIT_DATA + u64 word1, word2; +#else + ulong word1, word2; +#endif + + if (argc != 4) + return CMD_RET_USAGE; + + /* Check for size specification. + */ + if ((size = cmd_get_data_size(argv[0], 4)) < 0) + return 1; + type = size == 8 ? "double word" : + size == 4 ? "word" : + size == 2 ? "halfword" : "byte"; + + addr1 = simple_strtoul(argv[1], NULL, 16); + addr1 += base_address; + + addr2 = simple_strtoul(argv[2], NULL, 16); + addr2 += base_address; + + count = simple_strtoul(argv[3], NULL, 16); + +#ifdef CONFIG_HAS_DATAFLASH + if (addr_dataflash(addr1) | addr_dataflash(addr2)){ + puts ("Comparison with DataFlash space not supported.\n\r"); + return 0; + } +#endif + +#ifdef CONFIG_BLACKFIN + if (addr_bfin_on_chip_mem(addr1) || addr_bfin_on_chip_mem(addr2)) { + puts ("Comparison with L1 instruction memory not supported.\n\r"); + return 0; + } +#endif + + bytes = size * count; + base = buf1 = map_sysmem(addr1, bytes); + buf2 = map_sysmem(addr2, bytes); + for (ngood = 0; ngood < count; ++ngood) { + if (size == 4) { + word1 = *(u32 *)buf1; + word2 = *(u32 *)buf2; +#ifdef CONFIG_SYS_SUPPORT_64BIT_DATA + } else if (size == 8) { + word1 = *(u64 *)buf1; + word2 = *(u64 *)buf2; +#endif + } else if (size == 2) { + word1 = *(u16 *)buf1; + word2 = *(u16 *)buf2; + } else { + word1 = *(u8 *)buf1; + word2 = *(u8 *)buf2; + } + if (word1 != word2) { + ulong offset = buf1 - base; +#ifdef CONFIG_SYS_SUPPORT_64BIT_DATA + printf("%s at 0x%p (%#0*llx) != %s at 0x%p (%#0*llx)\n", + type, (void *)(addr1 + offset), size, word1, + type, (void *)(addr2 + offset), size, word2); +#else + printf("%s at 0x%08lx (%#0*lx) != %s at 0x%08lx (%#0*lx)\n", + type, (ulong)(addr1 + offset), size, word1, + type, (ulong)(addr2 + offset), size, word2); +#endif + rcode = 1; + break; + } + + buf1 += size; + buf2 += size; + + /* reset watchdog from time to time */ + if ((ngood % (64 << 10)) == 0) + WATCHDOG_RESET(); + } + unmap_sysmem(buf1); + unmap_sysmem(buf2); + + printf("Total of %ld %s(s) were the same\n", ngood, type); + return rcode; +} + +static int do_mem_cp(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + ulong addr, dest, count, bytes; + int size; + const void *src; + void *buf; + + if (argc != 4) + return CMD_RET_USAGE; + + /* Check for size specification. + */ + if ((size = cmd_get_data_size(argv[0], 4)) < 0) + return 1; + + addr = simple_strtoul(argv[1], NULL, 16); + addr += base_address; + + dest = simple_strtoul(argv[2], NULL, 16); + dest += base_address; + + count = simple_strtoul(argv[3], NULL, 16); + + if (count == 0) { + puts ("Zero length ???\n"); + return 1; + } + +#ifndef CONFIG_SYS_NO_FLASH + /* check if we are copying to Flash */ + if ( (addr2info(dest) != NULL) +#ifdef CONFIG_HAS_DATAFLASH + && (!addr_dataflash(dest)) +#endif + ) { + int rc; + + puts ("Copy to Flash... "); + + rc = flash_write ((char *)addr, dest, count*size); + if (rc != 0) { + flash_perror (rc); + return (1); + } + puts ("done\n"); + return 0; + } +#endif + +#ifdef CONFIG_HAS_DATAFLASH + /* Check if we are copying from RAM or Flash to DataFlash */ + if (addr_dataflash(dest) && !addr_dataflash(addr)){ + int rc; + + puts ("Copy to DataFlash... "); + + rc = write_dataflash (dest, addr, count*size); + + if (rc != 1) { + dataflash_perror (rc); + return (1); + } + puts ("done\n"); + return 0; + } + + /* Check if we are copying from DataFlash to RAM */ + if (addr_dataflash(addr) && !addr_dataflash(dest) +#ifndef CONFIG_SYS_NO_FLASH + && (addr2info(dest) == NULL) +#endif + ){ + int rc; + rc = read_dataflash(addr, count * size, (char *) dest); + if (rc != 1) { + dataflash_perror (rc); + return (1); + } + return 0; + } + + if (addr_dataflash(addr) && addr_dataflash(dest)){ + puts ("Unsupported combination of source/destination.\n\r"); + return 1; + } +#endif + +#ifdef CONFIG_BLACKFIN + /* See if we're copying to/from L1 inst */ + if (addr_bfin_on_chip_mem(dest) || addr_bfin_on_chip_mem(addr)) { + memcpy((void *)dest, (void *)addr, count * size); + return 0; + } +#endif + + bytes = size * count; + buf = map_sysmem(dest, bytes); + src = map_sysmem(addr, bytes); + while (count-- > 0) { + if (size == 4) + *((u32 *)buf) = *((u32 *)src); +#ifdef CONFIG_SYS_SUPPORT_64BIT_DATA + else if (size == 8) + *((u64 *)buf) = *((u64 *)src); +#endif + else if (size == 2) + *((u16 *)buf) = *((u16 *)src); + else + *((u8 *)buf) = *((u8 *)src); + src += size; + buf += size; + + /* reset watchdog from time to time */ + if ((count % (64 << 10)) == 0) + WATCHDOG_RESET(); + } + return 0; +} + +static int do_mem_base(cmd_tbl_t *cmdtp, int flag, int argc, + char * const argv[]) +{ + if (argc > 1) { + /* Set new base address. + */ + base_address = simple_strtoul(argv[1], NULL, 16); + } + /* Print the current base address. + */ + printf("Base Address: 0x%08lx\n", base_address); + return 0; +} + +static int do_mem_loop(cmd_tbl_t *cmdtp, int flag, int argc, + char * const argv[]) +{ + ulong addr, length, i, bytes; + int size; +#ifdef CONFIG_SYS_SUPPORT_64BIT_DATA + volatile u64 *llp; +#endif + volatile u32 *longp; + volatile u16 *shortp; + volatile u8 *cp; + const void *buf; + + if (argc < 3) + return CMD_RET_USAGE; + + /* + * Check for a size specification. + * Defaults to long if no or incorrect specification. + */ + if ((size = cmd_get_data_size(argv[0], 4)) < 0) + return 1; + + /* Address is always specified. + */ + addr = simple_strtoul(argv[1], NULL, 16); + + /* Length is the number of objects, not number of bytes. + */ + length = simple_strtoul(argv[2], NULL, 16); + + bytes = size * length; + buf = map_sysmem(addr, bytes); + + /* We want to optimize the loops to run as fast as possible. + * If we have only one object, just run infinite loops. + */ + if (length == 1) { +#ifdef CONFIG_SYS_SUPPORT_64BIT_DATA + if (size == 8) { + llp = (u64 *)buf; + for (;;) + i = *llp; + } +#endif + if (size == 4) { + longp = (u32 *)buf; + for (;;) + i = *longp; + } + if (size == 2) { + shortp = (u16 *)buf; + for (;;) + i = *shortp; + } + cp = (u8 *)buf; + for (;;) + i = *cp; + } + +#ifdef CONFIG_SYS_SUPPORT_64BIT_DATA + if (size == 8) { + for (;;) { + llp = (u64 *)buf; + i = length; + while (i-- > 0) + *llp++; + } + } +#endif + if (size == 4) { + for (;;) { + longp = (u32 *)buf; + i = length; + while (i-- > 0) + *longp++; + } + } + if (size == 2) { + for (;;) { + shortp = (u16 *)buf; + i = length; + while (i-- > 0) + *shortp++; + } + } + for (;;) { + cp = (u8 *)buf; + i = length; + while (i-- > 0) + *cp++; + } + unmap_sysmem(buf); + + return 0; +} + +#ifdef CONFIG_LOOPW +int do_mem_loopw (cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + ulong addr, length, i, bytes; + int size; +#ifdef CONFIG_SYS_SUPPORT_64BIT_DATA + volatile u64 *llp; + u64 data; +#else + ulong data; +#endif + volatile u32 *longp; + volatile u16 *shortp; + volatile u8 *cp; + void *buf; + + if (argc < 4) + return CMD_RET_USAGE; + + /* + * Check for a size specification. + * Defaults to long if no or incorrect specification. + */ + if ((size = cmd_get_data_size(argv[0], 4)) < 0) + return 1; + + /* Address is always specified. + */ + addr = simple_strtoul(argv[1], NULL, 16); + + /* Length is the number of objects, not number of bytes. + */ + length = simple_strtoul(argv[2], NULL, 16); + + /* data to write */ +#ifdef CONFIG_SYS_SUPPORT_64BIT_DATA + data = simple_strtoull(argv[3], NULL, 16); +#else + data = simple_strtoul(argv[3], NULL, 16); +#endif + + bytes = size * length; + buf = map_sysmem(addr, bytes); + + /* We want to optimize the loops to run as fast as possible. + * If we have only one object, just run infinite loops. + */ + if (length == 1) { +#ifdef CONFIG_SYS_SUPPORT_64BIT_DATA + if (size == 8) { + llp = (u64 *)buf; + for (;;) + *llp = data; + } +#endif + if (size == 4) { + longp = (u32 *)buf; + for (;;) + *longp = data; + } + if (size == 2) { + shortp = (u16 *)buf; + for (;;) + *shortp = data; + } + cp = (u8 *)buf; + for (;;) + *cp = data; + } + +#ifdef CONFIG_SYS_SUPPORT_64BIT_DATA + if (size == 8) { + for (;;) { + llp = (u64 *)buf; + i = length; + while (i-- > 0) + *llp++ = data; + } + } +#endif + if (size == 4) { + for (;;) { + longp = (u32 *)buf; + i = length; + while (i-- > 0) + *longp++ = data; + } + } + if (size == 2) { + for (;;) { + shortp = (u16 *)buf; + i = length; + while (i-- > 0) + *shortp++ = data; + } + } + for (;;) { + cp = (u8 *)buf; + i = length; + while (i-- > 0) + *cp++ = data; + } +} +#endif /* CONFIG_LOOPW */ + +#ifdef CONFIG_CMD_MEMTEST +static ulong mem_test_alt(vu_long *buf, ulong start_addr, ulong end_addr, + vu_long *dummy) +{ + vu_long *addr; + ulong errs = 0; + ulong val, readback; + int j; + vu_long offset; + vu_long test_offset; + vu_long pattern; + vu_long temp; + vu_long anti_pattern; + vu_long num_words; + static const ulong bitpattern[] = { + 0x00000001, /* single bit */ + 0x00000003, /* two adjacent bits */ + 0x00000007, /* three adjacent bits */ + 0x0000000F, /* four adjacent bits */ + 0x00000005, /* two non-adjacent bits */ + 0x00000015, /* three non-adjacent bits */ + 0x00000055, /* four non-adjacent bits */ + 0xaaaaaaaa, /* alternating 1/0 */ + }; + + num_words = (end_addr - start_addr) / sizeof(vu_long); + + /* + * Data line test: write a pattern to the first + * location, write the 1's complement to a 'parking' + * address (changes the state of the data bus so a + * floating bus doesn't give a false OK), and then + * read the value back. Note that we read it back + * into a variable because the next time we read it, + * it might be right (been there, tough to explain to + * the quality guys why it prints a failure when the + * "is" and "should be" are obviously the same in the + * error message). + * + * Rather than exhaustively testing, we test some + * patterns by shifting '1' bits through a field of + * '0's and '0' bits through a field of '1's (i.e. + * pattern and ~pattern). + */ + addr = buf; + for (j = 0; j < sizeof(bitpattern) / sizeof(bitpattern[0]); j++) { + val = bitpattern[j]; + for (; val != 0; val <<= 1) { + *addr = val; + *dummy = ~val; /* clear the test data off the bus */ + readback = *addr; + if (readback != val) { + printf("FAILURE (data line): " + "expected %08lx, actual %08lx\n", + val, readback); + errs++; + if (ctrlc()) + return -1; + } + *addr = ~val; + *dummy = val; + readback = *addr; + if (readback != ~val) { + printf("FAILURE (data line): " + "Is %08lx, should be %08lx\n", + readback, ~val); + errs++; + if (ctrlc()) + return -1; + } + } + } + + /* + * Based on code whose Original Author and Copyright + * information follows: Copyright (c) 1998 by Michael + * Barr. This software is placed into the public + * domain and may be used for any purpose. However, + * this notice must not be changed or removed and no + * warranty is either expressed or implied by its + * publication or distribution. + */ + + /* + * Address line test + + * Description: Test the address bus wiring in a + * memory region by performing a walking + * 1's test on the relevant bits of the + * address and checking for aliasing. + * This test will find single-bit + * address failures such as stuck-high, + * stuck-low, and shorted pins. The base + * address and size of the region are + * selected by the caller. + + * Notes: For best results, the selected base + * address should have enough LSB 0's to + * guarantee single address bit changes. + * For example, to test a 64-Kbyte + * region, select a base address on a + * 64-Kbyte boundary. Also, select the + * region size as a power-of-two if at + * all possible. + * + * Returns: 0 if the test succeeds, 1 if the test fails. + */ + pattern = (vu_long) 0xaaaaaaaa; + anti_pattern = (vu_long) 0x55555555; + + debug("%s:%d: length = 0x%.8lx\n", __func__, __LINE__, num_words); + /* + * Write the default pattern at each of the + * power-of-two offsets. + */ + for (offset = 1; offset < num_words; offset <<= 1) + addr[offset] = pattern; + + /* + * Check for address bits stuck high. + */ + test_offset = 0; + addr[test_offset] = anti_pattern; + + for (offset = 1; offset < num_words; offset <<= 1) { + temp = addr[offset]; + if (temp != pattern) { + printf("\nFAILURE: Address bit stuck high @ 0x%.8lx:" + " expected 0x%.8lx, actual 0x%.8lx\n", + start_addr + offset*sizeof(vu_long), + pattern, temp); + errs++; + if (ctrlc()) + return -1; + } + } + addr[test_offset] = pattern; + WATCHDOG_RESET(); + + /* + * Check for addr bits stuck low or shorted. + */ + for (test_offset = 1; test_offset < num_words; test_offset <<= 1) { + addr[test_offset] = anti_pattern; + + for (offset = 1; offset < num_words; offset <<= 1) { + temp = addr[offset]; + if ((temp != pattern) && (offset != test_offset)) { + printf("\nFAILURE: Address bit stuck low or" + " shorted @ 0x%.8lx: expected 0x%.8lx," + " actual 0x%.8lx\n", + start_addr + offset*sizeof(vu_long), + pattern, temp); + errs++; + if (ctrlc()) + return -1; + } + } + addr[test_offset] = pattern; + } + + /* + * Description: Test the integrity of a physical + * memory device by performing an + * increment/decrement test over the + * entire region. In the process every + * storage bit in the device is tested + * as a zero and a one. The base address + * and the size of the region are + * selected by the caller. + * + * Returns: 0 if the test succeeds, 1 if the test fails. + */ + num_words++; + + /* + * Fill memory with a known pattern. + */ + for (pattern = 1, offset = 0; offset < num_words; pattern++, offset++) { + WATCHDOG_RESET(); + addr[offset] = pattern; + } + + /* + * Check each location and invert it for the second pass. + */ + for (pattern = 1, offset = 0; offset < num_words; pattern++, offset++) { + WATCHDOG_RESET(); + temp = addr[offset]; + if (temp != pattern) { + printf("\nFAILURE (read/write) @ 0x%.8lx:" + " expected 0x%.8lx, actual 0x%.8lx)\n", + start_addr + offset*sizeof(vu_long), + pattern, temp); + errs++; + if (ctrlc()) + return -1; + } + + anti_pattern = ~pattern; + addr[offset] = anti_pattern; + } + + /* + * Check each location for the inverted pattern and zero it. + */ + for (pattern = 1, offset = 0; offset < num_words; pattern++, offset++) { + WATCHDOG_RESET(); + anti_pattern = ~pattern; + temp = addr[offset]; + if (temp != anti_pattern) { + printf("\nFAILURE (read/write): @ 0x%.8lx:" + " expected 0x%.8lx, actual 0x%.8lx)\n", + start_addr + offset*sizeof(vu_long), + anti_pattern, temp); + errs++; + if (ctrlc()) + return -1; + } + addr[offset] = 0; + } + + return 0; +} + +static ulong mem_test_quick(vu_long *buf, ulong start_addr, ulong end_addr, + vu_long pattern, int iteration) +{ + vu_long *end; + vu_long *addr; + ulong errs = 0; + ulong incr, length; + ulong val, readback; + + /* Alternate the pattern */ + incr = 1; + if (iteration & 1) { + incr = -incr; + /* + * Flip the pattern each time to make lots of zeros and + * then, the next time, lots of ones. We decrement + * the "negative" patterns and increment the "positive" + * patterns to preserve this feature. + */ + if (pattern & 0x80000000) + pattern = -pattern; /* complement & increment */ + else + pattern = ~pattern; + } + length = (end_addr - start_addr) / sizeof(ulong); + end = buf + length; + printf("\rPattern %08lX Writing..." + "%12s" + "\b\b\b\b\b\b\b\b\b\b", + pattern, ""); + + for (addr = buf, val = pattern; addr < end; addr++) { + WATCHDOG_RESET(); + *addr = val; + val += incr; + } + + puts("Reading..."); + + for (addr = buf, val = pattern; addr < end; addr++) { + WATCHDOG_RESET(); + readback = *addr; + if (readback != val) { + ulong offset = addr - buf; + + printf("\nMem error @ 0x%08X: " + "found %08lX, expected %08lX\n", + (uint)(uintptr_t)(start_addr + offset*sizeof(vu_long)), + readback, val); + errs++; + if (ctrlc()) + return -1; + } + val += incr; + } + + return 0; +} + +/* + * Perform a memory test. A more complete alternative test can be + * configured using CONFIG_SYS_ALT_MEMTEST. The complete test loops until + * interrupted by ctrl-c or by a failure of one of the sub-tests. + */ +static int do_mem_mtest(cmd_tbl_t *cmdtp, int flag, int argc, + char * const argv[]) +{ + ulong start, end; + vu_long *buf, *dummy; + int iteration_limit; + int ret; + ulong errs = 0; /* number of errors, or -1 if interrupted */ + ulong pattern; + int iteration; +#if defined(CONFIG_SYS_ALT_MEMTEST) + const int alt_test = 1; +#else + const int alt_test = 0; +#endif + + if (argc > 1) + start = simple_strtoul(argv[1], NULL, 16); + else + start = CONFIG_SYS_MEMTEST_START; + + if (argc > 2) + end = simple_strtoul(argv[2], NULL, 16); + else + end = CONFIG_SYS_MEMTEST_END; + + if (argc > 3) + pattern = (ulong)simple_strtoul(argv[3], NULL, 16); + else + pattern = 0; + + if (argc > 4) + iteration_limit = (ulong)simple_strtoul(argv[4], NULL, 16); + else + iteration_limit = 0; + + printf("Testing %08x ... %08x:\n", (uint)start, (uint)end); + debug("%s:%d: start %#08lx end %#08lx\n", __func__, __LINE__, + start, end); + + buf = map_sysmem(start, end - start); + dummy = map_sysmem(CONFIG_SYS_MEMTEST_SCRATCH, sizeof(vu_long)); + for (iteration = 0; + !iteration_limit || iteration < iteration_limit; + iteration++) { + if (ctrlc()) { + errs = -1UL; + break; + } + + printf("Iteration: %6d\r", iteration + 1); + debug("\n"); + if (alt_test) { + errs = mem_test_alt(buf, start, end, dummy); + } else { + errs = mem_test_quick(buf, start, end, pattern, + iteration); + } + if (errs == -1UL) + break; + } + + /* + * Work-around for eldk-4.2 which gives this warning if we try to + * case in the unmap_sysmem() call: + * warning: initialization discards qualifiers from pointer target type + */ + { + void *vbuf = (void *)buf; + void *vdummy = (void *)dummy; + + unmap_sysmem(vbuf); + unmap_sysmem(vdummy); + } + + if (errs == -1UL) { + /* Memory test was aborted - write a newline to finish off */ + putc('\n'); + ret = 1; + } else { + printf("Tested %d iteration(s) with %lu errors.\n", + iteration, errs); + ret = errs != 0; + } + + return ret; /* not reached */ +} +#endif /* CONFIG_CMD_MEMTEST */ + +/* Modify memory. + * + * Syntax: + * mm{.b, .w, .l, .q} {addr} + * nm{.b, .w, .l, .q} {addr} + */ +static int +mod_mem(cmd_tbl_t *cmdtp, int incrflag, int flag, int argc, char * const argv[]) +{ + ulong addr; +#ifdef CONFIG_SYS_SUPPORT_64BIT_DATA + u64 i; +#else + ulong i; +#endif + int nbytes, size; + void *ptr = NULL; + + if (argc != 2) + return CMD_RET_USAGE; + +#ifdef CONFIG_BOOT_RETRY_TIME + reset_cmd_timeout(); /* got a good command to get here */ +#endif + /* We use the last specified parameters, unless new ones are + * entered. + */ + addr = mm_last_addr; + size = mm_last_size; + + if ((flag & CMD_FLAG_REPEAT) == 0) { + /* New command specified. Check for a size specification. + * Defaults to long if no or incorrect specification. + */ + if ((size = cmd_get_data_size(argv[0], 4)) < 0) + return 1; + + /* Address is specified since argc > 1 + */ + addr = simple_strtoul(argv[1], NULL, 16); + addr += base_address; + } + +#ifdef CONFIG_HAS_DATAFLASH + if (addr_dataflash(addr)){ + puts ("Can't modify DataFlash in place. Use cp instead.\n\r"); + return 0; + } +#endif + +#ifdef CONFIG_BLACKFIN + if (addr_bfin_on_chip_mem(addr)) { + puts ("Can't modify L1 instruction in place. Use cp instead.\n\r"); + return 0; + } +#endif + + /* Print the address, followed by value. Then accept input for + * the next value. A non-converted value exits. + */ + do { + ptr = map_sysmem(addr, size); + printf("%08lx:", addr); + if (size == 4) + printf(" %08x", *((u32 *)ptr)); +#ifdef CONFIG_SYS_SUPPORT_64BIT_DATA + else if (size == 8) + printf(" %016llx", *((u64 *)ptr)); +#endif + else if (size == 2) + printf(" %04x", *((u16 *)ptr)); + else + printf(" %02x", *((u8 *)ptr)); + + nbytes = readline (" ? "); + if (nbytes == 0 || (nbytes == 1 && console_buffer[0] == '-')) { + /* <CR> pressed as only input, don't modify current + * location and move to next. "-" pressed will go back. + */ + if (incrflag) + addr += nbytes ? -size : size; + nbytes = 1; +#ifdef CONFIG_BOOT_RETRY_TIME + reset_cmd_timeout(); /* good enough to not time out */ +#endif + } +#ifdef CONFIG_BOOT_RETRY_TIME + else if (nbytes == -2) { + break; /* timed out, exit the command */ + } +#endif + else { + char *endp; +#ifdef CONFIG_SYS_SUPPORT_64BIT_DATA + i = simple_strtoull(console_buffer, &endp, 16); +#else + i = simple_strtoul(console_buffer, &endp, 16); +#endif + nbytes = endp - console_buffer; + if (nbytes) { +#ifdef CONFIG_BOOT_RETRY_TIME + /* good enough to not time out + */ + reset_cmd_timeout(); +#endif + if (size == 4) + *((u32 *)ptr) = i; +#ifdef CONFIG_SYS_SUPPORT_64BIT_DATA + else if (size == 8) + *((u64 *)ptr) = i; +#endif + else if (size == 2) + *((u16 *)ptr) = i; + else + *((u8 *)ptr) = i; + if (incrflag) + addr += size; + } + } + } while (nbytes); + if (ptr) + unmap_sysmem(ptr); + + mm_last_addr = addr; + mm_last_size = size; + return 0; +} + +#ifdef CONFIG_CMD_CRC32 + +static int do_mem_crc(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + int flags = 0; + int ac; + char * const *av; + + if (argc < 3) + return CMD_RET_USAGE; + + av = argv + 1; + ac = argc - 1; +#ifdef CONFIG_HASH_VERIFY + if (strcmp(*av, "-v") == 0) { + flags |= HASH_FLAG_VERIFY; + av++; + ac--; + } +#endif + + return hash_command("crc32", flags, cmdtp, flag, ac, av); +} + +#endif + +/**************************************************/ +U_BOOT_CMD( + md, 3, 1, do_mem_md, + "memory display", +#ifdef CONFIG_SYS_SUPPORT_64BIT_DATA + "[.b, .w, .l, .q] address [# of objects]" +#else + "[.b, .w, .l] address [# of objects]" +#endif +); + + +U_BOOT_CMD( + mm, 2, 1, do_mem_mm, + "memory modify (auto-incrementing address)", +#ifdef CONFIG_SYS_SUPPORT_64BIT_DATA + "[.b, .w, .l, .q] address" +#else + "[.b, .w, .l] address" +#endif +); + + +U_BOOT_CMD( + nm, 2, 1, do_mem_nm, + "memory modify (constant address)", +#ifdef CONFIG_SYS_SUPPORT_64BIT_DATA + "[.b, .w, .l, .q] address" +#else + "[.b, .w, .l] address" +#endif +); + +U_BOOT_CMD( + mw, 4, 1, do_mem_mw, + "memory write (fill)", +#ifdef CONFIG_SYS_SUPPORT_64BIT_DATA + "[.b, .w, .l, .q] address value [count]" +#else + "[.b, .w, .l] address value [count]" +#endif +); + +U_BOOT_CMD( + cp, 4, 1, do_mem_cp, + "memory copy", +#ifdef CONFIG_SYS_SUPPORT_64BIT_DATA + "[.b, .w, .l, .q] source target count" +#else + "[.b, .w, .l] source target count" +#endif +); + +U_BOOT_CMD( + cmp, 4, 1, do_mem_cmp, + "memory compare", +#ifdef CONFIG_SYS_SUPPORT_64BIT_DATA + "[.b, .w, .l, .q] addr1 addr2 count" +#else + "[.b, .w, .l] addr1 addr2 count" +#endif +); + +#ifdef CONFIG_CMD_CRC32 + +#ifndef CONFIG_CRC32_VERIFY + +U_BOOT_CMD( + crc32, 4, 1, do_mem_crc, + "checksum calculation", + "address count [addr]\n - compute CRC32 checksum [save at addr]" +); + +#else /* CONFIG_CRC32_VERIFY */ + +U_BOOT_CMD( + crc32, 5, 1, do_mem_crc, + "checksum calculation", + "address count [addr]\n - compute CRC32 checksum [save at addr]\n" + "-v address count crc\n - verify crc of memory area" +); + +#endif /* CONFIG_CRC32_VERIFY */ + +#endif + +#ifdef CONFIG_CMD_MEMINFO +__weak void board_show_dram(ulong size) +{ + puts("DRAM: "); + print_size(size, "\n"); +} + +static int do_mem_info(cmd_tbl_t *cmdtp, int flag, int argc, + char * const argv[]) +{ + board_show_dram(gd->ram_size); + + return 0; +} +#endif + +U_BOOT_CMD( + base, 2, 1, do_mem_base, + "print or set address offset", + "\n - print address offset for memory commands\n" + "base off\n - set address offset for memory commands to 'off'" +); + +U_BOOT_CMD( + loop, 3, 1, do_mem_loop, + "infinite loop on address range", +#ifdef CONFIG_SYS_SUPPORT_64BIT_DATA + "[.b, .w, .l, .q] address number_of_objects" +#else + "[.b, .w, .l] address number_of_objects" +#endif +); + +#ifdef CONFIG_LOOPW +U_BOOT_CMD( + loopw, 4, 1, do_mem_loopw, + "infinite write loop on address range", +#ifdef CONFIG_SYS_SUPPORT_64BIT_DATA + "[.b, .w, .l, .q] address number_of_objects data_to_write" +#else + "[.b, .w, .l] address number_of_objects data_to_write" +#endif +); +#endif /* CONFIG_LOOPW */ + +#ifdef CONFIG_CMD_MEMTEST +U_BOOT_CMD( + mtest, 5, 1, do_mem_mtest, + "simple RAM read/write test", + "[start [end [pattern [iterations]]]]" +); +#endif /* CONFIG_CMD_MEMTEST */ + +#ifdef CONFIG_MX_CYCLIC +U_BOOT_CMD( + mdc, 4, 1, do_mem_mdc, + "memory display cyclic", +#ifdef CONFIG_SYS_SUPPORT_64BIT_DATA + "[.b, .w, .l, .q] address count delay(ms)" +#else + "[.b, .w, .l] address count delay(ms)" +#endif +); + +U_BOOT_CMD( + mwc, 4, 1, do_mem_mwc, + "memory write cyclic", +#ifdef CONFIG_SYS_SUPPORT_64BIT_DATA + "[.b, .w, .l, .q] address value delay(ms)" +#else + "[.b, .w, .l] address value delay(ms)" +#endif +); +#endif /* CONFIG_MX_CYCLIC */ + +#ifdef CONFIG_CMD_MEMINFO +U_BOOT_CMD( + meminfo, 3, 1, do_mem_info, + "display memory information", + "" +); +#endif diff --git a/qemu/roms/u-boot/common/cmd_mfsl.c b/qemu/roms/u-boot/common/cmd_mfsl.c new file mode 100644 index 000000000..e8e8e3c0d --- /dev/null +++ b/qemu/roms/u-boot/common/cmd_mfsl.c @@ -0,0 +1,388 @@ +/* + * (C) Copyright 2007 Michal Simek + * + * Michal SIMEK <monstr@monstr.eu> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/* + * Microblaze FSL support + */ + +#include <common.h> +#include <config.h> +#include <command.h> +#include <asm/asm.h> + +int do_frd (cmd_tbl_t * cmdtp, int flag, int argc, char * const argv[]) +{ + unsigned int fslnum; + unsigned int num; + unsigned int blocking; + + if (argc < 2) + return CMD_RET_USAGE; + + fslnum = (unsigned int)simple_strtoul (argv[1], NULL, 16); + blocking = (unsigned int)simple_strtoul (argv[2], NULL, 16); + if (fslnum < 0 || fslnum >= XILINX_FSL_NUMBER) { + puts ("Bad number of FSL\n"); + return CMD_RET_USAGE; + } + + switch (fslnum) { +#if (XILINX_FSL_NUMBER > 0) + case 0: + switch (blocking) { + case 0: NGET (num, 0); + break; + case 1: NCGET (num, 0); + break; + case 2: GET (num, 0); + break; + case 3: CGET (num, 0); + break; + default: + return 2; + } + break; +#endif +#if (XILINX_FSL_NUMBER > 1) + case 1: + switch (blocking) { + case 0: NGET (num, 1); + break; + case 1: NCGET (num, 1); + break; + case 2: GET (num, 1); + break; + case 3: CGET (num, 1); + break; + default: + return 2; + } + break; +#endif +#if (XILINX_FSL_NUMBER > 2) + case 2: + switch (blocking) { + case 0: NGET (num, 2); + break; + case 1: NCGET (num, 2); + break; + case 2: GET (num, 2); + break; + case 3: CGET (num, 2); + break; + default: + return 2; + } + break; +#endif +#if (XILINX_FSL_NUMBER > 3) + case 3: + switch (blocking) { + case 0: NGET (num, 3); + break; + case 1: NCGET (num, 3); + break; + case 2: GET (num, 3); + break; + case 3: CGET (num, 3); + break; + default: + return 2; + } + break; +#endif +#if (XILINX_FSL_NUMBER > 4) + case 4: + switch (blocking) { + case 0: NGET (num, 4); + break; + case 1: NCGET (num, 4); + break; + case 2: GET (num, 4); + break; + case 3: CGET (num, 4); + break; + default: + return 2; + } + break; +#endif +#if (XILINX_FSL_NUMBER > 5) + case 5: + switch (blocking) { + case 0: NGET (num, 5); + break; + case 1: NCGET (num, 5); + break; + case 2: GET (num, 5); + break; + case 3: CGET (num, 5); + break; + default: + return 2; + } + break; +#endif +#if (XILINX_FSL_NUMBER > 6) + case 6: + switch (blocking) { + case 0: NGET (num, 6); + break; + case 1: NCGET (num, 6); + break; + case 2: GET (num, 6); + break; + case 3: CGET (num, 6); + break; + default: + return 2; + } + break; +#endif +#if (XILINX_FSL_NUMBER > 7) + case 7: + switch (blocking) { + case 0: NGET (num, 7); + break; + case 1: NCGET (num, 7); + break; + case 2: GET (num, 7); + break; + case 3: CGET (num, 7); + break; + default: + return 2; + } + break; +#endif + default: + return 1; + } + + printf ("%01x: 0x%08x - %s %s read\n", fslnum, num, + blocking < 2 ? "non blocking" : "blocking", + ((blocking == 1) || (blocking == 3)) ? "control" : "data" ); + return 0; +} + +int do_fwr (cmd_tbl_t * cmdtp, int flag, int argc, char * const argv[]) +{ + unsigned int fslnum; + unsigned int num; + unsigned int blocking; + + if (argc < 3) + return CMD_RET_USAGE; + + fslnum = (unsigned int)simple_strtoul (argv[1], NULL, 16); + num = (unsigned int)simple_strtoul (argv[2], NULL, 16); + blocking = (unsigned int)simple_strtoul (argv[3], NULL, 16); + if (fslnum < 0 || fslnum >= XILINX_FSL_NUMBER) + return CMD_RET_USAGE; + + switch (fslnum) { +#if (XILINX_FSL_NUMBER > 0) + case 0: + switch (blocking) { + case 0: NPUT (num, 0); + break; + case 1: NCPUT (num, 0); + break; + case 2: PUT (num, 0); + break; + case 3: CPUT (num, 0); + break; + default: + return 2; + } + break; +#endif +#if (XILINX_FSL_NUMBER > 1) + case 1: + switch (blocking) { + case 0: NPUT (num, 1); + break; + case 1: NCPUT (num, 1); + break; + case 2: PUT (num, 1); + break; + case 3: CPUT (num, 1); + break; + default: + return 2; + } + break; +#endif +#if (XILINX_FSL_NUMBER > 2) + case 2: + switch (blocking) { + case 0: NPUT (num, 2); + break; + case 1: NCPUT (num, 2); + break; + case 2: PUT (num, 2); + break; + case 3: CPUT (num, 2); + break; + default: + return 2; + } + break; +#endif +#if (XILINX_FSL_NUMBER > 3) + case 3: + switch (blocking) { + case 0: NPUT (num, 3); + break; + case 1: NCPUT (num, 3); + break; + case 2: PUT (num, 3); + break; + case 3: CPUT (num, 3); + break; + default: + return 2; + } + break; +#endif +#if (XILINX_FSL_NUMBER > 4) + case 4: + switch (blocking) { + case 0: NPUT (num, 4); + break; + case 1: NCPUT (num, 4); + break; + case 2: PUT (num, 4); + break; + case 3: CPUT (num, 4); + break; + default: + return 2; + } + break; +#endif +#if (XILINX_FSL_NUMBER > 5) + case 5: + switch (blocking) { + case 0: NPUT (num, 5); + break; + case 1: NCPUT (num, 5); + break; + case 2: PUT (num, 5); + break; + case 3: CPUT (num, 5); + break; + default: + return 2; + } + break; +#endif +#if (XILINX_FSL_NUMBER > 6) + case 6: + switch (blocking) { + case 0: NPUT (num, 6); + break; + case 1: NCPUT (num, 6); + break; + case 2: PUT (num, 6); + break; + case 3: CPUT (num, 6); + break; + default: + return 2; + } + break; +#endif +#if (XILINX_FSL_NUMBER > 7) + case 7: + switch (blocking) { + case 0: NPUT (num, 7); + break; + case 1: NCPUT (num, 7); + break; + case 2: PUT (num, 7); + break; + case 3: CPUT (num, 7); + break; + default: + return 2; + } + break; +#endif + default: + return 1; + } + + printf ("%01x: 0x%08x - %s %s write\n", fslnum, num, + blocking < 2 ? "non blocking" : "blocking", + ((blocking == 1) || (blocking == 3)) ? "control" : "data" ); + return 0; + +} + +int do_rspr (cmd_tbl_t * cmdtp, int flag, int argc, char * const argv[]) +{ + unsigned int reg = 0; + unsigned int val = 0; + + if (argc < 2) + return CMD_RET_USAGE; + + reg = (unsigned int)simple_strtoul (argv[1], NULL, 16); + val = (unsigned int)simple_strtoul (argv[2], NULL, 16); + switch (reg) { + case 0x1: + if (argc > 2) { + MTS (val, rmsr); + NOP; + MFS (val, rmsr); + } else { + MFS (val, rmsr); + } + puts ("MSR"); + break; + case 0x3: + MFS (val, rear); + puts ("EAR"); + break; + case 0x5: + MFS (val, resr); + puts ("ESR"); + break; + default: + puts ("Unsupported register\n"); + return 1; + } + printf (": 0x%08x\n", val); + return 0; +} + +/***************************************************/ + +U_BOOT_CMD (frd, 3, 1, do_frd, + "read data from FSL", + "- [fslnum [0|1|2|3]]\n" + " 0 - non blocking data read\n" + " 1 - non blocking control read\n" + " 2 - blocking data read\n" + " 3 - blocking control read"); + +U_BOOT_CMD (fwr, 4, 1, do_fwr, + "write data to FSL", + "- [fslnum [0|1|2|3]]\n" + " 0 - non blocking data write\n" + " 1 - non blocking control write\n" + " 2 - blocking data write\n" + " 3 - blocking control write"); + +U_BOOT_CMD (rspr, 3, 1, do_rspr, + "read/write special purpose register", + "- reg_num [write value] read/write special purpose register\n" + " 1 - MSR - Machine status register\n" + " 3 - EAR - Exception address register\n" + " 5 - ESR - Exception status register"); diff --git a/qemu/roms/u-boot/common/cmd_mii.c b/qemu/roms/u-boot/common/cmd_mii.c new file mode 100644 index 000000000..b82a7ce61 --- /dev/null +++ b/qemu/roms/u-boot/common/cmd_mii.c @@ -0,0 +1,436 @@ +/* + * (C) Copyright 2001 + * Gerald Van Baren, Custom IDEAS, vanbaren@cideas.com + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/* + * MII Utilities + */ + +#include <common.h> +#include <command.h> +#include <miiphy.h> + +typedef struct _MII_reg_desc_t { + ushort regno; + char * name; +} MII_reg_desc_t; + +static const MII_reg_desc_t reg_0_5_desc_tbl[] = { + { MII_BMCR, "PHY control register" }, + { MII_BMSR, "PHY status register" }, + { MII_PHYSID1, "PHY ID 1 register" }, + { MII_PHYSID2, "PHY ID 2 register" }, + { MII_ADVERTISE, "Autonegotiation advertisement register" }, + { MII_LPA, "Autonegotiation partner abilities register" }, +}; + +typedef struct _MII_field_desc_t { + ushort hi; + ushort lo; + ushort mask; + char * name; +} MII_field_desc_t; + +static const MII_field_desc_t reg_0_desc_tbl[] = { + { 15, 15, 0x01, "reset" }, + { 14, 14, 0x01, "loopback" }, + { 13, 6, 0x81, "speed selection" }, /* special */ + { 12, 12, 0x01, "A/N enable" }, + { 11, 11, 0x01, "power-down" }, + { 10, 10, 0x01, "isolate" }, + { 9, 9, 0x01, "restart A/N" }, + { 8, 8, 0x01, "duplex" }, /* special */ + { 7, 7, 0x01, "collision test enable" }, + { 5, 0, 0x3f, "(reserved)" } +}; + +static const MII_field_desc_t reg_1_desc_tbl[] = { + { 15, 15, 0x01, "100BASE-T4 able" }, + { 14, 14, 0x01, "100BASE-X full duplex able" }, + { 13, 13, 0x01, "100BASE-X half duplex able" }, + { 12, 12, 0x01, "10 Mbps full duplex able" }, + { 11, 11, 0x01, "10 Mbps half duplex able" }, + { 10, 10, 0x01, "100BASE-T2 full duplex able" }, + { 9, 9, 0x01, "100BASE-T2 half duplex able" }, + { 8, 8, 0x01, "extended status" }, + { 7, 7, 0x01, "(reserved)" }, + { 6, 6, 0x01, "MF preamble suppression" }, + { 5, 5, 0x01, "A/N complete" }, + { 4, 4, 0x01, "remote fault" }, + { 3, 3, 0x01, "A/N able" }, + { 2, 2, 0x01, "link status" }, + { 1, 1, 0x01, "jabber detect" }, + { 0, 0, 0x01, "extended capabilities" }, +}; + +static const MII_field_desc_t reg_2_desc_tbl[] = { + { 15, 0, 0xffff, "OUI portion" }, +}; + +static const MII_field_desc_t reg_3_desc_tbl[] = { + { 15, 10, 0x3f, "OUI portion" }, + { 9, 4, 0x3f, "manufacturer part number" }, + { 3, 0, 0x0f, "manufacturer rev. number" }, +}; + +static const MII_field_desc_t reg_4_desc_tbl[] = { + { 15, 15, 0x01, "next page able" }, + { 14, 14, 0x01, "(reserved)" }, + { 13, 13, 0x01, "remote fault" }, + { 12, 12, 0x01, "(reserved)" }, + { 11, 11, 0x01, "asymmetric pause" }, + { 10, 10, 0x01, "pause enable" }, + { 9, 9, 0x01, "100BASE-T4 able" }, + { 8, 8, 0x01, "100BASE-TX full duplex able" }, + { 7, 7, 0x01, "100BASE-TX able" }, + { 6, 6, 0x01, "10BASE-T full duplex able" }, + { 5, 5, 0x01, "10BASE-T able" }, + { 4, 0, 0x1f, "xxx to do" }, +}; + +static const MII_field_desc_t reg_5_desc_tbl[] = { + { 15, 15, 0x01, "next page able" }, + { 14, 14, 0x01, "acknowledge" }, + { 13, 13, 0x01, "remote fault" }, + { 12, 12, 0x01, "(reserved)" }, + { 11, 11, 0x01, "asymmetric pause able" }, + { 10, 10, 0x01, "pause able" }, + { 9, 9, 0x01, "100BASE-T4 able" }, + { 8, 8, 0x01, "100BASE-X full duplex able" }, + { 7, 7, 0x01, "100BASE-TX able" }, + { 6, 6, 0x01, "10BASE-T full duplex able" }, + { 5, 5, 0x01, "10BASE-T able" }, + { 4, 0, 0x1f, "xxx to do" }, +}; +typedef struct _MII_field_desc_and_len_t { + const MII_field_desc_t *pdesc; + ushort len; +} MII_field_desc_and_len_t; + +static const MII_field_desc_and_len_t desc_and_len_tbl[] = { + { reg_0_desc_tbl, ARRAY_SIZE(reg_0_desc_tbl) }, + { reg_1_desc_tbl, ARRAY_SIZE(reg_1_desc_tbl) }, + { reg_2_desc_tbl, ARRAY_SIZE(reg_2_desc_tbl) }, + { reg_3_desc_tbl, ARRAY_SIZE(reg_3_desc_tbl) }, + { reg_4_desc_tbl, ARRAY_SIZE(reg_4_desc_tbl) }, + { reg_5_desc_tbl, ARRAY_SIZE(reg_5_desc_tbl) }, +}; + +static void dump_reg( + ushort regval, + const MII_reg_desc_t *prd, + const MII_field_desc_and_len_t *pdl); + +static int special_field( + ushort regno, + const MII_field_desc_t *pdesc, + ushort regval); + +static void MII_dump_0_to_5( + ushort regvals[6], + uchar reglo, + uchar reghi) +{ + ulong i; + + for (i = 0; i < 6; i++) { + if ((reglo <= i) && (i <= reghi)) + dump_reg(regvals[i], ®_0_5_desc_tbl[i], + &desc_and_len_tbl[i]); + } +} + +static void dump_reg( + ushort regval, + const MII_reg_desc_t *prd, + const MII_field_desc_and_len_t *pdl) +{ + ulong i; + ushort mask_in_place; + const MII_field_desc_t *pdesc; + + printf("%u. (%04hx) -- %s --\n", + prd->regno, regval, prd->name); + + for (i = 0; i < pdl->len; i++) { + pdesc = &pdl->pdesc[i]; + + mask_in_place = pdesc->mask << pdesc->lo; + + printf(" (%04hx:%04hx) %u.", + mask_in_place, + regval & mask_in_place, + prd->regno); + + if (special_field(prd->regno, pdesc, regval)) { + } + else { + if (pdesc->hi == pdesc->lo) + printf("%2u ", pdesc->lo); + else + printf("%2u-%2u", pdesc->hi, pdesc->lo); + printf(" = %5u %s", + (regval & mask_in_place) >> pdesc->lo, + pdesc->name); + } + printf("\n"); + + } + printf("\n"); +} + +/* Special fields: +** 0.6,13 +** 0.8 +** 2.15-0 +** 3.15-0 +** 4.4-0 +** 5.4-0 +*/ + +static int special_field( + ushort regno, + const MII_field_desc_t *pdesc, + ushort regval) +{ + if ((regno == MII_BMCR) && (pdesc->lo == 6)) { + ushort speed_bits = regval & (BMCR_SPEED1000 | BMCR_SPEED100); + printf("%2u,%2u = b%u%u speed selection = %s Mbps", + 6, 13, + (regval >> 6) & 1, + (regval >> 13) & 1, + speed_bits == BMCR_SPEED1000 ? "1000" : + speed_bits == BMCR_SPEED100 ? "100" : + "10"); + return 1; + } + + else if ((regno == MII_BMCR) && (pdesc->lo == 8)) { + printf("%2u = %5u duplex = %s", + pdesc->lo, + (regval >> pdesc->lo) & 1, + ((regval >> pdesc->lo) & 1) ? "full" : "half"); + return 1; + } + + else if ((regno == MII_ADVERTISE) && (pdesc->lo == 0)) { + ushort sel_bits = (regval >> pdesc->lo) & pdesc->mask; + printf("%2u-%2u = %5u selector = %s", + pdesc->hi, pdesc->lo, sel_bits, + sel_bits == PHY_ANLPAR_PSB_802_3 ? + "IEEE 802.3" : + sel_bits == PHY_ANLPAR_PSB_802_9 ? + "IEEE 802.9 ISLAN-16T" : + "???"); + return 1; + } + + else if ((regno == MII_LPA) && (pdesc->lo == 0)) { + ushort sel_bits = (regval >> pdesc->lo) & pdesc->mask; + printf("%2u-%2u = %u selector = %s", + pdesc->hi, pdesc->lo, sel_bits, + sel_bits == PHY_ANLPAR_PSB_802_3 ? + "IEEE 802.3" : + sel_bits == PHY_ANLPAR_PSB_802_9 ? + "IEEE 802.9 ISLAN-16T" : + "???"); + return 1; + } + + return 0; +} + +static char last_op[2]; +static uint last_data; +static uint last_addr_lo; +static uint last_addr_hi; +static uint last_reg_lo; +static uint last_reg_hi; + +static void extract_range( + char * input, + unsigned char * plo, + unsigned char * phi) +{ + char * end; + *plo = simple_strtoul(input, &end, 16); + if (*end == '-') { + end++; + *phi = simple_strtoul(end, NULL, 16); + } + else { + *phi = *plo; + } +} + +/* ---------------------------------------------------------------- */ +static int do_mii(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + char op[2]; + unsigned char addrlo, addrhi, reglo, reghi; + unsigned char addr, reg; + unsigned short data; + int rcode = 0; + const char *devname; + + if (argc < 2) + return CMD_RET_USAGE; + +#if defined(CONFIG_MII_INIT) + mii_init (); +#endif + + /* + * We use the last specified parameters, unless new ones are + * entered. + */ + op[0] = last_op[0]; + op[1] = last_op[1]; + addrlo = last_addr_lo; + addrhi = last_addr_hi; + reglo = last_reg_lo; + reghi = last_reg_hi; + data = last_data; + + if ((flag & CMD_FLAG_REPEAT) == 0) { + op[0] = argv[1][0]; + if (strlen(argv[1]) > 1) + op[1] = argv[1][1]; + else + op[1] = '\0'; + + if (argc >= 3) + extract_range(argv[2], &addrlo, &addrhi); + if (argc >= 4) + extract_range(argv[3], ®lo, ®hi); + if (argc >= 5) + data = simple_strtoul (argv[4], NULL, 16); + } + + /* use current device */ + devname = miiphy_get_current_dev(); + + /* + * check info/read/write. + */ + if (op[0] == 'i') { + unsigned char j, start, end; + unsigned int oui; + unsigned char model; + unsigned char rev; + + /* + * Look for any and all PHYs. Valid addresses are 0..31. + */ + if (argc >= 3) { + start = addrlo; end = addrhi; + } else { + start = 0; end = 31; + } + + for (j = start; j <= end; j++) { + if (miiphy_info (devname, j, &oui, &model, &rev) == 0) { + printf("PHY 0x%02X: " + "OUI = 0x%04X, " + "Model = 0x%02X, " + "Rev = 0x%02X, " + "%3dbase%s, %s\n", + j, oui, model, rev, + miiphy_speed (devname, j), + miiphy_is_1000base_x (devname, j) + ? "X" : "T", + (miiphy_duplex (devname, j) == FULL) + ? "FDX" : "HDX"); + } + } + } else if (op[0] == 'r') { + for (addr = addrlo; addr <= addrhi; addr++) { + for (reg = reglo; reg <= reghi; reg++) { + data = 0xffff; + if (miiphy_read (devname, addr, reg, &data) != 0) { + printf( + "Error reading from the PHY addr=%02x reg=%02x\n", + addr, reg); + rcode = 1; + } else { + if ((addrlo != addrhi) || (reglo != reghi)) + printf("addr=%02x reg=%02x data=", + (uint)addr, (uint)reg); + printf("%04X\n", data & 0x0000FFFF); + } + } + if ((addrlo != addrhi) && (reglo != reghi)) + printf("\n"); + } + } else if (op[0] == 'w') { + for (addr = addrlo; addr <= addrhi; addr++) { + for (reg = reglo; reg <= reghi; reg++) { + if (miiphy_write (devname, addr, reg, data) != 0) { + printf("Error writing to the PHY addr=%02x reg=%02x\n", + addr, reg); + rcode = 1; + } + } + } + } else if (strncmp(op, "du", 2) == 0) { + ushort regs[6]; + int ok = 1; + if ((reglo > 5) || (reghi > 5)) { + printf( + "The MII dump command only formats the " + "standard MII registers, 0-5.\n"); + return 1; + } + for (addr = addrlo; addr <= addrhi; addr++) { + for (reg = reglo; reg < reghi + 1; reg++) { + if (miiphy_read(devname, addr, reg, ®s[reg]) != 0) { + ok = 0; + printf( + "Error reading from the PHY addr=%02x reg=%02x\n", + addr, reg); + rcode = 1; + } + } + if (ok) + MII_dump_0_to_5(regs, reglo, reghi); + printf("\n"); + } + } else if (strncmp(op, "de", 2) == 0) { + if (argc == 2) + miiphy_listdev (); + else + miiphy_set_current_dev (argv[2]); + } else { + return CMD_RET_USAGE; + } + + /* + * Save the parameters for repeats. + */ + last_op[0] = op[0]; + last_op[1] = op[1]; + last_addr_lo = addrlo; + last_addr_hi = addrhi; + last_reg_lo = reglo; + last_reg_hi = reghi; + last_data = data; + + return rcode; +} + +/***************************************************/ + +U_BOOT_CMD( + mii, 5, 1, do_mii, + "MII utility commands", + "device - list available devices\n" + "mii device <devname> - set current device\n" + "mii info <addr> - display MII PHY info\n" + "mii read <addr> <reg> - read MII PHY <addr> register <reg>\n" + "mii write <addr> <reg> <data> - write MII PHY <addr> register <reg>\n" + "mii dump <addr> <reg> - pretty-print <addr> <reg> (0-5 only)\n" + "Addr and/or reg may be ranges, e.g. 2-7." +); diff --git a/qemu/roms/u-boot/common/cmd_misc.c b/qemu/roms/u-boot/common/cmd_misc.c new file mode 100644 index 000000000..93f9eabd6 --- /dev/null +++ b/qemu/roms/u-boot/common/cmd_misc.c @@ -0,0 +1,66 @@ +/* + * (C) Copyright 2001 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/* + * Misc functions + */ +#include <common.h> +#include <command.h> + +static int do_sleep(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + ulong start = get_timer(0); + ulong delay; + + if (argc != 2) + return CMD_RET_USAGE; + + delay = simple_strtoul(argv[1], NULL, 10) * CONFIG_SYS_HZ; + + while (get_timer(start) < delay) { + if (ctrlc()) + return (-1); + + udelay(100); + } + + return 0; +} + +U_BOOT_CMD( + sleep , 2, 1, do_sleep, + "delay execution for some time", + "N\n" + " - delay execution for N seconds (N is _decimal_ !!!)" +); + +#ifdef CONFIG_CMD_TIMER +static int do_timer(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + static ulong start; + + if (argc != 2) + return CMD_RET_USAGE; + + if (!strcmp(argv[1], "start")) + start = get_timer(0); + + if (!strcmp(argv[1], "get")) { + ulong msecs = get_timer(start) * 1000 / CONFIG_SYS_HZ; + printf("%ld.%03d\n", msecs / 1000, (int)(msecs % 1000)); + } + + return 0; +} + +U_BOOT_CMD( + timer, 2, 1, do_timer, + "access the system timer", + "start - Reset the timer reference.\n" + "timer get - Print the time since 'start'." +); +#endif diff --git a/qemu/roms/u-boot/common/cmd_mmc.c b/qemu/roms/u-boot/common/cmd_mmc.c new file mode 100644 index 000000000..c1916c9b5 --- /dev/null +++ b/qemu/roms/u-boot/common/cmd_mmc.c @@ -0,0 +1,479 @@ +/* + * (C) Copyright 2003 + * Kyle Harris, kharris@nexus-tech.net + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <command.h> +#include <mmc.h> + +static int curr_device = -1; +#ifndef CONFIG_GENERIC_MMC +int do_mmc (cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + int dev; + + if (argc < 2) + return CMD_RET_USAGE; + + if (strcmp(argv[1], "init") == 0) { + if (argc == 2) { + if (curr_device < 0) + dev = 1; + else + dev = curr_device; + } else if (argc == 3) { + dev = (int)simple_strtoul(argv[2], NULL, 10); + } else { + return CMD_RET_USAGE; + } + + if (mmc_legacy_init(dev) != 0) { + puts("No MMC card found\n"); + return 1; + } + + curr_device = dev; + printf("mmc%d is available\n", curr_device); + } else if (strcmp(argv[1], "device") == 0) { + if (argc == 2) { + if (curr_device < 0) { + puts("No MMC device available\n"); + return 1; + } + } else if (argc == 3) { + dev = (int)simple_strtoul(argv[2], NULL, 10); + +#ifdef CONFIG_SYS_MMC_SET_DEV + if (mmc_set_dev(dev) != 0) + return 1; +#endif + curr_device = dev; + } else { + return CMD_RET_USAGE; + } + + printf("mmc%d is current device\n", curr_device); + } else { + return CMD_RET_USAGE; + } + + return 0; +} + +U_BOOT_CMD( + mmc, 3, 1, do_mmc, + "MMC sub-system", + "init [dev] - init MMC sub system\n" + "mmc device [dev] - show or set current device" +); +#else /* !CONFIG_GENERIC_MMC */ + +enum mmc_state { + MMC_INVALID, + MMC_READ, + MMC_WRITE, + MMC_ERASE, +}; +static void print_mmcinfo(struct mmc *mmc) +{ + printf("Device: %s\n", mmc->cfg->name); + printf("Manufacturer ID: %x\n", mmc->cid[0] >> 24); + printf("OEM: %x\n", (mmc->cid[0] >> 8) & 0xffff); + printf("Name: %c%c%c%c%c \n", mmc->cid[0] & 0xff, + (mmc->cid[1] >> 24), (mmc->cid[1] >> 16) & 0xff, + (mmc->cid[1] >> 8) & 0xff, mmc->cid[1] & 0xff); + + printf("Tran Speed: %d\n", mmc->tran_speed); + printf("Rd Block Len: %d\n", mmc->read_bl_len); + + printf("%s version %d.%d\n", IS_SD(mmc) ? "SD" : "MMC", + (mmc->version >> 8) & 0xf, mmc->version & 0xff); + + printf("High Capacity: %s\n", mmc->high_capacity ? "Yes" : "No"); + puts("Capacity: "); + print_size(mmc->capacity, "\n"); + + printf("Bus Width: %d-bit\n", mmc->bus_width); +} + +static int do_mmcinfo(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + struct mmc *mmc; + + if (curr_device < 0) { + if (get_mmc_num() > 0) + curr_device = 0; + else { + puts("No MMC device available\n"); + return 1; + } + } + + mmc = find_mmc_device(curr_device); + + if (mmc) { + mmc_init(mmc); + + print_mmcinfo(mmc); + return 0; + } else { + printf("no mmc device at slot %x\n", curr_device); + return 1; + } +} + +U_BOOT_CMD( + mmcinfo, 1, 0, do_mmcinfo, + "display MMC info", + "- display info of the current MMC device" +); + +static int do_mmcops(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + enum mmc_state state; + + if (argc < 2) + return CMD_RET_USAGE; + + if (curr_device < 0) { + if (get_mmc_num() > 0) + curr_device = 0; + else { + puts("No MMC device available\n"); + return 1; + } + } + + if (strcmp(argv[1], "rescan") == 0) { + struct mmc *mmc; + + if (argc != 2) + return CMD_RET_USAGE; + + mmc = find_mmc_device(curr_device); + if (!mmc) { + printf("no mmc device at slot %x\n", curr_device); + return 1; + } + + mmc->has_init = 0; + + if (mmc_init(mmc)) + return 1; + else + return 0; + } else if (strcmp(argv[1], "part") == 0) { + block_dev_desc_t *mmc_dev; + struct mmc *mmc; + + if (argc != 2) + return CMD_RET_USAGE; + + mmc = find_mmc_device(curr_device); + if (!mmc) { + printf("no mmc device at slot %x\n", curr_device); + return 1; + } + mmc_init(mmc); + mmc_dev = mmc_get_dev(curr_device); + if (mmc_dev != NULL && + mmc_dev->type != DEV_TYPE_UNKNOWN) { + print_part(mmc_dev); + return 0; + } + + puts("get mmc type error!\n"); + return 1; + } else if (strcmp(argv[1], "list") == 0) { + if (argc != 2) + return CMD_RET_USAGE; + print_mmc_devices('\n'); + return 0; + } else if (strcmp(argv[1], "dev") == 0) { + int dev, part = -1; + struct mmc *mmc; + + if (argc == 2) + dev = curr_device; + else if (argc == 3) + dev = simple_strtoul(argv[2], NULL, 10); + else if (argc == 4) { + dev = (int)simple_strtoul(argv[2], NULL, 10); + part = (int)simple_strtoul(argv[3], NULL, 10); + if (part > PART_ACCESS_MASK) { + printf("#part_num shouldn't be larger" + " than %d\n", PART_ACCESS_MASK); + return 1; + } + } else + return CMD_RET_USAGE; + + mmc = find_mmc_device(dev); + if (!mmc) { + printf("no mmc device at slot %x\n", dev); + return 1; + } + + mmc_init(mmc); + if (part != -1) { + int ret; + if (mmc->part_config == MMCPART_NOAVAILABLE) { + printf("Card doesn't support part_switch\n"); + return 1; + } + + if (part != mmc->part_num) { + ret = mmc_switch_part(dev, part); + if (!ret) + mmc->part_num = part; + + printf("switch to partitions #%d, %s\n", + part, (!ret) ? "OK" : "ERROR"); + } + } + curr_device = dev; + if (mmc->part_config == MMCPART_NOAVAILABLE) + printf("mmc%d is current device\n", curr_device); + else + printf("mmc%d(part %d) is current device\n", + curr_device, mmc->part_num); + + return 0; +#ifdef CONFIG_SUPPORT_EMMC_BOOT + } else if (strcmp(argv[1], "partconf") == 0) { + int dev; + struct mmc *mmc; + u8 ack, part_num, access; + + if (argc == 6) { + dev = simple_strtoul(argv[2], NULL, 10); + ack = simple_strtoul(argv[3], NULL, 10); + part_num = simple_strtoul(argv[4], NULL, 10); + access = simple_strtoul(argv[5], NULL, 10); + } else { + return CMD_RET_USAGE; + } + + mmc = find_mmc_device(dev); + if (!mmc) { + printf("no mmc device at slot %x\n", dev); + return 1; + } + + if (IS_SD(mmc)) { + puts("PARTITION_CONFIG only exists on eMMC\n"); + return 1; + } + + /* acknowledge to be sent during boot operation */ + return mmc_set_part_conf(mmc, ack, part_num, access); + } else if (strcmp(argv[1], "bootbus") == 0) { + int dev; + struct mmc *mmc; + u8 width, reset, mode; + + if (argc == 6) { + dev = simple_strtoul(argv[2], NULL, 10); + width = simple_strtoul(argv[3], NULL, 10); + reset = simple_strtoul(argv[4], NULL, 10); + mode = simple_strtoul(argv[5], NULL, 10); + } else { + return CMD_RET_USAGE; + } + + mmc = find_mmc_device(dev); + if (!mmc) { + printf("no mmc device at slot %x\n", dev); + return 1; + } + + if (IS_SD(mmc)) { + puts("BOOT_BUS_WIDTH only exists on eMMC\n"); + return 1; + } + + /* acknowledge to be sent during boot operation */ + return mmc_set_boot_bus_width(mmc, width, reset, mode); + } else if (strcmp(argv[1], "bootpart-resize") == 0) { + int dev; + struct mmc *mmc; + u32 bootsize, rpmbsize; + + if (argc == 5) { + dev = simple_strtoul(argv[2], NULL, 10); + bootsize = simple_strtoul(argv[3], NULL, 10); + rpmbsize = simple_strtoul(argv[4], NULL, 10); + } else { + return CMD_RET_USAGE; + } + + mmc = find_mmc_device(dev); + if (!mmc) { + printf("no mmc device at slot %x\n", dev); + return 1; + } + + if (IS_SD(mmc)) { + printf("It is not a EMMC device\n"); + return 1; + } + + if (0 == mmc_boot_partition_size_change(mmc, + bootsize, rpmbsize)) { + printf("EMMC boot partition Size %d MB\n", bootsize); + printf("EMMC RPMB partition Size %d MB\n", rpmbsize); + return 0; + } else { + printf("EMMC boot partition Size change Failed.\n"); + return 1; + } + } else if (strcmp(argv[1], "rst-function") == 0) { + /* + * Set the RST_n_ENABLE bit of RST_n_FUNCTION + * The only valid values are 0x0, 0x1 and 0x2 and writing + * a value of 0x1 or 0x2 sets the value permanently. + */ + int dev; + struct mmc *mmc; + u8 enable; + + if (argc == 4) { + dev = simple_strtoul(argv[2], NULL, 10); + enable = simple_strtoul(argv[3], NULL, 10); + } else { + return CMD_RET_USAGE; + } + + if (enable > 2 || enable < 0) { + puts("Invalid RST_n_ENABLE value\n"); + return CMD_RET_USAGE; + } + + mmc = find_mmc_device(dev); + if (!mmc) { + printf("no mmc device at slot %x\n", dev); + return 1; + } + + if (IS_SD(mmc)) { + puts("RST_n_FUNCTION only exists on eMMC\n"); + return 1; + } + + return mmc_set_rst_n_function(mmc, enable); +#endif /* CONFIG_SUPPORT_EMMC_BOOT */ + } + + else if (argc == 3 && strcmp(argv[1], "setdsr") == 0) { + struct mmc *mmc = find_mmc_device(curr_device); + u32 val = simple_strtoul(argv[2], NULL, 16); + int ret; + + if (!mmc) { + printf("no mmc device at slot %x\n", curr_device); + return 1; + } + ret = mmc_set_dsr(mmc, val); + printf("set dsr %s\n", (!ret) ? "OK, force rescan" : "ERROR"); + if (!ret) { + mmc->has_init = 0; + if (mmc_init(mmc)) + return 1; + else + return 0; + } + return ret; + } + + state = MMC_INVALID; + if (argc == 5 && strcmp(argv[1], "read") == 0) + state = MMC_READ; + else if (argc == 5 && strcmp(argv[1], "write") == 0) + state = MMC_WRITE; + else if (argc == 4 && strcmp(argv[1], "erase") == 0) + state = MMC_ERASE; + + if (state != MMC_INVALID) { + struct mmc *mmc = find_mmc_device(curr_device); + int idx = 2; + u32 blk, cnt, n; + void *addr; + + if (state != MMC_ERASE) { + addr = (void *)simple_strtoul(argv[idx], NULL, 16); + ++idx; + } else + addr = NULL; + blk = simple_strtoul(argv[idx], NULL, 16); + cnt = simple_strtoul(argv[idx + 1], NULL, 16); + + if (!mmc) { + printf("no mmc device at slot %x\n", curr_device); + return 1; + } + + printf("\nMMC %s: dev # %d, block # %d, count %d ... ", + argv[1], curr_device, blk, cnt); + + mmc_init(mmc); + + if ((state == MMC_WRITE || state == MMC_ERASE)) { + if (mmc_getwp(mmc) == 1) { + printf("Error: card is write protected!\n"); + return 1; + } + } + + switch (state) { + case MMC_READ: + n = mmc->block_dev.block_read(curr_device, blk, + cnt, addr); + /* flush cache after read */ + flush_cache((ulong)addr, cnt * 512); /* FIXME */ + break; + case MMC_WRITE: + n = mmc->block_dev.block_write(curr_device, blk, + cnt, addr); + break; + case MMC_ERASE: + n = mmc->block_dev.block_erase(curr_device, blk, cnt); + break; + default: + BUG(); + } + + printf("%d blocks %s: %s\n", + n, argv[1], (n == cnt) ? "OK" : "ERROR"); + return (n == cnt) ? 0 : 1; + } + + return CMD_RET_USAGE; +} + +U_BOOT_CMD( + mmc, 6, 1, do_mmcops, + "MMC sub system", + "read addr blk# cnt\n" + "mmc write addr blk# cnt\n" + "mmc erase blk# cnt\n" + "mmc rescan\n" + "mmc part - lists available partition on current mmc device\n" + "mmc dev [dev] [part] - show or set current mmc device [partition]\n" + "mmc list - lists available devices\n" +#ifdef CONFIG_SUPPORT_EMMC_BOOT + "mmc bootbus dev boot_bus_width reset_boot_bus_width boot_mode\n" + " - Set the BOOT_BUS_WIDTH field of the specified device\n" + "mmc bootpart-resize <dev> <boot part size MB> <RPMB part size MB>\n" + " - Change sizes of boot and RPMB partitions of specified device\n" + "mmc partconf dev boot_ack boot_partition partition_access\n" + " - Change the bits of the PARTITION_CONFIG field of the specified device\n" + "mmc rst-function dev value\n" + " - Change the RST_n_FUNCTION field of the specified device\n" + " WARNING: This is a write-once field and 0 / 1 / 2 are the only valid values.\n" +#endif + "mmc setdsr - set DSR register value\n" + ); +#endif /* !CONFIG_GENERIC_MMC */ diff --git a/qemu/roms/u-boot/common/cmd_mmc_spi.c b/qemu/roms/u-boot/common/cmd_mmc_spi.c new file mode 100644 index 000000000..a2138b865 --- /dev/null +++ b/qemu/roms/u-boot/common/cmd_mmc_spi.c @@ -0,0 +1,88 @@ +/* + * Command for mmc_spi setup. + * + * Copyright (C) 2010 Thomas Chou <thomas@wytron.com.tw> + * Licensed under the GPL-2 or later. + */ + +#include <common.h> +#include <mmc.h> +#include <spi.h> + +#ifndef CONFIG_MMC_SPI_BUS +# define CONFIG_MMC_SPI_BUS 0 +#endif +#ifndef CONFIG_MMC_SPI_CS +# define CONFIG_MMC_SPI_CS 1 +#endif +/* in SPI mode, MMC speed limit is 20MHz, while SD speed limit is 25MHz */ +#ifndef CONFIG_MMC_SPI_SPEED +# define CONFIG_MMC_SPI_SPEED 25000000 +#endif +/* MMC and SD specs only seem to care that sampling is on the + * rising edge ... meaning SPI modes 0 or 3. So either SPI mode + * should be legit. We'll use mode 0 since the steady state is 0, + * which is appropriate for hotplugging, unless the platform data + * specify mode 3 (if hardware is not compatible to mode 0). + */ +#ifndef CONFIG_MMC_SPI_MODE +# define CONFIG_MMC_SPI_MODE SPI_MODE_0 +#endif + +static int do_mmc_spi(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + uint bus = CONFIG_MMC_SPI_BUS; + uint cs = CONFIG_MMC_SPI_CS; + uint speed = CONFIG_MMC_SPI_SPEED; + uint mode = CONFIG_MMC_SPI_MODE; + char *endp; + struct mmc *mmc; + + if (argc < 2) + goto usage; + + cs = simple_strtoul(argv[1], &endp, 0); + if (*argv[1] == 0 || (*endp != 0 && *endp != ':')) + goto usage; + if (*endp == ':') { + if (endp[1] == 0) + goto usage; + bus = cs; + cs = simple_strtoul(endp + 1, &endp, 0); + if (*endp != 0) + goto usage; + } + if (argc >= 3) { + speed = simple_strtoul(argv[2], &endp, 0); + if (*argv[2] == 0 || *endp != 0) + goto usage; + } + if (argc >= 4) { + mode = simple_strtoul(argv[3], &endp, 16); + if (*argv[3] == 0 || *endp != 0) + goto usage; + } + if (!spi_cs_is_valid(bus, cs)) { + printf("Invalid SPI bus %u cs %u\n", bus, cs); + return 1; + } + + mmc = mmc_spi_init(bus, cs, speed, mode); + if (!mmc) { + printf("Failed to create MMC Device\n"); + return 1; + } + printf("%s: %d at %u:%u hz %u mode %u\n", mmc->cfg->name, mmc->block_dev.dev, + bus, cs, speed, mode); + mmc_init(mmc); + return 0; + +usage: + return CMD_RET_USAGE; +} + +U_BOOT_CMD( + mmc_spi, 4, 0, do_mmc_spi, + "mmc_spi setup", + "[bus:]cs [hz] [mode] - setup mmc_spi device" +); diff --git a/qemu/roms/u-boot/common/cmd_mp.c b/qemu/roms/u-boot/common/cmd_mp.c new file mode 100644 index 000000000..328b33806 --- /dev/null +++ b/qemu/roms/u-boot/common/cmd_mp.c @@ -0,0 +1,73 @@ +/* + * Copyright 2008-2009 Freescale Semiconductor, Inc. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <command.h> + +static int +cpu_cmd(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + unsigned long cpuid; + + if (argc < 3) + return CMD_RET_USAGE; + + cpuid = simple_strtoul(argv[1], NULL, 10); + if (!is_core_valid(cpuid)) { + printf ("Core num: %lu is not valid\n", cpuid); + return 1; + } + + + if (argc == 3) { + if (strncmp(argv[2], "reset", 5) == 0) + cpu_reset(cpuid); + else if (strncmp(argv[2], "status", 6) == 0) + cpu_status(cpuid); + else if (strncmp(argv[2], "disable", 7) == 0) + return cpu_disable(cpuid); + else + return CMD_RET_USAGE; + + return 0; + } + + /* 4 or greater, make sure its release */ + if (strncmp(argv[2], "release", 7) != 0) + return CMD_RET_USAGE; + + if (cpu_release(cpuid, argc - 3, argv + 3)) + return CMD_RET_USAGE; + + return 0; +} + +#ifdef CONFIG_SYS_LONGHELP +static char cpu_help_text[] = + "<num> reset - Reset cpu <num>\n" + "cpu <num> status - Status of cpu <num>\n" + "cpu <num> disable - Disable cpu <num>\n" + "cpu <num> release <addr> [args] - Release cpu <num> at <addr> with [args]" +#ifdef CONFIG_PPC + "\n" + " [args] : <pir> <r3> <r6>\n" \ + " pir - processor id (if writeable)\n" \ + " r3 - value for gpr 3\n" \ + " r6 - value for gpr 6\n" \ + "\n" \ + " Use '-' for any arg if you want the default value.\n" \ + " Default for r3 is <num> and r6 is 0\n" \ + "\n" \ + " When cpu <num> is released r4 and r5 = 0.\n" \ + " r7 will contain the size of the initial mapped area" +#endif + ""; +#endif + +U_BOOT_CMD( + cpu, CONFIG_SYS_MAXARGS, 1, cpu_cmd, + "Multiprocessor CPU boot manipulation and release", cpu_help_text +); diff --git a/qemu/roms/u-boot/common/cmd_mtdparts.c b/qemu/roms/u-boot/common/cmd_mtdparts.c new file mode 100644 index 000000000..40b6333eb --- /dev/null +++ b/qemu/roms/u-boot/common/cmd_mtdparts.c @@ -0,0 +1,2105 @@ +/* + * (C) Copyright 2002 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * (C) Copyright 2002 + * Robert Schwebel, Pengutronix, <r.schwebel@pengutronix.de> + * + * (C) Copyright 2003 + * Kai-Uwe Bloem, Auerswald GmbH & Co KG, <linux-development@auerswald.de> + * + * (C) Copyright 2005 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * Added support for reading flash partition table from environment. + * Parsing routines are based on driver/mtd/cmdline.c from the linux 2.4 + * kernel tree. + * + * (C) Copyright 2008 + * Harald Welte, OpenMoko, Inc., Harald Welte <laforge@openmoko.org> + * + * $Id: cmdlinepart.c,v 1.17 2004/11/26 11:18:47 lavinen Exp $ + * Copyright 2002 SYSGO Real-Time Solutions GmbH + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/* + * Three environment variables are used by the parsing routines: + * + * 'partition' - keeps current partition identifier + * + * partition := <part-id> + * <part-id> := <dev-id>,part_num + * + * + * 'mtdids' - linux kernel mtd device id <-> u-boot device id mapping + * + * mtdids=<idmap>[,<idmap>,...] + * + * <idmap> := <dev-id>=<mtd-id> + * <dev-id> := 'nand'|'nor'|'onenand'<dev-num> + * <dev-num> := mtd device number, 0... + * <mtd-id> := unique device tag used by linux kernel to find mtd device (mtd->name) + * + * + * 'mtdparts' - partition list + * + * mtdparts=mtdparts=<mtd-def>[;<mtd-def>...] + * + * <mtd-def> := <mtd-id>:<part-def>[,<part-def>...] + * <mtd-id> := unique device tag used by linux kernel to find mtd device (mtd->name) + * <part-def> := <size>[@<offset>][<name>][<ro-flag>] + * <size> := standard linux memsize OR '-' to denote all remaining space + * <offset> := partition start offset within the device + * <name> := '(' NAME ')' + * <ro-flag> := when set to 'ro' makes partition read-only (not used, passed to kernel) + * + * Notes: + * - each <mtd-id> used in mtdparts must albo exist in 'mtddis' mapping + * - if the above variables are not set defaults for a given target are used + * + * Examples: + * + * 1 NOR Flash, with 1 single writable partition: + * mtdids=nor0=edb7312-nor + * mtdparts=mtdparts=edb7312-nor:- + * + * 1 NOR Flash with 2 partitions, 1 NAND with one + * mtdids=nor0=edb7312-nor,nand0=edb7312-nand + * mtdparts=mtdparts=edb7312-nor:256k(ARMboot)ro,-(root);edb7312-nand:-(home) + * + */ + +#include <common.h> +#include <command.h> +#include <malloc.h> +#include <jffs2/load_kernel.h> +#include <linux/list.h> +#include <linux/ctype.h> +#include <linux/err.h> +#include <linux/mtd/mtd.h> + +#if defined(CONFIG_CMD_NAND) +#include <linux/mtd/nand.h> +#include <nand.h> +#endif + +#if defined(CONFIG_CMD_ONENAND) +#include <linux/mtd/onenand.h> +#include <onenand_uboot.h> +#endif + +DECLARE_GLOBAL_DATA_PTR; + +/* special size referring to all the remaining space in a partition */ +#define SIZE_REMAINING (~0llu) + +/* special offset value, it is used when not provided by user + * + * this value is used temporarily during parsing, later such offests + * are recalculated */ +#define OFFSET_NOT_SPECIFIED (~0llu) + +/* minimum partition size */ +#define MIN_PART_SIZE 4096 + +/* this flag needs to be set in part_info struct mask_flags + * field for read-only partitions */ +#define MTD_WRITEABLE_CMD 1 + +/* default values for mtdids and mtdparts variables */ +#if defined(MTDIDS_DEFAULT) +static const char *const mtdids_default = MTDIDS_DEFAULT; +#else +static const char *const mtdids_default = NULL; +#endif + +#if defined(MTDPARTS_DEFAULT) +static const char *const mtdparts_default = MTDPARTS_DEFAULT; +#else +static const char *const mtdparts_default = NULL; +#endif + +/* copies of last seen 'mtdids', 'mtdparts' and 'partition' env variables */ +#define MTDIDS_MAXLEN 128 +#define MTDPARTS_MAXLEN 512 +#define PARTITION_MAXLEN 16 +static char last_ids[MTDIDS_MAXLEN]; +static char last_parts[MTDPARTS_MAXLEN]; +static char last_partition[PARTITION_MAXLEN]; + +/* low level jffs2 cache cleaning routine */ +extern void jffs2_free_cache(struct part_info *part); + +/* mtdids mapping list, filled by parse_ids() */ +static struct list_head mtdids; + +/* device/partition list, parse_cmdline() parses into here */ +static struct list_head devices; + +/* current active device and partition number */ +struct mtd_device *current_mtd_dev = NULL; +u8 current_mtd_partnum = 0; + +static struct part_info* mtd_part_info(struct mtd_device *dev, unsigned int part_num); + +/* command line only routines */ +static struct mtdids* id_find_by_mtd_id(const char *mtd_id, unsigned int mtd_id_len); +static int device_del(struct mtd_device *dev); + +/** + * Parses a string into a number. The number stored at ptr is + * potentially suffixed with K (for kilobytes, or 1024 bytes), + * M (for megabytes, or 1048576 bytes), or G (for gigabytes, or + * 1073741824). If the number is suffixed with K, M, or G, then + * the return value is the number multiplied by one kilobyte, one + * megabyte, or one gigabyte, respectively. + * + * @param ptr where parse begins + * @param retptr output pointer to next char after parse completes (output) + * @return resulting unsigned int + */ +static u64 memsize_parse (const char *const ptr, const char **retptr) +{ + u64 ret = simple_strtoull(ptr, (char **)retptr, 0); + + switch (**retptr) { + case 'G': + case 'g': + ret <<= 10; + case 'M': + case 'm': + ret <<= 10; + case 'K': + case 'k': + ret <<= 10; + (*retptr)++; + default: + break; + } + + return ret; +} + +/** + * Format string describing supplied size. This routine does the opposite job + * to memsize_parse(). Size in bytes is converted to string and if possible + * shortened by using k (kilobytes), m (megabytes) or g (gigabytes) suffix. + * + * Note, that this routine does not check for buffer overflow, it's the caller + * who must assure enough space. + * + * @param buf output buffer + * @param size size to be converted to string + */ +static void memsize_format(char *buf, u64 size) +{ +#define SIZE_GB ((u32)1024*1024*1024) +#define SIZE_MB ((u32)1024*1024) +#define SIZE_KB ((u32)1024) + + if ((size % SIZE_GB) == 0) + sprintf(buf, "%llug", size/SIZE_GB); + else if ((size % SIZE_MB) == 0) + sprintf(buf, "%llum", size/SIZE_MB); + else if (size % SIZE_KB == 0) + sprintf(buf, "%lluk", size/SIZE_KB); + else + sprintf(buf, "%llu", size); +} + +/** + * This routine does global indexing of all partitions. Resulting index for + * current partition is saved in 'mtddevnum'. Current partition name in + * 'mtddevname'. + */ +static void index_partitions(void) +{ + u16 mtddevnum; + struct part_info *part; + struct list_head *dentry; + struct mtd_device *dev; + + debug("--- index partitions ---\n"); + + if (current_mtd_dev) { + mtddevnum = 0; + list_for_each(dentry, &devices) { + dev = list_entry(dentry, struct mtd_device, link); + if (dev == current_mtd_dev) { + mtddevnum += current_mtd_partnum; + setenv_ulong("mtddevnum", mtddevnum); + break; + } + mtddevnum += dev->num_parts; + } + + part = mtd_part_info(current_mtd_dev, current_mtd_partnum); + setenv("mtddevname", part->name); + + debug("=> mtddevnum %d,\n=> mtddevname %s\n", mtddevnum, part->name); + } else { + setenv("mtddevnum", NULL); + setenv("mtddevname", NULL); + + debug("=> mtddevnum NULL\n=> mtddevname NULL\n"); + } +} + +/** + * Save current device and partition in environment variable 'partition'. + */ +static void current_save(void) +{ + char buf[16]; + + debug("--- current_save ---\n"); + + if (current_mtd_dev) { + sprintf(buf, "%s%d,%d", MTD_DEV_TYPE(current_mtd_dev->id->type), + current_mtd_dev->id->num, current_mtd_partnum); + + setenv("partition", buf); + strncpy(last_partition, buf, 16); + + debug("=> partition %s\n", buf); + } else { + setenv("partition", NULL); + last_partition[0] = '\0'; + + debug("=> partition NULL\n"); + } + index_partitions(); +} + + +/** + * Produce a mtd_info given a type and num. + * + * @param type mtd type + * @param num mtd number + * @param mtd a pointer to an mtd_info instance (output) + * @return 0 if device is valid, 1 otherwise + */ +static int get_mtd_info(u8 type, u8 num, struct mtd_info **mtd) +{ + char mtd_dev[16]; + + sprintf(mtd_dev, "%s%d", MTD_DEV_TYPE(type), num); + *mtd = get_mtd_device_nm(mtd_dev); + if (IS_ERR(*mtd)) { + printf("Device %s not found!\n", mtd_dev); + return 1; + } + + return 0; +} + +/** + * Performs sanity check for supplied flash partition. + * Table of existing MTD flash devices is searched and partition device + * is located. Alignment with the granularity of nand erasesize is verified. + * + * @param id of the parent device + * @param part partition to validate + * @return 0 if partition is valid, 1 otherwise + */ +static int part_validate_eraseblock(struct mtdids *id, struct part_info *part) +{ + struct mtd_info *mtd = NULL; + int i, j; + ulong start; + u64 offset, size; + + if (get_mtd_info(id->type, id->num, &mtd)) + return 1; + + part->sector_size = mtd->erasesize; + + if (!mtd->numeraseregions) { + /* + * Only one eraseregion (NAND, OneNAND or uniform NOR), + * checking for alignment is easy here + */ + offset = part->offset; + if (do_div(offset, mtd->erasesize)) { + printf("%s%d: partition (%s) start offset" + "alignment incorrect\n", + MTD_DEV_TYPE(id->type), id->num, part->name); + return 1; + } + + size = part->size; + if (do_div(size, mtd->erasesize)) { + printf("%s%d: partition (%s) size alignment incorrect\n", + MTD_DEV_TYPE(id->type), id->num, part->name); + return 1; + } + } else { + /* + * Multiple eraseregions (non-uniform NOR), + * checking for alignment is more complex here + */ + + /* Check start alignment */ + for (i = 0; i < mtd->numeraseregions; i++) { + start = mtd->eraseregions[i].offset; + for (j = 0; j < mtd->eraseregions[i].numblocks; j++) { + if (part->offset == start) + goto start_ok; + start += mtd->eraseregions[i].erasesize; + } + } + + printf("%s%d: partition (%s) start offset alignment incorrect\n", + MTD_DEV_TYPE(id->type), id->num, part->name); + return 1; + + start_ok: + + /* Check end/size alignment */ + for (i = 0; i < mtd->numeraseregions; i++) { + start = mtd->eraseregions[i].offset; + for (j = 0; j < mtd->eraseregions[i].numblocks; j++) { + if ((part->offset + part->size) == start) + goto end_ok; + start += mtd->eraseregions[i].erasesize; + } + } + /* Check last sector alignment */ + if ((part->offset + part->size) == start) + goto end_ok; + + printf("%s%d: partition (%s) size alignment incorrect\n", + MTD_DEV_TYPE(id->type), id->num, part->name); + return 1; + + end_ok: + return 0; + } + + return 0; +} + + +/** + * Performs sanity check for supplied partition. Offset and size are + * verified to be within valid range. Partition type is checked and + * part_validate_eraseblock() is called with the argument of part. + * + * @param id of the parent device + * @param part partition to validate + * @return 0 if partition is valid, 1 otherwise + */ +static int part_validate(struct mtdids *id, struct part_info *part) +{ + if (part->size == SIZE_REMAINING) + part->size = id->size - part->offset; + + if (part->offset > id->size) { + printf("%s: offset %08llx beyond flash size %08llx\n", + id->mtd_id, part->offset, id->size); + return 1; + } + + if ((part->offset + part->size) <= part->offset) { + printf("%s%d: partition (%s) size too big\n", + MTD_DEV_TYPE(id->type), id->num, part->name); + return 1; + } + + if (part->offset + part->size > id->size) { + printf("%s: partitioning exceeds flash size\n", id->mtd_id); + return 1; + } + + /* + * Now we need to check if the partition starts and ends on + * sector (eraseblock) regions + */ + return part_validate_eraseblock(id, part); +} + +/** + * Delete selected partition from the partition list of the specified device. + * + * @param dev device to delete partition from + * @param part partition to delete + * @return 0 on success, 1 otherwise + */ +static int part_del(struct mtd_device *dev, struct part_info *part) +{ + u8 current_save_needed = 0; + + /* if there is only one partition, remove whole device */ + if (dev->num_parts == 1) + return device_del(dev); + + /* otherwise just delete this partition */ + + if (dev == current_mtd_dev) { + /* we are modyfing partitions for the current device, + * update current */ + struct part_info *curr_pi; + curr_pi = mtd_part_info(current_mtd_dev, current_mtd_partnum); + + if (curr_pi) { + if (curr_pi == part) { + printf("current partition deleted, resetting current to 0\n"); + current_mtd_partnum = 0; + } else if (part->offset <= curr_pi->offset) { + current_mtd_partnum--; + } + current_save_needed = 1; + } + } + + list_del(&part->link); + free(part); + dev->num_parts--; + + if (current_save_needed > 0) + current_save(); + else + index_partitions(); + + return 0; +} + +/** + * Delete all partitions from parts head list, free memory. + * + * @param head list of partitions to delete + */ +static void part_delall(struct list_head *head) +{ + struct list_head *entry, *n; + struct part_info *part_tmp; + + /* clean tmp_list and free allocated memory */ + list_for_each_safe(entry, n, head) { + part_tmp = list_entry(entry, struct part_info, link); + + list_del(entry); + free(part_tmp); + } +} + +/** + * Add new partition to the supplied partition list. Make sure partitions are + * sorted by offset in ascending order. + * + * @param head list this partition is to be added to + * @param new partition to be added + */ +static int part_sort_add(struct mtd_device *dev, struct part_info *part) +{ + struct list_head *entry; + struct part_info *new_pi, *curr_pi; + + /* link partition to parrent dev */ + part->dev = dev; + + if (list_empty(&dev->parts)) { + debug("part_sort_add: list empty\n"); + list_add(&part->link, &dev->parts); + dev->num_parts++; + index_partitions(); + return 0; + } + + new_pi = list_entry(&part->link, struct part_info, link); + + /* get current partition info if we are updating current device */ + curr_pi = NULL; + if (dev == current_mtd_dev) + curr_pi = mtd_part_info(current_mtd_dev, current_mtd_partnum); + + list_for_each(entry, &dev->parts) { + struct part_info *pi; + + pi = list_entry(entry, struct part_info, link); + + /* be compliant with kernel cmdline, allow only one partition at offset zero */ + if ((new_pi->offset == pi->offset) && (pi->offset == 0)) { + printf("cannot add second partition at offset 0\n"); + return 1; + } + + if (new_pi->offset <= pi->offset) { + list_add_tail(&part->link, entry); + dev->num_parts++; + + if (curr_pi && (pi->offset <= curr_pi->offset)) { + /* we are modyfing partitions for the current + * device, update current */ + current_mtd_partnum++; + current_save(); + } else { + index_partitions(); + } + return 0; + } + } + + list_add_tail(&part->link, &dev->parts); + dev->num_parts++; + index_partitions(); + return 0; +} + +/** + * Add provided partition to the partition list of a given device. + * + * @param dev device to which partition is added + * @param part partition to be added + * @return 0 on success, 1 otherwise + */ +static int part_add(struct mtd_device *dev, struct part_info *part) +{ + /* verify alignment and size */ + if (part_validate(dev->id, part) != 0) + return 1; + + /* partition is ok, add it to the list */ + if (part_sort_add(dev, part) != 0) + return 1; + + return 0; +} + +/** + * Parse one partition definition, allocate memory and return pointer to this + * location in retpart. + * + * @param partdef pointer to the partition definition string i.e. <part-def> + * @param ret output pointer to next char after parse completes (output) + * @param retpart pointer to the allocated partition (output) + * @return 0 on success, 1 otherwise + */ +static int part_parse(const char *const partdef, const char **ret, struct part_info **retpart) +{ + struct part_info *part; + u64 size; + u64 offset; + const char *name; + int name_len; + unsigned int mask_flags; + const char *p; + + p = partdef; + *retpart = NULL; + *ret = NULL; + + /* fetch the partition size */ + if (*p == '-') { + /* assign all remaining space to this partition */ + debug("'-': remaining size assigned\n"); + size = SIZE_REMAINING; + p++; + } else { + size = memsize_parse(p, &p); + if (size < MIN_PART_SIZE) { + printf("partition size too small (%llx)\n", size); + return 1; + } + } + + /* check for offset */ + offset = OFFSET_NOT_SPECIFIED; + if (*p == '@') { + p++; + offset = memsize_parse(p, &p); + } + + /* now look for the name */ + if (*p == '(') { + name = ++p; + if ((p = strchr(name, ')')) == NULL) { + printf("no closing ) found in partition name\n"); + return 1; + } + name_len = p - name + 1; + if ((name_len - 1) == 0) { + printf("empty partition name\n"); + return 1; + } + p++; + } else { + /* 0x00000000@0x00000000 */ + name_len = 22; + name = NULL; + } + + /* test for options */ + mask_flags = 0; + if (strncmp(p, "ro", 2) == 0) { + mask_flags |= MTD_WRITEABLE_CMD; + p += 2; + } + + /* check for next partition definition */ + if (*p == ',') { + if (size == SIZE_REMAINING) { + *ret = NULL; + printf("no partitions allowed after a fill-up partition\n"); + return 1; + } + *ret = ++p; + } else if ((*p == ';') || (*p == '\0')) { + *ret = p; + } else { + printf("unexpected character '%c' at the end of partition\n", *p); + *ret = NULL; + return 1; + } + + /* allocate memory */ + part = (struct part_info *)malloc(sizeof(struct part_info) + name_len); + if (!part) { + printf("out of memory\n"); + return 1; + } + memset(part, 0, sizeof(struct part_info) + name_len); + part->size = size; + part->offset = offset; + part->mask_flags = mask_flags; + part->name = (char *)(part + 1); + + if (name) { + /* copy user provided name */ + strncpy(part->name, name, name_len - 1); + part->auto_name = 0; + } else { + /* auto generated name in form of size@offset */ + sprintf(part->name, "0x%08llx@0x%08llx", size, offset); + part->auto_name = 1; + } + + part->name[name_len - 1] = '\0'; + INIT_LIST_HEAD(&part->link); + + debug("+ partition: name %-22s size 0x%08llx offset 0x%08llx mask flags %d\n", + part->name, part->size, + part->offset, part->mask_flags); + + *retpart = part; + return 0; +} + +/** + * Check device number to be within valid range for given device type. + * + * @param type mtd type + * @param num mtd number + * @param size a pointer to the size of the mtd device (output) + * @return 0 if device is valid, 1 otherwise + */ +static int mtd_device_validate(u8 type, u8 num, u64 *size) +{ + struct mtd_info *mtd = NULL; + + if (get_mtd_info(type, num, &mtd)) + return 1; + + *size = mtd->size; + + return 0; +} + +/** + * Delete all mtd devices from a supplied devices list, free memory allocated for + * each device and delete all device partitions. + * + * @return 0 on success, 1 otherwise + */ +static int device_delall(struct list_head *head) +{ + struct list_head *entry, *n; + struct mtd_device *dev_tmp; + + /* clean devices list */ + list_for_each_safe(entry, n, head) { + dev_tmp = list_entry(entry, struct mtd_device, link); + list_del(entry); + part_delall(&dev_tmp->parts); + free(dev_tmp); + } + INIT_LIST_HEAD(&devices); + + return 0; +} + +/** + * If provided device exists it's partitions are deleted, device is removed + * from device list and device memory is freed. + * + * @param dev device to be deleted + * @return 0 on success, 1 otherwise + */ +static int device_del(struct mtd_device *dev) +{ + part_delall(&dev->parts); + list_del(&dev->link); + free(dev); + + if (dev == current_mtd_dev) { + /* we just deleted current device */ + if (list_empty(&devices)) { + current_mtd_dev = NULL; + } else { + /* reset first partition from first dev from the + * devices list as current */ + current_mtd_dev = list_entry(devices.next, struct mtd_device, link); + current_mtd_partnum = 0; + } + current_save(); + return 0; + } + + index_partitions(); + return 0; +} + +/** + * Search global device list and return pointer to the device of type and num + * specified. + * + * @param type device type + * @param num device number + * @return NULL if requested device does not exist + */ +struct mtd_device *device_find(u8 type, u8 num) +{ + struct list_head *entry; + struct mtd_device *dev_tmp; + + list_for_each(entry, &devices) { + dev_tmp = list_entry(entry, struct mtd_device, link); + + if ((dev_tmp->id->type == type) && (dev_tmp->id->num == num)) + return dev_tmp; + } + + return NULL; +} + +/** + * Add specified device to the global device list. + * + * @param dev device to be added + */ +static void device_add(struct mtd_device *dev) +{ + u8 current_save_needed = 0; + + if (list_empty(&devices)) { + current_mtd_dev = dev; + current_mtd_partnum = 0; + current_save_needed = 1; + } + + list_add_tail(&dev->link, &devices); + + if (current_save_needed > 0) + current_save(); + else + index_partitions(); +} + +/** + * Parse device type, name and mtd-id. If syntax is ok allocate memory and + * return pointer to the device structure. + * + * @param mtd_dev pointer to the device definition string i.e. <mtd-dev> + * @param ret output pointer to next char after parse completes (output) + * @param retdev pointer to the allocated device (output) + * @return 0 on success, 1 otherwise + */ +static int device_parse(const char *const mtd_dev, const char **ret, struct mtd_device **retdev) +{ + struct mtd_device *dev; + struct part_info *part; + struct mtdids *id; + const char *mtd_id; + unsigned int mtd_id_len; + const char *p; + const char *pend; + LIST_HEAD(tmp_list); + struct list_head *entry, *n; + u16 num_parts; + u64 offset; + int err = 1; + + debug("===device_parse===\n"); + + assert(retdev); + *retdev = NULL; + + if (ret) + *ret = NULL; + + /* fetch <mtd-id> */ + mtd_id = p = mtd_dev; + if (!(p = strchr(mtd_id, ':'))) { + printf("no <mtd-id> identifier\n"); + return 1; + } + mtd_id_len = p - mtd_id + 1; + p++; + + /* verify if we have a valid device specified */ + if ((id = id_find_by_mtd_id(mtd_id, mtd_id_len - 1)) == NULL) { + printf("invalid mtd device '%.*s'\n", mtd_id_len - 1, mtd_id); + return 1; + } + +#ifdef DEBUG + pend = strchr(p, ';'); +#endif + debug("dev type = %d (%s), dev num = %d, mtd-id = %s\n", + id->type, MTD_DEV_TYPE(id->type), + id->num, id->mtd_id); + debug("parsing partitions %.*s\n", (pend ? pend - p : strlen(p)), p); + + + /* parse partitions */ + num_parts = 0; + + offset = 0; + if ((dev = device_find(id->type, id->num)) != NULL) { + /* if device already exists start at the end of the last partition */ + part = list_entry(dev->parts.prev, struct part_info, link); + offset = part->offset + part->size; + } + + while (p && (*p != '\0') && (*p != ';')) { + err = 1; + if ((part_parse(p, &p, &part) != 0) || (!part)) + break; + + /* calculate offset when not specified */ + if (part->offset == OFFSET_NOT_SPECIFIED) + part->offset = offset; + else + offset = part->offset; + + /* verify alignment and size */ + if (part_validate(id, part) != 0) + break; + + offset += part->size; + + /* partition is ok, add it to the list */ + list_add_tail(&part->link, &tmp_list); + num_parts++; + err = 0; + } + if (err == 1) { + part_delall(&tmp_list); + return 1; + } + + if (num_parts == 0) { + printf("no partitions for device %s%d (%s)\n", + MTD_DEV_TYPE(id->type), id->num, id->mtd_id); + return 1; + } + + debug("\ntotal partitions: %d\n", num_parts); + + /* check for next device presence */ + if (p) { + if (*p == ';') { + if (ret) + *ret = ++p; + } else if (*p == '\0') { + if (ret) + *ret = p; + } else { + printf("unexpected character '%c' at the end of device\n", *p); + if (ret) + *ret = NULL; + return 1; + } + } + + /* allocate memory for mtd_device structure */ + if ((dev = (struct mtd_device *)malloc(sizeof(struct mtd_device))) == NULL) { + printf("out of memory\n"); + return 1; + } + memset(dev, 0, sizeof(struct mtd_device)); + dev->id = id; + dev->num_parts = 0; /* part_sort_add increments num_parts */ + INIT_LIST_HEAD(&dev->parts); + INIT_LIST_HEAD(&dev->link); + + /* move partitions from tmp_list to dev->parts */ + list_for_each_safe(entry, n, &tmp_list) { + part = list_entry(entry, struct part_info, link); + list_del(entry); + if (part_sort_add(dev, part) != 0) { + device_del(dev); + return 1; + } + } + + *retdev = dev; + + debug("===\n\n"); + return 0; +} + +/** + * Initialize global device list. + * + * @return 0 on success, 1 otherwise + */ +static int mtd_devices_init(void) +{ + last_parts[0] = '\0'; + current_mtd_dev = NULL; + current_save(); + + return device_delall(&devices); +} + +/* + * Search global mtdids list and find id of requested type and number. + * + * @return pointer to the id if it exists, NULL otherwise + */ +static struct mtdids* id_find(u8 type, u8 num) +{ + struct list_head *entry; + struct mtdids *id; + + list_for_each(entry, &mtdids) { + id = list_entry(entry, struct mtdids, link); + + if ((id->type == type) && (id->num == num)) + return id; + } + + return NULL; +} + +/** + * Search global mtdids list and find id of a requested mtd_id. + * + * Note: first argument is not null terminated. + * + * @param mtd_id string containing requested mtd_id + * @param mtd_id_len length of supplied mtd_id + * @return pointer to the id if it exists, NULL otherwise + */ +static struct mtdids* id_find_by_mtd_id(const char *mtd_id, unsigned int mtd_id_len) +{ + struct list_head *entry; + struct mtdids *id; + + debug("--- id_find_by_mtd_id: '%.*s' (len = %d)\n", + mtd_id_len, mtd_id, mtd_id_len); + + list_for_each(entry, &mtdids) { + id = list_entry(entry, struct mtdids, link); + + debug("entry: '%s' (len = %d)\n", + id->mtd_id, strlen(id->mtd_id)); + + if (mtd_id_len != strlen(id->mtd_id)) + continue; + if (strncmp(id->mtd_id, mtd_id, mtd_id_len) == 0) + return id; + } + + return NULL; +} + +/** + * Parse device id string <dev-id> := 'nand'|'nor'|'onenand'<dev-num>, + * return device type and number. + * + * @param id string describing device id + * @param ret_id output pointer to next char after parse completes (output) + * @param dev_type parsed device type (output) + * @param dev_num parsed device number (output) + * @return 0 on success, 1 otherwise + */ +int mtd_id_parse(const char *id, const char **ret_id, u8 *dev_type, + u8 *dev_num) +{ + const char *p = id; + + *dev_type = 0; + if (strncmp(p, "nand", 4) == 0) { + *dev_type = MTD_DEV_TYPE_NAND; + p += 4; + } else if (strncmp(p, "nor", 3) == 0) { + *dev_type = MTD_DEV_TYPE_NOR; + p += 3; + } else if (strncmp(p, "onenand", 7) == 0) { + *dev_type = MTD_DEV_TYPE_ONENAND; + p += 7; + } else { + printf("incorrect device type in %s\n", id); + return 1; + } + + if (!isdigit(*p)) { + printf("incorrect device number in %s\n", id); + return 1; + } + + *dev_num = simple_strtoul(p, (char **)&p, 0); + if (ret_id) + *ret_id = p; + return 0; +} + +/** + * Process all devices and generate corresponding mtdparts string describing + * all partitions on all devices. + * + * @param buf output buffer holding generated mtdparts string (output) + * @param buflen buffer size + * @return 0 on success, 1 otherwise + */ +static int generate_mtdparts(char *buf, u32 buflen) +{ + struct list_head *pentry, *dentry; + struct mtd_device *dev; + struct part_info *part, *prev_part; + char *p = buf; + char tmpbuf[32]; + u64 size, offset; + u32 len, part_cnt; + u32 maxlen = buflen - 1; + + debug("--- generate_mtdparts ---\n"); + + if (list_empty(&devices)) { + buf[0] = '\0'; + return 0; + } + + sprintf(p, "mtdparts="); + p += 9; + + list_for_each(dentry, &devices) { + dev = list_entry(dentry, struct mtd_device, link); + + /* copy mtd_id */ + len = strlen(dev->id->mtd_id) + 1; + if (len > maxlen) + goto cleanup; + memcpy(p, dev->id->mtd_id, len - 1); + p += len - 1; + *(p++) = ':'; + maxlen -= len; + + /* format partitions */ + prev_part = NULL; + part_cnt = 0; + list_for_each(pentry, &dev->parts) { + part = list_entry(pentry, struct part_info, link); + size = part->size; + offset = part->offset; + part_cnt++; + + /* partition size */ + memsize_format(tmpbuf, size); + len = strlen(tmpbuf); + if (len > maxlen) + goto cleanup; + memcpy(p, tmpbuf, len); + p += len; + maxlen -= len; + + + /* add offset only when there is a gap between + * partitions */ + if ((!prev_part && (offset != 0)) || + (prev_part && ((prev_part->offset + prev_part->size) != part->offset))) { + + memsize_format(tmpbuf, offset); + len = strlen(tmpbuf) + 1; + if (len > maxlen) + goto cleanup; + *(p++) = '@'; + memcpy(p, tmpbuf, len - 1); + p += len - 1; + maxlen -= len; + } + + /* copy name only if user supplied */ + if(!part->auto_name) { + len = strlen(part->name) + 2; + if (len > maxlen) + goto cleanup; + + *(p++) = '('; + memcpy(p, part->name, len - 2); + p += len - 2; + *(p++) = ')'; + maxlen -= len; + } + + /* ro mask flag */ + if (part->mask_flags && MTD_WRITEABLE_CMD) { + len = 2; + if (len > maxlen) + goto cleanup; + *(p++) = 'r'; + *(p++) = 'o'; + maxlen -= 2; + } + + /* print ',' separator if there are other partitions + * following */ + if (dev->num_parts > part_cnt) { + if (1 > maxlen) + goto cleanup; + *(p++) = ','; + maxlen--; + } + prev_part = part; + } + /* print ';' separator if there are other devices following */ + if (dentry->next != &devices) { + if (1 > maxlen) + goto cleanup; + *(p++) = ';'; + maxlen--; + } + } + + /* we still have at least one char left, as we decremented maxlen at + * the begining */ + *p = '\0'; + + return 0; + +cleanup: + last_parts[0] = '\0'; + return 1; +} + +/** + * Call generate_mtdparts to process all devices and generate corresponding + * mtdparts string, save it in mtdparts environment variable. + * + * @param buf output buffer holding generated mtdparts string (output) + * @param buflen buffer size + * @return 0 on success, 1 otherwise + */ +static int generate_mtdparts_save(char *buf, u32 buflen) +{ + int ret; + + ret = generate_mtdparts(buf, buflen); + + if ((buf[0] != '\0') && (ret == 0)) + setenv("mtdparts", buf); + else + setenv("mtdparts", NULL); + + return ret; +} + +#if defined(CONFIG_CMD_MTDPARTS_SHOW_NET_SIZES) +/** + * Get the net size (w/o bad blocks) of the given partition. + * + * @param mtd the mtd info + * @param part the partition + * @return the calculated net size of this partition + */ +static uint64_t net_part_size(struct mtd_info *mtd, struct part_info *part) +{ + uint64_t i, net_size = 0; + + if (!mtd->block_isbad) + return part->size; + + for (i = 0; i < part->size; i += mtd->erasesize) { + if (!mtd->block_isbad(mtd, part->offset + i)) + net_size += mtd->erasesize; + } + + return net_size; +} +#endif + +static void print_partition_table(void) +{ + struct list_head *dentry, *pentry; + struct part_info *part; + struct mtd_device *dev; + int part_num; + + list_for_each(dentry, &devices) { + dev = list_entry(dentry, struct mtd_device, link); + /* list partitions for given device */ + part_num = 0; +#if defined(CONFIG_CMD_MTDPARTS_SHOW_NET_SIZES) + struct mtd_info *mtd; + + if (get_mtd_info(dev->id->type, dev->id->num, &mtd)) + return; + + printf("\ndevice %s%d <%s>, # parts = %d\n", + MTD_DEV_TYPE(dev->id->type), dev->id->num, + dev->id->mtd_id, dev->num_parts); + printf(" #: name\t\tsize\t\tnet size\toffset\t\tmask_flags\n"); + + list_for_each(pentry, &dev->parts) { + u32 net_size; + char *size_note; + + part = list_entry(pentry, struct part_info, link); + net_size = net_part_size(mtd, part); + size_note = part->size == net_size ? " " : " (!)"; + printf("%2d: %-20s0x%08x\t0x%08x%s\t0x%08x\t%d\n", + part_num, part->name, part->size, + net_size, size_note, part->offset, + part->mask_flags); +#else /* !defined(CONFIG_CMD_MTDPARTS_SHOW_NET_SIZES) */ + printf("\ndevice %s%d <%s>, # parts = %d\n", + MTD_DEV_TYPE(dev->id->type), dev->id->num, + dev->id->mtd_id, dev->num_parts); + printf(" #: name\t\tsize\t\toffset\t\tmask_flags\n"); + + list_for_each(pentry, &dev->parts) { + part = list_entry(pentry, struct part_info, link); + printf("%2d: %-20s0x%08llx\t0x%08llx\t%d\n", + part_num, part->name, part->size, + part->offset, part->mask_flags); +#endif /* defined(CONFIG_CMD_MTDPARTS_SHOW_NET_SIZES) */ + part_num++; + } + } + + if (list_empty(&devices)) + printf("no partitions defined\n"); +} + +/** + * Format and print out a partition list for each device from global device + * list. + */ +static void list_partitions(void) +{ + struct part_info *part; + + debug("\n---list_partitions---\n"); + print_partition_table(); + + /* current_mtd_dev is not NULL only when we have non empty device list */ + if (current_mtd_dev) { + part = mtd_part_info(current_mtd_dev, current_mtd_partnum); + if (part) { + printf("\nactive partition: %s%d,%d - (%s) 0x%08llx @ 0x%08llx\n", + MTD_DEV_TYPE(current_mtd_dev->id->type), + current_mtd_dev->id->num, current_mtd_partnum, + part->name, part->size, part->offset); + } else { + printf("could not get current partition info\n\n"); + } + } + + printf("\ndefaults:\n"); + printf("mtdids : %s\n", + mtdids_default ? mtdids_default : "none"); + /* + * Using printf() here results in printbuffer overflow + * if default mtdparts string is greater than console + * printbuffer. Use puts() to prevent system crashes. + */ + puts("mtdparts: "); + puts(mtdparts_default ? mtdparts_default : "none"); + puts("\n"); +} + +/** + * Given partition identifier in form of <dev_type><dev_num>,<part_num> find + * corresponding device and verify partition number. + * + * @param id string describing device and partition or partition name + * @param dev pointer to the requested device (output) + * @param part_num verified partition number (output) + * @param part pointer to requested partition (output) + * @return 0 on success, 1 otherwise + */ +int find_dev_and_part(const char *id, struct mtd_device **dev, + u8 *part_num, struct part_info **part) +{ + struct list_head *dentry, *pentry; + u8 type, dnum, pnum; + const char *p; + + debug("--- find_dev_and_part ---\nid = %s\n", id); + + list_for_each(dentry, &devices) { + *part_num = 0; + *dev = list_entry(dentry, struct mtd_device, link); + list_for_each(pentry, &(*dev)->parts) { + *part = list_entry(pentry, struct part_info, link); + if (strcmp((*part)->name, id) == 0) + return 0; + (*part_num)++; + } + } + + p = id; + *dev = NULL; + *part = NULL; + *part_num = 0; + + if (mtd_id_parse(p, &p, &type, &dnum) != 0) + return 1; + + if ((*p++ != ',') || (*p == '\0')) { + printf("no partition number specified\n"); + return 1; + } + pnum = simple_strtoul(p, (char **)&p, 0); + if (*p != '\0') { + printf("unexpected trailing character '%c'\n", *p); + return 1; + } + + if ((*dev = device_find(type, dnum)) == NULL) { + printf("no such device %s%d\n", MTD_DEV_TYPE(type), dnum); + return 1; + } + + if ((*part = mtd_part_info(*dev, pnum)) == NULL) { + printf("no such partition\n"); + *dev = NULL; + return 1; + } + + *part_num = pnum; + + return 0; +} + +/** + * Find and delete partition. For partition id format see find_dev_and_part(). + * + * @param id string describing device and partition + * @return 0 on success, 1 otherwise + */ +static int delete_partition(const char *id) +{ + u8 pnum; + struct mtd_device *dev; + struct part_info *part; + + if (find_dev_and_part(id, &dev, &pnum, &part) == 0) { + + debug("delete_partition: device = %s%d, partition %d = (%s) 0x%08llx@0x%08llx\n", + MTD_DEV_TYPE(dev->id->type), dev->id->num, pnum, + part->name, part->size, part->offset); + + if (part_del(dev, part) != 0) + return 1; + + if (generate_mtdparts_save(last_parts, MTDPARTS_MAXLEN) != 0) { + printf("generated mtdparts too long, resetting to null\n"); + return 1; + } + return 0; + } + + printf("partition %s not found\n", id); + return 1; +} + +#if defined(CONFIG_CMD_MTDPARTS_SPREAD) +/** + * Increase the size of the given partition so that it's net size is at least + * as large as the size member and such that the next partition would start on a + * good block if it were adjacent to this partition. + * + * @param mtd the mtd device + * @param part the partition + * @param next_offset pointer to the offset of the next partition after this + * partition's size has been modified (output) + */ +static void spread_partition(struct mtd_info *mtd, struct part_info *part, + uint64_t *next_offset) +{ + uint64_t net_size, padding_size = 0; + int truncated; + + mtd_get_len_incl_bad(mtd, part->offset, part->size, &net_size, + &truncated); + + /* + * Absorb bad blocks immediately following this + * partition also into the partition, such that + * the next partition starts with a good block. + */ + if (!truncated) { + mtd_get_len_incl_bad(mtd, part->offset + net_size, + mtd->erasesize, &padding_size, &truncated); + if (truncated) + padding_size = 0; + else + padding_size -= mtd->erasesize; + } + + if (truncated) { + printf("truncated partition %s to %lld bytes\n", part->name, + (uint64_t) net_size + padding_size); + } + + part->size = net_size + padding_size; + *next_offset = part->offset + part->size; +} + +/** + * Adjust all of the partition sizes, such that all partitions are at least + * as big as their mtdparts environment variable sizes and they each start + * on a good block. + * + * @return 0 on success, 1 otherwise + */ +static int spread_partitions(void) +{ + struct list_head *dentry, *pentry; + struct mtd_device *dev; + struct part_info *part; + struct mtd_info *mtd; + int part_num; + uint64_t cur_offs; + + list_for_each(dentry, &devices) { + dev = list_entry(dentry, struct mtd_device, link); + + if (get_mtd_info(dev->id->type, dev->id->num, &mtd)) + return 1; + + part_num = 0; + cur_offs = 0; + list_for_each(pentry, &dev->parts) { + part = list_entry(pentry, struct part_info, link); + + debug("spread_partitions: device = %s%d, partition %d =" + " (%s) 0x%08x@0x%08x\n", + MTD_DEV_TYPE(dev->id->type), dev->id->num, + part_num, part->name, part->size, + part->offset); + + if (cur_offs > part->offset) + part->offset = cur_offs; + + spread_partition(mtd, part, &cur_offs); + + part_num++; + } + } + + index_partitions(); + + if (generate_mtdparts_save(last_parts, MTDPARTS_MAXLEN) != 0) { + printf("generated mtdparts too long, resetting to null\n"); + return 1; + } + return 0; +} +#endif /* CONFIG_CMD_MTDPARTS_SPREAD */ + +/** + * Accept character string describing mtd partitions and call device_parse() + * for each entry. Add created devices to the global devices list. + * + * @param mtdparts string specifing mtd partitions + * @return 0 on success, 1 otherwise + */ +static int parse_mtdparts(const char *const mtdparts) +{ + const char *p = mtdparts; + struct mtd_device *dev; + int err = 1; + char tmp_parts[MTDPARTS_MAXLEN]; + + debug("\n---parse_mtdparts---\nmtdparts = %s\n\n", p); + + /* delete all devices and partitions */ + if (mtd_devices_init() != 0) { + printf("could not initialise device list\n"); + return err; + } + + /* re-read 'mtdparts' variable, mtd_devices_init may be updating env */ + if (gd->flags & GD_FLG_ENV_READY) { + p = getenv("mtdparts"); + } else { + p = tmp_parts; + getenv_f("mtdparts", tmp_parts, MTDPARTS_MAXLEN); + } + + if (strncmp(p, "mtdparts=", 9) != 0) { + printf("mtdparts variable doesn't start with 'mtdparts='\n"); + return err; + } + p += 9; + + while (p && (*p != '\0')) { + err = 1; + if ((device_parse(p, &p, &dev) != 0) || (!dev)) + break; + + debug("+ device: %s\t%d\t%s\n", MTD_DEV_TYPE(dev->id->type), + dev->id->num, dev->id->mtd_id); + + /* check if parsed device is already on the list */ + if (device_find(dev->id->type, dev->id->num) != NULL) { + printf("device %s%d redefined, please correct mtdparts variable\n", + MTD_DEV_TYPE(dev->id->type), dev->id->num); + break; + } + + list_add_tail(&dev->link, &devices); + err = 0; + } + if (err == 1) { + device_delall(&devices); + return 1; + } + + return 0; +} + +/** + * Parse provided string describing mtdids mapping (see file header for mtdids + * variable format). Allocate memory for each entry and add all found entries + * to the global mtdids list. + * + * @param ids mapping string + * @return 0 on success, 1 otherwise + */ +static int parse_mtdids(const char *const ids) +{ + const char *p = ids; + const char *mtd_id; + int mtd_id_len; + struct mtdids *id; + struct list_head *entry, *n; + struct mtdids *id_tmp; + u8 type, num; + u64 size; + int ret = 1; + + debug("\n---parse_mtdids---\nmtdids = %s\n\n", ids); + + /* clean global mtdids list */ + list_for_each_safe(entry, n, &mtdids) { + id_tmp = list_entry(entry, struct mtdids, link); + debug("mtdids del: %d %d\n", id_tmp->type, id_tmp->num); + list_del(entry); + free(id_tmp); + } + last_ids[0] = '\0'; + INIT_LIST_HEAD(&mtdids); + + while(p && (*p != '\0')) { + + ret = 1; + /* parse 'nor'|'nand'|'onenand'<dev-num> */ + if (mtd_id_parse(p, &p, &type, &num) != 0) + break; + + if (*p != '=') { + printf("mtdids: incorrect <dev-num>\n"); + break; + } + p++; + + /* check if requested device exists */ + if (mtd_device_validate(type, num, &size) != 0) + return 1; + + /* locate <mtd-id> */ + mtd_id = p; + if ((p = strchr(mtd_id, ',')) != NULL) { + mtd_id_len = p - mtd_id + 1; + p++; + } else { + mtd_id_len = strlen(mtd_id) + 1; + } + if (mtd_id_len == 0) { + printf("mtdids: no <mtd-id> identifier\n"); + break; + } + + /* check if this id is already on the list */ + int double_entry = 0; + list_for_each(entry, &mtdids) { + id_tmp = list_entry(entry, struct mtdids, link); + if ((id_tmp->type == type) && (id_tmp->num == num)) { + double_entry = 1; + break; + } + } + if (double_entry) { + printf("device id %s%d redefined, please correct mtdids variable\n", + MTD_DEV_TYPE(type), num); + break; + } + + /* allocate mtdids structure */ + if (!(id = (struct mtdids *)malloc(sizeof(struct mtdids) + mtd_id_len))) { + printf("out of memory\n"); + break; + } + memset(id, 0, sizeof(struct mtdids) + mtd_id_len); + id->num = num; + id->type = type; + id->size = size; + id->mtd_id = (char *)(id + 1); + strncpy(id->mtd_id, mtd_id, mtd_id_len - 1); + id->mtd_id[mtd_id_len - 1] = '\0'; + INIT_LIST_HEAD(&id->link); + + debug("+ id %s%d\t%16lld bytes\t%s\n", + MTD_DEV_TYPE(id->type), id->num, + id->size, id->mtd_id); + + list_add_tail(&id->link, &mtdids); + ret = 0; + } + if (ret == 1) { + /* clean mtdids list and free allocated memory */ + list_for_each_safe(entry, n, &mtdids) { + id_tmp = list_entry(entry, struct mtdids, link); + list_del(entry); + free(id_tmp); + } + return 1; + } + + return 0; +} + +/** + * Parse and initialize global mtdids mapping and create global + * device/partition list. + * + * @return 0 on success, 1 otherwise + */ +int mtdparts_init(void) +{ + static int initialized = 0; + const char *ids, *parts; + const char *current_partition; + int ids_changed; + char tmp_ep[PARTITION_MAXLEN]; + char tmp_parts[MTDPARTS_MAXLEN]; + + debug("\n---mtdparts_init---\n"); + if (!initialized) { + INIT_LIST_HEAD(&mtdids); + INIT_LIST_HEAD(&devices); + memset(last_ids, 0, MTDIDS_MAXLEN); + memset(last_parts, 0, MTDPARTS_MAXLEN); + memset(last_partition, 0, PARTITION_MAXLEN); + initialized = 1; + } + + /* get variables */ + ids = getenv("mtdids"); + /* + * The mtdparts variable tends to be long. If we need to access it + * before the env is relocated, then we need to use our own stack + * buffer. gd->env_buf will be too small. + */ + if (gd->flags & GD_FLG_ENV_READY) { + parts = getenv("mtdparts"); + } else { + parts = tmp_parts; + getenv_f("mtdparts", tmp_parts, MTDPARTS_MAXLEN); + } + current_partition = getenv("partition"); + + /* save it for later parsing, cannot rely on current partition pointer + * as 'partition' variable may be updated during init */ + tmp_ep[0] = '\0'; + if (current_partition) + strncpy(tmp_ep, current_partition, PARTITION_MAXLEN); + + debug("last_ids : %s\n", last_ids); + debug("env_ids : %s\n", ids); + debug("last_parts: %s\n", last_parts); + debug("env_parts : %s\n\n", parts); + + debug("last_partition : %s\n", last_partition); + debug("env_partition : %s\n", current_partition); + + /* if mtdids varible is empty try to use defaults */ + if (!ids) { + if (mtdids_default) { + debug("mtdids variable not defined, using default\n"); + ids = mtdids_default; + setenv("mtdids", (char *)ids); + } else { + printf("mtdids not defined, no default present\n"); + return 1; + } + } + if (strlen(ids) > MTDIDS_MAXLEN - 1) { + printf("mtdids too long (> %d)\n", MTDIDS_MAXLEN); + return 1; + } + + /* do no try to use defaults when mtdparts variable is not defined, + * just check the length */ + if (!parts) + printf("mtdparts variable not set, see 'help mtdparts'\n"); + + if (parts && (strlen(parts) > MTDPARTS_MAXLEN - 1)) { + printf("mtdparts too long (> %d)\n", MTDPARTS_MAXLEN); + return 1; + } + + /* check if we have already parsed those mtdids */ + if ((last_ids[0] != '\0') && (strcmp(last_ids, ids) == 0)) { + ids_changed = 0; + } else { + ids_changed = 1; + + if (parse_mtdids(ids) != 0) { + mtd_devices_init(); + return 1; + } + + /* ok it's good, save new ids */ + strncpy(last_ids, ids, MTDIDS_MAXLEN); + } + + /* parse partitions if either mtdparts or mtdids were updated */ + if (parts && ((last_parts[0] == '\0') || ((strcmp(last_parts, parts) != 0)) || ids_changed)) { + if (parse_mtdparts(parts) != 0) + return 1; + + if (list_empty(&devices)) { + printf("mtdparts_init: no valid partitions\n"); + return 1; + } + + /* ok it's good, save new parts */ + strncpy(last_parts, parts, MTDPARTS_MAXLEN); + + /* reset first partition from first dev from the list as current */ + current_mtd_dev = list_entry(devices.next, struct mtd_device, link); + current_mtd_partnum = 0; + current_save(); + + debug("mtdparts_init: current_mtd_dev = %s%d, current_mtd_partnum = %d\n", + MTD_DEV_TYPE(current_mtd_dev->id->type), + current_mtd_dev->id->num, current_mtd_partnum); + } + + /* mtdparts variable was reset to NULL, delete all devices/partitions */ + if (!parts && (last_parts[0] != '\0')) + return mtd_devices_init(); + + /* do not process current partition if mtdparts variable is null */ + if (!parts) + return 0; + + /* is current partition set in environment? if so, use it */ + if ((tmp_ep[0] != '\0') && (strcmp(tmp_ep, last_partition) != 0)) { + struct part_info *p; + struct mtd_device *cdev; + u8 pnum; + + debug("--- getting current partition: %s\n", tmp_ep); + + if (find_dev_and_part(tmp_ep, &cdev, &pnum, &p) == 0) { + current_mtd_dev = cdev; + current_mtd_partnum = pnum; + current_save(); + } + } else if (getenv("partition") == NULL) { + debug("no partition variable set, setting...\n"); + current_save(); + } + + return 0; +} + +/** + * Return pointer to the partition of a requested number from a requested + * device. + * + * @param dev device that is to be searched for a partition + * @param part_num requested partition number + * @return pointer to the part_info, NULL otherwise + */ +static struct part_info* mtd_part_info(struct mtd_device *dev, unsigned int part_num) +{ + struct list_head *entry; + struct part_info *part; + int num; + + if (!dev) + return NULL; + + debug("\n--- mtd_part_info: partition number %d for device %s%d (%s)\n", + part_num, MTD_DEV_TYPE(dev->id->type), + dev->id->num, dev->id->mtd_id); + + if (part_num >= dev->num_parts) { + printf("invalid partition number %d for device %s%d (%s)\n", + part_num, MTD_DEV_TYPE(dev->id->type), + dev->id->num, dev->id->mtd_id); + return NULL; + } + + /* locate partition number, return it */ + num = 0; + list_for_each(entry, &dev->parts) { + part = list_entry(entry, struct part_info, link); + + if (part_num == num++) { + return part; + } + } + + return NULL; +} + +/***************************************************/ +/* U-boot commands */ +/***************************************************/ +/* command line only */ +/** + * Routine implementing u-boot chpart command. Sets new current partition based + * on the user supplied partition id. For partition id format see find_dev_and_part(). + * + * @param cmdtp command internal data + * @param flag command flag + * @param argc number of arguments supplied to the command + * @param argv arguments list + * @return 0 on success, 1 otherwise + */ +static int do_chpart(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ +/* command line only */ + struct mtd_device *dev; + struct part_info *part; + u8 pnum; + + if (mtdparts_init() !=0) + return 1; + + if (argc < 2) { + printf("no partition id specified\n"); + return 1; + } + + if (find_dev_and_part(argv[1], &dev, &pnum, &part) != 0) + return 1; + + current_mtd_dev = dev; + current_mtd_partnum = pnum; + current_save(); + + printf("partition changed to %s%d,%d\n", + MTD_DEV_TYPE(dev->id->type), dev->id->num, pnum); + + return 0; +} + +/** + * Routine implementing u-boot mtdparts command. Initialize/update default global + * partition list and process user partition request (list, add, del). + * + * @param cmdtp command internal data + * @param flag command flag + * @param argc number of arguments supplied to the command + * @param argv arguments list + * @return 0 on success, 1 otherwise + */ +static int do_mtdparts(cmd_tbl_t *cmdtp, int flag, int argc, + char * const argv[]) +{ + if (argc == 2) { + if (strcmp(argv[1], "default") == 0) { + setenv("mtdids", (char *)mtdids_default); + setenv("mtdparts", (char *)mtdparts_default); + setenv("partition", NULL); + + mtdparts_init(); + return 0; + } else if (strcmp(argv[1], "delall") == 0) { + /* this may be the first run, initialize lists if needed */ + mtdparts_init(); + + setenv("mtdparts", NULL); + + /* mtd_devices_init() calls current_save() */ + return mtd_devices_init(); + } + } + + /* make sure we are in sync with env variables */ + if (mtdparts_init() != 0) + return 1; + + if (argc == 1) { + list_partitions(); + return 0; + } + + /* mtdparts add <mtd-dev> <size>[@<offset>] <name> [ro] */ + if (((argc == 5) || (argc == 6)) && (strncmp(argv[1], "add", 3) == 0)) { +#define PART_ADD_DESC_MAXLEN 64 + char tmpbuf[PART_ADD_DESC_MAXLEN]; +#if defined(CONFIG_CMD_MTDPARTS_SPREAD) + struct mtd_info *mtd; + uint64_t next_offset; +#endif + u8 type, num, len; + struct mtd_device *dev; + struct mtd_device *dev_tmp; + struct mtdids *id; + struct part_info *p; + + if (mtd_id_parse(argv[2], NULL, &type, &num) != 0) + return 1; + + if ((id = id_find(type, num)) == NULL) { + printf("no such device %s defined in mtdids variable\n", argv[2]); + return 1; + } + + len = strlen(id->mtd_id) + 1; /* 'mtd_id:' */ + len += strlen(argv[3]); /* size@offset */ + len += strlen(argv[4]) + 2; /* '(' name ')' */ + if (argv[5] && (strlen(argv[5]) == 2)) + len += 2; /* 'ro' */ + + if (len >= PART_ADD_DESC_MAXLEN) { + printf("too long partition description\n"); + return 1; + } + sprintf(tmpbuf, "%s:%s(%s)%s", + id->mtd_id, argv[3], argv[4], argv[5] ? argv[5] : ""); + debug("add tmpbuf: %s\n", tmpbuf); + + if ((device_parse(tmpbuf, NULL, &dev) != 0) || (!dev)) + return 1; + + debug("+ %s\t%d\t%s\n", MTD_DEV_TYPE(dev->id->type), + dev->id->num, dev->id->mtd_id); + + p = list_entry(dev->parts.next, struct part_info, link); + +#if defined(CONFIG_CMD_MTDPARTS_SPREAD) + if (get_mtd_info(dev->id->type, dev->id->num, &mtd)) + return 1; + + if (!strcmp(&argv[1][3], ".spread")) { + spread_partition(mtd, p, &next_offset); + debug("increased %s to %d bytes\n", p->name, p->size); + } +#endif + + dev_tmp = device_find(dev->id->type, dev->id->num); + if (dev_tmp == NULL) { + device_add(dev); + } else if (part_add(dev_tmp, p) != 0) { + /* merge new partition with existing ones*/ + device_del(dev); + return 1; + } + + if (generate_mtdparts_save(last_parts, MTDPARTS_MAXLEN) != 0) { + printf("generated mtdparts too long, resetting to null\n"); + return 1; + } + + return 0; + } + + /* mtdparts del part-id */ + if ((argc == 3) && (strcmp(argv[1], "del") == 0)) { + debug("del: part-id = %s\n", argv[2]); + + return delete_partition(argv[2]); + } + +#if defined(CONFIG_CMD_MTDPARTS_SPREAD) + if ((argc == 2) && (strcmp(argv[1], "spread") == 0)) + return spread_partitions(); +#endif /* CONFIG_CMD_MTDPARTS_SPREAD */ + + return CMD_RET_USAGE; +} + +/***************************************************/ +U_BOOT_CMD( + chpart, 2, 0, do_chpart, + "change active partition", + "part-id\n" + " - change active partition (e.g. part-id = nand0,1)" +); + +#ifdef CONFIG_SYS_LONGHELP +static char mtdparts_help_text[] = + "\n" + " - list partition table\n" + "mtdparts delall\n" + " - delete all partitions\n" + "mtdparts del part-id\n" + " - delete partition (e.g. part-id = nand0,1)\n" + "mtdparts add <mtd-dev> <size>[@<offset>] [<name>] [ro]\n" + " - add partition\n" +#if defined(CONFIG_CMD_MTDPARTS_SPREAD) + "mtdparts add.spread <mtd-dev> <size>[@<offset>] [<name>] [ro]\n" + " - add partition, padding size by skipping bad blocks\n" +#endif + "mtdparts default\n" + " - reset partition table to defaults\n" +#if defined(CONFIG_CMD_MTDPARTS_SPREAD) + "mtdparts spread\n" + " - adjust the sizes of the partitions so they are\n" + " at least as big as the mtdparts variable specifies\n" + " and they each start on a good block\n\n" +#else + "\n" +#endif /* CONFIG_CMD_MTDPARTS_SPREAD */ + "-----\n\n" + "this command uses three environment variables:\n\n" + "'partition' - keeps current partition identifier\n\n" + "partition := <part-id>\n" + "<part-id> := <dev-id>,part_num\n\n" + "'mtdids' - linux kernel mtd device id <-> u-boot device id mapping\n\n" + "mtdids=<idmap>[,<idmap>,...]\n\n" + "<idmap> := <dev-id>=<mtd-id>\n" + "<dev-id> := 'nand'|'nor'|'onenand'<dev-num>\n" + "<dev-num> := mtd device number, 0...\n" + "<mtd-id> := unique device tag used by linux kernel to find mtd device (mtd->name)\n\n" + "'mtdparts' - partition list\n\n" + "mtdparts=mtdparts=<mtd-def>[;<mtd-def>...]\n\n" + "<mtd-def> := <mtd-id>:<part-def>[,<part-def>...]\n" + "<mtd-id> := unique device tag used by linux kernel to find mtd device (mtd->name)\n" + "<part-def> := <size>[@<offset>][<name>][<ro-flag>]\n" + "<size> := standard linux memsize OR '-' to denote all remaining space\n" + "<offset> := partition start offset within the device\n" + "<name> := '(' NAME ')'\n" + "<ro-flag> := when set to 'ro' makes partition read-only (not used, passed to kernel)"; +#endif + +U_BOOT_CMD( + mtdparts, 6, 0, do_mtdparts, + "define flash/nand partitions", mtdparts_help_text +); +/***************************************************/ diff --git a/qemu/roms/u-boot/common/cmd_nand.c b/qemu/roms/u-boot/common/cmd_nand.c new file mode 100644 index 000000000..04ab0f19b --- /dev/null +++ b/qemu/roms/u-boot/common/cmd_nand.c @@ -0,0 +1,1066 @@ +/* + * Driver for NAND support, Rick Bronson + * borrowed heavily from: + * (c) 1999 Machine Vision Holdings, Inc. + * (c) 1999, 2000 David Woodhouse <dwmw2@infradead.org> + * + * Ported 'dynenv' to 'nand env.oob' command + * (C) 2010 Nanometrics, Inc. + * 'dynenv' -- Dynamic environment offset in NAND OOB + * (C) Copyright 2006-2007 OpenMoko, Inc. + * Added 16-bit nand support + * (C) 2004 Texas Instruments + * + * Copyright 2010, 2012 Freescale Semiconductor + * The portions of this file whose copyright is held by Freescale and which + * are not considered a derived work of GPL v2-only code may be distributed + * and/or modified 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. + */ + +#include <common.h> +#include <linux/mtd/mtd.h> +#include <command.h> +#include <watchdog.h> +#include <malloc.h> +#include <asm/byteorder.h> +#include <jffs2/jffs2.h> +#include <nand.h> + +#if defined(CONFIG_CMD_MTDPARTS) + +/* partition handling routines */ +int mtdparts_init(void); +int id_parse(const char *id, const char **ret_id, u8 *dev_type, u8 *dev_num); +int find_dev_and_part(const char *id, struct mtd_device **dev, + u8 *part_num, struct part_info **part); +#endif + +static int nand_dump(nand_info_t *nand, ulong off, int only_oob, int repeat) +{ + int i; + u_char *datbuf, *oobbuf, *p; + static loff_t last; + int ret = 0; + + if (repeat) + off = last + nand->writesize; + + last = off; + + datbuf = memalign(ARCH_DMA_MINALIGN, nand->writesize); + if (!datbuf) { + puts("No memory for page buffer\n"); + return 1; + } + + oobbuf = memalign(ARCH_DMA_MINALIGN, nand->oobsize); + if (!oobbuf) { + puts("No memory for page buffer\n"); + ret = 1; + goto free_dat; + } + off &= ~(nand->writesize - 1); + loff_t addr = (loff_t) off; + struct mtd_oob_ops ops; + memset(&ops, 0, sizeof(ops)); + ops.datbuf = datbuf; + ops.oobbuf = oobbuf; + ops.len = nand->writesize; + ops.ooblen = nand->oobsize; + ops.mode = MTD_OPS_RAW; + i = mtd_read_oob(nand, addr, &ops); + if (i < 0) { + printf("Error (%d) reading page %08lx\n", i, off); + ret = 1; + goto free_all; + } + printf("Page %08lx dump:\n", off); + + if (!only_oob) { + i = nand->writesize >> 4; + p = datbuf; + + while (i--) { + printf("\t%02x %02x %02x %02x %02x %02x %02x %02x" + " %02x %02x %02x %02x %02x %02x %02x %02x\n", + p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7], + p[8], p[9], p[10], p[11], p[12], p[13], p[14], + p[15]); + p += 16; + } + } + + puts("OOB:\n"); + i = nand->oobsize >> 3; + p = oobbuf; + while (i--) { + printf("\t%02x %02x %02x %02x %02x %02x %02x %02x\n", + p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7]); + p += 8; + } + +free_all: + free(oobbuf); +free_dat: + free(datbuf); + + return ret; +} + +/* ------------------------------------------------------------------------- */ + +static int set_dev(int dev) +{ + if (dev < 0 || dev >= CONFIG_SYS_MAX_NAND_DEVICE || + !nand_info[dev].name) { + puts("No such device\n"); + return -1; + } + + if (nand_curr_device == dev) + return 0; + + printf("Device %d: %s", dev, nand_info[dev].name); + puts("... is now current device\n"); + nand_curr_device = dev; + +#ifdef CONFIG_SYS_NAND_SELECT_DEVICE + board_nand_select_device(nand_info[dev].priv, dev); +#endif + + return 0; +} + +static inline int str2off(const char *p, loff_t *num) +{ + char *endptr; + + *num = simple_strtoull(p, &endptr, 16); + return *p != '\0' && *endptr == '\0'; +} + +static inline int str2long(const char *p, ulong *num) +{ + char *endptr; + + *num = simple_strtoul(p, &endptr, 16); + return *p != '\0' && *endptr == '\0'; +} + +static int get_part(const char *partname, int *idx, loff_t *off, loff_t *size, + loff_t *maxsize) +{ +#ifdef CONFIG_CMD_MTDPARTS + struct mtd_device *dev; + struct part_info *part; + u8 pnum; + int ret; + + ret = mtdparts_init(); + if (ret) + return ret; + + ret = find_dev_and_part(partname, &dev, &pnum, &part); + if (ret) + return ret; + + if (dev->id->type != MTD_DEV_TYPE_NAND) { + puts("not a NAND device\n"); + return -1; + } + + *off = part->offset; + *size = part->size; + *maxsize = part->size; + *idx = dev->id->num; + + ret = set_dev(*idx); + if (ret) + return ret; + + return 0; +#else + puts("offset is not a number\n"); + return -1; +#endif +} + +static int arg_off(const char *arg, int *idx, loff_t *off, loff_t *size, + loff_t *maxsize) +{ + if (!str2off(arg, off)) + return get_part(arg, idx, off, size, maxsize); + + if (*off >= nand_info[*idx].size) { + puts("Offset exceeds device limit\n"); + return -1; + } + + *maxsize = nand_info[*idx].size - *off; + *size = *maxsize; + return 0; +} + +static int arg_off_size(int argc, char *const argv[], int *idx, + loff_t *off, loff_t *size, loff_t *maxsize) +{ + int ret; + + if (argc == 0) { + *off = 0; + *size = nand_info[*idx].size; + *maxsize = *size; + goto print; + } + + ret = arg_off(argv[0], idx, off, size, maxsize); + if (ret) + return ret; + + if (argc == 1) + goto print; + + if (!str2off(argv[1], size)) { + printf("'%s' is not a number\n", argv[1]); + return -1; + } + + if (*size > *maxsize) { + puts("Size exceeds partition or device limit\n"); + return -1; + } + +print: + printf("device %d ", *idx); + if (*size == nand_info[*idx].size) + puts("whole chip\n"); + else + printf("offset 0x%llx, size 0x%llx\n", + (unsigned long long)*off, (unsigned long long)*size); + return 0; +} + +#ifdef CONFIG_CMD_NAND_LOCK_UNLOCK +static void print_status(ulong start, ulong end, ulong erasesize, int status) +{ + /* + * Micron NAND flash (e.g. MT29F4G08ABADAH4) BLOCK LOCK READ STATUS is + * not the same as others. Instead of bit 1 being lock, it is + * #lock_tight. To make the driver support either format, ignore bit 1 + * and use only bit 0 and bit 2. + */ + printf("%08lx - %08lx: %08lx blocks %s%s%s\n", + start, + end - 1, + (end - start) / erasesize, + ((status & NAND_LOCK_STATUS_TIGHT) ? "TIGHT " : ""), + (!(status & NAND_LOCK_STATUS_UNLOCK) ? "LOCK " : ""), + ((status & NAND_LOCK_STATUS_UNLOCK) ? "UNLOCK " : "")); +} + +static void do_nand_status(nand_info_t *nand) +{ + ulong block_start = 0; + ulong off; + int last_status = -1; + + struct nand_chip *nand_chip = nand->priv; + /* check the WP bit */ + nand_chip->cmdfunc(nand, NAND_CMD_STATUS, -1, -1); + printf("device is %swrite protected\n", + (nand_chip->read_byte(nand) & 0x80 ? + "NOT " : "")); + + for (off = 0; off < nand->size; off += nand->erasesize) { + int s = nand_get_lock_status(nand, off); + + /* print message only if status has changed */ + if (s != last_status && off != 0) { + print_status(block_start, off, nand->erasesize, + last_status); + block_start = off; + } + last_status = s; + } + /* Print the last block info */ + print_status(block_start, off, nand->erasesize, last_status); +} +#endif + +#ifdef CONFIG_ENV_OFFSET_OOB +unsigned long nand_env_oob_offset; + +int do_nand_env_oob(cmd_tbl_t *cmdtp, int argc, char *const argv[]) +{ + int ret; + uint32_t oob_buf[ENV_OFFSET_SIZE/sizeof(uint32_t)]; + nand_info_t *nand = &nand_info[0]; + char *cmd = argv[1]; + + if (CONFIG_SYS_MAX_NAND_DEVICE == 0 || !nand->name) { + puts("no devices available\n"); + return 1; + } + + set_dev(0); + + if (!strcmp(cmd, "get")) { + ret = get_nand_env_oob(nand, &nand_env_oob_offset); + if (ret) + return 1; + + printf("0x%08lx\n", nand_env_oob_offset); + } else if (!strcmp(cmd, "set")) { + loff_t addr; + loff_t maxsize; + struct mtd_oob_ops ops; + int idx = 0; + + if (argc < 3) + goto usage; + + /* We don't care about size, or maxsize. */ + if (arg_off(argv[2], &idx, &addr, &maxsize, &maxsize)) { + puts("Offset or partition name expected\n"); + return 1; + } + + if (idx != 0) { + puts("Partition not on first NAND device\n"); + return 1; + } + + if (nand->oobavail < ENV_OFFSET_SIZE) { + printf("Insufficient available OOB bytes:\n" + "%d OOB bytes available but %d required for " + "env.oob support\n", + nand->oobavail, ENV_OFFSET_SIZE); + return 1; + } + + if ((addr & (nand->erasesize - 1)) != 0) { + printf("Environment offset must be block-aligned\n"); + return 1; + } + + ops.datbuf = NULL; + ops.mode = MTD_OOB_AUTO; + ops.ooboffs = 0; + ops.ooblen = ENV_OFFSET_SIZE; + ops.oobbuf = (void *) oob_buf; + + oob_buf[0] = ENV_OOB_MARKER; + oob_buf[1] = addr / nand->erasesize; + + ret = nand->write_oob(nand, ENV_OFFSET_SIZE, &ops); + if (ret) { + printf("Error writing OOB block 0\n"); + return ret; + } + + ret = get_nand_env_oob(nand, &nand_env_oob_offset); + if (ret) { + printf("Error reading env offset in OOB\n"); + return ret; + } + + if (addr != nand_env_oob_offset) { + printf("Verification of env offset in OOB failed: " + "0x%08llx expected but got 0x%08lx\n", + (unsigned long long)addr, nand_env_oob_offset); + return 1; + } + } else { + goto usage; + } + + return ret; + +usage: + return CMD_RET_USAGE; +} + +#endif + +static void nand_print_and_set_info(int idx) +{ + nand_info_t *nand = &nand_info[idx]; + struct nand_chip *chip = nand->priv; + + printf("Device %d: ", idx); + if (chip->numchips > 1) + printf("%dx ", chip->numchips); + printf("%s, sector size %u KiB\n", + nand->name, nand->erasesize >> 10); + printf(" Page size %8d b\n", nand->writesize); + printf(" OOB size %8d b\n", nand->oobsize); + printf(" Erase size %8d b\n", nand->erasesize); + + /* Set geometry info */ + setenv_hex("nand_writesize", nand->writesize); + setenv_hex("nand_oobsize", nand->oobsize); + setenv_hex("nand_erasesize", nand->erasesize); +} + +static int raw_access(nand_info_t *nand, ulong addr, loff_t off, ulong count, + int read) +{ + int ret = 0; + + while (count--) { + /* Raw access */ + mtd_oob_ops_t ops = { + .datbuf = (u8 *)addr, + .oobbuf = ((u8 *)addr) + nand->writesize, + .len = nand->writesize, + .ooblen = nand->oobsize, + .mode = MTD_OPS_RAW + }; + + if (read) + ret = mtd_read_oob(nand, off, &ops); + else + ret = mtd_write_oob(nand, off, &ops); + + if (ret) { + printf("%s: error at offset %llx, ret %d\n", + __func__, (long long)off, ret); + break; + } + + addr += nand->writesize + nand->oobsize; + off += nand->writesize; + } + + return ret; +} + +/* Adjust a chip/partition size down for bad blocks so we don't + * read/write past the end of a chip/partition by accident. + */ +static void adjust_size_for_badblocks(loff_t *size, loff_t offset, int dev) +{ + /* We grab the nand info object here fresh because this is usually + * called after arg_off_size() which can change the value of dev. + */ + nand_info_t *nand = &nand_info[dev]; + loff_t maxoffset = offset + *size; + int badblocks = 0; + + /* count badblocks in NAND from offset to offset + size */ + for (; offset < maxoffset; offset += nand->erasesize) { + if (nand_block_isbad(nand, offset)) + badblocks++; + } + /* adjust size if any bad blocks found */ + if (badblocks) { + *size -= badblocks * nand->erasesize; + printf("size adjusted to 0x%llx (%d bad blocks)\n", + (unsigned long long)*size, badblocks); + } +} + +static int do_nand(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + int i, ret = 0; + ulong addr; + loff_t off, size, maxsize; + char *cmd, *s; + nand_info_t *nand; +#ifdef CONFIG_SYS_NAND_QUIET + int quiet = CONFIG_SYS_NAND_QUIET; +#else + int quiet = 0; +#endif + const char *quiet_str = getenv("quiet"); + int dev = nand_curr_device; + int repeat = flag & CMD_FLAG_REPEAT; + + /* at least two arguments please */ + if (argc < 2) + goto usage; + + if (quiet_str) + quiet = simple_strtoul(quiet_str, NULL, 0) != 0; + + cmd = argv[1]; + + /* Only "dump" is repeatable. */ + if (repeat && strcmp(cmd, "dump")) + return 0; + + if (strcmp(cmd, "info") == 0) { + + putc('\n'); + for (i = 0; i < CONFIG_SYS_MAX_NAND_DEVICE; i++) { + if (nand_info[i].name) + nand_print_and_set_info(i); + } + return 0; + } + + if (strcmp(cmd, "device") == 0) { + if (argc < 3) { + putc('\n'); + if (dev < 0 || dev >= CONFIG_SYS_MAX_NAND_DEVICE) + puts("no devices available\n"); + else + nand_print_and_set_info(dev); + return 0; + } + + dev = (int)simple_strtoul(argv[2], NULL, 10); + set_dev(dev); + + return 0; + } + +#ifdef CONFIG_ENV_OFFSET_OOB + /* this command operates only on the first nand device */ + if (strcmp(cmd, "env.oob") == 0) + return do_nand_env_oob(cmdtp, argc - 1, argv + 1); +#endif + + /* The following commands operate on the current device, unless + * overridden by a partition specifier. Note that if somehow the + * current device is invalid, it will have to be changed to a valid + * one before these commands can run, even if a partition specifier + * for another device is to be used. + */ + if (dev < 0 || dev >= CONFIG_SYS_MAX_NAND_DEVICE || + !nand_info[dev].name) { + puts("\nno devices available\n"); + return 1; + } + nand = &nand_info[dev]; + + if (strcmp(cmd, "bad") == 0) { + printf("\nDevice %d bad blocks:\n", dev); + for (off = 0; off < nand->size; off += nand->erasesize) + if (nand_block_isbad(nand, off)) + printf(" %08llx\n", (unsigned long long)off); + return 0; + } + + /* + * Syntax is: + * 0 1 2 3 4 + * nand erase [clean] [off size] + */ + if (strncmp(cmd, "erase", 5) == 0 || strncmp(cmd, "scrub", 5) == 0) { + nand_erase_options_t opts; + /* "clean" at index 2 means request to write cleanmarker */ + int clean = argc > 2 && !strcmp("clean", argv[2]); + int scrub_yes = argc > 2 && !strcmp("-y", argv[2]); + int o = (clean || scrub_yes) ? 3 : 2; + int scrub = !strncmp(cmd, "scrub", 5); + int spread = 0; + int args = 2; + const char *scrub_warn = + "Warning: " + "scrub option will erase all factory set bad blocks!\n" + " " + "There is no reliable way to recover them.\n" + " " + "Use this command only for testing purposes if you\n" + " " + "are sure of what you are doing!\n" + "\nReally scrub this NAND flash? <y/N>\n"; + + if (cmd[5] != 0) { + if (!strcmp(&cmd[5], ".spread")) { + spread = 1; + } else if (!strcmp(&cmd[5], ".part")) { + args = 1; + } else if (!strcmp(&cmd[5], ".chip")) { + args = 0; + } else { + goto usage; + } + } + + /* + * Don't allow missing arguments to cause full chip/partition + * erases -- easy to do accidentally, e.g. with a misspelled + * variable name. + */ + if (argc != o + args) + goto usage; + + printf("\nNAND %s: ", cmd); + /* skip first two or three arguments, look for offset and size */ + if (arg_off_size(argc - o, argv + o, &dev, &off, &size, + &maxsize) != 0) + return 1; + + nand = &nand_info[dev]; + + memset(&opts, 0, sizeof(opts)); + opts.offset = off; + opts.length = size; + opts.jffs2 = clean; + opts.quiet = quiet; + opts.spread = spread; + + if (scrub) { + if (!scrub_yes) + puts(scrub_warn); + + if (scrub_yes) + opts.scrub = 1; + else if (getc() == 'y') { + puts("y"); + if (getc() == '\r') + opts.scrub = 1; + else { + puts("scrub aborted\n"); + return 1; + } + } else { + puts("scrub aborted\n"); + return 1; + } + } + ret = nand_erase_opts(nand, &opts); + printf("%s\n", ret ? "ERROR" : "OK"); + + return ret == 0 ? 0 : 1; + } + + if (strncmp(cmd, "dump", 4) == 0) { + if (argc < 3) + goto usage; + + off = (int)simple_strtoul(argv[2], NULL, 16); + ret = nand_dump(nand, off, !strcmp(&cmd[4], ".oob"), repeat); + + return ret == 0 ? 1 : 0; + } + + if (strncmp(cmd, "read", 4) == 0 || strncmp(cmd, "write", 5) == 0) { + size_t rwsize; + ulong pagecount = 1; + int read; + int raw = 0; + + if (argc < 4) + goto usage; + + addr = (ulong)simple_strtoul(argv[2], NULL, 16); + + read = strncmp(cmd, "read", 4) == 0; /* 1 = read, 0 = write */ + printf("\nNAND %s: ", read ? "read" : "write"); + + nand = &nand_info[dev]; + + s = strchr(cmd, '.'); + + if (s && !strcmp(s, ".raw")) { + raw = 1; + + if (arg_off(argv[3], &dev, &off, &size, &maxsize)) + return 1; + + if (argc > 4 && !str2long(argv[4], &pagecount)) { + printf("'%s' is not a number\n", argv[4]); + return 1; + } + + if (pagecount * nand->writesize > size) { + puts("Size exceeds partition or device limit\n"); + return -1; + } + + rwsize = pagecount * (nand->writesize + nand->oobsize); + } else { + if (arg_off_size(argc - 3, argv + 3, &dev, + &off, &size, &maxsize) != 0) + return 1; + + /* size is unspecified */ + if (argc < 5) + adjust_size_for_badblocks(&size, off, dev); + rwsize = size; + } + + if (!s || !strcmp(s, ".jffs2") || + !strcmp(s, ".e") || !strcmp(s, ".i")) { + if (read) + ret = nand_read_skip_bad(nand, off, &rwsize, + NULL, maxsize, + (u_char *)addr); + else + ret = nand_write_skip_bad(nand, off, &rwsize, + NULL, maxsize, + (u_char *)addr, 0); +#ifdef CONFIG_CMD_NAND_TRIMFFS + } else if (!strcmp(s, ".trimffs")) { + if (read) { + printf("Unknown nand command suffix '%s'\n", s); + return 1; + } + ret = nand_write_skip_bad(nand, off, &rwsize, NULL, + maxsize, (u_char *)addr, + WITH_DROP_FFS); +#endif +#ifdef CONFIG_CMD_NAND_YAFFS + } else if (!strcmp(s, ".yaffs")) { + if (read) { + printf("Unknown nand command suffix '%s'.\n", s); + return 1; + } + ret = nand_write_skip_bad(nand, off, &rwsize, NULL, + maxsize, (u_char *)addr, + WITH_YAFFS_OOB); +#endif + } else if (!strcmp(s, ".oob")) { + /* out-of-band data */ + mtd_oob_ops_t ops = { + .oobbuf = (u8 *)addr, + .ooblen = rwsize, + .mode = MTD_OPS_RAW + }; + + if (read) + ret = mtd_read_oob(nand, off, &ops); + else + ret = mtd_write_oob(nand, off, &ops); + } else if (raw) { + ret = raw_access(nand, addr, off, pagecount, read); + } else { + printf("Unknown nand command suffix '%s'.\n", s); + return 1; + } + + printf(" %zu bytes %s: %s\n", rwsize, + read ? "read" : "written", ret ? "ERROR" : "OK"); + + return ret == 0 ? 0 : 1; + } + +#ifdef CONFIG_CMD_NAND_TORTURE + if (strcmp(cmd, "torture") == 0) { + if (argc < 3) + goto usage; + + if (!str2off(argv[2], &off)) { + puts("Offset is not a valid number\n"); + return 1; + } + + printf("\nNAND torture: device %d offset 0x%llx size 0x%x\n", + dev, off, nand->erasesize); + ret = nand_torture(nand, off); + printf(" %s\n", ret ? "Failed" : "Passed"); + + return ret == 0 ? 0 : 1; + } +#endif + + if (strcmp(cmd, "markbad") == 0) { + argc -= 2; + argv += 2; + + if (argc <= 0) + goto usage; + + while (argc > 0) { + addr = simple_strtoul(*argv, NULL, 16); + + if (mtd_block_markbad(nand, addr)) { + printf("block 0x%08lx NOT marked " + "as bad! ERROR %d\n", + addr, ret); + ret = 1; + } else { + printf("block 0x%08lx successfully " + "marked as bad\n", + addr); + } + --argc; + ++argv; + } + return ret; + } + + if (strcmp(cmd, "biterr") == 0) { + /* todo */ + return 1; + } + +#ifdef CONFIG_CMD_NAND_LOCK_UNLOCK + if (strcmp(cmd, "lock") == 0) { + int tight = 0; + int status = 0; + if (argc == 3) { + if (!strcmp("tight", argv[2])) + tight = 1; + if (!strcmp("status", argv[2])) + status = 1; + } + if (status) { + do_nand_status(nand); + } else { + if (!nand_lock(nand, tight)) { + puts("NAND flash successfully locked\n"); + } else { + puts("Error locking NAND flash\n"); + return 1; + } + } + return 0; + } + + if (strncmp(cmd, "unlock", 5) == 0) { + int allexcept = 0; + + s = strchr(cmd, '.'); + + if (s && !strcmp(s, ".allexcept")) + allexcept = 1; + + if (arg_off_size(argc - 2, argv + 2, &dev, &off, &size, + &maxsize) < 0) + return 1; + + if (!nand_unlock(&nand_info[dev], off, size, allexcept)) { + puts("NAND flash successfully unlocked\n"); + } else { + puts("Error unlocking NAND flash, " + "write and erase will probably fail\n"); + return 1; + } + return 0; + } +#endif + +usage: + return CMD_RET_USAGE; +} + +#ifdef CONFIG_SYS_LONGHELP +static char nand_help_text[] = + "info - show available NAND devices\n" + "nand device [dev] - show or set current device\n" + "nand read - addr off|partition size\n" + "nand write - addr off|partition size\n" + " read/write 'size' bytes starting at offset 'off'\n" + " to/from memory address 'addr', skipping bad blocks.\n" + "nand read.raw - addr off|partition [count]\n" + "nand write.raw - addr off|partition [count]\n" + " Use read.raw/write.raw to avoid ECC and access the flash as-is.\n" +#ifdef CONFIG_CMD_NAND_TRIMFFS + "nand write.trimffs - addr off|partition size\n" + " write 'size' bytes starting at offset 'off' from memory address\n" + " 'addr', skipping bad blocks and dropping any pages at the end\n" + " of eraseblocks that contain only 0xFF\n" +#endif +#ifdef CONFIG_CMD_NAND_YAFFS + "nand write.yaffs - addr off|partition size\n" + " write 'size' bytes starting at offset 'off' with yaffs format\n" + " from memory address 'addr', skipping bad blocks.\n" +#endif + "nand erase[.spread] [clean] off size - erase 'size' bytes " + "from offset 'off'\n" + " With '.spread', erase enough for given file size, otherwise,\n" + " 'size' includes skipped bad blocks.\n" + "nand erase.part [clean] partition - erase entire mtd partition'\n" + "nand erase.chip [clean] - erase entire chip'\n" + "nand bad - show bad blocks\n" + "nand dump[.oob] off - dump page\n" +#ifdef CONFIG_CMD_NAND_TORTURE + "nand torture off - torture block at offset\n" +#endif + "nand scrub [-y] off size | scrub.part partition | scrub.chip\n" + " really clean NAND erasing bad blocks (UNSAFE)\n" + "nand markbad off [...] - mark bad block(s) at offset (UNSAFE)\n" + "nand biterr off - make a bit error at offset (UNSAFE)" +#ifdef CONFIG_CMD_NAND_LOCK_UNLOCK + "\n" + "nand lock [tight] [status]\n" + " bring nand to lock state or display locked pages\n" + "nand unlock[.allexcept] [offset] [size] - unlock section" +#endif +#ifdef CONFIG_ENV_OFFSET_OOB + "\n" + "nand env.oob - environment offset in OOB of block 0 of" + " first device.\n" + "nand env.oob set off|partition - set enviromnent offset\n" + "nand env.oob get - get environment offset" +#endif + ""; +#endif + +U_BOOT_CMD( + nand, CONFIG_SYS_MAXARGS, 1, do_nand, + "NAND sub-system", nand_help_text +); + +static int nand_load_image(cmd_tbl_t *cmdtp, nand_info_t *nand, + ulong offset, ulong addr, char *cmd) +{ + int r; + char *s; + size_t cnt; + image_header_t *hdr; +#if defined(CONFIG_FIT) + const void *fit_hdr = NULL; +#endif + + s = strchr(cmd, '.'); + if (s != NULL && + (strcmp(s, ".jffs2") && strcmp(s, ".e") && strcmp(s, ".i"))) { + printf("Unknown nand load suffix '%s'\n", s); + bootstage_error(BOOTSTAGE_ID_NAND_SUFFIX); + return 1; + } + + printf("\nLoading from %s, offset 0x%lx\n", nand->name, offset); + + cnt = nand->writesize; + r = nand_read_skip_bad(nand, offset, &cnt, NULL, nand->size, + (u_char *)addr); + if (r) { + puts("** Read error\n"); + bootstage_error(BOOTSTAGE_ID_NAND_HDR_READ); + return 1; + } + bootstage_mark(BOOTSTAGE_ID_NAND_HDR_READ); + + switch (genimg_get_format ((void *)addr)) { + case IMAGE_FORMAT_LEGACY: + hdr = (image_header_t *)addr; + + bootstage_mark(BOOTSTAGE_ID_NAND_TYPE); + image_print_contents (hdr); + + cnt = image_get_image_size (hdr); + break; +#if defined(CONFIG_FIT) + case IMAGE_FORMAT_FIT: + fit_hdr = (const void *)addr; + puts ("Fit image detected...\n"); + + cnt = fit_get_size (fit_hdr); + break; +#endif + default: + bootstage_error(BOOTSTAGE_ID_NAND_TYPE); + puts ("** Unknown image type\n"); + return 1; + } + bootstage_mark(BOOTSTAGE_ID_NAND_TYPE); + + r = nand_read_skip_bad(nand, offset, &cnt, NULL, nand->size, + (u_char *)addr); + if (r) { + puts("** Read error\n"); + bootstage_error(BOOTSTAGE_ID_NAND_READ); + return 1; + } + bootstage_mark(BOOTSTAGE_ID_NAND_READ); + +#if defined(CONFIG_FIT) + /* This cannot be done earlier, we need complete FIT image in RAM first */ + if (genimg_get_format ((void *)addr) == IMAGE_FORMAT_FIT) { + if (!fit_check_format (fit_hdr)) { + bootstage_error(BOOTSTAGE_ID_NAND_FIT_READ); + puts ("** Bad FIT image format\n"); + return 1; + } + bootstage_mark(BOOTSTAGE_ID_NAND_FIT_READ_OK); + fit_print_contents (fit_hdr); + } +#endif + + /* Loading ok, update default load address */ + + load_addr = addr; + + return bootm_maybe_autostart(cmdtp, cmd); +} + +static int do_nandboot(cmd_tbl_t *cmdtp, int flag, int argc, + char * const argv[]) +{ + char *boot_device = NULL; + int idx; + ulong addr, offset = 0; +#if defined(CONFIG_CMD_MTDPARTS) + struct mtd_device *dev; + struct part_info *part; + u8 pnum; + + if (argc >= 2) { + char *p = (argc == 2) ? argv[1] : argv[2]; + if (!(str2long(p, &addr)) && (mtdparts_init() == 0) && + (find_dev_and_part(p, &dev, &pnum, &part) == 0)) { + if (dev->id->type != MTD_DEV_TYPE_NAND) { + puts("Not a NAND device\n"); + return 1; + } + if (argc > 3) + goto usage; + if (argc == 3) + addr = simple_strtoul(argv[1], NULL, 16); + else + addr = CONFIG_SYS_LOAD_ADDR; + return nand_load_image(cmdtp, &nand_info[dev->id->num], + part->offset, addr, argv[0]); + } + } +#endif + + bootstage_mark(BOOTSTAGE_ID_NAND_PART); + switch (argc) { + case 1: + addr = CONFIG_SYS_LOAD_ADDR; + boot_device = getenv("bootdevice"); + break; + case 2: + addr = simple_strtoul(argv[1], NULL, 16); + boot_device = getenv("bootdevice"); + break; + case 3: + addr = simple_strtoul(argv[1], NULL, 16); + boot_device = argv[2]; + break; + case 4: + addr = simple_strtoul(argv[1], NULL, 16); + boot_device = argv[2]; + offset = simple_strtoul(argv[3], NULL, 16); + break; + default: +#if defined(CONFIG_CMD_MTDPARTS) +usage: +#endif + bootstage_error(BOOTSTAGE_ID_NAND_SUFFIX); + return CMD_RET_USAGE; + } + bootstage_mark(BOOTSTAGE_ID_NAND_SUFFIX); + + if (!boot_device) { + puts("\n** No boot device **\n"); + bootstage_error(BOOTSTAGE_ID_NAND_BOOT_DEVICE); + return 1; + } + bootstage_mark(BOOTSTAGE_ID_NAND_BOOT_DEVICE); + + idx = simple_strtoul(boot_device, NULL, 16); + + if (idx < 0 || idx >= CONFIG_SYS_MAX_NAND_DEVICE || !nand_info[idx].name) { + printf("\n** Device %d not available\n", idx); + bootstage_error(BOOTSTAGE_ID_NAND_AVAILABLE); + return 1; + } + bootstage_mark(BOOTSTAGE_ID_NAND_AVAILABLE); + + return nand_load_image(cmdtp, &nand_info[idx], offset, addr, argv[0]); +} + +U_BOOT_CMD(nboot, 4, 1, do_nandboot, + "boot from NAND device", + "[partition] | [[[loadAddr] dev] offset]" +); diff --git a/qemu/roms/u-boot/common/cmd_net.c b/qemu/roms/u-boot/common/cmd_net.c new file mode 100644 index 000000000..09489d404 --- /dev/null +++ b/qemu/roms/u-boot/common/cmd_net.c @@ -0,0 +1,447 @@ +/* + * (C) Copyright 2000 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/* + * Boot support + */ +#include <common.h> +#include <command.h> +#include <net.h> + +static int netboot_common(enum proto_t, cmd_tbl_t *, int, char * const []); + +static int do_bootp(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + return netboot_common(BOOTP, cmdtp, argc, argv); +} + +U_BOOT_CMD( + bootp, 3, 1, do_bootp, + "boot image via network using BOOTP/TFTP protocol", + "[loadAddress] [[hostIPaddr:]bootfilename]" +); + +int do_tftpb(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + int ret; + + bootstage_mark_name(BOOTSTAGE_KERNELREAD_START, "tftp_start"); + ret = netboot_common(TFTPGET, cmdtp, argc, argv); + bootstage_mark_name(BOOTSTAGE_KERNELREAD_STOP, "tftp_done"); + return ret; +} + +U_BOOT_CMD( + tftpboot, 3, 1, do_tftpb, + "boot image via network using TFTP protocol", + "[loadAddress] [[hostIPaddr:]bootfilename]" +); + +#ifdef CONFIG_CMD_TFTPPUT +int do_tftpput(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + int ret; + + ret = netboot_common(TFTPPUT, cmdtp, argc, argv); + return ret; +} + +U_BOOT_CMD( + tftpput, 4, 1, do_tftpput, + "TFTP put command, for uploading files to a server", + "Address Size [[hostIPaddr:]filename]" +); +#endif + +#ifdef CONFIG_CMD_TFTPSRV +static int do_tftpsrv(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[]) +{ + return netboot_common(TFTPSRV, cmdtp, argc, argv); +} + +U_BOOT_CMD( + tftpsrv, 2, 1, do_tftpsrv, + "act as a TFTP server and boot the first received file", + "[loadAddress]\n" + "Listen for an incoming TFTP transfer, receive a file and boot it.\n" + "The transfer is aborted if a transfer has not been started after\n" + "about 50 seconds or if Ctrl-C is pressed." +); +#endif + + +#ifdef CONFIG_CMD_RARP +int do_rarpb(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + return netboot_common(RARP, cmdtp, argc, argv); +} + +U_BOOT_CMD( + rarpboot, 3, 1, do_rarpb, + "boot image via network using RARP/TFTP protocol", + "[loadAddress] [[hostIPaddr:]bootfilename]" +); +#endif + +#if defined(CONFIG_CMD_DHCP) +static int do_dhcp(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + return netboot_common(DHCP, cmdtp, argc, argv); +} + +U_BOOT_CMD( + dhcp, 3, 1, do_dhcp, + "boot image via network using DHCP/TFTP protocol", + "[loadAddress] [[hostIPaddr:]bootfilename]" +); +#endif + +#if defined(CONFIG_CMD_NFS) +static int do_nfs(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + return netboot_common(NFS, cmdtp, argc, argv); +} + +U_BOOT_CMD( + nfs, 3, 1, do_nfs, + "boot image via network using NFS protocol", + "[loadAddress] [[hostIPaddr:]bootfilename]" +); +#endif + +static void netboot_update_env(void) +{ + char tmp[22]; + + if (NetOurGatewayIP) { + ip_to_string(NetOurGatewayIP, tmp); + setenv("gatewayip", tmp); + } + + if (NetOurSubnetMask) { + ip_to_string(NetOurSubnetMask, tmp); + setenv("netmask", tmp); + } + + if (NetOurHostName[0]) + setenv("hostname", NetOurHostName); + + if (NetOurRootPath[0]) + setenv("rootpath", NetOurRootPath); + + if (NetOurIP) { + ip_to_string(NetOurIP, tmp); + setenv("ipaddr", tmp); + } +#if !defined(CONFIG_BOOTP_SERVERIP) + /* + * Only attempt to change serverip if net/bootp.c:BootpCopyNetParams() + * could have set it + */ + if (NetServerIP) { + ip_to_string(NetServerIP, tmp); + setenv("serverip", tmp); + } +#endif + if (NetOurDNSIP) { + ip_to_string(NetOurDNSIP, tmp); + setenv("dnsip", tmp); + } +#if defined(CONFIG_BOOTP_DNS2) + if (NetOurDNS2IP) { + ip_to_string(NetOurDNS2IP, tmp); + setenv("dnsip2", tmp); + } +#endif + if (NetOurNISDomain[0]) + setenv("domain", NetOurNISDomain); + +#if defined(CONFIG_CMD_SNTP) \ + && defined(CONFIG_BOOTP_TIMEOFFSET) + if (NetTimeOffset) { + sprintf(tmp, "%d", NetTimeOffset); + setenv("timeoffset", tmp); + } +#endif +#if defined(CONFIG_CMD_SNTP) \ + && defined(CONFIG_BOOTP_NTPSERVER) + if (NetNtpServerIP) { + ip_to_string(NetNtpServerIP, tmp); + setenv("ntpserverip", tmp); + } +#endif +} + +static int netboot_common(enum proto_t proto, cmd_tbl_t *cmdtp, int argc, + char * const argv[]) +{ + char *s; + char *end; + int rcode = 0; + int size; + ulong addr; + + /* pre-set load_addr */ + if ((s = getenv("loadaddr")) != NULL) { + load_addr = simple_strtoul(s, NULL, 16); + } + + switch (argc) { + case 1: + break; + + case 2: /* + * Only one arg - accept two forms: + * Just load address, or just boot file name. The latter + * form must be written in a format which can not be + * mis-interpreted as a valid number. + */ + addr = simple_strtoul(argv[1], &end, 16); + if (end == (argv[1] + strlen(argv[1]))) + load_addr = addr; + else + copy_filename(BootFile, argv[1], sizeof(BootFile)); + break; + + case 3: load_addr = simple_strtoul(argv[1], NULL, 16); + copy_filename(BootFile, argv[2], sizeof(BootFile)); + + break; + +#ifdef CONFIG_CMD_TFTPPUT + case 4: + if (strict_strtoul(argv[1], 16, &save_addr) < 0 || + strict_strtoul(argv[2], 16, &save_size) < 0) { + printf("Invalid address/size\n"); + return cmd_usage(cmdtp); + } + copy_filename(BootFile, argv[3], sizeof(BootFile)); + break; +#endif + default: + bootstage_error(BOOTSTAGE_ID_NET_START); + return CMD_RET_USAGE; + } + bootstage_mark(BOOTSTAGE_ID_NET_START); + + if ((size = NetLoop(proto)) < 0) { + bootstage_error(BOOTSTAGE_ID_NET_NETLOOP_OK); + return 1; + } + bootstage_mark(BOOTSTAGE_ID_NET_NETLOOP_OK); + + /* NetLoop ok, update environment */ + netboot_update_env(); + + /* done if no file was loaded (no errors though) */ + if (size == 0) { + bootstage_error(BOOTSTAGE_ID_NET_LOADED); + return 0; + } + + /* flush cache */ + flush_cache(load_addr, size); + + bootstage_mark(BOOTSTAGE_ID_NET_LOADED); + + rcode = bootm_maybe_autostart(cmdtp, argv[0]); + + if (rcode < 0) + bootstage_error(BOOTSTAGE_ID_NET_DONE_ERR); + else + bootstage_mark(BOOTSTAGE_ID_NET_DONE); + return rcode; +} + +#if defined(CONFIG_CMD_PING) +static int do_ping(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + if (argc < 2) + return -1; + + NetPingIP = string_to_ip(argv[1]); + if (NetPingIP == 0) + return CMD_RET_USAGE; + + if (NetLoop(PING) < 0) { + printf("ping failed; host %s is not alive\n", argv[1]); + return 1; + } + + printf("host %s is alive\n", argv[1]); + + return 0; +} + +U_BOOT_CMD( + ping, 2, 1, do_ping, + "send ICMP ECHO_REQUEST to network host", + "pingAddress" +); +#endif + +#if defined(CONFIG_CMD_CDP) + +static void cdp_update_env(void) +{ + char tmp[16]; + + if (CDPApplianceVLAN != htons(-1)) { + printf("CDP offered appliance VLAN %d\n", ntohs(CDPApplianceVLAN)); + VLAN_to_string(CDPApplianceVLAN, tmp); + setenv("vlan", tmp); + NetOurVLAN = CDPApplianceVLAN; + } + + if (CDPNativeVLAN != htons(-1)) { + printf("CDP offered native VLAN %d\n", ntohs(CDPNativeVLAN)); + VLAN_to_string(CDPNativeVLAN, tmp); + setenv("nvlan", tmp); + NetOurNativeVLAN = CDPNativeVLAN; + } + +} + +int do_cdp(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + int r; + + r = NetLoop(CDP); + if (r < 0) { + printf("cdp failed; perhaps not a CISCO switch?\n"); + return 1; + } + + cdp_update_env(); + + return 0; +} + +U_BOOT_CMD( + cdp, 1, 1, do_cdp, + "Perform CDP network configuration", + "\n" +); +#endif + +#if defined(CONFIG_CMD_SNTP) +int do_sntp(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + char *toff; + + if (argc < 2) { + NetNtpServerIP = getenv_IPaddr("ntpserverip"); + if (NetNtpServerIP == 0) { + printf("ntpserverip not set\n"); + return (1); + } + } else { + NetNtpServerIP = string_to_ip(argv[1]); + if (NetNtpServerIP == 0) { + printf("Bad NTP server IP address\n"); + return (1); + } + } + + toff = getenv("timeoffset"); + if (toff == NULL) + NetTimeOffset = 0; + else + NetTimeOffset = simple_strtol(toff, NULL, 10); + + if (NetLoop(SNTP) < 0) { + printf("SNTP failed: host %pI4 not responding\n", + &NetNtpServerIP); + return 1; + } + + return 0; +} + +U_BOOT_CMD( + sntp, 2, 1, do_sntp, + "synchronize RTC via network", + "[NTP server IP]\n" +); +#endif + +#if defined(CONFIG_CMD_DNS) +int do_dns(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + if (argc == 1) + return CMD_RET_USAGE; + + /* + * We should check for a valid hostname: + * - Each label must be between 1 and 63 characters long + * - the entire hostname has a maximum of 255 characters + * - only the ASCII letters 'a' through 'z' (case-insensitive), + * the digits '0' through '9', and the hyphen + * - cannot begin or end with a hyphen + * - no other symbols, punctuation characters, or blank spaces are + * permitted + * but hey - this is a minimalist implmentation, so only check length + * and let the name server deal with things. + */ + if (strlen(argv[1]) >= 255) { + printf("dns error: hostname too long\n"); + return 1; + } + + NetDNSResolve = argv[1]; + + if (argc == 3) + NetDNSenvvar = argv[2]; + else + NetDNSenvvar = NULL; + + if (NetLoop(DNS) < 0) { + printf("dns lookup of %s failed, check setup\n", argv[1]); + return 1; + } + + return 0; +} + +U_BOOT_CMD( + dns, 3, 1, do_dns, + "lookup the IP of a hostname", + "hostname [envvar]" +); + +#endif /* CONFIG_CMD_DNS */ + +#if defined(CONFIG_CMD_LINK_LOCAL) +static int do_link_local(cmd_tbl_t *cmdtp, int flag, int argc, + char * const argv[]) +{ + char tmp[22]; + + if (NetLoop(LINKLOCAL) < 0) + return 1; + + NetOurGatewayIP = 0; + ip_to_string(NetOurGatewayIP, tmp); + setenv("gatewayip", tmp); + + ip_to_string(NetOurSubnetMask, tmp); + setenv("netmask", tmp); + + ip_to_string(NetOurIP, tmp); + setenv("ipaddr", tmp); + setenv("llipaddr", tmp); /* store this for next time */ + + return 0; +} + +U_BOOT_CMD( + linklocal, 1, 1, do_link_local, + "acquire a network IP address using the link-local protocol", + "" +); + +#endif /* CONFIG_CMD_LINK_LOCAL */ diff --git a/qemu/roms/u-boot/common/cmd_nvedit.c b/qemu/roms/u-boot/common/cmd_nvedit.c new file mode 100644 index 000000000..f4e306ceb --- /dev/null +++ b/qemu/roms/u-boot/common/cmd_nvedit.c @@ -0,0 +1,1270 @@ +/* + * (C) Copyright 2000-2013 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * (C) Copyright 2001 Sysgo Real-Time Solutions, GmbH <www.elinos.com> + * Andreas Heppel <aheppel@sysgo.de> + * + * Copyright 2011 Freescale Semiconductor, Inc. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/* + * Support for persistent environment data + * + * The "environment" is stored on external storage as a list of '\0' + * terminated "name=value" strings. The end of the list is marked by + * a double '\0'. The environment is preceeded by a 32 bit CRC over + * the data part and, in case of redundant environment, a byte of + * flags. + * + * This linearized representation will also be used before + * relocation, i. e. as long as we don't have a full C runtime + * environment. After that, we use a hash table. + */ + +#include <common.h> +#include <command.h> +#include <environment.h> +#include <search.h> +#include <errno.h> +#include <malloc.h> +#include <watchdog.h> +#include <linux/stddef.h> +#include <asm/byteorder.h> +#include <asm/io.h> + +DECLARE_GLOBAL_DATA_PTR; + +#if !defined(CONFIG_ENV_IS_IN_EEPROM) && \ + !defined(CONFIG_ENV_IS_IN_FLASH) && \ + !defined(CONFIG_ENV_IS_IN_DATAFLASH) && \ + !defined(CONFIG_ENV_IS_IN_MMC) && \ + !defined(CONFIG_ENV_IS_IN_FAT) && \ + !defined(CONFIG_ENV_IS_IN_NAND) && \ + !defined(CONFIG_ENV_IS_IN_NVRAM) && \ + !defined(CONFIG_ENV_IS_IN_ONENAND) && \ + !defined(CONFIG_ENV_IS_IN_SPI_FLASH) && \ + !defined(CONFIG_ENV_IS_IN_REMOTE) && \ + !defined(CONFIG_ENV_IS_IN_UBI) && \ + !defined(CONFIG_ENV_IS_NOWHERE) +# error Define one of CONFIG_ENV_IS_IN_{EEPROM|FLASH|DATAFLASH|ONENAND|\ +SPI_FLASH|NVRAM|MMC|FAT|REMOTE|UBI} or CONFIG_ENV_IS_NOWHERE +#endif + +/* + * Maximum expected input data size for import command + */ +#define MAX_ENV_SIZE (1 << 20) /* 1 MiB */ + +/* + * 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 + * variable only if the environment was changed ... done so for + * example in NetInitLoop() + */ +static int env_id = 1; + +int get_env_id(void) +{ + return env_id; +} + +#ifndef CONFIG_SPL_BUILD +/* + * Command interface: print one or all environment variables + * + * Returns 0 in case of error, or length of printed string + */ +static int env_print(char *name, int flag) +{ + char *res = NULL; + ssize_t len; + + if (name) { /* print a single name */ + ENTRY e, *ep; + + e.key = name; + e.data = NULL; + hsearch_r(e, FIND, &ep, &env_htab, flag); + if (ep == NULL) + return 0; + len = printf("%s=%s\n", ep->key, ep->data); + return len; + } + + /* print whole list */ + len = hexport_r(&env_htab, '\n', flag, &res, 0, 0, NULL); + + if (len > 0) { + puts(res); + free(res); + return len; + } + + /* should never happen */ + printf("## Error: cannot export environment\n"); + return 0; +} + +static int do_env_print(cmd_tbl_t *cmdtp, int flag, int argc, + char * const argv[]) +{ + 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, env_flag); + if (!rcode) + return 1; + printf("\nEnvironment size: %d/%ld bytes\n", + rcode, (ulong)ENV_SIZE); + return 0; + } + + /* print selected env vars */ + env_flag &= ~H_HIDE_DOT; + for (i = 1; i < argc; ++i) { + int rc = env_print(argv[i], env_flag); + if (!rc) { + printf("## Error: \"%s\" not defined\n", argv[i]); + ++rcode; + } + } + + return rcode; +} + +#ifdef CONFIG_CMD_GREPENV +static int do_env_grep(cmd_tbl_t *cmdtp, int flag, + int argc, char * const argv[]) +{ + char *res = NULL; + int len, grep_how, grep_what; + + if (argc < 2) + return CMD_RET_USAGE; + + grep_how = H_MATCH_SUBSTR; /* default: substring search */ + grep_what = H_MATCH_BOTH; /* default: grep names and values */ + + while (--argc > 0 && **++argv == '-') { + char *arg = *argv; + while (*++arg) { + switch (*arg) { +#ifdef CONFIG_REGEX + case 'e': /* use regex matching */ + grep_how = H_MATCH_REGEX; + break; +#endif + case 'n': /* grep for name */ + grep_what = H_MATCH_KEY; + break; + case 'v': /* grep for value */ + grep_what = H_MATCH_DATA; + break; + case 'b': /* grep for both */ + grep_what = H_MATCH_BOTH; + break; + case '-': + goto DONE; + default: + return CMD_RET_USAGE; + } + } + } + +DONE: + len = hexport_r(&env_htab, '\n', + flag | grep_what | grep_how, + &res, 0, argc, argv); + + if (len > 0) { + puts(res); + free(res); + } + + if (len < 2) + return 1; + + return 0; +} +#endif +#endif /* CONFIG_SPL_BUILD */ + +/* + * 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; + 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]; + + if (strchr(name, '=')) { + printf("## Error: illegal character '='" + "in variable name \"%s\"\n", name); + return 1; + } + + env_id++; + + /* Delete only ? */ + if (argc < 3 || argv[2] == NULL) { + int rc = hdelete_r(name, &env_htab, env_flag); + return !rc; + } + + /* + * Insert / replace new value + */ + for (i = 2, len = 0; i < argc; ++i) + len += strlen(argv[i]) + 1; + + value = malloc(len); + if (value == NULL) { + printf("## Can't malloc %d bytes\n", len); + return 1; + } + for (i = 2, s = value; i < argc; ++i) { + char *v = argv[i]; + + while ((*s++ = *v++) != '\0') + ; + *(s - 1) = ' '; + } + if (s != value) + *--s = '\0'; + + e.key = name; + e.data = value; + hsearch_r(e, ENTER, &ep, &env_htab, env_flag); + free(value); + if (!ep) { + printf("## Error inserting \"%s\" variable, errno=%d\n", + name, errno); + return 1; + } + + return 0; +} + +int setenv(const char *varname, const char *varvalue) +{ + const char * const argv[4] = { "setenv", varname, varvalue, NULL }; + + /* before import into hashtable */ + if (!(gd->flags & GD_FLG_ENV_READY)) + return 1; + + if (varvalue == NULL || varvalue[0] == '\0') + return _do_env_set(0, 2, (char * const *)argv); + else + return _do_env_set(0, 3, (char * const *)argv); +} + +/** + * Set an environment variable to an integer value + * + * @param varname Environment variable to set + * @param value Value to set it to + * @return 0 if ok, 1 on error + */ +int setenv_ulong(const char *varname, ulong value) +{ + /* TODO: this should be unsigned */ + char *str = simple_itoa(value); + + return setenv(varname, str); +} + +/** + * Set an environment variable to an value in hex + * + * @param varname Environment variable to set + * @param value Value to set it to + * @return 0 if ok, 1 on error + */ +int setenv_hex(const char *varname, ulong value) +{ + char str[17]; + + sprintf(str, "%lx", value); + return setenv(varname, str); +} + +ulong getenv_hex(const char *varname, ulong default_val) +{ + const char *s; + ulong value; + char *endp; + + s = getenv(varname); + if (s) + value = simple_strtoul(s, &endp, 16); + if (!s || endp == s) + return default_val; + + return value; +} + +#ifndef CONFIG_SPL_BUILD +static int do_env_set(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + if (argc < 2) + return CMD_RET_USAGE; + + return _do_env_set(flag, argc, argv); +} + +/* + * Prompt for environment variable + */ +#if defined(CONFIG_CMD_ASKENV) +int do_env_ask(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + char message[CONFIG_SYS_CBSIZE]; + int i, len, pos, size; + char *local_args[4]; + char *endptr; + + local_args[0] = argv[0]; + local_args[1] = argv[1]; + local_args[2] = NULL; + local_args[3] = NULL; + + /* + * Check the syntax: + * + * env_ask envname [message1 ...] [size] + */ + if (argc == 1) + return CMD_RET_USAGE; + + /* + * We test the last argument if it can be converted + * into a decimal number. If yes, we assume it's + * the size. Otherwise we echo it as part of the + * message. + */ + i = simple_strtoul(argv[argc - 1], &endptr, 10); + if (*endptr != '\0') { /* no size */ + size = CONFIG_SYS_CBSIZE - 1; + } else { /* size given */ + size = i; + --argc; + } + + if (argc <= 2) { + sprintf(message, "Please enter '%s': ", argv[1]); + } else { + /* env_ask envname message1 ... messagen [size] */ + for (i = 2, pos = 0; i < argc; i++) { + if (pos) + message[pos++] = ' '; + + strcpy(message + pos, argv[i]); + pos += strlen(argv[i]); + } + message[pos++] = ' '; + message[pos] = '\0'; + } + + if (size >= CONFIG_SYS_CBSIZE) + size = CONFIG_SYS_CBSIZE - 1; + + if (size <= 0) + return 1; + + /* prompt for input */ + len = readline(message); + + if (size < len) + console_buffer[size] = '\0'; + + len = 2; + if (console_buffer[0] != '\0') { + local_args[2] = console_buffer; + len = 3; + } + + /* Continue calling setenv code */ + return _do_env_set(flag, len, local_args); +} +#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 + +#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); + enum env_flags_varaccess access = env_flags_parse_varaccess(flags); + + printf("\t%-20s %-20s %-20s\n", var_name, + env_flags_get_vartype_name(type), + env_flags_get_varaccess_name(access)); + + return 0; +} + +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); + 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; +} + +/* + * 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 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 %-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 %-20s\n", "Variable Name", "Variable Type", + "Variable Access"); + printf("\t%-20s %-20s %-20s\n", "-------------", "-------------", + "---------------"); + hwalk_r(&env_htab, print_active_flags); + return 0; +} +#endif + +/* + * Interactively edit an environment variable + */ +#if defined(CONFIG_CMD_EDITENV) +static int do_env_edit(cmd_tbl_t *cmdtp, int flag, int argc, + char * const argv[]) +{ + char buffer[CONFIG_SYS_CBSIZE]; + char *init_val; + + if (argc < 2) + return CMD_RET_USAGE; + + /* Set read buffer to initial value or empty sting */ + init_val = getenv(argv[1]); + if (init_val) + sprintf(buffer, "%s", init_val); + else + buffer[0] = '\0'; + + if (readline_into_buffer("edit: ", buffer, 0) < 0) + return 1; + + return setenv(argv[1], buffer); +} +#endif /* CONFIG_CMD_EDITENV */ +#endif /* CONFIG_SPL_BUILD */ + +/* + * Look up variable from environment, + * return address of storage for that variable, + * or NULL if not found + */ +char *getenv(const char *name) +{ + if (gd->flags & GD_FLG_ENV_READY) { /* after import into hashtable */ + ENTRY e, *ep; + + WATCHDOG_RESET(); + + e.key = name; + e.data = NULL; + hsearch_r(e, FIND, &ep, &env_htab, 0); + + return ep ? ep->data : NULL; + } + + /* restricted capabilities before import */ + if (getenv_f(name, (char *)(gd->env_buf), sizeof(gd->env_buf)) > 0) + return (char *)(gd->env_buf); + + return NULL; +} + +/* + * Look up variable from environment for restricted C runtime env. + */ +int getenv_f(const char *name, char *buf, unsigned len) +{ + int i, nxt; + + for (i = 0; env_get_char(i) != '\0'; i = nxt + 1) { + int val, n; + + for (nxt = i; env_get_char(nxt) != '\0'; ++nxt) { + if (nxt >= CONFIG_ENV_SIZE) + return -1; + } + + val = envmatch((uchar *)name, i); + if (val < 0) + continue; + + /* found; copy out */ + for (n = 0; n < len; ++n, ++buf) { + *buf = env_get_char(val++); + if (*buf == '\0') + return n; + } + + if (n) + *--buf = '\0'; + + printf("env_buf [%d bytes] too small for value of \"%s\"\n", + len, name); + + return n; + } + + return -1; +} + +/** + * Decode the integer value of an environment variable and return it. + * + * @param name Name of environemnt variable + * @param base Number base to use (normally 10, or 16 for hex) + * @param default_val Default value to return if the variable is not + * found + * @return the decoded value, or default_val if not found + */ +ulong getenv_ulong(const char *name, int base, ulong default_val) +{ + /* + * We can use getenv() here, even before relocation, since the + * environment variable value is an integer and thus short. + */ + const char *str = getenv(name); + + return str ? simple_strtoul(str, NULL, base) : default_val; +} + +#ifndef CONFIG_SPL_BUILD +#if defined(CONFIG_CMD_SAVEENV) && !defined(CONFIG_ENV_IS_NOWHERE) +static int do_env_save(cmd_tbl_t *cmdtp, int flag, int argc, + char * const argv[]) +{ + printf("Saving Environment to %s...\n", env_name_spec); + + return saveenv() ? 1 : 0; +} + +U_BOOT_CMD( + saveenv, 1, 0, do_env_save, + "save environment variables to persistent storage", + "" +); +#endif +#endif /* CONFIG_SPL_BUILD */ + + +/* + * Match a name / name=value pair + * + * s1 is either a simple 'name', or a 'name=value' pair. + * i2 is the environment index for a 'name2=value2' pair. + * If the names match, return the index for the value2, else -1. + */ +int envmatch(uchar *s1, int i2) +{ + if (s1 == NULL) + return -1; + + while (*s1 == env_get_char(i2++)) + if (*s1++ == '=') + return i2; + + if (*s1 == '\0' && env_get_char(i2-1) == '=') + return i2; + + return -1; +} + +#ifndef CONFIG_SPL_BUILD +static int do_env_default(cmd_tbl_t *cmdtp, int __flag, + int argc, char * const argv[]) +{ + int all = 0, flag = 0; + + debug("Initial value for argc=%d\n", argc); + while (--argc > 0 && **++argv == '-') { + char *arg = *argv; + + while (*++arg) { + switch (*arg) { + case 'a': /* default all */ + all = 1; + break; + case 'f': /* force */ + flag |= H_FORCE; + break; + default: + return cmd_usage(cmdtp); + } + } + } + debug("Final value for argc=%d\n", argc); + if (all && (argc == 0)) { + /* Reset the whole environment */ + set_default_env("## Resetting to default environment\n"); + return 0; + } + if (!all && (argc > 0)) { + /* Reset individual variables */ + set_default_vars(argc, argv); + return 0; + } + + return cmd_usage(cmdtp); +} + +static int do_env_delete(cmd_tbl_t *cmdtp, int flag, + int argc, char * const argv[]) +{ + 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 +/* + * env export [-t | -b | -c] [-s size] addr [var ...] + * -t: export as text format; if size is given, data will be + * padded with '\0' bytes; if not, one terminating '\0' + * will be added (which is included in the "filesize" + * setting so you can for exmple copy this to flash and + * keep the termination). + * -b: export as binary format (name=value pairs separated by + * '\0', list end marked by double "\0\0") + * -c: export as checksum protected environment format as + * used for example by "saveenv" command + * -s size: + * size of output buffer + * addr: memory address where environment gets stored + * var... List of variable names that get included into the + * export. Without arguments, the whole environment gets + * exported. + * + * With "-c" and size is NOT given, then the export command will + * format the data as currently used for the persistent storage, + * i. e. it will use CONFIG_ENV_SECT_SIZE as output block size and + * prepend a valid CRC32 checksum and, in case of resundant + * environment, a "current" redundancy flag. If size is given, this + * value will be used instead of CONFIG_ENV_SECT_SIZE; again, CRC32 + * checksum and redundancy flag will be inserted. + * + * With "-b" and "-t", always only the real data (including a + * terminating '\0' byte) will be written; here the optional size + * argument will be used to make sure not to overflow the user + * provided buffer; the command will abort if the size is not + * sufficient. Any remainign space will be '\0' padded. + * + * On successful return, the variable "filesize" will be set. + * Note that filesize includes the trailing/terminating '\0' byte(s). + * + * Usage szenario: create a text snapshot/backup of the current settings: + * + * => env export -t 100000 + * => era ${backup_addr} +${filesize} + * => cp.b 100000 ${backup_addr} ${filesize} + * + * Re-import this snapshot, deleting all other settings: + * + * => env import -d -t ${backup_addr} + */ +static int do_env_export(cmd_tbl_t *cmdtp, int flag, + int argc, char * const argv[]) +{ + char buf[32]; + ulong addr; + char *ptr, *cmd, *res; + size_t size = 0; + ssize_t len; + env_t *envp; + char sep = '\n'; + int chk = 0; + int fmt = 0; + + cmd = *argv; + + while (--argc > 0 && **++argv == '-') { + char *arg = *argv; + while (*++arg) { + switch (*arg) { + case 'b': /* raw binary format */ + if (fmt++) + goto sep_err; + sep = '\0'; + break; + case 'c': /* external checksum format */ + if (fmt++) + goto sep_err; + sep = '\0'; + chk = 1; + break; + case 's': /* size given */ + if (--argc <= 0) + return cmd_usage(cmdtp); + size = simple_strtoul(*++argv, NULL, 16); + goto NXTARG; + case 't': /* text format */ + if (fmt++) + goto sep_err; + sep = '\n'; + break; + default: + return CMD_RET_USAGE; + } + } +NXTARG: ; + } + + if (argc < 1) + return CMD_RET_USAGE; + + addr = simple_strtoul(argv[0], NULL, 16); + ptr = map_sysmem(addr, size); + + if (size) + memset(ptr, '\0', size); + + argc--; + argv++; + + if (sep) { /* export as text file */ + len = hexport_r(&env_htab, sep, + H_MATCH_KEY | H_MATCH_IDENT, + &ptr, size, argc, argv); + if (len < 0) { + error("Cannot export environment: errno = %d\n", errno); + return 1; + } + sprintf(buf, "%zX", (size_t)len); + setenv("filesize", buf); + + return 0; + } + + envp = (env_t *)ptr; + + if (chk) /* export as checksum protected block */ + res = (char *)envp->data; + else /* export as raw binary data */ + res = ptr; + + len = hexport_r(&env_htab, '\0', + H_MATCH_KEY | H_MATCH_IDENT, + &res, ENV_SIZE, argc, argv); + if (len < 0) { + error("Cannot export environment: errno = %d\n", errno); + return 1; + } + + if (chk) { + envp->crc = crc32(0, envp->data, ENV_SIZE); +#ifdef CONFIG_ENV_ADDR_REDUND + envp->flags = ACTIVE_FLAG; +#endif + } + setenv_hex("filesize", len + offsetof(env_t, data)); + + return 0; + +sep_err: + printf("## %s: only one of \"-b\", \"-c\" or \"-t\" allowed\n", cmd); + return 1; +} +#endif + +#ifdef CONFIG_CMD_IMPORTENV +/* + * env import [-d] [-t | -b | -c] addr [size] + * -d: delete existing environment before importing; + * otherwise overwrite / append to existion definitions + * -t: assume text format; either "size" must be given or the + * text data must be '\0' terminated + * -b: assume binary format ('\0' separated, "\0\0" terminated) + * -c: assume checksum protected environment format + * addr: memory address to read from + * size: length of input data; if missing, proper '\0' + * termination is mandatory + */ +static int do_env_import(cmd_tbl_t *cmdtp, int flag, + int argc, char * const argv[]) +{ + ulong addr; + char *cmd, *ptr; + char sep = '\n'; + int chk = 0; + int fmt = 0; + int del = 0; + size_t size; + + cmd = *argv; + + while (--argc > 0 && **++argv == '-') { + char *arg = *argv; + while (*++arg) { + switch (*arg) { + case 'b': /* raw binary format */ + if (fmt++) + goto sep_err; + sep = '\0'; + break; + case 'c': /* external checksum format */ + if (fmt++) + goto sep_err; + sep = '\0'; + chk = 1; + break; + case 't': /* text format */ + if (fmt++) + goto sep_err; + sep = '\n'; + break; + case 'd': + del = 1; + break; + default: + return CMD_RET_USAGE; + } + } + } + + if (argc < 1) + return CMD_RET_USAGE; + + if (!fmt) + printf("## Warning: defaulting to text format\n"); + + addr = simple_strtoul(argv[0], NULL, 16); + ptr = map_sysmem(addr, 0); + + if (argc == 2) { + size = simple_strtoul(argv[1], NULL, 16); + } else if (argc == 1 && chk) { + puts("## Error: external checksum format must pass size\n"); + return CMD_RET_FAILURE; + } else { + char *s = ptr; + + size = 0; + + while (size < MAX_ENV_SIZE) { + if ((*s == sep) && (*(s+1) == '\0')) + break; + ++s; + ++size; + } + if (size == MAX_ENV_SIZE) { + printf("## Warning: Input data exceeds %d bytes" + " - truncated\n", MAX_ENV_SIZE); + } + size += 2; + printf("## Info: input data size = %zu = 0x%zX\n", size, size); + } + + if (chk) { + uint32_t crc; + env_t *ep = (env_t *)ptr; + + size -= offsetof(env_t, data); + memcpy(&crc, &ep->crc, sizeof(crc)); + + if (crc32(0, ep->data, size) != crc) { + puts("## Error: bad CRC, import failed\n"); + return 1; + } + ptr = (char *)ep->data; + } + + if (himport_r(&env_htab, ptr, size, sep, del ? 0 : H_NOCLEAR, 0, + NULL) == 0) { + error("Environment import failed: errno = %d\n", errno); + return 1; + } + gd->flags |= GD_FLG_ENV_READY; + + return 0; + +sep_err: + printf("## %s: only one of \"-b\", \"-c\" or \"-t\" allowed\n", + cmd); + return 1; +} +#endif + +#if defined(CONFIG_CMD_ENV_EXISTS) +static int do_env_exists(cmd_tbl_t *cmdtp, int flag, int argc, + char * const argv[]) +{ + ENTRY e, *ep; + + if (argc < 2) + return CMD_RET_USAGE; + + e.key = argv[1]; + e.data = NULL; + hsearch_r(e, FIND, &ep, &env_htab, 0); + + return (ep == NULL) ? 1 : 0; +} +#endif + +/* + * New command line interface: "env" command with subcommands + */ +static cmd_tbl_t cmd_env_sub[] = { +#if defined(CONFIG_CMD_ASKENV) + 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, CONFIG_SYS_MAXARGS, 0, do_env_delete, "", ""), +#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_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 +#if defined(CONFIG_CMD_GREPENV) + U_BOOT_CMD_MKENT(grep, CONFIG_SYS_MAXARGS, 1, do_env_grep, "", ""), +#endif +#if defined(CONFIG_CMD_IMPORTENV) + U_BOOT_CMD_MKENT(import, 5, 0, do_env_import, "", ""), +#endif + U_BOOT_CMD_MKENT(print, CONFIG_SYS_MAXARGS, 1, do_env_print, "", ""), +#if defined(CONFIG_CMD_RUN) + U_BOOT_CMD_MKENT(run, CONFIG_SYS_MAXARGS, 1, do_run, "", ""), +#endif +#if defined(CONFIG_CMD_SAVEENV) && !defined(CONFIG_ENV_IS_NOWHERE) + U_BOOT_CMD_MKENT(save, 1, 0, do_env_save, "", ""), +#endif + U_BOOT_CMD_MKENT(set, CONFIG_SYS_MAXARGS, 0, do_env_set, "", ""), +#if defined(CONFIG_CMD_ENV_EXISTS) + U_BOOT_CMD_MKENT(exists, 2, 0, do_env_exists, "", ""), +#endif +}; + +#if defined(CONFIG_NEEDS_MANUAL_RELOC) +void env_reloc(void) +{ + fixup_cmdtable(cmd_env_sub, ARRAY_SIZE(cmd_env_sub)); +} +#endif + +static int do_env(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + cmd_tbl_t *cp; + + if (argc < 2) + return CMD_RET_USAGE; + + /* drop initial "env" arg */ + argc--; + argv++; + + cp = find_cmd_tbl(argv[0], cmd_env_sub, ARRAY_SIZE(cmd_env_sub)); + + if (cp) + return cp->cmd(cmdtp, flag, argc, argv); + + return CMD_RET_USAGE; +} + +#ifdef CONFIG_SYS_LONGHELP +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" + "env delete [-f] var [...] - [forcibly] delete variable(s)\n" +#if defined(CONFIG_CMD_EDITENV) + "env edit name - edit environment variable\n" +#endif +#if defined(CONFIG_CMD_ENV_EXISTS) + "env exists name - tests for existence of variable\n" +#endif +#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) +#ifdef CONFIG_REGEX + "env grep [-e] [-n | -v | -b] string [...] - search environment\n" +#else + "env grep [-n | -v | -b] string [...] - search environment\n" +#endif +#endif +#if defined(CONFIG_CMD_IMPORTENV) + "env import [-d] [-t | -b | -c] addr [size] - import environment\n" +#endif + "env print [-a | name ...] - print environment\n" +#if defined(CONFIG_CMD_RUN) + "env run var [...] - run commands in an environment variable\n" +#endif +#if defined(CONFIG_CMD_SAVEENV) && !defined(CONFIG_ENV_IS_NOWHERE) + "env save - save environment\n" +#endif + "env set [-f] name [arg ...]\n"; +#endif + +U_BOOT_CMD( + env, CONFIG_SYS_MAXARGS, 1, do_env, + "environment handling commands", env_help_text +); + +/* + * Old command line interface, kept for compatibility + */ + +#if defined(CONFIG_CMD_EDITENV) +U_BOOT_CMD_COMPLETE( + editenv, 2, 0, do_env_edit, + "edit environment variable", + "name\n" + " - edit environment variable 'name'", + var_complete +); +#endif + +U_BOOT_CMD_COMPLETE( + printenv, CONFIG_SYS_MAXARGS, 1, do_env_print, + "print environment variables", + "[-a]\n - print [all] values of all environment variables\n" + "printenv name ...\n" + " - print value of environment variable 'name'", + var_complete +); + +#ifdef CONFIG_CMD_GREPENV +U_BOOT_CMD_COMPLETE( + grepenv, CONFIG_SYS_MAXARGS, 0, do_env_grep, + "search environment variables", +#ifdef CONFIG_REGEX + "[-e] [-n | -v | -b] string ...\n" +#else + "[-n | -v | -b] string ...\n" +#endif + " - list environment name=value pairs matching 'string'\n" +#ifdef CONFIG_REGEX + " \"-e\": enable regular expressions;\n" +#endif + " \"-n\": search variable names; \"-v\": search values;\n" + " \"-b\": search both names and values (default)", + var_complete +); +#endif + +U_BOOT_CMD_COMPLETE( + setenv, CONFIG_SYS_MAXARGS, 0, do_env_set, + "set environment variables", + "[-f] name value ...\n" + " - [forcibly] set environment variable 'name' to 'value ...'\n" + "setenv [-f] name\n" + " - [forcibly] delete environment variable 'name'", + var_complete +); + +#if defined(CONFIG_CMD_ASKENV) + +U_BOOT_CMD( + askenv, CONFIG_SYS_MAXARGS, 1, do_env_ask, + "get environment variables from stdin", + "name [message] [size]\n" + " - get environment variable 'name' from stdin (max 'size' chars)" +); +#endif + +#if defined(CONFIG_CMD_RUN) +U_BOOT_CMD_COMPLETE( + run, CONFIG_SYS_MAXARGS, 1, do_run, + "run commands in an environment variable", + "var [...]\n" + " - run the commands in the environment variable(s) 'var'", + var_complete +); +#endif +#endif /* CONFIG_SPL_BUILD */ diff --git a/qemu/roms/u-boot/common/cmd_onenand.c b/qemu/roms/u-boot/common/cmd_onenand.c new file mode 100644 index 000000000..06cc14056 --- /dev/null +++ b/qemu/roms/u-boot/common/cmd_onenand.c @@ -0,0 +1,602 @@ +/* + * U-Boot command for OneNAND support + * + * Copyright (C) 2005-2008 Samsung Electronics + * Kyungmin Park <kyungmin.park@samsung.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <common.h> +#include <command.h> +#include <malloc.h> + +#include <linux/compat.h> +#include <linux/mtd/mtd.h> +#include <linux/mtd/onenand.h> + +#include <asm/io.h> + +static struct mtd_info *mtd; + +static loff_t next_ofs; +static loff_t skip_ofs; + +static inline int str2long(char *p, ulong *num) +{ + char *endptr; + + *num = simple_strtoul(p, &endptr, 16); + return (*p != '\0' && *endptr == '\0') ? 1 : 0; +} + +static int arg_off_size(int argc, char * const argv[], ulong *off, size_t *size) +{ + if (argc >= 1) { + if (!(str2long(argv[0], off))) { + printf("'%s' is not a number\n", argv[0]); + return -1; + } + } else { + *off = 0; + } + + if (argc >= 2) { + if (!(str2long(argv[1], (ulong *)size))) { + printf("'%s' is not a number\n", argv[1]); + return -1; + } + } else { + *size = mtd->size - *off; + } + + if ((*off + *size) > mtd->size) { + printf("total chip size (0x%llx) exceeded!\n", mtd->size); + return -1; + } + + if (*size == mtd->size) + puts("whole chip\n"); + else + printf("offset 0x%lx, size 0x%x\n", *off, *size); + + return 0; +} + +static int onenand_block_read(loff_t from, size_t len, + size_t *retlen, u_char *buf, int oob) +{ + struct onenand_chip *this = mtd->priv; + int blocks = (int) len >> this->erase_shift; + int blocksize = (1 << this->erase_shift); + loff_t ofs = from; + struct mtd_oob_ops ops = { + .retlen = 0, + }; + int ret; + + if (oob) + ops.ooblen = blocksize; + else + ops.len = blocksize; + + while (blocks) { + ret = mtd_block_isbad(mtd, ofs); + if (ret) { + printk("Bad blocks %d at 0x%x\n", + (u32)(ofs >> this->erase_shift), (u32)ofs); + ofs += blocksize; + continue; + } + + if (oob) + ops.oobbuf = buf; + else + ops.datbuf = buf; + + ops.retlen = 0; + ret = mtd_read_oob(mtd, ofs, &ops); + if (ret) { + printk("Read failed 0x%x, %d\n", (u32)ofs, ret); + ofs += blocksize; + continue; + } + ofs += blocksize; + buf += blocksize; + blocks--; + *retlen += ops.retlen; + } + + return 0; +} + +static int onenand_write_oneblock_withoob(loff_t to, const u_char * buf, + size_t *retlen) +{ + struct mtd_oob_ops ops = { + .len = mtd->writesize, + .ooblen = mtd->oobsize, + .mode = MTD_OPS_AUTO_OOB, + }; + int page, ret = 0; + for (page = 0; page < (mtd->erasesize / mtd->writesize); page ++) { + ops.datbuf = (u_char *)buf; + buf += mtd->writesize; + ops.oobbuf = (u_char *)buf; + buf += mtd->oobsize; + ret = mtd_write_oob(mtd, to, &ops); + if (ret) + break; + to += mtd->writesize; + } + + *retlen = (ret) ? 0 : mtd->erasesize; + return ret; +} + +static int onenand_block_write(loff_t to, size_t len, + size_t *retlen, const u_char * buf, int withoob) +{ + struct onenand_chip *this = mtd->priv; + int blocks = len >> this->erase_shift; + int blocksize = (1 << this->erase_shift); + loff_t ofs; + size_t _retlen = 0; + int ret; + + if (to == next_ofs) { + next_ofs = to + len; + to += skip_ofs; + } else { + next_ofs = to + len; + skip_ofs = 0; + } + ofs = to; + + while (blocks) { + ret = mtd_block_isbad(mtd, ofs); + if (ret) { + printk("Bad blocks %d at 0x%x\n", + (u32)(ofs >> this->erase_shift), (u32)ofs); + skip_ofs += blocksize; + goto next; + } + + if (!withoob) + ret = mtd_write(mtd, ofs, blocksize, &_retlen, buf); + else + ret = onenand_write_oneblock_withoob(ofs, buf, &_retlen); + if (ret) { + printk("Write failed 0x%x, %d", (u32)ofs, ret); + skip_ofs += blocksize; + goto next; + } + + buf += blocksize; + blocks--; + *retlen += _retlen; +next: + ofs += blocksize; + } + + return 0; +} + +static int onenand_block_erase(u32 start, u32 size, int force) +{ + struct onenand_chip *this = mtd->priv; + struct erase_info instr = { + .callback = NULL, + }; + loff_t ofs; + int ret; + int blocksize = 1 << this->erase_shift; + + for (ofs = start; ofs < (start + size); ofs += blocksize) { + ret = mtd_block_isbad(mtd, ofs); + if (ret && !force) { + printf("Skip erase bad block %d at 0x%x\n", + (u32)(ofs >> this->erase_shift), (u32)ofs); + continue; + } + + instr.addr = ofs; + instr.len = blocksize; + instr.priv = force; + instr.mtd = mtd; + ret = mtd_erase(mtd, &instr); + if (ret) { + printf("erase failed block %d at 0x%x\n", + (u32)(ofs >> this->erase_shift), (u32)ofs); + continue; + } + } + + return 0; +} + +static int onenand_block_test(u32 start, u32 size) +{ + struct onenand_chip *this = mtd->priv; + struct erase_info instr = { + .callback = NULL, + .priv = 0, + }; + + int blocks; + loff_t ofs; + int blocksize = 1 << this->erase_shift; + int start_block, end_block; + size_t retlen; + u_char *buf; + u_char *verify_buf; + int ret; + + buf = malloc(blocksize); + if (!buf) { + printf("Not enough malloc space available!\n"); + return -1; + } + + verify_buf = malloc(blocksize); + if (!verify_buf) { + printf("Not enough malloc space available!\n"); + return -1; + } + + start_block = start >> this->erase_shift; + end_block = (start + size) >> this->erase_shift; + + /* Protect boot-loader from badblock testing */ + if (start_block < 2) + start_block = 2; + + if (end_block > (mtd->size >> this->erase_shift)) + end_block = mtd->size >> this->erase_shift; + + blocks = start_block; + ofs = start; + while (blocks < end_block) { + printf("\rTesting block %d at 0x%x", (u32)(ofs >> this->erase_shift), (u32)ofs); + + ret = mtd_block_isbad(mtd, ofs); + if (ret) { + printf("Skip erase bad block %d at 0x%x\n", + (u32)(ofs >> this->erase_shift), (u32)ofs); + goto next; + } + + instr.addr = ofs; + instr.len = blocksize; + ret = mtd_erase(mtd, &instr); + if (ret) { + printk("Erase failed 0x%x, %d\n", (u32)ofs, ret); + goto next; + } + + ret = mtd_write(mtd, ofs, blocksize, &retlen, buf); + if (ret) { + printk("Write failed 0x%x, %d\n", (u32)ofs, ret); + goto next; + } + + ret = mtd_read(mtd, ofs, blocksize, &retlen, verify_buf); + if (ret) { + printk("Read failed 0x%x, %d\n", (u32)ofs, ret); + goto next; + } + + if (memcmp(buf, verify_buf, blocksize)) + printk("\nRead/Write test failed at 0x%x\n", (u32)ofs); + +next: + ofs += blocksize; + blocks++; + } + printf("...Done\n"); + + free(buf); + free(verify_buf); + + return 0; +} + +static int onenand_dump(struct mtd_info *mtd, ulong off, int only_oob) +{ + int i; + u_char *datbuf, *oobbuf, *p; + struct mtd_oob_ops ops; + loff_t addr; + + datbuf = malloc(mtd->writesize + mtd->oobsize); + oobbuf = malloc(mtd->oobsize); + if (!datbuf || !oobbuf) { + puts("No memory for page buffer\n"); + return 1; + } + off &= ~(mtd->writesize - 1); + addr = (loff_t) off; + memset(&ops, 0, sizeof(ops)); + ops.datbuf = datbuf; + ops.oobbuf = oobbuf; + ops.len = mtd->writesize; + ops.ooblen = mtd->oobsize; + ops.retlen = 0; + i = mtd_read_oob(mtd, addr, &ops); + if (i < 0) { + printf("Error (%d) reading page %08lx\n", i, off); + free(datbuf); + free(oobbuf); + return 1; + } + printf("Page %08lx dump:\n", off); + i = mtd->writesize >> 4; + p = datbuf; + + while (i--) { + if (!only_oob) + printf("\t%02x %02x %02x %02x %02x %02x %02x %02x" + " %02x %02x %02x %02x %02x %02x %02x %02x\n", + p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7], + p[8], p[9], p[10], p[11], p[12], p[13], p[14], + p[15]); + p += 16; + } + puts("OOB:\n"); + i = mtd->oobsize >> 3; + p = oobbuf; + + while (i--) { + printf("\t%02x %02x %02x %02x %02x %02x %02x %02x\n", + p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7]); + p += 8; + } + free(datbuf); + free(oobbuf); + + return 0; +} + +static int do_onenand_info(cmd_tbl_t * cmdtp, int flag, int argc, char * const argv[]) +{ + printf("%s\n", mtd->name); + return 0; +} + +static int do_onenand_bad(cmd_tbl_t * cmdtp, int flag, int argc, char * const argv[]) +{ + ulong ofs; + + mtd = &onenand_mtd; + /* Currently only one OneNAND device is supported */ + printf("\nDevice %d bad blocks:\n", 0); + for (ofs = 0; ofs < mtd->size; ofs += mtd->erasesize) { + if (mtd_block_isbad(mtd, ofs)) + printf(" %08x\n", (u32)ofs); + } + + return 0; +} + +static int do_onenand_read(cmd_tbl_t * cmdtp, int flag, int argc, char * const argv[]) +{ + char *s; + int oob = 0; + ulong addr, ofs; + size_t len; + int ret = 0; + size_t retlen = 0; + + if (argc < 3) + return CMD_RET_USAGE; + + s = strchr(argv[0], '.'); + if ((s != NULL) && (!strcmp(s, ".oob"))) + oob = 1; + + addr = (ulong)simple_strtoul(argv[1], NULL, 16); + + printf("\nOneNAND read: "); + if (arg_off_size(argc - 2, argv + 2, &ofs, &len) != 0) + return 1; + + ret = onenand_block_read(ofs, len, &retlen, (u8 *)addr, oob); + + printf(" %d bytes read: %s\n", retlen, ret ? "ERROR" : "OK"); + + return ret == 0 ? 0 : 1; +} + +static int do_onenand_write(cmd_tbl_t * cmdtp, int flag, int argc, char * const argv[]) +{ + ulong addr, ofs; + size_t len; + int ret = 0, withoob = 0; + size_t retlen = 0; + + if (argc < 3) + return CMD_RET_USAGE; + + if (strncmp(argv[0] + 6, "yaffs", 5) == 0) + withoob = 1; + + addr = (ulong)simple_strtoul(argv[1], NULL, 16); + + printf("\nOneNAND write: "); + if (arg_off_size(argc - 2, argv + 2, &ofs, &len) != 0) + return 1; + + ret = onenand_block_write(ofs, len, &retlen, (u8 *)addr, withoob); + + printf(" %d bytes written: %s\n", retlen, ret ? "ERROR" : "OK"); + + return ret == 0 ? 0 : 1; +} + +static int do_onenand_erase(cmd_tbl_t * cmdtp, int flag, int argc, char * const argv[]) +{ + ulong ofs; + int ret = 0; + size_t len; + int force; + + /* + * Syntax is: + * 0 1 2 3 4 + * onenand erase [force] [off size] + */ + argc--; + argv++; + if (argc) + { + if (!strcmp("force", argv[0])) + { + force = 1; + argc--; + argv++; + } + } + printf("\nOneNAND erase: "); + + /* skip first two or three arguments, look for offset and size */ + if (arg_off_size(argc, argv, &ofs, &len) != 0) + return 1; + + ret = onenand_block_erase(ofs, len, force); + + printf("%s\n", ret ? "ERROR" : "OK"); + + return ret == 0 ? 0 : 1; +} + +static int do_onenand_test(cmd_tbl_t * cmdtp, int flag, int argc, char * const argv[]) +{ + ulong ofs; + int ret = 0; + size_t len; + + /* + * Syntax is: + * 0 1 2 3 4 + * onenand test [force] [off size] + */ + + printf("\nOneNAND test: "); + + /* skip first two or three arguments, look for offset and size */ + if (arg_off_size(argc - 1, argv + 1, &ofs, &len) != 0) + return 1; + + ret = onenand_block_test(ofs, len); + + printf("%s\n", ret ? "ERROR" : "OK"); + + return ret == 0 ? 0 : 1; +} + +static int do_onenand_dump(cmd_tbl_t * cmdtp, int flag, int argc, char * const argv[]) +{ + ulong ofs; + int ret = 0; + char *s; + + if (argc < 2) + return CMD_RET_USAGE; + + s = strchr(argv[0], '.'); + ofs = (int)simple_strtoul(argv[1], NULL, 16); + + if (s != NULL && strcmp(s, ".oob") == 0) + ret = onenand_dump(mtd, ofs, 1); + else + ret = onenand_dump(mtd, ofs, 0); + + return ret == 0 ? 1 : 0; +} + +static int do_onenand_markbad(cmd_tbl_t * cmdtp, int flag, int argc, char * const argv[]) +{ + int ret = 0; + ulong addr; + + argc -= 2; + argv += 2; + + if (argc <= 0) + return CMD_RET_USAGE; + + while (argc > 0) { + addr = simple_strtoul(*argv, NULL, 16); + + if (mtd_block_markbad(mtd, addr)) { + printf("block 0x%08lx NOT marked " + "as bad! ERROR %d\n", + addr, ret); + ret = 1; + } else { + printf("block 0x%08lx successfully " + "marked as bad\n", + addr); + } + --argc; + ++argv; + } + return ret; +} + +static cmd_tbl_t cmd_onenand_sub[] = { + U_BOOT_CMD_MKENT(info, 1, 0, do_onenand_info, "", ""), + U_BOOT_CMD_MKENT(bad, 1, 0, do_onenand_bad, "", ""), + U_BOOT_CMD_MKENT(read, 4, 0, do_onenand_read, "", ""), + U_BOOT_CMD_MKENT(write, 4, 0, do_onenand_write, "", ""), + U_BOOT_CMD_MKENT(write.yaffs, 4, 0, do_onenand_write, "", ""), + U_BOOT_CMD_MKENT(erase, 3, 0, do_onenand_erase, "", ""), + U_BOOT_CMD_MKENT(test, 3, 0, do_onenand_test, "", ""), + U_BOOT_CMD_MKENT(dump, 2, 0, do_onenand_dump, "", ""), + U_BOOT_CMD_MKENT(markbad, CONFIG_SYS_MAXARGS, 0, do_onenand_markbad, "", ""), +}; + +#ifdef CONFIG_NEEDS_MANUAL_RELOC +void onenand_reloc(void) { + fixup_cmdtable(cmd_onenand_sub, ARRAY_SIZE(cmd_onenand_sub)); +} +#endif + +static int do_onenand(cmd_tbl_t * cmdtp, int flag, int argc, char * const argv[]) +{ + cmd_tbl_t *c; + + if (argc < 2) + return CMD_RET_USAGE; + + mtd = &onenand_mtd; + + /* Strip off leading 'onenand' command argument */ + argc--; + argv++; + + c = find_cmd_tbl(argv[0], &cmd_onenand_sub[0], ARRAY_SIZE(cmd_onenand_sub)); + + if (c) + return c->cmd(cmdtp, flag, argc, argv); + else + return CMD_RET_USAGE; +} + +U_BOOT_CMD( + onenand, CONFIG_SYS_MAXARGS, 1, do_onenand, + "OneNAND sub-system", + "info - show available OneNAND devices\n" + "onenand bad - show bad blocks\n" + "onenand read[.oob] addr off size\n" + "onenand write[.yaffs] addr off size\n" + " read/write 'size' bytes starting at offset 'off'\n" + " to/from memory address 'addr', skipping bad blocks.\n" + "onenand erase [force] [off size] - erase 'size' bytes from\n" + "onenand test [off size] - test 'size' bytes from\n" + " offset 'off' (entire device if not specified)\n" + "onenand dump[.oob] off - dump page\n" + "onenand markbad off [...] - mark bad block(s) at offset (UNSAFE)" +); diff --git a/qemu/roms/u-boot/common/cmd_otp.c b/qemu/roms/u-boot/common/cmd_otp.c new file mode 100644 index 000000000..67808aa37 --- /dev/null +++ b/qemu/roms/u-boot/common/cmd_otp.c @@ -0,0 +1,239 @@ +/* + * cmd_otp.c - interface to Blackfin on-chip One-Time-Programmable memory + * + * Copyright (c) 2007-2008 Analog Devices Inc. + * + * Licensed under the GPL-2 or later. + */ + +/* There are 512 128-bit "pages" (0x000 through 0x1FF). + * The pages are accessable as 64-bit "halfpages" (an upper and lower half). + * The pages are not part of the memory map. There is an OTP controller which + * handles scanning in/out of bits. While access is done through OTP MMRs, + * the bootrom provides C-callable helper functions to handle the interaction. + */ + +#include <config.h> +#include <common.h> +#include <command.h> + +#include <asm/blackfin.h> +#include <asm/clock.h> +#include <asm/mach-common/bits/otp.h> + +static const char *otp_strerror(uint32_t err) +{ + switch (err) { + case 0: return "no error"; + case OTP_WRITE_ERROR: return "OTP fuse write error"; + case OTP_READ_ERROR: return "OTP fuse read error"; + case OTP_ACC_VIO_ERROR: return "invalid OTP address"; + case OTP_DATA_MULT_ERROR: return "multiple bad bits detected"; + case OTP_ECC_MULT_ERROR: return "error in ECC bits"; + case OTP_PREV_WR_ERROR: return "space already written"; + case OTP_DATA_SB_WARN: return "single bad bit in half page"; + case OTP_ECC_SB_WARN: return "single bad bit in ECC"; + default: return "unknown error"; + } +} + +#define lowup(x) ((x) % 2 ? "upper" : "lower") + +static int check_voltage(void) +{ + /* Make sure voltage limits are within datasheet spec */ + uint16_t vr_ctl = bfin_read_VR_CTL(); + +#ifdef __ADSPBF54x__ + /* 0.9V <= VDDINT <= 1.1V */ + if ((vr_ctl & 0xc) && (vr_ctl & 0xc0) == 0xc0) + return 1; +#else + /* for the parts w/out qualification yet */ + (void)vr_ctl; +#endif + + return 0; +} + +static void set_otp_timing(bool write) +{ + static uint32_t timing; + if (!timing) { + uint32_t tp1, tp2, tp3; + /* OTP_TP1 = 1000 / sclk_period (in nanoseconds) + * OTP_TP1 = 1000 / (1 / get_sclk() * 10^9) + * OTP_TP1 = (1000 * get_sclk()) / 10^9 + * OTP_TP1 = get_sclk() / 10^6 + */ + tp1 = get_sclk() / 1000000; + /* OTP_TP2 = 400 / (2 * sclk_period) + * OTP_TP2 = 400 / (2 * 1 / get_sclk() * 10^9) + * OTP_TP2 = (400 * get_sclk()) / (2 * 10^9) + * OTP_TP2 = (2 * get_sclk()) / 10^7 + */ + tp2 = (2 * get_sclk() / 10000000) << 8; + /* OTP_TP3 = magic constant */ + tp3 = (0x1401) << 15; + timing = tp1 | tp2 | tp3; + } + + bfrom_OtpCommand(OTP_INIT, write ? timing : timing & ~(-1 << 15)); +} + +int do_otp(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + char *cmd; + uint32_t ret, base_flags; + bool prompt_user, force_read; + uint32_t (*otp_func)(uint32_t page, uint32_t flags, uint64_t *page_content); + + if (argc < 4) { + usage: + return CMD_RET_USAGE; + } + + prompt_user = false; + base_flags = 0; + cmd = argv[1]; + if (!strcmp(cmd, "read")) + otp_func = bfrom_OtpRead; + else if (!strcmp(cmd, "dump")) { + otp_func = bfrom_OtpRead; + force_read = true; + } else if (!strcmp(cmd, "write")) { + otp_func = bfrom_OtpWrite; + base_flags = OTP_CHECK_FOR_PREV_WRITE; + if (!strcmp(argv[2], "--force")) { + argv++; + --argc; + } else + prompt_user = false; + } else if (!strcmp(cmd, "lock")) { + if (argc != 4) + goto usage; + otp_func = bfrom_OtpWrite; + base_flags = OTP_LOCK; + } else + goto usage; + + uint64_t *addr = (uint64_t *)simple_strtoul(argv[2], NULL, 16); + uint32_t page = simple_strtoul(argv[3], NULL, 16); + uint32_t flags; + size_t i, count; + ulong half; + + if (argc > 4) + count = simple_strtoul(argv[4], NULL, 16); + else + count = 2; + + if (argc > 5) { + half = simple_strtoul(argv[5], NULL, 16); + if (half != 0 && half != 1) { + puts("Error: 'half' can only be '0' or '1'\n"); + goto usage; + } + } else + half = 0; + + /* "otp lock" has slightly different semantics */ + if (base_flags & OTP_LOCK) { + count = page; + page = (uint32_t)addr; + addr = NULL; + } + + /* do to the nature of OTP, make sure users are sure */ + if (prompt_user) { + printf( + "Writing one time programmable memory\n" + "Make sure your operating voltages and temperature are within spec\n" + " source address: 0x%p\n" + " OTP destination: %s page 0x%03X - %s page 0x%03lX\n" + " number to write: %lu halfpages\n" + " type \"YES\" (no quotes) to confirm: ", + addr, + lowup(half), page, + lowup(half + count - 1), page + (half + count - 1) / 2, + half + count + ); + + i = 0; + while (1) { + if (tstc()) { + const char exp_ans[] = "YES\r"; + char c; + putc(c = getc()); + if (exp_ans[i++] != c) { + printf(" Aborting\n"); + return 1; + } else if (!exp_ans[i]) { + puts("\n"); + break; + } + } + } + } + + printf("OTP memory %s: addr 0x%p page 0x%03X count %zu ... ", + cmd, addr, page, count); + + set_otp_timing(otp_func == bfrom_OtpWrite); + if (otp_func == bfrom_OtpWrite && check_voltage()) { + puts("ERROR: VDDINT voltage is out of spec for writing\n"); + return -1; + } + + /* Do the actual reading/writing stuff */ + ret = 0; + for (i = half; i < count + half; ++i) { + flags = base_flags | (i % 2 ? OTP_UPPER_HALF : OTP_LOWER_HALF); + try_again: + ret = otp_func(page, flags, addr); + if (ret & OTP_MASTER_ERROR) { + if (force_read) { + if (flags & OTP_NO_ECC) + break; + else + flags |= OTP_NO_ECC; + puts("E"); + goto try_again; + } else + break; + } else if (ret) + puts("W"); + else + puts("."); + if (!(base_flags & OTP_LOCK)) { + ++addr; + if (i % 2) + ++page; + } else + ++page; + } + if (ret & 0x1) + printf("\nERROR at page 0x%03X (%s-halfpage): 0x%03X: %s\n", + page, lowup(i), ret, otp_strerror(ret)); + else + puts(" done\n"); + + /* Make sure we disable writing */ + set_otp_timing(false); + bfrom_OtpCommand(OTP_CLOSE, 0); + + return ret; +} + +U_BOOT_CMD( + otp, 7, 0, do_otp, + "One-Time-Programmable sub-system", + "read <addr> <page> [count] [half]\n" + " - read 'count' half-pages starting at 'page' (offset 'half') to 'addr'\n" + "otp dump <addr> <page> [count] [half]\n" + " - like 'otp read', but skip read errors\n" + "otp write [--force] <addr> <page> [count] [half]\n" + " - write 'count' half-pages starting at 'page' (offset 'half') from 'addr'\n" + "otp lock <page> <count>\n" + " - lock 'count' pages starting at 'page'" +); diff --git a/qemu/roms/u-boot/common/cmd_part.c b/qemu/roms/u-boot/common/cmd_part.c new file mode 100644 index 000000000..14248548d --- /dev/null +++ b/qemu/roms/u-boot/common/cmd_part.c @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved. + * + * made from cmd_ext2, which was: + * + * (C) Copyright 2004 + * esd gmbh <www.esd-electronics.com> + * Reinhard Arlt <reinhard.arlt@esd-electronics.com> + * + * made from cmd_reiserfs by + * + * (C) Copyright 2003 - 2004 + * Sysgo Real-Time Solutions, AG <www.elinos.com> + * Pavel Bartusek <pba@sysgo.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <config.h> +#include <command.h> +#include <part.h> +#include <vsprintf.h> + +#ifndef CONFIG_PARTITION_UUIDS +#error CONFIG_PARTITION_UUIDS must be enabled for CONFIG_CMD_PART to be enabled +#endif + +int do_part_uuid(int argc, char * const argv[]) +{ + int part; + block_dev_desc_t *dev_desc; + disk_partition_t info; + + if (argc < 2) + return CMD_RET_USAGE; + if (argc > 3) + return CMD_RET_USAGE; + + part = get_device_and_partition(argv[0], argv[1], &dev_desc, &info, 0); + if (part < 0) + return 1; + + if (argc > 2) + setenv(argv[2], info.uuid); + else + printf("%s\n", info.uuid); + + return 0; +} + +int do_part_list(int argc, char * const argv[]) +{ + int ret; + block_dev_desc_t *desc; + + if (argc != 2) + return CMD_RET_USAGE; + + ret = get_device(argv[0], argv[1], &desc); + if (ret < 0) + return 1; + + print_part(desc); + + return 0; +} + +int do_part(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + if (argc < 2) + return CMD_RET_USAGE; + + if (!strcmp(argv[1], "uuid")) + return do_part_uuid(argc - 2, argv + 2); + else if (!strcmp(argv[1], "list")) + return do_part_list(argc - 2, argv + 2); + + return CMD_RET_USAGE; +} + +U_BOOT_CMD( + part, 5, 1, do_part, + "disk partition related commands", + "uuid <interface> <dev>:<part>\n" + " - print partition UUID\n" + "part uuid <interface> <dev>:<part> <varname>\n" + " - set environment variable to partition UUID\n" + "part list <interface> <dev>\n" + " - print a device's partition table" +); diff --git a/qemu/roms/u-boot/common/cmd_pci.c b/qemu/roms/u-boot/common/cmd_pci.c new file mode 100644 index 000000000..d3e7c089b --- /dev/null +++ b/qemu/roms/u-boot/common/cmd_pci.c @@ -0,0 +1,493 @@ +/* + * (C) Copyright 2001 Sysgo Real-Time Solutions, GmbH <www.elinos.com> + * Andreas Heppel <aheppel@sysgo.de> + * + * (C) Copyright 2002 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * Wolfgang Grandegger, DENX Software Engineering, wg@denx.de. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/* + * PCI routines + */ + +#include <common.h> +#include <command.h> +#include <asm/processor.h> +#include <asm/io.h> +#include <pci.h> + +/* + * Follows routines for the output of infos about devices on PCI bus. + */ + +void pci_header_show(pci_dev_t dev); +void pci_header_show_brief(pci_dev_t dev); + +/* + * Subroutine: pciinfo + * + * Description: Show information about devices on PCI bus. + * Depending on the define CONFIG_SYS_SHORT_PCI_LISTING + * the output will be more or less exhaustive. + * + * Inputs: bus_no the number of the bus to be scanned. + * + * Return: None + * + */ +void pciinfo(int BusNum, int ShortPCIListing) +{ + int Device; + int Function; + unsigned char HeaderType; + unsigned short VendorID; + pci_dev_t dev; + + printf("Scanning PCI devices on bus %d\n", BusNum); + + if (ShortPCIListing) { + printf("BusDevFun VendorId DeviceId Device Class Sub-Class\n"); + printf("_____________________________________________________________\n"); + } + + for (Device = 0; Device < PCI_MAX_PCI_DEVICES; Device++) { + HeaderType = 0; + VendorID = 0; + for (Function = 0; Function < PCI_MAX_PCI_FUNCTIONS; Function++) { + /* + * If this is not a multi-function device, we skip the rest. + */ + if (Function && !(HeaderType & 0x80)) + break; + + dev = PCI_BDF(BusNum, Device, Function); + + pci_read_config_word(dev, PCI_VENDOR_ID, &VendorID); + if ((VendorID == 0xFFFF) || (VendorID == 0x0000)) + continue; + + if (!Function) pci_read_config_byte(dev, PCI_HEADER_TYPE, &HeaderType); + + if (ShortPCIListing) + { + printf("%02x.%02x.%02x ", BusNum, Device, Function); + pci_header_show_brief(dev); + } + else + { + printf("\nFound PCI device %02x.%02x.%02x:\n", + BusNum, Device, Function); + pci_header_show(dev); + } + } + } +} + + +/* + * Subroutine: pci_header_show_brief + * + * Description: Reads and prints the header of the + * specified PCI device in short form. + * + * Inputs: dev Bus+Device+Function number + * + * Return: None + * + */ +void pci_header_show_brief(pci_dev_t dev) +{ + u16 vendor, device; + u8 class, subclass; + + pci_read_config_word(dev, PCI_VENDOR_ID, &vendor); + pci_read_config_word(dev, PCI_DEVICE_ID, &device); + pci_read_config_byte(dev, PCI_CLASS_CODE, &class); + pci_read_config_byte(dev, PCI_CLASS_SUB_CODE, &subclass); + + printf("0x%.4x 0x%.4x %-23s 0x%.2x\n", + vendor, device, + pci_class_str(class), subclass); +} + +/* + * Subroutine: PCI_Header_Show + * + * Description: Reads the header of the specified PCI device. + * + * Inputs: BusDevFunc Bus+Device+Function number + * + * Return: None + * + */ +void pci_header_show(pci_dev_t dev) +{ + u8 _byte, header_type; + u16 _word; + u32 _dword; + +#define PRINT(msg, type, reg) \ + pci_read_config_##type(dev, reg, &_##type); \ + printf(msg, _##type) + +#define PRINT2(msg, type, reg, func) \ + pci_read_config_##type(dev, reg, &_##type); \ + printf(msg, _##type, func(_##type)) + + pci_read_config_byte(dev, PCI_HEADER_TYPE, &header_type); + + PRINT (" vendor ID = 0x%.4x\n", word, PCI_VENDOR_ID); + PRINT (" device ID = 0x%.4x\n", word, PCI_DEVICE_ID); + PRINT (" command register = 0x%.4x\n", word, PCI_COMMAND); + PRINT (" status register = 0x%.4x\n", word, PCI_STATUS); + PRINT (" revision ID = 0x%.2x\n", byte, PCI_REVISION_ID); + PRINT2(" class code = 0x%.2x (%s)\n", byte, PCI_CLASS_CODE, + pci_class_str); + PRINT (" sub class code = 0x%.2x\n", byte, PCI_CLASS_SUB_CODE); + PRINT (" programming interface = 0x%.2x\n", byte, PCI_CLASS_PROG); + PRINT (" cache line = 0x%.2x\n", byte, PCI_CACHE_LINE_SIZE); + PRINT (" latency time = 0x%.2x\n", byte, PCI_LATENCY_TIMER); + PRINT (" header type = 0x%.2x\n", byte, PCI_HEADER_TYPE); + PRINT (" BIST = 0x%.2x\n", byte, PCI_BIST); + PRINT (" base address 0 = 0x%.8x\n", dword, PCI_BASE_ADDRESS_0); + + switch (header_type & 0x03) { + case PCI_HEADER_TYPE_NORMAL: /* "normal" PCI device */ + PRINT (" base address 1 = 0x%.8x\n", dword, PCI_BASE_ADDRESS_1); + PRINT (" base address 2 = 0x%.8x\n", dword, PCI_BASE_ADDRESS_2); + PRINT (" base address 3 = 0x%.8x\n", dword, PCI_BASE_ADDRESS_3); + PRINT (" base address 4 = 0x%.8x\n", dword, PCI_BASE_ADDRESS_4); + PRINT (" base address 5 = 0x%.8x\n", dword, PCI_BASE_ADDRESS_5); + PRINT (" cardBus CIS pointer = 0x%.8x\n", dword, PCI_CARDBUS_CIS); + PRINT (" sub system vendor ID = 0x%.4x\n", word, PCI_SUBSYSTEM_VENDOR_ID); + PRINT (" sub system ID = 0x%.4x\n", word, PCI_SUBSYSTEM_ID); + PRINT (" expansion ROM base address = 0x%.8x\n", dword, PCI_ROM_ADDRESS); + PRINT (" interrupt line = 0x%.2x\n", byte, PCI_INTERRUPT_LINE); + PRINT (" interrupt pin = 0x%.2x\n", byte, PCI_INTERRUPT_PIN); + PRINT (" min Grant = 0x%.2x\n", byte, PCI_MIN_GNT); + PRINT (" max Latency = 0x%.2x\n", byte, PCI_MAX_LAT); + break; + + case PCI_HEADER_TYPE_BRIDGE: /* PCI-to-PCI bridge */ + + PRINT (" base address 1 = 0x%.8x\n", dword, PCI_BASE_ADDRESS_1); + PRINT (" primary bus number = 0x%.2x\n", byte, PCI_PRIMARY_BUS); + PRINT (" secondary bus number = 0x%.2x\n", byte, PCI_SECONDARY_BUS); + PRINT (" subordinate bus number = 0x%.2x\n", byte, PCI_SUBORDINATE_BUS); + PRINT (" secondary latency timer = 0x%.2x\n", byte, PCI_SEC_LATENCY_TIMER); + PRINT (" IO base = 0x%.2x\n", byte, PCI_IO_BASE); + PRINT (" IO limit = 0x%.2x\n", byte, PCI_IO_LIMIT); + PRINT (" secondary status = 0x%.4x\n", word, PCI_SEC_STATUS); + PRINT (" memory base = 0x%.4x\n", word, PCI_MEMORY_BASE); + PRINT (" memory limit = 0x%.4x\n", word, PCI_MEMORY_LIMIT); + PRINT (" prefetch memory base = 0x%.4x\n", word, PCI_PREF_MEMORY_BASE); + PRINT (" prefetch memory limit = 0x%.4x\n", word, PCI_PREF_MEMORY_LIMIT); + PRINT (" prefetch memory base upper = 0x%.8x\n", dword, PCI_PREF_BASE_UPPER32); + PRINT (" prefetch memory limit upper = 0x%.8x\n", dword, PCI_PREF_LIMIT_UPPER32); + PRINT (" IO base upper 16 bits = 0x%.4x\n", word, PCI_IO_BASE_UPPER16); + PRINT (" IO limit upper 16 bits = 0x%.4x\n", word, PCI_IO_LIMIT_UPPER16); + PRINT (" expansion ROM base address = 0x%.8x\n", dword, PCI_ROM_ADDRESS1); + PRINT (" interrupt line = 0x%.2x\n", byte, PCI_INTERRUPT_LINE); + PRINT (" interrupt pin = 0x%.2x\n", byte, PCI_INTERRUPT_PIN); + PRINT (" bridge control = 0x%.4x\n", word, PCI_BRIDGE_CONTROL); + break; + + case PCI_HEADER_TYPE_CARDBUS: /* PCI-to-CardBus bridge */ + + PRINT (" capabilities = 0x%.2x\n", byte, PCI_CB_CAPABILITY_LIST); + PRINT (" secondary status = 0x%.4x\n", word, PCI_CB_SEC_STATUS); + PRINT (" primary bus number = 0x%.2x\n", byte, PCI_CB_PRIMARY_BUS); + PRINT (" CardBus number = 0x%.2x\n", byte, PCI_CB_CARD_BUS); + PRINT (" subordinate bus number = 0x%.2x\n", byte, PCI_CB_SUBORDINATE_BUS); + PRINT (" CardBus latency timer = 0x%.2x\n", byte, PCI_CB_LATENCY_TIMER); + PRINT (" CardBus memory base 0 = 0x%.8x\n", dword, PCI_CB_MEMORY_BASE_0); + PRINT (" CardBus memory limit 0 = 0x%.8x\n", dword, PCI_CB_MEMORY_LIMIT_0); + PRINT (" CardBus memory base 1 = 0x%.8x\n", dword, PCI_CB_MEMORY_BASE_1); + PRINT (" CardBus memory limit 1 = 0x%.8x\n", dword, PCI_CB_MEMORY_LIMIT_1); + PRINT (" CardBus IO base 0 = 0x%.4x\n", word, PCI_CB_IO_BASE_0); + PRINT (" CardBus IO base high 0 = 0x%.4x\n", word, PCI_CB_IO_BASE_0_HI); + PRINT (" CardBus IO limit 0 = 0x%.4x\n", word, PCI_CB_IO_LIMIT_0); + PRINT (" CardBus IO limit high 0 = 0x%.4x\n", word, PCI_CB_IO_LIMIT_0_HI); + PRINT (" CardBus IO base 1 = 0x%.4x\n", word, PCI_CB_IO_BASE_1); + PRINT (" CardBus IO base high 1 = 0x%.4x\n", word, PCI_CB_IO_BASE_1_HI); + PRINT (" CardBus IO limit 1 = 0x%.4x\n", word, PCI_CB_IO_LIMIT_1); + PRINT (" CardBus IO limit high 1 = 0x%.4x\n", word, PCI_CB_IO_LIMIT_1_HI); + PRINT (" interrupt line = 0x%.2x\n", byte, PCI_INTERRUPT_LINE); + PRINT (" interrupt pin = 0x%.2x\n", byte, PCI_INTERRUPT_PIN); + PRINT (" bridge control = 0x%.4x\n", word, PCI_CB_BRIDGE_CONTROL); + PRINT (" subvendor ID = 0x%.4x\n", word, PCI_CB_SUBSYSTEM_VENDOR_ID); + PRINT (" subdevice ID = 0x%.4x\n", word, PCI_CB_SUBSYSTEM_ID); + PRINT (" PC Card 16bit base address = 0x%.8x\n", dword, PCI_CB_LEGACY_MODE_BASE); + break; + + default: + printf("unknown header\n"); + break; + } + +#undef PRINT +#undef PRINT2 +} + +/* Convert the "bus.device.function" identifier into a number. + */ +static pci_dev_t get_pci_dev(char* name) +{ + char cnum[12]; + int len, i, iold, n; + int bdfs[3] = {0,0,0}; + + len = strlen(name); + if (len > 8) + return -1; + for (i = 0, iold = 0, n = 0; i < len; i++) { + if (name[i] == '.') { + memcpy(cnum, &name[iold], i - iold); + cnum[i - iold] = '\0'; + bdfs[n++] = simple_strtoul(cnum, NULL, 16); + iold = i + 1; + } + } + strcpy(cnum, &name[iold]); + if (n == 0) + n = 1; + bdfs[n] = simple_strtoul(cnum, NULL, 16); + return PCI_BDF(bdfs[0], bdfs[1], bdfs[2]); +} + +static int pci_cfg_display(pci_dev_t bdf, ulong addr, ulong size, ulong length) +{ +#define DISP_LINE_LEN 16 + ulong i, nbytes, linebytes; + int rc = 0; + + if (length == 0) + length = 0x40 / size; /* Standard PCI configuration space */ + + /* Print the lines. + * once, and all accesses are with the specified bus width. + */ + nbytes = length * size; + do { + uint val4; + ushort val2; + u_char val1; + + printf("%08lx:", addr); + linebytes = (nbytes>DISP_LINE_LEN)?DISP_LINE_LEN:nbytes; + for (i=0; i<linebytes; i+= size) { + if (size == 4) { + pci_read_config_dword(bdf, addr, &val4); + printf(" %08x", val4); + } else if (size == 2) { + pci_read_config_word(bdf, addr, &val2); + printf(" %04x", val2); + } else { + pci_read_config_byte(bdf, addr, &val1); + printf(" %02x", val1); + } + addr += size; + } + printf("\n"); + nbytes -= linebytes; + if (ctrlc()) { + rc = 1; + break; + } + } while (nbytes > 0); + + return (rc); +} + +static int pci_cfg_write (pci_dev_t bdf, ulong addr, ulong size, ulong value) +{ + if (size == 4) { + pci_write_config_dword(bdf, addr, value); + } + else if (size == 2) { + ushort val = value & 0xffff; + pci_write_config_word(bdf, addr, val); + } + else { + u_char val = value & 0xff; + pci_write_config_byte(bdf, addr, val); + } + return 0; +} + +static int +pci_cfg_modify (pci_dev_t bdf, ulong addr, ulong size, ulong value, int incrflag) +{ + ulong i; + int nbytes; + uint val4; + ushort val2; + u_char val1; + + /* Print the address, followed by value. Then accept input for + * the next value. A non-converted value exits. + */ + do { + printf("%08lx:", addr); + if (size == 4) { + pci_read_config_dword(bdf, addr, &val4); + printf(" %08x", val4); + } + else if (size == 2) { + pci_read_config_word(bdf, addr, &val2); + printf(" %04x", val2); + } + else { + pci_read_config_byte(bdf, addr, &val1); + printf(" %02x", val1); + } + + nbytes = readline (" ? "); + if (nbytes == 0 || (nbytes == 1 && console_buffer[0] == '-')) { + /* <CR> pressed as only input, don't modify current + * location and move to next. "-" pressed will go back. + */ + if (incrflag) + addr += nbytes ? -size : size; + nbytes = 1; +#ifdef CONFIG_BOOT_RETRY_TIME + reset_cmd_timeout(); /* good enough to not time out */ +#endif + } +#ifdef CONFIG_BOOT_RETRY_TIME + else if (nbytes == -2) { + break; /* timed out, exit the command */ + } +#endif + else { + char *endp; + i = simple_strtoul(console_buffer, &endp, 16); + nbytes = endp - console_buffer; + if (nbytes) { +#ifdef CONFIG_BOOT_RETRY_TIME + /* good enough to not time out + */ + reset_cmd_timeout(); +#endif + pci_cfg_write (bdf, addr, size, i); + if (incrflag) + addr += size; + } + } + } while (nbytes); + + return 0; +} + +/* PCI Configuration Space access commands + * + * Syntax: + * pci display[.b, .w, .l] bus.device.function} [addr] [len] + * pci next[.b, .w, .l] bus.device.function [addr] + * pci modify[.b, .w, .l] bus.device.function [addr] + * pci write[.b, .w, .l] bus.device.function addr value + */ +static int do_pci(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + ulong addr = 0, value = 0, size = 0; + pci_dev_t bdf = 0; + char cmd = 's'; + + if (argc > 1) + cmd = argv[1][0]; + + switch (cmd) { + case 'd': /* display */ + case 'n': /* next */ + case 'm': /* modify */ + case 'w': /* write */ + /* Check for a size specification. */ + size = cmd_get_data_size(argv[1], 4); + if (argc > 3) + addr = simple_strtoul(argv[3], NULL, 16); + if (argc > 4) + value = simple_strtoul(argv[4], NULL, 16); + case 'h': /* header */ + if (argc < 3) + goto usage; + if ((bdf = get_pci_dev(argv[2])) == -1) + return 1; + break; +#ifdef CONFIG_CMD_PCI_ENUM + case 'e': + break; +#endif + default: /* scan bus */ + value = 1; /* short listing */ + bdf = 0; /* bus number */ + if (argc > 1) { + if (argv[argc-1][0] == 'l') { + value = 0; + argc--; + } + if (argc > 1) + bdf = simple_strtoul(argv[1], NULL, 16); + } + pciinfo(bdf, value); + return 0; + } + + switch (argv[1][0]) { + case 'h': /* header */ + pci_header_show(bdf); + return 0; + case 'd': /* display */ + return pci_cfg_display(bdf, addr, size, value); +#ifdef CONFIG_CMD_PCI_ENUM + case 'e': + pci_init(); + return 0; +#endif + case 'n': /* next */ + if (argc < 4) + goto usage; + return pci_cfg_modify(bdf, addr, size, value, 0); + case 'm': /* modify */ + if (argc < 4) + goto usage; + return pci_cfg_modify(bdf, addr, size, value, 1); + case 'w': /* write */ + if (argc < 5) + goto usage; + return pci_cfg_write(bdf, addr, size, value); + } + + return 1; + usage: + return CMD_RET_USAGE; +} + +/***************************************************/ + +#ifdef CONFIG_SYS_LONGHELP +static char pci_help_text[] = + "[bus] [long]\n" + " - short or long list of PCI devices on bus 'bus'\n" +#ifdef CONFIG_CMD_PCI_ENUM + "pci enum\n" + " - re-enumerate PCI buses\n" +#endif + "pci header b.d.f\n" + " - show header of PCI device 'bus.device.function'\n" + "pci display[.b, .w, .l] b.d.f [address] [# of objects]\n" + " - display PCI configuration space (CFG)\n" + "pci next[.b, .w, .l] b.d.f address\n" + " - modify, read and keep CFG address\n" + "pci modify[.b, .w, .l] b.d.f address\n" + " - modify, auto increment CFG address\n" + "pci write[.b, .w, .l] b.d.f address value\n" + " - write to CFG address"; +#endif + +U_BOOT_CMD( + pci, 5, 1, do_pci, + "list and access PCI Configuration Space", pci_help_text +); diff --git a/qemu/roms/u-boot/common/cmd_pcmcia.c b/qemu/roms/u-boot/common/cmd_pcmcia.c new file mode 100644 index 000000000..ddc24be7d --- /dev/null +++ b/qemu/roms/u-boot/common/cmd_pcmcia.c @@ -0,0 +1,349 @@ +/* + * (C) Copyright 2000-2006 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * SPDX-License-Identifier: GPL-2.0+ + * + ******************************************************************** + * + * Lots of code copied from: + * + * m8xx_pcmcia.c - Linux PCMCIA socket driver for the mpc8xx series. + * (C) 1999-2000 Magnus Damm <damm@bitsmart.com> + * + * "The ExCA standard specifies that socket controllers should provide + * two IO and five memory windows per socket, which can be independently + * configured and positioned in the host address space and mapped to + * arbitrary segments of card address space. " - David A Hinds. 1999 + * + * This controller does _not_ meet the ExCA standard. + * + * m8xx pcmcia controller brief info: + * + 8 windows (attrib, mem, i/o) + * + up to two slots (SLOT_A and SLOT_B) + * + inputpins, outputpins, event and mask registers. + * - no offset register. sigh. + * + * Because of the lacking offset register we must map the whole card. + * We assign each memory window PCMCIA_MEM_WIN_SIZE address space. + * Make sure there is (PCMCIA_MEM_WIN_SIZE * PCMCIA_MEM_WIN_NO + * * PCMCIA_SOCKETS_NO) bytes at PCMCIA_MEM_WIN_BASE. + * The i/o windows are dynamically allocated at PCMCIA_IO_WIN_BASE. + * They are maximum 64KByte each... + */ + +/* #define DEBUG 1 */ + +/* + * PCMCIA support + */ +#include <common.h> +#include <command.h> +#include <config.h> +#include <pcmcia.h> +#include <asm/io.h> + +/* -------------------------------------------------------------------- */ + +#if defined(CONFIG_CMD_PCMCIA) + +extern int pcmcia_on (void); +extern int pcmcia_off (void); + +int do_pinit (cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + int rcode = 0; + + if (argc != 2) { + printf ("Usage: pinit {on | off}\n"); + return 1; + } + if (strcmp(argv[1],"on") == 0) { + rcode = pcmcia_on (); + } else if (strcmp(argv[1],"off") == 0) { + rcode = pcmcia_off (); + } else { + printf ("Usage: pinit {on | off}\n"); + return 1; + } + + return rcode; +} + +U_BOOT_CMD( + pinit, 2, 0, do_pinit, + "PCMCIA sub-system", + "on - power on PCMCIA socket\n" + "pinit off - power off PCMCIA socket" +); + +#endif + +/* -------------------------------------------------------------------- */ + +#undef CHECK_IDE_DEVICE + +#if defined(CONFIG_CMD_IDE) && defined(CONFIG_IDE_8xx_PCCARD) +#define CHECK_IDE_DEVICE +#endif + +#if defined(CONFIG_PXA_PCMCIA) +#define CHECK_IDE_DEVICE +#endif + +#ifdef CHECK_IDE_DEVICE + +int ide_devices_found; +static uchar *known_cards[] = { + (uchar *)"ARGOSY PnPIDE D5", + NULL +}; + +#define MAX_TUPEL_SZ 512 +#define MAX_FEATURES 4 + +#define MAX_IDENT_CHARS 64 +#define MAX_IDENT_FIELDS 4 + +#define indent "\t " + +static void print_funcid (int func) +{ + puts (indent); + switch (func) { + case CISTPL_FUNCID_MULTI: + puts (" Multi-Function"); + break; + case CISTPL_FUNCID_MEMORY: + puts (" Memory"); + break; + case CISTPL_FUNCID_SERIAL: + puts (" Serial Port"); + break; + case CISTPL_FUNCID_PARALLEL: + puts (" Parallel Port"); + break; + case CISTPL_FUNCID_FIXED: + puts (" Fixed Disk"); + break; + case CISTPL_FUNCID_VIDEO: + puts (" Video Adapter"); + break; + case CISTPL_FUNCID_NETWORK: + puts (" Network Adapter"); + break; + case CISTPL_FUNCID_AIMS: + puts (" AIMS Card"); + break; + case CISTPL_FUNCID_SCSI: + puts (" SCSI Adapter"); + break; + default: + puts (" Unknown"); + break; + } + puts (" Card\n"); +} + +static void print_fixed (volatile uchar *p) +{ + if (p == NULL) + return; + + puts(indent); + + switch (*p) { + case CISTPL_FUNCE_IDE_IFACE: + { uchar iface = *(p+2); + + puts ((iface == CISTPL_IDE_INTERFACE) ? " IDE" : " unknown"); + puts (" interface "); + break; + } + case CISTPL_FUNCE_IDE_MASTER: + case CISTPL_FUNCE_IDE_SLAVE: + { uchar f1 = *(p+2); + uchar f2 = *(p+4); + + puts ((f1 & CISTPL_IDE_SILICON) ? " [silicon]" : " [rotating]"); + + if (f1 & CISTPL_IDE_UNIQUE) + puts (" [unique]"); + + puts ((f1 & CISTPL_IDE_DUAL) ? " [dual]" : " [single]"); + + if (f2 & CISTPL_IDE_HAS_SLEEP) + puts (" [sleep]"); + + if (f2 & CISTPL_IDE_HAS_STANDBY) + puts (" [standby]"); + + if (f2 & CISTPL_IDE_HAS_IDLE) + puts (" [idle]"); + + if (f2 & CISTPL_IDE_LOW_POWER) + puts (" [low power]"); + + if (f2 & CISTPL_IDE_REG_INHIBIT) + puts (" [reg inhibit]"); + + if (f2 & CISTPL_IDE_HAS_INDEX) + puts (" [index]"); + + if (f2 & CISTPL_IDE_IOIS16) + puts (" [IOis16]"); + + break; + } + } + putc ('\n'); +} + +static int identify (volatile uchar *p) +{ + uchar id_str[MAX_IDENT_CHARS]; + uchar data; + uchar *t; + uchar **card; + int i, done; + + if (p == NULL) + return (0); /* Don't know */ + + t = id_str; + done =0; + + for (i=0; i<=4 && !done; ++i, p+=2) { + while ((data = *p) != '\0') { + if (data == 0xFF) { + done = 1; + break; + } + *t++ = data; + if (t == &id_str[MAX_IDENT_CHARS-1]) { + done = 1; + break; + } + p += 2; + } + if (!done) + *t++ = ' '; + } + *t = '\0'; + while (--t > id_str) { + if (*t == ' ') + *t = '\0'; + else + break; + } + puts ((char *)id_str); + putc ('\n'); + + for (card=known_cards; *card; ++card) { + debug ("## Compare against \"%s\"\n", *card); + if (strcmp((char *)*card, (char *)id_str) == 0) { /* found! */ + debug ("## CARD FOUND ##\n"); + return (1); + } + } + + return (0); /* don't know */ +} + +int check_ide_device (int slot) +{ + volatile uchar *ident = NULL; + volatile uchar *feature_p[MAX_FEATURES]; + volatile uchar *p, *start, *addr; + int n_features = 0; + uchar func_id = ~0; + uchar code, len; + ushort config_base = 0; + int found = 0; + int i; + + addr = (volatile uchar *)(CONFIG_SYS_PCMCIA_MEM_ADDR + + CONFIG_SYS_PCMCIA_MEM_SIZE * (slot * 4)); + debug ("PCMCIA MEM: %08lX\n", (ulong)addr); + + start = p = (volatile uchar *) addr; + + while ((p - start) < MAX_TUPEL_SZ) { + + code = *p; p += 2; + + if (code == 0xFF) { /* End of chain */ + break; + } + + len = *p; p += 2; +#if defined(DEBUG) && (DEBUG > 1) + { volatile uchar *q = p; + printf ("\nTuple code %02x length %d\n\tData:", + code, len); + + for (i = 0; i < len; ++i) { + printf (" %02x", *q); + q+= 2; + } + } +#endif /* DEBUG */ + switch (code) { + case CISTPL_VERS_1: + ident = p + 4; + break; + case CISTPL_FUNCID: + /* Fix for broken SanDisk which may have 0x80 bit set */ + func_id = *p & 0x7F; + break; + case CISTPL_FUNCE: + if (n_features < MAX_FEATURES) + feature_p[n_features++] = p; + break; + case CISTPL_CONFIG: + config_base = (*(p+6) << 8) + (*(p+4)); + debug ("\n## Config_base = %04x ###\n", config_base); + default: + break; + } + p += 2 * len; + } + + found = identify (ident); + + if (func_id != ((uchar)~0)) { + print_funcid (func_id); + + if (func_id == CISTPL_FUNCID_FIXED) + found = 1; + else + return (1); /* no disk drive */ + } + + for (i=0; i<n_features; ++i) { + print_fixed (feature_p[i]); + } + + if (!found) { + printf ("unknown card type\n"); + return (1); + } + + ide_devices_found |= (1 << slot); + +#if CONFIG_CPC45 +#else + /* set I/O area in config reg -> only valid for ARGOSY D5!!! */ + *((uchar *)(addr + config_base)) = 1; +#endif +#if 0 + printf("\n## Config_base = %04x ###\n", config_base); + printf("Configuration Option Register: %02x @ %x\n", readb(addr + config_base), addr + config_base); + printf("Card Configuration and Status Register: %02x\n", readb(addr + config_base + 2)); + printf("Pin Replacement Register Register: %02x\n", readb(addr + config_base + 4)); + printf("Socket and Copy Register: %02x\n", readb(addr + config_base + 6)); +#endif + return (0); +} + +#endif /* CHECK_IDE_DEVICE */ diff --git a/qemu/roms/u-boot/common/cmd_portio.c b/qemu/roms/u-boot/common/cmd_portio.c new file mode 100644 index 000000000..bf3a99791 --- /dev/null +++ b/qemu/roms/u-boot/common/cmd_portio.c @@ -0,0 +1,145 @@ +/* + * (C) Copyright 2003 + * Marc Singer, elf@buici.com + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/* + * Port I/O Functions + * + * Copied from FADS ROM, Dan Malek (dmalek@jlc.net) + */ + +#include <common.h> +#include <command.h> + +/* Display values from last command. + * Memory modify remembered values are different from display memory. + */ +static uint in_last_addr, in_last_size; +static uint out_last_addr, out_last_size, out_last_value; + + +int do_portio_out (cmd_tbl_t * cmdtp, int flag, int argc, char * const argv[]) +{ + uint addr = out_last_addr; + uint size = out_last_size; + uint value = out_last_value; + + if (argc != 3) + return CMD_RET_USAGE; + + if ((flag & CMD_FLAG_REPEAT) == 0) { + /* + * New command specified. Check for a size specification. + * Defaults to long if no or incorrect specification. + */ + size = cmd_get_data_size (argv[0], 1); + addr = simple_strtoul (argv[1], NULL, 16); + value = simple_strtoul (argv[2], NULL, 16); + } +#if defined (CONFIG_X86) + + { + unsigned short port = addr; + + switch (size) { + default: + case 1: + { + unsigned char ch = value; + __asm__ volatile ("out %0, %%dx"::"a" (ch), "d" (port)); + } + break; + case 2: + { + unsigned short w = value; + __asm__ volatile ("out %0, %%dx"::"a" (w), "d" (port)); + } + break; + case 4: + __asm__ volatile ("out %0, %%dx"::"a" (value), "d" (port)); + + break; + } + } + +#endif /* CONFIG_X86 */ + + out_last_addr = addr; + out_last_size = size; + out_last_value = value; + + return 0; +} + +U_BOOT_CMD( + out, 3, 1, do_portio_out, + "write datum to IO port", + "[.b, .w, .l] port value\n - output to IO port" +); + +int do_portio_in (cmd_tbl_t * cmdtp, int flag, int argc, char * const argv[]) +{ + uint addr = in_last_addr; + uint size = in_last_size; + + if (argc != 2) + return CMD_RET_USAGE; + + if ((flag & CMD_FLAG_REPEAT) == 0) { + /* + * New command specified. Check for a size specification. + * Defaults to long if no or incorrect specification. + */ + size = cmd_get_data_size (argv[0], 1); + addr = simple_strtoul (argv[1], NULL, 16); + } +#if defined (CONFIG_X86) + + { + unsigned short port = addr; + + switch (size) { + default: + case 1: + { + unsigned char ch; + __asm__ volatile ("in %%dx, %0":"=a" (ch):"d" (port)); + + printf (" %02x\n", ch); + } + break; + case 2: + { + unsigned short w; + __asm__ volatile ("in %%dx, %0":"=a" (w):"d" (port)); + + printf (" %04x\n", w); + } + break; + case 4: + { + unsigned long l; + __asm__ volatile ("in %%dx, %0":"=a" (l):"d" (port)); + + printf (" %08lx\n", l); + } + break; + } + } +#endif /* CONFIG_X86 */ + + in_last_addr = addr; + in_last_size = size; + + return 0; +} + +U_BOOT_CMD( + in, 2, 1, do_portio_in, + "read data from an IO port", + "[.b, .w, .l] port\n" + " - read datum from IO port" +); diff --git a/qemu/roms/u-boot/common/cmd_pxe.c b/qemu/roms/u-boot/common/cmd_pxe.c new file mode 100644 index 000000000..348332874 --- /dev/null +++ b/qemu/roms/u-boot/common/cmd_pxe.c @@ -0,0 +1,1679 @@ +/* + * Copyright 2010-2011 Calxeda, Inc. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <command.h> +#include <malloc.h> +#include <linux/string.h> +#include <linux/ctype.h> +#include <errno.h> +#include <linux/list.h> +#include <fs.h> + +#include "menu.h" + +#define MAX_TFTP_PATH_LEN 127 + +const char *pxe_default_paths[] = { +#ifdef CONFIG_SYS_SOC + "default-" CONFIG_SYS_ARCH "-" CONFIG_SYS_SOC, +#endif + "default-" CONFIG_SYS_ARCH, + "default", + NULL +}; + +static bool is_pxe; + +/* + * Like getenv, but prints an error if envvar isn't defined in the + * environment. It always returns what getenv does, so it can be used in + * place of getenv without changing error handling otherwise. + */ +static char *from_env(const char *envvar) +{ + char *ret; + + ret = getenv(envvar); + + if (!ret) + printf("missing environment variable: %s\n", envvar); + + return ret; +} + +#ifdef CONFIG_CMD_NET +/* + * Convert an ethaddr from the environment to the format used by pxelinux + * filenames based on mac addresses. Convert's ':' to '-', and adds "01-" to + * the beginning of the ethernet address to indicate a hardware type of + * Ethernet. Also converts uppercase hex characters into lowercase, to match + * pxelinux's behavior. + * + * Returns 1 for success, -ENOENT if 'ethaddr' is undefined in the + * environment, or some other value < 0 on error. + */ +static int format_mac_pxe(char *outbuf, size_t outbuf_len) +{ + uchar ethaddr[6]; + + if (outbuf_len < 21) { + printf("outbuf is too small (%zd < 21)\n", outbuf_len); + + return -EINVAL; + } + + if (!eth_getenv_enetaddr_by_index("eth", eth_get_dev_index(), + ethaddr)) + return -ENOENT; + + sprintf(outbuf, "01-%02x-%02x-%02x-%02x-%02x-%02x", + ethaddr[0], ethaddr[1], ethaddr[2], + ethaddr[3], ethaddr[4], ethaddr[5]); + + return 1; +} +#endif + +/* + * Returns the directory the file specified in the bootfile env variable is + * in. If bootfile isn't defined in the environment, return NULL, which should + * be interpreted as "don't prepend anything to paths". + */ +static int get_bootfile_path(const char *file_path, char *bootfile_path, + size_t bootfile_path_size) +{ + char *bootfile, *last_slash; + size_t path_len = 0; + + /* Only syslinux allows absolute paths */ + if (file_path[0] == '/' && !is_pxe) + goto ret; + + bootfile = from_env("bootfile"); + + if (!bootfile) + goto ret; + + last_slash = strrchr(bootfile, '/'); + + if (last_slash == NULL) + goto ret; + + path_len = (last_slash - bootfile) + 1; + + if (bootfile_path_size < path_len) { + printf("bootfile_path too small. (%zd < %zd)\n", + bootfile_path_size, path_len); + + return -1; + } + + strncpy(bootfile_path, bootfile, path_len); + + ret: + bootfile_path[path_len] = '\0'; + + return 1; +} + +static int (*do_getfile)(cmd_tbl_t *cmdtp, const char *file_path, char *file_addr); + +#ifdef CONFIG_CMD_NET +static int do_get_tftp(cmd_tbl_t *cmdtp, const char *file_path, char *file_addr) +{ + char *tftp_argv[] = {"tftp", NULL, NULL, NULL}; + + tftp_argv[1] = file_addr; + tftp_argv[2] = (void *)file_path; + + if (do_tftpb(cmdtp, 0, 3, tftp_argv)) + return -ENOENT; + + return 1; +} +#endif + +static char *fs_argv[5]; + +static int do_get_ext2(cmd_tbl_t *cmdtp, const char *file_path, char *file_addr) +{ +#ifdef CONFIG_CMD_EXT2 + fs_argv[0] = "ext2load"; + fs_argv[3] = file_addr; + fs_argv[4] = (void *)file_path; + + if (!do_ext2load(cmdtp, 0, 5, fs_argv)) + return 1; +#endif + return -ENOENT; +} + +static int do_get_fat(cmd_tbl_t *cmdtp, const char *file_path, char *file_addr) +{ +#ifdef CONFIG_CMD_FAT + fs_argv[0] = "fatload"; + fs_argv[3] = file_addr; + fs_argv[4] = (void *)file_path; + + if (!do_fat_fsload(cmdtp, 0, 5, fs_argv)) + return 1; +#endif + return -ENOENT; +} + +static int do_get_any(cmd_tbl_t *cmdtp, const char *file_path, char *file_addr) +{ +#ifdef CONFIG_CMD_FS_GENERIC + fs_argv[0] = "load"; + fs_argv[3] = file_addr; + fs_argv[4] = (void *)file_path; + + if (!do_load(cmdtp, 0, 5, fs_argv, FS_TYPE_ANY)) + return 1; +#endif + return -ENOENT; +} + +/* + * As in pxelinux, paths to files referenced from files we retrieve are + * relative to the location of bootfile. get_relfile takes such a path and + * joins it with the bootfile path to get the full path to the target file. If + * the bootfile path is NULL, we use file_path as is. + * + * Returns 1 for success, or < 0 on error. + */ +static int get_relfile(cmd_tbl_t *cmdtp, const char *file_path, void *file_addr) +{ + size_t path_len; + char relfile[MAX_TFTP_PATH_LEN+1]; + char addr_buf[10]; + int err; + + err = get_bootfile_path(file_path, relfile, sizeof(relfile)); + + if (err < 0) + return err; + + path_len = strlen(file_path); + path_len += strlen(relfile); + + if (path_len > MAX_TFTP_PATH_LEN) { + printf("Base path too long (%s%s)\n", + relfile, + file_path); + + return -ENAMETOOLONG; + } + + strcat(relfile, file_path); + + printf("Retrieving file: %s\n", relfile); + + sprintf(addr_buf, "%p", file_addr); + + return do_getfile(cmdtp, relfile, addr_buf); +} + +/* + * Retrieve the file at 'file_path' to the locate given by 'file_addr'. If + * 'bootfile' was specified in the environment, the path to bootfile will be + * prepended to 'file_path' and the resulting path will be used. + * + * Returns 1 on success, or < 0 for error. + */ +static int get_pxe_file(cmd_tbl_t *cmdtp, const char *file_path, void *file_addr) +{ + unsigned long config_file_size; + char *tftp_filesize; + int err; + + err = get_relfile(cmdtp, file_path, file_addr); + + if (err < 0) + return err; + + /* + * the file comes without a NUL byte at the end, so find out its size + * and add the NUL byte. + */ + tftp_filesize = from_env("filesize"); + + if (!tftp_filesize) + return -ENOENT; + + if (strict_strtoul(tftp_filesize, 16, &config_file_size) < 0) + return -EINVAL; + + *(char *)(file_addr + config_file_size) = '\0'; + + return 1; +} + +#ifdef CONFIG_CMD_NET + +#define PXELINUX_DIR "pxelinux.cfg/" + +/* + * Retrieves a file in the 'pxelinux.cfg' folder. Since this uses get_pxe_file + * to do the hard work, the location of the 'pxelinux.cfg' folder is generated + * from the bootfile path, as described above. + * + * Returns 1 on success or < 0 on error. + */ +static int get_pxelinux_path(cmd_tbl_t *cmdtp, const char *file, void *pxefile_addr_r) +{ + size_t base_len = strlen(PXELINUX_DIR); + char path[MAX_TFTP_PATH_LEN+1]; + + if (base_len + strlen(file) > MAX_TFTP_PATH_LEN) { + printf("path (%s%s) too long, skipping\n", + PXELINUX_DIR, file); + return -ENAMETOOLONG; + } + + sprintf(path, PXELINUX_DIR "%s", file); + + return get_pxe_file(cmdtp, path, pxefile_addr_r); +} + +/* + * Looks for a pxe file with a name based on the pxeuuid environment variable. + * + * Returns 1 on success or < 0 on error. + */ +static int pxe_uuid_path(cmd_tbl_t *cmdtp, void *pxefile_addr_r) +{ + char *uuid_str; + + uuid_str = from_env("pxeuuid"); + + if (!uuid_str) + return -ENOENT; + + return get_pxelinux_path(cmdtp, uuid_str, pxefile_addr_r); +} + +/* + * Looks for a pxe file with a name based on the 'ethaddr' environment + * variable. + * + * Returns 1 on success or < 0 on error. + */ +static int pxe_mac_path(cmd_tbl_t *cmdtp, void *pxefile_addr_r) +{ + char mac_str[21]; + int err; + + err = format_mac_pxe(mac_str, sizeof(mac_str)); + + if (err < 0) + return err; + + return get_pxelinux_path(cmdtp, mac_str, pxefile_addr_r); +} + +/* + * Looks for pxe files with names based on our IP address. See pxelinux + * documentation for details on what these file names look like. We match + * that exactly. + * + * Returns 1 on success or < 0 on error. + */ +static int pxe_ipaddr_paths(cmd_tbl_t *cmdtp, void *pxefile_addr_r) +{ + char ip_addr[9]; + int mask_pos, err; + + sprintf(ip_addr, "%08X", ntohl(NetOurIP)); + + for (mask_pos = 7; mask_pos >= 0; mask_pos--) { + err = get_pxelinux_path(cmdtp, ip_addr, pxefile_addr_r); + + if (err > 0) + return err; + + ip_addr[mask_pos] = '\0'; + } + + return -ENOENT; +} + +/* + * Entry point for the 'pxe get' command. + * This Follows pxelinux's rules to download a config file from a tftp server. + * The file is stored at the location given by the pxefile_addr_r environment + * variable, which must be set. + * + * UUID comes from pxeuuid env variable, if defined + * MAC addr comes from ethaddr env variable, if defined + * IP + * + * see http://syslinux.zytor.com/wiki/index.php/PXELINUX + * + * Returns 0 on success or 1 on error. + */ +static int +do_pxe_get(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + char *pxefile_addr_str; + unsigned long pxefile_addr_r; + int err, i = 0; + + do_getfile = do_get_tftp; + + if (argc != 1) + return CMD_RET_USAGE; + + pxefile_addr_str = from_env("pxefile_addr_r"); + + if (!pxefile_addr_str) + return 1; + + err = strict_strtoul(pxefile_addr_str, 16, + (unsigned long *)&pxefile_addr_r); + if (err < 0) + return 1; + + /* + * Keep trying paths until we successfully get a file we're looking + * for. + */ + if (pxe_uuid_path(cmdtp, (void *)pxefile_addr_r) > 0 || + pxe_mac_path(cmdtp, (void *)pxefile_addr_r) > 0 || + pxe_ipaddr_paths(cmdtp, (void *)pxefile_addr_r) > 0) { + printf("Config file found\n"); + + return 0; + } + + while (pxe_default_paths[i]) { + if (get_pxelinux_path(cmdtp, pxe_default_paths[i], + (void *)pxefile_addr_r) > 0) { + printf("Config file found\n"); + return 0; + } + i++; + } + + printf("Config file not found\n"); + + return 1; +} +#endif + +/* + * Wrapper to make it easier to store the file at file_path in the location + * specified by envaddr_name. file_path will be joined to the bootfile path, + * if any is specified. + * + * Returns 1 on success or < 0 on error. + */ +static int get_relfile_envaddr(cmd_tbl_t *cmdtp, const char *file_path, const char *envaddr_name) +{ + unsigned long file_addr; + char *envaddr; + + envaddr = from_env(envaddr_name); + + if (!envaddr) + return -ENOENT; + + if (strict_strtoul(envaddr, 16, &file_addr) < 0) + return -EINVAL; + + return get_relfile(cmdtp, file_path, (void *)file_addr); +} + +/* + * A note on the pxe file parser. + * + * We're parsing files that use syslinux grammar, which has a few quirks. + * String literals must be recognized based on context - there is no + * quoting or escaping support. There's also nothing to explicitly indicate + * when a label section completes. We deal with that by ending a label + * section whenever we see a line that doesn't include. + * + * As with the syslinux family, this same file format could be reused in the + * future for non pxe purposes. The only action it takes during parsing that + * would throw this off is handling of include files. It assumes we're using + * pxe, and does a tftp download of a file listed as an include file in the + * middle of the parsing operation. That could be handled by refactoring it to + * take a 'include file getter' function. + */ + +/* + * Describes a single label given in a pxe file. + * + * Create these with the 'label_create' function given below. + * + * name - the name of the menu as given on the 'menu label' line. + * kernel - the path to the kernel file to use for this label. + * append - kernel command line to use when booting this label + * initrd - path to the initrd to use for this label. + * attempted - 0 if we haven't tried to boot this label, 1 if we have. + * localboot - 1 if this label specified 'localboot', 0 otherwise. + * list - lets these form a list, which a pxe_menu struct will hold. + */ +struct pxe_label { + char num[4]; + char *name; + char *menu; + char *kernel; + char *append; + char *initrd; + char *fdt; + char *fdtdir; + int ipappend; + int attempted; + int localboot; + int localboot_val; + struct list_head list; +}; + +/* + * Describes a pxe menu as given via pxe files. + * + * title - the name of the menu as given by a 'menu title' line. + * default_label - the name of the default label, if any. + * timeout - time in tenths of a second to wait for a user key-press before + * booting the default label. + * prompt - if 0, don't prompt for a choice unless the timeout period is + * interrupted. If 1, always prompt for a choice regardless of + * timeout. + * labels - a list of labels defined for the menu. + */ +struct pxe_menu { + char *title; + char *default_label; + int timeout; + int prompt; + struct list_head labels; +}; + +/* + * Allocates memory for and initializes a pxe_label. This uses malloc, so the + * result must be free()'d to reclaim the memory. + * + * Returns NULL if malloc fails. + */ +static struct pxe_label *label_create(void) +{ + struct pxe_label *label; + + label = malloc(sizeof(struct pxe_label)); + + if (!label) + return NULL; + + memset(label, 0, sizeof(struct pxe_label)); + + return label; +} + +/* + * Free the memory used by a pxe_label, including that used by its name, + * kernel, append and initrd members, if they're non NULL. + * + * So - be sure to only use dynamically allocated memory for the members of + * the pxe_label struct, unless you want to clean it up first. These are + * currently only created by the pxe file parsing code. + */ +static void label_destroy(struct pxe_label *label) +{ + if (label->name) + free(label->name); + + if (label->kernel) + free(label->kernel); + + if (label->append) + free(label->append); + + if (label->initrd) + free(label->initrd); + + if (label->fdt) + free(label->fdt); + + if (label->fdtdir) + free(label->fdtdir); + + free(label); +} + +/* + * Print a label and its string members if they're defined. + * + * This is passed as a callback to the menu code for displaying each + * menu entry. + */ +static void label_print(void *data) +{ + struct pxe_label *label = data; + const char *c = label->menu ? label->menu : label->name; + + printf("%s:\t%s\n", label->num, c); +} + +/* + * Boot a label that specified 'localboot'. This requires that the 'localcmd' + * environment variable is defined. Its contents will be executed as U-boot + * command. If the label specified an 'append' line, its contents will be + * used to overwrite the contents of the 'bootargs' environment variable prior + * to running 'localcmd'. + * + * Returns 1 on success or < 0 on error. + */ +static int label_localboot(struct pxe_label *label) +{ + char *localcmd; + + localcmd = from_env("localcmd"); + + if (!localcmd) + return -ENOENT; + + if (label->append) + setenv("bootargs", label->append); + + debug("running: %s\n", localcmd); + + return run_command_list(localcmd, strlen(localcmd), 0); +} + +/* + * Boot according to the contents of a pxe_label. + * + * If we can't boot for any reason, we return. A successful boot never + * returns. + * + * The kernel will be stored in the location given by the 'kernel_addr_r' + * environment variable. + * + * If the label specifies an initrd file, it will be stored in the location + * given by the 'ramdisk_addr_r' environment variable. + * + * If the label specifies an 'append' line, its contents will overwrite that + * of the 'bootargs' environment variable. + */ +static int label_boot(cmd_tbl_t *cmdtp, struct pxe_label *label) +{ + char *bootm_argv[] = { "bootm", NULL, NULL, NULL, NULL }; + char initrd_str[22]; + char mac_str[29] = ""; + char ip_str[68] = ""; + char *bootargs; + int bootm_argc = 3; + int len = 0; + + label_print(label); + + label->attempted = 1; + + if (label->localboot) { + if (label->localboot_val >= 0) + label_localboot(label); + return 0; + } + + if (label->kernel == NULL) { + printf("No kernel given, skipping %s\n", + label->name); + return 1; + } + + if (label->initrd) { + if (get_relfile_envaddr(cmdtp, label->initrd, "ramdisk_addr_r") < 0) { + printf("Skipping %s for failure retrieving initrd\n", + label->name); + return 1; + } + + bootm_argv[2] = initrd_str; + strcpy(bootm_argv[2], getenv("ramdisk_addr_r")); + strcat(bootm_argv[2], ":"); + strcat(bootm_argv[2], getenv("filesize")); + } else { + bootm_argv[2] = "-"; + } + + if (get_relfile_envaddr(cmdtp, label->kernel, "kernel_addr_r") < 0) { + printf("Skipping %s for failure retrieving kernel\n", + label->name); + return 1; + } + + if (label->ipappend & 0x1) { + sprintf(ip_str, " ip=%s:%s:%s:%s", + getenv("ipaddr"), getenv("serverip"), + getenv("gatewayip"), getenv("netmask")); + len += strlen(ip_str); + } + +#ifdef CONFIG_CMD_NET + if (label->ipappend & 0x2) { + int err; + strcpy(mac_str, " BOOTIF="); + err = format_mac_pxe(mac_str + 8, sizeof(mac_str) - 8); + if (err < 0) + mac_str[0] = '\0'; + len += strlen(mac_str); + } +#endif + + if (label->append) + len += strlen(label->append); + + if (len) { + bootargs = malloc(len + 1); + if (!bootargs) + return 1; + bootargs[0] = '\0'; + if (label->append) + strcpy(bootargs, label->append); + strcat(bootargs, ip_str); + strcat(bootargs, mac_str); + + setenv("bootargs", bootargs); + printf("append: %s\n", bootargs); + + free(bootargs); + } + + bootm_argv[1] = getenv("kernel_addr_r"); + + /* + * fdt usage is optional: + * It handles the following scenarios. All scenarios are exclusive + * + * Scenario 1: If fdt_addr_r specified and "fdt" label is defined in + * pxe file, retrieve fdt blob from server. Pass fdt_addr_r to bootm, + * and adjust argc appropriately. + * + * Scenario 2: If there is an fdt_addr specified, pass it along to + * bootm, and adjust argc appropriately. + * + * Scenario 3: fdt blob is not available. + */ + bootm_argv[3] = getenv("fdt_addr_r"); + + /* if fdt label is defined then get fdt from server */ + if (bootm_argv[3]) { + char *fdtfile = NULL; + char *fdtfilefree = NULL; + + if (label->fdt) { + fdtfile = label->fdt; + } else if (label->fdtdir) { + char *f1, *f2, *f3, *f4, *slash; + + f1 = getenv("fdtfile"); + if (f1) { + f2 = ""; + f3 = ""; + f4 = ""; + } else { + /* + * For complex cases where this code doesn't + * generate the correct filename, the board + * code should set $fdtfile during early boot, + * or the boot scripts should set $fdtfile + * before invoking "pxe" or "sysboot". + */ + f1 = getenv("soc"); + f2 = "-"; + f3 = getenv("board"); + f4 = ".dtb"; + } + + len = strlen(label->fdtdir); + if (!len) + slash = "./"; + else if (label->fdtdir[len - 1] != '/') + slash = "/"; + else + slash = ""; + + len = strlen(label->fdtdir) + strlen(slash) + + strlen(f1) + strlen(f2) + strlen(f3) + + strlen(f4) + 1; + fdtfilefree = malloc(len); + if (!fdtfilefree) { + printf("malloc fail (FDT filename)\n"); + return 1; + } + + snprintf(fdtfilefree, len, "%s%s%s%s%s%s", + label->fdtdir, slash, f1, f2, f3, f4); + fdtfile = fdtfilefree; + } + + if (fdtfile) { + int err = get_relfile_envaddr(cmdtp, fdtfile, "fdt_addr_r"); + free(fdtfilefree); + if (err < 0) { + printf("Skipping %s for failure retrieving fdt\n", + label->name); + return 1; + } + } else { + bootm_argv[3] = NULL; + } + } + + if (!bootm_argv[3]) + bootm_argv[3] = getenv("fdt_addr"); + + if (bootm_argv[3]) + bootm_argc = 4; + + do_bootm(cmdtp, 0, bootm_argc, bootm_argv); + +#ifdef CONFIG_CMD_BOOTZ + /* Try booting a zImage if do_bootm returns */ + do_bootz(cmdtp, 0, bootm_argc, bootm_argv); +#endif + return 1; +} + +/* + * Tokens for the pxe file parser. + */ +enum token_type { + T_EOL, + T_STRING, + T_EOF, + T_MENU, + T_TITLE, + T_TIMEOUT, + T_LABEL, + T_KERNEL, + T_LINUX, + T_APPEND, + T_INITRD, + T_LOCALBOOT, + T_DEFAULT, + T_PROMPT, + T_INCLUDE, + T_FDT, + T_FDTDIR, + T_ONTIMEOUT, + T_IPAPPEND, + T_INVALID +}; + +/* + * A token - given by a value and a type. + */ +struct token { + char *val; + enum token_type type; +}; + +/* + * Keywords recognized. + */ +static const struct token keywords[] = { + {"menu", T_MENU}, + {"title", T_TITLE}, + {"timeout", T_TIMEOUT}, + {"default", T_DEFAULT}, + {"prompt", T_PROMPT}, + {"label", T_LABEL}, + {"kernel", T_KERNEL}, + {"linux", T_LINUX}, + {"localboot", T_LOCALBOOT}, + {"append", T_APPEND}, + {"initrd", T_INITRD}, + {"include", T_INCLUDE}, + {"devicetree", T_FDT}, + {"fdt", T_FDT}, + {"devicetreedir", T_FDTDIR}, + {"fdtdir", T_FDTDIR}, + {"ontimeout", T_ONTIMEOUT,}, + {"ipappend", T_IPAPPEND,}, + {NULL, T_INVALID} +}; + +/* + * Since pxe(linux) files don't have a token to identify the start of a + * literal, we have to keep track of when we're in a state where a literal is + * expected vs when we're in a state a keyword is expected. + */ +enum lex_state { + L_NORMAL = 0, + L_KEYWORD, + L_SLITERAL +}; + +/* + * get_string retrieves a string from *p and stores it as a token in + * *t. + * + * get_string used for scanning both string literals and keywords. + * + * Characters from *p are copied into t-val until a character equal to + * delim is found, or a NUL byte is reached. If delim has the special value of + * ' ', any whitespace character will be used as a delimiter. + * + * If lower is unequal to 0, uppercase characters will be converted to + * lowercase in the result. This is useful to make keywords case + * insensitive. + * + * The location of *p is updated to point to the first character after the end + * of the token - the ending delimiter. + * + * On success, the new value of t->val is returned. Memory for t->val is + * allocated using malloc and must be free()'d to reclaim it. If insufficient + * memory is available, NULL is returned. + */ +static char *get_string(char **p, struct token *t, char delim, int lower) +{ + char *b, *e; + size_t len, i; + + /* + * b and e both start at the beginning of the input stream. + * + * e is incremented until we find the ending delimiter, or a NUL byte + * is reached. Then, we take e - b to find the length of the token. + */ + b = e = *p; + + while (*e) { + if ((delim == ' ' && isspace(*e)) || delim == *e) + break; + e++; + } + + len = e - b; + + /* + * Allocate memory to hold the string, and copy it in, converting + * characters to lowercase if lower is != 0. + */ + t->val = malloc(len + 1); + if (!t->val) + return NULL; + + for (i = 0; i < len; i++, b++) { + if (lower) + t->val[i] = tolower(*b); + else + t->val[i] = *b; + } + + t->val[len] = '\0'; + + /* + * Update *p so the caller knows where to continue scanning. + */ + *p = e; + + t->type = T_STRING; + + return t->val; +} + +/* + * Populate a keyword token with a type and value. + */ +static void get_keyword(struct token *t) +{ + int i; + + for (i = 0; keywords[i].val; i++) { + if (!strcmp(t->val, keywords[i].val)) { + t->type = keywords[i].type; + break; + } + } +} + +/* + * Get the next token. We have to keep track of which state we're in to know + * if we're looking to get a string literal or a keyword. + * + * *p is updated to point at the first character after the current token. + */ +static void get_token(char **p, struct token *t, enum lex_state state) +{ + char *c = *p; + + t->type = T_INVALID; + + /* eat non EOL whitespace */ + while (isblank(*c)) + c++; + + /* + * eat comments. note that string literals can't begin with #, but + * can contain a # after their first character. + */ + if (*c == '#') { + while (*c && *c != '\n') + c++; + } + + if (*c == '\n') { + t->type = T_EOL; + c++; + } else if (*c == '\0') { + t->type = T_EOF; + c++; + } else if (state == L_SLITERAL) { + get_string(&c, t, '\n', 0); + } else if (state == L_KEYWORD) { + /* + * when we expect a keyword, we first get the next string + * token delimited by whitespace, and then check if it + * matches a keyword in our keyword list. if it does, it's + * converted to a keyword token of the appropriate type, and + * if not, it remains a string token. + */ + get_string(&c, t, ' ', 1); + get_keyword(t); + } + + *p = c; +} + +/* + * Increment *c until we get to the end of the current line, or EOF. + */ +static void eol_or_eof(char **c) +{ + while (**c && **c != '\n') + (*c)++; +} + +/* + * All of these parse_* functions share some common behavior. + * + * They finish with *c pointing after the token they parse, and return 1 on + * success, or < 0 on error. + */ + +/* + * Parse a string literal and store a pointer it at *dst. String literals + * terminate at the end of the line. + */ +static int parse_sliteral(char **c, char **dst) +{ + struct token t; + char *s = *c; + + get_token(c, &t, L_SLITERAL); + + if (t.type != T_STRING) { + printf("Expected string literal: %.*s\n", (int)(*c - s), s); + return -EINVAL; + } + + *dst = t.val; + + return 1; +} + +/* + * Parse a base 10 (unsigned) integer and store it at *dst. + */ +static int parse_integer(char **c, int *dst) +{ + struct token t; + char *s = *c; + + get_token(c, &t, L_SLITERAL); + + if (t.type != T_STRING) { + printf("Expected string: %.*s\n", (int)(*c - s), s); + return -EINVAL; + } + + *dst = simple_strtol(t.val, NULL, 10); + + free(t.val); + + return 1; +} + +static int parse_pxefile_top(cmd_tbl_t *cmdtp, char *p, struct pxe_menu *cfg, int nest_level); + +/* + * Parse an include statement, and retrieve and parse the file it mentions. + * + * base should point to a location where it's safe to store the file, and + * nest_level should indicate how many nested includes have occurred. For this + * include, nest_level has already been incremented and doesn't need to be + * incremented here. + */ +static int handle_include(cmd_tbl_t *cmdtp, char **c, char *base, + struct pxe_menu *cfg, int nest_level) +{ + char *include_path; + char *s = *c; + int err; + + err = parse_sliteral(c, &include_path); + + if (err < 0) { + printf("Expected include path: %.*s\n", + (int)(*c - s), s); + return err; + } + + err = get_pxe_file(cmdtp, include_path, base); + + if (err < 0) { + printf("Couldn't retrieve %s\n", include_path); + return err; + } + + return parse_pxefile_top(cmdtp, base, cfg, nest_level); +} + +/* + * Parse lines that begin with 'menu'. + * + * b and nest are provided to handle the 'menu include' case. + * + * b should be the address where the file currently being parsed is stored. + * + * nest_level should be 1 when parsing the top level pxe file, 2 when parsing + * a file it includes, 3 when parsing a file included by that file, and so on. + */ +static int parse_menu(cmd_tbl_t *cmdtp, char **c, struct pxe_menu *cfg, char *b, int nest_level) +{ + struct token t; + char *s = *c; + int err = 0; + + get_token(c, &t, L_KEYWORD); + + switch (t.type) { + case T_TITLE: + err = parse_sliteral(c, &cfg->title); + + break; + + case T_INCLUDE: + err = handle_include(cmdtp, c, b + strlen(b) + 1, cfg, + nest_level + 1); + break; + + default: + printf("Ignoring malformed menu command: %.*s\n", + (int)(*c - s), s); + } + + if (err < 0) + return err; + + eol_or_eof(c); + + return 1; +} + +/* + * Handles parsing a 'menu line' when we're parsing a label. + */ +static int parse_label_menu(char **c, struct pxe_menu *cfg, + struct pxe_label *label) +{ + struct token t; + char *s; + + s = *c; + + get_token(c, &t, L_KEYWORD); + + switch (t.type) { + case T_DEFAULT: + if (!cfg->default_label) + cfg->default_label = strdup(label->name); + + if (!cfg->default_label) + return -ENOMEM; + + break; + case T_LABEL: + parse_sliteral(c, &label->menu); + break; + default: + printf("Ignoring malformed menu command: %.*s\n", + (int)(*c - s), s); + } + + eol_or_eof(c); + + return 0; +} + +/* + * Parses a label and adds it to the list of labels for a menu. + * + * A label ends when we either get to the end of a file, or + * get some input we otherwise don't have a handler defined + * for. + * + */ +static int parse_label(char **c, struct pxe_menu *cfg) +{ + struct token t; + int len; + char *s = *c; + struct pxe_label *label; + int err; + + label = label_create(); + if (!label) + return -ENOMEM; + + err = parse_sliteral(c, &label->name); + if (err < 0) { + printf("Expected label name: %.*s\n", (int)(*c - s), s); + label_destroy(label); + return -EINVAL; + } + + list_add_tail(&label->list, &cfg->labels); + + while (1) { + s = *c; + get_token(c, &t, L_KEYWORD); + + err = 0; + switch (t.type) { + case T_MENU: + err = parse_label_menu(c, cfg, label); + break; + + case T_KERNEL: + case T_LINUX: + err = parse_sliteral(c, &label->kernel); + break; + + case T_APPEND: + err = parse_sliteral(c, &label->append); + if (label->initrd) + break; + s = strstr(label->append, "initrd="); + if (!s) + break; + s += 7; + len = (int)(strchr(s, ' ') - s); + label->initrd = malloc(len + 1); + strncpy(label->initrd, s, len); + label->initrd[len] = '\0'; + + break; + + case T_INITRD: + if (!label->initrd) + err = parse_sliteral(c, &label->initrd); + break; + + case T_FDT: + if (!label->fdt) + err = parse_sliteral(c, &label->fdt); + break; + + case T_FDTDIR: + if (!label->fdtdir) + err = parse_sliteral(c, &label->fdtdir); + break; + + case T_LOCALBOOT: + label->localboot = 1; + err = parse_integer(c, &label->localboot_val); + break; + + case T_IPAPPEND: + err = parse_integer(c, &label->ipappend); + break; + + case T_EOL: + break; + + default: + /* + * put the token back! we don't want it - it's the end + * of a label and whatever token this is, it's + * something for the menu level context to handle. + */ + *c = s; + return 1; + } + + if (err < 0) + return err; + } +} + +/* + * This 16 comes from the limit pxelinux imposes on nested includes. + * + * There is no reason at all we couldn't do more, but some limit helps prevent + * infinite (until crash occurs) recursion if a file tries to include itself. + */ +#define MAX_NEST_LEVEL 16 + +/* + * Entry point for parsing a menu file. nest_level indicates how many times + * we've nested in includes. It will be 1 for the top level menu file. + * + * Returns 1 on success, < 0 on error. + */ +static int parse_pxefile_top(cmd_tbl_t *cmdtp, char *p, struct pxe_menu *cfg, int nest_level) +{ + struct token t; + char *s, *b, *label_name; + int err; + + b = p; + + if (nest_level > MAX_NEST_LEVEL) { + printf("Maximum nesting (%d) exceeded\n", MAX_NEST_LEVEL); + return -EMLINK; + } + + while (1) { + s = p; + + get_token(&p, &t, L_KEYWORD); + + err = 0; + switch (t.type) { + case T_MENU: + cfg->prompt = 1; + err = parse_menu(cmdtp, &p, cfg, b, nest_level); + break; + + case T_TIMEOUT: + err = parse_integer(&p, &cfg->timeout); + break; + + case T_LABEL: + err = parse_label(&p, cfg); + break; + + case T_DEFAULT: + case T_ONTIMEOUT: + err = parse_sliteral(&p, &label_name); + + if (label_name) { + if (cfg->default_label) + free(cfg->default_label); + + cfg->default_label = label_name; + } + + break; + + case T_INCLUDE: + err = handle_include(cmdtp, &p, b + ALIGN(strlen(b), 4), cfg, + nest_level + 1); + break; + + case T_PROMPT: + eol_or_eof(&p); + break; + + case T_EOL: + break; + + case T_EOF: + return 1; + + default: + printf("Ignoring unknown command: %.*s\n", + (int)(p - s), s); + eol_or_eof(&p); + } + + if (err < 0) + return err; + } +} + +/* + * Free the memory used by a pxe_menu and its labels. + */ +static void destroy_pxe_menu(struct pxe_menu *cfg) +{ + struct list_head *pos, *n; + struct pxe_label *label; + + if (cfg->title) + free(cfg->title); + + if (cfg->default_label) + free(cfg->default_label); + + list_for_each_safe(pos, n, &cfg->labels) { + label = list_entry(pos, struct pxe_label, list); + + label_destroy(label); + } + + free(cfg); +} + +/* + * Entry point for parsing a pxe file. This is only used for the top level + * file. + * + * Returns NULL if there is an error, otherwise, returns a pointer to a + * pxe_menu struct populated with the results of parsing the pxe file (and any + * files it includes). The resulting pxe_menu struct can be free()'d by using + * the destroy_pxe_menu() function. + */ +static struct pxe_menu *parse_pxefile(cmd_tbl_t *cmdtp, char *menucfg) +{ + struct pxe_menu *cfg; + + cfg = malloc(sizeof(struct pxe_menu)); + + if (!cfg) + return NULL; + + memset(cfg, 0, sizeof(struct pxe_menu)); + + INIT_LIST_HEAD(&cfg->labels); + + if (parse_pxefile_top(cmdtp, menucfg, cfg, 1) < 0) { + destroy_pxe_menu(cfg); + return NULL; + } + + return cfg; +} + +/* + * Converts a pxe_menu struct into a menu struct for use with U-boot's generic + * menu code. + */ +static struct menu *pxe_menu_to_menu(struct pxe_menu *cfg) +{ + struct pxe_label *label; + struct list_head *pos; + struct menu *m; + int err; + int i = 1; + char *default_num = NULL; + + /* + * Create a menu and add items for all the labels. + */ + m = menu_create(cfg->title, cfg->timeout, cfg->prompt, label_print, + NULL, NULL); + + if (!m) + return NULL; + + list_for_each(pos, &cfg->labels) { + label = list_entry(pos, struct pxe_label, list); + + sprintf(label->num, "%d", i++); + if (menu_item_add(m, label->num, label) != 1) { + menu_destroy(m); + return NULL; + } + if (cfg->default_label && + (strcmp(label->name, cfg->default_label) == 0)) + default_num = label->num; + + } + + /* + * After we've created items for each label in the menu, set the + * menu's default label if one was specified. + */ + if (default_num) { + err = menu_default_set(m, default_num); + if (err != 1) { + if (err != -ENOENT) { + menu_destroy(m); + return NULL; + } + + printf("Missing default: %s\n", cfg->default_label); + } + } + + return m; +} + +/* + * Try to boot any labels we have yet to attempt to boot. + */ +static void boot_unattempted_labels(cmd_tbl_t *cmdtp, struct pxe_menu *cfg) +{ + struct list_head *pos; + struct pxe_label *label; + + list_for_each(pos, &cfg->labels) { + label = list_entry(pos, struct pxe_label, list); + + if (!label->attempted) + label_boot(cmdtp, label); + } +} + +/* + * Boot the system as prescribed by a pxe_menu. + * + * Use the menu system to either get the user's choice or the default, based + * on config or user input. If there is no default or user's choice, + * attempted to boot labels in the order they were given in pxe files. + * If the default or user's choice fails to boot, attempt to boot other + * labels in the order they were given in pxe files. + * + * If this function returns, there weren't any labels that successfully + * booted, or the user interrupted the menu selection via ctrl+c. + */ +static void handle_pxe_menu(cmd_tbl_t *cmdtp, struct pxe_menu *cfg) +{ + void *choice; + struct menu *m; + int err; + + m = pxe_menu_to_menu(cfg); + if (!m) + return; + + err = menu_get_choice(m, &choice); + + menu_destroy(m); + + /* + * err == 1 means we got a choice back from menu_get_choice. + * + * err == -ENOENT if the menu was setup to select the default but no + * default was set. in that case, we should continue trying to boot + * labels that haven't been attempted yet. + * + * otherwise, the user interrupted or there was some other error and + * we give up. + */ + + if (err == 1) { + err = label_boot(cmdtp, choice); + if (!err) + return; + } else if (err != -ENOENT) { + return; + } + + boot_unattempted_labels(cmdtp, cfg); +} + +#ifdef CONFIG_CMD_NET +/* + * Boots a system using a pxe file + * + * Returns 0 on success, 1 on error. + */ +static int +do_pxe_boot(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + unsigned long pxefile_addr_r; + struct pxe_menu *cfg; + char *pxefile_addr_str; + + do_getfile = do_get_tftp; + + if (argc == 1) { + pxefile_addr_str = from_env("pxefile_addr_r"); + if (!pxefile_addr_str) + return 1; + + } else if (argc == 2) { + pxefile_addr_str = argv[1]; + } else { + return CMD_RET_USAGE; + } + + if (strict_strtoul(pxefile_addr_str, 16, &pxefile_addr_r) < 0) { + printf("Invalid pxefile address: %s\n", pxefile_addr_str); + return 1; + } + + cfg = parse_pxefile(cmdtp, (char *)(pxefile_addr_r)); + + if (cfg == NULL) { + printf("Error parsing config file\n"); + return 1; + } + + handle_pxe_menu(cmdtp, cfg); + + destroy_pxe_menu(cfg); + + return 0; +} + +static cmd_tbl_t cmd_pxe_sub[] = { + U_BOOT_CMD_MKENT(get, 1, 1, do_pxe_get, "", ""), + U_BOOT_CMD_MKENT(boot, 2, 1, do_pxe_boot, "", "") +}; + +int do_pxe(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + cmd_tbl_t *cp; + + if (argc < 2) + return CMD_RET_USAGE; + + is_pxe = true; + + /* drop initial "pxe" arg */ + argc--; + argv++; + + cp = find_cmd_tbl(argv[0], cmd_pxe_sub, ARRAY_SIZE(cmd_pxe_sub)); + + if (cp) + return cp->cmd(cmdtp, flag, argc, argv); + + return CMD_RET_USAGE; +} + +U_BOOT_CMD( + pxe, 3, 1, do_pxe, + "commands to get and boot from pxe files", + "get - try to retrieve a pxe file using tftp\npxe " + "boot [pxefile_addr_r] - boot from the pxe file at pxefile_addr_r\n" +); +#endif + +/* + * Boots a system using a local disk syslinux/extlinux file + * + * Returns 0 on success, 1 on error. + */ +int do_sysboot(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + unsigned long pxefile_addr_r; + struct pxe_menu *cfg; + char *pxefile_addr_str; + char *filename; + int prompt = 0; + + is_pxe = false; + + if (strstr(argv[1], "-p")) { + prompt = 1; + argc--; + argv++; + } + + if (argc < 4) + return cmd_usage(cmdtp); + + if (argc < 5) { + pxefile_addr_str = from_env("pxefile_addr_r"); + if (!pxefile_addr_str) + return 1; + } else { + pxefile_addr_str = argv[4]; + } + + if (argc < 6) + filename = getenv("bootfile"); + else { + filename = argv[5]; + setenv("bootfile", filename); + } + + if (strstr(argv[3], "ext2")) + do_getfile = do_get_ext2; + else if (strstr(argv[3], "fat")) + do_getfile = do_get_fat; + else if (strstr(argv[3], "any")) + do_getfile = do_get_any; + else { + printf("Invalid filesystem: %s\n", argv[3]); + return 1; + } + fs_argv[1] = argv[1]; + fs_argv[2] = argv[2]; + + if (strict_strtoul(pxefile_addr_str, 16, &pxefile_addr_r) < 0) { + printf("Invalid pxefile address: %s\n", pxefile_addr_str); + return 1; + } + + if (get_pxe_file(cmdtp, filename, (void *)pxefile_addr_r) < 0) { + printf("Error reading config file\n"); + return 1; + } + + cfg = parse_pxefile(cmdtp, (char *)(pxefile_addr_r)); + + if (cfg == NULL) { + printf("Error parsing config file\n"); + return 1; + } + + if (prompt) + cfg->prompt = 1; + + handle_pxe_menu(cmdtp, cfg); + + destroy_pxe_menu(cfg); + + return 0; +} + +U_BOOT_CMD( + sysboot, 7, 1, do_sysboot, + "command to get and boot from syslinux files", + "[-p] <interface> <dev[:part]> <ext2|fat|any> [addr] [filename]\n" + " - load and parse syslinux menu file 'filename' from ext2, fat\n" + " or any filesystem on 'dev' on 'interface' to address 'addr'" +); diff --git a/qemu/roms/u-boot/common/cmd_read.c b/qemu/roms/u-boot/common/cmd_read.c new file mode 100644 index 000000000..f0fc9bfe1 --- /dev/null +++ b/qemu/roms/u-boot/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 <common.h> +#include <command.h> +#include <part.h> + +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", + "<interface> <dev[:part]> addr blk# cnt" +); diff --git a/qemu/roms/u-boot/common/cmd_reginfo.c b/qemu/roms/u-boot/common/cmd_reginfo.c new file mode 100644 index 000000000..5f19e7911 --- /dev/null +++ b/qemu/roms/u-boot/common/cmd_reginfo.c @@ -0,0 +1,245 @@ +/* + * (C) Copyright 2000 + * Subodh Nijsure, SkyStream Networks, snijsure@skystream.com + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <command.h> +#if defined(CONFIG_8xx) +#include <mpc8xx.h> +#elif defined (CONFIG_4xx) +extern void ppc4xx_reginfo(void); +#elif defined (CONFIG_5xx) +#include <mpc5xx.h> +#elif defined (CONFIG_MPC5200) +#include <mpc5xxx.h> +#elif defined (CONFIG_MPC86xx) +extern void mpc86xx_reginfo(void); +#elif defined(CONFIG_MPC85xx) +extern void mpc85xx_reginfo(void); +#endif + +static int do_reginfo(cmd_tbl_t *cmdtp, int flag, int argc, + char * const argv[]) +{ +#if defined(CONFIG_8xx) + volatile immap_t *immap = (immap_t *)CONFIG_SYS_IMMR; + volatile memctl8xx_t *memctl = &immap->im_memctl; + volatile sysconf8xx_t *sysconf = &immap->im_siu_conf; + volatile sit8xx_t *timers = &immap->im_sit; + + /* Hopefully more PowerPC knowledgable people will add code to display + * other useful registers + */ + + printf ("\nSystem Configuration registers\n" + + "\tIMMR\t0x%08X\n", get_immr(0)); + + printf("\tSIUMCR\t0x%08X", sysconf->sc_siumcr); + printf("\tSYPCR\t0x%08X\n",sysconf->sc_sypcr); + + printf("\tSWT\t0x%08X", sysconf->sc_swt); + printf("\tSWSR\t0x%04X\n", sysconf->sc_swsr); + + printf("\tSIPEND\t0x%08X\tSIMASK\t0x%08X\n", + sysconf->sc_sipend, sysconf->sc_simask); + printf("\tSIEL\t0x%08X\tSIVEC\t0x%08X\n", + sysconf->sc_siel, sysconf->sc_sivec); + printf("\tTESR\t0x%08X\tSDCR\t0x%08X\n", + sysconf->sc_tesr, sysconf->sc_sdcr); + + printf ("Memory Controller Registers\n" + + "\tBR0\t0x%08X\tOR0\t0x%08X \n", memctl->memc_br0, memctl->memc_or0); + printf("\tBR1\t0x%08X\tOR1\t0x%08X \n", memctl->memc_br1, memctl->memc_or1); + printf("\tBR2\t0x%08X\tOR2\t0x%08X \n", memctl->memc_br2, memctl->memc_or2); + printf("\tBR3\t0x%08X\tOR3\t0x%08X \n", memctl->memc_br3, memctl->memc_or3); + printf("\tBR4\t0x%08X\tOR4\t0x%08X \n", memctl->memc_br4, memctl->memc_or4); + printf("\tBR5\t0x%08X\tOR5\t0x%08X \n", memctl->memc_br5, memctl->memc_or5); + printf("\tBR6\t0x%08X\tOR6\t0x%08X \n", memctl->memc_br6, memctl->memc_or6); + printf("\tBR7\t0x%08X\tOR7\t0x%08X \n", memctl->memc_br7, memctl->memc_or7); + printf ("\n" + "\tmamr\t0x%08X\tmbmr\t0x%08X \n", + memctl->memc_mamr, memctl->memc_mbmr ); + printf("\tmstat\t0x%08X\tmptpr\t0x%08X \n", + memctl->memc_mstat, memctl->memc_mptpr ); + printf("\tmdr\t0x%08X \n", memctl->memc_mdr); + + printf ("\nSystem Integration Timers\n" + "\tTBSCR\t0x%08X\tRTCSC\t0x%08X \n", + timers->sit_tbscr, timers->sit_rtcsc); + printf("\tPISCR\t0x%08X \n", timers->sit_piscr); + + /* + * May be some CPM info here? + */ + +#elif defined (CONFIG_4xx) + ppc4xx_reginfo(); +#elif defined(CONFIG_5xx) + + volatile immap_t *immap = (immap_t *)CONFIG_SYS_IMMR; + volatile memctl5xx_t *memctl = &immap->im_memctl; + volatile sysconf5xx_t *sysconf = &immap->im_siu_conf; + volatile sit5xx_t *timers = &immap->im_sit; + volatile car5xx_t *car = &immap->im_clkrst; + volatile uimb5xx_t *uimb = &immap->im_uimb; + + puts ("\nSystem Configuration registers\n"); + printf("\tIMMR\t0x%08X\tSIUMCR\t0x%08X \n", get_immr(0), sysconf->sc_siumcr); + printf("\tSYPCR\t0x%08X\tSWSR\t0x%04X \n" ,sysconf->sc_sypcr, sysconf->sc_swsr); + printf("\tSIPEND\t0x%08X\tSIMASK\t0x%08X \n", sysconf->sc_sipend, sysconf->sc_simask); + printf("\tSIEL\t0x%08X\tSIVEC\t0x%08X \n", sysconf->sc_siel, sysconf->sc_sivec); + printf("\tTESR\t0x%08X\n", sysconf->sc_tesr); + + puts ("\nMemory Controller Registers\n"); + printf("\tBR0\t0x%08X\tOR0\t0x%08X \n", memctl->memc_br0, memctl->memc_or0); + printf("\tBR1\t0x%08X\tOR1\t0x%08X \n", memctl->memc_br1, memctl->memc_or1); + printf("\tBR2\t0x%08X\tOR2\t0x%08X \n", memctl->memc_br2, memctl->memc_or2); + printf("\tBR3\t0x%08X\tOR3\t0x%08X \n", memctl->memc_br3, memctl->memc_or3); + printf("\tDMBR\t0x%08X\tDMOR\t0x%08X \n", memctl->memc_dmbr, memctl->memc_dmor ); + printf("\tMSTAT\t0x%08X\n", memctl->memc_mstat); + + puts ("\nSystem Integration Timers\n"); + printf("\tTBSCR\t0x%08X\tRTCSC\t0x%08X \n", timers->sit_tbscr, timers->sit_rtcsc); + printf("\tPISCR\t0x%08X \n", timers->sit_piscr); + + puts ("\nClocks and Reset\n"); + printf("\tSCCR\t0x%08X\tPLPRCR\t0x%08X \n", car->car_sccr, car->car_plprcr); + + puts ("\nU-Bus to IMB3 Bus Interface\n"); + printf("\tUMCR\t0x%08X\tUIPEND\t0x%08X \n", uimb->uimb_umcr, uimb->uimb_uipend); + puts ("\n\n"); + +#elif defined(CONFIG_MPC5200) + puts ("\nMPC5200 registers\n"); + printf ("MBAR=%08x\n", CONFIG_SYS_MBAR); + puts ("Memory map registers\n"); + printf ("\tCS0: start %08lX\tstop %08lX\tconfig %08lX\ten %d\n", + *(volatile ulong*)MPC5XXX_CS0_START, + *(volatile ulong*)MPC5XXX_CS0_STOP, + *(volatile ulong*)MPC5XXX_CS0_CFG, + (*(volatile ulong*)MPC5XXX_ADDECR & 0x00010000) ? 1 : 0); + printf ("\tCS1: start %08lX\tstop %08lX\tconfig %08lX\ten %d\n", + *(volatile ulong*)MPC5XXX_CS1_START, + *(volatile ulong*)MPC5XXX_CS1_STOP, + *(volatile ulong*)MPC5XXX_CS1_CFG, + (*(volatile ulong*)MPC5XXX_ADDECR & 0x00020000) ? 1 : 0); + printf ("\tCS2: start %08lX\tstop %08lX\tconfig %08lX\ten %d\n", + *(volatile ulong*)MPC5XXX_CS2_START, + *(volatile ulong*)MPC5XXX_CS2_STOP, + *(volatile ulong*)MPC5XXX_CS2_CFG, + (*(volatile ulong*)MPC5XXX_ADDECR & 0x00040000) ? 1 : 0); + printf ("\tCS3: start %08lX\tstop %08lX\tconfig %08lX\ten %d\n", + *(volatile ulong*)MPC5XXX_CS3_START, + *(volatile ulong*)MPC5XXX_CS3_STOP, + *(volatile ulong*)MPC5XXX_CS3_CFG, + (*(volatile ulong*)MPC5XXX_ADDECR & 0x00080000) ? 1 : 0); + printf ("\tCS4: start %08lX\tstop %08lX\tconfig %08lX\ten %d\n", + *(volatile ulong*)MPC5XXX_CS4_START, + *(volatile ulong*)MPC5XXX_CS4_STOP, + *(volatile ulong*)MPC5XXX_CS4_CFG, + (*(volatile ulong*)MPC5XXX_ADDECR & 0x00100000) ? 1 : 0); + printf ("\tCS5: start %08lX\tstop %08lX\tconfig %08lX\ten %d\n", + *(volatile ulong*)MPC5XXX_CS5_START, + *(volatile ulong*)MPC5XXX_CS5_STOP, + *(volatile ulong*)MPC5XXX_CS5_CFG, + (*(volatile ulong*)MPC5XXX_ADDECR & 0x00200000) ? 1 : 0); + printf ("\tCS6: start %08lX\tstop %08lX\tconfig %08lX\ten %d\n", + *(volatile ulong*)MPC5XXX_CS6_START, + *(volatile ulong*)MPC5XXX_CS6_STOP, + *(volatile ulong*)MPC5XXX_CS6_CFG, + (*(volatile ulong*)MPC5XXX_ADDECR & 0x04000000) ? 1 : 0); + printf ("\tCS7: start %08lX\tstop %08lX\tconfig %08lX\ten %d\n", + *(volatile ulong*)MPC5XXX_CS7_START, + *(volatile ulong*)MPC5XXX_CS7_STOP, + *(volatile ulong*)MPC5XXX_CS7_CFG, + (*(volatile ulong*)MPC5XXX_ADDECR & 0x08000000) ? 1 : 0); + printf ("\tBOOTCS: start %08lX\tstop %08lX\tconfig %08lX\ten %d\n", + *(volatile ulong*)MPC5XXX_BOOTCS_START, + *(volatile ulong*)MPC5XXX_BOOTCS_STOP, + *(volatile ulong*)MPC5XXX_BOOTCS_CFG, + (*(volatile ulong*)MPC5XXX_ADDECR & 0x02000000) ? 1 : 0); + printf ("\tSDRAMCS0: %08lX\n", + *(volatile ulong*)MPC5XXX_SDRAM_CS0CFG); + printf ("\tSDRAMCS1: %08lX\n", + *(volatile ulong*)MPC5XXX_SDRAM_CS1CFG); +#elif defined(CONFIG_MPC86xx) + mpc86xx_reginfo(); + +#elif defined(CONFIG_MPC85xx) + mpc85xx_reginfo(); + +#elif defined(CONFIG_BLACKFIN) + puts("\nSystem Configuration registers\n"); +#ifndef __ADSPBF60x__ + puts("\nPLL Registers\n"); + printf("\tPLL_DIV: 0x%04x PLL_CTL: 0x%04x\n", + bfin_read_PLL_DIV(), bfin_read_PLL_CTL()); + printf("\tPLL_STAT: 0x%04x PLL_LOCKCNT: 0x%04x\n", + bfin_read_PLL_STAT(), bfin_read_PLL_LOCKCNT()); + printf("\tVR_CTL: 0x%04x\n", bfin_read_VR_CTL()); + + puts("\nEBIU AMC Registers\n"); + printf("\tEBIU_AMGCTL: 0x%04x\n", bfin_read_EBIU_AMGCTL()); + printf("\tEBIU_AMBCTL0: 0x%08x EBIU_AMBCTL1: 0x%08x\n", + bfin_read_EBIU_AMBCTL0(), bfin_read_EBIU_AMBCTL1()); +# ifdef EBIU_MODE + printf("\tEBIU_MBSCTL: 0x%08x EBIU_ARBSTAT: 0x%08x\n", + bfin_read_EBIU_MBSCTL(), bfin_read_EBIU_ARBSTAT()); + printf("\tEBIU_MODE: 0x%08x EBIU_FCTL: 0x%08x\n", + bfin_read_EBIU_MODE(), bfin_read_EBIU_FCTL()); +# endif + +# ifdef EBIU_RSTCTL + puts("\nEBIU DDR Registers\n"); + printf("\tEBIU_DDRCTL0: 0x%08x EBIU_DDRCTL1: 0x%08x\n", + bfin_read_EBIU_DDRCTL0(), bfin_read_EBIU_DDRCTL1()); + printf("\tEBIU_DDRCTL2: 0x%08x EBIU_DDRCTL3: 0x%08x\n", + bfin_read_EBIU_DDRCTL2(), bfin_read_EBIU_DDRCTL3()); + printf("\tEBIU_DDRQUE: 0x%08x EBIU_RSTCTL 0x%04x\n", + bfin_read_EBIU_DDRQUE(), bfin_read_EBIU_RSTCTL()); + printf("\tEBIU_ERRADD: 0x%08x EBIU_ERRMST: 0x%04x\n", + bfin_read_EBIU_ERRADD(), bfin_read_EBIU_ERRMST()); +# else + puts("\nEBIU SDC Registers\n"); + printf("\tEBIU_SDRRC: 0x%04x EBIU_SDBCTL: 0x%04x\n", + bfin_read_EBIU_SDRRC(), bfin_read_EBIU_SDBCTL()); + printf("\tEBIU_SDSTAT: 0x%04x EBIU_SDGCTL: 0x%08x\n", + bfin_read_EBIU_SDSTAT(), bfin_read_EBIU_SDGCTL()); +# endif +#else + puts("\nCGU Registers\n"); + printf("\tCGU_DIV: 0x%08x CGU_CTL: 0x%08x\n", + bfin_read_CGU_DIV(), bfin_read_CGU_CTL()); + printf("\tCGU_STAT: 0x%08x CGU_LOCKCNT: 0x%08x\n", + bfin_read_CGU_STAT(), bfin_read_CGU_CLKOUTSEL()); + + puts("\nSMC DDR Registers\n"); + printf("\tDDR_CFG: 0x%08x DDR_TR0: 0x%08x\n", + bfin_read_DMC0_CFG(), bfin_read_DMC0_TR0()); + printf("\tDDR_TR1: 0x%08x DDR_TR2: 0x%08x\n", + bfin_read_DMC0_TR1(), bfin_read_DMC0_TR2()); + printf("\tDDR_MR: 0x%08x DDR_EMR1: 0x%08x\n", + bfin_read_DMC0_MR(), bfin_read_DMC0_EMR1()); + printf("\tDDR_CTL: 0x%08x DDR_STAT: 0x%08x\n", + bfin_read_DMC0_CTL(), bfin_read_DMC0_STAT()); + printf("\tDDR_DLLCTL:0x%08x\n", bfin_read_DMC0_DLLCTL()); +#endif +#endif /* CONFIG_BLACKFIN */ + + return 0; +} + + /**************************************************/ + +#if defined(CONFIG_CMD_REGINFO) +U_BOOT_CMD( + reginfo, 2, 1, do_reginfo, + "print register information", + "" +); +#endif diff --git a/qemu/roms/u-boot/common/cmd_reiser.c b/qemu/roms/u-boot/common/cmd_reiser.c new file mode 100644 index 000000000..887156486 --- /dev/null +++ b/qemu/roms/u-boot/common/cmd_reiser.c @@ -0,0 +1,171 @@ +/* + * (C) Copyright 2003 - 2004 + * Sysgo Real-Time Solutions, AG <www.elinos.com> + * Pavel Bartusek <pba@sysgo.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/* + * Reiserfs support + */ +#include <common.h> +#include <config.h> +#include <command.h> +#include <image.h> +#include <linux/ctype.h> +#include <asm/byteorder.h> +#include <reiserfs.h> +#include <part.h> + +#ifndef CONFIG_DOS_PARTITION +#error DOS partition support must be selected +#endif + +/* #define REISER_DEBUG */ + +#ifdef REISER_DEBUG +#define PRINTF(fmt,args...) printf (fmt ,##args) +#else +#define PRINTF(fmt,args...) +#endif + +int do_reiserls (cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + char *filename = "/"; + int dev, part; + block_dev_desc_t *dev_desc=NULL; + disk_partition_t info; + + if (argc < 3) + return CMD_RET_USAGE; + + part = get_device_and_partition(argv[1], argv[2], &dev_desc, &info, 1); + if (part < 0) + return 1; + + if (argc == 4) { + filename = argv[3]; + } + + dev = dev_desc->dev; + PRINTF("Using device %s %d:%d, directory: %s\n", argv[1], dev, part, filename); + + reiserfs_set_blk_dev(dev_desc, &info); + + if (!reiserfs_mount(info.size)) { + printf ("** Bad Reiserfs partition or disk - %s %d:%d **\n", argv[1], dev, part); + return 1; + } + + if (reiserfs_ls (filename)) { + printf ("** Error reiserfs_ls() **\n"); + return 1; + }; + + return 0; +} + +U_BOOT_CMD( + reiserls, 4, 1, do_reiserls, + "list files in a directory (default /)", + "<interface> <dev[:part]> [directory]\n" + " - list files from 'dev' on 'interface' in a 'directory'" +); + +/****************************************************************************** + * Reiserfs boot command intepreter. Derived from diskboot + */ +int do_reiserload (cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + char *filename = NULL; + int dev, part; + ulong addr = 0, filelen; + disk_partition_t info; + block_dev_desc_t *dev_desc = NULL; + unsigned long count; + char *addr_str; + + switch (argc) { + case 3: + addr_str = getenv("loadaddr"); + if (addr_str != NULL) { + addr = simple_strtoul (addr_str, NULL, 16); + } else { + addr = CONFIG_SYS_LOAD_ADDR; + } + filename = getenv ("bootfile"); + count = 0; + break; + case 4: + addr = simple_strtoul (argv[3], NULL, 16); + filename = getenv ("bootfile"); + count = 0; + break; + case 5: + addr = simple_strtoul (argv[3], NULL, 16); + filename = argv[4]; + count = 0; + break; + case 6: + addr = simple_strtoul (argv[3], NULL, 16); + filename = argv[4]; + count = simple_strtoul (argv[5], NULL, 16); + break; + + default: + return CMD_RET_USAGE; + } + + if (!filename) { + puts ("\n** No boot file defined **\n"); + return 1; + } + + part = get_device_and_partition(argv[1], argv[2], &dev_desc, &info, 1); + if (part < 0) + return 1; + + dev = dev_desc->dev; + + printf("Loading file \"%s\" from %s device %d%c%c\n", + filename, argv[1], dev, + part ? ':' : ' ', part ? part + '0' : ' '); + + reiserfs_set_blk_dev(dev_desc, &info); + + if (!reiserfs_mount(info.size)) { + printf ("** Bad Reiserfs partition or disk - %s %d:%d **\n", argv[1], dev, part); + return 1; + } + + filelen = reiserfs_open(filename); + if (filelen < 0) { + printf("** File not found %s **\n", filename); + return 1; + } + if ((count < filelen) && (count != 0)) { + filelen = count; + } + + if (reiserfs_read((char *)addr, filelen) != filelen) { + printf("\n** Unable to read \"%s\" from %s %d:%d **\n", filename, argv[1], dev, part); + return 1; + } + + /* Loading ok, update default load address */ + load_addr = addr; + + printf ("\n%ld bytes read\n", filelen); + setenv_hex("filesize", filelen); + + return filelen; +} + +U_BOOT_CMD( + reiserload, 6, 0, do_reiserload, + "load binary file from a Reiser filesystem", + "<interface> <dev[:part]> [addr] [filename] [bytes]\n" + " - load binary file 'filename' from 'dev' on 'interface'\n" + " to address 'addr' from dos filesystem" +); diff --git a/qemu/roms/u-boot/common/cmd_sandbox.c b/qemu/roms/u-boot/common/cmd_sandbox.c new file mode 100644 index 000000000..00982b164 --- /dev/null +++ b/qemu/roms/u-boot/common/cmd_sandbox.c @@ -0,0 +1,124 @@ +/* + * Copyright (c) 2012, Google Inc. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <fs.h> +#include <part.h> +#include <sandboxblockdev.h> +#include <asm/errno.h> + +static int do_sandbox_load(cmd_tbl_t *cmdtp, int flag, int argc, + char * const argv[]) +{ + return do_load(cmdtp, flag, argc, argv, FS_TYPE_SANDBOX); +} + +static int do_sandbox_ls(cmd_tbl_t *cmdtp, int flag, int argc, + char * const argv[]) +{ + return do_ls(cmdtp, flag, argc, argv, FS_TYPE_SANDBOX); +} + +static int do_sandbox_save(cmd_tbl_t *cmdtp, int flag, int argc, + char * const argv[]) +{ + return do_save(cmdtp, flag, argc, argv, FS_TYPE_SANDBOX); +} + +static int do_sandbox_bind(cmd_tbl_t *cmdtp, int flag, int argc, + char * const argv[]) +{ + if (argc < 2 || argc > 3) + return CMD_RET_USAGE; + char *ep; + char *dev_str = argv[1]; + char *file = argc >= 3 ? argv[2] : NULL; + int dev = simple_strtoul(dev_str, &ep, 16); + if (*ep) { + printf("** Bad device specification %s **\n", dev_str); + return CMD_RET_USAGE; + } + return host_dev_bind(dev, file); +} + +static int do_sandbox_info(cmd_tbl_t *cmdtp, int flag, int argc, + char * const argv[]) +{ + if (argc < 1 || argc > 2) + return CMD_RET_USAGE; + int min_dev = 0; + int max_dev = CONFIG_HOST_MAX_DEVICES - 1; + if (argc >= 2) { + char *ep; + char *dev_str = argv[1]; + int dev = simple_strtoul(dev_str, &ep, 16); + if (*ep) { + printf("** Bad device specification %s **\n", dev_str); + return CMD_RET_USAGE; + } + min_dev = dev; + max_dev = dev; + } + int dev; + printf("%3s %12s %s\n", "dev", "blocks", "path"); + for (dev = min_dev; dev <= max_dev; dev++) { + block_dev_desc_t *blk_dev; + int ret; + + printf("%3d ", dev); + ret = host_get_dev_err(dev, &blk_dev); + if (ret) { + if (ret == -ENOENT) + puts("Not bound to a backing file\n"); + else if (ret == -ENODEV) + puts("Invalid host device number\n"); + + continue; + } + struct host_block_dev *host_dev = blk_dev->priv; + printf("%12lu %s\n", (unsigned long)blk_dev->lba, + host_dev->filename); + } + return 0; +} + +static cmd_tbl_t cmd_sandbox_sub[] = { + U_BOOT_CMD_MKENT(load, 7, 0, do_sandbox_load, "", ""), + U_BOOT_CMD_MKENT(ls, 3, 0, do_sandbox_ls, "", ""), + U_BOOT_CMD_MKENT(save, 6, 0, do_sandbox_save, "", ""), + U_BOOT_CMD_MKENT(bind, 3, 0, do_sandbox_bind, "", ""), + U_BOOT_CMD_MKENT(info, 3, 0, do_sandbox_info, "", ""), +}; + +static int do_sandbox(cmd_tbl_t *cmdtp, int flag, int argc, + char * const argv[]) +{ + cmd_tbl_t *c; + + /* Skip past 'sandbox' */ + argc--; + argv++; + + c = find_cmd_tbl(argv[0], cmd_sandbox_sub, + ARRAY_SIZE(cmd_sandbox_sub)); + + if (c) + return c->cmd(cmdtp, flag, argc, argv); + else + return CMD_RET_USAGE; +} + +U_BOOT_CMD( + sb, 8, 1, do_sandbox, + "Miscellaneous sandbox commands", + "load host <dev> <addr> <filename> [<bytes> <offset>] - " + "load a file from host\n" + "sb ls host <filename> - list files on host\n" + "sb save host <dev> <filename> <addr> <bytes> [<offset>] - " + "save a file to host\n" + "sb bind <dev> [<filename>] - bind \"host\" device to file\n" + "sb info [<dev>] - show device binding & info" +); diff --git a/qemu/roms/u-boot/common/cmd_sata.c b/qemu/roms/u-boot/common/cmd_sata.c new file mode 100644 index 000000000..fc9213196 --- /dev/null +++ b/qemu/roms/u-boot/common/cmd_sata.c @@ -0,0 +1,193 @@ +/* + * Copyright (C) 2000-2005, DENX Software Engineering + * Wolfgang Denk <wd@denx.de> + * Copyright (C) Procsys. All rights reserved. + * Mushtaq Khan <mushtaq_k@procsys.com> + * <mushtaqk_921@yahoo.co.in> + * Copyright (C) 2008 Freescale Semiconductor, Inc. + * Dave Liu <daveliu@freescale.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <command.h> +#include <part.h> +#include <sata.h> + +static int sata_curr_device = -1; +block_dev_desc_t sata_dev_desc[CONFIG_SYS_SATA_MAX_DEVICE]; + +int __sata_initialize(void) +{ + int rc; + int i; + + for (i = 0; i < CONFIG_SYS_SATA_MAX_DEVICE; i++) { + memset(&sata_dev_desc[i], 0, sizeof(struct block_dev_desc)); + sata_dev_desc[i].if_type = IF_TYPE_SATA; + sata_dev_desc[i].dev = i; + sata_dev_desc[i].part_type = PART_TYPE_UNKNOWN; + sata_dev_desc[i].type = DEV_TYPE_HARDDISK; + sata_dev_desc[i].lba = 0; + sata_dev_desc[i].blksz = 512; + sata_dev_desc[i].log2blksz = LOG2(sata_dev_desc[i].blksz); + sata_dev_desc[i].block_read = sata_read; + sata_dev_desc[i].block_write = sata_write; + + rc = init_sata(i); + if (!rc) { + rc = scan_sata(i); + if (!rc && (sata_dev_desc[i].lba > 0) && + (sata_dev_desc[i].blksz > 0)) + init_part(&sata_dev_desc[i]); + } + } + sata_curr_device = 0; + return rc; +} +int sata_initialize(void) __attribute__((weak,alias("__sata_initialize"))); + +#ifdef CONFIG_PARTITIONS +block_dev_desc_t *sata_get_dev(int dev) +{ + return (dev < CONFIG_SYS_SATA_MAX_DEVICE) ? &sata_dev_desc[dev] : NULL; +} +#endif + +static int do_sata(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + int rc = 0; + + if (argc == 2 && strcmp(argv[1], "init") == 0) + return sata_initialize(); + + /* If the user has not yet run `sata init`, do it now */ + if (sata_curr_device == -1) + if (sata_initialize()) + return 1; + + switch (argc) { + case 0: + case 1: + return CMD_RET_USAGE; + case 2: + if (strncmp(argv[1],"inf", 3) == 0) { + int i; + putc('\n'); + for (i = 0; i < CONFIG_SYS_SATA_MAX_DEVICE; ++i) { + if (sata_dev_desc[i].type == DEV_TYPE_UNKNOWN) + continue; + printf ("SATA device %d: ", i); + dev_print(&sata_dev_desc[i]); + } + return 0; + } else if (strncmp(argv[1],"dev", 3) == 0) { + if ((sata_curr_device < 0) || (sata_curr_device >= CONFIG_SYS_SATA_MAX_DEVICE)) { + puts("\nno SATA devices available\n"); + return 1; + } + printf("\nSATA device %d: ", sata_curr_device); + dev_print(&sata_dev_desc[sata_curr_device]); + return 0; + } else if (strncmp(argv[1],"part",4) == 0) { + int dev, ok; + + for (ok = 0, dev = 0; dev < CONFIG_SYS_SATA_MAX_DEVICE; ++dev) { + if (sata_dev_desc[dev].part_type != PART_TYPE_UNKNOWN) { + ++ok; + if (dev) + putc ('\n'); + print_part(&sata_dev_desc[dev]); + } + } + if (!ok) { + puts("\nno SATA devices available\n"); + rc ++; + } + return rc; + } + return CMD_RET_USAGE; + case 3: + if (strncmp(argv[1], "dev", 3) == 0) { + int dev = (int)simple_strtoul(argv[2], NULL, 10); + + printf("\nSATA device %d: ", dev); + if (dev >= CONFIG_SYS_SATA_MAX_DEVICE) { + puts ("unknown device\n"); + return 1; + } + dev_print(&sata_dev_desc[dev]); + + if (sata_dev_desc[dev].type == DEV_TYPE_UNKNOWN) + return 1; + + sata_curr_device = dev; + + puts("... is now current device\n"); + + return 0; + } else if (strncmp(argv[1], "part", 4) == 0) { + int dev = (int)simple_strtoul(argv[2], NULL, 10); + + if (sata_dev_desc[dev].part_type != PART_TYPE_UNKNOWN) { + print_part(&sata_dev_desc[dev]); + } else { + printf("\nSATA device %d not available\n", dev); + rc = 1; + } + return rc; + } + return CMD_RET_USAGE; + + default: /* at least 4 args */ + if (strcmp(argv[1], "read") == 0) { + ulong addr = simple_strtoul(argv[2], NULL, 16); + ulong cnt = simple_strtoul(argv[4], NULL, 16); + ulong n; + lbaint_t blk = simple_strtoul(argv[3], NULL, 16); + + printf("\nSATA read: device %d block # %ld, count %ld ... ", + sata_curr_device, blk, cnt); + + n = sata_read(sata_curr_device, blk, cnt, (u32 *)addr); + + /* flush cache after read */ + flush_cache(addr, cnt * sata_dev_desc[sata_curr_device].blksz); + + printf("%ld blocks read: %s\n", + n, (n==cnt) ? "OK" : "ERROR"); + return (n == cnt) ? 0 : 1; + } else if (strcmp(argv[1], "write") == 0) { + ulong addr = simple_strtoul(argv[2], NULL, 16); + ulong cnt = simple_strtoul(argv[4], NULL, 16); + ulong n; + + lbaint_t blk = simple_strtoul(argv[3], NULL, 16); + + printf("\nSATA write: device %d block # %ld, count %ld ... ", + sata_curr_device, blk, cnt); + + n = sata_write(sata_curr_device, blk, cnt, (u32 *)addr); + + printf("%ld blocks written: %s\n", + n, (n == cnt) ? "OK" : "ERROR"); + return (n == cnt) ? 0 : 1; + } else { + return CMD_RET_USAGE; + } + + return rc; + } +} + +U_BOOT_CMD( + sata, 5, 1, do_sata, + "SATA sub system", + "init - init SATA sub system\n" + "sata info - show available SATA devices\n" + "sata device [dev] - show or set current device\n" + "sata part [dev] - print partition table\n" + "sata read addr blk# cnt\n" + "sata write addr blk# cnt" +); diff --git a/qemu/roms/u-boot/common/cmd_scsi.c b/qemu/roms/u-boot/common/cmd_scsi.c new file mode 100644 index 000000000..b3f7687ae --- /dev/null +++ b/qemu/roms/u-boot/common/cmd_scsi.c @@ -0,0 +1,670 @@ +/* + * (C) Copyright 2001 + * Denis Peter, MPL AG Switzerland + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/* + * SCSI support. + */ +#include <common.h> +#include <command.h> +#include <asm/processor.h> +#include <scsi.h> +#include <image.h> +#include <pci.h> + +#ifdef CONFIG_SCSI_DEV_LIST +#define SCSI_DEV_LIST CONFIG_SCSI_DEV_LIST +#else +#ifdef CONFIG_SCSI_SYM53C8XX +#define SCSI_VEND_ID 0x1000 +#ifndef CONFIG_SCSI_DEV_ID +#define SCSI_DEV_ID 0x0001 +#else +#define SCSI_DEV_ID CONFIG_SCSI_DEV_ID +#endif +#elif defined CONFIG_SATA_ULI5288 + +#define SCSI_VEND_ID 0x10b9 +#define SCSI_DEV_ID 0x5288 + +#elif !defined(CONFIG_SCSI_AHCI_PLAT) +#error no scsi device defined +#endif +#define SCSI_DEV_LIST {SCSI_VEND_ID, SCSI_DEV_ID} +#endif + +#ifdef CONFIG_PCI +const struct pci_device_id scsi_device_list[] = { SCSI_DEV_LIST }; +#endif +static ccb tempccb; /* temporary scsi command buffer */ + +static unsigned char tempbuff[512]; /* temporary data buffer */ + +static int scsi_max_devs; /* number of highest available scsi device */ + +static int scsi_curr_dev; /* current device */ + +static block_dev_desc_t scsi_dev_desc[CONFIG_SYS_SCSI_MAX_DEVICE]; + +/******************************************************************************** + * forward declerations of some Setup Routines + */ +void scsi_setup_test_unit_ready(ccb * pccb); +void scsi_setup_read6(ccb * pccb, unsigned long start, unsigned short blocks); +void scsi_setup_read_ext(ccb * pccb, unsigned long start, unsigned short blocks); +static void scsi_setup_write_ext(ccb *pccb, unsigned long start, + unsigned short blocks); +void scsi_setup_inquiry(ccb * pccb); +void scsi_ident_cpy (unsigned char *dest, unsigned char *src, unsigned int len); + + +static int scsi_read_capacity(ccb *pccb, lbaint_t *capacity, + unsigned long *blksz); +static ulong scsi_read(int device, lbaint_t blknr, lbaint_t blkcnt, + void *buffer); +static ulong scsi_write(int device, lbaint_t blknr, + lbaint_t blkcnt, const void *buffer); + + +/********************************************************************************* + * (re)-scan the scsi bus and reports scsi device info + * to the user if mode = 1 + */ +void scsi_scan(int mode) +{ + unsigned char i,perq,modi,lun; + lbaint_t capacity; + unsigned long blksz; + ccb* pccb=(ccb *)&tempccb; + + if(mode==1) { + printf("scanning bus for devices...\n"); + } + for(i=0;i<CONFIG_SYS_SCSI_MAX_DEVICE;i++) { + scsi_dev_desc[i].target=0xff; + scsi_dev_desc[i].lun=0xff; + scsi_dev_desc[i].lba=0; + scsi_dev_desc[i].blksz=0; + scsi_dev_desc[i].log2blksz = + LOG2_INVALID(typeof(scsi_dev_desc[i].log2blksz)); + scsi_dev_desc[i].type=DEV_TYPE_UNKNOWN; + scsi_dev_desc[i].vendor[0]=0; + scsi_dev_desc[i].product[0]=0; + scsi_dev_desc[i].revision[0]=0; + scsi_dev_desc[i].removable = false; + scsi_dev_desc[i].if_type=IF_TYPE_SCSI; + scsi_dev_desc[i].dev=i; + scsi_dev_desc[i].part_type=PART_TYPE_UNKNOWN; + scsi_dev_desc[i].block_read=scsi_read; + scsi_dev_desc[i].block_write = scsi_write; + } + scsi_max_devs=0; + for(i=0;i<CONFIG_SYS_SCSI_MAX_SCSI_ID;i++) { + pccb->target=i; + for(lun=0;lun<CONFIG_SYS_SCSI_MAX_LUN;lun++) { + pccb->lun=lun; + pccb->pdata=(unsigned char *)&tempbuff; + pccb->datalen=512; + scsi_setup_inquiry(pccb); + if (scsi_exec(pccb) != true) { + if(pccb->contr_stat==SCSI_SEL_TIME_OUT) { + debug ("Selection timeout ID %d\n",pccb->target); + continue; /* selection timeout => assuming no device present */ + } + scsi_print_error(pccb); + continue; + } + perq=tempbuff[0]; + modi=tempbuff[1]; + if((perq & 0x1f)==0x1f) { + continue; /* skip unknown devices */ + } + if((modi&0x80)==0x80) /* drive is removable */ + scsi_dev_desc[scsi_max_devs].removable=true; + /* get info for this device */ + scsi_ident_cpy((unsigned char *)&scsi_dev_desc[scsi_max_devs].vendor[0], + &tempbuff[8], 8); + scsi_ident_cpy((unsigned char *)&scsi_dev_desc[scsi_max_devs].product[0], + &tempbuff[16], 16); + scsi_ident_cpy((unsigned char *)&scsi_dev_desc[scsi_max_devs].revision[0], + &tempbuff[32], 4); + scsi_dev_desc[scsi_max_devs].target=pccb->target; + scsi_dev_desc[scsi_max_devs].lun=pccb->lun; + + pccb->datalen=0; + scsi_setup_test_unit_ready(pccb); + if (scsi_exec(pccb) != true) { + if (scsi_dev_desc[scsi_max_devs].removable == true) { + scsi_dev_desc[scsi_max_devs].type=perq; + goto removable; + } + scsi_print_error(pccb); + continue; + } + if (scsi_read_capacity(pccb, &capacity, &blksz)) { + scsi_print_error(pccb); + continue; + } + scsi_dev_desc[scsi_max_devs].lba=capacity; + scsi_dev_desc[scsi_max_devs].blksz=blksz; + scsi_dev_desc[scsi_max_devs].log2blksz = + LOG2(scsi_dev_desc[scsi_max_devs].blksz); + scsi_dev_desc[scsi_max_devs].type=perq; + init_part(&scsi_dev_desc[scsi_max_devs]); +removable: + if(mode==1) { + printf (" Device %d: ", scsi_max_devs); + dev_print(&scsi_dev_desc[scsi_max_devs]); + } /* if mode */ + scsi_max_devs++; + } /* next LUN */ + } + if(scsi_max_devs>0) + scsi_curr_dev=0; + else + scsi_curr_dev = -1; + + printf("Found %d device(s).\n", scsi_max_devs); +#ifndef CONFIG_SPL_BUILD + setenv_ulong("scsidevs", scsi_max_devs); +#endif +} + +int scsi_get_disk_count(void) +{ + return scsi_max_devs; +} + +#ifdef CONFIG_PCI +void scsi_init(void) +{ + int busdevfunc; + int i; + /* + * Find a device from the list, this driver will support a single + * controller. + */ + for (i = 0; i < ARRAY_SIZE(scsi_device_list); i++) { + /* get PCI Device ID */ + busdevfunc = pci_find_device(scsi_device_list[i].vendor, + scsi_device_list[i].device, + 0); + if (busdevfunc != -1) + break; + } + + if (busdevfunc == -1) { + printf("Error: SCSI Controller(s) "); + for (i = 0; i < ARRAY_SIZE(scsi_device_list); i++) { + printf("%04X:%04X ", + scsi_device_list[i].vendor, + scsi_device_list[i].device); + } + printf("not found\n"); + return; + } +#ifdef DEBUG + else { + printf("SCSI Controller (%04X,%04X) found (%d:%d:%d)\n", + scsi_device_list[i].vendor, + scsi_device_list[i].device, + (busdevfunc >> 16) & 0xFF, + (busdevfunc >> 11) & 0x1F, + (busdevfunc >> 8) & 0x7); + } +#endif + scsi_low_level_init(busdevfunc); + scsi_scan(1); +} +#endif + +#ifdef CONFIG_PARTITIONS +block_dev_desc_t * scsi_get_dev(int dev) +{ + return (dev < CONFIG_SYS_SCSI_MAX_DEVICE) ? &scsi_dev_desc[dev] : NULL; +} +#endif + +/****************************************************************************** + * scsi boot command intepreter. Derived from diskboot + */ +int do_scsiboot (cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + return common_diskboot(cmdtp, "scsi", argc, argv); +} + +/********************************************************************************* + * scsi command intepreter + */ +int do_scsi (cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + switch (argc) { + case 0: + case 1: + return CMD_RET_USAGE; + + case 2: + if (strncmp(argv[1],"res",3) == 0) { + printf("\nReset SCSI\n"); + scsi_bus_reset(); + scsi_scan(1); + return 0; + } + if (strncmp(argv[1],"inf",3) == 0) { + int i; + for (i=0; i<CONFIG_SYS_SCSI_MAX_DEVICE; ++i) { + if(scsi_dev_desc[i].type==DEV_TYPE_UNKNOWN) + continue; /* list only known devices */ + printf ("SCSI dev. %d: ", i); + dev_print(&scsi_dev_desc[i]); + } + return 0; + } + if (strncmp(argv[1],"dev",3) == 0) { + if ((scsi_curr_dev < 0) || (scsi_curr_dev >= CONFIG_SYS_SCSI_MAX_DEVICE)) { + printf("\nno SCSI devices available\n"); + return 1; + } + printf ("\n Device %d: ", scsi_curr_dev); + dev_print(&scsi_dev_desc[scsi_curr_dev]); + return 0; + } + if (strncmp(argv[1],"scan",4) == 0) { + scsi_scan(1); + return 0; + } + if (strncmp(argv[1],"part",4) == 0) { + int dev, ok; + for (ok=0, dev=0; dev<CONFIG_SYS_SCSI_MAX_DEVICE; ++dev) { + if (scsi_dev_desc[dev].type!=DEV_TYPE_UNKNOWN) { + ok++; + if (dev) + printf("\n"); + debug ("print_part of %x\n",dev); + print_part(&scsi_dev_desc[dev]); + } + } + if (!ok) + printf("\nno SCSI devices available\n"); + return 1; + } + return CMD_RET_USAGE; + case 3: + if (strncmp(argv[1],"dev",3) == 0) { + int dev = (int)simple_strtoul(argv[2], NULL, 10); + printf ("\nSCSI device %d: ", dev); + if (dev >= CONFIG_SYS_SCSI_MAX_DEVICE) { + printf("unknown device\n"); + return 1; + } + printf ("\n Device %d: ", dev); + dev_print(&scsi_dev_desc[dev]); + if(scsi_dev_desc[dev].type == DEV_TYPE_UNKNOWN) { + return 1; + } + scsi_curr_dev = dev; + printf("... is now current device\n"); + return 0; + } + if (strncmp(argv[1],"part",4) == 0) { + int dev = (int)simple_strtoul(argv[2], NULL, 10); + if(scsi_dev_desc[dev].type != DEV_TYPE_UNKNOWN) { + print_part(&scsi_dev_desc[dev]); + } + else { + printf ("\nSCSI device %d not available\n", dev); + } + return 1; + } + return CMD_RET_USAGE; + default: + /* at least 4 args */ + if (strcmp(argv[1],"read") == 0) { + ulong addr = simple_strtoul(argv[2], NULL, 16); + ulong blk = simple_strtoul(argv[3], NULL, 16); + ulong cnt = simple_strtoul(argv[4], NULL, 16); + ulong n; + printf ("\nSCSI read: device %d block # %ld, count %ld ... ", + scsi_curr_dev, blk, cnt); + n = scsi_read(scsi_curr_dev, blk, cnt, (ulong *)addr); + printf ("%ld blocks read: %s\n",n,(n==cnt) ? "OK" : "ERROR"); + return 0; + } else if (strcmp(argv[1], "write") == 0) { + ulong addr = simple_strtoul(argv[2], NULL, 16); + ulong blk = simple_strtoul(argv[3], NULL, 16); + ulong cnt = simple_strtoul(argv[4], NULL, 16); + ulong n; + printf("\nSCSI write: device %d block # %ld, " + "count %ld ... ", + scsi_curr_dev, blk, cnt); + n = scsi_write(scsi_curr_dev, blk, cnt, + (ulong *)addr); + printf("%ld blocks written: %s\n", n, + (n == cnt) ? "OK" : "ERROR"); + return 0; + } + } /* switch */ + return CMD_RET_USAGE; +} + +/**************************************************************************************** + * scsi_read + */ + +#define SCSI_MAX_READ_BLK 0xFFFF /* almost the maximum amount of the scsi_ext command.. */ + +static ulong scsi_read(int device, lbaint_t blknr, lbaint_t blkcnt, + void *buffer) +{ + lbaint_t start, blks; + uintptr_t buf_addr; + unsigned short smallblks; + ccb* pccb=(ccb *)&tempccb; + device&=0xff; + /* Setup device + */ + pccb->target=scsi_dev_desc[device].target; + pccb->lun=scsi_dev_desc[device].lun; + buf_addr=(unsigned long)buffer; + start=blknr; + blks=blkcnt; + debug("\nscsi_read: dev %d startblk " LBAF + ", blccnt " LBAF " buffer %lx\n", + device, start, blks, (unsigned long)buffer); + do { + pccb->pdata=(unsigned char *)buf_addr; + if(blks>SCSI_MAX_READ_BLK) { + pccb->datalen=scsi_dev_desc[device].blksz * SCSI_MAX_READ_BLK; + smallblks=SCSI_MAX_READ_BLK; + scsi_setup_read_ext(pccb,start,smallblks); + start+=SCSI_MAX_READ_BLK; + blks-=SCSI_MAX_READ_BLK; + } + else { + pccb->datalen=scsi_dev_desc[device].blksz * blks; + smallblks=(unsigned short) blks; + scsi_setup_read_ext(pccb,start,smallblks); + start+=blks; + blks=0; + } + debug("scsi_read_ext: startblk " LBAF + ", blccnt %x buffer %lx\n", + start, smallblks, buf_addr); + if (scsi_exec(pccb) != true) { + scsi_print_error(pccb); + blkcnt-=blks; + break; + } + buf_addr+=pccb->datalen; + } while(blks!=0); + debug("scsi_read_ext: end startblk " LBAF + ", blccnt %x buffer %lx\n", start, smallblks, buf_addr); + return(blkcnt); +} + +/******************************************************************************* + * scsi_write + */ + +/* Almost the maximum amount of the scsi_ext command.. */ +#define SCSI_MAX_WRITE_BLK 0xFFFF + +static ulong scsi_write(int device, lbaint_t blknr, + lbaint_t blkcnt, const void *buffer) +{ + lbaint_t start, blks; + uintptr_t buf_addr; + unsigned short smallblks; + ccb* pccb = (ccb *)&tempccb; + device &= 0xff; + /* Setup device + */ + pccb->target = scsi_dev_desc[device].target; + pccb->lun = scsi_dev_desc[device].lun; + buf_addr = (unsigned long)buffer; + start = blknr; + blks = blkcnt; + debug("\n%s: dev %d startblk " LBAF ", blccnt " LBAF " buffer %lx\n", + __func__, device, start, blks, (unsigned long)buffer); + do { + pccb->pdata = (unsigned char *)buf_addr; + if (blks > SCSI_MAX_WRITE_BLK) { + pccb->datalen = (scsi_dev_desc[device].blksz * + SCSI_MAX_WRITE_BLK); + smallblks = SCSI_MAX_WRITE_BLK; + scsi_setup_write_ext(pccb, start, smallblks); + start += SCSI_MAX_WRITE_BLK; + blks -= SCSI_MAX_WRITE_BLK; + } else { + pccb->datalen = scsi_dev_desc[device].blksz * blks; + smallblks = (unsigned short)blks; + scsi_setup_write_ext(pccb, start, smallblks); + start += blks; + blks = 0; + } + debug("%s: startblk " LBAF ", blccnt %x buffer %lx\n", + __func__, start, smallblks, buf_addr); + if (scsi_exec(pccb) != true) { + scsi_print_error(pccb); + blkcnt -= blks; + break; + } + buf_addr += pccb->datalen; + } while (blks != 0); + debug("%s: end startblk " LBAF ", blccnt %x buffer %lx\n", + __func__, start, smallblks, buf_addr); + return blkcnt; +} + +/* copy src to dest, skipping leading and trailing blanks + * and null terminate the string + */ +void scsi_ident_cpy (unsigned char *dest, unsigned char *src, unsigned int len) +{ + int start,end; + + start=0; + while(start<len) { + if(src[start]!=' ') + break; + start++; + } + end=len-1; + while(end>start) { + if(src[end]!=' ') + break; + end--; + } + for( ; start<=end; start++) { + *dest++=src[start]; + } + *dest='\0'; +} + + +/* Trim trailing blanks, and NUL-terminate string + */ +void scsi_trim_trail (unsigned char *str, unsigned int len) +{ + unsigned char *p = str + len - 1; + + while (len-- > 0) { + *p-- = '\0'; + if (*p != ' ') { + return; + } + } +} + +int scsi_read_capacity(ccb *pccb, lbaint_t *capacity, unsigned long *blksz) +{ + *capacity = 0; + + memset(pccb->cmd, 0, sizeof(pccb->cmd)); + pccb->cmd[0] = SCSI_RD_CAPAC10; + pccb->cmd[1] = pccb->lun << 5; + pccb->cmdlen = 10; + pccb->msgout[0] = SCSI_IDENTIFY; /* NOT USED */ + + pccb->datalen = 8; + if (scsi_exec(pccb) != true) + return 1; + + *capacity = ((lbaint_t)pccb->pdata[0] << 24) | + ((lbaint_t)pccb->pdata[1] << 16) | + ((lbaint_t)pccb->pdata[2] << 8) | + ((lbaint_t)pccb->pdata[3]); + + if (*capacity != 0xffffffff) { + /* Read capacity (10) was sufficient for this drive. */ + *blksz = ((unsigned long)pccb->pdata[4] << 24) | + ((unsigned long)pccb->pdata[5] << 16) | + ((unsigned long)pccb->pdata[6] << 8) | + ((unsigned long)pccb->pdata[7]); + return 0; + } + + /* Read capacity (10) was insufficient. Use read capacity (16). */ + + memset(pccb->cmd, 0, sizeof(pccb->cmd)); + pccb->cmd[0] = SCSI_RD_CAPAC16; + pccb->cmd[1] = 0x10; + pccb->cmdlen = 16; + pccb->msgout[0] = SCSI_IDENTIFY; /* NOT USED */ + + pccb->datalen = 16; + if (scsi_exec(pccb) != true) + return 1; + + *capacity = ((uint64_t)pccb->pdata[0] << 56) | + ((uint64_t)pccb->pdata[1] << 48) | + ((uint64_t)pccb->pdata[2] << 40) | + ((uint64_t)pccb->pdata[3] << 32) | + ((uint64_t)pccb->pdata[4] << 24) | + ((uint64_t)pccb->pdata[5] << 16) | + ((uint64_t)pccb->pdata[6] << 8) | + ((uint64_t)pccb->pdata[7]); + + *blksz = ((uint64_t)pccb->pdata[8] << 56) | + ((uint64_t)pccb->pdata[9] << 48) | + ((uint64_t)pccb->pdata[10] << 40) | + ((uint64_t)pccb->pdata[11] << 32) | + ((uint64_t)pccb->pdata[12] << 24) | + ((uint64_t)pccb->pdata[13] << 16) | + ((uint64_t)pccb->pdata[14] << 8) | + ((uint64_t)pccb->pdata[15]); + + return 0; +} + + +/************************************************************************************ + * Some setup (fill-in) routines + */ +void scsi_setup_test_unit_ready(ccb * pccb) +{ + pccb->cmd[0]=SCSI_TST_U_RDY; + pccb->cmd[1]=pccb->lun<<5; + pccb->cmd[2]=0; + pccb->cmd[3]=0; + pccb->cmd[4]=0; + pccb->cmd[5]=0; + pccb->cmdlen=6; + pccb->msgout[0]=SCSI_IDENTIFY; /* NOT USED */ +} + +void scsi_setup_read_ext(ccb * pccb, unsigned long start, unsigned short blocks) +{ + pccb->cmd[0]=SCSI_READ10; + pccb->cmd[1]=pccb->lun<<5; + pccb->cmd[2]=((unsigned char) (start>>24))&0xff; + pccb->cmd[3]=((unsigned char) (start>>16))&0xff; + pccb->cmd[4]=((unsigned char) (start>>8))&0xff; + pccb->cmd[5]=((unsigned char) (start))&0xff; + pccb->cmd[6]=0; + pccb->cmd[7]=((unsigned char) (blocks>>8))&0xff; + pccb->cmd[8]=(unsigned char) blocks & 0xff; + pccb->cmd[6]=0; + pccb->cmdlen=10; + pccb->msgout[0]=SCSI_IDENTIFY; /* NOT USED */ + debug ("scsi_setup_read_ext: cmd: %02X %02X startblk %02X%02X%02X%02X blccnt %02X%02X\n", + pccb->cmd[0],pccb->cmd[1], + pccb->cmd[2],pccb->cmd[3],pccb->cmd[4],pccb->cmd[5], + pccb->cmd[7],pccb->cmd[8]); +} + +void scsi_setup_write_ext(ccb *pccb, unsigned long start, unsigned short blocks) +{ + pccb->cmd[0] = SCSI_WRITE10; + pccb->cmd[1] = pccb->lun << 5; + pccb->cmd[2] = ((unsigned char) (start>>24)) & 0xff; + pccb->cmd[3] = ((unsigned char) (start>>16)) & 0xff; + pccb->cmd[4] = ((unsigned char) (start>>8)) & 0xff; + pccb->cmd[5] = ((unsigned char) (start)) & 0xff; + pccb->cmd[6] = 0; + pccb->cmd[7] = ((unsigned char) (blocks>>8)) & 0xff; + pccb->cmd[8] = (unsigned char)blocks & 0xff; + pccb->cmd[9] = 0; + pccb->cmdlen = 10; + pccb->msgout[0] = SCSI_IDENTIFY; /* NOT USED */ + debug("%s: cmd: %02X %02X startblk %02X%02X%02X%02X blccnt %02X%02X\n", + __func__, + pccb->cmd[0], pccb->cmd[1], + pccb->cmd[2], pccb->cmd[3], pccb->cmd[4], pccb->cmd[5], + pccb->cmd[7], pccb->cmd[8]); +} + +void scsi_setup_read6(ccb * pccb, unsigned long start, unsigned short blocks) +{ + pccb->cmd[0]=SCSI_READ6; + pccb->cmd[1]=pccb->lun<<5 | (((unsigned char)(start>>16))&0x1f); + pccb->cmd[2]=((unsigned char) (start>>8))&0xff; + pccb->cmd[3]=((unsigned char) (start))&0xff; + pccb->cmd[4]=(unsigned char) blocks & 0xff; + pccb->cmd[5]=0; + pccb->cmdlen=6; + pccb->msgout[0]=SCSI_IDENTIFY; /* NOT USED */ + debug ("scsi_setup_read6: cmd: %02X %02X startblk %02X%02X blccnt %02X\n", + pccb->cmd[0],pccb->cmd[1], + pccb->cmd[2],pccb->cmd[3],pccb->cmd[4]); +} + + +void scsi_setup_inquiry(ccb * pccb) +{ + pccb->cmd[0]=SCSI_INQUIRY; + pccb->cmd[1]=pccb->lun<<5; + pccb->cmd[2]=0; + pccb->cmd[3]=0; + if(pccb->datalen>255) + pccb->cmd[4]=255; + else + pccb->cmd[4]=(unsigned char)pccb->datalen; + pccb->cmd[5]=0; + pccb->cmdlen=6; + pccb->msgout[0]=SCSI_IDENTIFY; /* NOT USED */ +} + + +U_BOOT_CMD( + scsi, 5, 1, do_scsi, + "SCSI sub-system", + "reset - reset SCSI controller\n" + "scsi info - show available SCSI devices\n" + "scsi scan - (re-)scan SCSI bus\n" + "scsi device [dev] - show or set current device\n" + "scsi part [dev] - print partition table of one or all SCSI devices\n" + "scsi read addr blk# cnt - read `cnt' blocks starting at block `blk#'\n" + " to memory address `addr'\n" + "scsi write addr blk# cnt - write `cnt' blocks starting at block\n" + " `blk#' from memory address `addr'" +); + +U_BOOT_CMD( + scsiboot, 3, 1, do_scsiboot, + "boot from SCSI device", + "loadAddr dev:part" +); diff --git a/qemu/roms/u-boot/common/cmd_setexpr.c b/qemu/roms/u-boot/common/cmd_setexpr.c new file mode 100644 index 000000000..926339b9f --- /dev/null +++ b/qemu/roms/u-boot/common/cmd_setexpr.c @@ -0,0 +1,384 @@ +/* + * Copyright 2008 Freescale Semiconductor, Inc. + * Copyright 2013 Wolfgang Denk <wd@denx.de> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/* + * This file provides a shell like 'expr' function to return. + */ + +#include <common.h> +#include <config.h> +#include <command.h> + +static ulong get_arg(char *s, int w) +{ + ulong *p; + + /* + * if the parameter starts with a '*' then assume + * it is a pointer to the value we want + */ + + if (s[0] == '*') { + p = (ulong *)simple_strtoul(&s[1], NULL, 16); + switch (w) { + case 1: return((ulong)(*(uchar *)p)); + case 2: return((ulong)(*(ushort *)p)); + case 4: + default: return(*p); + } + } else { + return simple_strtoul(s, NULL, 16); + } +} + +#ifdef CONFIG_REGEX + +#include <slre.h> + +#define SLRE_BUFSZ 16384 +#define SLRE_PATSZ 4096 + +/* + * memstr - Find the first substring in memory + * @s1: The string to be searched + * @s2: The string to search for + * + * Similar to and based on strstr(), + * but strings do not need to be NUL terminated. + */ +static char *memstr(const char *s1, int l1, const char *s2, int l2) +{ + if (!l2) + return (char *)s1; + + while (l1 >= l2) { + l1--; + if (!memcmp(s1, s2, l2)) + return (char *)s1; + s1++; + } + return NULL; +} + +static char *substitute(char *string, /* string buffer */ + int *slen, /* current string length */ + int ssize, /* string bufer size */ + const char *old,/* old (replaced) string */ + int olen, /* length of old string */ + const char *new,/* new (replacement) string */ + int nlen) /* length of new string */ +{ + char *p = memstr(string, *slen, old, olen); + + if (p == NULL) + return NULL; + + debug("## Match at pos %ld: match len %d, subst len %d\n", + (long)(p - string), olen, nlen); + + /* make sure replacement matches */ + if (*slen + nlen - olen > ssize) { + printf("## error: substitution buffer overflow\n"); + return NULL; + } + + /* move tail if needed */ + if (olen != nlen) { + int tail, len; + + len = (olen > nlen) ? olen : nlen; + + tail = ssize - (p + len - string); + + debug("## tail len %d\n", tail); + + memmove(p + nlen, p + olen, tail); + } + + /* insert substitue */ + memcpy(p, new, nlen); + + *slen += nlen - olen; + + return p + nlen; +} + +/* + * Perform regex operations on a environment variable + * + * Returns 0 if OK, 1 in case of errors. + */ +static int regex_sub(const char *name, + const char *r, const char *s, const char *t, + int global) +{ + struct slre slre; + char data[SLRE_BUFSZ]; + char *datap = data; + const char *value; + int res, len, nlen, loop; + + if (name == NULL) + return 1; + + if (slre_compile(&slre, r) == 0) { + printf("Error compiling regex: %s\n", slre.err_str); + return 1; + } + + if (t == NULL) { + value = getenv(name); + + if (value == NULL) { + printf("## Error: variable \"%s\" not defined\n", name); + return 1; + } + t = value; + } + + debug("REGEX on %s=%s\n", name, t); + debug("REGEX=\"%s\", SUBST=\"%s\", GLOBAL=%d\n", + r, s ? s : "<NULL>", global); + + len = strlen(t); + if (len + 1 > SLRE_BUFSZ) { + printf("## error: subst buffer overflow: have %d, need %d\n", + SLRE_BUFSZ, len + 1); + return 1; + } + + strcpy(data, t); + + if (s == NULL) + nlen = 0; + else + nlen = strlen(s); + + for (loop = 0;; loop++) { + struct cap caps[slre.num_caps + 2]; + char nbuf[SLRE_PATSZ]; + const char *old; + char *np; + int i, olen; + + (void) memset(caps, 0, sizeof(caps)); + + res = slre_match(&slre, datap, len, caps); + + debug("Result: %d\n", res); + + for (i = 0; i < slre.num_caps; i++) { + if (caps[i].len > 0) { + debug("Substring %d: [%.*s]\n", i, + caps[i].len, caps[i].ptr); + } + } + + if (res == 0) { + if (loop == 0) { + printf("%s: No match\n", t); + return 1; + } else { + break; + } + } + + debug("## MATCH ## %s\n", data); + + if (s == NULL) { + printf("%s=%s\n", name, t); + return 1; + } + + old = caps[0].ptr; + olen = caps[0].len; + + if (nlen + 1 >= SLRE_PATSZ) { + printf("## error: pattern buffer overflow: have %d, need %d\n", + SLRE_BUFSZ, nlen + 1); + return 1; + } + strcpy(nbuf, s); + + debug("## SUBST(1) ## %s\n", nbuf); + + /* + * Handle back references + * + * Support for \0 ... \9, where \0 is the + * whole matched pattern (similar to &). + * + * Implementation is a bit simpleminded as + * backrefs are substituted sequentially, one + * by one. This will lead to somewhat + * unexpected results if the replacement + * strings contain any \N strings then then + * may get substitued, too. We accept this + * restriction for the sake of simplicity. + */ + for (i = 0; i < 10; ++i) { + char backref[2] = { + '\\', + '0', + }; + + if (caps[i].len == 0) + break; + + backref[1] += i; + + debug("## BACKREF %d: replace \"%.*s\" by \"%.*s\" in \"%s\"\n", + i, + 2, backref, + caps[i].len, caps[i].ptr, + nbuf); + + for (np = nbuf;;) { + char *p = memstr(np, nlen, backref, 2); + + if (p == NULL) + break; + + np = substitute(np, &nlen, + SLRE_PATSZ, + backref, 2, + caps[i].ptr, caps[i].len); + + if (np == NULL) + return 1; + } + } + debug("## SUBST(2) ## %s\n", nbuf); + + datap = substitute(datap, &len, SLRE_BUFSZ, + old, olen, + nbuf, nlen); + + if (datap == NULL) + return 1; + + debug("## REMAINDER: %s\n", datap); + + debug("## RESULT: %s\n", data); + + if (!global) + break; + } + debug("## FINAL (now setenv()) : %s\n", data); + + printf("%s=%s\n", name, data); + + return setenv(name, data); +} +#endif + +static int do_setexpr(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + ulong a, b; + ulong value; + int w; + + /* + * We take 3, 5, or 6 arguments: + * 3 : setexpr name value + * 5 : setexpr name val1 op val2 + * setexpr name [g]sub r s + * 6 : setexpr name [g]sub r s t + */ + + /* > 6 already tested by max command args */ + if ((argc < 3) || (argc == 4)) + return CMD_RET_USAGE; + + w = cmd_get_data_size(argv[0], 4); + + a = get_arg(argv[2], w); + + /* plain assignment: "setexpr name value" */ + if (argc == 3) { + setenv_hex(argv[1], a); + return 0; + } + + /* 5 or 6 args (6 args only with [g]sub) */ +#ifdef CONFIG_REGEX + /* + * rexep handling: "setexpr name [g]sub r s [t]" + * with 5 args, "t" will be NULL + */ + if (strcmp(argv[2], "gsub") == 0) + return regex_sub(argv[1], argv[3], argv[4], argv[5], 1); + + if (strcmp(argv[2], "sub") == 0) + return regex_sub(argv[1], argv[3], argv[4], argv[5], 0); +#endif + + /* standard operators: "setexpr name val1 op val2" */ + if (argc != 5) + return CMD_RET_USAGE; + + if (strlen(argv[3]) != 1) + return CMD_RET_USAGE; + + b = get_arg(argv[4], w); + + switch (argv[3][0]) { + case '|': + value = a | b; + break; + case '&': + value = a & b; + break; + case '+': + value = a + b; + break; + case '^': + value = a ^ b; + break; + case '-': + value = a - b; + break; + case '*': + value = a * b; + break; + case '/': + value = a / b; + break; + case '%': + value = a % b; + break; + default: + printf("invalid op\n"); + return 1; + } + + setenv_hex(argv[1], value); + + return 0; +} + +U_BOOT_CMD( + setexpr, 6, 0, do_setexpr, + "set environment variable as the result of eval expression", + "[.b, .w, .l] name [*]value1 <op> [*]value2\n" + " - set environment variable 'name' to the result of the evaluated\n" + " expression specified by <op>. <op> can be &, |, ^, +, -, *, /, %\n" + " size argument is only meaningful if value1 and/or value2 are\n" + " memory addresses (*)\n" + "setexpr[.b, .w, .l] name [*]value\n" + " - load a value into a variable" +#ifdef CONFIG_REGEX + "\n" + "setexpr name gsub r s [t]\n" + " - For each substring matching the regular expression <r> in the\n" + " string <t>, substitute the string <s>. The result is\n" + " assigned to <name>. If <t> is not supplied, use the old\n" + " value of <name>\n" + "setexpr name sub r s [t]\n" + " - Just like gsub(), but replace only the first matching substring" +#endif +); diff --git a/qemu/roms/u-boot/common/cmd_sf.c b/qemu/roms/u-boot/common/cmd_sf.c new file mode 100644 index 000000000..b4ceb7146 --- /dev/null +++ b/qemu/roms/u-boot/common/cmd_sf.c @@ -0,0 +1,556 @@ +/* + * Command for accessing SPI flash. + * + * Copyright (C) 2008 Atmel Corporation + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <div64.h> +#include <malloc.h> +#include <spi_flash.h> + +#include <asm/io.h> + +#ifndef CONFIG_SF_DEFAULT_SPEED +# define CONFIG_SF_DEFAULT_SPEED 1000000 +#endif +#ifndef CONFIG_SF_DEFAULT_MODE +# define CONFIG_SF_DEFAULT_MODE SPI_MODE_3 +#endif +#ifndef CONFIG_SF_DEFAULT_CS +# define CONFIG_SF_DEFAULT_CS 0 +#endif +#ifndef CONFIG_SF_DEFAULT_BUS +# define CONFIG_SF_DEFAULT_BUS 0 +#endif + +static struct spi_flash *flash; + + +/* + * This function computes the length argument for the erase command. + * The length on which the command is to operate can be given in two forms: + * 1. <cmd> offset len - operate on <'offset', 'len') + * 2. <cmd> offset +len - operate on <'offset', 'round_up(len)') + * If the second form is used and the length doesn't fall on the + * sector boundary, than it will be adjusted to the next sector boundary. + * If it isn't in the flash, the function will fail (return -1). + * Input: + * arg: length specification (i.e. both command arguments) + * Output: + * len: computed length for operation + * Return: + * 1: success + * -1: failure (bad format, bad address). + */ +static int sf_parse_len_arg(char *arg, ulong *len) +{ + char *ep; + char round_up_len; /* indicates if the "+length" form used */ + ulong len_arg; + + round_up_len = 0; + if (*arg == '+') { + round_up_len = 1; + ++arg; + } + + len_arg = simple_strtoul(arg, &ep, 16); + if (ep == arg || *ep != '\0') + return -1; + + if (round_up_len && flash->sector_size > 0) + *len = ROUND(len_arg, flash->sector_size); + else + *len = len_arg; + + return 1; +} + +/** + * This function takes a byte length and a delta unit of time to compute the + * approximate bytes per second + * + * @param len amount of bytes currently processed + * @param start_ms start time of processing in ms + * @return bytes per second if OK, 0 on error + */ +static ulong bytes_per_second(unsigned int len, ulong start_ms) +{ + /* less accurate but avoids overflow */ + if (len >= ((unsigned int) -1) / 1024) + return len / (max(get_timer(start_ms) / 1024, 1)); + else + return 1024 * len / max(get_timer(start_ms), 1); +} + +static int do_spi_flash_probe(int argc, char * const argv[]) +{ + unsigned int bus = CONFIG_SF_DEFAULT_BUS; + unsigned int cs = CONFIG_SF_DEFAULT_CS; + unsigned int speed = CONFIG_SF_DEFAULT_SPEED; + unsigned int mode = CONFIG_SF_DEFAULT_MODE; + char *endp; + struct spi_flash *new; + + if (argc >= 2) { + cs = simple_strtoul(argv[1], &endp, 0); + if (*argv[1] == 0 || (*endp != 0 && *endp != ':')) + return -1; + if (*endp == ':') { + if (endp[1] == 0) + return -1; + + bus = cs; + cs = simple_strtoul(endp + 1, &endp, 0); + if (*endp != 0) + return -1; + } + } + + if (argc >= 3) { + speed = simple_strtoul(argv[2], &endp, 0); + if (*argv[2] == 0 || *endp != 0) + return -1; + } + if (argc >= 4) { + mode = simple_strtoul(argv[3], &endp, 16); + if (*argv[3] == 0 || *endp != 0) + return -1; + } + + new = spi_flash_probe(bus, cs, speed, mode); + if (!new) { + printf("Failed to initialize SPI flash at %u:%u\n", bus, cs); + return 1; + } + + if (flash) + spi_flash_free(flash); + flash = new; + + return 0; +} + +/** + * Write a block of data to SPI flash, first checking if it is different from + * what is already there. + * + * If the data being written is the same, then *skipped is incremented by len. + * + * @param flash flash context pointer + * @param offset flash offset to write + * @param len number of bytes to write + * @param buf buffer to write from + * @param cmp_buf read buffer to use to compare data + * @param skipped Count of skipped data (incremented by this function) + * @return NULL if OK, else a string containing the stage which failed + */ +static const char *spi_flash_update_block(struct spi_flash *flash, u32 offset, + size_t len, const char *buf, char *cmp_buf, size_t *skipped) +{ + debug("offset=%#x, sector_size=%#x, len=%#zx\n", + offset, flash->sector_size, len); + /* Read the entire sector so to allow for rewriting */ + if (spi_flash_read(flash, offset, flash->sector_size, cmp_buf)) + return "read"; + /* Compare only what is meaningful (len) */ + if (memcmp(cmp_buf, buf, len) == 0) { + debug("Skip region %x size %zx: no change\n", + offset, len); + *skipped += len; + return NULL; + } + /* Erase the entire sector */ + if (spi_flash_erase(flash, offset, flash->sector_size)) + return "erase"; + /* Write the initial part of the block from the source */ + if (spi_flash_write(flash, offset, len, buf)) + return "write"; + /* If it's a partial sector, rewrite the existing part */ + if (len != flash->sector_size) { + /* Rewrite the original data to the end of the sector */ + if (spi_flash_write(flash, offset + len, + flash->sector_size - len, &cmp_buf[len])) + return "write"; + } + + return NULL; +} + +/** + * Update an area of SPI flash by erasing and writing any blocks which need + * to change. Existing blocks with the correct data are left unchanged. + * + * @param flash flash context pointer + * @param offset flash offset to write + * @param len number of bytes to write + * @param buf buffer to write from + * @return 0 if ok, 1 on error + */ +static int spi_flash_update(struct spi_flash *flash, u32 offset, + size_t len, const char *buf) +{ + const char *err_oper = NULL; + char *cmp_buf; + const char *end = buf + len; + size_t todo; /* number of bytes to do in this pass */ + size_t skipped = 0; /* statistics */ + const ulong start_time = get_timer(0); + size_t scale = 1; + const char *start_buf = buf; + ulong delta; + + if (end - buf >= 200) + scale = (end - buf) / 100; + cmp_buf = malloc(flash->sector_size); + if (cmp_buf) { + ulong last_update = get_timer(0); + + for (; buf < end && !err_oper; buf += todo, offset += todo) { + todo = min(end - buf, flash->sector_size); + if (get_timer(last_update) > 100) { + printf(" \rUpdating, %zu%% %lu B/s", + 100 - (end - buf) / scale, + bytes_per_second(buf - start_buf, + start_time)); + last_update = get_timer(0); + } + err_oper = spi_flash_update_block(flash, offset, todo, + buf, cmp_buf, &skipped); + } + } else { + err_oper = "malloc"; + } + free(cmp_buf); + putc('\r'); + if (err_oper) { + printf("SPI flash failed in %s step\n", err_oper); + return 1; + } + + delta = get_timer(start_time); + printf("%zu bytes written, %zu bytes skipped", len - skipped, + skipped); + printf(" in %ld.%lds, speed %ld B/s\n", + delta / 1000, delta % 1000, bytes_per_second(len, start_time)); + + return 0; +} + +static int do_spi_flash_read_write(int argc, char * const argv[]) +{ + unsigned long addr; + unsigned long offset; + unsigned long len; + void *buf; + char *endp; + int ret = 1; + + if (argc < 4) + return -1; + + addr = simple_strtoul(argv[1], &endp, 16); + if (*argv[1] == 0 || *endp != 0) + return -1; + offset = simple_strtoul(argv[2], &endp, 16); + if (*argv[2] == 0 || *endp != 0) + return -1; + len = simple_strtoul(argv[3], &endp, 16); + if (*argv[3] == 0 || *endp != 0) + return -1; + + /* Consistency checking */ + if (offset + len > flash->size) { + printf("ERROR: attempting %s past flash size (%#x)\n", + argv[0], flash->size); + return 1; + } + + buf = map_physmem(addr, len, MAP_WRBACK); + if (!buf) { + puts("Failed to map physical memory\n"); + return 1; + } + + if (strcmp(argv[0], "update") == 0) { + ret = spi_flash_update(flash, offset, len, buf); + } else if (strncmp(argv[0], "read", 4) == 0 || + strncmp(argv[0], "write", 5) == 0) { + int read; + + read = strncmp(argv[0], "read", 4) == 0; + if (read) + ret = spi_flash_read(flash, offset, len, buf); + else + ret = spi_flash_write(flash, offset, len, buf); + + printf("SF: %zu bytes @ %#x %s: %s\n", (size_t)len, (u32)offset, + read ? "Read" : "Written", ret ? "ERROR" : "OK"); + } + + unmap_physmem(buf, len); + + return ret == 0 ? 0 : 1; +} + +static int do_spi_flash_erase(int argc, char * const argv[]) +{ + unsigned long offset; + unsigned long len; + char *endp; + int ret; + + if (argc < 3) + return -1; + + offset = simple_strtoul(argv[1], &endp, 16); + if (*argv[1] == 0 || *endp != 0) + return -1; + + ret = sf_parse_len_arg(argv[2], &len); + if (ret != 1) + return -1; + + /* Consistency checking */ + if (offset + len > flash->size) { + printf("ERROR: attempting %s past flash size (%#x)\n", + argv[0], flash->size); + return 1; + } + + ret = spi_flash_erase(flash, offset, len); + printf("SF: %zu bytes @ %#x Erased: %s\n", (size_t)len, (u32)offset, + ret ? "ERROR" : "OK"); + + return ret == 0 ? 0 : 1; +} + +#ifdef CONFIG_CMD_SF_TEST +enum { + STAGE_ERASE, + STAGE_CHECK, + STAGE_WRITE, + STAGE_READ, + + STAGE_COUNT, +}; + +static char *stage_name[STAGE_COUNT] = { + "erase", + "check", + "write", + "read", +}; + +struct test_info { + int stage; + int bytes; + unsigned base_ms; + unsigned time_ms[STAGE_COUNT]; +}; + +static void show_time(struct test_info *test, int stage) +{ + uint64_t speed; /* KiB/s */ + int bps; /* Bits per second */ + + speed = (long long)test->bytes * 1000; + if (test->time_ms[stage]) + do_div(speed, test->time_ms[stage] * 1024); + bps = speed * 8; + + printf("%d %s: %d ticks, %d KiB/s %d.%03d Mbps\n", stage, + stage_name[stage], test->time_ms[stage], + (int)speed, bps / 1000, bps % 1000); +} + +static void spi_test_next_stage(struct test_info *test) +{ + test->time_ms[test->stage] = get_timer(test->base_ms); + show_time(test, test->stage); + test->base_ms = get_timer(0); + test->stage++; +} + +/** + * Run a test on the SPI flash + * + * @param flash SPI flash to use + * @param buf Source buffer for data to write + * @param len Size of data to read/write + * @param offset Offset within flash to check + * @param vbuf Verification buffer + * @return 0 if ok, -1 on error + */ +static int spi_flash_test(struct spi_flash *flash, uint8_t *buf, ulong len, + ulong offset, uint8_t *vbuf) +{ + struct test_info test; + int i; + + printf("SPI flash test:\n"); + memset(&test, '\0', sizeof(test)); + test.base_ms = get_timer(0); + test.bytes = len; + if (spi_flash_erase(flash, offset, len)) { + printf("Erase failed\n"); + return -1; + } + spi_test_next_stage(&test); + + if (spi_flash_read(flash, offset, len, vbuf)) { + printf("Check read failed\n"); + return -1; + } + for (i = 0; i < len; i++) { + if (vbuf[i] != 0xff) { + printf("Check failed at %d\n", i); + print_buffer(i, vbuf + i, 1, min(len - i, 0x40), 0); + return -1; + } + } + spi_test_next_stage(&test); + + if (spi_flash_write(flash, offset, len, buf)) { + printf("Write failed\n"); + return -1; + } + memset(vbuf, '\0', len); + spi_test_next_stage(&test); + + if (spi_flash_read(flash, offset, len, vbuf)) { + printf("Read failed\n"); + return -1; + } + spi_test_next_stage(&test); + + for (i = 0; i < len; i++) { + if (buf[i] != vbuf[i]) { + printf("Verify failed at %d, good data:\n", i); + print_buffer(i, buf + i, 1, min(len - i, 0x40), 0); + printf("Bad data:\n"); + print_buffer(i, vbuf + i, 1, min(len - i, 0x40), 0); + return -1; + } + } + printf("Test passed\n"); + for (i = 0; i < STAGE_COUNT; i++) + show_time(&test, i); + + return 0; +} + +static int do_spi_flash_test(int argc, char * const argv[]) +{ + unsigned long offset; + unsigned long len; + uint8_t *buf, *from; + char *endp; + uint8_t *vbuf; + int ret; + + if (argc < 3) + return -1; + offset = simple_strtoul(argv[1], &endp, 16); + if (*argv[1] == 0 || *endp != 0) + return -1; + len = simple_strtoul(argv[2], &endp, 16); + if (*argv[2] == 0 || *endp != 0) + return -1; + + vbuf = malloc(len); + if (!vbuf) { + printf("Cannot allocate memory (%lu bytes)\n", len); + return 1; + } + buf = malloc(len); + if (!buf) { + free(vbuf); + printf("Cannot allocate memory (%lu bytes)\n", len); + return 1; + } + + from = map_sysmem(CONFIG_SYS_TEXT_BASE, 0); + memcpy(buf, from, len); + ret = spi_flash_test(flash, buf, len, offset, vbuf); + free(vbuf); + free(buf); + if (ret) { + printf("Test failed\n"); + return 1; + } + + return 0; +} +#endif /* CONFIG_CMD_SF_TEST */ + +static int do_spi_flash(cmd_tbl_t *cmdtp, int flag, int argc, + char * const argv[]) +{ + const char *cmd; + int ret; + + /* need at least two arguments */ + if (argc < 2) + goto usage; + + cmd = argv[1]; + --argc; + ++argv; + + if (strcmp(cmd, "probe") == 0) { + ret = do_spi_flash_probe(argc, argv); + goto done; + } + + /* The remaining commands require a selected device */ + if (!flash) { + puts("No SPI flash selected. Please run `sf probe'\n"); + return 1; + } + + if (strcmp(cmd, "read") == 0 || strcmp(cmd, "write") == 0 || + strcmp(cmd, "update") == 0) + ret = do_spi_flash_read_write(argc, argv); + else if (strcmp(cmd, "erase") == 0) + ret = do_spi_flash_erase(argc, argv); +#ifdef CONFIG_CMD_SF_TEST + else if (!strcmp(cmd, "test")) + ret = do_spi_flash_test(argc, argv); +#endif + else + ret = -1; + +done: + if (ret != -1) + return ret; + +usage: + return CMD_RET_USAGE; +} + +#ifdef CONFIG_CMD_SF_TEST +#define SF_TEST_HELP "\nsf test offset len " \ + "- run a very basic destructive test" +#else +#define SF_TEST_HELP +#endif + +U_BOOT_CMD( + sf, 5, 1, do_spi_flash, + "SPI flash sub-system", + "probe [[bus:]cs] [hz] [mode] - init flash device on given SPI bus\n" + " and chip select\n" + "sf read addr offset len - read `len' bytes starting at\n" + " `offset' to memory at `addr'\n" + "sf write addr offset len - write `len' bytes from memory\n" + " at `addr' to flash at `offset'\n" + "sf erase offset [+]len - erase `len' bytes from `offset'\n" + " `+len' round up `len' to block size\n" + "sf update addr offset len - erase and write `len' bytes from memory\n" + " at `addr' to flash at `offset'" + SF_TEST_HELP +); diff --git a/qemu/roms/u-boot/common/cmd_sha1sum.c b/qemu/roms/u-boot/common/cmd_sha1sum.c new file mode 100644 index 000000000..644b9a0e6 --- /dev/null +++ b/qemu/roms/u-boot/common/cmd_sha1sum.c @@ -0,0 +1,54 @@ +/* + * (C) Copyright 2011 + * Joe Hershberger, National Instruments, joe.hershberger@ni.com + * + * (C) Copyright 2000 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <command.h> +#include <hash.h> +#include <sha1.h> + +int do_sha1sum(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + int flags = HASH_FLAG_ENV; + int ac; + char * const *av; + + if (argc < 3) + return CMD_RET_USAGE; + + av = argv + 1; + ac = argc - 1; +#ifdef CONFIG_SHA1SUM_VERIFY + if (strcmp(*av, "-v") == 0) { + flags |= HASH_FLAG_VERIFY; + av++; + ac--; + } +#endif + + return hash_command("sha1", flags, cmdtp, flag, ac, av); +} + +#ifdef CONFIG_SHA1SUM_VERIFY +U_BOOT_CMD( + sha1sum, 5, 1, do_sha1sum, + "compute SHA1 message digest", + "address count [[*]sum]\n" + " - compute SHA1 message digest [save to sum]\n" + "sha1sum -v address count [*]sum\n" + " - verify sha1sum of memory area" +); +#else +U_BOOT_CMD( + sha1sum, 4, 1, do_sha1sum, + "compute SHA1 message digest", + "address count [[*]sum]\n" + " - compute SHA1 message digest [save to sum]" +); +#endif diff --git a/qemu/roms/u-boot/common/cmd_softswitch.c b/qemu/roms/u-boot/common/cmd_softswitch.c new file mode 100644 index 000000000..f75d92677 --- /dev/null +++ b/qemu/roms/u-boot/common/cmd_softswitch.c @@ -0,0 +1,41 @@ +/* + * cmd_softswitch.c - set the softswitch for bf60x + * + * Copyright (c) 2012 Analog Devices Inc. + * + * Licensed under the GPL-2 or later. + */ + +#include <common.h> +#include <command.h> +#include <asm/blackfin.h> +#include <asm/soft_switch.h> + +int do_softswitch(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + int switchaddr, value, pin, port; + + if (argc != 5) + return CMD_RET_USAGE; + + if (strcmp(argv[2], "GPA") == 0) + port = IO_PORT_A; + else if (strcmp(argv[2], "GPB") == 0) + port = IO_PORT_B; + else + return CMD_RET_USAGE; + + switchaddr = simple_strtoul(argv[1], NULL, 16); + pin = simple_strtoul(argv[3], NULL, 16); + value = simple_strtoul(argv[4], NULL, 16); + + config_switch_bit(switchaddr, port, (1 << pin), IO_PORT_OUTPUT, value); + + return 0; +} + +U_BOOT_CMD( + softswitch_output, 5, 1, do_softswitch, + "switchaddr GPA/GPB pin_offset value", + "" +); diff --git a/qemu/roms/u-boot/common/cmd_sound.c b/qemu/roms/u-boot/common/cmd_sound.c new file mode 100644 index 000000000..f5dd8bcf2 --- /dev/null +++ b/qemu/roms/u-boot/common/cmd_sound.c @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2012 Samsung Electronics + * Rajeshwari Shinde <rajeshwari.s@samsung.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <command.h> +#include <fdtdec.h> +#include <sound.h> + +DECLARE_GLOBAL_DATA_PTR; + +/* Initilaise sound subsystem */ +static int do_init(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[]) +{ + int ret; + + ret = sound_init(gd->fdt_blob); + if (ret) { + printf("Initialise Audio driver failed\n"); + return CMD_RET_FAILURE; + } + + return 0; +} + +/* play sound from buffer */ +static int do_play(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[]) +{ + int ret = 0; + int msec = 1000; + int freq = 400; + + if (argc > 1) + msec = simple_strtoul(argv[1], NULL, 10); + if (argc > 2) + freq = simple_strtoul(argv[2], NULL, 10); + + ret = sound_play(msec, freq); + if (ret) { + printf("play failed"); + return CMD_RET_FAILURE; + } + + return 0; +} + +static cmd_tbl_t cmd_sound_sub[] = { + U_BOOT_CMD_MKENT(init, 0, 1, do_init, "", ""), + U_BOOT_CMD_MKENT(play, 2, 1, do_play, "", ""), +}; + +/* process sound command */ +static int do_sound(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[]) +{ + cmd_tbl_t *c; + + if (argc < 1) + return CMD_RET_USAGE; + + /* Strip off leading 'sound' command argument */ + argc--; + argv++; + + c = find_cmd_tbl(argv[0], &cmd_sound_sub[0], ARRAY_SIZE(cmd_sound_sub)); + + if (c) + return c->cmd(cmdtp, flag, argc, argv); + else + return CMD_RET_USAGE; +} + +U_BOOT_CMD( + sound, 4, 1, do_sound, + "sound sub-system", + "init - initialise the sound driver\n" + "sound play [len] [freq] - play a sound for len ms at freq hz\n" +); diff --git a/qemu/roms/u-boot/common/cmd_source.c b/qemu/roms/u-boot/common/cmd_source.c new file mode 100644 index 000000000..54ffd1647 --- /dev/null +++ b/qemu/roms/u-boot/common/cmd_source.c @@ -0,0 +1,184 @@ +/* + * (C) Copyright 2001 + * Kyle Harris, kharris@nexus-tech.net + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/* + * The "source" command allows to define "script images", i. e. files + * that contain command sequences that can be executed by the command + * interpreter. It returns the exit status of the last command + * executed from the script. This is very similar to running a shell + * script in a UNIX shell, hence the name for the command. + */ + +/* #define DEBUG */ + +#include <common.h> +#include <command.h> +#include <image.h> +#include <malloc.h> +#include <asm/byteorder.h> +#include <asm/io.h> +#if defined(CONFIG_8xx) +#include <mpc8xx.h> +#endif + +int +source (ulong addr, const char *fit_uname) +{ + ulong len; + const image_header_t *hdr; + ulong *data; + int verify; + void *buf; +#if defined(CONFIG_FIT) + const void* fit_hdr; + int noffset; + const void *fit_data; + size_t fit_len; +#endif + + verify = getenv_yesno ("verify"); + + buf = map_sysmem(addr, 0); + switch (genimg_get_format(buf)) { + case IMAGE_FORMAT_LEGACY: + hdr = buf; + + if (!image_check_magic (hdr)) { + puts ("Bad magic number\n"); + return 1; + } + + if (!image_check_hcrc (hdr)) { + puts ("Bad header crc\n"); + return 1; + } + + if (verify) { + if (!image_check_dcrc (hdr)) { + puts ("Bad data crc\n"); + return 1; + } + } + + if (!image_check_type (hdr, IH_TYPE_SCRIPT)) { + puts ("Bad image type\n"); + return 1; + } + + /* get length of script */ + data = (ulong *)image_get_data (hdr); + + if ((len = uimage_to_cpu (*data)) == 0) { + puts ("Empty Script\n"); + return 1; + } + + /* + * scripts are just multi-image files with one component, seek + * past the zero-terminated sequence of image lengths to get + * to the actual image data + */ + while (*data++); + break; +#if defined(CONFIG_FIT) + case IMAGE_FORMAT_FIT: + if (fit_uname == NULL) { + puts ("No FIT subimage unit name\n"); + return 1; + } + + fit_hdr = buf; + if (!fit_check_format (fit_hdr)) { + puts ("Bad FIT image format\n"); + return 1; + } + + /* get script component image node offset */ + noffset = fit_image_get_node (fit_hdr, fit_uname); + if (noffset < 0) { + printf ("Can't find '%s' FIT subimage\n", fit_uname); + return 1; + } + + if (!fit_image_check_type (fit_hdr, noffset, IH_TYPE_SCRIPT)) { + puts ("Not a image image\n"); + return 1; + } + + /* verify integrity */ + if (verify) { + if (!fit_image_verify(fit_hdr, noffset)) { + puts ("Bad Data Hash\n"); + return 1; + } + } + + /* get script subimage data address and length */ + if (fit_image_get_data (fit_hdr, noffset, &fit_data, &fit_len)) { + puts ("Could not find script subimage data\n"); + return 1; + } + + data = (ulong *)fit_data; + len = (ulong)fit_len; + break; +#endif + default: + puts ("Wrong image format for \"source\" command\n"); + return 1; + } + + debug ("** Script length: %ld\n", len); + return run_command_list((char *)data, len, 0); +} + +/**************************************************/ +#if defined(CONFIG_CMD_SOURCE) +int +do_source (cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + ulong addr; + int rcode; + const char *fit_uname = NULL; + + /* Find script image */ + if (argc < 2) { + addr = CONFIG_SYS_LOAD_ADDR; + debug ("* source: default load address = 0x%08lx\n", addr); +#if defined(CONFIG_FIT) + } else if (fit_parse_subimage (argv[1], load_addr, &addr, &fit_uname)) { + debug ("* source: subimage '%s' from FIT image at 0x%08lx\n", + fit_uname, addr); +#endif + } else { + addr = simple_strtoul(argv[1], NULL, 16); + debug ("* source: cmdline image address = 0x%08lx\n", addr); + } + + printf ("## Executing script at %08lx\n", addr); + rcode = source (addr, fit_uname); + return rcode; +} + +#ifdef CONFIG_SYS_LONGHELP +static char source_help_text[] = + "[addr]\n" + "\t- run script starting at addr\n" + "\t- A valid image header must be present" +#if defined(CONFIG_FIT) + "\n" + "For FIT format uImage addr must include subimage\n" + "unit name in the form of addr:<subimg_uname>" +#endif + ""; +#endif + +U_BOOT_CMD( + source, 2, 0, do_source, + "run script from memory", source_help_text +); +#endif diff --git a/qemu/roms/u-boot/common/cmd_spi.c b/qemu/roms/u-boot/common/cmd_spi.c new file mode 100644 index 000000000..3c8e913be --- /dev/null +++ b/qemu/roms/u-boot/common/cmd_spi.c @@ -0,0 +1,140 @@ +/* + * (C) Copyright 2002 + * Gerald Van Baren, Custom IDEAS, vanbaren@cideas.com + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/* + * SPI Read/Write Utilities + */ + +#include <common.h> +#include <command.h> +#include <spi.h> + +/*----------------------------------------------------------------------- + * Definitions + */ + +#ifndef MAX_SPI_BYTES +# define MAX_SPI_BYTES 32 /* Maximum number of bytes we can handle */ +#endif + +#ifndef CONFIG_DEFAULT_SPI_BUS +# define CONFIG_DEFAULT_SPI_BUS 0 +#endif +#ifndef CONFIG_DEFAULT_SPI_MODE +# define CONFIG_DEFAULT_SPI_MODE SPI_MODE_0 +#endif + +/* + * Values from last command. + */ +static unsigned int bus; +static unsigned int cs; +static unsigned int mode; +static int bitlen; +static uchar dout[MAX_SPI_BYTES]; +static uchar din[MAX_SPI_BYTES]; + +/* + * SPI read/write + * + * Syntax: + * spi {dev} {num_bits} {dout} + * {dev} is the device number for controlling chip select (see TBD) + * {num_bits} is the number of bits to send & receive (base 10) + * {dout} is a hexadecimal string of data to send + * The command prints out the hexadecimal string received via SPI. + */ + +int do_spi (cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + struct spi_slave *slave; + char *cp = 0; + uchar tmp; + int j; + int rcode = 0; + + /* + * We use the last specified parameters, unless new ones are + * entered. + */ + + if ((flag & CMD_FLAG_REPEAT) == 0) + { + if (argc >= 2) { + mode = CONFIG_DEFAULT_SPI_MODE; + bus = simple_strtoul(argv[1], &cp, 10); + if (*cp == ':') { + cs = simple_strtoul(cp+1, &cp, 10); + } else { + cs = bus; + bus = CONFIG_DEFAULT_SPI_BUS; + } + if (*cp == '.') + mode = simple_strtoul(cp+1, NULL, 10); + } + if (argc >= 3) + bitlen = simple_strtoul(argv[2], NULL, 10); + if (argc >= 4) { + cp = argv[3]; + for(j = 0; *cp; j++, cp++) { + tmp = *cp - '0'; + if(tmp > 9) + tmp -= ('A' - '0') - 10; + if(tmp > 15) + tmp -= ('a' - 'A'); + if(tmp > 15) { + printf("Hex conversion error on %c\n", *cp); + return 1; + } + if((j % 2) == 0) + dout[j / 2] = (tmp << 4); + else + dout[j / 2] |= tmp; + } + } + } + + if ((bitlen < 0) || (bitlen > (MAX_SPI_BYTES * 8))) { + printf("Invalid bitlen %d\n", bitlen); + return 1; + } + + slave = spi_setup_slave(bus, cs, 1000000, mode); + if (!slave) { + printf("Invalid device %d:%d\n", bus, cs); + return 1; + } + + spi_claim_bus(slave); + if(spi_xfer(slave, bitlen, dout, din, + SPI_XFER_BEGIN | SPI_XFER_END) != 0) { + printf("Error during SPI transaction\n"); + rcode = 1; + } else { + for(j = 0; j < ((bitlen + 7) / 8); j++) { + printf("%02X", din[j]); + } + printf("\n"); + } + spi_release_bus(slave); + spi_free_slave(slave); + + return rcode; +} + +/***************************************************/ + +U_BOOT_CMD( + sspi, 5, 1, do_spi, + "SPI utility command", + "[<bus>:]<cs>[.<mode>] <bit_len> <dout> - Send and receive bits\n" + "<bus> - Identifies the SPI bus\n" + "<cs> - Identifies the chip select\n" + "<mode> - Identifies the SPI mode to use\n" + "<bit_len> - Number of bits to send (base 10)\n" + "<dout> - Hexadecimal string that gets sent" +); diff --git a/qemu/roms/u-boot/common/cmd_spibootldr.c b/qemu/roms/u-boot/common/cmd_spibootldr.c new file mode 100644 index 000000000..ca76dde1d --- /dev/null +++ b/qemu/roms/u-boot/common/cmd_spibootldr.c @@ -0,0 +1,37 @@ +/* + * U-boot - spibootldr.c + * + * Copyright (c) 2005-2008 Analog Devices Inc. + * + * See file CREDITS for list of people who contributed to this + * project. + * + * Licensed under the GPL-2 or later. + */ + +#include <common.h> +#include <command.h> + +#include <asm/blackfin.h> +#include <asm/mach-common/bits/bootrom.h> + +int do_spibootldr(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + s32 addr; + + /* Get the address */ + if (argc < 2) + addr = 0; + else + addr = simple_strtoul(argv[1], NULL, 16); + + printf("## Booting ldr image at SPI offset 0x%x ...\n", addr); + + return bfrom_SpiBoot(addr, BFLAG_PERIPHERAL | 4, 0, NULL); +} + +U_BOOT_CMD( + spibootldr, 2, 0, do_spibootldr, + "boot ldr image from spi", + "[offset]\n" + " - boot ldr image stored at offset into spi\n"); diff --git a/qemu/roms/u-boot/common/cmd_spl.c b/qemu/roms/u-boot/common/cmd_spl.c new file mode 100644 index 000000000..057764aa8 --- /dev/null +++ b/qemu/roms/u-boot/common/cmd_spl.c @@ -0,0 +1,178 @@ +/* + * Copyright (C) 2011 + * Corscience GmbH & Co. KG - Simon Schwarz <schwarz@corscience.de> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <command.h> +#include <cmd_spl.h> + +DECLARE_GLOBAL_DATA_PTR; + +static const char **subcmd_list[] = { + + [SPL_EXPORT_FDT] = (const char * []) { +#ifdef CONFIG_OF_LIBFDT + "start", + "loados", + #ifdef CONFIG_SYS_BOOT_RAMDISK_HIGH + "ramdisk", + #endif + "fdt", + "cmdline", + "bdt", + "prep", +#endif + NULL, + }, + [SPL_EXPORT_ATAGS] = (const char * []) { +#if defined(CONFIG_SETUP_MEMORY_TAGS) || \ + defined(CONFIG_CMDLINE_TAG) || \ + defined(CONFIG_INITRD_TAG) || \ + defined(CONFIG_SERIAL_TAG) || \ + defined(CONFIG_REVISION_TAG) + "start", + "loados", +#ifdef CONFIG_SYS_BOOT_RAMDISK_HIGH + "ramdisk", +#endif + "cmdline", + "bdt", + "prep", +#endif + NULL, + }, + NULL +}; + +/* Calls bootm with the parameters given */ +static int call_bootm(int argc, char * const argv[], const char *subcommand[]) +{ + char *bootm_argv[5]; + + int i = 0; + int ret = 0; + int j; + + /* create paramter array */ + bootm_argv[0] = "do_bootm"; + switch (argc) { + case 3: + bootm_argv[4] = argv[2]; /* fdt addr */ + case 2: + bootm_argv[3] = argv[1]; /* initrd addr */ + case 1: + bootm_argv[2] = argv[0]; /* kernel addr */ + } + + + /* + * - do the work - + * exec subcommands of do_bootm to init the images + * data structure + */ + while (subcommand[i] != NULL) { + bootm_argv[1] = (char *)subcommand[i]; + debug("args %d: %s %s ", argc, bootm_argv[0], bootm_argv[1]); + for (j = 0; j < argc; j++) + debug("%s ", bootm_argv[j + 2]); + debug("\n"); + + ret = do_bootm(find_cmd("do_bootm"), 0, argc+2, + bootm_argv); + debug("Subcommand retcode: %d\n", ret); + i++; + } + + if (ret) { + printf("ERROR prep subcommand failed!\n"); + return -1; + } + + return 0; +} + +static cmd_tbl_t cmd_spl_export_sub[] = { + U_BOOT_CMD_MKENT(fdt, 0, 1, (void *)SPL_EXPORT_FDT, "", ""), + U_BOOT_CMD_MKENT(atags, 0, 1, (void *)SPL_EXPORT_ATAGS, "", ""), +}; + +static int spl_export(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + const cmd_tbl_t *c; + + if (argc < 2) /* no subcommand */ + return cmd_usage(cmdtp); + + c = find_cmd_tbl(argv[1], &cmd_spl_export_sub[0], + ARRAY_SIZE(cmd_spl_export_sub)); + if ((c) && ((int)c->cmd <= SPL_EXPORT_LAST)) { + argc -= 2; + argv += 2; + 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); + break; + } + } else { + /* Unrecognized command */ + return cmd_usage(cmdtp); + } + + return 0; +} + +static cmd_tbl_t cmd_spl_sub[] = { + U_BOOT_CMD_MKENT(export, 0, 1, (void *)SPL_EXPORT, "", ""), +}; + +static int do_spl(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + const cmd_tbl_t *c; + int cmd; + + if (argc < 2) /* no subcommand */ + return cmd_usage(cmdtp); + + c = find_cmd_tbl(argv[1], &cmd_spl_sub[0], ARRAY_SIZE(cmd_spl_sub)); + if (c) { + cmd = (int)c->cmd; + switch (cmd) { + case SPL_EXPORT: + argc--; + argv++; + if (spl_export(cmdtp, flag, argc, argv)) + printf("Subcommand failed\n"); + break; + default: + /* unrecognized command */ + return cmd_usage(cmdtp); + } + } else { + /* Unrecognized command */ + return cmd_usage(cmdtp); + } + return 0; +} + +U_BOOT_CMD( + spl, 6 , 1, do_spl, "SPL configuration", + "export <img=atags|fdt> [kernel_addr] [initrd_addr] [fdt_addr]\n" + "\timg\t\t\"atags\" or \"fdt\"\n" + "\tkernel_addr\taddress where a kernel image is stored.\n" + "\t\t\tkernel is loaded as part of the boot process, but it is not started.\n" + "\tinitrd_addr\taddress of initial ramdisk\n" + "\t\t\tcan be set to \"-\" if fdt_addr without initrd_addr is used.\n" + "\tfdt_addr\tin case of fdt, the address of the device tree.\n" + ); diff --git a/qemu/roms/u-boot/common/cmd_strings.c b/qemu/roms/u-boot/common/cmd_strings.c new file mode 100644 index 000000000..41b166592 --- /dev/null +++ b/qemu/roms/u-boot/common/cmd_strings.c @@ -0,0 +1,46 @@ +/* + * cmd_strings.c - just like `strings` command + * + * Copyright (c) 2008 Analog Devices Inc. + * + * Licensed under the GPL-2 or later. + */ + +#include <config.h> +#include <common.h> +#include <command.h> + +static char *start_addr, *last_addr; + +int do_strings(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + if (argc == 1) + return CMD_RET_USAGE; + + if ((flag & CMD_FLAG_REPEAT) == 0) { + start_addr = (char *)simple_strtoul(argv[1], NULL, 16); + if (argc > 2) + last_addr = (char *)simple_strtoul(argv[2], NULL, 16); + else + last_addr = (char *)-1; + } + + char *addr = start_addr; + do { + puts(addr); + puts("\n"); + addr += strlen(addr) + 1; + } while (addr[0] && addr < last_addr); + + last_addr = addr + (last_addr - start_addr); + start_addr = addr; + + return 0; +} + +U_BOOT_CMD( + strings, 3, 1, do_strings, + "display strings", + "<addr> [byte count]\n" + " - display strings at <addr> for at least [byte count] or first double NUL" +); diff --git a/qemu/roms/u-boot/common/cmd_terminal.c b/qemu/roms/u-boot/common/cmd_terminal.c new file mode 100644 index 000000000..4b9c0b34d --- /dev/null +++ b/qemu/roms/u-boot/common/cmd_terminal.c @@ -0,0 +1,76 @@ +/* + * (C) Copyright 2007 OpenMoko, Inc. + * Written by Harald Welte <laforge@openmoko.org> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/* + * Boot support + */ +#include <common.h> +#include <command.h> +#include <stdio_dev.h> +#include <serial.h> + +int do_terminal(cmd_tbl_t * cmd, int flag, int argc, char * const argv[]) +{ + int last_tilde = 0; + struct stdio_dev *dev = NULL; + + if (argc < 1) + return -1; + + /* Scan for selected output/input device */ + dev = stdio_get_by_name(argv[1]); + if (!dev) + return -1; + + serial_reinit_all(); + printf("Entering terminal mode for port %s\n", dev->name); + puts("Use '~.' to leave the terminal and get back to u-boot\n"); + + while (1) { + int c; + + /* read from console and display on serial port */ + if (stdio_devices[0]->tstc()) { + c = stdio_devices[0]->getc(); + if (last_tilde == 1) { + if (c == '.') { + putc(c); + putc('\n'); + break; + } else { + last_tilde = 0; + /* write the delayed tilde */ + dev->putc('~'); + /* fall-through to print current + * character */ + } + } + if (c == '~') { + last_tilde = 1; + puts("[u-boot]"); + putc(c); + } + dev->putc(c); + } + + /* read from serial port and display on console */ + if (dev->tstc()) { + c = dev->getc(); + putc(c); + } + } + return 0; +} + + +/***************************************************/ + +U_BOOT_CMD( + terminal, 3, 1, do_terminal, + "start terminal emulator", + "" +); diff --git a/qemu/roms/u-boot/common/cmd_test.c b/qemu/roms/u-boot/common/cmd_test.c new file mode 100644 index 000000000..c93fe7823 --- /dev/null +++ b/qemu/roms/u-boot/common/cmd_test.c @@ -0,0 +1,220 @@ +/* + * Copyright 2000-2009 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/* + * Define _STDBOOL_H here to avoid macro expansion of true and false. + * If the future code requires macro true or false, remove this define + * and undef true and false before U_BOOT_CMD. This define and comment + * shall be removed if change to U_BOOT_CMD is made to take string + * instead of stringifying it. + */ +#define _STDBOOL_H + +#include <common.h> +#include <command.h> +#include <fs.h> + +#define OP_INVALID 0 +#define OP_NOT 1 +#define OP_OR 2 +#define OP_AND 3 +#define OP_STR_EMPTY 4 +#define OP_STR_NEMPTY 5 +#define OP_STR_EQ 6 +#define OP_STR_NEQ 7 +#define OP_STR_LT 8 +#define OP_STR_GT 9 +#define OP_INT_EQ 10 +#define OP_INT_NEQ 11 +#define OP_INT_LT 12 +#define OP_INT_LE 13 +#define OP_INT_GT 14 +#define OP_INT_GE 15 +#define OP_FILE_EXISTS 16 + +const struct { + int arg; + const char *str; + int op; + int adv; +} op_adv[] = { + {1, "=", OP_STR_EQ, 3}, + {1, "!=", OP_STR_NEQ, 3}, + {1, "<", OP_STR_LT, 3}, + {1, ">", OP_STR_GT, 3}, + {1, "-eq", OP_INT_EQ, 3}, + {1, "-ne", OP_INT_NEQ, 3}, + {1, "-lt", OP_INT_LT, 3}, + {1, "-le", OP_INT_LE, 3}, + {1, "-gt", OP_INT_GT, 3}, + {1, "-ge", OP_INT_GE, 3}, + {0, "!", OP_NOT, 1}, + {0, "-o", OP_OR, 1}, + {0, "-a", OP_AND, 1}, + {0, "-z", OP_STR_EMPTY, 2}, + {0, "-n", OP_STR_NEMPTY, 2}, + {0, "-e", OP_FILE_EXISTS, 4}, +}; + +static int do_test(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + char * const *ap; + int i, op, left, adv, expr, last_expr, last_unop, last_binop; + + /* args? */ + if (argc < 3) + return 1; + +#ifdef DEBUG + { + debug("test(%d):", argc); + left = 1; + while (argv[left]) + debug(" '%s'", argv[left++]); + } +#endif + + left = argc - 1; + ap = argv + 1; + expr = 0; + last_unop = OP_INVALID; + last_binop = OP_INVALID; + last_expr = -1; + while (left > 0) { + for (i = 0; i < ARRAY_SIZE(op_adv); i++) { + if (left <= op_adv[i].arg) + continue; + if (!strcmp(ap[op_adv[i].arg], op_adv[i].str)) { + op = op_adv[i].op; + adv = op_adv[i].adv; + break; + } + } + if (i == ARRAY_SIZE(op_adv)) { + expr = 1; + break; + } + if (left < adv) { + expr = 1; + break; + } + + switch (op) { + case OP_STR_EMPTY: + expr = strlen(ap[1]) == 0 ? 1 : 0; + break; + case OP_STR_NEMPTY: + expr = strlen(ap[1]) == 0 ? 0 : 1; + break; + case OP_STR_EQ: + expr = strcmp(ap[0], ap[2]) == 0; + break; + case OP_STR_NEQ: + expr = strcmp(ap[0], ap[2]) != 0; + break; + case OP_STR_LT: + expr = strcmp(ap[0], ap[2]) < 0; + break; + case OP_STR_GT: + expr = strcmp(ap[0], ap[2]) > 0; + break; + case OP_INT_EQ: + expr = simple_strtol(ap[0], NULL, 10) == + simple_strtol(ap[2], NULL, 10); + break; + case OP_INT_NEQ: + expr = simple_strtol(ap[0], NULL, 10) != + simple_strtol(ap[2], NULL, 10); + break; + case OP_INT_LT: + expr = simple_strtol(ap[0], NULL, 10) < + simple_strtol(ap[2], NULL, 10); + break; + case OP_INT_LE: + expr = simple_strtol(ap[0], NULL, 10) <= + simple_strtol(ap[2], NULL, 10); + break; + case OP_INT_GT: + expr = simple_strtol(ap[0], NULL, 10) > + simple_strtol(ap[2], NULL, 10); + break; + case OP_INT_GE: + expr = simple_strtol(ap[0], NULL, 10) >= + simple_strtol(ap[2], NULL, 10); + break; + case OP_FILE_EXISTS: + expr = file_exists(ap[1], ap[2], ap[3], FS_TYPE_ANY); + break; + } + + switch (op) { + case OP_OR: + last_expr = expr; + last_binop = OP_OR; + break; + case OP_AND: + last_expr = expr; + last_binop = OP_AND; + break; + case OP_NOT: + if (last_unop == OP_NOT) + last_unop = OP_INVALID; + else + last_unop = OP_NOT; + break; + default: + if (last_unop == OP_NOT) { + expr = !expr; + last_unop = OP_INVALID; + } + + if (last_binop == OP_OR) + expr = last_expr || expr; + else if (last_binop == OP_AND) + expr = last_expr && expr; + last_binop = OP_INVALID; + + break; + } + + ap += adv; left -= adv; + } + + expr = !expr; + + debug (": returns %d\n", expr); + + return expr; +} + +U_BOOT_CMD( + test, CONFIG_SYS_MAXARGS, 1, do_test, + "minimal test like /bin/sh", + "[args..]" +); + +static int do_false(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + return 1; +} + +U_BOOT_CMD( + false, CONFIG_SYS_MAXARGS, 1, do_false, + "do nothing, unsuccessfully", + NULL +); + +static int do_true(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + return 0; +} + +U_BOOT_CMD( + true, CONFIG_SYS_MAXARGS, 1, do_true, + "do nothing, successfully", + NULL +); diff --git a/qemu/roms/u-boot/common/cmd_thordown.c b/qemu/roms/u-boot/common/cmd_thordown.c new file mode 100644 index 000000000..2dd750928 --- /dev/null +++ b/qemu/roms/u-boot/common/cmd_thordown.c @@ -0,0 +1,71 @@ +/* + * cmd_thordown.c -- USB TIZEN "THOR" Downloader gadget + * + * Copyright (C) 2013 Lukasz Majewski <l.majewski@samsung.com> + * All rights reserved. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <thor.h> +#include <dfu.h> +#include <g_dnl.h> +#include <usb.h> + +int do_thor_down(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + if (argc < 4) + return CMD_RET_USAGE; + + char *usb_controller = argv[1]; + char *interface = argv[2]; + char *devstring = argv[3]; + + int ret; + + puts("TIZEN \"THOR\" Downloader\n"); + + ret = dfu_init_env_entities(interface, simple_strtoul(devstring, + NULL, 10)); + if (ret) + return ret; + + int controller_index = simple_strtoul(usb_controller, NULL, 0); + ret = board_usb_init(controller_index, USB_INIT_DEVICE); + if (ret) { + error("USB init failed: %d", ret); + ret = CMD_RET_FAILURE; + goto exit; + } + + g_dnl_register("usb_dnl_thor"); + + ret = thor_init(); + if (ret) { + error("THOR DOWNLOAD failed: %d", ret); + ret = CMD_RET_FAILURE; + goto exit; + } + + ret = thor_handle(); + if (ret) { + error("THOR failed: %d", ret); + ret = CMD_RET_FAILURE; + goto exit; + } + +exit: + g_dnl_unregister(); + dfu_free_entities(); + + return ret; +} + +U_BOOT_CMD(thordown, CONFIG_SYS_MAXARGS, 1, do_thor_down, + "TIZEN \"THOR\" downloader", + "<USB_controller> <interface> <dev>\n" + " - device software upgrade via LTHOR TIZEN dowload\n" + " program via <USB_controller> on device <dev>,\n" + " attached to interface <interface>\n" +); diff --git a/qemu/roms/u-boot/common/cmd_time.c b/qemu/roms/u-boot/common/cmd_time.c new file mode 100644 index 000000000..de57e3b9d --- /dev/null +++ b/qemu/roms/u-boot/common/cmd_time.c @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2011 The Chromium OS Authors. + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <command.h> + +static void report_time(ulong cycles) +{ + ulong minutes, seconds, milliseconds; + ulong total_seconds, remainder; + + total_seconds = cycles / CONFIG_SYS_HZ; + remainder = cycles % CONFIG_SYS_HZ; + minutes = total_seconds / 60; + seconds = total_seconds % 60; + /* approximate millisecond value */ + milliseconds = (remainder * 1000 + CONFIG_SYS_HZ / 2) / CONFIG_SYS_HZ; + + printf("\ntime:"); + if (minutes) + printf(" %lu minutes,", minutes); + printf(" %lu.%03lu seconds\n", seconds, milliseconds); +} + +static int do_time(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + ulong cycles = 0; + int retval = 0; + int repeatable; + + if (argc == 1) + return CMD_RET_USAGE; + + retval = cmd_process(0, argc - 1, argv + 1, &repeatable, &cycles); + report_time(cycles); + + return retval; +} + +U_BOOT_CMD(time, CONFIG_SYS_MAXARGS, 0, do_time, + "run commands and summarize execution time", + "command [args...]\n"); diff --git a/qemu/roms/u-boot/common/cmd_tpm.c b/qemu/roms/u-boot/common/cmd_tpm.c new file mode 100644 index 000000000..0294952e6 --- /dev/null +++ b/qemu/roms/u-boot/common/cmd_tpm.c @@ -0,0 +1,757 @@ +/* + * Copyright (c) 2013 The Chromium OS Authors. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <command.h> +#include <malloc.h> +#include <tpm.h> +#include <asm/unaligned.h> +#include <linux/string.h> + +/* Useful constants */ +enum { + DIGEST_LENGTH = 20, + /* max lengths, valid for RSA keys <= 2048 bits */ + TPM_PUBKEY_MAX_LENGTH = 288, +}; + +/** + * Print a byte string in hexdecimal format, 16-bytes per line. + * + * @param data byte string to be printed + * @param count number of bytes to be printed + */ +static void print_byte_string(uint8_t *data, size_t count) +{ + int i, print_newline = 0; + + for (i = 0; i < count; i++) { + printf(" %02x", data[i]); + print_newline = (i % 16 == 15); + if (print_newline) + putc('\n'); + } + /* Avoid duplicated newline at the end */ + if (!print_newline) + putc('\n'); +} + +/** + * Convert a text string of hexdecimal values into a byte string. + * + * @param bytes text string of hexdecimal values with no space + * between them + * @param data output buffer for byte string. The caller has to make + * sure it is large enough for storing the output. If + * NULL is passed, a large enough buffer will be allocated, + * and the caller must free it. + * @param count_ptr output variable for the length of byte string + * @return pointer to output buffer + */ +static void *parse_byte_string(char *bytes, uint8_t *data, size_t *count_ptr) +{ + char byte[3]; + size_t count, length; + int i; + + length = strlen(bytes); + count = length / 2; + + if (!data) + data = malloc(count); + if (!data) + return NULL; + + byte[2] = '\0'; + for (i = 0; i < length; i += 2) { + byte[0] = bytes[i]; + byte[1] = bytes[i + 1]; + data[i / 2] = (uint8_t)simple_strtoul(byte, NULL, 16); + } + + if (count_ptr) + *count_ptr = count; + + return data; +} + +/** + * Convert TPM command return code to U-Boot command error codes. + * + * @param return_code TPM command return code + * @return value of enum command_ret_t + */ +static int convert_return_code(uint32_t return_code) +{ + if (return_code) + return CMD_RET_FAILURE; + else + return CMD_RET_SUCCESS; +} + +/** + * Return number of values defined by a type string. + * + * @param type_str type string + * @return number of values of type string + */ +static int type_string_get_num_values(const char *type_str) +{ + return strlen(type_str); +} + +/** + * Return total size of values defined by a type string. + * + * @param type_str type string + * @return total size of values of type string, or 0 if type string + * contains illegal type character. + */ +static size_t type_string_get_space_size(const char *type_str) +{ + size_t size; + + for (size = 0; *type_str; type_str++) { + switch (*type_str) { + case 'b': + size += 1; + break; + case 'w': + size += 2; + break; + case 'd': + size += 4; + break; + default: + return 0; + } + } + + return size; +} + +/** + * Allocate a buffer large enough to hold values defined by a type + * string. The caller has to free the buffer. + * + * @param type_str type string + * @param count pointer for storing size of buffer + * @return pointer to buffer or NULL on error + */ +static void *type_string_alloc(const char *type_str, uint32_t *count) +{ + void *data; + size_t size; + + size = type_string_get_space_size(type_str); + if (!size) + return NULL; + data = malloc(size); + if (data) + *count = size; + + return data; +} + +/** + * Pack values defined by a type string into a buffer. The buffer must have + * large enough space. + * + * @param type_str type string + * @param values text strings of values to be packed + * @param data output buffer of values + * @return 0 on success, non-0 on error + */ +static int type_string_pack(const char *type_str, char * const values[], + uint8_t *data) +{ + size_t offset; + uint32_t value; + + for (offset = 0; *type_str; type_str++, values++) { + value = simple_strtoul(values[0], NULL, 0); + switch (*type_str) { + case 'b': + data[offset] = value; + offset += 1; + break; + case 'w': + put_unaligned_be16(value, data + offset); + offset += 2; + break; + case 'd': + put_unaligned_be32(value, data + offset); + offset += 4; + break; + default: + return -1; + } + } + + return 0; +} + +/** + * Read values defined by a type string from a buffer, and write these values + * to environment variables. + * + * @param type_str type string + * @param data input buffer of values + * @param vars names of environment variables + * @return 0 on success, non-0 on error + */ +static int type_string_write_vars(const char *type_str, uint8_t *data, + char * const vars[]) +{ + size_t offset; + uint32_t value; + + for (offset = 0; *type_str; type_str++, vars++) { + switch (*type_str) { + case 'b': + value = data[offset]; + offset += 1; + break; + case 'w': + value = get_unaligned_be16(data + offset); + offset += 2; + break; + case 'd': + value = get_unaligned_be32(data + offset); + offset += 4; + break; + default: + return -1; + } + if (setenv_ulong(*vars, value)) + return -1; + } + + return 0; +} + +static int do_tpm_startup(cmd_tbl_t *cmdtp, int flag, + int argc, char * const argv[]) +{ + enum tpm_startup_type mode; + + if (argc != 2) + return CMD_RET_USAGE; + if (!strcasecmp("TPM_ST_CLEAR", argv[1])) { + mode = TPM_ST_CLEAR; + } else if (!strcasecmp("TPM_ST_STATE", argv[1])) { + mode = TPM_ST_STATE; + } else if (!strcasecmp("TPM_ST_DEACTIVATED", argv[1])) { + mode = TPM_ST_DEACTIVATED; + } else { + printf("Couldn't recognize mode string: %s\n", argv[1]); + return CMD_RET_FAILURE; + } + + return convert_return_code(tpm_startup(mode)); +} + +static int do_tpm_nv_define_space(cmd_tbl_t *cmdtp, int flag, + int argc, char * const argv[]) +{ + uint32_t index, perm, size; + + if (argc != 4) + return CMD_RET_USAGE; + index = simple_strtoul(argv[1], NULL, 0); + perm = simple_strtoul(argv[2], NULL, 0); + size = simple_strtoul(argv[3], NULL, 0); + + return convert_return_code(tpm_nv_define_space(index, perm, size)); +} + +static int do_tpm_nv_read_value(cmd_tbl_t *cmdtp, int flag, + int argc, char * const argv[]) +{ + uint32_t index, count, rc; + void *data; + + if (argc != 4) + return CMD_RET_USAGE; + index = simple_strtoul(argv[1], NULL, 0); + data = (void *)simple_strtoul(argv[2], NULL, 0); + count = simple_strtoul(argv[3], NULL, 0); + + rc = tpm_nv_read_value(index, data, count); + if (!rc) { + puts("area content:\n"); + print_byte_string(data, count); + } + + return convert_return_code(rc); +} + +static int do_tpm_nv_write_value(cmd_tbl_t *cmdtp, int flag, + int argc, char * const argv[]) +{ + uint32_t index, rc; + size_t count; + void *data; + + if (argc != 3) + return CMD_RET_USAGE; + index = simple_strtoul(argv[1], NULL, 0); + data = parse_byte_string(argv[2], NULL, &count); + if (!data) { + printf("Couldn't parse byte string %s\n", argv[2]); + return CMD_RET_FAILURE; + } + + rc = tpm_nv_write_value(index, data, count); + free(data); + + return convert_return_code(rc); +} + +static int do_tpm_extend(cmd_tbl_t *cmdtp, int flag, + int argc, char * const argv[]) +{ + uint32_t index, rc; + uint8_t in_digest[20], out_digest[20]; + + if (argc != 3) + return CMD_RET_USAGE; + index = simple_strtoul(argv[1], NULL, 0); + if (!parse_byte_string(argv[2], in_digest, NULL)) { + printf("Couldn't parse byte string %s\n", argv[2]); + return CMD_RET_FAILURE; + } + + rc = tpm_extend(index, in_digest, out_digest); + if (!rc) { + puts("PCR value after execution of the command:\n"); + print_byte_string(out_digest, sizeof(out_digest)); + } + + return convert_return_code(rc); +} + +static int do_tpm_pcr_read(cmd_tbl_t *cmdtp, int flag, + int argc, char * const argv[]) +{ + uint32_t index, count, rc; + void *data; + + if (argc != 4) + return CMD_RET_USAGE; + index = simple_strtoul(argv[1], NULL, 0); + data = (void *)simple_strtoul(argv[2], NULL, 0); + count = simple_strtoul(argv[3], NULL, 0); + + rc = tpm_pcr_read(index, data, count); + if (!rc) { + puts("Named PCR content:\n"); + print_byte_string(data, count); + } + + return convert_return_code(rc); +} + +static int do_tpm_tsc_physical_presence(cmd_tbl_t *cmdtp, int flag, + int argc, char * const argv[]) +{ + uint16_t presence; + + if (argc != 2) + return CMD_RET_USAGE; + presence = (uint16_t)simple_strtoul(argv[1], NULL, 0); + + return convert_return_code(tpm_tsc_physical_presence(presence)); +} + +static int do_tpm_read_pubek(cmd_tbl_t *cmdtp, int flag, + int argc, char * const argv[]) +{ + uint32_t count, rc; + void *data; + + if (argc != 3) + return CMD_RET_USAGE; + data = (void *)simple_strtoul(argv[1], NULL, 0); + count = simple_strtoul(argv[2], NULL, 0); + + rc = tpm_read_pubek(data, count); + if (!rc) { + puts("pubek value:\n"); + print_byte_string(data, count); + } + + return convert_return_code(rc); +} + +static int do_tpm_physical_set_deactivated(cmd_tbl_t *cmdtp, int flag, + int argc, char * const argv[]) +{ + uint8_t state; + + if (argc != 2) + return CMD_RET_USAGE; + state = (uint8_t)simple_strtoul(argv[1], NULL, 0); + + return convert_return_code(tpm_physical_set_deactivated(state)); +} + +static int do_tpm_get_capability(cmd_tbl_t *cmdtp, int flag, + int argc, char * const argv[]) +{ + uint32_t cap_area, sub_cap, rc; + void *cap; + size_t count; + + if (argc != 5) + return CMD_RET_USAGE; + cap_area = simple_strtoul(argv[1], NULL, 0); + sub_cap = simple_strtoul(argv[2], NULL, 0); + cap = (void *)simple_strtoul(argv[3], NULL, 0); + count = simple_strtoul(argv[4], NULL, 0); + + rc = tpm_get_capability(cap_area, sub_cap, cap, count); + if (!rc) { + puts("capability information:\n"); + print_byte_string(cap, count); + } + + return convert_return_code(rc); +} + +#define TPM_COMMAND_NO_ARG(cmd) \ +static int do_##cmd(cmd_tbl_t *cmdtp, int flag, \ + int argc, char * const argv[]) \ +{ \ + if (argc != 1) \ + return CMD_RET_USAGE; \ + return convert_return_code(cmd()); \ +} + +TPM_COMMAND_NO_ARG(tpm_init) +TPM_COMMAND_NO_ARG(tpm_self_test_full) +TPM_COMMAND_NO_ARG(tpm_continue_self_test) +TPM_COMMAND_NO_ARG(tpm_force_clear) +TPM_COMMAND_NO_ARG(tpm_physical_enable) +TPM_COMMAND_NO_ARG(tpm_physical_disable) + +static int do_tpm_raw_transfer(cmd_tbl_t *cmdtp, int flag, + int argc, char * const argv[]) +{ + void *command; + uint8_t response[1024]; + size_t count, response_length = sizeof(response); + uint32_t rc; + + command = parse_byte_string(argv[1], NULL, &count); + if (!command) { + printf("Couldn't parse byte string %s\n", argv[1]); + return CMD_RET_FAILURE; + } + + rc = tis_sendrecv(command, count, response, &response_length); + free(command); + if (!rc) { + puts("tpm response:\n"); + print_byte_string(response, response_length); + } + + return convert_return_code(rc); +} + +static int do_tpm_nv_define(cmd_tbl_t *cmdtp, int flag, + int argc, char * const argv[]) +{ + uint32_t index, perm, size; + + if (argc != 4) + return CMD_RET_USAGE; + size = type_string_get_space_size(argv[1]); + if (!size) { + printf("Couldn't parse arguments\n"); + return CMD_RET_USAGE; + } + index = simple_strtoul(argv[2], NULL, 0); + perm = simple_strtoul(argv[3], NULL, 0); + + return convert_return_code(tpm_nv_define_space(index, perm, size)); +} + +static int do_tpm_nv_read(cmd_tbl_t *cmdtp, int flag, + int argc, char * const argv[]) +{ + uint32_t index, count, err; + void *data; + + if (argc < 3) + return CMD_RET_USAGE; + if (argc != 3 + type_string_get_num_values(argv[1])) + return CMD_RET_USAGE; + index = simple_strtoul(argv[2], NULL, 0); + data = type_string_alloc(argv[1], &count); + if (!data) { + printf("Couldn't parse arguments\n"); + return CMD_RET_USAGE; + } + + err = tpm_nv_read_value(index, data, count); + if (!err) { + if (type_string_write_vars(argv[1], data, argv + 3)) { + printf("Couldn't write to variables\n"); + err = ~0; + } + } + free(data); + + return convert_return_code(err); +} + +static int do_tpm_nv_write(cmd_tbl_t *cmdtp, int flag, + int argc, char * const argv[]) +{ + uint32_t index, count, err; + void *data; + + if (argc < 3) + return CMD_RET_USAGE; + if (argc != 3 + type_string_get_num_values(argv[1])) + return CMD_RET_USAGE; + index = simple_strtoul(argv[2], NULL, 0); + data = type_string_alloc(argv[1], &count); + if (!data) { + printf("Couldn't parse arguments\n"); + return CMD_RET_USAGE; + } + if (type_string_pack(argv[1], argv + 3, data)) { + printf("Couldn't parse arguments\n"); + free(data); + return CMD_RET_USAGE; + } + + err = tpm_nv_write_value(index, data, count); + free(data); + + return convert_return_code(err); +} + +#ifdef CONFIG_TPM_AUTH_SESSIONS + +static int do_tpm_oiap(cmd_tbl_t *cmdtp, int flag, + int argc, char * const argv[]) +{ + uint32_t auth_handle, err; + + err = tpm_oiap(&auth_handle); + + return convert_return_code(err); +} + +static int do_tpm_load_key2_oiap(cmd_tbl_t *cmdtp, int flag, + int argc, char * const argv[]) +{ + uint32_t parent_handle, key_len, key_handle, err; + uint8_t usage_auth[DIGEST_LENGTH]; + void *key; + + if (argc < 5) + return CMD_RET_USAGE; + + parent_handle = simple_strtoul(argv[1], NULL, 0); + key = (void *)simple_strtoul(argv[2], NULL, 0); + key_len = simple_strtoul(argv[3], NULL, 0); + if (strlen(argv[4]) != 2 * DIGEST_LENGTH) + return CMD_RET_FAILURE; + parse_byte_string(argv[4], usage_auth, NULL); + + err = tpm_load_key2_oiap(parent_handle, key, key_len, usage_auth, + &key_handle); + if (!err) + printf("Key handle is 0x%x\n", key_handle); + + return convert_return_code(err); +} + +static int do_tpm_get_pub_key_oiap(cmd_tbl_t *cmdtp, int flag, + int argc, char * const argv[]) +{ + uint32_t key_handle, err; + uint8_t usage_auth[DIGEST_LENGTH]; + uint8_t pub_key_buffer[TPM_PUBKEY_MAX_LENGTH]; + size_t pub_key_len = sizeof(pub_key_buffer); + + if (argc < 3) + return CMD_RET_USAGE; + + key_handle = simple_strtoul(argv[1], NULL, 0); + if (strlen(argv[2]) != 2 * DIGEST_LENGTH) + return CMD_RET_FAILURE; + parse_byte_string(argv[2], usage_auth, NULL); + + err = tpm_get_pub_key_oiap(key_handle, usage_auth, + pub_key_buffer, &pub_key_len); + if (!err) { + printf("dump of received pub key structure:\n"); + print_byte_string(pub_key_buffer, pub_key_len); + } + return convert_return_code(err); +} + +TPM_COMMAND_NO_ARG(tpm_end_oiap) + +#endif /* CONFIG_TPM_AUTH_SESSIONS */ + +#define MAKE_TPM_CMD_ENTRY(cmd) \ + U_BOOT_CMD_MKENT(cmd, 0, 1, do_tpm_ ## cmd, "", "") + +static cmd_tbl_t tpm_commands[] = { + U_BOOT_CMD_MKENT(init, 0, 1, + do_tpm_init, "", ""), + U_BOOT_CMD_MKENT(startup, 0, 1, + do_tpm_startup, "", ""), + U_BOOT_CMD_MKENT(self_test_full, 0, 1, + do_tpm_self_test_full, "", ""), + U_BOOT_CMD_MKENT(continue_self_test, 0, 1, + do_tpm_continue_self_test, "", ""), + U_BOOT_CMD_MKENT(force_clear, 0, 1, + do_tpm_force_clear, "", ""), + U_BOOT_CMD_MKENT(physical_enable, 0, 1, + do_tpm_physical_enable, "", ""), + U_BOOT_CMD_MKENT(physical_disable, 0, 1, + do_tpm_physical_disable, "", ""), + U_BOOT_CMD_MKENT(nv_define_space, 0, 1, + do_tpm_nv_define_space, "", ""), + U_BOOT_CMD_MKENT(nv_read_value, 0, 1, + do_tpm_nv_read_value, "", ""), + U_BOOT_CMD_MKENT(nv_write_value, 0, 1, + do_tpm_nv_write_value, "", ""), + U_BOOT_CMD_MKENT(extend, 0, 1, + do_tpm_extend, "", ""), + U_BOOT_CMD_MKENT(pcr_read, 0, 1, + do_tpm_pcr_read, "", ""), + U_BOOT_CMD_MKENT(tsc_physical_presence, 0, 1, + do_tpm_tsc_physical_presence, "", ""), + U_BOOT_CMD_MKENT(read_pubek, 0, 1, + do_tpm_read_pubek, "", ""), + U_BOOT_CMD_MKENT(physical_set_deactivated, 0, 1, + do_tpm_physical_set_deactivated, "", ""), + U_BOOT_CMD_MKENT(get_capability, 0, 1, + do_tpm_get_capability, "", ""), + U_BOOT_CMD_MKENT(raw_transfer, 0, 1, + do_tpm_raw_transfer, "", ""), + U_BOOT_CMD_MKENT(nv_define, 0, 1, + do_tpm_nv_define, "", ""), + U_BOOT_CMD_MKENT(nv_read, 0, 1, + do_tpm_nv_read, "", ""), + U_BOOT_CMD_MKENT(nv_write, 0, 1, + do_tpm_nv_write, "", ""), +#ifdef CONFIG_TPM_AUTH_SESSIONS + U_BOOT_CMD_MKENT(oiap, 0, 1, + do_tpm_oiap, "", ""), + U_BOOT_CMD_MKENT(end_oiap, 0, 1, + do_tpm_end_oiap, "", ""), + U_BOOT_CMD_MKENT(load_key2_oiap, 0, 1, + do_tpm_load_key2_oiap, "", ""), + U_BOOT_CMD_MKENT(get_pub_key_oiap, 0, 1, + do_tpm_get_pub_key_oiap, "", ""), +#endif /* CONFIG_TPM_AUTH_SESSIONS */ +}; + +static int do_tpm(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + cmd_tbl_t *tpm_cmd; + + if (argc < 2) + return CMD_RET_USAGE; + tpm_cmd = find_cmd_tbl(argv[1], tpm_commands, ARRAY_SIZE(tpm_commands)); + if (!tpm_cmd) + return CMD_RET_USAGE; + + return tpm_cmd->cmd(cmdtp, flag, argc - 1, argv + 1); +} + +U_BOOT_CMD(tpm, CONFIG_SYS_MAXARGS, 1, do_tpm, +"Issue a TPM command", +"cmd args...\n" +" - Issue TPM command <cmd> with arguments <args...>.\n" +"Admin Startup and State Commands:\n" +" init\n" +" - Put TPM into a state where it waits for 'startup' command.\n" +" startup mode\n" +" - Issue TPM_Starup command. <mode> is one of TPM_ST_CLEAR,\n" +" TPM_ST_STATE, and TPM_ST_DEACTIVATED.\n" +"Admin Testing Commands:\n" +" self_test_full\n" +" - Test all of the TPM capabilities.\n" +" continue_self_test\n" +" - Inform TPM that it should complete the self-test.\n" +"Admin Opt-in Commands:\n" +" physical_enable\n" +" - Set the PERMANENT disable flag to FALSE using physical presence as\n" +" authorization.\n" +" physical_disable\n" +" - Set the PERMANENT disable flag to TRUE using physical presence as\n" +" authorization.\n" +" physical_set_deactivated 0|1\n" +" - Set deactivated flag.\n" +"Admin Ownership Commands:\n" +" force_clear\n" +" - Issue TPM_ForceClear command.\n" +" tsc_physical_presence flags\n" +" - Set TPM device's Physical Presence flags to <flags>.\n" +"The Capability Commands:\n" +" get_capability cap_area sub_cap addr count\n" +" - Read <count> bytes of TPM capability indexed by <cap_area> and\n" +" <sub_cap> to memory address <addr>.\n" +#ifdef CONFIG_TPM_AUTH_SESSIONS +"Storage functions\n" +" loadkey2_oiap parent_handle key_addr key_len usage_auth\n" +" - loads a key data from memory address <key_addr>, <key_len> bytes\n" +" into TPM using the parent key <parent_handle> with authorization\n" +" <usage_auth> (20 bytes hex string).\n" +" get_pub_key_oiap key_handle usage_auth\n" +" - get the public key portion of a loaded key <key_handle> using\n" +" authorization <usage auth> (20 bytes hex string)\n" +#endif /* CONFIG_TPM_AUTH_SESSIONS */ +"Endorsement Key Handling Commands:\n" +" read_pubek addr count\n" +" - Read <count> bytes of the public endorsement key to memory\n" +" address <addr>\n" +"Integrity Collection and Reporting Commands:\n" +" extend index digest_hex_string\n" +" - Add a new measurement to a PCR. Update PCR <index> with the 20-bytes\n" +" <digest_hex_string>\n" +" pcr_read index addr count\n" +" - Read <count> bytes from PCR <index> to memory address <addr>.\n" +#ifdef CONFIG_TPM_AUTH_SESSIONS +"Authorization Sessions\n" +" oiap\n" +" - setup an OIAP session\n" +" end_oiap\n" +" - terminates an active OIAP session\n" +#endif /* CONFIG_TPM_AUTH_SESSIONS */ +"Non-volatile Storage Commands:\n" +" nv_define_space index permission size\n" +" - Establish a space at index <index> with <permission> of <size> bytes.\n" +" nv_read_value index addr count\n" +" - Read <count> bytes from space <index> to memory address <addr>.\n" +" nv_write_value index addr count\n" +" - Write <count> bytes from memory address <addr> to space <index>.\n" +"Miscellaneous helper functions:\n" +" raw_transfer byte_string\n" +" - Send a byte string <byte_string> to TPM and print the response.\n" +" Non-volatile storage helper functions:\n" +" These helper functions treat a non-volatile space as a non-padded\n" +" sequence of integer values. These integer values are defined by a type\n" +" string, which is a text string of 'bwd' characters: 'b' means a 8-bit\n" +" value, 'w' 16-bit value, 'd' 32-bit value. All helper functions take\n" +" a type string as their first argument.\n" +" nv_define type_string index perm\n" +" - Define a space <index> with permission <perm>.\n" +" nv_read types_string index vars...\n" +" - Read from space <index> to environment variables <vars...>.\n" +" nv_write types_string index values...\n" +" - Write to space <index> from values <values...>.\n" +); diff --git a/qemu/roms/u-boot/common/cmd_trace.c b/qemu/roms/u-boot/common/cmd_trace.c new file mode 100644 index 000000000..8c630e6a8 --- /dev/null +++ b/qemu/roms/u-boot/common/cmd_trace.c @@ -0,0 +1,120 @@ +/* + * Copyright (c) 2011 The Chromium OS Authors. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <command.h> +#include <trace.h> +#include <asm/io.h> + +static int get_args(int argc, char * const argv[], char **buff, + size_t *buff_ptr, size_t *buff_size) +{ + if (argc < 2) + return -1; + if (argc < 4) { + *buff_size = getenv_ulong("profsize", 16, 0); + *buff = map_sysmem(getenv_ulong("profbase", 16, 0), + *buff_size); + *buff_ptr = getenv_ulong("profoffset", 16, 0); + } else { + *buff_size = simple_strtoul(argv[3], NULL, 16); + *buff = map_sysmem(simple_strtoul(argv[2], NULL, 16), + *buff_size); + *buff_ptr = 0; + }; + return 0; +} + +static int create_func_list(int argc, char * const argv[]) +{ + size_t buff_size, avail, buff_ptr, used; + unsigned int needed; + char *buff; + int err; + + if (get_args(argc, argv, &buff, &buff_ptr, &buff_size)) + return -1; + + avail = buff_size - buff_ptr; + err = trace_list_functions(buff + buff_ptr, avail, &needed); + if (err) + printf("Error: truncated (%#x bytes needed)\n", needed); + used = min(avail, needed); + printf("Function trace dumped to %08lx, size %#zx\n", + (ulong)map_to_sysmem(buff + buff_ptr), used); + setenv_hex("profbase", map_to_sysmem(buff)); + setenv_hex("profsize", buff_size); + setenv_hex("profoffset", buff_ptr + used); + + return 0; +} + +static int create_call_list(int argc, char * const argv[]) +{ + size_t buff_size, avail, buff_ptr, used; + unsigned int needed; + char *buff; + int err; + + if (get_args(argc, argv, &buff, &buff_ptr, &buff_size)) + return -1; + + avail = buff_size - buff_ptr; + err = trace_list_calls(buff + buff_ptr, avail, &needed); + if (err) + printf("Error: truncated (%#x bytes needed)\n", needed); + used = min(avail, needed); + printf("Call list dumped to %08lx, size %#zx\n", + (ulong)map_to_sysmem(buff + buff_ptr), used); + + setenv_hex("profbase", map_to_sysmem(buff)); + setenv_hex("profsize", buff_size); + setenv_hex("profoffset", buff_ptr + used); + + return 0; +} + +int do_trace(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + const char *cmd = argc < 2 ? NULL : argv[1]; + + if (!cmd) + return cmd_usage(cmdtp); + switch (*cmd) { + case 'p': + trace_set_enabled(0); + break; + case 'c': + if (create_call_list(argc, argv)) + return cmd_usage(cmdtp); + break; + case 'r': + trace_set_enabled(1); + break; + case 'f': + if (create_func_list(argc, argv)) + return cmd_usage(cmdtp); + break; + case 's': + trace_print_stats(); + break; + default: + return CMD_RET_USAGE; + } + + return 0; +} + +U_BOOT_CMD( + trace, 4, 1, do_trace, + "trace utility commands", + "stats - display tracing statistics\n" + "trace pause - pause tracing\n" + "trace resume - resume tracing\n" + "trace funclist [<addr> <size>] - dump function list into buffer\n" + "trace calls [<addr> <size>] " + "- dump function call trace into buffer" +); diff --git a/qemu/roms/u-boot/common/cmd_tsi148.c b/qemu/roms/u-boot/common/cmd_tsi148.c new file mode 100644 index 000000000..dc488b2b3 --- /dev/null +++ b/qemu/roms/u-boot/common/cmd_tsi148.c @@ -0,0 +1,472 @@ +/* + * (C) Copyright 2009 Reinhard Arlt, reinhard.arlt@esd-electronics.com + * + * base on universe.h by + * + * (C) Copyright 2003 Stefan Roese, stefan.roese@esd-electronics.com + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <command.h> +#include <malloc.h> +#include <asm/io.h> +#include <pci.h> + +#include <tsi148.h> + +#define PCI_VENDOR PCI_VENDOR_ID_TUNDRA +#define PCI_DEVICE PCI_DEVICE_ID_TUNDRA_TSI148 + +typedef struct _TSI148_DEV TSI148_DEV; + +struct _TSI148_DEV { + int bus; + pci_dev_t busdevfn; + TSI148 *uregs; + unsigned int pci_bs; +}; + +static TSI148_DEV *dev; + +/* + * Most of the TSI148 register are BIGENDIAN + * This is the reason for the __raw_writel(htonl(x), x) usage! + */ + +int tsi148_init(void) +{ + int j, result; + pci_dev_t busdevfn; + unsigned int val; + + busdevfn = pci_find_device(PCI_VENDOR, PCI_DEVICE, 0); + if (busdevfn == -1) { + puts("Tsi148: No Tundra Tsi148 found!\n"); + return -1; + } + + /* Lets turn Latency off */ + pci_write_config_dword(busdevfn, 0x0c, 0); + + dev = malloc(sizeof(*dev)); + if (NULL == dev) { + puts("Tsi148: No memory!\n"); + return -1; + } + + memset(dev, 0, sizeof(*dev)); + dev->busdevfn = busdevfn; + + pci_read_config_dword(busdevfn, PCI_BASE_ADDRESS_0, &val); + val &= ~0xf; + dev->uregs = (TSI148 *)val; + + debug("Tsi148: Base : %p\n", dev->uregs); + + /* check mapping */ + debug("Tsi148: Read via mapping, PCI_ID = %08X\n", + readl(&dev->uregs->pci_id)); + if (((PCI_DEVICE << 16) | PCI_VENDOR) != readl(&dev->uregs->pci_id)) { + printf("Tsi148: Cannot read PCI-ID via Mapping: %08x\n", + readl(&dev->uregs->pci_id)); + result = -1; + goto break_30; + } + + debug("Tsi148: PCI_BS = %08X\n", readl(&dev->uregs->pci_mbarl)); + + dev->pci_bs = readl(&dev->uregs->pci_mbarl); + + /* turn off windows */ + for (j = 0; j < 8; j++) { + __raw_writel(htonl(0x00000000), &dev->uregs->outbound[j].otat); + __raw_writel(htonl(0x00000000), &dev->uregs->inbound[j].itat); + } + + /* Tsi148 VME timeout etc */ + __raw_writel(htonl(0x00000084), &dev->uregs->vctrl); + +#ifdef DEBUG + if ((__raw_readl(&dev->uregs->vstat) & 0x00000100) != 0) + printf("Tsi148: System Controller!\n"); + else + printf("Tsi148: Not System Controller!\n"); +#endif + + /* + * Lets turn off interrupts + */ + /* Disable interrupts in Tsi148 first */ + __raw_writel(htonl(0x00000000), &dev->uregs->inten); + /* Disable interrupt out */ + __raw_writel(htonl(0x00000000), &dev->uregs->inteo); + eieio(); + /* Reset all IRQ's */ + __raw_writel(htonl(0x03ff3f00), &dev->uregs->intc); + /* Map all ints to 0 */ + __raw_writel(htonl(0x00000000), &dev->uregs->intm1); + __raw_writel(htonl(0x00000000), &dev->uregs->intm2); + eieio(); + + val = __raw_readl(&dev->uregs->vstat); + val &= ~(0x00004000); + __raw_writel(val, &dev->uregs->vstat); + eieio(); + + debug("Tsi148: register struct size %08x\n", sizeof(TSI148)); + + return 0; + + break_30: + free(dev); + dev = NULL; + + return result; +} + +/* + * Create pci slave window (access: pci -> vme) + */ +int tsi148_pci_slave_window(unsigned int pciAddr, unsigned int vmeAddr, + int size, int vam, int vdw) +{ + int result, i; + unsigned int ctl = 0; + + if (NULL == dev) { + result = -1; + goto exit_10; + } + + for (i = 0; i < 8; i++) { + if (0x00000000 == readl(&dev->uregs->outbound[i].otat)) + break; + } + + if (i > 7) { + printf("Tsi148: No Image available\n"); + result = -1; + goto exit_10; + } + + debug("Tsi148: Using image %d\n", i); + + printf("Tsi148: Pci addr %08x\n", pciAddr); + + __raw_writel(htonl(pciAddr), &dev->uregs->outbound[i].otsal); + __raw_writel(0x00000000, &dev->uregs->outbound[i].otsau); + __raw_writel(htonl(pciAddr + size), &dev->uregs->outbound[i].oteal); + __raw_writel(0x00000000, &dev->uregs->outbound[i].oteau); + __raw_writel(htonl(vmeAddr - pciAddr), &dev->uregs->outbound[i].otofl); + __raw_writel(0x00000000, &dev->uregs->outbound[i].otofu); + + switch (vam & VME_AM_Axx) { + case VME_AM_A16: + ctl = 0x00000000; + break; + case VME_AM_A24: + ctl = 0x00000001; + break; + case VME_AM_A32: + ctl = 0x00000002; + break; + } + + switch (vam & VME_AM_Mxx) { + case VME_AM_DATA: + ctl |= 0x00000000; + break; + case VME_AM_PROG: + ctl |= 0x00000010; + break; + } + + if (vam & VME_AM_SUP) + ctl |= 0x00000020; + + switch (vdw & VME_FLAG_Dxx) { + case VME_FLAG_D16: + ctl |= 0x00000000; + break; + case VME_FLAG_D32: + ctl |= 0x00000040; + break; + } + + ctl |= 0x80040000; /* enable, no prefetch */ + + __raw_writel(htonl(ctl), &dev->uregs->outbound[i].otat); + + debug("Tsi148: window-addr =%p\n", + &dev->uregs->outbound[i].otsau); + debug("Tsi148: pci slave window[%d] attr =%08x\n", + i, ntohl(__raw_readl(&dev->uregs->outbound[i].otat))); + debug("Tsi148: pci slave window[%d] start =%08x\n", + i, ntohl(__raw_readl(&dev->uregs->outbound[i].otsal))); + debug("Tsi148: pci slave window[%d] end =%08x\n", + i, ntohl(__raw_readl(&dev->uregs->outbound[i].oteal))); + debug("Tsi148: pci slave window[%d] offset=%08x\n", + i, ntohl(__raw_readl(&dev->uregs->outbound[i].otofl))); + + return 0; + + exit_10: + return -result; +} + +unsigned int tsi148_eval_vam(int vam) +{ + unsigned int ctl = 0; + + switch (vam & VME_AM_Axx) { + case VME_AM_A16: + ctl = 0x00000000; + break; + case VME_AM_A24: + ctl = 0x00000010; + break; + case VME_AM_A32: + ctl = 0x00000020; + break; + } + switch (vam & VME_AM_Mxx) { + case VME_AM_DATA: + ctl |= 0x00000001; + break; + case VME_AM_PROG: + ctl |= 0x00000002; + break; + case (VME_AM_PROG | VME_AM_DATA): + ctl |= 0x00000003; + break; + } + + if (vam & VME_AM_SUP) + ctl |= 0x00000008; + if (vam & VME_AM_USR) + ctl |= 0x00000004; + + return ctl; +} + +/* + * Create vme slave window (access: vme -> pci) + */ +int tsi148_vme_slave_window(unsigned int vmeAddr, unsigned int pciAddr, + int size, int vam) +{ + int result, i; + unsigned int ctl = 0; + + if (NULL == dev) { + result = -1; + goto exit_10; + } + + for (i = 0; i < 8; i++) { + if (0x00000000 == readl(&dev->uregs->inbound[i].itat)) + break; + } + + if (i > 7) { + printf("Tsi148: No Image available\n"); + result = -1; + goto exit_10; + } + + debug("Tsi148: Using image %d\n", i); + + __raw_writel(htonl(vmeAddr), &dev->uregs->inbound[i].itsal); + __raw_writel(0x00000000, &dev->uregs->inbound[i].itsau); + __raw_writel(htonl(vmeAddr + size), &dev->uregs->inbound[i].iteal); + __raw_writel(0x00000000, &dev->uregs->inbound[i].iteau); + __raw_writel(htonl(pciAddr - vmeAddr), &dev->uregs->inbound[i].itofl); + if (vmeAddr > pciAddr) + __raw_writel(0xffffffff, &dev->uregs->inbound[i].itofu); + else + __raw_writel(0x00000000, &dev->uregs->inbound[i].itofu); + + ctl = tsi148_eval_vam(vam); + ctl |= 0x80000000; /* enable */ + __raw_writel(htonl(ctl), &dev->uregs->inbound[i].itat); + + debug("Tsi148: window-addr =%p\n", + &dev->uregs->inbound[i].itsau); + debug("Tsi148: vme slave window[%d] attr =%08x\n", + i, ntohl(__raw_readl(&dev->uregs->inbound[i].itat))); + debug("Tsi148: vme slave window[%d] start =%08x\n", + i, ntohl(__raw_readl(&dev->uregs->inbound[i].itsal))); + debug("Tsi148: vme slave window[%d] end =%08x\n", + i, ntohl(__raw_readl(&dev->uregs->inbound[i].iteal))); + debug("Tsi148: vme slave window[%d] offset=%08x\n", + i, ntohl(__raw_readl(&dev->uregs->inbound[i].itofl))); + + return 0; + + exit_10: + return -result; +} + +/* + * Create vme slave window (access: vme -> gcsr) + */ +int tsi148_vme_gcsr_window(unsigned int vmeAddr, int vam) +{ + int result; + unsigned int ctl; + + result = 0; + + if (NULL == dev) { + result = 1; + } else { + __raw_writel(htonl(vmeAddr), &dev->uregs->gbal); + __raw_writel(0x00000000, &dev->uregs->gbau); + + ctl = tsi148_eval_vam(vam); + ctl |= 0x00000080; /* enable */ + __raw_writel(htonl(ctl), &dev->uregs->gcsrat); + } + + return result; +} + +/* + * Create vme slave window (access: vme -> crcsr) + */ +int tsi148_vme_crcsr_window(unsigned int vmeAddr) +{ + int result; + unsigned int ctl; + + result = 0; + + if (NULL == dev) { + result = 1; + } else { + __raw_writel(htonl(vmeAddr), &dev->uregs->crol); + __raw_writel(0x00000000, &dev->uregs->crou); + + ctl = 0x00000080; /* enable */ + __raw_writel(htonl(ctl), &dev->uregs->crat); + } + + return result; +} + +/* + * Create vme slave window (access: vme -> crg) + */ +int tsi148_vme_crg_window(unsigned int vmeAddr, int vam) +{ + int result; + unsigned int ctl; + + result = 0; + + if (NULL == dev) { + result = 1; + } else { + __raw_writel(htonl(vmeAddr), &dev->uregs->cbal); + __raw_writel(0x00000000, &dev->uregs->cbau); + + ctl = tsi148_eval_vam(vam); + ctl |= 0x00000080; /* enable */ + __raw_writel(htonl(ctl), &dev->uregs->crgat); + } + + return result; +} + +/* + * Tundra Tsi148 configuration + */ +int do_tsi148(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + ulong addr1 = 0, addr2 = 0, size = 0, vam = 0, vdw = 0; + char cmd = 'x'; + + /* get parameter */ + if (argc > 1) + cmd = argv[1][0]; + if (argc > 2) + addr1 = simple_strtoul(argv[2], NULL, 16); + if (argc > 3) + addr2 = simple_strtoul(argv[3], NULL, 16); + if (argc > 4) + size = simple_strtoul(argv[4], NULL, 16); + if (argc > 5) + vam = simple_strtoul(argv[5], NULL, 16); + if (argc > 6) + vdw = simple_strtoul(argv[6], NULL, 16); + + switch (cmd) { + case 'c': + if (strcmp(argv[1], "crg") == 0) { + vam = addr2; + printf("Tsi148: Configuring VME CRG Window " + "(VME->CRG):\n"); + printf(" vme=%08lx vam=%02lx\n", addr1, vam); + tsi148_vme_crg_window(addr1, vam); + } else { + printf("Tsi148: Configuring VME CR/CSR Window " + "(VME->CR/CSR):\n"); + printf(" pci=%08lx\n", addr1); + tsi148_vme_crcsr_window(addr1); + } + break; + case 'i': /* init */ + tsi148_init(); + break; + case 'g': + vam = addr2; + printf("Tsi148: Configuring VME GCSR Window (VME->GCSR):\n"); + printf(" vme=%08lx vam=%02lx\n", addr1, vam); + tsi148_vme_gcsr_window(addr1, vam); + break; + case 'v': /* vme */ + printf("Tsi148: Configuring VME Slave Window (VME->PCI):\n"); + printf(" vme=%08lx pci=%08lx size=%08lx vam=%02lx\n", + addr1, addr2, size, vam); + tsi148_vme_slave_window(addr1, addr2, size, vam); + break; + case 'p': /* pci */ + printf("Tsi148: Configuring PCI Slave Window (PCI->VME):\n"); + printf(" pci=%08lx vme=%08lx size=%08lx vam=%02lx vdw=%02lx\n", + addr1, addr2, size, vam, vdw); + tsi148_pci_slave_window(addr1, addr2, size, vam, vdw); + break; + default: + printf("Tsi148: Command %s not supported!\n", argv[1]); + } + + return 0; +} + +U_BOOT_CMD( + tsi148, 7, 1, do_tsi148, + "initialize and configure Turndra Tsi148\n", + "init\n" + " - initialize tsi148\n" + "tsi148 vme [vme_addr] [pci_addr] [size] [vam]\n" + " - create vme slave window (access: vme->pci)\n" + "tsi148 pci [pci_addr] [vme_addr] [size] [vam] [vdw]\n" + " - create pci slave window (access: pci->vme)\n" + "tsi148 crg [vme_addr] [vam]\n" + " - create vme slave window: (access vme->CRG\n" + "tsi148 crcsr [pci_addr]\n" + " - create vme slave window: (access vme->CR/CSR\n" + "tsi148 gcsr [vme_addr] [vam]\n" + " - create vme slave window: (access vme->GCSR\n" + " [vam] = VMEbus Address-Modifier: 01 -> A16 Address Space\n" + " 02 -> A24 Address Space\n" + " 03 -> A32 Address Space\n" + " 04 -> Usr AM Code\n" + " 08 -> Supervisor AM Code\n" + " 10 -> Data AM Code\n" + " 20 -> Program AM Code\n" + " [vdw] = VMEbus Maximum Datawidth: 02 -> D16 Data Width\n" + " 03 -> D32 Data Width\n" +); diff --git a/qemu/roms/u-boot/common/cmd_ubi.c b/qemu/roms/u-boot/common/cmd_ubi.c new file mode 100644 index 000000000..7c4d950e9 --- /dev/null +++ b/qemu/roms/u-boot/common/cmd_ubi.c @@ -0,0 +1,711 @@ +/* + * Unsorted Block Image commands + * + * Copyright (C) 2008 Samsung Electronics + * Kyungmin Park <kyungmin.park@samsung.com> + * + * Copyright 2008-2009 Stefan Roese <sr@denx.de>, DENX Software Engineering + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <common.h> +#include <command.h> +#include <exports.h> + +#include <nand.h> +#include <onenand_uboot.h> +#include <linux/mtd/mtd.h> +#include <linux/mtd/partitions.h> +#include <ubi_uboot.h> +#include <asm/errno.h> +#include <jffs2/load_kernel.h> + +#undef ubi_msg +#define ubi_msg(fmt, ...) printf("UBI: " fmt "\n", ##__VA_ARGS__) + +#define DEV_TYPE_NONE 0 +#define DEV_TYPE_NAND 1 +#define DEV_TYPE_ONENAND 2 +#define DEV_TYPE_NOR 3 + +/* Private own data */ +static struct ubi_device *ubi; +static char buffer[80]; +static int ubi_initialized; + +struct selected_dev { + char part_name[80]; + int selected; + int nr; + struct mtd_info *mtd_info; +}; + +static struct selected_dev ubi_dev; + +#ifdef CONFIG_CMD_UBIFS +int ubifs_is_mounted(void); +void cmd_ubifs_umount(void); +#endif + +static void ubi_dump_vol_info(const struct ubi_volume *vol) +{ + ubi_msg("volume information dump:"); + ubi_msg("vol_id %d", vol->vol_id); + ubi_msg("reserved_pebs %d", vol->reserved_pebs); + ubi_msg("alignment %d", vol->alignment); + ubi_msg("data_pad %d", vol->data_pad); + ubi_msg("vol_type %d", vol->vol_type); + ubi_msg("name_len %d", vol->name_len); + ubi_msg("usable_leb_size %d", vol->usable_leb_size); + ubi_msg("used_ebs %d", vol->used_ebs); + ubi_msg("used_bytes %lld", vol->used_bytes); + ubi_msg("last_eb_bytes %d", vol->last_eb_bytes); + ubi_msg("corrupted %d", vol->corrupted); + ubi_msg("upd_marker %d", vol->upd_marker); + + if (vol->name_len <= UBI_VOL_NAME_MAX && + strnlen(vol->name, vol->name_len + 1) == vol->name_len) { + ubi_msg("name %s", vol->name); + } else { + ubi_msg("the 1st 5 characters of the name: %c%c%c%c%c", + vol->name[0], vol->name[1], vol->name[2], + vol->name[3], vol->name[4]); + } + printf("\n"); +} + +static void display_volume_info(struct ubi_device *ubi) +{ + int i; + + for (i = 0; i < (ubi->vtbl_slots + 1); i++) { + if (!ubi->volumes[i]) + continue; /* Empty record */ + ubi_dump_vol_info(ubi->volumes[i]); + } +} + +static void display_ubi_info(struct ubi_device *ubi) +{ + ubi_msg("MTD device name: \"%s\"", ubi->mtd->name); + ubi_msg("MTD device size: %llu MiB", ubi->flash_size >> 20); + ubi_msg("physical eraseblock size: %d bytes (%d KiB)", + ubi->peb_size, ubi->peb_size >> 10); + ubi_msg("logical eraseblock size: %d bytes", ubi->leb_size); + ubi_msg("number of good PEBs: %d", ubi->good_peb_count); + ubi_msg("number of bad PEBs: %d", ubi->bad_peb_count); + ubi_msg("smallest flash I/O unit: %d", ubi->min_io_size); + ubi_msg("VID header offset: %d (aligned %d)", + ubi->vid_hdr_offset, ubi->vid_hdr_aloffset); + ubi_msg("data offset: %d", ubi->leb_start); + ubi_msg("max. allowed volumes: %d", ubi->vtbl_slots); + ubi_msg("wear-leveling threshold: %d", CONFIG_MTD_UBI_WL_THRESHOLD); + ubi_msg("number of internal volumes: %d", UBI_INT_VOL_COUNT); + ubi_msg("number of user volumes: %d", + ubi->vol_count - UBI_INT_VOL_COUNT); + ubi_msg("available PEBs: %d", ubi->avail_pebs); + ubi_msg("total number of reserved PEBs: %d", ubi->rsvd_pebs); + ubi_msg("number of PEBs reserved for bad PEB handling: %d", + ubi->beb_rsvd_pebs); + ubi_msg("max/mean erase counter: %d/%d", ubi->max_ec, ubi->mean_ec); +} + +static int ubi_info(int layout) +{ + if (layout) + display_volume_info(ubi); + else + display_ubi_info(ubi); + + return 0; +} + +static int ubi_check_volumename(const struct ubi_volume *vol, char *name) +{ + return strcmp(vol->name, name); +} + +static int ubi_check(char *name) +{ + int i; + + for (i = 0; i < (ubi->vtbl_slots + 1); i++) { + if (!ubi->volumes[i]) + continue; /* Empty record */ + + if (!ubi_check_volumename(ubi->volumes[i], name)) + return 0; + } + + return -EEXIST; +} + + +static int verify_mkvol_req(const struct ubi_device *ubi, + const struct ubi_mkvol_req *req) +{ + int n, err = EINVAL; + + if (req->bytes < 0 || req->alignment < 0 || req->vol_type < 0 || + req->name_len < 0) + goto bad; + + if ((req->vol_id < 0 || req->vol_id >= ubi->vtbl_slots) && + req->vol_id != UBI_VOL_NUM_AUTO) + goto bad; + + if (req->alignment == 0) + goto bad; + + if (req->bytes == 0) { + printf("No space left in UBI device!\n"); + err = ENOMEM; + goto bad; + } + + if (req->vol_type != UBI_DYNAMIC_VOLUME && + req->vol_type != UBI_STATIC_VOLUME) + goto bad; + + if (req->alignment > ubi->leb_size) + goto bad; + + n = req->alignment % ubi->min_io_size; + if (req->alignment != 1 && n) + goto bad; + + if (req->name_len > UBI_VOL_NAME_MAX) { + printf("Name too long!\n"); + err = ENAMETOOLONG; + goto bad; + } + + return 0; +bad: + return err; +} + +static int ubi_create_vol(char *volume, int64_t size, int dynamic) +{ + struct ubi_mkvol_req req; + int err; + + if (dynamic) + req.vol_type = UBI_DYNAMIC_VOLUME; + else + req.vol_type = UBI_STATIC_VOLUME; + + req.vol_id = UBI_VOL_NUM_AUTO; + req.alignment = 1; + req.bytes = size; + + strcpy(req.name, volume); + req.name_len = strlen(volume); + req.name[req.name_len] = '\0'; + req.padding1 = 0; + /* It's duplicated at drivers/mtd/ubi/cdev.c */ + err = verify_mkvol_req(ubi, &req); + if (err) { + printf("verify_mkvol_req failed %d\n", err); + return err; + } + printf("Creating %s volume %s of size %lld\n", + dynamic ? "dynamic" : "static", volume, size); + /* Call real ubi create volume */ + return ubi_create_volume(ubi, &req); +} + +static struct ubi_volume *ubi_find_volume(char *volume) +{ + struct ubi_volume *vol = NULL; + int i; + + for (i = 0; i < ubi->vtbl_slots; i++) { + vol = ubi->volumes[i]; + if (vol && !strcmp(vol->name, volume)) + return vol; + } + + printf("Volume %s not found!\n", volume); + return NULL; +} + +static int ubi_remove_vol(char *volume) +{ + int err, reserved_pebs, i; + struct ubi_volume *vol; + + vol = ubi_find_volume(volume); + if (vol == NULL) + return ENODEV; + + printf("Remove UBI volume %s (id %d)\n", vol->name, vol->vol_id); + + if (ubi->ro_mode) { + printf("It's read-only mode\n"); + err = EROFS; + goto out_err; + } + + err = ubi_change_vtbl_record(ubi, vol->vol_id, NULL); + if (err) { + printf("Error changing Vol tabel record err=%x\n", err); + goto out_err; + } + reserved_pebs = vol->reserved_pebs; + for (i = 0; i < vol->reserved_pebs; i++) { + err = ubi_eba_unmap_leb(ubi, vol, i); + if (err) + goto out_err; + } + + kfree(vol->eba_tbl); + ubi->volumes[vol->vol_id]->eba_tbl = NULL; + ubi->volumes[vol->vol_id] = NULL; + + ubi->rsvd_pebs -= reserved_pebs; + ubi->avail_pebs += reserved_pebs; + i = ubi->beb_rsvd_level - ubi->beb_rsvd_pebs; + if (i > 0) { + i = ubi->avail_pebs >= i ? i : ubi->avail_pebs; + ubi->avail_pebs -= i; + ubi->rsvd_pebs += i; + ubi->beb_rsvd_pebs += i; + if (i > 0) + ubi_msg("reserve more %d PEBs", i); + } + ubi->vol_count -= 1; + + return 0; +out_err: + ubi_err("cannot remove volume %s, error %d", volume, err); + if (err < 0) + err = -err; + return err; +} + +int ubi_volume_continue_write(char *volume, void *buf, size_t size) +{ + int err = 1; + struct ubi_volume *vol; + + vol = ubi_find_volume(volume); + if (vol == NULL) + return ENODEV; + + err = ubi_more_update_data(ubi, vol, buf, size); + if (err < 0) { + printf("Couldnt or partially wrote data\n"); + return -err; + } + + if (err) { + size = err; + + err = ubi_check_volume(ubi, vol->vol_id); + if (err < 0) + return -err; + + if (err) { + ubi_warn("volume %d on UBI device %d is corrupted", + vol->vol_id, ubi->ubi_num); + vol->corrupted = 1; + } + + vol->checked = 1; + ubi_gluebi_updated(vol); + } + + return 0; +} + +int ubi_volume_begin_write(char *volume, void *buf, size_t size, + size_t full_size) +{ + int err = 1; + int rsvd_bytes = 0; + struct ubi_volume *vol; + + vol = ubi_find_volume(volume); + if (vol == NULL) + return ENODEV; + + rsvd_bytes = vol->reserved_pebs * (ubi->leb_size - vol->data_pad); + if (size < 0 || size > rsvd_bytes) { + printf("size > volume size! Aborting!\n"); + return EINVAL; + } + + err = ubi_start_update(ubi, vol, full_size); + if (err < 0) { + printf("Cannot start volume update\n"); + return -err; + } + + return ubi_volume_continue_write(volume, buf, size); +} + +int ubi_volume_write(char *volume, void *buf, size_t size) +{ + return ubi_volume_begin_write(volume, buf, size, size); +} + +int ubi_volume_read(char *volume, char *buf, size_t size) +{ + int err, lnum, off, len, tbuf_size; + void *tbuf; + unsigned long long tmp; + struct ubi_volume *vol; + loff_t offp = 0; + + vol = ubi_find_volume(volume); + if (vol == NULL) + return ENODEV; + + if (vol->updating) { + printf("updating"); + return EBUSY; + } + if (vol->upd_marker) { + printf("damaged volume, update marker is set"); + return EBADF; + } + if (offp == vol->used_bytes) + return 0; + + if (size == 0) { + printf("No size specified -> Using max size (%lld)\n", vol->used_bytes); + size = vol->used_bytes; + } + + if (vol->corrupted) + printf("read from corrupted volume %d", vol->vol_id); + if (offp + size > vol->used_bytes) + size = vol->used_bytes - offp; + + tbuf_size = vol->usable_leb_size; + if (size < tbuf_size) + tbuf_size = ALIGN(size, ubi->min_io_size); + tbuf = malloc(tbuf_size); + if (!tbuf) { + printf("NO MEM\n"); + return ENOMEM; + } + len = size > tbuf_size ? tbuf_size : size; + + tmp = offp; + off = do_div(tmp, vol->usable_leb_size); + lnum = tmp; + do { + if (off + len >= vol->usable_leb_size) + len = vol->usable_leb_size - off; + + err = ubi_eba_read_leb(ubi, vol, lnum, tbuf, off, len, 0); + if (err) { + printf("read err %x\n", err); + err = -err; + break; + } + off += len; + if (off == vol->usable_leb_size) { + lnum += 1; + off -= vol->usable_leb_size; + } + + size -= len; + offp += len; + + memcpy(buf, tbuf, len); + + buf += len; + len = size > tbuf_size ? tbuf_size : size; + } while (size); + + free(tbuf); + return err; +} + +static int ubi_dev_scan(struct mtd_info *info, char *ubidev, + const char *vid_header_offset) +{ + struct mtd_device *dev; + struct part_info *part; + struct mtd_partition mtd_part; + char ubi_mtd_param_buffer[80]; + u8 pnum; + int err; + + if (find_dev_and_part(ubidev, &dev, &pnum, &part) != 0) + return 1; + + sprintf(buffer, "mtd=%d", pnum); + memset(&mtd_part, 0, sizeof(mtd_part)); + mtd_part.name = buffer; + mtd_part.size = part->size; + mtd_part.offset = part->offset; + add_mtd_partitions(info, &mtd_part, 1); + + strcpy(ubi_mtd_param_buffer, buffer); + if (vid_header_offset) + sprintf(ubi_mtd_param_buffer, "mtd=%d,%s", pnum, + vid_header_offset); + err = ubi_mtd_param_parse(ubi_mtd_param_buffer, NULL); + if (err) { + del_mtd_partitions(info); + return -err; + } + + err = ubi_init(); + if (err) { + del_mtd_partitions(info); + return -err; + } + + ubi_initialized = 1; + + return 0; +} + +int ubi_part(char *part_name, const char *vid_header_offset) +{ + int err = 0; + char mtd_dev[16]; + struct mtd_device *dev; + struct part_info *part; + u8 pnum; + + if (mtdparts_init() != 0) { + printf("Error initializing mtdparts!\n"); + return 1; + } + +#ifdef CONFIG_CMD_UBIFS + /* + * Automatically unmount UBIFS partition when user + * changes the UBI device. Otherwise the following + * UBIFS commands will crash. + */ + if (ubifs_is_mounted()) + cmd_ubifs_umount(); +#endif + + /* todo: get dev number for NAND... */ + ubi_dev.nr = 0; + + /* + * Call ubi_exit() before re-initializing the UBI subsystem + */ + if (ubi_initialized) { + ubi_exit(); + del_mtd_partitions(ubi_dev.mtd_info); + } + + /* + * Search the mtd device number where this partition + * is located + */ + if (find_dev_and_part(part_name, &dev, &pnum, &part)) { + printf("Partition %s not found!\n", part_name); + return 1; + } + sprintf(mtd_dev, "%s%d", MTD_DEV_TYPE(dev->id->type), dev->id->num); + ubi_dev.mtd_info = get_mtd_device_nm(mtd_dev); + if (IS_ERR(ubi_dev.mtd_info)) { + printf("Partition %s not found on device %s!\n", part_name, + mtd_dev); + return 1; + } + + ubi_dev.selected = 1; + + strcpy(ubi_dev.part_name, part_name); + err = ubi_dev_scan(ubi_dev.mtd_info, ubi_dev.part_name, + vid_header_offset); + if (err) { + printf("UBI init error %d\n", err); + ubi_dev.selected = 0; + return err; + } + + ubi = ubi_devices[0]; + + return 0; +} + +static int do_ubi(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + int64_t size = 0; + ulong addr = 0; + + if (argc < 2) + return CMD_RET_USAGE; + + if (strcmp(argv[1], "part") == 0) { + const char *vid_header_offset = NULL; + + /* Print current partition */ + if (argc == 2) { + if (!ubi_dev.selected) { + printf("Error, no UBI device/partition selected!\n"); + return 1; + } + + printf("Device %d: %s, partition %s\n", + ubi_dev.nr, ubi_dev.mtd_info->name, ubi_dev.part_name); + return 0; + } + + if (argc < 3) + return CMD_RET_USAGE; + + if (argc > 3) + vid_header_offset = argv[3]; + + return ubi_part(argv[2], vid_header_offset); + } + + if ((strcmp(argv[1], "part") != 0) && (!ubi_dev.selected)) { + printf("Error, no UBI device/partition selected!\n"); + return 1; + } + + if (strcmp(argv[1], "info") == 0) { + int layout = 0; + if (argc > 2 && !strncmp(argv[2], "l", 1)) + layout = 1; + return ubi_info(layout); + } + + if (strcmp(argv[1], "check") == 0) { + if (argc > 2) + return ubi_check(argv[2]); + + printf("Error, no volume name passed\n"); + return 1; + } + + if (strncmp(argv[1], "create", 6) == 0) { + int dynamic = 1; /* default: dynamic volume */ + + /* Use maximum available size */ + size = 0; + + /* E.g., create volume size type */ + if (argc == 5) { + if (strncmp(argv[4], "s", 1) == 0) + dynamic = 0; + else if (strncmp(argv[4], "d", 1) != 0) { + printf("Incorrect type\n"); + return 1; + } + argc--; + } + /* E.g., create volume size */ + if (argc == 4) { + size = simple_strtoull(argv[3], NULL, 16); + argc--; + } + /* Use maximum available size */ + if (!size) { + size = (int64_t)ubi->avail_pebs * ubi->leb_size; + printf("No size specified -> Using max size (%lld)\n", size); + } + /* E.g., create volume */ + if (argc == 3) + return ubi_create_vol(argv[2], size, dynamic); + } + + if (strncmp(argv[1], "remove", 6) == 0) { + /* E.g., remove volume */ + if (argc == 3) + return ubi_remove_vol(argv[2]); + } + + if (strncmp(argv[1], "write", 5) == 0) { + int ret; + + if (argc < 5) { + printf("Please see usage\n"); + return 1; + } + + addr = simple_strtoul(argv[2], NULL, 16); + size = simple_strtoul(argv[4], NULL, 16); + + if (strlen(argv[1]) == 10 && + strncmp(argv[1] + 5, ".part", 5) == 0) { + if (argc < 6) { + ret = ubi_volume_continue_write(argv[3], + (void *)addr, size); + } else { + size_t full_size; + full_size = simple_strtoul(argv[5], NULL, 16); + ret = ubi_volume_begin_write(argv[3], + (void *)addr, size, full_size); + } + } else { + ret = ubi_volume_write(argv[3], (void *)addr, size); + } + if (!ret) { + printf("%lld bytes written to volume %s\n", size, + argv[3]); + } + + return ret; + } + + if (strncmp(argv[1], "read", 4) == 0) { + size = 0; + + /* E.g., read volume size */ + if (argc == 5) { + size = simple_strtoul(argv[4], NULL, 16); + argc--; + } + + /* E.g., read volume */ + if (argc == 4) { + addr = simple_strtoul(argv[2], NULL, 16); + argc--; + } + + if (argc == 3) { + printf("Read %lld bytes from volume %s to %lx\n", size, + argv[3], addr); + + return ubi_volume_read(argv[3], (char *)addr, size); + } + } + + printf("Please see usage\n"); + return 1; +} + +U_BOOT_CMD( + ubi, 6, 1, do_ubi, + "ubi commands", + "part [part] [offset]\n" + " - Show or set current partition (with optional VID" + " header offset)\n" + "ubi info [l[ayout]]" + " - Display volume and ubi layout information\n" + "ubi check volumename" + " - check if volumename exists\n" + "ubi create[vol] volume [size] [type]" + " - create volume name with size\n" + "ubi write[vol] address volume size" + " - Write volume from address with size\n" + "ubi write.part address volume size [fullsize]\n" + " - Write part of a volume from address\n" + "ubi read[vol] address volume [size]" + " - Read volume to address with size\n" + "ubi remove[vol] volume" + " - Remove volume\n" + "[Legends]\n" + " volume: character name\n" + " size: specified in bytes\n" + " type: s[tatic] or d[ynamic] (default=dynamic)" +); diff --git a/qemu/roms/u-boot/common/cmd_ubifs.c b/qemu/roms/u-boot/common/cmd_ubifs.c new file mode 100644 index 000000000..fdc8bfe46 --- /dev/null +++ b/qemu/roms/u-boot/common/cmd_ubifs.c @@ -0,0 +1,169 @@ +/* + * (C) Copyright 2008 + * Stefan Roese, DENX Software Engineering, sr@denx.de. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + + +/* + * UBIFS command support + */ + +#undef DEBUG + +#include <common.h> +#include <config.h> +#include <command.h> + +#include "../fs/ubifs/ubifs.h" + +static int ubifs_initialized; +static int ubifs_mounted; + +int do_ubifs_mount(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + char *vol_name; + int ret; + + if (argc != 2) + return CMD_RET_USAGE; + + vol_name = argv[1]; + debug("Using volume %s\n", vol_name); + + if (ubifs_initialized == 0) { + ubifs_init(); + ubifs_initialized = 1; + } + + ret = ubifs_mount(vol_name); + if (ret) + return -1; + + ubifs_mounted = 1; + + return 0; +} + +int ubifs_is_mounted(void) +{ + return ubifs_mounted; +} + +void cmd_ubifs_umount(void) +{ + + if (ubifs_sb) { + printf("Unmounting UBIFS volume %s!\n", + ((struct ubifs_info *)(ubifs_sb->s_fs_info))->vi.name); + ubifs_umount(ubifs_sb->s_fs_info); + } + + ubifs_sb = NULL; + ubifs_mounted = 0; + ubifs_initialized = 0; +} + +int do_ubifs_umount(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + if (argc != 1) + return CMD_RET_USAGE; + + if (ubifs_initialized == 0) { + printf("No UBIFS volume mounted!\n"); + return -1; + } + + cmd_ubifs_umount(); + + return 0; +} + +int do_ubifs_ls(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + char *filename = "/"; + int ret; + + if (!ubifs_mounted) { + printf("UBIFS not mounted, use ubifsmount to mount volume first!\n"); + return -1; + } + + if (argc == 2) + filename = argv[1]; + debug("Using filename %s\n", filename); + + ret = ubifs_ls(filename); + if (ret) { + printf("** File not found %s **\n", filename); + ret = CMD_RET_FAILURE; + } + + return ret; +} + +int do_ubifs_load(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + char *filename; + char *endp; + int ret; + u32 addr; + u32 size = 0; + + if (!ubifs_mounted) { + printf("UBIFS not mounted, use ubifs mount to mount volume first!\n"); + return -1; + } + + if (argc < 3) + return CMD_RET_USAGE; + + addr = simple_strtoul(argv[1], &endp, 16); + if (endp == argv[1]) + return CMD_RET_USAGE; + + filename = argv[2]; + + if (argc == 4) { + size = simple_strtoul(argv[3], &endp, 16); + if (endp == argv[3]) + return CMD_RET_USAGE; + } + debug("Loading file '%s' to address 0x%08x (size %d)\n", filename, addr, size); + + ret = ubifs_load(filename, addr, size); + if (ret) { + printf("** File not found %s **\n", filename); + ret = CMD_RET_FAILURE; + } + + return ret; +} + +U_BOOT_CMD( + ubifsmount, 2, 0, do_ubifs_mount, + "mount UBIFS volume", + "<volume-name>\n" + " - mount 'volume-name' volume" +); + +U_BOOT_CMD( + ubifsumount, 1, 0, do_ubifs_umount, + "unmount UBIFS volume", + " - unmount current volume" +); + +U_BOOT_CMD( + ubifsls, 2, 0, do_ubifs_ls, + "list files in a directory", + "[directory]\n" + " - list files in a 'directory' (default '/')" +); + +U_BOOT_CMD( + ubifsload, 4, 0, do_ubifs_load, + "load file from an UBIFS filesystem", + "<addr> <filename> [bytes]\n" + " - load file 'filename' to address 'addr'" +); diff --git a/qemu/roms/u-boot/common/cmd_universe.c b/qemu/roms/u-boot/common/cmd_universe.c new file mode 100644 index 000000000..c9310363f --- /dev/null +++ b/qemu/roms/u-boot/common/cmd_universe.c @@ -0,0 +1,368 @@ +/* + * (C) Copyright 2003 Stefan Roese, stefan.roese@esd-electronics.com + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <command.h> +#include <malloc.h> +#include <asm/io.h> +#include <pci.h> + +#include <universe.h> + +#define PCI_VENDOR PCI_VENDOR_ID_TUNDRA +#define PCI_DEVICE PCI_DEVICE_ID_TUNDRA_CA91C042 + + +typedef struct _UNI_DEV UNI_DEV; + +struct _UNI_DEV { + int bus; + pci_dev_t busdevfn; + UNIVERSE *uregs; + unsigned int pci_bs; +}; + +static UNI_DEV *dev; + + +int universe_init(void) +{ + int j, result; + pci_dev_t busdevfn; + unsigned int val; + + busdevfn = pci_find_device(PCI_VENDOR, PCI_DEVICE, 0); + if (busdevfn == -1) { + puts("No Tundra Universe found!\n"); + return -1; + } + + /* Lets turn Latency off */ + pci_write_config_dword(busdevfn, 0x0c, 0); + + dev = malloc(sizeof(*dev)); + if (NULL == dev) { + puts("UNIVERSE: No memory!\n"); + result = -1; + goto break_20; + } + + memset(dev, 0, sizeof(*dev)); + dev->busdevfn = busdevfn; + + pci_read_config_dword(busdevfn, PCI_BASE_ADDRESS_1, &val); + if (val & 1) { + pci_read_config_dword(busdevfn, PCI_BASE_ADDRESS_0, &val); + } + val &= ~0xf; + dev->uregs = (UNIVERSE *)val; + + debug ("UNIVERSE-Base : %p\n", dev->uregs); + + /* check mapping */ + debug (" Read via mapping, PCI_ID = %08X\n", readl(&dev->uregs->pci_id)); + if (((PCI_DEVICE <<16) | PCI_VENDOR) != readl(&dev->uregs->pci_id)) { + printf ("UNIVERSE: Cannot read PCI-ID via Mapping: %08x\n", + readl(&dev->uregs->pci_id)); + result = -1; + goto break_30; + } + + debug ("PCI_BS = %08X\n", readl(&dev->uregs->pci_bs)); + + dev->pci_bs = readl(&dev->uregs->pci_bs); + + /* turn off windows */ + for (j=0; j <4; j ++) { + writel(0x00800000, &dev->uregs->lsi[j].ctl); + writel(0x00800000, &dev->uregs->vsi[j].ctl); + } + + /* + * Write to Misc Register + * Set VME Bus Time-out + * Arbitration Mode + * DTACK Enable + */ + writel(0x15040000 | (readl(&dev->uregs->misc_ctl) & 0x00020000), &dev->uregs->misc_ctl); + + if (readl(&dev->uregs->misc_ctl) & 0x00020000) { + debug ("System Controller!\n"); /* test-only */ + } else { + debug ("Not System Controller!\n"); /* test-only */ + } + + /* + * Lets turn off interrupts + */ + writel(0x00000000,&dev->uregs->lint_en); /* Disable interrupts in the Universe first */ + writel(0x0000FFFF,&dev->uregs->lint_stat); /* Clear Any Pending Interrupts */ + eieio(); + writel(0x0000, &dev->uregs->lint_map0); /* Map all ints to 0 */ + writel(0x0000, &dev->uregs->lint_map1); /* Map all ints to 0 */ + eieio(); + + return 0; + + break_30: + free(dev); + break_20: + return result; +} + + +/* + * Create pci slave window (access: pci -> vme) + */ +int universe_pci_slave_window(unsigned int pciAddr, unsigned int vmeAddr, int size, int vam, int pms, int vdw) +{ + int result, i; + unsigned int ctl = 0; + + if (NULL == dev) { + result = -1; + goto exit_10; + } + + for (i = 0; i < 4; i++) { + if (0x00800000 == readl(&dev->uregs->lsi[i].ctl)) + break; + } + + if (i == 4) { + printf ("universe: No Image available\n"); + result = -1; + goto exit_10; + } + + debug ("universe: Using image %d\n", i); + + writel(pciAddr , &dev->uregs->lsi[i].bs); + writel((pciAddr + size), &dev->uregs->lsi[i].bd); + writel((vmeAddr - pciAddr), &dev->uregs->lsi[i].to); + + switch (vam & VME_AM_Axx) { + case VME_AM_A16: + ctl = 0x00000000; + break; + case VME_AM_A24: + ctl = 0x00010000; + break; + case VME_AM_A32: + ctl = 0x00020000; + break; + } + + switch (vam & VME_AM_Mxx) { + case VME_AM_DATA: + ctl |= 0x00000000; + break; + case VME_AM_PROG: + ctl |= 0x00008000; + break; + } + + if (vam & VME_AM_SUP) { + ctl |= 0x00001000; + + } + + switch (vdw & VME_FLAG_Dxx) { + case VME_FLAG_D8: + ctl |= 0x00000000; + break; + case VME_FLAG_D16: + ctl |= 0x00400000; + break; + case VME_FLAG_D32: + ctl |= 0x00800000; + break; + } + + switch (pms & PCI_MS_Mxx) { + case PCI_MS_MEM: + ctl |= 0x00000000; + break; + case PCI_MS_IO: + ctl |= 0x00000001; + break; + case PCI_MS_CONFIG: + ctl |= 0x00000002; + break; + } + + ctl |= 0x80000000; /* enable */ + + writel(ctl, &dev->uregs->lsi[i].ctl); + + debug ("universe: window-addr=%p\n", &dev->uregs->lsi[i].ctl); + debug ("universe: pci slave window[%d] ctl=%08x\n", i, readl(&dev->uregs->lsi[i].ctl)); + debug ("universe: pci slave window[%d] bs=%08x\n", i, readl(&dev->uregs->lsi[i].bs)); + debug ("universe: pci slave window[%d] bd=%08x\n", i, readl(&dev->uregs->lsi[i].bd)); + debug ("universe: pci slave window[%d] to=%08x\n", i, readl(&dev->uregs->lsi[i].to)); + + return 0; + + exit_10: + return -result; +} + + +/* + * Create vme slave window (access: vme -> pci) + */ +int universe_vme_slave_window(unsigned int vmeAddr, unsigned int pciAddr, int size, int vam, int pms) +{ + int result, i; + unsigned int ctl = 0; + + if (NULL == dev) { + result = -1; + goto exit_10; + } + + for (i = 0; i < 4; i++) { + if (0x00800000 == readl(&dev->uregs->vsi[i].ctl)) + break; + } + + if (i == 4) { + printf ("universe: No Image available\n"); + result = -1; + goto exit_10; + } + + debug ("universe: Using image %d\n", i); + + writel(vmeAddr , &dev->uregs->vsi[i].bs); + writel((vmeAddr + size), &dev->uregs->vsi[i].bd); + writel((pciAddr - vmeAddr), &dev->uregs->vsi[i].to); + + switch (vam & VME_AM_Axx) { + case VME_AM_A16: + ctl = 0x00000000; + break; + case VME_AM_A24: + ctl = 0x00010000; + break; + case VME_AM_A32: + ctl = 0x00020000; + break; + } + + switch (vam & VME_AM_Mxx) { + case VME_AM_DATA: + ctl |= 0x00000000; + break; + case VME_AM_PROG: + ctl |= 0x00800000; + break; + } + + if (vam & VME_AM_SUP) { + ctl |= 0x00100000; + + } + + switch (pms & PCI_MS_Mxx) { + case PCI_MS_MEM: + ctl |= 0x00000000; + break; + case PCI_MS_IO: + ctl |= 0x00000001; + break; + case PCI_MS_CONFIG: + ctl |= 0x00000002; + break; + } + + ctl |= 0x80f00000; /* enable */ + + writel(ctl, &dev->uregs->vsi[i].ctl); + + debug ("universe: window-addr=%p\n", &dev->uregs->vsi[i].ctl); + debug ("universe: vme slave window[%d] ctl=%08x\n", i, readl(&dev->uregs->vsi[i].ctl)); + debug ("universe: vme slave window[%d] bs=%08x\n", i, readl(&dev->uregs->vsi[i].bs)); + debug ("universe: vme slave window[%d] bd=%08x\n", i, readl(&dev->uregs->vsi[i].bd)); + debug ("universe: vme slave window[%d] to=%08x\n", i, readl(&dev->uregs->vsi[i].to)); + + return 0; + + exit_10: + return -result; +} + + +/* + * Tundra Universe configuration + */ +int do_universe(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + ulong addr1 = 0, addr2 = 0, size = 0, vam = 0, pms = 0, vdw = 0; + char cmd = 'x'; + + /* get parameter */ + if (argc > 1) + cmd = argv[1][0]; + if (argc > 2) + addr1 = simple_strtoul(argv[2], NULL, 16); + if (argc > 3) + addr2 = simple_strtoul(argv[3], NULL, 16); + if (argc > 4) + size = simple_strtoul(argv[4], NULL, 16); + if (argc > 5) + vam = simple_strtoul(argv[5], NULL, 16); + if (argc > 6) + pms = simple_strtoul(argv[6], NULL, 16); + if (argc > 7) + vdw = simple_strtoul(argv[7], NULL, 16); + + switch (cmd) { + case 'i': /* init */ + universe_init(); + break; + case 'v': /* vme */ + printf("Configuring Universe VME Slave Window (VME->PCI):\n"); + printf(" vme=%08lx pci=%08lx size=%08lx vam=%02lx pms=%02lx\n", + addr1, addr2, size, vam, pms); + universe_vme_slave_window(addr1, addr2, size, vam, pms); + break; + case 'p': /* pci */ + printf("Configuring Universe PCI Slave Window (PCI->VME):\n"); + printf(" pci=%08lx vme=%08lx size=%08lx vam=%02lx pms=%02lx vdw=%02lx\n", + addr1, addr2, size, vam, pms, vdw); + universe_pci_slave_window(addr1, addr2, size, vam, pms, vdw); + break; + default: + printf("Universe command %s not supported!\n", argv[1]); + } + + return 0; +} + + +U_BOOT_CMD( + universe, 8, 1, do_universe, + "initialize and configure Turndra Universe", + "init\n" + " - initialize universe\n" + "universe vme [vme_addr] [pci_addr] [size] [vam] [pms]\n" + " - create vme slave window (access: vme->pci)\n" + "universe pci [pci_addr] [vme_addr] [size] [vam] [pms] [vdw]\n" + " - create pci slave window (access: pci->vme)\n" + " [vam] = VMEbus Address-Modifier: 01 -> A16 Address Space\n" + " 02 -> A24 Address Space\n" + " 03 -> A32 Address Space\n" + " 04 -> Supervisor AM Code\n" + " 10 -> Data AM Code\n" + " 20 -> Program AM Code\n" + " [pms] = PCI Memory Space: 01 -> Memory Space\n" + " 02 -> I/O Space\n" + " 03 -> Configuration Space\n" + " [vdw] = VMEbus Maximum Datawidth: 01 -> D8 Data Width\n" + " 02 -> D16 Data Width\n" + " 03 -> D32 Data Width" +); diff --git a/qemu/roms/u-boot/common/cmd_unzip.c b/qemu/roms/u-boot/common/cmd_unzip.c new file mode 100644 index 000000000..b02c69e58 --- /dev/null +++ b/qemu/roms/u-boot/common/cmd_unzip.c @@ -0,0 +1,41 @@ +/* + * (C) Copyright 2000 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <command.h> + +static int do_unzip(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + unsigned long src, dst; + unsigned long src_len = ~0UL, dst_len = ~0UL; + + switch (argc) { + case 4: + dst_len = simple_strtoul(argv[3], NULL, 16); + /* fall through */ + case 3: + src = simple_strtoul(argv[1], NULL, 16); + dst = simple_strtoul(argv[2], NULL, 16); + break; + default: + return CMD_RET_USAGE; + } + + if (gunzip((void *) dst, dst_len, (void *) src, &src_len) != 0) + return 1; + + printf("Uncompressed size: %ld = 0x%lX\n", src_len, src_len); + setenv_hex("filesize", src_len); + + return 0; +} + +U_BOOT_CMD( + unzip, 4, 1, do_unzip, + "unzip a memory region", + "srcaddr dstaddr [dstsize]" +); diff --git a/qemu/roms/u-boot/common/cmd_usb.c b/qemu/roms/u-boot/common/cmd_usb.c new file mode 100644 index 000000000..2519497da --- /dev/null +++ b/qemu/roms/u-boot/common/cmd_usb.c @@ -0,0 +1,679 @@ +/* + * (C) Copyright 2001 + * Denis Peter, MPL AG Switzerland + * + * Most of this source has been derived from the Linux USB + * project. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <command.h> +#include <asm/byteorder.h> +#include <asm/unaligned.h> +#include <part.h> +#include <usb.h> + +#ifdef CONFIG_USB_STORAGE +static int usb_stor_curr_dev = -1; /* current device */ +#endif +#ifdef CONFIG_USB_HOST_ETHER +static int usb_ether_curr_dev = -1; /* current ethernet device */ +#endif + +/* some display routines (info command) */ +static char *usb_get_class_desc(unsigned char dclass) +{ + switch (dclass) { + case USB_CLASS_PER_INTERFACE: + return "See Interface"; + case USB_CLASS_AUDIO: + return "Audio"; + case USB_CLASS_COMM: + return "Communication"; + case USB_CLASS_HID: + return "Human Interface"; + case USB_CLASS_PRINTER: + return "Printer"; + case USB_CLASS_MASS_STORAGE: + return "Mass Storage"; + case USB_CLASS_HUB: + return "Hub"; + case USB_CLASS_DATA: + return "CDC Data"; + case USB_CLASS_VENDOR_SPEC: + return "Vendor specific"; + default: + return ""; + } +} + +static void usb_display_class_sub(unsigned char dclass, unsigned char subclass, + unsigned char proto) +{ + switch (dclass) { + case USB_CLASS_PER_INTERFACE: + printf("See Interface"); + break; + case USB_CLASS_HID: + printf("Human Interface, Subclass: "); + switch (subclass) { + case USB_SUB_HID_NONE: + printf("None"); + break; + case USB_SUB_HID_BOOT: + printf("Boot "); + switch (proto) { + case USB_PROT_HID_NONE: + printf("None"); + break; + case USB_PROT_HID_KEYBOARD: + printf("Keyboard"); + break; + case USB_PROT_HID_MOUSE: + printf("Mouse"); + break; + default: + printf("reserved"); + break; + } + break; + default: + printf("reserved"); + break; + } + break; + case USB_CLASS_MASS_STORAGE: + printf("Mass Storage, "); + switch (subclass) { + case US_SC_RBC: + printf("RBC "); + break; + case US_SC_8020: + printf("SFF-8020i (ATAPI)"); + break; + case US_SC_QIC: + printf("QIC-157 (Tape)"); + break; + case US_SC_UFI: + printf("UFI"); + break; + case US_SC_8070: + printf("SFF-8070"); + break; + case US_SC_SCSI: + printf("Transp. SCSI"); + break; + default: + printf("reserved"); + break; + } + printf(", "); + switch (proto) { + case US_PR_CB: + printf("Command/Bulk"); + break; + case US_PR_CBI: + printf("Command/Bulk/Int"); + break; + case US_PR_BULK: + printf("Bulk only"); + break; + default: + printf("reserved"); + break; + } + break; + default: + printf("%s", usb_get_class_desc(dclass)); + break; + } +} + +static void usb_display_string(struct usb_device *dev, int index) +{ + ALLOC_CACHE_ALIGN_BUFFER(char, buffer, 256); + + if (index != 0) { + if (usb_string(dev, index, &buffer[0], 256) > 0) + printf("String: \"%s\"", buffer); + } +} + +static void usb_display_desc(struct usb_device *dev) +{ + if (dev->descriptor.bDescriptorType == USB_DT_DEVICE) { + printf("%d: %s, USB Revision %x.%x\n", dev->devnum, + usb_get_class_desc(dev->config.if_desc[0].desc.bInterfaceClass), + (dev->descriptor.bcdUSB>>8) & 0xff, + dev->descriptor.bcdUSB & 0xff); + + if (strlen(dev->mf) || strlen(dev->prod) || + strlen(dev->serial)) + printf(" - %s %s %s\n", dev->mf, dev->prod, + dev->serial); + if (dev->descriptor.bDeviceClass) { + printf(" - Class: "); + usb_display_class_sub(dev->descriptor.bDeviceClass, + dev->descriptor.bDeviceSubClass, + dev->descriptor.bDeviceProtocol); + printf("\n"); + } else { + printf(" - Class: (from Interface) %s\n", + usb_get_class_desc( + dev->config.if_desc[0].desc.bInterfaceClass)); + } + printf(" - PacketSize: %d Configurations: %d\n", + dev->descriptor.bMaxPacketSize0, + dev->descriptor.bNumConfigurations); + printf(" - Vendor: 0x%04x Product 0x%04x Version %d.%d\n", + dev->descriptor.idVendor, dev->descriptor.idProduct, + (dev->descriptor.bcdDevice>>8) & 0xff, + dev->descriptor.bcdDevice & 0xff); + } + +} + +static void usb_display_conf_desc(struct usb_config_descriptor *config, + struct usb_device *dev) +{ + printf(" Configuration: %d\n", config->bConfigurationValue); + printf(" - Interfaces: %d %s%s%dmA\n", config->bNumInterfaces, + (config->bmAttributes & 0x40) ? "Self Powered " : "Bus Powered ", + (config->bmAttributes & 0x20) ? "Remote Wakeup " : "", + config->bMaxPower*2); + if (config->iConfiguration) { + printf(" - "); + usb_display_string(dev, config->iConfiguration); + printf("\n"); + } +} + +static void usb_display_if_desc(struct usb_interface_descriptor *ifdesc, + struct usb_device *dev) +{ + printf(" Interface: %d\n", ifdesc->bInterfaceNumber); + printf(" - Alternate Setting %d, Endpoints: %d\n", + ifdesc->bAlternateSetting, ifdesc->bNumEndpoints); + printf(" - Class "); + usb_display_class_sub(ifdesc->bInterfaceClass, + ifdesc->bInterfaceSubClass, ifdesc->bInterfaceProtocol); + printf("\n"); + if (ifdesc->iInterface) { + printf(" - "); + usb_display_string(dev, ifdesc->iInterface); + printf("\n"); + } +} + +static void usb_display_ep_desc(struct usb_endpoint_descriptor *epdesc) +{ + printf(" - Endpoint %d %s ", epdesc->bEndpointAddress & 0xf, + (epdesc->bEndpointAddress & 0x80) ? "In" : "Out"); + switch ((epdesc->bmAttributes & 0x03)) { + case 0: + printf("Control"); + break; + case 1: + printf("Isochronous"); + break; + case 2: + printf("Bulk"); + break; + case 3: + printf("Interrupt"); + break; + } + printf(" MaxPacket %d", get_unaligned(&epdesc->wMaxPacketSize)); + if ((epdesc->bmAttributes & 0x03) == 0x3) + printf(" Interval %dms", epdesc->bInterval); + printf("\n"); +} + +/* main routine to diasplay the configs, interfaces and endpoints */ +static void usb_display_config(struct usb_device *dev) +{ + struct usb_config *config; + struct usb_interface *ifdesc; + struct usb_endpoint_descriptor *epdesc; + int i, ii; + + config = &dev->config; + usb_display_conf_desc(&config->desc, dev); + for (i = 0; i < config->no_of_if; i++) { + ifdesc = &config->if_desc[i]; + usb_display_if_desc(&ifdesc->desc, dev); + for (ii = 0; ii < ifdesc->no_of_ep; ii++) { + epdesc = &ifdesc->ep_desc[ii]; + usb_display_ep_desc(epdesc); + } + } + printf("\n"); +} + +static struct usb_device *usb_find_device(int devnum) +{ + struct usb_device *dev; + int d; + + for (d = 0; d < USB_MAX_DEVICE; d++) { + dev = usb_get_dev_index(d); + if (dev == NULL) + return NULL; + if (dev->devnum == devnum) + return dev; + } + + return NULL; +} + +static inline char *portspeed(int speed) +{ + char *speed_str; + + switch (speed) { + case USB_SPEED_SUPER: + speed_str = "5 Gb/s"; + break; + case USB_SPEED_HIGH: + speed_str = "480 Mb/s"; + break; + case USB_SPEED_LOW: + speed_str = "1.5 Mb/s"; + break; + default: + speed_str = "12 Mb/s"; + break; + } + + return speed_str; +} + +/* shows the device tree recursively */ +static void usb_show_tree_graph(struct usb_device *dev, char *pre) +{ + int i, index; + int has_child, last_child; + + index = strlen(pre); + printf(" %s", pre); + /* check if the device has connected children */ + has_child = 0; + for (i = 0; i < dev->maxchild; i++) { + if (dev->children[i] != NULL) + has_child = 1; + } + /* check if we are the last one */ + last_child = 1; + if (dev->parent != NULL) { + for (i = 0; i < dev->parent->maxchild; i++) { + /* search for children */ + if (dev->parent->children[i] == dev) { + /* found our pointer, see if we have a + * little sister + */ + while (i++ < dev->parent->maxchild) { + if (dev->parent->children[i] != NULL) { + /* found a sister */ + last_child = 0; + break; + } /* if */ + } /* while */ + } /* device found */ + } /* for all children of the parent */ + printf("\b+-"); + /* correct last child */ + if (last_child) + pre[index-1] = ' '; + } /* if not root hub */ + else + printf(" "); + printf("%d ", dev->devnum); + pre[index++] = ' '; + pre[index++] = has_child ? '|' : ' '; + pre[index] = 0; + printf(" %s (%s, %dmA)\n", usb_get_class_desc( + dev->config.if_desc[0].desc.bInterfaceClass), + portspeed(dev->speed), + dev->config.desc.bMaxPower * 2); + if (strlen(dev->mf) || strlen(dev->prod) || strlen(dev->serial)) + printf(" %s %s %s %s\n", pre, dev->mf, dev->prod, dev->serial); + printf(" %s\n", pre); + if (dev->maxchild > 0) { + for (i = 0; i < dev->maxchild; i++) { + if (dev->children[i] != NULL) { + usb_show_tree_graph(dev->children[i], pre); + pre[index] = 0; + } + } + } +} + +/* main routine for the tree command */ +static void usb_show_tree(struct usb_device *dev) +{ + char preamble[32]; + + memset(preamble, 0, 32); + usb_show_tree_graph(dev, &preamble[0]); +} + +static int usb_test(struct usb_device *dev, int port, char* arg) +{ + int mode; + + if (port > dev->maxchild) { + printf("Device is no hub or does not have %d ports.\n", port); + return 1; + } + + switch (arg[0]) { + case 'J': + case 'j': + printf("Setting Test_J mode"); + mode = USB_TEST_MODE_J; + break; + case 'K': + case 'k': + printf("Setting Test_K mode"); + mode = USB_TEST_MODE_K; + break; + case 'S': + case 's': + printf("Setting Test_SE0_NAK mode"); + mode = USB_TEST_MODE_SE0_NAK; + break; + case 'P': + case 'p': + printf("Setting Test_Packet mode"); + mode = USB_TEST_MODE_PACKET; + break; + case 'F': + case 'f': + printf("Setting Test_Force_Enable mode"); + mode = USB_TEST_MODE_FORCE_ENABLE; + break; + default: + printf("Unrecognized test mode: %s\nAvailable modes: " + "J, K, S[E0_NAK], P[acket], F[orce_Enable]\n", arg); + return 1; + } + + if (port) + printf(" on downstream facing port %d...\n", port); + else + printf(" on upstream facing port...\n"); + + if (usb_control_msg(dev, usb_sndctrlpipe(dev, 0), USB_REQ_SET_FEATURE, + port ? USB_RT_PORT : USB_RECIP_DEVICE, + port ? USB_PORT_FEAT_TEST : USB_FEAT_TEST, + (mode << 8) | port, + NULL, 0, USB_CNTL_TIMEOUT) == -1) { + printf("Error during SET_FEATURE.\n"); + return 1; + } else { + printf("Test mode successfully set. Use 'usb start' " + "to return to normal operation.\n"); + return 0; + } +} + + +/****************************************************************************** + * usb boot command intepreter. Derived from diskboot + */ +#ifdef CONFIG_USB_STORAGE +static int do_usbboot(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + return common_diskboot(cmdtp, "usb", argc, argv); +} +#endif /* CONFIG_USB_STORAGE */ + + +/****************************************************************************** + * usb command intepreter + */ +static int do_usb(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + + int i; + struct usb_device *dev = NULL; + extern char usb_started; +#ifdef CONFIG_USB_STORAGE + block_dev_desc_t *stor_dev; +#endif + + if (argc < 2) + return CMD_RET_USAGE; + + if ((strncmp(argv[1], "reset", 5) == 0) || + (strncmp(argv[1], "start", 5) == 0)) { + bootstage_mark_name(BOOTSTAGE_ID_USB_START, "usb_start"); + usb_stop(); + printf("(Re)start USB...\n"); + if (usb_init() >= 0) { +#ifdef CONFIG_USB_STORAGE + /* try to recognize storage devices immediately */ + usb_stor_curr_dev = usb_stor_scan(1); +#endif +#ifdef CONFIG_USB_HOST_ETHER + /* try to recognize ethernet devices immediately */ + usb_ether_curr_dev = usb_host_eth_scan(1); +#endif +#ifdef CONFIG_USB_KEYBOARD + drv_usb_kbd_init(); +#endif + } + return 0; + } + if (strncmp(argv[1], "stop", 4) == 0) { +#ifdef CONFIG_USB_KEYBOARD + if (argc == 2) { + if (usb_kbd_deregister() != 0) { + printf("USB not stopped: usbkbd still" + " using USB\n"); + return 1; + } + } else { + /* forced stop, switch console in to serial */ + console_assign(stdin, "serial"); + usb_kbd_deregister(); + } +#endif + printf("stopping USB..\n"); + usb_stop(); + return 0; + } + if (!usb_started) { + printf("USB is stopped. Please issue 'usb start' first.\n"); + return 1; + } + if (strncmp(argv[1], "tree", 4) == 0) { + puts("USB device tree:\n"); + for (i = 0; i < USB_MAX_DEVICE; i++) { + dev = usb_get_dev_index(i); + if (dev == NULL) + break; + if (dev->parent == NULL) + usb_show_tree(dev); + } + return 0; + } + if (strncmp(argv[1], "inf", 3) == 0) { + int d; + if (argc == 2) { + for (d = 0; d < USB_MAX_DEVICE; d++) { + dev = usb_get_dev_index(d); + if (dev == NULL) + break; + usb_display_desc(dev); + usb_display_config(dev); + } + return 0; + } else { + i = simple_strtoul(argv[2], NULL, 10); + printf("config for device %d\n", i); + dev = usb_find_device(i); + if (dev == NULL) { + printf("*** No device available ***\n"); + return 0; + } else { + usb_display_desc(dev); + usb_display_config(dev); + } + } + return 0; + } + if (strncmp(argv[1], "test", 4) == 0) { + if (argc < 5) + return CMD_RET_USAGE; + i = simple_strtoul(argv[2], NULL, 10); + dev = usb_find_device(i); + if (dev == NULL) { + printf("Device %d does not exist.\n", i); + return 1; + } + i = simple_strtoul(argv[3], NULL, 10); + return usb_test(dev, i, argv[4]); + } +#ifdef CONFIG_USB_STORAGE + if (strncmp(argv[1], "stor", 4) == 0) + return usb_stor_info(); + + if (strncmp(argv[1], "part", 4) == 0) { + int devno, ok = 0; + if (argc == 2) { + for (devno = 0; ; ++devno) { + stor_dev = usb_stor_get_dev(devno); + if (stor_dev == NULL) + break; + if (stor_dev->type != DEV_TYPE_UNKNOWN) { + ok++; + if (devno) + printf("\n"); + debug("print_part of %x\n", devno); + print_part(stor_dev); + } + } + } else { + devno = simple_strtoul(argv[2], NULL, 16); + stor_dev = usb_stor_get_dev(devno); + if (stor_dev != NULL && + stor_dev->type != DEV_TYPE_UNKNOWN) { + ok++; + debug("print_part of %x\n", devno); + print_part(stor_dev); + } + } + if (!ok) { + printf("\nno USB devices available\n"); + return 1; + } + return 0; + } + if (strcmp(argv[1], "read") == 0) { + if (usb_stor_curr_dev < 0) { + printf("no current device selected\n"); + return 1; + } + if (argc == 5) { + unsigned long addr = simple_strtoul(argv[2], NULL, 16); + unsigned long blk = simple_strtoul(argv[3], NULL, 16); + unsigned long cnt = simple_strtoul(argv[4], NULL, 16); + unsigned long n; + printf("\nUSB read: device %d block # %ld, count %ld" + " ... ", usb_stor_curr_dev, blk, cnt); + stor_dev = usb_stor_get_dev(usb_stor_curr_dev); + n = stor_dev->block_read(usb_stor_curr_dev, blk, cnt, + (ulong *)addr); + printf("%ld blocks read: %s\n", n, + (n == cnt) ? "OK" : "ERROR"); + if (n == cnt) + return 0; + return 1; + } + } + if (strcmp(argv[1], "write") == 0) { + if (usb_stor_curr_dev < 0) { + printf("no current device selected\n"); + return 1; + } + if (argc == 5) { + unsigned long addr = simple_strtoul(argv[2], NULL, 16); + unsigned long blk = simple_strtoul(argv[3], NULL, 16); + unsigned long cnt = simple_strtoul(argv[4], NULL, 16); + unsigned long n; + printf("\nUSB write: device %d block # %ld, count %ld" + " ... ", usb_stor_curr_dev, blk, cnt); + stor_dev = usb_stor_get_dev(usb_stor_curr_dev); + n = stor_dev->block_write(usb_stor_curr_dev, blk, cnt, + (ulong *)addr); + printf("%ld blocks write: %s\n", n, + (n == cnt) ? "OK" : "ERROR"); + if (n == cnt) + return 0; + return 1; + } + } + if (strncmp(argv[1], "dev", 3) == 0) { + if (argc == 3) { + int dev = (int)simple_strtoul(argv[2], NULL, 10); + printf("\nUSB device %d: ", dev); + stor_dev = usb_stor_get_dev(dev); + if (stor_dev == NULL) { + printf("unknown device\n"); + return 1; + } + printf("\n Device %d: ", dev); + dev_print(stor_dev); + if (stor_dev->type == DEV_TYPE_UNKNOWN) + return 1; + usb_stor_curr_dev = dev; + printf("... is now current device\n"); + return 0; + } else { + printf("\nUSB device %d: ", usb_stor_curr_dev); + stor_dev = usb_stor_get_dev(usb_stor_curr_dev); + dev_print(stor_dev); + if (stor_dev->type == DEV_TYPE_UNKNOWN) + return 1; + return 0; + } + return 0; + } +#endif /* CONFIG_USB_STORAGE */ + return CMD_RET_USAGE; +} + +U_BOOT_CMD( + usb, 5, 1, do_usb, + "USB sub-system", + "start - start (scan) USB controller\n" + "usb reset - reset (rescan) USB controller\n" + "usb stop [f] - stop USB [f]=force stop\n" + "usb tree - show USB device tree\n" + "usb info [dev] - show available USB devices\n" + "usb test [dev] [port] [mode] - set USB 2.0 test mode\n" + " (specify port 0 to indicate the device's upstream port)\n" + " Available modes: J, K, S[E0_NAK], P[acket], F[orce_Enable]\n" +#ifdef CONFIG_USB_STORAGE + "usb storage - show details of USB storage devices\n" + "usb dev [dev] - show or set current USB storage device\n" + "usb part [dev] - print partition table of one or all USB storage" + " devices\n" + "usb read addr blk# cnt - read `cnt' blocks starting at block `blk#'\n" + " to memory address `addr'\n" + "usb write addr blk# cnt - write `cnt' blocks starting at block `blk#'\n" + " from memory address `addr'" +#endif /* CONFIG_USB_STORAGE */ +); + + +#ifdef CONFIG_USB_STORAGE +U_BOOT_CMD( + usbboot, 3, 1, do_usbboot, + "boot from USB device", + "loadAddr dev:part" +); +#endif /* CONFIG_USB_STORAGE */ diff --git a/qemu/roms/u-boot/common/cmd_usb_mass_storage.c b/qemu/roms/u-boot/common/cmd_usb_mass_storage.c new file mode 100644 index 000000000..d8d9efd4f --- /dev/null +++ b/qemu/roms/u-boot/common/cmd_usb_mass_storage.c @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2011 Samsung Electronics + * Lukasz Majewski <l.majewski@samsung.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <errno.h> +#include <common.h> +#include <command.h> +#include <g_dnl.h> +#include <usb.h> +#include <usb_mass_storage.h> + +int do_usb_mass_storage(cmd_tbl_t *cmdtp, int flag, + int argc, char * const argv[]) +{ + if (argc < 3) + return CMD_RET_USAGE; + + const char *usb_controller = argv[1]; + const char *mmc_devstring = argv[2]; + + unsigned int dev_num = simple_strtoul(mmc_devstring, NULL, 0); + + struct ums *ums = ums_init(dev_num); + if (!ums) + return CMD_RET_FAILURE; + + unsigned int controller_index = (unsigned int)(simple_strtoul( + usb_controller, NULL, 0)); + if (board_usb_init(controller_index, USB_INIT_DEVICE)) { + error("Couldn't init USB controller."); + return CMD_RET_FAILURE; + } + + int rc = fsg_init(ums); + if (rc) { + error("fsg_init failed"); + return CMD_RET_FAILURE; + } + + g_dnl_register("usb_dnl_ums"); + + /* Timeout unit: seconds */ + int cable_ready_timeout = UMS_CABLE_READY_TIMEOUT; + + if (!g_dnl_board_usb_cable_connected()) { + /* + * Won't execute if we don't know whether the cable is + * connected. + */ + puts("Please connect USB cable.\n"); + + while (!g_dnl_board_usb_cable_connected()) { + if (ctrlc()) { + puts("\rCTRL+C - Operation aborted.\n"); + goto exit; + } + if (!cable_ready_timeout) { + puts("\rUSB cable not detected.\n" \ + "Command exit.\n"); + goto exit; + } + + printf("\rAuto exit in: %.2d s.", cable_ready_timeout); + mdelay(1000); + cable_ready_timeout--; + } + puts("\r\n"); + } + + while (1) { + usb_gadget_handle_interrupts(); + + rc = fsg_main_thread(NULL); + if (rc) { + /* Check I/O error */ + if (rc == -EIO) + printf("\rCheck USB cable connection\n"); + + /* Check CTRL+C */ + if (rc == -EPIPE) + printf("\rCTRL+C - Operation aborted\n"); + + goto exit; + } + } +exit: + g_dnl_unregister(); + return CMD_RET_SUCCESS; +} + +U_BOOT_CMD(ums, CONFIG_SYS_MAXARGS, 1, do_usb_mass_storage, + "Use the UMS [User Mass Storage]", + "ums <USB_controller> <mmc_dev> e.g. ums 0 0" +); diff --git a/qemu/roms/u-boot/common/cmd_version.c b/qemu/roms/u-boot/common/cmd_version.c new file mode 100644 index 000000000..1be0667f0 --- /dev/null +++ b/qemu/roms/u-boot/common/cmd_version.c @@ -0,0 +1,37 @@ +/* + * Copyright 2000-2009 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <command.h> +#include <version.h> +#include <linux/compiler.h> +#ifdef CONFIG_SYS_COREBOOT +#include <asm/arch/sysinfo.h> +#endif + +const char __weak version_string[] = U_BOOT_VERSION_STRING; + +static int do_version(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + printf("\n%s\n", version_string); +#ifdef CC_VERSION_STRING + puts(CC_VERSION_STRING "\n"); +#endif +#ifdef LD_VERSION_STRING + puts(LD_VERSION_STRING "\n"); +#endif +#ifdef CONFIG_SYS_COREBOOT + printf("coreboot-%s (%s)\n", lib_sysinfo.version, lib_sysinfo.build); +#endif + return 0; +} + +U_BOOT_CMD( + version, 1, 1, do_version, + "print monitor, compiler and linker version", + "" +); diff --git a/qemu/roms/u-boot/common/cmd_ximg.c b/qemu/roms/u-boot/common/cmd_ximg.c new file mode 100644 index 000000000..65a831966 --- /dev/null +++ b/qemu/roms/u-boot/common/cmd_ximg.c @@ -0,0 +1,266 @@ +/* + * (C) Copyright 2000-2004 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * (C) Copyright 2003 + * Kai-Uwe Bloem, Auerswald GmbH & Co KG, <linux-development@auerswald.de> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + + +/* + * Multi Image extract + */ +#include <common.h> +#include <command.h> +#include <image.h> +#include <watchdog.h> +#if defined(CONFIG_BZIP2) +#include <bzlib.h> +#endif +#include <asm/byteorder.h> +#include <asm/io.h> + +#ifndef CONFIG_SYS_XIMG_LEN +/* use 8MByte as default max gunzip size */ +#define CONFIG_SYS_XIMG_LEN 0x800000 +#endif + +static int +do_imgextract(cmd_tbl_t * cmdtp, int flag, int argc, char * const argv[]) +{ + ulong addr = load_addr; + ulong dest = 0; + ulong data, len, count; + int verify; + int part = 0; + image_header_t *hdr = NULL; +#if defined(CONFIG_FIT) + const char *uname = NULL; + const void* fit_hdr; + int noffset; + const void *fit_data; + size_t fit_len; +#endif +#ifdef CONFIG_GZIP + uint unc_len = CONFIG_SYS_XIMG_LEN; +#endif + uint8_t comp; + + verify = getenv_yesno("verify"); + + if (argc > 1) { + addr = simple_strtoul(argv[1], NULL, 16); + } + if (argc > 2) { + part = simple_strtoul(argv[2], NULL, 16); +#if defined(CONFIG_FIT) + uname = argv[2]; +#endif + } + if (argc > 3) { + dest = simple_strtoul(argv[3], NULL, 16); + } + + switch (genimg_get_format((void *)addr)) { + case IMAGE_FORMAT_LEGACY: + + printf("## Copying part %d from legacy image " + "at %08lx ...\n", part, addr); + + hdr = (image_header_t *)addr; + if (!image_check_magic(hdr)) { + printf("Bad Magic Number\n"); + return 1; + } + + if (!image_check_hcrc(hdr)) { + printf("Bad Header Checksum\n"); + return 1; + } +#ifdef DEBUG + image_print_contents(hdr); +#endif + + if (!image_check_type(hdr, IH_TYPE_MULTI)) { + printf("Wrong Image Type for %s command\n", + cmdtp->name); + return 1; + } + + comp = image_get_comp(hdr); + if ((comp != IH_COMP_NONE) && (argc < 4)) { + printf("Must specify load address for %s command " + "with compressed image\n", + cmdtp->name); + return 1; + } + + if (verify) { + printf(" Verifying Checksum ... "); + if (!image_check_dcrc(hdr)) { + printf("Bad Data CRC\n"); + return 1; + } + printf("OK\n"); + } + + count = image_multi_count(hdr); + if (part >= count) { + printf("Bad Image Part\n"); + return 1; + } + + image_multi_getimg(hdr, part, &data, &len); + break; +#if defined(CONFIG_FIT) + case IMAGE_FORMAT_FIT: + if (uname == NULL) { + puts("No FIT subimage unit name\n"); + return 1; + } + + printf("## Copying '%s' subimage from FIT image " + "at %08lx ...\n", uname, addr); + + fit_hdr = (const void *)addr; + if (!fit_check_format(fit_hdr)) { + puts("Bad FIT image format\n"); + return 1; + } + + /* get subimage node offset */ + noffset = fit_image_get_node(fit_hdr, uname); + if (noffset < 0) { + printf("Can't find '%s' FIT subimage\n", uname); + return 1; + } + + if (fit_image_check_comp(fit_hdr, noffset, IH_COMP_NONE) + && (argc < 4)) { + printf("Must specify load address for %s command " + "with compressed image\n", + cmdtp->name); + return 1; + } + + /* verify integrity */ + if (verify) { + if (!fit_image_verify(fit_hdr, noffset)) { + puts("Bad Data Hash\n"); + return 1; + } + } + + /* get subimage data address and length */ + if (fit_image_get_data(fit_hdr, noffset, + &fit_data, &fit_len)) { + puts("Could not find script subimage data\n"); + return 1; + } + + if (fit_image_get_comp(fit_hdr, noffset, &comp)) { + puts("Could not find script subimage " + "compression type\n"); + return 1; + } + + data = (ulong)fit_data; + len = (ulong)fit_len; + break; +#endif + default: + puts("Invalid image type for imxtract\n"); + return 1; + } + + if (argc > 3) { + switch (comp) { + case IH_COMP_NONE: +#if defined(CONFIG_HW_WATCHDOG) || defined(CONFIG_WATCHDOG) + { + size_t l = len; + size_t tail; + void *to = (void *) dest; + void *from = (void *)data; + + printf(" Loading part %d ... ", part); + + while (l > 0) { + tail = (l > CHUNKSZ) ? CHUNKSZ : l; + WATCHDOG_RESET(); + memmove(to, from, tail); + to += tail; + from += tail; + l -= tail; + } + } +#else /* !(CONFIG_HW_WATCHDOG || CONFIG_WATCHDOG) */ + printf(" Loading part %d ... ", part); + memmove((char *) dest, (char *)data, len); +#endif /* CONFIG_HW_WATCHDOG || CONFIG_WATCHDOG */ + break; +#ifdef CONFIG_GZIP + case IH_COMP_GZIP: + printf(" Uncompressing part %d ... ", part); + if (gunzip((void *) dest, unc_len, + (uchar *) data, &len) != 0) { + puts("GUNZIP ERROR - image not loaded\n"); + return 1; + } + break; +#endif +#if defined(CONFIG_BZIP2) + case IH_COMP_BZIP2: + { + int i; + + printf(" Uncompressing part %d ... ", part); + /* + * If we've got less than 4 MB of malloc() + * space, use slower decompression algorithm + * which requires at most 2300 KB of memory. + */ + i = BZ2_bzBuffToBuffDecompress( + map_sysmem(ntohl(hdr->ih_load), 0), + &unc_len, (char *)data, len, + CONFIG_SYS_MALLOC_LEN < (4096 * 1024), + 0); + if (i != BZ_OK) { + printf("BUNZIP2 ERROR %d - " + "image not loaded\n", i); + return 1; + } + } + break; +#endif /* CONFIG_BZIP2 */ + default: + printf("Unimplemented compression type %d\n", comp); + return 1; + } + puts("OK\n"); + } + + setenv_hex("fileaddr", data); + setenv_hex("filesize", len); + + return 0; +} + +#ifdef CONFIG_SYS_LONGHELP +static char imgextract_help_text[] = + "addr part [dest]\n" + " - extract <part> from legacy image at <addr> and copy to <dest>" +#if defined(CONFIG_FIT) + "\n" + "addr uname [dest]\n" + " - extract <uname> subimage from FIT image at <addr> and copy to <dest>" +#endif + ""; +#endif + +U_BOOT_CMD( + imxtract, 4, 1, do_imgextract, + "extract a part of a multi-image", imgextract_help_text +); diff --git a/qemu/roms/u-boot/common/cmd_yaffs2.c b/qemu/roms/u-boot/common/cmd_yaffs2.c new file mode 100644 index 000000000..d43a9d444 --- /dev/null +++ b/qemu/roms/u-boot/common/cmd_yaffs2.c @@ -0,0 +1,326 @@ +/* Yaffs commands. + * Modified by Charles Manning by adding ydevconfig command. + * + * Use ydevconfig to configure a mountpoint before use. + * For example: + * # Configure mountpt xxx using nand device 0 using blocks 100-500 + * ydevconfig xxx 0 100 500 + * # Mount it + * ymount xxx + * # yls, yrdm etc + * yls -l xxx + * yrdm xxx/boot-image 82000000 + * ... + */ + +#include <common.h> + +#include <config.h> +#include <command.h> + +#ifdef YAFFS2_DEBUG +#define PRINTF(fmt, args...) printf(fmt, ##args) +#else +#define PRINTF(fmt, args...) do { } while (0) +#endif + +extern void cmd_yaffs_dev_ls(void); +extern void cmd_yaffs_tracemask(unsigned set, unsigned mask); +extern void cmd_yaffs_devconfig(char *mp, int flash_dev, + int start_block, int end_block); +extern void cmd_yaffs_mount(char *mp); +extern void cmd_yaffs_umount(char *mp); +extern void cmd_yaffs_read_file(char *fn); +extern void cmd_yaffs_write_file(char *fn, char bval, int sizeOfFile); +extern void cmd_yaffs_ls(const char *mountpt, int longlist); +extern void cmd_yaffs_mwrite_file(char *fn, char *addr, int size); +extern void cmd_yaffs_mread_file(char *fn, char *addr); +extern void cmd_yaffs_mkdir(const char *dir); +extern void cmd_yaffs_rmdir(const char *dir); +extern void cmd_yaffs_rm(const char *path); +extern void cmd_yaffs_mv(const char *oldPath, const char *newPath); + +extern int yaffs_dump_dev(const char *path); + +/* ytrace - show/set yaffs trace mask */ +int do_ytrace(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[]) +{ + if (argc > 1) + cmd_yaffs_tracemask(1, simple_strtol(argv[1], NULL, 16)); + else + cmd_yaffs_tracemask(0, 0); + + return 0; +} + +/* ydevls - lists yaffs mount points. */ +int do_ydevls(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[]) +{ + cmd_yaffs_dev_ls(); + + return 0; +} + +/* ydevconfig mount_pt mtd_dev_num start_block end_block */ +int do_ydevconfig(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[]) +{ + char *mtpoint; + int mtd_dev; + int start_block; + int end_block; + + if (argc != 5) { + printf + ("Bad arguments: ydevconfig mount_pt mtd_dev start_block end_block\n"); + return -1; + } + + mtpoint = argv[1]; + mtd_dev = simple_strtol(argv[2], NULL, 16); + start_block = simple_strtol(argv[3], NULL, 16); + end_block = simple_strtol(argv[4], NULL, 16); + + cmd_yaffs_devconfig(mtpoint, mtd_dev, start_block, end_block); + + return 0; +} + +int do_ymount(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[]) +{ + char *mtpoint; + + if (argc != 2) { + printf("Bad arguments: ymount mount_pt\n"); + return -1; + } + + mtpoint = argv[1]; + printf("Mounting yaffs2 mount point %s\n", mtpoint); + + cmd_yaffs_mount(mtpoint); + + return 0; +} + +int do_yumount(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[]) +{ + char *mtpoint; + + if (argc != 2) { + printf("Bad arguments: yumount mount_pt\n"); + return -1; + } + + mtpoint = argv[1]; + printf("Unmounting yaffs2 mount point %s\n", mtpoint); + cmd_yaffs_umount(mtpoint); + + return 0; +} + +int do_yls(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[]) +{ + char *dirname; + + if (argc < 2 || argc > 3 || (argc == 3 && strcmp(argv[1], "-l"))) { + printf("Bad arguments: yls [-l] dir\n"); + return -1; + } + + dirname = argv[argc - 1]; + + cmd_yaffs_ls(dirname, (argc > 2) ? 1 : 0); + + return 0; +} + +int do_yrd(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[]) +{ + char *filename; + + if (argc != 2) { + printf("Bad arguments: yrd file_name\n"); + return -1; + } + + filename = argv[1]; + + printf("Reading file %s ", filename); + + cmd_yaffs_read_file(filename); + + printf("done\n"); + return 0; +} + +int do_ywr(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[]) +{ + char *filename; + ulong value; + ulong numValues; + + if (argc != 4) { + printf("Bad arguments: ywr file_name value n_values\n"); + return -1; + } + + filename = argv[1]; + value = simple_strtoul(argv[2], NULL, 16); + numValues = simple_strtoul(argv[3], NULL, 16); + + printf("Writing value (%lx) %lx times to %s... ", value, numValues, + filename); + + cmd_yaffs_write_file(filename, value, numValues); + + printf("done\n"); + return 0; +} + +int do_yrdm(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[]) +{ + char *filename; + ulong addr; + + if (argc != 3) { + printf("Bad arguments: yrdm file_name addr\n"); + return -1; + } + + filename = argv[1]; + addr = simple_strtoul(argv[2], NULL, 16); + + cmd_yaffs_mread_file(filename, (char *)addr); + + return 0; +} + +int do_ywrm(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[]) +{ + char *filename; + ulong addr; + ulong size; + + if (argc != 4) { + printf("Bad arguments: ywrm file_name addr size\n"); + return -1; + } + + filename = argv[1]; + addr = simple_strtoul(argv[2], NULL, 16); + size = simple_strtoul(argv[3], NULL, 16); + + cmd_yaffs_mwrite_file(filename, (char *)addr, size); + + return 0; +} + +int do_ymkdir(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[]) +{ + char *dirname; + + if (argc != 2) { + printf("Bad arguments: ymkdir dir_name\n"); + return -1; + } + + dirname = argv[1]; + cmd_yaffs_mkdir(dirname); + + return 0; +} + +int do_yrmdir(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[]) +{ + char *dirname; + + if (argc != 2) { + printf("Bad arguments: yrmdir dir_name\n"); + return -1; + } + + dirname = argv[1]; + cmd_yaffs_rmdir(dirname); + + return 0; +} + +int do_yrm(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[]) +{ + char *name; + + if (argc != 2) { + printf("Bad arguments: yrm name\n"); + return -1; + } + + name = argv[1]; + + cmd_yaffs_rm(name); + + return 0; +} + +int do_ymv(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[]) +{ + char *oldPath; + char *newPath; + + if (argc != 3) { + printf("Bad arguments: ymv old_path new_path\n"); + return -1; + } + + oldPath = argv[1]; + newPath = argv[2]; + + cmd_yaffs_mv(newPath, oldPath); + + return 0; +} + +U_BOOT_CMD(ytrace, 2, 0, do_ytrace, + "show/set yaffs trace mask", + "ytrace [new_mask] show/set yaffs trace mask"); + +U_BOOT_CMD(ydevls, 1, 0, do_ydevls, + "list yaffs mount points", "list yaffs mount points"); + +U_BOOT_CMD(ydevconfig, 5, 0, do_ydevconfig, + "configure yaffs mount point", + "ydevconfig mtpoint mtd_id start_block end_block configures a yaffs2 mount point"); + +U_BOOT_CMD(ymount, 2, 0, do_ymount, + "mount yaffs", "ymount mtpoint mounts a yaffs2 mount point"); + +U_BOOT_CMD(yumount, 2, 0, do_yumount, + "unmount yaffs", "yunmount mtpoint unmounts a yaffs2 mount point"); + +U_BOOT_CMD(yls, 3, 0, do_yls, "yaffs ls", "yls [-l] dirname"); + +U_BOOT_CMD(yrd, 2, 0, do_yrd, + "read file from yaffs", "yrd path read file from yaffs"); + +U_BOOT_CMD(ywr, 4, 0, do_ywr, + "write file to yaffs", + "ywr filename value num_vlues write values to yaffs file"); + +U_BOOT_CMD(yrdm, 3, 0, do_yrdm, + "read file to memory from yaffs", + "yrdm filename offset reads yaffs file into memory"); + +U_BOOT_CMD(ywrm, 4, 0, do_ywrm, + "write file from memory to yaffs", + "ywrm filename offset size writes memory to yaffs file"); + +U_BOOT_CMD(ymkdir, 2, 0, do_ymkdir, + "YAFFS mkdir", "ymkdir dir create a yaffs directory"); + +U_BOOT_CMD(yrmdir, 2, 0, do_yrmdir, + "YAFFS rmdir", "yrmdir dirname removes a yaffs directory"); + +U_BOOT_CMD(yrm, 2, 0, do_yrm, "YAFFS rm", "yrm path removes a yaffs file"); + +U_BOOT_CMD(ymv, 4, 0, do_ymv, + "YAFFS mv", + "ymv old_path new_path moves/rename files within a yaffs mount point"); diff --git a/qemu/roms/u-boot/common/cmd_zfs.c b/qemu/roms/u-boot/common/cmd_zfs.c new file mode 100644 index 000000000..0aed29e9b --- /dev/null +++ b/qemu/roms/u-boot/common/cmd_zfs.c @@ -0,0 +1,171 @@ +/* + * + * ZFS filesystem porting to Uboot by + * Jorgen Lundman <lundman at lundman.net> + * + * zfsfs support + * made from existing GRUB Sources by Sun, GNU and others. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <part.h> +#include <config.h> +#include <command.h> +#include <image.h> +#include <linux/ctype.h> +#include <asm/byteorder.h> +#include <zfs_common.h> +#include <linux/stat.h> +#include <malloc.h> + +#if defined(CONFIG_CMD_USB) && defined(CONFIG_USB_STORAGE) +#include <usb.h> +#endif + +#if !defined(CONFIG_DOS_PARTITION) && !defined(CONFIG_EFI_PARTITION) +#error DOS or EFI partition support must be selected +#endif + +#define DOS_PART_MAGIC_OFFSET 0x1fe +#define DOS_FS_TYPE_OFFSET 0x36 +#define DOS_FS32_TYPE_OFFSET 0x52 + +static int do_zfs_load(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + char *filename = NULL; + int dev; + int part; + ulong addr = 0; + disk_partition_t info; + block_dev_desc_t *dev_desc; + char buf[12]; + unsigned long count; + const char *addr_str; + struct zfs_file zfile; + struct device_s vdev; + + if (argc < 3) + return CMD_RET_USAGE; + + count = 0; + addr = simple_strtoul(argv[3], NULL, 16); + filename = getenv("bootfile"); + switch (argc) { + case 3: + addr_str = getenv("loadaddr"); + if (addr_str != NULL) + addr = simple_strtoul(addr_str, NULL, 16); + else + addr = CONFIG_SYS_LOAD_ADDR; + + break; + case 4: + break; + case 5: + filename = argv[4]; + break; + case 6: + filename = argv[4]; + count = simple_strtoul(argv[5], NULL, 16); + break; + + default: + return cmd_usage(cmdtp); + } + + if (!filename) { + puts("** No boot file defined **\n"); + return 1; + } + + part = get_device_and_partition(argv[1], argv[2], &dev_desc, &info, 1); + if (part < 0) + return 1; + + dev = dev_desc->dev; + printf("Loading file \"%s\" from %s device %d%c%c\n", + filename, argv[1], dev, + part ? ':' : ' ', part ? part + '0' : ' '); + + zfs_set_blk_dev(dev_desc, &info); + vdev.part_length = info.size; + + memset(&zfile, 0, sizeof(zfile)); + zfile.device = &vdev; + if (zfs_open(&zfile, filename)) { + printf("** File not found %s **\n", filename); + return 1; + } + + if ((count < zfile.size) && (count != 0)) + zfile.size = (uint64_t)count; + + if (zfs_read(&zfile, (char *)addr, zfile.size) != zfile.size) { + printf("** Unable to read \"%s\" from %s %d:%d **\n", + filename, argv[1], dev, part); + zfs_close(&zfile); + return 1; + } + + zfs_close(&zfile); + + /* Loading ok, update default load address */ + load_addr = addr; + + printf("%llu bytes read\n", zfile.size); + setenv_hex("filesize", zfile.size); + + return 0; +} + + +int zfs_print(const char *entry, const struct zfs_dirhook_info *data) +{ + printf("%s %s\n", + data->dir ? "<DIR> " : " ", + entry); + return 0; /* 0 continue, 1 stop */ +} + + + +static int do_zfs_ls(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + const char *filename = "/"; + int part; + block_dev_desc_t *dev_desc; + disk_partition_t info; + struct device_s vdev; + + if (argc < 2) + return cmd_usage(cmdtp); + + if (argc == 4) + filename = argv[3]; + + part = get_device_and_partition(argv[1], argv[2], &dev_desc, &info, 1); + if (part < 0) + return 1; + + zfs_set_blk_dev(dev_desc, &info); + vdev.part_length = info.size; + + zfs_ls(&vdev, filename, + zfs_print); + + return 0; +} + + +U_BOOT_CMD(zfsls, 4, 1, do_zfs_ls, + "list files in a directory (default /)", + "<interface> <dev[:part]> [directory]\n" + " - list files from 'dev' on 'interface' in a '/DATASET/@/$dir/'"); + +U_BOOT_CMD(zfsload, 6, 0, do_zfs_load, + "load binary file from a ZFS filesystem", + "<interface> <dev[:part]> [addr] [filename] [bytes]\n" + " - load binary file '/DATASET/@/$dir/$file' from 'dev' on 'interface'\n" + " to address 'addr' from ZFS filesystem"); diff --git a/qemu/roms/u-boot/common/cmd_zip.c b/qemu/roms/u-boot/common/cmd_zip.c new file mode 100644 index 000000000..7fcd9d5bf --- /dev/null +++ b/qemu/roms/u-boot/common/cmd_zip.c @@ -0,0 +1,42 @@ +/* + * (C) Copyright 2012 + * Lei Wen <leiwen@marvell.com>, Marvell Inc. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <command.h> + +static int do_zip(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + unsigned long src, dst; + unsigned long src_len, dst_len = ~0UL; + + switch (argc) { + case 5: + dst_len = simple_strtoul(argv[4], NULL, 16); + /* fall through */ + case 4: + src = simple_strtoul(argv[1], NULL, 16); + src_len = simple_strtoul(argv[2], NULL, 16); + dst = simple_strtoul(argv[3], NULL, 16); + break; + default: + return cmd_usage(cmdtp); + } + + if (gzip((void *) dst, &dst_len, (void *) src, src_len) != 0) + return 1; + + printf("Compressed size: %ld = 0x%lX\n", dst_len, dst_len); + setenv_hex("filesize", dst_len); + + return 0; +} + +U_BOOT_CMD( + zip, 5, 1, do_zip, + "zip a memory region", + "srcaddr srcsize dstaddr [dstsize]" +); diff --git a/qemu/roms/u-boot/common/command.c b/qemu/roms/u-boot/common/command.c new file mode 100644 index 000000000..746b7e3f0 --- /dev/null +++ b/qemu/roms/u-boot/common/command.c @@ -0,0 +1,554 @@ +/* + * (C) Copyright 2000-2009 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/* + * Command Processor Table + */ + +#include <common.h> +#include <command.h> +#include <linux/ctype.h> + +/* + * Use puts() instead of printf() to avoid printf buffer overflow + * for long help messages + */ + +int _do_help (cmd_tbl_t *cmd_start, int cmd_items, cmd_tbl_t * cmdtp, int + flag, int argc, char * const argv[]) +{ + int i; + int rcode = 0; + + if (argc == 1) { /*show list of commands */ + cmd_tbl_t *cmd_array[cmd_items]; + int i, j, swaps; + + /* Make array of commands from .uboot_cmd section */ + cmdtp = cmd_start; + for (i = 0; i < cmd_items; i++) { + cmd_array[i] = cmdtp++; + } + + /* Sort command list (trivial bubble sort) */ + for (i = cmd_items - 1; i > 0; --i) { + swaps = 0; + for (j = 0; j < i; ++j) { + if (strcmp (cmd_array[j]->name, + cmd_array[j + 1]->name) > 0) { + cmd_tbl_t *tmp; + tmp = cmd_array[j]; + cmd_array[j] = cmd_array[j + 1]; + cmd_array[j + 1] = tmp; + ++swaps; + } + } + if (!swaps) + break; + } + + /* print short help (usage) */ + for (i = 0; i < cmd_items; i++) { + const char *usage = cmd_array[i]->usage; + + /* allow user abort */ + if (ctrlc ()) + return 1; + if (usage == NULL) + continue; + printf("%-*s- %s\n", CONFIG_SYS_HELP_CMD_WIDTH, + cmd_array[i]->name, usage); + } + return 0; + } + /* + * command help (long version) + */ + for (i = 1; i < argc; ++i) { + if ((cmdtp = find_cmd_tbl (argv[i], cmd_start, cmd_items )) != NULL) { + rcode |= cmd_usage(cmdtp); + } else { + printf ("Unknown command '%s' - try 'help'" + " without arguments for list of all" + " known commands\n\n", argv[i] + ); + rcode = 1; + } + } + return rcode; +} + +/*************************************************************************** + * find command table entry for a command + */ +cmd_tbl_t *find_cmd_tbl (const char *cmd, cmd_tbl_t *table, int table_len) +{ + cmd_tbl_t *cmdtp; + cmd_tbl_t *cmdtp_temp = table; /*Init value */ + const char *p; + int len; + int n_found = 0; + + if (!cmd) + return NULL; + /* + * Some commands allow length modifiers (like "cp.b"); + * compare command name only until first dot. + */ + len = ((p = strchr(cmd, '.')) == NULL) ? strlen (cmd) : (p - cmd); + + for (cmdtp = table; + cmdtp != table + table_len; + cmdtp++) { + if (strncmp (cmd, cmdtp->name, len) == 0) { + if (len == strlen (cmdtp->name)) + return cmdtp; /* full match */ + + cmdtp_temp = cmdtp; /* abbreviated command ? */ + n_found++; + } + } + if (n_found == 1) { /* exactly one match */ + return cmdtp_temp; + } + + return NULL; /* not found or ambiguous command */ +} + +cmd_tbl_t *find_cmd (const char *cmd) +{ + cmd_tbl_t *start = ll_entry_start(cmd_tbl_t, cmd); + const int len = ll_entry_count(cmd_tbl_t, cmd); + return find_cmd_tbl(cmd, start, len); +} + +int cmd_usage(const cmd_tbl_t *cmdtp) +{ + printf("%s - %s\n\n", cmdtp->name, cmdtp->usage); + +#ifdef CONFIG_SYS_LONGHELP + printf("Usage:\n%s ", cmdtp->name); + + if (!cmdtp->help) { + puts ("- No additional help available.\n"); + return 1; + } + + puts (cmdtp->help); + putc ('\n'); +#endif /* CONFIG_SYS_LONGHELP */ + return 1; +} + +#ifdef CONFIG_AUTO_COMPLETE + +int var_complete(int argc, char * const argv[], char last_char, int maxv, char *cmdv[]) +{ + static char tmp_buf[512]; + int space; + + space = last_char == '\0' || isblank(last_char); + + if (space && argc == 1) + return env_complete("", maxv, cmdv, sizeof(tmp_buf), tmp_buf); + + if (!space && argc == 2) + return env_complete(argv[1], maxv, cmdv, sizeof(tmp_buf), tmp_buf); + + return 0; +} + +/*************************************************************************************/ + +static int complete_cmdv(int argc, char * const argv[], char last_char, int maxv, char *cmdv[]) +{ + cmd_tbl_t *cmdtp = ll_entry_start(cmd_tbl_t, cmd); + const int count = ll_entry_count(cmd_tbl_t, cmd); + const cmd_tbl_t *cmdend = cmdtp + count; + const char *p; + int len, clen; + int n_found = 0; + const char *cmd; + + /* sanity? */ + if (maxv < 2) + return -2; + + cmdv[0] = NULL; + + if (argc == 0) { + /* output full list of commands */ + for (; cmdtp != cmdend; cmdtp++) { + if (n_found >= maxv - 2) { + cmdv[n_found++] = "..."; + break; + } + cmdv[n_found++] = cmdtp->name; + } + cmdv[n_found] = NULL; + return n_found; + } + + /* more than one arg or one but the start of the next */ + if (argc > 1 || (last_char == '\0' || isblank(last_char))) { + cmdtp = find_cmd(argv[0]); + if (cmdtp == NULL || cmdtp->complete == NULL) { + cmdv[0] = NULL; + return 0; + } + return (*cmdtp->complete)(argc, argv, last_char, maxv, cmdv); + } + + cmd = argv[0]; + /* + * Some commands allow length modifiers (like "cp.b"); + * compare command name only until first dot. + */ + p = strchr(cmd, '.'); + if (p == NULL) + len = strlen(cmd); + else + len = p - cmd; + + /* return the partial matches */ + for (; cmdtp != cmdend; cmdtp++) { + + clen = strlen(cmdtp->name); + if (clen < len) + continue; + + if (memcmp(cmd, cmdtp->name, len) != 0) + continue; + + /* too many! */ + if (n_found >= maxv - 2) { + cmdv[n_found++] = "..."; + break; + } + + cmdv[n_found++] = cmdtp->name; + } + + cmdv[n_found] = NULL; + return n_found; +} + +static int make_argv(char *s, int argvsz, char *argv[]) +{ + int argc = 0; + + /* split into argv */ + while (argc < argvsz - 1) { + + /* skip any white space */ + while (isblank(*s)) + ++s; + + if (*s == '\0') /* end of s, no more args */ + break; + + argv[argc++] = s; /* begin of argument string */ + + /* find end of string */ + while (*s && !isblank(*s)) + ++s; + + if (*s == '\0') /* end of s, no more args */ + break; + + *s++ = '\0'; /* terminate current arg */ + } + argv[argc] = NULL; + + return argc; +} + +static void print_argv(const char *banner, const char *leader, const char *sep, int linemax, char * const argv[]) +{ + int ll = leader != NULL ? strlen(leader) : 0; + int sl = sep != NULL ? strlen(sep) : 0; + int len, i; + + if (banner) { + puts("\n"); + puts(banner); + } + + i = linemax; /* force leader and newline */ + while (*argv != NULL) { + len = strlen(*argv) + sl; + if (i + len >= linemax) { + puts("\n"); + if (leader) + puts(leader); + i = ll - sl; + } else if (sep) + puts(sep); + puts(*argv++); + i += len; + } + printf("\n"); +} + +static int find_common_prefix(char * const argv[]) +{ + int i, len; + char *anchor, *s, *t; + + if (*argv == NULL) + return 0; + + /* begin with max */ + anchor = *argv++; + len = strlen(anchor); + while ((t = *argv++) != NULL) { + s = anchor; + for (i = 0; i < len; i++, t++, s++) { + if (*t != *s) + break; + } + len = s - anchor; + } + return len; +} + +static char tmp_buf[CONFIG_SYS_CBSIZE]; /* copy of console I/O buffer */ + +int cmd_auto_complete(const char *const prompt, char *buf, int *np, int *colp) +{ + int n = *np, col = *colp; + char *argv[CONFIG_SYS_MAXARGS + 1]; /* NULL terminated */ + char *cmdv[20]; + char *s, *t; + const char *sep; + int i, j, k, len, seplen, argc; + int cnt; + char last_char; + + if (strcmp(prompt, CONFIG_SYS_PROMPT) != 0) + return 0; /* not in normal console */ + + cnt = strlen(buf); + if (cnt >= 1) + last_char = buf[cnt - 1]; + else + last_char = '\0'; + + /* copy to secondary buffer which will be affected */ + strcpy(tmp_buf, buf); + + /* separate into argv */ + argc = make_argv(tmp_buf, sizeof(argv)/sizeof(argv[0]), argv); + + /* do the completion and return the possible completions */ + i = complete_cmdv(argc, argv, last_char, sizeof(cmdv)/sizeof(cmdv[0]), cmdv); + + /* no match; bell and out */ + if (i == 0) { + if (argc > 1) /* allow tab for non command */ + return 0; + putc('\a'); + return 1; + } + + s = NULL; + len = 0; + sep = NULL; + seplen = 0; + if (i == 1) { /* one match; perfect */ + k = strlen(argv[argc - 1]); + s = cmdv[0] + k; + len = strlen(s); + sep = " "; + seplen = 1; + } else if (i > 1 && (j = find_common_prefix(cmdv)) != 0) { /* more */ + k = strlen(argv[argc - 1]); + j -= k; + if (j > 0) { + s = cmdv[0] + k; + len = j; + } + } + + if (s != NULL) { + k = len + seplen; + /* make sure it fits */ + if (n + k >= CONFIG_SYS_CBSIZE - 2) { + putc('\a'); + return 1; + } + + t = buf + cnt; + for (i = 0; i < len; i++) + *t++ = *s++; + if (sep != NULL) + for (i = 0; i < seplen; i++) + *t++ = sep[i]; + *t = '\0'; + n += k; + col += k; + puts(t - k); + if (sep == NULL) + putc('\a'); + *np = n; + *colp = col; + } else { + print_argv(NULL, " ", " ", 78, cmdv); + + puts(prompt); + puts(buf); + } + return 1; +} + +#endif + +#ifdef CMD_DATA_SIZE +int cmd_get_data_size(char* arg, int default_size) +{ + /* Check for a size specification .b, .w or .l. + */ + int len = strlen(arg); + if (len > 2 && arg[len-2] == '.') { + switch(arg[len-1]) { + case 'b': + return 1; + case 'w': + return 2; + case 'l': + return 4; +#ifdef CONFIG_SYS_SUPPORT_64BIT_DATA + case 'q': + return 8; +#endif + case 's': + return -2; + default: + return -1; + } + } + return default_size; +} +#endif + +#if defined(CONFIG_NEEDS_MANUAL_RELOC) +DECLARE_GLOBAL_DATA_PTR; + +void fixup_cmdtable(cmd_tbl_t *cmdtp, int size) +{ + int i; + + if (gd->reloc_off == 0) + return; + + for (i = 0; i < size; i++) { + ulong addr; + + addr = (ulong) (cmdtp->cmd) + gd->reloc_off; +#if DEBUG_COMMANDS + printf("Command \"%s\": 0x%08lx => 0x%08lx\n", + cmdtp->name, (ulong) (cmdtp->cmd), addr); +#endif + cmdtp->cmd = + (int (*)(struct cmd_tbl_s *, int, int, char * const []))addr; + addr = (ulong)(cmdtp->name) + gd->reloc_off; + cmdtp->name = (char *)addr; + if (cmdtp->usage) { + addr = (ulong)(cmdtp->usage) + gd->reloc_off; + cmdtp->usage = (char *)addr; + } +#ifdef CONFIG_SYS_LONGHELP + if (cmdtp->help) { + addr = (ulong)(cmdtp->help) + gd->reloc_off; + cmdtp->help = (char *)addr; + } +#endif +#ifdef CONFIG_AUTO_COMPLETE + if (cmdtp->complete) { + addr = (ulong)(cmdtp->complete) + gd->reloc_off; + cmdtp->complete = + (int (*)(int, char * const [], char, int, char * []))addr; + } +#endif + cmdtp++; + } +} +#endif + +/** + * Call a command function. This should be the only route in U-Boot to call + * a command, so that we can track whether we are waiting for input or + * executing a command. + * + * @param cmdtp Pointer to the command to execute + * @param flag Some flags normally 0 (see CMD_FLAG_.. above) + * @param argc Number of arguments (arg 0 must be the command text) + * @param argv Arguments + * @return 0 if command succeeded, else non-zero (CMD_RET_...) + */ +static int cmd_call(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + int result; + + result = (cmdtp->cmd)(cmdtp, flag, argc, argv); + if (result) + debug("Command failed, result=%d", result); + return result; +} + +enum command_ret_t cmd_process(int flag, int argc, char * const argv[], + int *repeatable, ulong *ticks) +{ + enum command_ret_t rc = CMD_RET_SUCCESS; + cmd_tbl_t *cmdtp; + + /* Look up command in command table */ + cmdtp = find_cmd(argv[0]); + if (cmdtp == NULL) { + printf("Unknown command '%s' - try 'help'\n", argv[0]); + return 1; + } + + /* found - check max args */ + if (argc > cmdtp->maxargs) + rc = CMD_RET_USAGE; + +#if defined(CONFIG_CMD_BOOTD) + /* avoid "bootd" recursion */ + else if (cmdtp->cmd == do_bootd) { + if (flag & CMD_FLAG_BOOTD) { + puts("'bootd' recursion detected\n"); + rc = CMD_RET_FAILURE; + } else { + flag |= CMD_FLAG_BOOTD; + } + } +#endif + + /* If OK so far, then do the command */ + if (!rc) { + if (ticks) + *ticks = get_timer(0); + rc = cmd_call(cmdtp, flag, argc, argv); + if (ticks) + *ticks = get_timer(*ticks); + *repeatable &= cmdtp->repeatable; + } + if (rc == CMD_RET_USAGE) + rc = cmd_usage(cmdtp); + return rc; +} + +int cmd_process_error(cmd_tbl_t *cmdtp, int err) +{ + if (err) { + printf("Command '%s' failed: Error %d\n", cmdtp->name, err); + return 1; + } + + return 0; +} diff --git a/qemu/roms/u-boot/common/console.c b/qemu/roms/u-boot/common/console.c new file mode 100644 index 000000000..2dfb78888 --- /dev/null +++ b/qemu/roms/u-boot/common/console.c @@ -0,0 +1,846 @@ +/* + * (C) Copyright 2000 + * Paolo Scaffardi, AIRVENT SAM s.p.a - RIMINI(ITALY), arsenio@tin.it + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <stdarg.h> +#include <malloc.h> +#include <os.h> +#include <serial.h> +#include <stdio_dev.h> +#include <exports.h> +#include <environment.h> + +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_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 + * are switched to the serial port, else the settings in the + * environment are used + */ +#ifdef CONFIG_SYS_CONSOLE_OVERWRITE_ROUTINE +extern int overwrite_console(void); +#define OVERWRITE_CONSOLE overwrite_console() +#else +#define OVERWRITE_CONSOLE 0 +#endif /* CONFIG_SYS_CONSOLE_OVERWRITE_ROUTINE */ + +#endif /* CONFIG_SYS_CONSOLE_IS_IN_ENV */ + +static int console_setfile(int file, struct stdio_dev * dev) +{ + int error = 0; + + if (dev == NULL) + return -1; + + switch (file) { + case stdin: + case stdout: + case stderr: + /* Start new device */ + if (dev->start) { + error = dev->start(); + /* If it's not started dont use it */ + if (error < 0) + break; + } + + /* Assign the new device (leaving the existing one started) */ + stdio_devices[file] = dev; + + /* + * Update monitor functions + * (to use the console stuff by other applications) + */ + switch (file) { + case stdin: + gd->jt[XF_getc] = dev->getc; + gd->jt[XF_tstc] = dev->tstc; + break; + case stdout: + gd->jt[XF_putc] = dev->putc; + gd->jt[XF_puts] = dev->puts; + gd->jt[XF_printf] = printf; + break; + } + break; + + default: /* Invalid file ID */ + error = -1; + } + return error; +} + +#if defined(CONFIG_CONSOLE_MUX) +/** Console I/O multiplexing *******************************************/ + +static struct stdio_dev *tstcdev; +struct stdio_dev **console_devices[MAX_FILES]; +int cd_count[MAX_FILES]; + +/* + * This depends on tstc() always being called before getc(). + * This is guaranteed to be true because this routine is called + * only from fgetc() which assures it. + * No attempt is made to demultiplex multiple input sources. + */ +static int console_getc(int file) +{ + unsigned char ret; + + /* This is never called with testcdev == NULL */ + ret = tstcdev->getc(); + tstcdev = NULL; + return ret; +} + +static int console_tstc(int file) +{ + int i, ret; + struct stdio_dev *dev; + + disable_ctrlc(1); + for (i = 0; i < cd_count[file]; i++) { + dev = console_devices[file][i]; + if (dev->tstc != NULL) { + ret = dev->tstc(); + if (ret > 0) { + tstcdev = dev; + disable_ctrlc(0); + return ret; + } + } + } + disable_ctrlc(0); + + return 0; +} + +static void console_putc(int file, const char c) +{ + int i; + struct stdio_dev *dev; + + for (i = 0; i < cd_count[file]; i++) { + dev = console_devices[file][i]; + if (dev->putc != NULL) + dev->putc(c); + } +} + +static void console_puts(int file, const char *s) +{ + int i; + struct stdio_dev *dev; + + for (i = 0; i < cd_count[file]; i++) { + dev = console_devices[file][i]; + if (dev->puts != NULL) + dev->puts(s); + } +} + +static inline void console_printdevs(int file) +{ + iomux_printdevs(file); +} + +static inline void console_doenv(int file, struct stdio_dev *dev) +{ + iomux_doenv(file, dev->name); +} +#else +static inline int console_getc(int file) +{ + return stdio_devices[file]->getc(); +} + +static inline int console_tstc(int file) +{ + return stdio_devices[file]->tstc(); +} + +static inline void console_putc(int file, const char c) +{ + stdio_devices[file]->putc(c); +} + +static inline void console_puts(int file, const char *s) +{ + stdio_devices[file]->puts(s); +} + +static inline void console_printdevs(int file) +{ + printf("%s\n", stdio_devices[file]->name); +} + +static inline void console_doenv(int file, struct stdio_dev *dev) +{ + console_setfile(file, dev); +} +#endif /* defined(CONFIG_CONSOLE_MUX) */ + +/** U-Boot INITIAL CONSOLE-NOT COMPATIBLE FUNCTIONS *************************/ + +int serial_printf(const char *fmt, ...) +{ + va_list args; + uint i; + char printbuffer[CONFIG_SYS_PBSIZE]; + + va_start(args, fmt); + + /* For this to work, printbuffer must be larger than + * anything we ever want to print. + */ + i = vscnprintf(printbuffer, sizeof(printbuffer), fmt, args); + va_end(args); + + serial_puts(printbuffer); + return i; +} + +int fgetc(int file) +{ + if (file < MAX_FILES) { +#if defined(CONFIG_CONSOLE_MUX) + /* + * Effectively poll for input wherever it may be available. + */ + for (;;) { + /* + * Upper layer may have already called tstc() so + * check for that first. + */ + if (tstcdev != NULL) + return console_getc(file); + console_tstc(file); +#ifdef CONFIG_WATCHDOG + /* + * If the watchdog must be rate-limited then it should + * already be handled in board-specific code. + */ + udelay(1); +#endif + } +#else + return console_getc(file); +#endif + } + + return -1; +} + +int ftstc(int file) +{ + if (file < MAX_FILES) + return console_tstc(file); + + return -1; +} + +void fputc(int file, const char c) +{ + if (file < MAX_FILES) + console_putc(file, c); +} + +void fputs(int file, const char *s) +{ + if (file < MAX_FILES) + console_puts(file, s); +} + +int fprintf(int file, const char *fmt, ...) +{ + va_list args; + uint i; + char printbuffer[CONFIG_SYS_PBSIZE]; + + va_start(args, fmt); + + /* For this to work, printbuffer must be larger than + * anything we ever want to print. + */ + i = vscnprintf(printbuffer, sizeof(printbuffer), fmt, args); + va_end(args); + + /* Send to desired file */ + fputs(file, printbuffer); + return i; +} + +/** U-Boot INITIAL CONSOLE-COMPATIBLE FUNCTION *****************************/ + +int getc(void) +{ +#ifdef CONFIG_DISABLE_CONSOLE + if (gd->flags & GD_FLG_DISABLE_CONSOLE) + return 0; +#endif + + if (!gd->have_console) + return 0; + + if (gd->flags & GD_FLG_DEVINIT) { + /* Get from the standard input */ + return fgetc(stdin); + } + + /* Send directly to the handler */ + return serial_getc(); +} + +int tstc(void) +{ +#ifdef CONFIG_DISABLE_CONSOLE + if (gd->flags & GD_FLG_DISABLE_CONSOLE) + return 0; +#endif + + if (!gd->have_console) + return 0; + + if (gd->flags & GD_FLG_DEVINIT) { + /* Test the standard input */ + return ftstc(stdin); + } + + /* Send directly to the handler */ + return serial_tstc(); +} + +#ifdef CONFIG_PRE_CONSOLE_BUFFER +#define CIRC_BUF_IDX(idx) ((idx) % (unsigned long)CONFIG_PRE_CON_BUF_SZ) + +static void pre_console_putc(const char c) +{ + char *buffer = (char *)CONFIG_PRE_CON_BUF_ADDR; + + buffer[CIRC_BUF_IDX(gd->precon_buf_idx++)] = c; +} + +static void pre_console_puts(const char *s) +{ + while (*s) + pre_console_putc(*s++); +} + +static void print_pre_console_buffer(void) +{ + unsigned long i = 0; + char *buffer = (char *)CONFIG_PRE_CON_BUF_ADDR; + + if (gd->precon_buf_idx > CONFIG_PRE_CON_BUF_SZ) + i = gd->precon_buf_idx - CONFIG_PRE_CON_BUF_SZ; + + while (i < gd->precon_buf_idx) + putc(buffer[CIRC_BUF_IDX(i++)]); +} +#else +static inline void pre_console_putc(const char c) {} +static inline void pre_console_puts(const char *s) {} +static inline void print_pre_console_buffer(void) {} +#endif + +void putc(const char c) +{ +#ifdef CONFIG_SANDBOX + if (!gd) { + os_putc(c); + return; + } +#endif +#ifdef CONFIG_SILENT_CONSOLE + if (gd->flags & GD_FLG_SILENT) + return; +#endif + +#ifdef CONFIG_DISABLE_CONSOLE + if (gd->flags & GD_FLG_DISABLE_CONSOLE) + return; +#endif + + if (!gd->have_console) + return pre_console_putc(c); + + if (gd->flags & GD_FLG_DEVINIT) { + /* Send to the standard output */ + fputc(stdout, c); + } else { + /* Send directly to the handler */ + serial_putc(c); + } +} + +void puts(const char *s) +{ +#ifdef CONFIG_SANDBOX + if (!gd) { + os_puts(s); + return; + } +#endif + +#ifdef CONFIG_SILENT_CONSOLE + if (gd->flags & GD_FLG_SILENT) + return; +#endif + +#ifdef CONFIG_DISABLE_CONSOLE + if (gd->flags & GD_FLG_DISABLE_CONSOLE) + return; +#endif + + if (!gd->have_console) + return pre_console_puts(s); + + if (gd->flags & GD_FLG_DEVINIT) { + /* Send to the standard output */ + fputs(stdout, s); + } else { + /* Send directly to the handler */ + serial_puts(s); + } +} + +int printf(const char *fmt, ...) +{ + va_list args; + uint i; + char printbuffer[CONFIG_SYS_PBSIZE]; + +#if !defined(CONFIG_SANDBOX) && !defined(CONFIG_PRE_CONSOLE_BUFFER) + if (!gd->have_console) + return 0; +#endif + + va_start(args, fmt); + + /* For this to work, printbuffer must be larger than + * anything we ever want to print. + */ + i = vscnprintf(printbuffer, sizeof(printbuffer), fmt, args); + va_end(args); + + /* Print the string */ + puts(printbuffer); + return i; +} + +int vprintf(const char *fmt, va_list args) +{ + uint i; + char printbuffer[CONFIG_SYS_PBSIZE]; + +#ifndef CONFIG_PRE_CONSOLE_BUFFER + if (!gd->have_console) + return 0; +#endif + + /* For this to work, printbuffer must be larger than + * anything we ever want to print. + */ + i = vscnprintf(printbuffer, sizeof(printbuffer), fmt, args); + + /* Print the string */ + puts(printbuffer); + return i; +} + +/* test if ctrl-c was pressed */ +static int ctrlc_disabled = 0; /* see disable_ctrl() */ +static int ctrlc_was_pressed = 0; +int ctrlc(void) +{ + if (!ctrlc_disabled && gd->have_console) { + if (tstc()) { + switch (getc()) { + case 0x03: /* ^C - Control C */ + ctrlc_was_pressed = 1; + return 1; + default: + break; + } + } + } + return 0; +} + +/* pass 1 to disable ctrlc() checking, 0 to enable. + * returns previous state + */ +int disable_ctrlc(int disable) +{ + int prev = ctrlc_disabled; /* save previous state */ + + ctrlc_disabled = disable; + return prev; +} + +int had_ctrlc (void) +{ + return ctrlc_was_pressed; +} + +void clear_ctrlc(void) +{ + ctrlc_was_pressed = 0; +} + +#ifdef CONFIG_MODEM_SUPPORT_DEBUG +char screen[1024]; +char *cursor = screen; +int once = 0; +inline void dbg(const char *fmt, ...) +{ + va_list args; + uint i; + char printbuffer[CONFIG_SYS_PBSIZE]; + + if (!once) { + memset(screen, 0, sizeof(screen)); + once++; + } + + va_start(args, fmt); + + /* For this to work, printbuffer must be larger than + * anything we ever want to print. + */ + i = vsnprintf(printbuffer, sizeof(printbuffer), fmt, args); + va_end(args); + + if ((screen + sizeof(screen) - 1 - cursor) + < strlen(printbuffer) + 1) { + memset(screen, 0, sizeof(screen)); + cursor = screen; + } + sprintf(cursor, printbuffer); + cursor += strlen(printbuffer); + +} +#else +inline void dbg(const char *fmt, ...) +{ +} +#endif + +/** U-Boot INIT FUNCTIONS *************************************************/ + +struct stdio_dev *search_device(int flags, const char *name) +{ + struct stdio_dev *dev; + + dev = stdio_get_by_name(name); + + if (dev && (dev->flags & flags)) + return dev; + + return NULL; +} + +int console_assign(int file, const char *devname) +{ + int flag; + struct stdio_dev *dev; + + /* Check for valid file */ + switch (file) { + case stdin: + flag = DEV_FLAGS_INPUT; + break; + case stdout: + case stderr: + flag = DEV_FLAGS_OUTPUT; + break; + default: + return -1; + } + + /* Check for valid device name */ + + dev = search_device(flag, devname); + + if (dev) + return console_setfile(file, dev); + + return -1; +} + +/* Called before relocation - use serial functions */ +int console_init_f(void) +{ + gd->have_console = 1; + +#ifdef CONFIG_SILENT_CONSOLE + if (getenv("silent") != NULL) + gd->flags |= GD_FLG_SILENT; +#endif + + print_pre_console_buffer(); + + return 0; +} + +void stdio_print_current_devices(void) +{ + /* Print information */ + puts("In: "); + if (stdio_devices[stdin] == NULL) { + puts("No input devices available!\n"); + } else { + printf ("%s\n", stdio_devices[stdin]->name); + } + + puts("Out: "); + if (stdio_devices[stdout] == NULL) { + puts("No output devices available!\n"); + } else { + printf ("%s\n", stdio_devices[stdout]->name); + } + + puts("Err: "); + if (stdio_devices[stderr] == NULL) { + puts("No error devices available!\n"); + } else { + printf ("%s\n", stdio_devices[stderr]->name); + } +} + +#ifdef CONFIG_SYS_CONSOLE_IS_IN_ENV +/* Called after the relocation - use desired console functions */ +int console_init_r(void) +{ + char *stdinname, *stdoutname, *stderrname; + struct stdio_dev *inputdev = NULL, *outputdev = NULL, *errdev = NULL; +#ifdef CONFIG_SYS_CONSOLE_ENV_OVERWRITE + int i; +#endif /* CONFIG_SYS_CONSOLE_ENV_OVERWRITE */ +#ifdef CONFIG_CONSOLE_MUX + int iomux_err = 0; +#endif + + /* set default handlers at first */ + gd->jt[XF_getc] = serial_getc; + gd->jt[XF_tstc] = serial_tstc; + gd->jt[XF_putc] = serial_putc; + gd->jt[XF_puts] = serial_puts; + gd->jt[XF_printf] = serial_printf; + + /* stdin stdout and stderr are in environment */ + /* scan for it */ + stdinname = getenv("stdin"); + stdoutname = getenv("stdout"); + stderrname = getenv("stderr"); + + if (OVERWRITE_CONSOLE == 0) { /* if not overwritten by config switch */ + inputdev = search_device(DEV_FLAGS_INPUT, stdinname); + outputdev = search_device(DEV_FLAGS_OUTPUT, stdoutname); + errdev = search_device(DEV_FLAGS_OUTPUT, stderrname); +#ifdef CONFIG_CONSOLE_MUX + iomux_err = iomux_doenv(stdin, stdinname); + iomux_err += iomux_doenv(stdout, stdoutname); + iomux_err += iomux_doenv(stderr, stderrname); + if (!iomux_err) + /* Successful, so skip all the code below. */ + goto done; +#endif + } + /* if the devices are overwritten or not found, use default device */ + if (inputdev == NULL) { + inputdev = search_device(DEV_FLAGS_INPUT, "serial"); + } + if (outputdev == NULL) { + outputdev = search_device(DEV_FLAGS_OUTPUT, "serial"); + } + if (errdev == NULL) { + errdev = search_device(DEV_FLAGS_OUTPUT, "serial"); + } + /* Initializes output console first */ + if (outputdev != NULL) { + /* need to set a console if not done above. */ + console_doenv(stdout, outputdev); + } + if (errdev != NULL) { + /* need to set a console if not done above. */ + console_doenv(stderr, errdev); + } + if (inputdev != NULL) { + /* need to set a console if not done above. */ + console_doenv(stdin, inputdev); + } + +#ifdef CONFIG_CONSOLE_MUX +done: +#endif + +#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) */ + for (i = 0; i < 3; i++) { + setenv(stdio_names[i], stdio_devices[i]->name); + } +#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)) + return 0; +#endif + return 0; +} + +#else /* CONFIG_SYS_CONSOLE_IS_IN_ENV */ + +/* Called after the relocation - use desired console functions */ +int console_init_r(void) +{ + struct stdio_dev *inputdev = NULL, *outputdev = NULL; + int i; + struct list_head *list = stdio_get_list(); + struct list_head *pos; + struct stdio_dev *dev; + +#ifdef CONFIG_SPLASH_SCREEN + /* + * suppress all output if splash screen is enabled and we have + * a bmp to display. We redirect the output from frame buffer + * console to serial console in this case or suppress it if + * "silent" mode was requested. + */ + if (getenv("splashimage") != NULL) { + if (!(gd->flags & GD_FLG_SILENT)) + outputdev = search_device (DEV_FLAGS_OUTPUT, "serial"); + } +#endif + + /* Scan devices looking for input and output devices */ + list_for_each(pos, list) { + dev = list_entry(pos, struct stdio_dev, list); + + if ((dev->flags & DEV_FLAGS_INPUT) && (inputdev == NULL)) { + inputdev = dev; + } + if ((dev->flags & DEV_FLAGS_OUTPUT) && (outputdev == NULL)) { + outputdev = dev; + } + if(inputdev && outputdev) + break; + } + + /* Initializes output console first */ + if (outputdev != NULL) { + console_setfile(stdout, outputdev); + console_setfile(stderr, outputdev); +#ifdef CONFIG_CONSOLE_MUX + console_devices[stdout][0] = outputdev; + console_devices[stderr][0] = outputdev; +#endif + } + + /* Initializes input console */ + if (inputdev != NULL) { + console_setfile(stdin, inputdev); +#ifdef CONFIG_CONSOLE_MUX + console_devices[stdin][0] = inputdev; +#endif + } + +#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++) { + 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)) + return 0; +#endif + + return 0; +} + +#endif /* CONFIG_SYS_CONSOLE_IS_IN_ENV */ diff --git a/qemu/roms/u-boot/common/cros_ec.c b/qemu/roms/u-boot/common/cros_ec.c new file mode 100644 index 000000000..b8ce1b581 --- /dev/null +++ b/qemu/roms/u-boot/common/cros_ec.c @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2012 The Chromium OS Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + * + * 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 <common.h> +#include <cros_ec.h> +DECLARE_GLOBAL_DATA_PTR; + +struct local_info { + struct cros_ec_dev *cros_ec_dev; /* Pointer to cros_ec device */ + int cros_ec_err; /* Error for cros_ec, 0 if ok */ +}; + +static struct local_info local; + +struct cros_ec_dev *board_get_cros_ec_dev(void) +{ + return local.cros_ec_dev; +} + +static int board_init_cros_ec_devices(const void *blob) +{ + local.cros_ec_err = cros_ec_init(blob, &local.cros_ec_dev); + if (local.cros_ec_err) + return -1; /* Will report in board_late_init() */ + + return 0; +} + +int cros_ec_board_init(void) +{ + return board_init_cros_ec_devices(gd->fdt_blob); +} + +int cros_ec_get_error(void) +{ + return local.cros_ec_err; +} diff --git a/qemu/roms/u-boot/common/ddr_spd.c b/qemu/roms/u-boot/common/ddr_spd.c new file mode 100644 index 000000000..438e71afd --- /dev/null +++ b/qemu/roms/u-boot/common/ddr_spd.c @@ -0,0 +1,161 @@ +/* + * Copyright 2008-2014 Freescale Semiconductor, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * Version 2 as published by the Free Software Foundation. + */ + +#include <common.h> +#include <ddr_spd.h> + +/* used for ddr1 and ddr2 spd */ +static int +spd_check(const u8 *buf, u8 spd_rev, u8 spd_cksum) +{ + unsigned int cksum = 0; + unsigned int i; + + /* + * Check SPD revision supported + * Rev 1.X or less supported by this code + */ + if (spd_rev >= 0x20) { + printf("SPD revision %02X not supported by this code\n", + spd_rev); + return 1; + } + if (spd_rev > 0x13) { + printf("SPD revision %02X not verified by this code\n", + spd_rev); + } + + /* + * Calculate checksum + */ + for (i = 0; i < 63; i++) { + cksum += *buf++; + } + cksum &= 0xFF; + + if (cksum != spd_cksum) { + printf("SPD checksum unexpected. " + "Checksum in SPD = %02X, computed SPD = %02X\n", + spd_cksum, cksum); + return 1; + } + + return 0; +} + +unsigned int +ddr1_spd_check(const ddr1_spd_eeprom_t *spd) +{ + const u8 *p = (const u8 *)spd; + + return spd_check(p, spd->spd_rev, spd->cksum); +} + +unsigned int +ddr2_spd_check(const ddr2_spd_eeprom_t *spd) +{ + const u8 *p = (const u8 *)spd; + + return spd_check(p, spd->spd_rev, spd->cksum); +} + +/* + * CRC16 compute for DDR3 SPD + * Copied from DDR3 SPD spec. + */ +static int +crc16(char *ptr, int count) +{ + int crc, i; + + crc = 0; + while (--count >= 0) { + crc = crc ^ (int)*ptr++ << 8; + for (i = 0; i < 8; ++i) + if (crc & 0x8000) + crc = crc << 1 ^ 0x1021; + else + crc = crc << 1; + } + return crc & 0xffff; +} + +unsigned int +ddr3_spd_check(const ddr3_spd_eeprom_t *spd) +{ + char *p = (char *)spd; + int csum16; + int len; + char crc_lsb; /* byte 126 */ + char crc_msb; /* byte 127 */ + + /* + * SPD byte0[7] - CRC coverage + * 0 = CRC covers bytes 0~125 + * 1 = CRC covers bytes 0~116 + */ + + len = !(spd->info_size_crc & 0x80) ? 126 : 117; + csum16 = crc16(p, len); + + crc_lsb = (char) (csum16 & 0xff); + crc_msb = (char) (csum16 >> 8); + + if (spd->crc[0] == crc_lsb && spd->crc[1] == crc_msb) { + return 0; + } else { + printf("SPD checksum unexpected.\n" + "Checksum lsb in SPD = %02X, computed SPD = %02X\n" + "Checksum msb in SPD = %02X, computed SPD = %02X\n", + spd->crc[0], crc_lsb, spd->crc[1], crc_msb); + return 1; + } +} + +unsigned int ddr4_spd_check(const struct ddr4_spd_eeprom_s *spd) +{ + char *p = (char *)spd; + int csum16; + int len; + char crc_lsb; /* byte 126 */ + char crc_msb; /* byte 127 */ + + len = 126; + csum16 = crc16(p, len); + + crc_lsb = (char) (csum16 & 0xff); + crc_msb = (char) (csum16 >> 8); + + if (spd->crc[0] != crc_lsb || spd->crc[1] != crc_msb) { + printf("SPD checksum unexpected.\n" + "Checksum lsb in SPD = %02X, computed SPD = %02X\n" + "Checksum msb in SPD = %02X, computed SPD = %02X\n", + spd->crc[0], crc_lsb, spd->crc[1], crc_msb); + return 1; + } + + p = (char *)((ulong)spd + 128); + len = 126; + csum16 = crc16(p, len); + + crc_lsb = (char) (csum16 & 0xff); + crc_msb = (char) (csum16 >> 8); + + if (spd->mod_section.uc[126] != crc_lsb || + spd->mod_section.uc[127] != crc_msb) { + printf("SPD checksum unexpected.\n" + "Checksum lsb in SPD = %02X, computed SPD = %02X\n" + "Checksum msb in SPD = %02X, computed SPD = %02X\n", + spd->mod_section.uc[126], + crc_lsb, spd->mod_section.uc[127], + crc_msb); + return 1; + } + + return 0; +} diff --git a/qemu/roms/u-boot/common/dlmalloc.c b/qemu/roms/u-boot/common/dlmalloc.c new file mode 100644 index 000000000..3c70d5ded --- /dev/null +++ b/qemu/roms/u-boot/common/dlmalloc.c @@ -0,0 +1,3332 @@ +#include <common.h> + +#if 0 /* Moved to malloc.h */ +/* ---------- To make a malloc.h, start cutting here ------------ */ + +/* + A version of malloc/free/realloc written by Doug Lea and released to the + public domain. Send questions/comments/complaints/performance data + to dl@cs.oswego.edu + +* VERSION 2.6.6 Sun Mar 5 19:10:03 2000 Doug Lea (dl at gee) + + Note: There may be an updated version of this malloc obtainable at + ftp://g.oswego.edu/pub/misc/malloc.c + Check before installing! + +* Why use this malloc? + + This is not the fastest, most space-conserving, most portable, or + most tunable malloc ever written. However it is among the fastest + while also being among the most space-conserving, portable and tunable. + Consistent balance across these factors results in a good general-purpose + allocator. For a high-level description, see + http://g.oswego.edu/dl/html/malloc.html + +* Synopsis of public routines + + (Much fuller descriptions are contained in the program documentation below.) + + malloc(size_t n); + Return a pointer to a newly allocated chunk of at least n bytes, or null + if no space is available. + free(Void_t* p); + Release the chunk of memory pointed to by p, or no effect if p is null. + realloc(Void_t* p, size_t n); + Return a pointer to a chunk of size n that contains the same data + as does chunk p up to the minimum of (n, p's size) bytes, or null + if no space is available. The returned pointer may or may not be + the same as p. If p is null, equivalent to malloc. Unless the + #define REALLOC_ZERO_BYTES_FREES below is set, realloc with a + size argument of zero (re)allocates a minimum-sized chunk. + memalign(size_t alignment, size_t n); + Return a pointer to a newly allocated chunk of n bytes, aligned + in accord with the alignment argument, which must be a power of + two. + valloc(size_t n); + Equivalent to memalign(pagesize, n), where pagesize is the page + size of the system (or as near to this as can be figured out from + all the includes/defines below.) + pvalloc(size_t n); + Equivalent to valloc(minimum-page-that-holds(n)), that is, + round up n to nearest pagesize. + calloc(size_t unit, size_t quantity); + Returns a pointer to quantity * unit bytes, with all locations + set to zero. + cfree(Void_t* p); + Equivalent to free(p). + malloc_trim(size_t pad); + Release all but pad bytes of freed top-most memory back + to the system. Return 1 if successful, else 0. + malloc_usable_size(Void_t* p); + Report the number usable allocated bytes associated with allocated + chunk p. This may or may not report more bytes than were requested, + due to alignment and minimum size constraints. + malloc_stats(); + Prints brief summary statistics. + mallinfo() + Returns (by copy) a struct containing various summary statistics. + mallopt(int parameter_number, int parameter_value) + Changes one of the tunable parameters described below. Returns + 1 if successful in changing the parameter, else 0. + +* Vital statistics: + + Alignment: 8-byte + 8 byte alignment is currently hardwired into the design. This + seems to suffice for all current machines and C compilers. + + Assumed pointer representation: 4 or 8 bytes + Code for 8-byte pointers is untested by me but has worked + reliably by Wolfram Gloger, who contributed most of the + changes supporting this. + + Assumed size_t representation: 4 or 8 bytes + Note that size_t is allowed to be 4 bytes even if pointers are 8. + + Minimum overhead per allocated chunk: 4 or 8 bytes + Each malloced chunk has a hidden overhead of 4 bytes holding size + and status information. + + Minimum allocated size: 4-byte ptrs: 16 bytes (including 4 overhead) + 8-byte ptrs: 24/32 bytes (including, 4/8 overhead) + + When a chunk is freed, 12 (for 4byte ptrs) or 20 (for 8 byte + ptrs but 4 byte size) or 24 (for 8/8) additional bytes are + needed; 4 (8) for a trailing size field + and 8 (16) bytes for free list pointers. Thus, the minimum + allocatable size is 16/24/32 bytes. + + Even a request for zero bytes (i.e., malloc(0)) returns a + pointer to something of the minimum allocatable size. + + Maximum allocated size: 4-byte size_t: 2^31 - 8 bytes + 8-byte size_t: 2^63 - 16 bytes + + It is assumed that (possibly signed) size_t bit values suffice to + represent chunk sizes. `Possibly signed' is due to the fact + that `size_t' may be defined on a system as either a signed or + an unsigned type. To be conservative, values that would appear + as negative numbers are avoided. + Requests for sizes with a negative sign bit when the request + size is treaded as a long will return null. + + Maximum overhead wastage per allocated chunk: normally 15 bytes + + Alignnment demands, plus the minimum allocatable size restriction + make the normal worst-case wastage 15 bytes (i.e., up to 15 + more bytes will be allocated than were requested in malloc), with + two exceptions: + 1. Because requests for zero bytes allocate non-zero space, + the worst case wastage for a request of zero bytes is 24 bytes. + 2. For requests >= mmap_threshold that are serviced via + mmap(), the worst case wastage is 8 bytes plus the remainder + from a system page (the minimal mmap unit); typically 4096 bytes. + +* Limitations + + Here are some features that are NOT currently supported + + * No user-definable hooks for callbacks and the like. + * No automated mechanism for fully checking that all accesses + to malloced memory stay within their bounds. + * No support for compaction. + +* Synopsis of compile-time options: + + People have reported using previous versions of this malloc on all + versions of Unix, sometimes by tweaking some of the defines + below. It has been tested most extensively on Solaris and + Linux. It is also reported to work on WIN32 platforms. + People have also reported adapting this malloc for use in + stand-alone embedded systems. + + The implementation is in straight, hand-tuned ANSI C. Among other + consequences, it uses a lot of macros. Because of this, to be at + all usable, this code should be compiled using an optimizing compiler + (for example gcc -O2) that can simplify expressions and control + paths. + + __STD_C (default: derived from C compiler defines) + Nonzero if using ANSI-standard C compiler, a C++ compiler, or + a C compiler sufficiently close to ANSI to get away with it. + DEBUG (default: NOT defined) + Define to enable debugging. Adds fairly extensive assertion-based + checking to help track down memory errors, but noticeably slows down + execution. + REALLOC_ZERO_BYTES_FREES (default: NOT defined) + Define this if you think that realloc(p, 0) should be equivalent + to free(p). Otherwise, since malloc returns a unique pointer for + malloc(0), so does realloc(p, 0). + HAVE_MEMCPY (default: defined) + Define if you are not otherwise using ANSI STD C, but still + have memcpy and memset in your C library and want to use them. + Otherwise, simple internal versions are supplied. + USE_MEMCPY (default: 1 if HAVE_MEMCPY is defined, 0 otherwise) + Define as 1 if you want the C library versions of memset and + memcpy called in realloc and calloc (otherwise macro versions are used). + At least on some platforms, the simple macro versions usually + outperform libc versions. + HAVE_MMAP (default: defined as 1) + Define to non-zero to optionally make malloc() use mmap() to + allocate very large blocks. + HAVE_MREMAP (default: defined as 0 unless Linux libc set) + Define to non-zero to optionally make realloc() use mremap() to + reallocate very large blocks. + malloc_getpagesize (default: derived from system #includes) + Either a constant or routine call returning the system page size. + HAVE_USR_INCLUDE_MALLOC_H (default: NOT defined) + Optionally define if you are on a system with a /usr/include/malloc.h + that declares struct mallinfo. It is not at all necessary to + define this even if you do, but will ensure consistency. + INTERNAL_SIZE_T (default: size_t) + Define to a 32-bit type (probably `unsigned int') if you are on a + 64-bit machine, yet do not want or need to allow malloc requests of + greater than 2^31 to be handled. This saves space, especially for + very small chunks. + INTERNAL_LINUX_C_LIB (default: NOT defined) + Defined only when compiled as part of Linux libc. + Also note that there is some odd internal name-mangling via defines + (for example, internally, `malloc' is named `mALLOc') needed + when compiling in this case. These look funny but don't otherwise + affect anything. + WIN32 (default: undefined) + Define this on MS win (95, nt) platforms to compile in sbrk emulation. + LACKS_UNISTD_H (default: undefined if not WIN32) + Define this if your system does not have a <unistd.h>. + LACKS_SYS_PARAM_H (default: undefined if not WIN32) + Define this if your system does not have a <sys/param.h>. + MORECORE (default: sbrk) + The name of the routine to call to obtain more memory from the system. + MORECORE_FAILURE (default: -1) + The value returned upon failure of MORECORE. + MORECORE_CLEARS (default 1) + true (1) if the routine mapped to MORECORE zeroes out memory (which + holds for sbrk). + DEFAULT_TRIM_THRESHOLD + DEFAULT_TOP_PAD + DEFAULT_MMAP_THRESHOLD + DEFAULT_MMAP_MAX + Default values of tunable parameters (described in detail below) + controlling interaction with host system routines (sbrk, mmap, etc). + These values may also be changed dynamically via mallopt(). The + preset defaults are those that give best performance for typical + programs/systems. + USE_DL_PREFIX (default: undefined) + Prefix all public routines with the string 'dl'. Useful to + quickly avoid procedure declaration conflicts and linker symbol + conflicts with existing memory allocation routines. + + +*/ + + + +/* Preliminaries */ + +#ifndef __STD_C +#ifdef __STDC__ +#define __STD_C 1 +#else +#if __cplusplus +#define __STD_C 1 +#else +#define __STD_C 0 +#endif /*__cplusplus*/ +#endif /*__STDC__*/ +#endif /*__STD_C*/ + +#ifndef Void_t +#if (__STD_C || defined(WIN32)) +#define Void_t void +#else +#define Void_t char +#endif +#endif /*Void_t*/ + +#if __STD_C +#include <stddef.h> /* for size_t */ +#else +#include <sys/types.h> +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#include <stdio.h> /* needed for malloc_stats */ + + +/* + Compile-time options +*/ + + +/* + Debugging: + + Because freed chunks may be overwritten with link fields, this + malloc will often die when freed memory is overwritten by user + programs. This can be very effective (albeit in an annoying way) + in helping track down dangling pointers. + + If you compile with -DDEBUG, a number of assertion checks are + enabled that will catch more memory errors. You probably won't be + able to make much sense of the actual assertion errors, but they + should help you locate incorrectly overwritten memory. The + checking is fairly extensive, and will slow down execution + noticeably. Calling malloc_stats or mallinfo with DEBUG set will + attempt to check every non-mmapped allocated and free chunk in the + course of computing the summmaries. (By nature, mmapped regions + cannot be checked very much automatically.) + + Setting DEBUG may also be helpful if you are trying to modify + this code. The assertions in the check routines spell out in more + detail the assumptions and invariants underlying the algorithms. + +*/ + +/* + INTERNAL_SIZE_T is the word-size used for internal bookkeeping + of chunk sizes. On a 64-bit machine, you can reduce malloc + overhead by defining INTERNAL_SIZE_T to be a 32 bit `unsigned int' + at the expense of not being able to handle requests greater than + 2^31. This limitation is hardly ever a concern; you are encouraged + to set this. However, the default version is the same as size_t. +*/ + +#ifndef INTERNAL_SIZE_T +#define INTERNAL_SIZE_T size_t +#endif + +/* + REALLOC_ZERO_BYTES_FREES should be set if a call to + realloc with zero bytes should be the same as a call to free. + Some people think it should. Otherwise, since this malloc + returns a unique pointer for malloc(0), so does realloc(p, 0). +*/ + + +/* #define REALLOC_ZERO_BYTES_FREES */ + + +/* + WIN32 causes an emulation of sbrk to be compiled in + mmap-based options are not currently supported in WIN32. +*/ + +/* #define WIN32 */ +#ifdef WIN32 +#define MORECORE wsbrk +#define HAVE_MMAP 0 + +#define LACKS_UNISTD_H +#define LACKS_SYS_PARAM_H + +/* + Include 'windows.h' to get the necessary declarations for the + Microsoft Visual C++ data structures and routines used in the 'sbrk' + emulation. + + Define WIN32_LEAN_AND_MEAN so that only the essential Microsoft + Visual C++ header files are included. +*/ +#define WIN32_LEAN_AND_MEAN +#include <windows.h> +#endif + + +/* + HAVE_MEMCPY should be defined if you are not otherwise using + ANSI STD C, but still have memcpy and memset in your C library + and want to use them in calloc and realloc. Otherwise simple + macro versions are defined here. + + USE_MEMCPY should be defined as 1 if you actually want to + have memset and memcpy called. People report that the macro + versions are often enough faster than libc versions on many + systems that it is better to use them. + +*/ + +#define HAVE_MEMCPY + +#ifndef USE_MEMCPY +#ifdef HAVE_MEMCPY +#define USE_MEMCPY 1 +#else +#define USE_MEMCPY 0 +#endif +#endif + +#if (__STD_C || defined(HAVE_MEMCPY)) + +#if __STD_C +void* memset(void*, int, size_t); +void* memcpy(void*, const void*, size_t); +#else +#ifdef WIN32 +/* On Win32 platforms, 'memset()' and 'memcpy()' are already declared in */ +/* 'windows.h' */ +#else +Void_t* memset(); +Void_t* memcpy(); +#endif +#endif +#endif + +#if USE_MEMCPY + +/* The following macros are only invoked with (2n+1)-multiples of + INTERNAL_SIZE_T units, with a positive integer n. This is exploited + for fast inline execution when n is small. */ + +#define MALLOC_ZERO(charp, nbytes) \ +do { \ + INTERNAL_SIZE_T mzsz = (nbytes); \ + if(mzsz <= 9*sizeof(mzsz)) { \ + INTERNAL_SIZE_T* mz = (INTERNAL_SIZE_T*) (charp); \ + if(mzsz >= 5*sizeof(mzsz)) { *mz++ = 0; \ + *mz++ = 0; \ + if(mzsz >= 7*sizeof(mzsz)) { *mz++ = 0; \ + *mz++ = 0; \ + if(mzsz >= 9*sizeof(mzsz)) { *mz++ = 0; \ + *mz++ = 0; }}} \ + *mz++ = 0; \ + *mz++ = 0; \ + *mz = 0; \ + } else memset((charp), 0, mzsz); \ +} while(0) + +#define MALLOC_COPY(dest,src,nbytes) \ +do { \ + INTERNAL_SIZE_T mcsz = (nbytes); \ + if(mcsz <= 9*sizeof(mcsz)) { \ + INTERNAL_SIZE_T* mcsrc = (INTERNAL_SIZE_T*) (src); \ + INTERNAL_SIZE_T* mcdst = (INTERNAL_SIZE_T*) (dest); \ + if(mcsz >= 5*sizeof(mcsz)) { *mcdst++ = *mcsrc++; \ + *mcdst++ = *mcsrc++; \ + if(mcsz >= 7*sizeof(mcsz)) { *mcdst++ = *mcsrc++; \ + *mcdst++ = *mcsrc++; \ + if(mcsz >= 9*sizeof(mcsz)) { *mcdst++ = *mcsrc++; \ + *mcdst++ = *mcsrc++; }}} \ + *mcdst++ = *mcsrc++; \ + *mcdst++ = *mcsrc++; \ + *mcdst = *mcsrc ; \ + } else memcpy(dest, src, mcsz); \ +} while(0) + +#else /* !USE_MEMCPY */ + +/* Use Duff's device for good zeroing/copying performance. */ + +#define MALLOC_ZERO(charp, nbytes) \ +do { \ + INTERNAL_SIZE_T* mzp = (INTERNAL_SIZE_T*)(charp); \ + long mctmp = (nbytes)/sizeof(INTERNAL_SIZE_T), mcn; \ + if (mctmp < 8) mcn = 0; else { mcn = (mctmp-1)/8; mctmp %= 8; } \ + switch (mctmp) { \ + case 0: for(;;) { *mzp++ = 0; \ + case 7: *mzp++ = 0; \ + case 6: *mzp++ = 0; \ + case 5: *mzp++ = 0; \ + case 4: *mzp++ = 0; \ + case 3: *mzp++ = 0; \ + case 2: *mzp++ = 0; \ + case 1: *mzp++ = 0; if(mcn <= 0) break; mcn--; } \ + } \ +} while(0) + +#define MALLOC_COPY(dest,src,nbytes) \ +do { \ + INTERNAL_SIZE_T* mcsrc = (INTERNAL_SIZE_T*) src; \ + INTERNAL_SIZE_T* mcdst = (INTERNAL_SIZE_T*) dest; \ + long mctmp = (nbytes)/sizeof(INTERNAL_SIZE_T), mcn; \ + if (mctmp < 8) mcn = 0; else { mcn = (mctmp-1)/8; mctmp %= 8; } \ + switch (mctmp) { \ + case 0: for(;;) { *mcdst++ = *mcsrc++; \ + case 7: *mcdst++ = *mcsrc++; \ + case 6: *mcdst++ = *mcsrc++; \ + case 5: *mcdst++ = *mcsrc++; \ + case 4: *mcdst++ = *mcsrc++; \ + case 3: *mcdst++ = *mcsrc++; \ + case 2: *mcdst++ = *mcsrc++; \ + case 1: *mcdst++ = *mcsrc++; if(mcn <= 0) break; mcn--; } \ + } \ +} while(0) + +#endif + + +/* + Define HAVE_MMAP to optionally make malloc() use mmap() to + allocate very large blocks. These will be returned to the + operating system immediately after a free(). +*/ + +#ifndef HAVE_MMAP +#define HAVE_MMAP 1 +#endif + +/* + Define HAVE_MREMAP to make realloc() use mremap() to re-allocate + large blocks. This is currently only possible on Linux with + kernel versions newer than 1.3.77. +*/ + +#ifndef HAVE_MREMAP +#ifdef INTERNAL_LINUX_C_LIB +#define HAVE_MREMAP 1 +#else +#define HAVE_MREMAP 0 +#endif +#endif + +#if HAVE_MMAP + +#include <unistd.h> +#include <fcntl.h> +#include <sys/mman.h> + +#if !defined(MAP_ANONYMOUS) && defined(MAP_ANON) +#define MAP_ANONYMOUS MAP_ANON +#endif + +#endif /* HAVE_MMAP */ + +/* + Access to system page size. To the extent possible, this malloc + manages memory from the system in page-size units. + + The following mechanics for getpagesize were adapted from + bsd/gnu getpagesize.h +*/ + +#ifndef LACKS_UNISTD_H +# include <unistd.h> +#endif + +#ifndef malloc_getpagesize +# ifdef _SC_PAGESIZE /* some SVR4 systems omit an underscore */ +# ifndef _SC_PAGE_SIZE +# define _SC_PAGE_SIZE _SC_PAGESIZE +# endif +# endif +# ifdef _SC_PAGE_SIZE +# define malloc_getpagesize sysconf(_SC_PAGE_SIZE) +# else +# if defined(BSD) || defined(DGUX) || defined(HAVE_GETPAGESIZE) + extern size_t getpagesize(); +# define malloc_getpagesize getpagesize() +# else +# ifdef WIN32 +# define malloc_getpagesize (4096) /* TBD: Use 'GetSystemInfo' instead */ +# else +# ifndef LACKS_SYS_PARAM_H +# include <sys/param.h> +# endif +# ifdef EXEC_PAGESIZE +# define malloc_getpagesize EXEC_PAGESIZE +# else +# ifdef NBPG +# ifndef CLSIZE +# define malloc_getpagesize NBPG +# else +# define malloc_getpagesize (NBPG * CLSIZE) +# endif +# else +# ifdef NBPC +# define malloc_getpagesize NBPC +# else +# ifdef PAGESIZE +# define malloc_getpagesize PAGESIZE +# else +# define malloc_getpagesize (4096) /* just guess */ +# endif +# endif +# endif +# endif +# endif +# endif +# endif +#endif + + +/* + + This version of malloc supports the standard SVID/XPG mallinfo + routine that returns a struct containing the same kind of + information you can get from malloc_stats. It should work on + any SVID/XPG compliant system that has a /usr/include/malloc.h + defining struct mallinfo. (If you'd like to install such a thing + yourself, cut out the preliminary declarations as described above + and below and save them in a malloc.h file. But there's no + compelling reason to bother to do this.) + + The main declaration needed is the mallinfo struct that is returned + (by-copy) by mallinfo(). The SVID/XPG malloinfo struct contains a + bunch of fields, most of which are not even meaningful in this + version of malloc. Some of these fields are are instead filled by + mallinfo() with other numbers that might possibly be of interest. + + HAVE_USR_INCLUDE_MALLOC_H should be set if you have a + /usr/include/malloc.h file that includes a declaration of struct + mallinfo. If so, it is included; else an SVID2/XPG2 compliant + version is declared below. These must be precisely the same for + mallinfo() to work. + +*/ + +/* #define HAVE_USR_INCLUDE_MALLOC_H */ + +#if HAVE_USR_INCLUDE_MALLOC_H +#include "/usr/include/malloc.h" +#else + +/* SVID2/XPG mallinfo structure */ + +struct mallinfo { + int arena; /* total space allocated from system */ + int ordblks; /* number of non-inuse chunks */ + int smblks; /* unused -- always zero */ + int hblks; /* number of mmapped regions */ + int hblkhd; /* total space in mmapped regions */ + int usmblks; /* unused -- always zero */ + int fsmblks; /* unused -- always zero */ + int uordblks; /* total allocated space */ + int fordblks; /* total non-inuse space */ + int keepcost; /* top-most, releasable (via malloc_trim) space */ +}; + +/* SVID2/XPG mallopt options */ + +#define M_MXFAST 1 /* UNUSED in this malloc */ +#define M_NLBLKS 2 /* UNUSED in this malloc */ +#define M_GRAIN 3 /* UNUSED in this malloc */ +#define M_KEEP 4 /* UNUSED in this malloc */ + +#endif + +/* mallopt options that actually do something */ + +#define M_TRIM_THRESHOLD -1 +#define M_TOP_PAD -2 +#define M_MMAP_THRESHOLD -3 +#define M_MMAP_MAX -4 + + +#ifndef DEFAULT_TRIM_THRESHOLD +#define DEFAULT_TRIM_THRESHOLD (128 * 1024) +#endif + +/* + M_TRIM_THRESHOLD is the maximum amount of unused top-most memory + to keep before releasing via malloc_trim in free(). + + Automatic trimming is mainly useful in long-lived programs. + Because trimming via sbrk can be slow on some systems, and can + sometimes be wasteful (in cases where programs immediately + afterward allocate more large chunks) the value should be high + enough so that your overall system performance would improve by + releasing. + + The trim threshold and the mmap control parameters (see below) + can be traded off with one another. Trimming and mmapping are + two different ways of releasing unused memory back to the + system. Between these two, it is often possible to keep + system-level demands of a long-lived program down to a bare + minimum. For example, in one test suite of sessions measuring + the XF86 X server on Linux, using a trim threshold of 128K and a + mmap threshold of 192K led to near-minimal long term resource + consumption. + + If you are using this malloc in a long-lived program, it should + pay to experiment with these values. As a rough guide, you + might set to a value close to the average size of a process + (program) running on your system. Releasing this much memory + would allow such a process to run in memory. Generally, it's + worth it to tune for trimming rather tham memory mapping when a + program undergoes phases where several large chunks are + allocated and released in ways that can reuse each other's + storage, perhaps mixed with phases where there are no such + chunks at all. And in well-behaved long-lived programs, + controlling release of large blocks via trimming versus mapping + is usually faster. + + However, in most programs, these parameters serve mainly as + protection against the system-level effects of carrying around + massive amounts of unneeded memory. Since frequent calls to + sbrk, mmap, and munmap otherwise degrade performance, the default + parameters are set to relatively high values that serve only as + safeguards. + + The default trim value is high enough to cause trimming only in + fairly extreme (by current memory consumption standards) cases. + It must be greater than page size to have any useful effect. To + disable trimming completely, you can set to (unsigned long)(-1); + + +*/ + + +#ifndef DEFAULT_TOP_PAD +#define DEFAULT_TOP_PAD (0) +#endif + +/* + M_TOP_PAD is the amount of extra `padding' space to allocate or + retain whenever sbrk is called. It is used in two ways internally: + + * When sbrk is called to extend the top of the arena to satisfy + a new malloc request, this much padding is added to the sbrk + request. + + * When malloc_trim is called automatically from free(), + it is used as the `pad' argument. + + In both cases, the actual amount of padding is rounded + so that the end of the arena is always a system page boundary. + + The main reason for using padding is to avoid calling sbrk so + often. Having even a small pad greatly reduces the likelihood + that nearly every malloc request during program start-up (or + after trimming) will invoke sbrk, which needlessly wastes + time. + + Automatic rounding-up to page-size units is normally sufficient + to avoid measurable overhead, so the default is 0. However, in + systems where sbrk is relatively slow, it can pay to increase + this value, at the expense of carrying around more memory than + the program needs. + +*/ + + +#ifndef DEFAULT_MMAP_THRESHOLD +#define DEFAULT_MMAP_THRESHOLD (128 * 1024) +#endif + +/* + + M_MMAP_THRESHOLD is the request size threshold for using mmap() + to service a request. Requests of at least this size that cannot + be allocated using already-existing space will be serviced via mmap. + (If enough normal freed space already exists it is used instead.) + + Using mmap segregates relatively large chunks of memory so that + they can be individually obtained and released from the host + system. A request serviced through mmap is never reused by any + other request (at least not directly; the system may just so + happen to remap successive requests to the same locations). + + Segregating space in this way has the benefit that mmapped space + can ALWAYS be individually released back to the system, which + helps keep the system level memory demands of a long-lived + program low. Mapped memory can never become `locked' between + other chunks, as can happen with normally allocated chunks, which + menas that even trimming via malloc_trim would not release them. + + However, it has the disadvantages that: + + 1. The space cannot be reclaimed, consolidated, and then + used to service later requests, as happens with normal chunks. + 2. It can lead to more wastage because of mmap page alignment + requirements + 3. It causes malloc performance to be more dependent on host + system memory management support routines which may vary in + implementation quality and may impose arbitrary + limitations. Generally, servicing a request via normal + malloc steps is faster than going through a system's mmap. + + All together, these considerations should lead you to use mmap + only for relatively large requests. + + +*/ + + +#ifndef DEFAULT_MMAP_MAX +#if HAVE_MMAP +#define DEFAULT_MMAP_MAX (64) +#else +#define DEFAULT_MMAP_MAX (0) +#endif +#endif + +/* + M_MMAP_MAX is the maximum number of requests to simultaneously + service using mmap. This parameter exists because: + + 1. Some systems have a limited number of internal tables for + use by mmap. + 2. In most systems, overreliance on mmap can degrade overall + performance. + 3. If a program allocates many large regions, it is probably + better off using normal sbrk-based allocation routines that + can reclaim and reallocate normal heap memory. Using a + small value allows transition into this mode after the + first few allocations. + + Setting to 0 disables all use of mmap. If HAVE_MMAP is not set, + the default value is 0, and attempts to set it to non-zero values + in mallopt will fail. +*/ + + +/* + USE_DL_PREFIX will prefix all public routines with the string 'dl'. + Useful to quickly avoid procedure declaration conflicts and linker + symbol conflicts with existing memory allocation routines. + +*/ + +/* #define USE_DL_PREFIX */ + + +/* + + Special defines for linux libc + + Except when compiled using these special defines for Linux libc + using weak aliases, this malloc is NOT designed to work in + multithreaded applications. No semaphores or other concurrency + control are provided to ensure that multiple malloc or free calls + don't run at the same time, which could be disasterous. A single + semaphore could be used across malloc, realloc, and free (which is + essentially the effect of the linux weak alias approach). It would + be hard to obtain finer granularity. + +*/ + + +#ifdef INTERNAL_LINUX_C_LIB + +#if __STD_C + +Void_t * __default_morecore_init (ptrdiff_t); +Void_t *(*__morecore)(ptrdiff_t) = __default_morecore_init; + +#else + +Void_t * __default_morecore_init (); +Void_t *(*__morecore)() = __default_morecore_init; + +#endif + +#define MORECORE (*__morecore) +#define MORECORE_FAILURE 0 +#define MORECORE_CLEARS 1 + +#else /* INTERNAL_LINUX_C_LIB */ + +#if __STD_C +extern Void_t* sbrk(ptrdiff_t); +#else +extern Void_t* sbrk(); +#endif + +#ifndef MORECORE +#define MORECORE sbrk +#endif + +#ifndef MORECORE_FAILURE +#define MORECORE_FAILURE -1 +#endif + +#ifndef MORECORE_CLEARS +#define MORECORE_CLEARS 1 +#endif + +#endif /* INTERNAL_LINUX_C_LIB */ + +#if defined(INTERNAL_LINUX_C_LIB) && defined(__ELF__) + +#define cALLOc __libc_calloc +#define fREe __libc_free +#define mALLOc __libc_malloc +#define mEMALIGn __libc_memalign +#define rEALLOc __libc_realloc +#define vALLOc __libc_valloc +#define pvALLOc __libc_pvalloc +#define mALLINFo __libc_mallinfo +#define mALLOPt __libc_mallopt + +#pragma weak calloc = __libc_calloc +#pragma weak free = __libc_free +#pragma weak cfree = __libc_free +#pragma weak malloc = __libc_malloc +#pragma weak memalign = __libc_memalign +#pragma weak realloc = __libc_realloc +#pragma weak valloc = __libc_valloc +#pragma weak pvalloc = __libc_pvalloc +#pragma weak mallinfo = __libc_mallinfo +#pragma weak mallopt = __libc_mallopt + +#else + +#ifdef USE_DL_PREFIX +#define cALLOc dlcalloc +#define fREe dlfree +#define mALLOc dlmalloc +#define mEMALIGn dlmemalign +#define rEALLOc dlrealloc +#define vALLOc dlvalloc +#define pvALLOc dlpvalloc +#define mALLINFo dlmallinfo +#define mALLOPt dlmallopt +#else /* USE_DL_PREFIX */ +#define cALLOc calloc +#define fREe free +#define mALLOc malloc +#define mEMALIGn memalign +#define rEALLOc realloc +#define vALLOc valloc +#define pvALLOc pvalloc +#define mALLINFo mallinfo +#define mALLOPt mallopt +#endif /* USE_DL_PREFIX */ + +#endif + +/* Public routines */ + +#if __STD_C + +Void_t* mALLOc(size_t); +void fREe(Void_t*); +Void_t* rEALLOc(Void_t*, size_t); +Void_t* mEMALIGn(size_t, size_t); +Void_t* vALLOc(size_t); +Void_t* pvALLOc(size_t); +Void_t* cALLOc(size_t, size_t); +void cfree(Void_t*); +int malloc_trim(size_t); +size_t malloc_usable_size(Void_t*); +void malloc_stats(); +int mALLOPt(int, int); +struct mallinfo mALLINFo(void); +#else +Void_t* mALLOc(); +void fREe(); +Void_t* rEALLOc(); +Void_t* mEMALIGn(); +Void_t* vALLOc(); +Void_t* pvALLOc(); +Void_t* cALLOc(); +void cfree(); +int malloc_trim(); +size_t malloc_usable_size(); +void malloc_stats(); +int mALLOPt(); +struct mallinfo mALLINFo(); +#endif + + +#ifdef __cplusplus +}; /* end of extern "C" */ +#endif + +/* ---------- To make a malloc.h, end cutting here ------------ */ +#endif /* 0 */ /* Moved to malloc.h */ + +#include <malloc.h> +#ifdef DEBUG +#if __STD_C +static void malloc_update_mallinfo (void); +void malloc_stats (void); +#else +static void malloc_update_mallinfo (); +void malloc_stats(); +#endif +#endif /* DEBUG */ + +DECLARE_GLOBAL_DATA_PTR; + +/* + Emulation of sbrk for WIN32 + All code within the ifdef WIN32 is untested by me. + + Thanks to Martin Fong and others for supplying this. +*/ + + +#ifdef WIN32 + +#define AlignPage(add) (((add) + (malloc_getpagesize-1)) & \ +~(malloc_getpagesize-1)) +#define AlignPage64K(add) (((add) + (0x10000 - 1)) & ~(0x10000 - 1)) + +/* resrve 64MB to insure large contiguous space */ +#define RESERVED_SIZE (1024*1024*64) +#define NEXT_SIZE (2048*1024) +#define TOP_MEMORY ((unsigned long)2*1024*1024*1024) + +struct GmListElement; +typedef struct GmListElement GmListElement; + +struct GmListElement +{ + GmListElement* next; + void* base; +}; + +static GmListElement* head = 0; +static unsigned int gNextAddress = 0; +static unsigned int gAddressBase = 0; +static unsigned int gAllocatedSize = 0; + +static +GmListElement* makeGmListElement (void* bas) +{ + GmListElement* this; + this = (GmListElement*)(void*)LocalAlloc (0, sizeof (GmListElement)); + assert (this); + if (this) + { + this->base = bas; + this->next = head; + head = this; + } + return this; +} + +void gcleanup () +{ + BOOL rval; + assert ( (head == NULL) || (head->base == (void*)gAddressBase)); + if (gAddressBase && (gNextAddress - gAddressBase)) + { + rval = VirtualFree ((void*)gAddressBase, + gNextAddress - gAddressBase, + MEM_DECOMMIT); + assert (rval); + } + while (head) + { + GmListElement* next = head->next; + rval = VirtualFree (head->base, 0, MEM_RELEASE); + assert (rval); + LocalFree (head); + head = next; + } +} + +static +void* findRegion (void* start_address, unsigned long size) +{ + MEMORY_BASIC_INFORMATION info; + if (size >= TOP_MEMORY) return NULL; + + while ((unsigned long)start_address + size < TOP_MEMORY) + { + VirtualQuery (start_address, &info, sizeof (info)); + if ((info.State == MEM_FREE) && (info.RegionSize >= size)) + return start_address; + else + { + /* Requested region is not available so see if the */ + /* next region is available. Set 'start_address' */ + /* to the next region and call 'VirtualQuery()' */ + /* again. */ + + start_address = (char*)info.BaseAddress + info.RegionSize; + + /* Make sure we start looking for the next region */ + /* on the *next* 64K boundary. Otherwise, even if */ + /* the new region is free according to */ + /* 'VirtualQuery()', the subsequent call to */ + /* 'VirtualAlloc()' (which follows the call to */ + /* this routine in 'wsbrk()') will round *down* */ + /* the requested address to a 64K boundary which */ + /* we already know is an address in the */ + /* unavailable region. Thus, the subsequent call */ + /* to 'VirtualAlloc()' will fail and bring us back */ + /* here, causing us to go into an infinite loop. */ + + start_address = + (void *) AlignPage64K((unsigned long) start_address); + } + } + return NULL; + +} + + +void* wsbrk (long size) +{ + void* tmp; + if (size > 0) + { + if (gAddressBase == 0) + { + gAllocatedSize = max (RESERVED_SIZE, AlignPage (size)); + gNextAddress = gAddressBase = + (unsigned int)VirtualAlloc (NULL, gAllocatedSize, + MEM_RESERVE, PAGE_NOACCESS); + } else if (AlignPage (gNextAddress + size) > (gAddressBase + +gAllocatedSize)) + { + long new_size = max (NEXT_SIZE, AlignPage (size)); + void* new_address = (void*)(gAddressBase+gAllocatedSize); + do + { + new_address = findRegion (new_address, new_size); + + if (new_address == 0) + return (void*)-1; + + gAddressBase = gNextAddress = + (unsigned int)VirtualAlloc (new_address, new_size, + MEM_RESERVE, PAGE_NOACCESS); + /* repeat in case of race condition */ + /* The region that we found has been snagged */ + /* by another thread */ + } + while (gAddressBase == 0); + + assert (new_address == (void*)gAddressBase); + + gAllocatedSize = new_size; + + if (!makeGmListElement ((void*)gAddressBase)) + return (void*)-1; + } + if ((size + gNextAddress) > AlignPage (gNextAddress)) + { + void* res; + res = VirtualAlloc ((void*)AlignPage (gNextAddress), + (size + gNextAddress - + AlignPage (gNextAddress)), + MEM_COMMIT, PAGE_READWRITE); + if (res == 0) + return (void*)-1; + } + tmp = (void*)gNextAddress; + gNextAddress = (unsigned int)tmp + size; + return tmp; + } + else if (size < 0) + { + unsigned int alignedGoal = AlignPage (gNextAddress + size); + /* Trim by releasing the virtual memory */ + if (alignedGoal >= gAddressBase) + { + VirtualFree ((void*)alignedGoal, gNextAddress - alignedGoal, + MEM_DECOMMIT); + gNextAddress = gNextAddress + size; + return (void*)gNextAddress; + } + else + { + VirtualFree ((void*)gAddressBase, gNextAddress - gAddressBase, + MEM_DECOMMIT); + gNextAddress = gAddressBase; + return (void*)-1; + } + } + else + { + return (void*)gNextAddress; + } +} + +#endif + + + +/* + Type declarations +*/ + + +struct malloc_chunk +{ + INTERNAL_SIZE_T prev_size; /* Size of previous chunk (if free). */ + INTERNAL_SIZE_T size; /* Size in bytes, including overhead. */ + struct malloc_chunk* fd; /* double links -- used only if free. */ + struct malloc_chunk* bk; +} __attribute__((__may_alias__)) ; + +typedef struct malloc_chunk* mchunkptr; + +/* + + malloc_chunk details: + + (The following includes lightly edited explanations by Colin Plumb.) + + Chunks of memory are maintained using a `boundary tag' method as + described in e.g., Knuth or Standish. (See the paper by Paul + Wilson ftp://ftp.cs.utexas.edu/pub/garbage/allocsrv.ps for a + survey of such techniques.) Sizes of free chunks are stored both + in the front of each chunk and at the end. This makes + consolidating fragmented chunks into bigger chunks very fast. The + size fields also hold bits representing whether chunks are free or + in use. + + An allocated chunk looks like this: + + + chunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Size of previous chunk, if allocated | | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Size of chunk, in bytes |P| + mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | User data starts here... . + . . + . (malloc_usable_space() bytes) . + . | +nextchunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Size of chunk | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + + Where "chunk" is the front of the chunk for the purpose of most of + the malloc code, but "mem" is the pointer that is returned to the + user. "Nextchunk" is the beginning of the next contiguous chunk. + + Chunks always begin on even word boundries, so the mem portion + (which is returned to the user) is also on an even word boundary, and + thus double-word aligned. + + Free chunks are stored in circular doubly-linked lists, and look like this: + + chunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Size of previous chunk | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + `head:' | Size of chunk, in bytes |P| + mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Forward pointer to next chunk in list | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Back pointer to previous chunk in list | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Unused space (may be 0 bytes long) . + . . + . | +nextchunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + `foot:' | Size of chunk, in bytes | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + The P (PREV_INUSE) bit, stored in the unused low-order bit of the + chunk size (which is always a multiple of two words), is an in-use + bit for the *previous* chunk. If that bit is *clear*, then the + word before the current chunk size contains the previous chunk + size, and can be used to find the front of the previous chunk. + (The very first chunk allocated always has this bit set, + preventing access to non-existent (or non-owned) memory.) + + Note that the `foot' of the current chunk is actually represented + as the prev_size of the NEXT chunk. (This makes it easier to + deal with alignments etc). + + The two exceptions to all this are + + 1. The special chunk `top', which doesn't bother using the + trailing size field since there is no + next contiguous chunk that would have to index off it. (After + initialization, `top' is forced to always exist. If it would + become less than MINSIZE bytes long, it is replenished via + malloc_extend_top.) + + 2. Chunks allocated via mmap, which have the second-lowest-order + bit (IS_MMAPPED) set in their size fields. Because they are + never merged or traversed from any other chunk, they have no + foot size or inuse information. + + Available chunks are kept in any of several places (all declared below): + + * `av': An array of chunks serving as bin headers for consolidated + chunks. Each bin is doubly linked. The bins are approximately + proportionally (log) spaced. There are a lot of these bins + (128). This may look excessive, but works very well in + practice. All procedures maintain the invariant that no + consolidated chunk physically borders another one. Chunks in + bins are kept in size order, with ties going to the + approximately least recently used chunk. + + The chunks in each bin are maintained in decreasing sorted order by + size. This is irrelevant for the small bins, which all contain + the same-sized chunks, but facilitates best-fit allocation for + larger chunks. (These lists are just sequential. Keeping them in + order almost never requires enough traversal to warrant using + fancier ordered data structures.) Chunks of the same size are + linked with the most recently freed at the front, and allocations + are taken from the back. This results in LRU or FIFO allocation + order, which tends to give each chunk an equal opportunity to be + consolidated with adjacent freed chunks, resulting in larger free + chunks and less fragmentation. + + * `top': The top-most available chunk (i.e., the one bordering the + end of available memory) is treated specially. It is never + included in any bin, is used only if no other chunk is + available, and is released back to the system if it is very + large (see M_TRIM_THRESHOLD). + + * `last_remainder': A bin holding only the remainder of the + most recently split (non-top) chunk. This bin is checked + before other non-fitting chunks, so as to provide better + locality for runs of sequentially allocated chunks. + + * Implicitly, through the host system's memory mapping tables. + If supported, requests greater than a threshold are usually + serviced via calls to mmap, and then later released via munmap. + +*/ + +/* sizes, alignments */ + +#define SIZE_SZ (sizeof(INTERNAL_SIZE_T)) +#define MALLOC_ALIGNMENT (SIZE_SZ + SIZE_SZ) +#define MALLOC_ALIGN_MASK (MALLOC_ALIGNMENT - 1) +#define MINSIZE (sizeof(struct malloc_chunk)) + +/* conversion from malloc headers to user pointers, and back */ + +#define chunk2mem(p) ((Void_t*)((char*)(p) + 2*SIZE_SZ)) +#define mem2chunk(mem) ((mchunkptr)((char*)(mem) - 2*SIZE_SZ)) + +/* pad request bytes into a usable size */ + +#define request2size(req) \ + (((long)((req) + (SIZE_SZ + MALLOC_ALIGN_MASK)) < \ + (long)(MINSIZE + MALLOC_ALIGN_MASK)) ? MINSIZE : \ + (((req) + (SIZE_SZ + MALLOC_ALIGN_MASK)) & ~(MALLOC_ALIGN_MASK))) + +/* Check if m has acceptable alignment */ + +#define aligned_OK(m) (((unsigned long)((m)) & (MALLOC_ALIGN_MASK)) == 0) + + + + +/* + Physical chunk operations +*/ + + +/* size field is or'ed with PREV_INUSE when previous adjacent chunk in use */ + +#define PREV_INUSE 0x1 + +/* size field is or'ed with IS_MMAPPED if the chunk was obtained with mmap() */ + +#define IS_MMAPPED 0x2 + +/* Bits to mask off when extracting size */ + +#define SIZE_BITS (PREV_INUSE|IS_MMAPPED) + + +/* Ptr to next physical malloc_chunk. */ + +#define next_chunk(p) ((mchunkptr)( ((char*)(p)) + ((p)->size & ~PREV_INUSE) )) + +/* Ptr to previous physical malloc_chunk */ + +#define prev_chunk(p)\ + ((mchunkptr)( ((char*)(p)) - ((p)->prev_size) )) + + +/* Treat space at ptr + offset as a chunk */ + +#define chunk_at_offset(p, s) ((mchunkptr)(((char*)(p)) + (s))) + + + + +/* + Dealing with use bits +*/ + +/* extract p's inuse bit */ + +#define inuse(p)\ +((((mchunkptr)(((char*)(p))+((p)->size & ~PREV_INUSE)))->size) & PREV_INUSE) + +/* extract inuse bit of previous chunk */ + +#define prev_inuse(p) ((p)->size & PREV_INUSE) + +/* check for mmap()'ed chunk */ + +#define chunk_is_mmapped(p) ((p)->size & IS_MMAPPED) + +/* set/clear chunk as in use without otherwise disturbing */ + +#define set_inuse(p)\ +((mchunkptr)(((char*)(p)) + ((p)->size & ~PREV_INUSE)))->size |= PREV_INUSE + +#define clear_inuse(p)\ +((mchunkptr)(((char*)(p)) + ((p)->size & ~PREV_INUSE)))->size &= ~(PREV_INUSE) + +/* check/set/clear inuse bits in known places */ + +#define inuse_bit_at_offset(p, s)\ + (((mchunkptr)(((char*)(p)) + (s)))->size & PREV_INUSE) + +#define set_inuse_bit_at_offset(p, s)\ + (((mchunkptr)(((char*)(p)) + (s)))->size |= PREV_INUSE) + +#define clear_inuse_bit_at_offset(p, s)\ + (((mchunkptr)(((char*)(p)) + (s)))->size &= ~(PREV_INUSE)) + + + + +/* + Dealing with size fields +*/ + +/* Get size, ignoring use bits */ + +#define chunksize(p) ((p)->size & ~(SIZE_BITS)) + +/* Set size at head, without disturbing its use bit */ + +#define set_head_size(p, s) ((p)->size = (((p)->size & PREV_INUSE) | (s))) + +/* Set size/use ignoring previous bits in header */ + +#define set_head(p, s) ((p)->size = (s)) + +/* Set size at footer (only when chunk is not in use) */ + +#define set_foot(p, s) (((mchunkptr)((char*)(p) + (s)))->prev_size = (s)) + + + + + +/* + Bins + + The bins, `av_' are an array of pairs of pointers serving as the + heads of (initially empty) doubly-linked lists of chunks, laid out + in a way so that each pair can be treated as if it were in a + malloc_chunk. (This way, the fd/bk offsets for linking bin heads + and chunks are the same). + + Bins for sizes < 512 bytes contain chunks of all the same size, spaced + 8 bytes apart. Larger bins are approximately logarithmically + spaced. (See the table below.) The `av_' array is never mentioned + directly in the code, but instead via bin access macros. + + Bin layout: + + 64 bins of size 8 + 32 bins of size 64 + 16 bins of size 512 + 8 bins of size 4096 + 4 bins of size 32768 + 2 bins of size 262144 + 1 bin of size what's left + + There is actually a little bit of slop in the numbers in bin_index + for the sake of speed. This makes no difference elsewhere. + + The special chunks `top' and `last_remainder' get their own bins, + (this is implemented via yet more trickery with the av_ array), + although `top' is never properly linked to its bin since it is + always handled specially. + +*/ + +#define NAV 128 /* number of bins */ + +typedef struct malloc_chunk* mbinptr; + +/* access macros */ + +#define bin_at(i) ((mbinptr)((char*)&(av_[2*(i) + 2]) - 2*SIZE_SZ)) +#define next_bin(b) ((mbinptr)((char*)(b) + 2 * sizeof(mbinptr))) +#define prev_bin(b) ((mbinptr)((char*)(b) - 2 * sizeof(mbinptr))) + +/* + The first 2 bins are never indexed. The corresponding av_ cells are instead + used for bookkeeping. This is not to save space, but to simplify + indexing, maintain locality, and avoid some initialization tests. +*/ + +#define top (av_[2]) /* The topmost chunk */ +#define last_remainder (bin_at(1)) /* remainder from last split */ + + +/* + Because top initially points to its own bin with initial + zero size, thus forcing extension on the first malloc request, + we avoid having any special code in malloc to check whether + it even exists yet. But we still need to in malloc_extend_top. +*/ + +#define initial_top ((mchunkptr)(bin_at(0))) + +/* Helper macro to initialize bins */ + +#define IAV(i) bin_at(i), bin_at(i) + +static mbinptr av_[NAV * 2 + 2] = { + NULL, NULL, + IAV(0), IAV(1), IAV(2), IAV(3), IAV(4), IAV(5), IAV(6), IAV(7), + IAV(8), IAV(9), IAV(10), IAV(11), IAV(12), IAV(13), IAV(14), IAV(15), + IAV(16), IAV(17), IAV(18), IAV(19), IAV(20), IAV(21), IAV(22), IAV(23), + IAV(24), IAV(25), IAV(26), IAV(27), IAV(28), IAV(29), IAV(30), IAV(31), + IAV(32), IAV(33), IAV(34), IAV(35), IAV(36), IAV(37), IAV(38), IAV(39), + IAV(40), IAV(41), IAV(42), IAV(43), IAV(44), IAV(45), IAV(46), IAV(47), + IAV(48), IAV(49), IAV(50), IAV(51), IAV(52), IAV(53), IAV(54), IAV(55), + IAV(56), IAV(57), IAV(58), IAV(59), IAV(60), IAV(61), IAV(62), IAV(63), + IAV(64), IAV(65), IAV(66), IAV(67), IAV(68), IAV(69), IAV(70), IAV(71), + IAV(72), IAV(73), IAV(74), IAV(75), IAV(76), IAV(77), IAV(78), IAV(79), + IAV(80), IAV(81), IAV(82), IAV(83), IAV(84), IAV(85), IAV(86), IAV(87), + IAV(88), IAV(89), IAV(90), IAV(91), IAV(92), IAV(93), IAV(94), IAV(95), + IAV(96), IAV(97), IAV(98), IAV(99), IAV(100), IAV(101), IAV(102), IAV(103), + IAV(104), IAV(105), IAV(106), IAV(107), IAV(108), IAV(109), IAV(110), IAV(111), + IAV(112), IAV(113), IAV(114), IAV(115), IAV(116), IAV(117), IAV(118), IAV(119), + IAV(120), IAV(121), IAV(122), IAV(123), IAV(124), IAV(125), IAV(126), IAV(127) +}; + +#ifdef CONFIG_NEEDS_MANUAL_RELOC +static void malloc_bin_reloc(void) +{ + mbinptr *p = &av_[2]; + size_t i; + + for (i = 2; i < ARRAY_SIZE(av_); ++i, ++p) + *p = (mbinptr)((ulong)*p + gd->reloc_off); +} +#else +static inline void malloc_bin_reloc(void) {} +#endif + +ulong mem_malloc_start = 0; +ulong mem_malloc_end = 0; +ulong mem_malloc_brk = 0; + +void *sbrk(ptrdiff_t increment) +{ + ulong old = mem_malloc_brk; + ulong new = old + increment; + + /* + * if we are giving memory back make sure we clear it out since + * we set MORECORE_CLEARS to 1 + */ + if (increment < 0) + memset((void *)new, 0, -increment); + + if ((new < mem_malloc_start) || (new > mem_malloc_end)) + return (void *)MORECORE_FAILURE; + + mem_malloc_brk = new; + + return (void *)old; +} + +void mem_malloc_init(ulong start, ulong size) +{ + mem_malloc_start = start; + mem_malloc_end = start + size; + mem_malloc_brk = start; + + memset((void *)mem_malloc_start, 0, size); + + malloc_bin_reloc(); +} + +/* field-extraction macros */ + +#define first(b) ((b)->fd) +#define last(b) ((b)->bk) + +/* + Indexing into bins +*/ + +#define bin_index(sz) \ +(((((unsigned long)(sz)) >> 9) == 0) ? (((unsigned long)(sz)) >> 3): \ + ((((unsigned long)(sz)) >> 9) <= 4) ? 56 + (((unsigned long)(sz)) >> 6): \ + ((((unsigned long)(sz)) >> 9) <= 20) ? 91 + (((unsigned long)(sz)) >> 9): \ + ((((unsigned long)(sz)) >> 9) <= 84) ? 110 + (((unsigned long)(sz)) >> 12): \ + ((((unsigned long)(sz)) >> 9) <= 340) ? 119 + (((unsigned long)(sz)) >> 15): \ + ((((unsigned long)(sz)) >> 9) <= 1364) ? 124 + (((unsigned long)(sz)) >> 18): \ + 126) +/* + bins for chunks < 512 are all spaced 8 bytes apart, and hold + identically sized chunks. This is exploited in malloc. +*/ + +#define MAX_SMALLBIN 63 +#define MAX_SMALLBIN_SIZE 512 +#define SMALLBIN_WIDTH 8 + +#define smallbin_index(sz) (((unsigned long)(sz)) >> 3) + +/* + Requests are `small' if both the corresponding and the next bin are small +*/ + +#define is_small_request(nb) (nb < MAX_SMALLBIN_SIZE - SMALLBIN_WIDTH) + + + +/* + To help compensate for the large number of bins, a one-level index + structure is used for bin-by-bin searching. `binblocks' is a + one-word bitvector recording whether groups of BINBLOCKWIDTH bins + have any (possibly) non-empty bins, so they can be skipped over + all at once during during traversals. The bits are NOT always + cleared as soon as all bins in a block are empty, but instead only + when all are noticed to be empty during traversal in malloc. +*/ + +#define BINBLOCKWIDTH 4 /* bins per block */ + +#define binblocks_r ((INTERNAL_SIZE_T)av_[1]) /* bitvector of nonempty blocks */ +#define binblocks_w (av_[1]) + +/* bin<->block macros */ + +#define idx2binblock(ix) ((unsigned)1 << (ix / BINBLOCKWIDTH)) +#define mark_binblock(ii) (binblocks_w = (mbinptr)(binblocks_r | idx2binblock(ii))) +#define clear_binblock(ii) (binblocks_w = (mbinptr)(binblocks_r & ~(idx2binblock(ii)))) + + + + + +/* Other static bookkeeping data */ + +/* variables holding tunable values */ + +static unsigned long trim_threshold = DEFAULT_TRIM_THRESHOLD; +static unsigned long top_pad = DEFAULT_TOP_PAD; +static unsigned int n_mmaps_max = DEFAULT_MMAP_MAX; +static unsigned long mmap_threshold = DEFAULT_MMAP_THRESHOLD; + +/* The first value returned from sbrk */ +static char* sbrk_base = (char*)(-1); + +/* The maximum memory obtained from system via sbrk */ +static unsigned long max_sbrked_mem = 0; + +/* The maximum via either sbrk or mmap */ +static unsigned long max_total_mem = 0; + +/* internal working copy of mallinfo */ +static struct mallinfo current_mallinfo = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + +/* The total memory obtained from system via sbrk */ +#define sbrked_mem (current_mallinfo.arena) + +/* Tracking mmaps */ + +#ifdef DEBUG +static unsigned int n_mmaps = 0; +#endif /* DEBUG */ +static unsigned long mmapped_mem = 0; +#if HAVE_MMAP +static unsigned int max_n_mmaps = 0; +static unsigned long max_mmapped_mem = 0; +#endif + + + +/* + Debugging support +*/ + +#ifdef DEBUG + + +/* + These routines make a number of assertions about the states + of data structures that should be true at all times. If any + are not true, it's very likely that a user program has somehow + trashed memory. (It's also possible that there is a coding error + in malloc. In which case, please report it!) +*/ + +#if __STD_C +static void do_check_chunk(mchunkptr p) +#else +static void do_check_chunk(p) mchunkptr p; +#endif +{ + INTERNAL_SIZE_T sz = p->size & ~PREV_INUSE; + + /* No checkable chunk is mmapped */ + assert(!chunk_is_mmapped(p)); + + /* Check for legal address ... */ + assert((char*)p >= sbrk_base); + if (p != top) + assert((char*)p + sz <= (char*)top); + else + assert((char*)p + sz <= sbrk_base + sbrked_mem); + +} + + +#if __STD_C +static void do_check_free_chunk(mchunkptr p) +#else +static void do_check_free_chunk(p) mchunkptr p; +#endif +{ + INTERNAL_SIZE_T sz = p->size & ~PREV_INUSE; + mchunkptr next = chunk_at_offset(p, sz); + + do_check_chunk(p); + + /* Check whether it claims to be free ... */ + assert(!inuse(p)); + + /* Unless a special marker, must have OK fields */ + if ((long)sz >= (long)MINSIZE) + { + assert((sz & MALLOC_ALIGN_MASK) == 0); + assert(aligned_OK(chunk2mem(p))); + /* ... matching footer field */ + assert(next->prev_size == sz); + /* ... and is fully consolidated */ + assert(prev_inuse(p)); + assert (next == top || inuse(next)); + + /* ... and has minimally sane links */ + assert(p->fd->bk == p); + assert(p->bk->fd == p); + } + else /* markers are always of size SIZE_SZ */ + assert(sz == SIZE_SZ); +} + +#if __STD_C +static void do_check_inuse_chunk(mchunkptr p) +#else +static void do_check_inuse_chunk(p) mchunkptr p; +#endif +{ + mchunkptr next = next_chunk(p); + do_check_chunk(p); + + /* Check whether it claims to be in use ... */ + assert(inuse(p)); + + /* ... and is surrounded by OK chunks. + Since more things can be checked with free chunks than inuse ones, + if an inuse chunk borders them and debug is on, it's worth doing them. + */ + if (!prev_inuse(p)) + { + mchunkptr prv = prev_chunk(p); + assert(next_chunk(prv) == p); + do_check_free_chunk(prv); + } + if (next == top) + { + assert(prev_inuse(next)); + assert(chunksize(next) >= MINSIZE); + } + else if (!inuse(next)) + do_check_free_chunk(next); + +} + +#if __STD_C +static void do_check_malloced_chunk(mchunkptr p, INTERNAL_SIZE_T s) +#else +static void do_check_malloced_chunk(p, s) mchunkptr p; INTERNAL_SIZE_T s; +#endif +{ + INTERNAL_SIZE_T sz = p->size & ~PREV_INUSE; + long room = sz - s; + + do_check_inuse_chunk(p); + + /* Legal size ... */ + assert((long)sz >= (long)MINSIZE); + assert((sz & MALLOC_ALIGN_MASK) == 0); + assert(room >= 0); + assert(room < (long)MINSIZE); + + /* ... and alignment */ + assert(aligned_OK(chunk2mem(p))); + + + /* ... and was allocated at front of an available chunk */ + assert(prev_inuse(p)); + +} + + +#define check_free_chunk(P) do_check_free_chunk(P) +#define check_inuse_chunk(P) do_check_inuse_chunk(P) +#define check_chunk(P) do_check_chunk(P) +#define check_malloced_chunk(P,N) do_check_malloced_chunk(P,N) +#else +#define check_free_chunk(P) +#define check_inuse_chunk(P) +#define check_chunk(P) +#define check_malloced_chunk(P,N) +#endif + + + +/* + Macro-based internal utilities +*/ + + +/* + Linking chunks in bin lists. + Call these only with variables, not arbitrary expressions, as arguments. +*/ + +/* + Place chunk p of size s in its bin, in size order, + putting it ahead of others of same size. +*/ + + +#define frontlink(P, S, IDX, BK, FD) \ +{ \ + if (S < MAX_SMALLBIN_SIZE) \ + { \ + IDX = smallbin_index(S); \ + mark_binblock(IDX); \ + BK = bin_at(IDX); \ + FD = BK->fd; \ + P->bk = BK; \ + P->fd = FD; \ + FD->bk = BK->fd = P; \ + } \ + else \ + { \ + IDX = bin_index(S); \ + BK = bin_at(IDX); \ + FD = BK->fd; \ + if (FD == BK) mark_binblock(IDX); \ + else \ + { \ + while (FD != BK && S < chunksize(FD)) FD = FD->fd; \ + BK = FD->bk; \ + } \ + P->bk = BK; \ + P->fd = FD; \ + FD->bk = BK->fd = P; \ + } \ +} + + +/* take a chunk off a list */ + +#define unlink(P, BK, FD) \ +{ \ + BK = P->bk; \ + FD = P->fd; \ + FD->bk = BK; \ + BK->fd = FD; \ +} \ + +/* Place p as the last remainder */ + +#define link_last_remainder(P) \ +{ \ + last_remainder->fd = last_remainder->bk = P; \ + P->fd = P->bk = last_remainder; \ +} + +/* Clear the last_remainder bin */ + +#define clear_last_remainder \ + (last_remainder->fd = last_remainder->bk = last_remainder) + + + + + +/* Routines dealing with mmap(). */ + +#if HAVE_MMAP + +#if __STD_C +static mchunkptr mmap_chunk(size_t size) +#else +static mchunkptr mmap_chunk(size) size_t size; +#endif +{ + size_t page_mask = malloc_getpagesize - 1; + mchunkptr p; + +#ifndef MAP_ANONYMOUS + static int fd = -1; +#endif + + if(n_mmaps >= n_mmaps_max) return 0; /* too many regions */ + + /* For mmapped chunks, the overhead is one SIZE_SZ unit larger, because + * there is no following chunk whose prev_size field could be used. + */ + size = (size + SIZE_SZ + page_mask) & ~page_mask; + +#ifdef MAP_ANONYMOUS + p = (mchunkptr)mmap(0, size, PROT_READ|PROT_WRITE, + MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); +#else /* !MAP_ANONYMOUS */ + if (fd < 0) + { + fd = open("/dev/zero", O_RDWR); + if(fd < 0) return 0; + } + p = (mchunkptr)mmap(0, size, PROT_READ|PROT_WRITE, MAP_PRIVATE, fd, 0); +#endif + + if(p == (mchunkptr)-1) return 0; + + n_mmaps++; + if (n_mmaps > max_n_mmaps) max_n_mmaps = n_mmaps; + + /* We demand that eight bytes into a page must be 8-byte aligned. */ + assert(aligned_OK(chunk2mem(p))); + + /* The offset to the start of the mmapped region is stored + * in the prev_size field of the chunk; normally it is zero, + * but that can be changed in memalign(). + */ + p->prev_size = 0; + set_head(p, size|IS_MMAPPED); + + mmapped_mem += size; + if ((unsigned long)mmapped_mem > (unsigned long)max_mmapped_mem) + max_mmapped_mem = mmapped_mem; + if ((unsigned long)(mmapped_mem + sbrked_mem) > (unsigned long)max_total_mem) + max_total_mem = mmapped_mem + sbrked_mem; + return p; +} + +#if __STD_C +static void munmap_chunk(mchunkptr p) +#else +static void munmap_chunk(p) mchunkptr p; +#endif +{ + INTERNAL_SIZE_T size = chunksize(p); + int ret; + + assert (chunk_is_mmapped(p)); + assert(! ((char*)p >= sbrk_base && (char*)p < sbrk_base + sbrked_mem)); + assert((n_mmaps > 0)); + assert(((p->prev_size + size) & (malloc_getpagesize-1)) == 0); + + n_mmaps--; + mmapped_mem -= (size + p->prev_size); + + ret = munmap((char *)p - p->prev_size, size + p->prev_size); + + /* munmap returns non-zero on failure */ + assert(ret == 0); +} + +#if HAVE_MREMAP + +#if __STD_C +static mchunkptr mremap_chunk(mchunkptr p, size_t new_size) +#else +static mchunkptr mremap_chunk(p, new_size) mchunkptr p; size_t new_size; +#endif +{ + size_t page_mask = malloc_getpagesize - 1; + INTERNAL_SIZE_T offset = p->prev_size; + INTERNAL_SIZE_T size = chunksize(p); + char *cp; + + assert (chunk_is_mmapped(p)); + assert(! ((char*)p >= sbrk_base && (char*)p < sbrk_base + sbrked_mem)); + assert((n_mmaps > 0)); + assert(((size + offset) & (malloc_getpagesize-1)) == 0); + + /* Note the extra SIZE_SZ overhead as in mmap_chunk(). */ + new_size = (new_size + offset + SIZE_SZ + page_mask) & ~page_mask; + + cp = (char *)mremap((char *)p - offset, size + offset, new_size, 1); + + if (cp == (char *)-1) return 0; + + p = (mchunkptr)(cp + offset); + + assert(aligned_OK(chunk2mem(p))); + + assert((p->prev_size == offset)); + set_head(p, (new_size - offset)|IS_MMAPPED); + + mmapped_mem -= size + offset; + mmapped_mem += new_size; + if ((unsigned long)mmapped_mem > (unsigned long)max_mmapped_mem) + max_mmapped_mem = mmapped_mem; + if ((unsigned long)(mmapped_mem + sbrked_mem) > (unsigned long)max_total_mem) + max_total_mem = mmapped_mem + sbrked_mem; + return p; +} + +#endif /* HAVE_MREMAP */ + +#endif /* HAVE_MMAP */ + + + + +/* + Extend the top-most chunk by obtaining memory from system. + Main interface to sbrk (but see also malloc_trim). +*/ + +#if __STD_C +static void malloc_extend_top(INTERNAL_SIZE_T nb) +#else +static void malloc_extend_top(nb) INTERNAL_SIZE_T nb; +#endif +{ + char* brk; /* return value from sbrk */ + INTERNAL_SIZE_T front_misalign; /* unusable bytes at front of sbrked space */ + INTERNAL_SIZE_T correction; /* bytes for 2nd sbrk call */ + char* new_brk; /* return of 2nd sbrk call */ + INTERNAL_SIZE_T top_size; /* new size of top chunk */ + + mchunkptr old_top = top; /* Record state of old top */ + INTERNAL_SIZE_T old_top_size = chunksize(old_top); + char* old_end = (char*)(chunk_at_offset(old_top, old_top_size)); + + /* Pad request with top_pad plus minimal overhead */ + + INTERNAL_SIZE_T sbrk_size = nb + top_pad + MINSIZE; + unsigned long pagesz = malloc_getpagesize; + + /* If not the first time through, round to preserve page boundary */ + /* Otherwise, we need to correct to a page size below anyway. */ + /* (We also correct below if an intervening foreign sbrk call.) */ + + if (sbrk_base != (char*)(-1)) + sbrk_size = (sbrk_size + (pagesz - 1)) & ~(pagesz - 1); + + brk = (char*)(MORECORE (sbrk_size)); + + /* Fail if sbrk failed or if a foreign sbrk call killed our space */ + if (brk == (char*)(MORECORE_FAILURE) || + (brk < old_end && old_top != initial_top)) + return; + + sbrked_mem += sbrk_size; + + if (brk == old_end) /* can just add bytes to current top */ + { + top_size = sbrk_size + old_top_size; + set_head(top, top_size | PREV_INUSE); + } + else + { + if (sbrk_base == (char*)(-1)) /* First time through. Record base */ + sbrk_base = brk; + else /* Someone else called sbrk(). Count those bytes as sbrked_mem. */ + sbrked_mem += brk - (char*)old_end; + + /* Guarantee alignment of first new chunk made from this space */ + front_misalign = (unsigned long)chunk2mem(brk) & MALLOC_ALIGN_MASK; + if (front_misalign > 0) + { + correction = (MALLOC_ALIGNMENT) - front_misalign; + brk += correction; + } + else + correction = 0; + + /* Guarantee the next brk will be at a page boundary */ + + correction += ((((unsigned long)(brk + sbrk_size))+(pagesz-1)) & + ~(pagesz - 1)) - ((unsigned long)(brk + sbrk_size)); + + /* Allocate correction */ + new_brk = (char*)(MORECORE (correction)); + if (new_brk == (char*)(MORECORE_FAILURE)) return; + + sbrked_mem += correction; + + top = (mchunkptr)brk; + top_size = new_brk - brk + correction; + set_head(top, top_size | PREV_INUSE); + + if (old_top != initial_top) + { + + /* There must have been an intervening foreign sbrk call. */ + /* A double fencepost is necessary to prevent consolidation */ + + /* If not enough space to do this, then user did something very wrong */ + if (old_top_size < MINSIZE) + { + set_head(top, PREV_INUSE); /* will force null return from malloc */ + return; + } + + /* Also keep size a multiple of MALLOC_ALIGNMENT */ + old_top_size = (old_top_size - 3*SIZE_SZ) & ~MALLOC_ALIGN_MASK; + set_head_size(old_top, old_top_size); + chunk_at_offset(old_top, old_top_size )->size = + SIZE_SZ|PREV_INUSE; + chunk_at_offset(old_top, old_top_size + SIZE_SZ)->size = + SIZE_SZ|PREV_INUSE; + /* If possible, release the rest. */ + if (old_top_size >= MINSIZE) + fREe(chunk2mem(old_top)); + } + } + + if ((unsigned long)sbrked_mem > (unsigned long)max_sbrked_mem) + max_sbrked_mem = sbrked_mem; + if ((unsigned long)(mmapped_mem + sbrked_mem) > (unsigned long)max_total_mem) + max_total_mem = mmapped_mem + sbrked_mem; + + /* We always land on a page boundary */ + assert(((unsigned long)((char*)top + top_size) & (pagesz - 1)) == 0); +} + + + + +/* Main public routines */ + + +/* + Malloc Algorthim: + + The requested size is first converted into a usable form, `nb'. + This currently means to add 4 bytes overhead plus possibly more to + obtain 8-byte alignment and/or to obtain a size of at least + MINSIZE (currently 16 bytes), the smallest allocatable size. + (All fits are considered `exact' if they are within MINSIZE bytes.) + + From there, the first successful of the following steps is taken: + + 1. The bin corresponding to the request size is scanned, and if + a chunk of exactly the right size is found, it is taken. + + 2. The most recently remaindered chunk is used if it is big + enough. This is a form of (roving) first fit, used only in + the absence of exact fits. Runs of consecutive requests use + the remainder of the chunk used for the previous such request + whenever possible. This limited use of a first-fit style + allocation strategy tends to give contiguous chunks + coextensive lifetimes, which improves locality and can reduce + fragmentation in the long run. + + 3. Other bins are scanned in increasing size order, using a + chunk big enough to fulfill the request, and splitting off + any remainder. This search is strictly by best-fit; i.e., + the smallest (with ties going to approximately the least + recently used) chunk that fits is selected. + + 4. If large enough, the chunk bordering the end of memory + (`top') is split off. (This use of `top' is in accord with + the best-fit search rule. In effect, `top' is treated as + larger (and thus less well fitting) than any other available + chunk since it can be extended to be as large as necessary + (up to system limitations). + + 5. If the request size meets the mmap threshold and the + system supports mmap, and there are few enough currently + allocated mmapped regions, and a call to mmap succeeds, + the request is allocated via direct memory mapping. + + 6. Otherwise, the top of memory is extended by + obtaining more space from the system (normally using sbrk, + but definable to anything else via the MORECORE macro). + Memory is gathered from the system (in system page-sized + units) in a way that allows chunks obtained across different + sbrk calls to be consolidated, but does not require + contiguous memory. Thus, it should be safe to intersperse + mallocs with other sbrk calls. + + + All allocations are made from the the `lowest' part of any found + chunk. (The implementation invariant is that prev_inuse is + always true of any allocated chunk; i.e., that each allocated + chunk borders either a previously allocated and still in-use chunk, + or the base of its memory arena.) + +*/ + +#if __STD_C +Void_t* mALLOc(size_t bytes) +#else +Void_t* mALLOc(bytes) size_t bytes; +#endif +{ + mchunkptr victim; /* inspected/selected chunk */ + INTERNAL_SIZE_T victim_size; /* its size */ + int idx; /* index for bin traversal */ + mbinptr bin; /* associated bin */ + mchunkptr remainder; /* remainder from a split */ + long remainder_size; /* its size */ + int remainder_index; /* its bin index */ + unsigned long block; /* block traverser bit */ + int startidx; /* first bin of a traversed block */ + mchunkptr fwd; /* misc temp for linking */ + mchunkptr bck; /* misc temp for linking */ + mbinptr q; /* misc temp */ + + INTERNAL_SIZE_T nb; + + /* check if mem_malloc_init() was run */ + if ((mem_malloc_start == 0) && (mem_malloc_end == 0)) { + /* not initialized yet */ + return NULL; + } + + if ((long)bytes < 0) return NULL; + + nb = request2size(bytes); /* padded request size; */ + + /* Check for exact match in a bin */ + + if (is_small_request(nb)) /* Faster version for small requests */ + { + idx = smallbin_index(nb); + + /* No traversal or size check necessary for small bins. */ + + q = bin_at(idx); + victim = last(q); + + /* Also scan the next one, since it would have a remainder < MINSIZE */ + if (victim == q) + { + q = next_bin(q); + victim = last(q); + } + if (victim != q) + { + victim_size = chunksize(victim); + unlink(victim, bck, fwd); + set_inuse_bit_at_offset(victim, victim_size); + check_malloced_chunk(victim, nb); + return chunk2mem(victim); + } + + idx += 2; /* Set for bin scan below. We've already scanned 2 bins. */ + + } + else + { + idx = bin_index(nb); + bin = bin_at(idx); + + for (victim = last(bin); victim != bin; victim = victim->bk) + { + victim_size = chunksize(victim); + remainder_size = victim_size - nb; + + if (remainder_size >= (long)MINSIZE) /* too big */ + { + --idx; /* adjust to rescan below after checking last remainder */ + break; + } + + else if (remainder_size >= 0) /* exact fit */ + { + unlink(victim, bck, fwd); + set_inuse_bit_at_offset(victim, victim_size); + check_malloced_chunk(victim, nb); + return chunk2mem(victim); + } + } + + ++idx; + + } + + /* Try to use the last split-off remainder */ + + if ( (victim = last_remainder->fd) != last_remainder) + { + victim_size = chunksize(victim); + remainder_size = victim_size - nb; + + if (remainder_size >= (long)MINSIZE) /* re-split */ + { + remainder = chunk_at_offset(victim, nb); + set_head(victim, nb | PREV_INUSE); + link_last_remainder(remainder); + set_head(remainder, remainder_size | PREV_INUSE); + set_foot(remainder, remainder_size); + check_malloced_chunk(victim, nb); + return chunk2mem(victim); + } + + clear_last_remainder; + + if (remainder_size >= 0) /* exhaust */ + { + set_inuse_bit_at_offset(victim, victim_size); + check_malloced_chunk(victim, nb); + return chunk2mem(victim); + } + + /* Else place in bin */ + + frontlink(victim, victim_size, remainder_index, bck, fwd); + } + + /* + If there are any possibly nonempty big-enough blocks, + search for best fitting chunk by scanning bins in blockwidth units. + */ + + if ( (block = idx2binblock(idx)) <= binblocks_r) + { + + /* Get to the first marked block */ + + if ( (block & binblocks_r) == 0) + { + /* force to an even block boundary */ + idx = (idx & ~(BINBLOCKWIDTH - 1)) + BINBLOCKWIDTH; + block <<= 1; + while ((block & binblocks_r) == 0) + { + idx += BINBLOCKWIDTH; + block <<= 1; + } + } + + /* For each possibly nonempty block ... */ + for (;;) + { + startidx = idx; /* (track incomplete blocks) */ + q = bin = bin_at(idx); + + /* For each bin in this block ... */ + do + { + /* Find and use first big enough chunk ... */ + + for (victim = last(bin); victim != bin; victim = victim->bk) + { + victim_size = chunksize(victim); + remainder_size = victim_size - nb; + + if (remainder_size >= (long)MINSIZE) /* split */ + { + remainder = chunk_at_offset(victim, nb); + set_head(victim, nb | PREV_INUSE); + unlink(victim, bck, fwd); + link_last_remainder(remainder); + set_head(remainder, remainder_size | PREV_INUSE); + set_foot(remainder, remainder_size); + check_malloced_chunk(victim, nb); + return chunk2mem(victim); + } + + else if (remainder_size >= 0) /* take */ + { + set_inuse_bit_at_offset(victim, victim_size); + unlink(victim, bck, fwd); + check_malloced_chunk(victim, nb); + return chunk2mem(victim); + } + + } + + bin = next_bin(bin); + + } while ((++idx & (BINBLOCKWIDTH - 1)) != 0); + + /* Clear out the block bit. */ + + do /* Possibly backtrack to try to clear a partial block */ + { + if ((startidx & (BINBLOCKWIDTH - 1)) == 0) + { + av_[1] = (mbinptr)(binblocks_r & ~block); + break; + } + --startidx; + q = prev_bin(q); + } while (first(q) == q); + + /* Get to the next possibly nonempty block */ + + if ( (block <<= 1) <= binblocks_r && (block != 0) ) + { + while ((block & binblocks_r) == 0) + { + idx += BINBLOCKWIDTH; + block <<= 1; + } + } + else + break; + } + } + + + /* Try to use top chunk */ + + /* Require that there be a remainder, ensuring top always exists */ + if ( (remainder_size = chunksize(top) - nb) < (long)MINSIZE) + { + +#if HAVE_MMAP + /* If big and would otherwise need to extend, try to use mmap instead */ + if ((unsigned long)nb >= (unsigned long)mmap_threshold && + (victim = mmap_chunk(nb)) != 0) + return chunk2mem(victim); +#endif + + /* Try to extend */ + malloc_extend_top(nb); + if ( (remainder_size = chunksize(top) - nb) < (long)MINSIZE) + return NULL; /* propagate failure */ + } + + victim = top; + set_head(victim, nb | PREV_INUSE); + top = chunk_at_offset(victim, nb); + set_head(top, remainder_size | PREV_INUSE); + check_malloced_chunk(victim, nb); + return chunk2mem(victim); + +} + + + + +/* + + free() algorithm : + + cases: + + 1. free(0) has no effect. + + 2. If the chunk was allocated via mmap, it is release via munmap(). + + 3. If a returned chunk borders the current high end of memory, + it is consolidated into the top, and if the total unused + topmost memory exceeds the trim threshold, malloc_trim is + called. + + 4. Other chunks are consolidated as they arrive, and + placed in corresponding bins. (This includes the case of + consolidating with the current `last_remainder'). + +*/ + + +#if __STD_C +void fREe(Void_t* mem) +#else +void fREe(mem) Void_t* mem; +#endif +{ + mchunkptr p; /* chunk corresponding to mem */ + INTERNAL_SIZE_T hd; /* its head field */ + INTERNAL_SIZE_T sz; /* its size */ + int idx; /* its bin index */ + mchunkptr next; /* next contiguous chunk */ + INTERNAL_SIZE_T nextsz; /* its size */ + INTERNAL_SIZE_T prevsz; /* size of previous contiguous chunk */ + mchunkptr bck; /* misc temp for linking */ + mchunkptr fwd; /* misc temp for linking */ + int islr; /* track whether merging with last_remainder */ + + if (mem == NULL) /* free(0) has no effect */ + return; + + p = mem2chunk(mem); + hd = p->size; + +#if HAVE_MMAP + if (hd & IS_MMAPPED) /* release mmapped memory. */ + { + munmap_chunk(p); + return; + } +#endif + + check_inuse_chunk(p); + + sz = hd & ~PREV_INUSE; + next = chunk_at_offset(p, sz); + nextsz = chunksize(next); + + if (next == top) /* merge with top */ + { + sz += nextsz; + + if (!(hd & PREV_INUSE)) /* consolidate backward */ + { + prevsz = p->prev_size; + p = chunk_at_offset(p, -((long) prevsz)); + sz += prevsz; + unlink(p, bck, fwd); + } + + set_head(p, sz | PREV_INUSE); + top = p; + if ((unsigned long)(sz) >= (unsigned long)trim_threshold) + malloc_trim(top_pad); + return; + } + + set_head(next, nextsz); /* clear inuse bit */ + + islr = 0; + + if (!(hd & PREV_INUSE)) /* consolidate backward */ + { + prevsz = p->prev_size; + p = chunk_at_offset(p, -((long) prevsz)); + sz += prevsz; + + if (p->fd == last_remainder) /* keep as last_remainder */ + islr = 1; + else + unlink(p, bck, fwd); + } + + if (!(inuse_bit_at_offset(next, nextsz))) /* consolidate forward */ + { + sz += nextsz; + + if (!islr && next->fd == last_remainder) /* re-insert last_remainder */ + { + islr = 1; + link_last_remainder(p); + } + else + unlink(next, bck, fwd); + } + + + set_head(p, sz | PREV_INUSE); + set_foot(p, sz); + if (!islr) + frontlink(p, sz, idx, bck, fwd); +} + + + + + +/* + + Realloc algorithm: + + Chunks that were obtained via mmap cannot be extended or shrunk + unless HAVE_MREMAP is defined, in which case mremap is used. + Otherwise, if their reallocation is for additional space, they are + copied. If for less, they are just left alone. + + Otherwise, if the reallocation is for additional space, and the + chunk can be extended, it is, else a malloc-copy-free sequence is + taken. There are several different ways that a chunk could be + extended. All are tried: + + * Extending forward into following adjacent free chunk. + * Shifting backwards, joining preceding adjacent space + * Both shifting backwards and extending forward. + * Extending into newly sbrked space + + Unless the #define REALLOC_ZERO_BYTES_FREES is set, realloc with a + size argument of zero (re)allocates a minimum-sized chunk. + + If the reallocation is for less space, and the new request is for + a `small' (<512 bytes) size, then the newly unused space is lopped + off and freed. + + The old unix realloc convention of allowing the last-free'd chunk + to be used as an argument to realloc is no longer supported. + I don't know of any programs still relying on this feature, + and allowing it would also allow too many other incorrect + usages of realloc to be sensible. + + +*/ + + +#if __STD_C +Void_t* rEALLOc(Void_t* oldmem, size_t bytes) +#else +Void_t* rEALLOc(oldmem, bytes) Void_t* oldmem; size_t bytes; +#endif +{ + INTERNAL_SIZE_T nb; /* padded request size */ + + mchunkptr oldp; /* chunk corresponding to oldmem */ + INTERNAL_SIZE_T oldsize; /* its size */ + + mchunkptr newp; /* chunk to return */ + INTERNAL_SIZE_T newsize; /* its size */ + Void_t* newmem; /* corresponding user mem */ + + mchunkptr next; /* next contiguous chunk after oldp */ + INTERNAL_SIZE_T nextsize; /* its size */ + + mchunkptr prev; /* previous contiguous chunk before oldp */ + INTERNAL_SIZE_T prevsize; /* its size */ + + mchunkptr remainder; /* holds split off extra space from newp */ + INTERNAL_SIZE_T remainder_size; /* its size */ + + mchunkptr bck; /* misc temp for linking */ + mchunkptr fwd; /* misc temp for linking */ + +#ifdef REALLOC_ZERO_BYTES_FREES + if (bytes == 0) { fREe(oldmem); return 0; } +#endif + + if ((long)bytes < 0) return NULL; + + /* realloc of null is supposed to be same as malloc */ + if (oldmem == NULL) return mALLOc(bytes); + + newp = oldp = mem2chunk(oldmem); + newsize = oldsize = chunksize(oldp); + + + nb = request2size(bytes); + +#if HAVE_MMAP + if (chunk_is_mmapped(oldp)) + { +#if HAVE_MREMAP + newp = mremap_chunk(oldp, nb); + if(newp) return chunk2mem(newp); +#endif + /* Note the extra SIZE_SZ overhead. */ + if(oldsize - SIZE_SZ >= nb) return oldmem; /* do nothing */ + /* Must alloc, copy, free. */ + newmem = mALLOc(bytes); + if (newmem == 0) return 0; /* propagate failure */ + MALLOC_COPY(newmem, oldmem, oldsize - 2*SIZE_SZ); + munmap_chunk(oldp); + return newmem; + } +#endif + + check_inuse_chunk(oldp); + + if ((long)(oldsize) < (long)(nb)) + { + + /* Try expanding forward */ + + next = chunk_at_offset(oldp, oldsize); + if (next == top || !inuse(next)) + { + nextsize = chunksize(next); + + /* Forward into top only if a remainder */ + if (next == top) + { + if ((long)(nextsize + newsize) >= (long)(nb + MINSIZE)) + { + newsize += nextsize; + top = chunk_at_offset(oldp, nb); + set_head(top, (newsize - nb) | PREV_INUSE); + set_head_size(oldp, nb); + return chunk2mem(oldp); + } + } + + /* Forward into next chunk */ + else if (((long)(nextsize + newsize) >= (long)(nb))) + { + unlink(next, bck, fwd); + newsize += nextsize; + goto split; + } + } + else + { + next = NULL; + nextsize = 0; + } + + /* Try shifting backwards. */ + + if (!prev_inuse(oldp)) + { + prev = prev_chunk(oldp); + prevsize = chunksize(prev); + + /* try forward + backward first to save a later consolidation */ + + if (next != NULL) + { + /* into top */ + if (next == top) + { + if ((long)(nextsize + prevsize + newsize) >= (long)(nb + MINSIZE)) + { + unlink(prev, bck, fwd); + newp = prev; + newsize += prevsize + nextsize; + newmem = chunk2mem(newp); + MALLOC_COPY(newmem, oldmem, oldsize - SIZE_SZ); + top = chunk_at_offset(newp, nb); + set_head(top, (newsize - nb) | PREV_INUSE); + set_head_size(newp, nb); + return newmem; + } + } + + /* into next chunk */ + else if (((long)(nextsize + prevsize + newsize) >= (long)(nb))) + { + unlink(next, bck, fwd); + unlink(prev, bck, fwd); + newp = prev; + newsize += nextsize + prevsize; + newmem = chunk2mem(newp); + MALLOC_COPY(newmem, oldmem, oldsize - SIZE_SZ); + goto split; + } + } + + /* backward only */ + if (prev != NULL && (long)(prevsize + newsize) >= (long)nb) + { + unlink(prev, bck, fwd); + newp = prev; + newsize += prevsize; + newmem = chunk2mem(newp); + MALLOC_COPY(newmem, oldmem, oldsize - SIZE_SZ); + goto split; + } + } + + /* Must allocate */ + + newmem = mALLOc (bytes); + + if (newmem == NULL) /* propagate failure */ + return NULL; + + /* Avoid copy if newp is next chunk after oldp. */ + /* (This can only happen when new chunk is sbrk'ed.) */ + + if ( (newp = mem2chunk(newmem)) == next_chunk(oldp)) + { + newsize += chunksize(newp); + newp = oldp; + goto split; + } + + /* Otherwise copy, free, and exit */ + MALLOC_COPY(newmem, oldmem, oldsize - SIZE_SZ); + fREe(oldmem); + return newmem; + } + + + split: /* split off extra room in old or expanded chunk */ + + if (newsize - nb >= MINSIZE) /* split off remainder */ + { + remainder = chunk_at_offset(newp, nb); + remainder_size = newsize - nb; + set_head_size(newp, nb); + set_head(remainder, remainder_size | PREV_INUSE); + set_inuse_bit_at_offset(remainder, remainder_size); + fREe(chunk2mem(remainder)); /* let free() deal with it */ + } + else + { + set_head_size(newp, newsize); + set_inuse_bit_at_offset(newp, newsize); + } + + check_inuse_chunk(newp); + return chunk2mem(newp); +} + + + + +/* + + memalign algorithm: + + memalign requests more than enough space from malloc, finds a spot + within that chunk that meets the alignment request, and then + possibly frees the leading and trailing space. + + The alignment argument must be a power of two. This property is not + checked by memalign, so misuse may result in random runtime errors. + + 8-byte alignment is guaranteed by normal malloc calls, so don't + bother calling memalign with an argument of 8 or less. + + Overreliance on memalign is a sure way to fragment space. + +*/ + + +#if __STD_C +Void_t* mEMALIGn(size_t alignment, size_t bytes) +#else +Void_t* mEMALIGn(alignment, bytes) size_t alignment; size_t bytes; +#endif +{ + INTERNAL_SIZE_T nb; /* padded request size */ + char* m; /* memory returned by malloc call */ + mchunkptr p; /* corresponding chunk */ + char* brk; /* alignment point within p */ + mchunkptr newp; /* chunk to return */ + INTERNAL_SIZE_T newsize; /* its size */ + INTERNAL_SIZE_T leadsize; /* leading space befor alignment point */ + mchunkptr remainder; /* spare room at end to split off */ + long remainder_size; /* its size */ + + if ((long)bytes < 0) return NULL; + + /* If need less alignment than we give anyway, just relay to malloc */ + + if (alignment <= MALLOC_ALIGNMENT) return mALLOc(bytes); + + /* Otherwise, ensure that it is at least a minimum chunk size */ + + if (alignment < MINSIZE) alignment = MINSIZE; + + /* Call malloc with worst case padding to hit alignment. */ + + nb = request2size(bytes); + m = (char*)(mALLOc(nb + alignment + MINSIZE)); + + if (m == NULL) return NULL; /* propagate failure */ + + p = mem2chunk(m); + + if ((((unsigned long)(m)) % alignment) == 0) /* aligned */ + { +#if HAVE_MMAP + if(chunk_is_mmapped(p)) + return chunk2mem(p); /* nothing more to do */ +#endif + } + else /* misaligned */ + { + /* + Find an aligned spot inside chunk. + Since we need to give back leading space in a chunk of at + least MINSIZE, if the first calculation places us at + a spot with less than MINSIZE leader, we can move to the + next aligned spot -- we've allocated enough total room so that + this is always possible. + */ + + brk = (char*)mem2chunk(((unsigned long)(m + alignment - 1)) & -((signed) alignment)); + if ((long)(brk - (char*)(p)) < MINSIZE) brk = brk + alignment; + + newp = (mchunkptr)brk; + leadsize = brk - (char*)(p); + newsize = chunksize(p) - leadsize; + +#if HAVE_MMAP + if(chunk_is_mmapped(p)) + { + newp->prev_size = p->prev_size + leadsize; + set_head(newp, newsize|IS_MMAPPED); + return chunk2mem(newp); + } +#endif + + /* give back leader, use the rest */ + + set_head(newp, newsize | PREV_INUSE); + set_inuse_bit_at_offset(newp, newsize); + set_head_size(p, leadsize); + fREe(chunk2mem(p)); + p = newp; + + assert (newsize >= nb && (((unsigned long)(chunk2mem(p))) % alignment) == 0); + } + + /* Also give back spare room at the end */ + + remainder_size = chunksize(p) - nb; + + if (remainder_size >= (long)MINSIZE) + { + remainder = chunk_at_offset(p, nb); + set_head(remainder, remainder_size | PREV_INUSE); + set_head_size(p, nb); + fREe(chunk2mem(remainder)); + } + + check_inuse_chunk(p); + return chunk2mem(p); + +} + + + + +/* + valloc just invokes memalign with alignment argument equal + to the page size of the system (or as near to this as can + be figured out from all the includes/defines above.) +*/ + +#if __STD_C +Void_t* vALLOc(size_t bytes) +#else +Void_t* vALLOc(bytes) size_t bytes; +#endif +{ + return mEMALIGn (malloc_getpagesize, bytes); +} + +/* + pvalloc just invokes valloc for the nearest pagesize + that will accommodate request +*/ + + +#if __STD_C +Void_t* pvALLOc(size_t bytes) +#else +Void_t* pvALLOc(bytes) size_t bytes; +#endif +{ + size_t pagesize = malloc_getpagesize; + return mEMALIGn (pagesize, (bytes + pagesize - 1) & ~(pagesize - 1)); +} + +/* + + calloc calls malloc, then zeroes out the allocated chunk. + +*/ + +#if __STD_C +Void_t* cALLOc(size_t n, size_t elem_size) +#else +Void_t* cALLOc(n, elem_size) size_t n; size_t elem_size; +#endif +{ + mchunkptr p; + INTERNAL_SIZE_T csz; + + INTERNAL_SIZE_T sz = n * elem_size; + + + /* check if expand_top called, in which case don't need to clear */ +#if MORECORE_CLEARS + mchunkptr oldtop = top; + INTERNAL_SIZE_T oldtopsize = chunksize(top); +#endif + Void_t* mem = mALLOc (sz); + + if ((long)n < 0) return NULL; + + if (mem == NULL) + return NULL; + else + { + p = mem2chunk(mem); + + /* Two optional cases in which clearing not necessary */ + + +#if HAVE_MMAP + if (chunk_is_mmapped(p)) return mem; +#endif + + csz = chunksize(p); + +#if MORECORE_CLEARS + if (p == oldtop && csz > oldtopsize) + { + /* clear only the bytes from non-freshly-sbrked memory */ + csz = oldtopsize; + } +#endif + + MALLOC_ZERO(mem, csz - SIZE_SZ); + return mem; + } +} + +/* + + cfree just calls free. It is needed/defined on some systems + that pair it with calloc, presumably for odd historical reasons. + +*/ + +#if !defined(INTERNAL_LINUX_C_LIB) || !defined(__ELF__) +#if __STD_C +void cfree(Void_t *mem) +#else +void cfree(mem) Void_t *mem; +#endif +{ + fREe(mem); +} +#endif + + + +/* + + Malloc_trim gives memory back to the system (via negative + arguments to sbrk) if there is unused memory at the `high' end of + the malloc pool. You can call this after freeing large blocks of + memory to potentially reduce the system-level memory requirements + of a program. However, it cannot guarantee to reduce memory. Under + some allocation patterns, some large free blocks of memory will be + locked between two used chunks, so they cannot be given back to + the system. + + The `pad' argument to malloc_trim represents the amount of free + trailing space to leave untrimmed. If this argument is zero, + only the minimum amount of memory to maintain internal data + structures will be left (one page or less). Non-zero arguments + can be supplied to maintain enough trailing space to service + future expected allocations without having to re-obtain memory + from the system. + + Malloc_trim returns 1 if it actually released any memory, else 0. + +*/ + +#if __STD_C +int malloc_trim(size_t pad) +#else +int malloc_trim(pad) size_t pad; +#endif +{ + long top_size; /* Amount of top-most memory */ + long extra; /* Amount to release */ + char* current_brk; /* address returned by pre-check sbrk call */ + char* new_brk; /* address returned by negative sbrk call */ + + unsigned long pagesz = malloc_getpagesize; + + top_size = chunksize(top); + extra = ((top_size - pad - MINSIZE + (pagesz-1)) / pagesz - 1) * pagesz; + + if (extra < (long)pagesz) /* Not enough memory to release */ + return 0; + + else + { + /* Test to make sure no one else called sbrk */ + current_brk = (char*)(MORECORE (0)); + if (current_brk != (char*)(top) + top_size) + return 0; /* Apparently we don't own memory; must fail */ + + else + { + new_brk = (char*)(MORECORE (-extra)); + + if (new_brk == (char*)(MORECORE_FAILURE)) /* sbrk failed? */ + { + /* Try to figure out what we have */ + current_brk = (char*)(MORECORE (0)); + top_size = current_brk - (char*)top; + if (top_size >= (long)MINSIZE) /* if not, we are very very dead! */ + { + sbrked_mem = current_brk - sbrk_base; + set_head(top, top_size | PREV_INUSE); + } + check_chunk(top); + return 0; + } + + else + { + /* Success. Adjust top accordingly. */ + set_head(top, (top_size - extra) | PREV_INUSE); + sbrked_mem -= extra; + check_chunk(top); + return 1; + } + } + } +} + + + +/* + malloc_usable_size: + + This routine tells you how many bytes you can actually use in an + allocated chunk, which may be more than you requested (although + often not). You can use this many bytes without worrying about + overwriting other allocated objects. Not a particularly great + programming practice, but still sometimes useful. + +*/ + +#if __STD_C +size_t malloc_usable_size(Void_t* mem) +#else +size_t malloc_usable_size(mem) Void_t* mem; +#endif +{ + mchunkptr p; + if (mem == NULL) + return 0; + else + { + p = mem2chunk(mem); + if(!chunk_is_mmapped(p)) + { + if (!inuse(p)) return 0; + check_inuse_chunk(p); + return chunksize(p) - SIZE_SZ; + } + return chunksize(p) - 2*SIZE_SZ; + } +} + + + + +/* Utility to update current_mallinfo for malloc_stats and mallinfo() */ + +#ifdef DEBUG +static void malloc_update_mallinfo() +{ + int i; + mbinptr b; + mchunkptr p; +#ifdef DEBUG + mchunkptr q; +#endif + + INTERNAL_SIZE_T avail = chunksize(top); + int navail = ((long)(avail) >= (long)MINSIZE)? 1 : 0; + + for (i = 1; i < NAV; ++i) + { + b = bin_at(i); + for (p = last(b); p != b; p = p->bk) + { +#ifdef DEBUG + check_free_chunk(p); + for (q = next_chunk(p); + q < top && inuse(q) && (long)(chunksize(q)) >= (long)MINSIZE; + q = next_chunk(q)) + check_inuse_chunk(q); +#endif + avail += chunksize(p); + navail++; + } + } + + current_mallinfo.ordblks = navail; + current_mallinfo.uordblks = sbrked_mem - avail; + current_mallinfo.fordblks = avail; + current_mallinfo.hblks = n_mmaps; + current_mallinfo.hblkhd = mmapped_mem; + current_mallinfo.keepcost = chunksize(top); + +} +#endif /* DEBUG */ + + + +/* + + malloc_stats: + + Prints on the amount of space obtain from the system (both + via sbrk and mmap), the maximum amount (which may be more than + current if malloc_trim and/or munmap got called), the maximum + number of simultaneous mmap regions used, and the current number + of bytes allocated via malloc (or realloc, etc) but not yet + freed. (Note that this is the number of bytes allocated, not the + number requested. It will be larger than the number requested + because of alignment and bookkeeping overhead.) + +*/ + +#ifdef DEBUG +void malloc_stats() +{ + malloc_update_mallinfo(); + printf("max system bytes = %10u\n", + (unsigned int)(max_total_mem)); + printf("system bytes = %10u\n", + (unsigned int)(sbrked_mem + mmapped_mem)); + printf("in use bytes = %10u\n", + (unsigned int)(current_mallinfo.uordblks + mmapped_mem)); +#if HAVE_MMAP + printf("max mmap regions = %10u\n", + (unsigned int)max_n_mmaps); +#endif +} +#endif /* DEBUG */ + +/* + mallinfo returns a copy of updated current mallinfo. +*/ + +#ifdef DEBUG +struct mallinfo mALLINFo() +{ + malloc_update_mallinfo(); + return current_mallinfo; +} +#endif /* DEBUG */ + + + + +/* + mallopt: + + mallopt is the general SVID/XPG interface to tunable parameters. + The format is to provide a (parameter-number, parameter-value) pair. + mallopt then sets the corresponding parameter to the argument + value if it can (i.e., so long as the value is meaningful), + and returns 1 if successful else 0. + + See descriptions of tunable parameters above. + +*/ + +#if __STD_C +int mALLOPt(int param_number, int value) +#else +int mALLOPt(param_number, value) int param_number; int value; +#endif +{ + switch(param_number) + { + case M_TRIM_THRESHOLD: + trim_threshold = value; return 1; + case M_TOP_PAD: + top_pad = value; return 1; + case M_MMAP_THRESHOLD: + mmap_threshold = value; return 1; + case M_MMAP_MAX: +#if HAVE_MMAP + n_mmaps_max = value; return 1; +#else + if (value != 0) return 0; else n_mmaps_max = value; return 1; +#endif + + default: + return 0; + } +} + +/* + +History: + + V2.6.6 Sun Dec 5 07:42:19 1999 Doug Lea (dl at gee) + * return null for negative arguments + * Added Several WIN32 cleanups from Martin C. Fong <mcfong@yahoo.com> + * Add 'LACKS_SYS_PARAM_H' for those systems without 'sys/param.h' + (e.g. WIN32 platforms) + * Cleanup up header file inclusion for WIN32 platforms + * Cleanup code to avoid Microsoft Visual C++ compiler complaints + * Add 'USE_DL_PREFIX' to quickly allow co-existence with existing + memory allocation routines + * Set 'malloc_getpagesize' for WIN32 platforms (needs more work) + * Use 'assert' rather than 'ASSERT' in WIN32 code to conform to + usage of 'assert' in non-WIN32 code + * Improve WIN32 'sbrk()' emulation's 'findRegion()' routine to + avoid infinite loop + * Always call 'fREe()' rather than 'free()' + + V2.6.5 Wed Jun 17 15:57:31 1998 Doug Lea (dl at gee) + * Fixed ordering problem with boundary-stamping + + V2.6.3 Sun May 19 08:17:58 1996 Doug Lea (dl at gee) + * Added pvalloc, as recommended by H.J. Liu + * Added 64bit pointer support mainly from Wolfram Gloger + * Added anonymously donated WIN32 sbrk emulation + * Malloc, calloc, getpagesize: add optimizations from Raymond Nijssen + * malloc_extend_top: fix mask error that caused wastage after + foreign sbrks + * Add linux mremap support code from HJ Liu + + V2.6.2 Tue Dec 5 06:52:55 1995 Doug Lea (dl at gee) + * Integrated most documentation with the code. + * Add support for mmap, with help from + Wolfram Gloger (Gloger@lrz.uni-muenchen.de). + * Use last_remainder in more cases. + * Pack bins using idea from colin@nyx10.cs.du.edu + * Use ordered bins instead of best-fit threshhold + * Eliminate block-local decls to simplify tracing and debugging. + * Support another case of realloc via move into top + * Fix error occuring when initial sbrk_base not word-aligned. + * Rely on page size for units instead of SBRK_UNIT to + avoid surprises about sbrk alignment conventions. + * Add mallinfo, mallopt. Thanks to Raymond Nijssen + (raymond@es.ele.tue.nl) for the suggestion. + * Add `pad' argument to malloc_trim and top_pad mallopt parameter. + * More precautions for cases where other routines call sbrk, + courtesy of Wolfram Gloger (Gloger@lrz.uni-muenchen.de). + * Added macros etc., allowing use in linux libc from + H.J. Lu (hjl@gnu.ai.mit.edu) + * Inverted this history list + + V2.6.1 Sat Dec 2 14:10:57 1995 Doug Lea (dl at gee) + * Re-tuned and fixed to behave more nicely with V2.6.0 changes. + * Removed all preallocation code since under current scheme + the work required to undo bad preallocations exceeds + the work saved in good cases for most test programs. + * No longer use return list or unconsolidated bins since + no scheme using them consistently outperforms those that don't + given above changes. + * Use best fit for very large chunks to prevent some worst-cases. + * Added some support for debugging + + V2.6.0 Sat Nov 4 07:05:23 1995 Doug Lea (dl at gee) + * Removed footers when chunks are in use. Thanks to + Paul Wilson (wilson@cs.texas.edu) for the suggestion. + + V2.5.4 Wed Nov 1 07:54:51 1995 Doug Lea (dl at gee) + * Added malloc_trim, with help from Wolfram Gloger + (wmglo@Dent.MED.Uni-Muenchen.DE). + + V2.5.3 Tue Apr 26 10:16:01 1994 Doug Lea (dl at g) + + V2.5.2 Tue Apr 5 16:20:40 1994 Doug Lea (dl at g) + * realloc: try to expand in both directions + * malloc: swap order of clean-bin strategy; + * realloc: only conditionally expand backwards + * Try not to scavenge used bins + * Use bin counts as a guide to preallocation + * Occasionally bin return list chunks in first scan + * Add a few optimizations from colin@nyx10.cs.du.edu + + V2.5.1 Sat Aug 14 15:40:43 1993 Doug Lea (dl at g) + * faster bin computation & slightly different binning + * merged all consolidations to one part of malloc proper + (eliminating old malloc_find_space & malloc_clean_bin) + * Scan 2 returns chunks (not just 1) + * Propagate failure in realloc if malloc returns 0 + * Add stuff to allow compilation on non-ANSI compilers + from kpv@research.att.com + + V2.5 Sat Aug 7 07:41:59 1993 Doug Lea (dl at g.oswego.edu) + * removed potential for odd address access in prev_chunk + * removed dependency on getpagesize.h + * misc cosmetics and a bit more internal documentation + * anticosmetics: mangled names in macros to evade debugger strangeness + * tested on sparc, hp-700, dec-mips, rs6000 + with gcc & native cc (hp, dec only) allowing + Detlefs & Zorn comparison study (in SIGPLAN Notices.) + + Trial version Fri Aug 28 13:14:29 1992 Doug Lea (dl at g.oswego.edu) + * Based loosely on libg++-1.2X malloc. (It retains some of the overall + structure of old version, but most details differ.) + +*/ diff --git a/qemu/roms/u-boot/common/dlmalloc.src b/qemu/roms/u-boot/common/dlmalloc.src new file mode 100644 index 000000000..d86acffde --- /dev/null +++ b/qemu/roms/u-boot/common/dlmalloc.src @@ -0,0 +1,3265 @@ +/* ---------- To make a malloc.h, start cutting here ------------ */ + +/* + A version of malloc/free/realloc written by Doug Lea and released to the + public domain. Send questions/comments/complaints/performance data + to dl@cs.oswego.edu + +* VERSION 2.6.6 Sun Mar 5 19:10:03 2000 Doug Lea (dl at gee) + + Note: There may be an updated version of this malloc obtainable at + ftp://g.oswego.edu/pub/misc/malloc.c + Check before installing! + +* Why use this malloc? + + This is not the fastest, most space-conserving, most portable, or + most tunable malloc ever written. However it is among the fastest + while also being among the most space-conserving, portable and tunable. + Consistent balance across these factors results in a good general-purpose + allocator. For a high-level description, see + http://g.oswego.edu/dl/html/malloc.html + +* Synopsis of public routines + + (Much fuller descriptions are contained in the program documentation below.) + + malloc(size_t n); + Return a pointer to a newly allocated chunk of at least n bytes, or null + if no space is available. + free(Void_t* p); + Release the chunk of memory pointed to by p, or no effect if p is null. + realloc(Void_t* p, size_t n); + Return a pointer to a chunk of size n that contains the same data + as does chunk p up to the minimum of (n, p's size) bytes, or null + if no space is available. The returned pointer may or may not be + the same as p. If p is null, equivalent to malloc. Unless the + #define REALLOC_ZERO_BYTES_FREES below is set, realloc with a + size argument of zero (re)allocates a minimum-sized chunk. + memalign(size_t alignment, size_t n); + Return a pointer to a newly allocated chunk of n bytes, aligned + in accord with the alignment argument, which must be a power of + two. + valloc(size_t n); + Equivalent to memalign(pagesize, n), where pagesize is the page + size of the system (or as near to this as can be figured out from + all the includes/defines below.) + pvalloc(size_t n); + Equivalent to valloc(minimum-page-that-holds(n)), that is, + round up n to nearest pagesize. + calloc(size_t unit, size_t quantity); + Returns a pointer to quantity * unit bytes, with all locations + set to zero. + cfree(Void_t* p); + Equivalent to free(p). + malloc_trim(size_t pad); + Release all but pad bytes of freed top-most memory back + to the system. Return 1 if successful, else 0. + malloc_usable_size(Void_t* p); + Report the number usable allocated bytes associated with allocated + chunk p. This may or may not report more bytes than were requested, + due to alignment and minimum size constraints. + malloc_stats(); + Prints brief summary statistics on stderr. + mallinfo() + Returns (by copy) a struct containing various summary statistics. + mallopt(int parameter_number, int parameter_value) + Changes one of the tunable parameters described below. Returns + 1 if successful in changing the parameter, else 0. + +* Vital statistics: + + Alignment: 8-byte + 8 byte alignment is currently hardwired into the design. This + seems to suffice for all current machines and C compilers. + + Assumed pointer representation: 4 or 8 bytes + Code for 8-byte pointers is untested by me but has worked + reliably by Wolfram Gloger, who contributed most of the + changes supporting this. + + Assumed size_t representation: 4 or 8 bytes + Note that size_t is allowed to be 4 bytes even if pointers are 8. + + Minimum overhead per allocated chunk: 4 or 8 bytes + Each malloced chunk has a hidden overhead of 4 bytes holding size + and status information. + + Minimum allocated size: 4-byte ptrs: 16 bytes (including 4 overhead) + 8-byte ptrs: 24/32 bytes (including, 4/8 overhead) + + When a chunk is freed, 12 (for 4byte ptrs) or 20 (for 8 byte + ptrs but 4 byte size) or 24 (for 8/8) additional bytes are + needed; 4 (8) for a trailing size field + and 8 (16) bytes for free list pointers. Thus, the minimum + allocatable size is 16/24/32 bytes. + + Even a request for zero bytes (i.e., malloc(0)) returns a + pointer to something of the minimum allocatable size. + + Maximum allocated size: 4-byte size_t: 2^31 - 8 bytes + 8-byte size_t: 2^63 - 16 bytes + + It is assumed that (possibly signed) size_t bit values suffice to + represent chunk sizes. `Possibly signed' is due to the fact + that `size_t' may be defined on a system as either a signed or + an unsigned type. To be conservative, values that would appear + as negative numbers are avoided. + Requests for sizes with a negative sign bit when the request + size is treaded as a long will return null. + + Maximum overhead wastage per allocated chunk: normally 15 bytes + + Alignnment demands, plus the minimum allocatable size restriction + make the normal worst-case wastage 15 bytes (i.e., up to 15 + more bytes will be allocated than were requested in malloc), with + two exceptions: + 1. Because requests for zero bytes allocate non-zero space, + the worst case wastage for a request of zero bytes is 24 bytes. + 2. For requests >= mmap_threshold that are serviced via + mmap(), the worst case wastage is 8 bytes plus the remainder + from a system page (the minimal mmap unit); typically 4096 bytes. + +* Limitations + + Here are some features that are NOT currently supported + + * No user-definable hooks for callbacks and the like. + * No automated mechanism for fully checking that all accesses + to malloced memory stay within their bounds. + * No support for compaction. + +* Synopsis of compile-time options: + + People have reported using previous versions of this malloc on all + versions of Unix, sometimes by tweaking some of the defines + below. It has been tested most extensively on Solaris and + Linux. It is also reported to work on WIN32 platforms. + People have also reported adapting this malloc for use in + stand-alone embedded systems. + + The implementation is in straight, hand-tuned ANSI C. Among other + consequences, it uses a lot of macros. Because of this, to be at + all usable, this code should be compiled using an optimizing compiler + (for example gcc -O2) that can simplify expressions and control + paths. + + __STD_C (default: derived from C compiler defines) + Nonzero if using ANSI-standard C compiler, a C++ compiler, or + a C compiler sufficiently close to ANSI to get away with it. + DEBUG (default: NOT defined) + Define to enable debugging. Adds fairly extensive assertion-based + checking to help track down memory errors, but noticeably slows down + execution. + REALLOC_ZERO_BYTES_FREES (default: NOT defined) + Define this if you think that realloc(p, 0) should be equivalent + to free(p). Otherwise, since malloc returns a unique pointer for + malloc(0), so does realloc(p, 0). + HAVE_MEMCPY (default: defined) + Define if you are not otherwise using ANSI STD C, but still + have memcpy and memset in your C library and want to use them. + Otherwise, simple internal versions are supplied. + USE_MEMCPY (default: 1 if HAVE_MEMCPY is defined, 0 otherwise) + Define as 1 if you want the C library versions of memset and + memcpy called in realloc and calloc (otherwise macro versions are used). + At least on some platforms, the simple macro versions usually + outperform libc versions. + HAVE_MMAP (default: defined as 1) + Define to non-zero to optionally make malloc() use mmap() to + allocate very large blocks. + HAVE_MREMAP (default: defined as 0 unless Linux libc set) + Define to non-zero to optionally make realloc() use mremap() to + reallocate very large blocks. + malloc_getpagesize (default: derived from system #includes) + Either a constant or routine call returning the system page size. + HAVE_USR_INCLUDE_MALLOC_H (default: NOT defined) + Optionally define if you are on a system with a /usr/include/malloc.h + that declares struct mallinfo. It is not at all necessary to + define this even if you do, but will ensure consistency. + INTERNAL_SIZE_T (default: size_t) + Define to a 32-bit type (probably `unsigned int') if you are on a + 64-bit machine, yet do not want or need to allow malloc requests of + greater than 2^31 to be handled. This saves space, especially for + very small chunks. + INTERNAL_LINUX_C_LIB (default: NOT defined) + Defined only when compiled as part of Linux libc. + Also note that there is some odd internal name-mangling via defines + (for example, internally, `malloc' is named `mALLOc') needed + when compiling in this case. These look funny but don't otherwise + affect anything. + WIN32 (default: undefined) + Define this on MS win (95, nt) platforms to compile in sbrk emulation. + LACKS_UNISTD_H (default: undefined if not WIN32) + Define this if your system does not have a <unistd.h>. + LACKS_SYS_PARAM_H (default: undefined if not WIN32) + Define this if your system does not have a <sys/param.h>. + MORECORE (default: sbrk) + The name of the routine to call to obtain more memory from the system. + MORECORE_FAILURE (default: -1) + The value returned upon failure of MORECORE. + MORECORE_CLEARS (default 1) + true (1) if the routine mapped to MORECORE zeroes out memory (which + holds for sbrk). + DEFAULT_TRIM_THRESHOLD + DEFAULT_TOP_PAD + DEFAULT_MMAP_THRESHOLD + DEFAULT_MMAP_MAX + Default values of tunable parameters (described in detail below) + controlling interaction with host system routines (sbrk, mmap, etc). + These values may also be changed dynamically via mallopt(). The + preset defaults are those that give best performance for typical + programs/systems. + USE_DL_PREFIX (default: undefined) + Prefix all public routines with the string 'dl'. Useful to + quickly avoid procedure declaration conflicts and linker symbol + conflicts with existing memory allocation routines. + + +*/ + + + + +/* Preliminaries */ + +#ifndef __STD_C +#ifdef __STDC__ +#define __STD_C 1 +#else +#if __cplusplus +#define __STD_C 1 +#else +#define __STD_C 0 +#endif /*__cplusplus*/ +#endif /*__STDC__*/ +#endif /*__STD_C*/ + +#ifndef Void_t +#if (__STD_C || defined(WIN32)) +#define Void_t void +#else +#define Void_t char +#endif +#endif /*Void_t*/ + +#if __STD_C +#include <stddef.h> /* for size_t */ +#else +#include <sys/types.h> +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#include <stdio.h> /* needed for malloc_stats */ + + +/* + Compile-time options +*/ + + +/* + Debugging: + + Because freed chunks may be overwritten with link fields, this + malloc will often die when freed memory is overwritten by user + programs. This can be very effective (albeit in an annoying way) + in helping track down dangling pointers. + + If you compile with -DDEBUG, a number of assertion checks are + enabled that will catch more memory errors. You probably won't be + able to make much sense of the actual assertion errors, but they + should help you locate incorrectly overwritten memory. The + checking is fairly extensive, and will slow down execution + noticeably. Calling malloc_stats or mallinfo with DEBUG set will + attempt to check every non-mmapped allocated and free chunk in the + course of computing the summmaries. (By nature, mmapped regions + cannot be checked very much automatically.) + + Setting DEBUG may also be helpful if you are trying to modify + this code. The assertions in the check routines spell out in more + detail the assumptions and invariants underlying the algorithms. + +*/ + +#if DEBUG +#include <assert.h> +#else +#define assert(x) ((void)0) +#endif + + +/* + INTERNAL_SIZE_T is the word-size used for internal bookkeeping + of chunk sizes. On a 64-bit machine, you can reduce malloc + overhead by defining INTERNAL_SIZE_T to be a 32 bit `unsigned int' + at the expense of not being able to handle requests greater than + 2^31. This limitation is hardly ever a concern; you are encouraged + to set this. However, the default version is the same as size_t. +*/ + +#ifndef INTERNAL_SIZE_T +#define INTERNAL_SIZE_T size_t +#endif + +/* + REALLOC_ZERO_BYTES_FREES should be set if a call to + realloc with zero bytes should be the same as a call to free. + Some people think it should. Otherwise, since this malloc + returns a unique pointer for malloc(0), so does realloc(p, 0). +*/ + + +/* #define REALLOC_ZERO_BYTES_FREES */ + + +/* + WIN32 causes an emulation of sbrk to be compiled in + mmap-based options are not currently supported in WIN32. +*/ + +/* #define WIN32 */ +#ifdef WIN32 +#define MORECORE wsbrk +#define HAVE_MMAP 0 + +#define LACKS_UNISTD_H +#define LACKS_SYS_PARAM_H + +/* + Include 'windows.h' to get the necessary declarations for the + Microsoft Visual C++ data structures and routines used in the 'sbrk' + emulation. + + Define WIN32_LEAN_AND_MEAN so that only the essential Microsoft + Visual C++ header files are included. +*/ +#define WIN32_LEAN_AND_MEAN +#include <windows.h> +#endif + + +/* + HAVE_MEMCPY should be defined if you are not otherwise using + ANSI STD C, but still have memcpy and memset in your C library + and want to use them in calloc and realloc. Otherwise simple + macro versions are defined here. + + USE_MEMCPY should be defined as 1 if you actually want to + have memset and memcpy called. People report that the macro + versions are often enough faster than libc versions on many + systems that it is better to use them. + +*/ + +#define HAVE_MEMCPY + +#ifndef USE_MEMCPY +#ifdef HAVE_MEMCPY +#define USE_MEMCPY 1 +#else +#define USE_MEMCPY 0 +#endif +#endif + +#if (__STD_C || defined(HAVE_MEMCPY)) + +#if __STD_C +void* memset(void*, int, size_t); +void* memcpy(void*, const void*, size_t); +#else +#ifdef WIN32 +/* On Win32 platforms, 'memset()' and 'memcpy()' are already declared in */ +/* 'windows.h' */ +#else +Void_t* memset(); +Void_t* memcpy(); +#endif +#endif +#endif + +#if USE_MEMCPY + +/* The following macros are only invoked with (2n+1)-multiples of + INTERNAL_SIZE_T units, with a positive integer n. This is exploited + for fast inline execution when n is small. */ + +#define MALLOC_ZERO(charp, nbytes) \ +do { \ + INTERNAL_SIZE_T mzsz = (nbytes); \ + if(mzsz <= 9*sizeof(mzsz)) { \ + INTERNAL_SIZE_T* mz = (INTERNAL_SIZE_T*) (charp); \ + if(mzsz >= 5*sizeof(mzsz)) { *mz++ = 0; \ + *mz++ = 0; \ + if(mzsz >= 7*sizeof(mzsz)) { *mz++ = 0; \ + *mz++ = 0; \ + if(mzsz >= 9*sizeof(mzsz)) { *mz++ = 0; \ + *mz++ = 0; }}} \ + *mz++ = 0; \ + *mz++ = 0; \ + *mz = 0; \ + } else memset((charp), 0, mzsz); \ +} while(0) + +#define MALLOC_COPY(dest,src,nbytes) \ +do { \ + INTERNAL_SIZE_T mcsz = (nbytes); \ + if(mcsz <= 9*sizeof(mcsz)) { \ + INTERNAL_SIZE_T* mcsrc = (INTERNAL_SIZE_T*) (src); \ + INTERNAL_SIZE_T* mcdst = (INTERNAL_SIZE_T*) (dest); \ + if(mcsz >= 5*sizeof(mcsz)) { *mcdst++ = *mcsrc++; \ + *mcdst++ = *mcsrc++; \ + if(mcsz >= 7*sizeof(mcsz)) { *mcdst++ = *mcsrc++; \ + *mcdst++ = *mcsrc++; \ + if(mcsz >= 9*sizeof(mcsz)) { *mcdst++ = *mcsrc++; \ + *mcdst++ = *mcsrc++; }}} \ + *mcdst++ = *mcsrc++; \ + *mcdst++ = *mcsrc++; \ + *mcdst = *mcsrc ; \ + } else memcpy(dest, src, mcsz); \ +} while(0) + +#else /* !USE_MEMCPY */ + +/* Use Duff's device for good zeroing/copying performance. */ + +#define MALLOC_ZERO(charp, nbytes) \ +do { \ + INTERNAL_SIZE_T* mzp = (INTERNAL_SIZE_T*)(charp); \ + long mctmp = (nbytes)/sizeof(INTERNAL_SIZE_T), mcn; \ + if (mctmp < 8) mcn = 0; else { mcn = (mctmp-1)/8; mctmp %= 8; } \ + switch (mctmp) { \ + case 0: for(;;) { *mzp++ = 0; \ + case 7: *mzp++ = 0; \ + case 6: *mzp++ = 0; \ + case 5: *mzp++ = 0; \ + case 4: *mzp++ = 0; \ + case 3: *mzp++ = 0; \ + case 2: *mzp++ = 0; \ + case 1: *mzp++ = 0; if(mcn <= 0) break; mcn--; } \ + } \ +} while(0) + +#define MALLOC_COPY(dest,src,nbytes) \ +do { \ + INTERNAL_SIZE_T* mcsrc = (INTERNAL_SIZE_T*) src; \ + INTERNAL_SIZE_T* mcdst = (INTERNAL_SIZE_T*) dest; \ + long mctmp = (nbytes)/sizeof(INTERNAL_SIZE_T), mcn; \ + if (mctmp < 8) mcn = 0; else { mcn = (mctmp-1)/8; mctmp %= 8; } \ + switch (mctmp) { \ + case 0: for(;;) { *mcdst++ = *mcsrc++; \ + case 7: *mcdst++ = *mcsrc++; \ + case 6: *mcdst++ = *mcsrc++; \ + case 5: *mcdst++ = *mcsrc++; \ + case 4: *mcdst++ = *mcsrc++; \ + case 3: *mcdst++ = *mcsrc++; \ + case 2: *mcdst++ = *mcsrc++; \ + case 1: *mcdst++ = *mcsrc++; if(mcn <= 0) break; mcn--; } \ + } \ +} while(0) + +#endif + + +/* + Define HAVE_MMAP to optionally make malloc() use mmap() to + allocate very large blocks. These will be returned to the + operating system immediately after a free(). +*/ + +#ifndef HAVE_MMAP +#define HAVE_MMAP 1 +#endif + +/* + Define HAVE_MREMAP to make realloc() use mremap() to re-allocate + large blocks. This is currently only possible on Linux with + kernel versions newer than 1.3.77. +*/ + +#ifndef HAVE_MREMAP +#ifdef INTERNAL_LINUX_C_LIB +#define HAVE_MREMAP 1 +#else +#define HAVE_MREMAP 0 +#endif +#endif + +#if HAVE_MMAP + +#include <unistd.h> +#include <fcntl.h> +#include <sys/mman.h> + +#if !defined(MAP_ANONYMOUS) && defined(MAP_ANON) +#define MAP_ANONYMOUS MAP_ANON +#endif + +#endif /* HAVE_MMAP */ + +/* + Access to system page size. To the extent possible, this malloc + manages memory from the system in page-size units. + + The following mechanics for getpagesize were adapted from + bsd/gnu getpagesize.h +*/ + +#ifndef LACKS_UNISTD_H +# include <unistd.h> +#endif + +#ifndef malloc_getpagesize +# ifdef _SC_PAGESIZE /* some SVR4 systems omit an underscore */ +# ifndef _SC_PAGE_SIZE +# define _SC_PAGE_SIZE _SC_PAGESIZE +# endif +# endif +# ifdef _SC_PAGE_SIZE +# define malloc_getpagesize sysconf(_SC_PAGE_SIZE) +# else +# if defined(BSD) || defined(DGUX) || defined(HAVE_GETPAGESIZE) + extern size_t getpagesize(); +# define malloc_getpagesize getpagesize() +# else +# ifdef WIN32 +# define malloc_getpagesize (4096) /* TBD: Use 'GetSystemInfo' instead */ +# else +# ifndef LACKS_SYS_PARAM_H +# include <sys/param.h> +# endif +# ifdef EXEC_PAGESIZE +# define malloc_getpagesize EXEC_PAGESIZE +# else +# ifdef NBPG +# ifndef CLSIZE +# define malloc_getpagesize NBPG +# else +# define malloc_getpagesize (NBPG * CLSIZE) +# endif +# else +# ifdef NBPC +# define malloc_getpagesize NBPC +# else +# ifdef PAGESIZE +# define malloc_getpagesize PAGESIZE +# else +# define malloc_getpagesize (4096) /* just guess */ +# endif +# endif +# endif +# endif +# endif +# endif +# endif +#endif + + +/* + + This version of malloc supports the standard SVID/XPG mallinfo + routine that returns a struct containing the same kind of + information you can get from malloc_stats. It should work on + any SVID/XPG compliant system that has a /usr/include/malloc.h + defining struct mallinfo. (If you'd like to install such a thing + yourself, cut out the preliminary declarations as described above + and below and save them in a malloc.h file. But there's no + compelling reason to bother to do this.) + + The main declaration needed is the mallinfo struct that is returned + (by-copy) by mallinfo(). The SVID/XPG malloinfo struct contains a + bunch of fields, most of which are not even meaningful in this + version of malloc. Some of these fields are are instead filled by + mallinfo() with other numbers that might possibly be of interest. + + HAVE_USR_INCLUDE_MALLOC_H should be set if you have a + /usr/include/malloc.h file that includes a declaration of struct + mallinfo. If so, it is included; else an SVID2/XPG2 compliant + version is declared below. These must be precisely the same for + mallinfo() to work. + +*/ + +/* #define HAVE_USR_INCLUDE_MALLOC_H */ + +#if HAVE_USR_INCLUDE_MALLOC_H +#include "/usr/include/malloc.h" +#else + +/* SVID2/XPG mallinfo structure */ + +struct mallinfo { + int arena; /* total space allocated from system */ + int ordblks; /* number of non-inuse chunks */ + int smblks; /* unused -- always zero */ + int hblks; /* number of mmapped regions */ + int hblkhd; /* total space in mmapped regions */ + int usmblks; /* unused -- always zero */ + int fsmblks; /* unused -- always zero */ + int uordblks; /* total allocated space */ + int fordblks; /* total non-inuse space */ + int keepcost; /* top-most, releasable (via malloc_trim) space */ +}; + +/* SVID2/XPG mallopt options */ + +#define M_MXFAST 1 /* UNUSED in this malloc */ +#define M_NLBLKS 2 /* UNUSED in this malloc */ +#define M_GRAIN 3 /* UNUSED in this malloc */ +#define M_KEEP 4 /* UNUSED in this malloc */ + +#endif + +/* mallopt options that actually do something */ + +#define M_TRIM_THRESHOLD -1 +#define M_TOP_PAD -2 +#define M_MMAP_THRESHOLD -3 +#define M_MMAP_MAX -4 + + +#ifndef DEFAULT_TRIM_THRESHOLD +#define DEFAULT_TRIM_THRESHOLD (128 * 1024) +#endif + +/* + M_TRIM_THRESHOLD is the maximum amount of unused top-most memory + to keep before releasing via malloc_trim in free(). + + Automatic trimming is mainly useful in long-lived programs. + Because trimming via sbrk can be slow on some systems, and can + sometimes be wasteful (in cases where programs immediately + afterward allocate more large chunks) the value should be high + enough so that your overall system performance would improve by + releasing. + + The trim threshold and the mmap control parameters (see below) + can be traded off with one another. Trimming and mmapping are + two different ways of releasing unused memory back to the + system. Between these two, it is often possible to keep + system-level demands of a long-lived program down to a bare + minimum. For example, in one test suite of sessions measuring + the XF86 X server on Linux, using a trim threshold of 128K and a + mmap threshold of 192K led to near-minimal long term resource + consumption. + + If you are using this malloc in a long-lived program, it should + pay to experiment with these values. As a rough guide, you + might set to a value close to the average size of a process + (program) running on your system. Releasing this much memory + would allow such a process to run in memory. Generally, it's + worth it to tune for trimming rather tham memory mapping when a + program undergoes phases where several large chunks are + allocated and released in ways that can reuse each other's + storage, perhaps mixed with phases where there are no such + chunks at all. And in well-behaved long-lived programs, + controlling release of large blocks via trimming versus mapping + is usually faster. + + However, in most programs, these parameters serve mainly as + protection against the system-level effects of carrying around + massive amounts of unneeded memory. Since frequent calls to + sbrk, mmap, and munmap otherwise degrade performance, the default + parameters are set to relatively high values that serve only as + safeguards. + + The default trim value is high enough to cause trimming only in + fairly extreme (by current memory consumption standards) cases. + It must be greater than page size to have any useful effect. To + disable trimming completely, you can set to (unsigned long)(-1); + + +*/ + + +#ifndef DEFAULT_TOP_PAD +#define DEFAULT_TOP_PAD (0) +#endif + +/* + M_TOP_PAD is the amount of extra `padding' space to allocate or + retain whenever sbrk is called. It is used in two ways internally: + + * When sbrk is called to extend the top of the arena to satisfy + a new malloc request, this much padding is added to the sbrk + request. + + * When malloc_trim is called automatically from free(), + it is used as the `pad' argument. + + In both cases, the actual amount of padding is rounded + so that the end of the arena is always a system page boundary. + + The main reason for using padding is to avoid calling sbrk so + often. Having even a small pad greatly reduces the likelihood + that nearly every malloc request during program start-up (or + after trimming) will invoke sbrk, which needlessly wastes + time. + + Automatic rounding-up to page-size units is normally sufficient + to avoid measurable overhead, so the default is 0. However, in + systems where sbrk is relatively slow, it can pay to increase + this value, at the expense of carrying around more memory than + the program needs. + +*/ + + +#ifndef DEFAULT_MMAP_THRESHOLD +#define DEFAULT_MMAP_THRESHOLD (128 * 1024) +#endif + +/* + + M_MMAP_THRESHOLD is the request size threshold for using mmap() + to service a request. Requests of at least this size that cannot + be allocated using already-existing space will be serviced via mmap. + (If enough normal freed space already exists it is used instead.) + + Using mmap segregates relatively large chunks of memory so that + they can be individually obtained and released from the host + system. A request serviced through mmap is never reused by any + other request (at least not directly; the system may just so + happen to remap successive requests to the same locations). + + Segregating space in this way has the benefit that mmapped space + can ALWAYS be individually released back to the system, which + helps keep the system level memory demands of a long-lived + program low. Mapped memory can never become `locked' between + other chunks, as can happen with normally allocated chunks, which + menas that even trimming via malloc_trim would not release them. + + However, it has the disadvantages that: + + 1. The space cannot be reclaimed, consolidated, and then + used to service later requests, as happens with normal chunks. + 2. It can lead to more wastage because of mmap page alignment + requirements + 3. It causes malloc performance to be more dependent on host + system memory management support routines which may vary in + implementation quality and may impose arbitrary + limitations. Generally, servicing a request via normal + malloc steps is faster than going through a system's mmap. + + All together, these considerations should lead you to use mmap + only for relatively large requests. + + +*/ + + +#ifndef DEFAULT_MMAP_MAX +#if HAVE_MMAP +#define DEFAULT_MMAP_MAX (64) +#else +#define DEFAULT_MMAP_MAX (0) +#endif +#endif + +/* + M_MMAP_MAX is the maximum number of requests to simultaneously + service using mmap. This parameter exists because: + + 1. Some systems have a limited number of internal tables for + use by mmap. + 2. In most systems, overreliance on mmap can degrade overall + performance. + 3. If a program allocates many large regions, it is probably + better off using normal sbrk-based allocation routines that + can reclaim and reallocate normal heap memory. Using a + small value allows transition into this mode after the + first few allocations. + + Setting to 0 disables all use of mmap. If HAVE_MMAP is not set, + the default value is 0, and attempts to set it to non-zero values + in mallopt will fail. +*/ + + +/* + USE_DL_PREFIX will prefix all public routines with the string 'dl'. + Useful to quickly avoid procedure declaration conflicts and linker + symbol conflicts with existing memory allocation routines. + +*/ + +/* #define USE_DL_PREFIX */ + + +/* + + Special defines for linux libc + + Except when compiled using these special defines for Linux libc + using weak aliases, this malloc is NOT designed to work in + multithreaded applications. No semaphores or other concurrency + control are provided to ensure that multiple malloc or free calls + don't run at the same time, which could be disasterous. A single + semaphore could be used across malloc, realloc, and free (which is + essentially the effect of the linux weak alias approach). It would + be hard to obtain finer granularity. + +*/ + + +#ifdef INTERNAL_LINUX_C_LIB + +#if __STD_C + +Void_t * __default_morecore_init (ptrdiff_t); +Void_t *(*__morecore)(ptrdiff_t) = __default_morecore_init; + +#else + +Void_t * __default_morecore_init (); +Void_t *(*__morecore)() = __default_morecore_init; + +#endif + +#define MORECORE (*__morecore) +#define MORECORE_FAILURE 0 +#define MORECORE_CLEARS 1 + +#else /* INTERNAL_LINUX_C_LIB */ + +#if __STD_C +extern Void_t* sbrk(ptrdiff_t); +#else +extern Void_t* sbrk(); +#endif + +#ifndef MORECORE +#define MORECORE sbrk +#endif + +#ifndef MORECORE_FAILURE +#define MORECORE_FAILURE -1 +#endif + +#ifndef MORECORE_CLEARS +#define MORECORE_CLEARS 1 +#endif + +#endif /* INTERNAL_LINUX_C_LIB */ + +#if defined(INTERNAL_LINUX_C_LIB) && defined(__ELF__) + +#define cALLOc __libc_calloc +#define fREe __libc_free +#define mALLOc __libc_malloc +#define mEMALIGn __libc_memalign +#define rEALLOc __libc_realloc +#define vALLOc __libc_valloc +#define pvALLOc __libc_pvalloc +#define mALLINFo __libc_mallinfo +#define mALLOPt __libc_mallopt + +#pragma weak calloc = __libc_calloc +#pragma weak free = __libc_free +#pragma weak cfree = __libc_free +#pragma weak malloc = __libc_malloc +#pragma weak memalign = __libc_memalign +#pragma weak realloc = __libc_realloc +#pragma weak valloc = __libc_valloc +#pragma weak pvalloc = __libc_pvalloc +#pragma weak mallinfo = __libc_mallinfo +#pragma weak mallopt = __libc_mallopt + +#else + +#ifdef USE_DL_PREFIX +#define cALLOc dlcalloc +#define fREe dlfree +#define mALLOc dlmalloc +#define mEMALIGn dlmemalign +#define rEALLOc dlrealloc +#define vALLOc dlvalloc +#define pvALLOc dlpvalloc +#define mALLINFo dlmallinfo +#define mALLOPt dlmallopt +#else /* USE_DL_PREFIX */ +#define cALLOc calloc +#define fREe free +#define mALLOc malloc +#define mEMALIGn memalign +#define rEALLOc realloc +#define vALLOc valloc +#define pvALLOc pvalloc +#define mALLINFo mallinfo +#define mALLOPt mallopt +#endif /* USE_DL_PREFIX */ + +#endif + +/* Public routines */ + +#if __STD_C + +Void_t* mALLOc(size_t); +void fREe(Void_t*); +Void_t* rEALLOc(Void_t*, size_t); +Void_t* mEMALIGn(size_t, size_t); +Void_t* vALLOc(size_t); +Void_t* pvALLOc(size_t); +Void_t* cALLOc(size_t, size_t); +void cfree(Void_t*); +int malloc_trim(size_t); +size_t malloc_usable_size(Void_t*); +void malloc_stats(); +int mALLOPt(int, int); +struct mallinfo mALLINFo(void); +#else +Void_t* mALLOc(); +void fREe(); +Void_t* rEALLOc(); +Void_t* mEMALIGn(); +Void_t* vALLOc(); +Void_t* pvALLOc(); +Void_t* cALLOc(); +void cfree(); +int malloc_trim(); +size_t malloc_usable_size(); +void malloc_stats(); +int mALLOPt(); +struct mallinfo mALLINFo(); +#endif + + +#ifdef __cplusplus +}; /* end of extern "C" */ +#endif + +/* ---------- To make a malloc.h, end cutting here ------------ */ + + +/* + Emulation of sbrk for WIN32 + All code within the ifdef WIN32 is untested by me. + + Thanks to Martin Fong and others for supplying this. +*/ + + +#ifdef WIN32 + +#define AlignPage(add) (((add) + (malloc_getpagesize-1)) & \ +~(malloc_getpagesize-1)) +#define AlignPage64K(add) (((add) + (0x10000 - 1)) & ~(0x10000 - 1)) + +/* resrve 64MB to insure large contiguous space */ +#define RESERVED_SIZE (1024*1024*64) +#define NEXT_SIZE (2048*1024) +#define TOP_MEMORY ((unsigned long)2*1024*1024*1024) + +struct GmListElement; +typedef struct GmListElement GmListElement; + +struct GmListElement +{ + GmListElement* next; + void* base; +}; + +static GmListElement* head = 0; +static unsigned int gNextAddress = 0; +static unsigned int gAddressBase = 0; +static unsigned int gAllocatedSize = 0; + +static +GmListElement* makeGmListElement (void* bas) +{ + GmListElement* this; + this = (GmListElement*)(void*)LocalAlloc (0, sizeof (GmListElement)); + assert (this); + if (this) + { + this->base = bas; + this->next = head; + head = this; + } + return this; +} + +void gcleanup () +{ + BOOL rval; + assert ( (head == NULL) || (head->base == (void*)gAddressBase)); + if (gAddressBase && (gNextAddress - gAddressBase)) + { + rval = VirtualFree ((void*)gAddressBase, + gNextAddress - gAddressBase, + MEM_DECOMMIT); + assert (rval); + } + while (head) + { + GmListElement* next = head->next; + rval = VirtualFree (head->base, 0, MEM_RELEASE); + assert (rval); + LocalFree (head); + head = next; + } +} + +static +void* findRegion (void* start_address, unsigned long size) +{ + MEMORY_BASIC_INFORMATION info; + if (size >= TOP_MEMORY) return NULL; + + while ((unsigned long)start_address + size < TOP_MEMORY) + { + VirtualQuery (start_address, &info, sizeof (info)); + if ((info.State == MEM_FREE) && (info.RegionSize >= size)) + return start_address; + else + { + /* Requested region is not available so see if the */ + /* next region is available. Set 'start_address' */ + /* to the next region and call 'VirtualQuery()' */ + /* again. */ + + start_address = (char*)info.BaseAddress + info.RegionSize; + + /* Make sure we start looking for the next region */ + /* on the *next* 64K boundary. Otherwise, even if */ + /* the new region is free according to */ + /* 'VirtualQuery()', the subsequent call to */ + /* 'VirtualAlloc()' (which follows the call to */ + /* this routine in 'wsbrk()') will round *down* */ + /* the requested address to a 64K boundary which */ + /* we already know is an address in the */ + /* unavailable region. Thus, the subsequent call */ + /* to 'VirtualAlloc()' will fail and bring us back */ + /* here, causing us to go into an infinite loop. */ + + start_address = + (void *) AlignPage64K((unsigned long) start_address); + } + } + return NULL; + +} + + +void* wsbrk (long size) +{ + void* tmp; + if (size > 0) + { + if (gAddressBase == 0) + { + gAllocatedSize = max (RESERVED_SIZE, AlignPage (size)); + gNextAddress = gAddressBase = + (unsigned int)VirtualAlloc (NULL, gAllocatedSize, + MEM_RESERVE, PAGE_NOACCESS); + } else if (AlignPage (gNextAddress + size) > (gAddressBase + +gAllocatedSize)) + { + long new_size = max (NEXT_SIZE, AlignPage (size)); + void* new_address = (void*)(gAddressBase+gAllocatedSize); + do + { + new_address = findRegion (new_address, new_size); + + if (new_address == 0) + return (void*)-1; + + gAddressBase = gNextAddress = + (unsigned int)VirtualAlloc (new_address, new_size, + MEM_RESERVE, PAGE_NOACCESS); + /* repeat in case of race condition */ + /* The region that we found has been snagged */ + /* by another thread */ + } + while (gAddressBase == 0); + + assert (new_address == (void*)gAddressBase); + + gAllocatedSize = new_size; + + if (!makeGmListElement ((void*)gAddressBase)) + return (void*)-1; + } + if ((size + gNextAddress) > AlignPage (gNextAddress)) + { + void* res; + res = VirtualAlloc ((void*)AlignPage (gNextAddress), + (size + gNextAddress - + AlignPage (gNextAddress)), + MEM_COMMIT, PAGE_READWRITE); + if (res == 0) + return (void*)-1; + } + tmp = (void*)gNextAddress; + gNextAddress = (unsigned int)tmp + size; + return tmp; + } + else if (size < 0) + { + unsigned int alignedGoal = AlignPage (gNextAddress + size); + /* Trim by releasing the virtual memory */ + if (alignedGoal >= gAddressBase) + { + VirtualFree ((void*)alignedGoal, gNextAddress - alignedGoal, + MEM_DECOMMIT); + gNextAddress = gNextAddress + size; + return (void*)gNextAddress; + } + else + { + VirtualFree ((void*)gAddressBase, gNextAddress - gAddressBase, + MEM_DECOMMIT); + gNextAddress = gAddressBase; + return (void*)-1; + } + } + else + { + return (void*)gNextAddress; + } +} + +#endif + + + +/* + Type declarations +*/ + + +struct malloc_chunk +{ + INTERNAL_SIZE_T prev_size; /* Size of previous chunk (if free). */ + INTERNAL_SIZE_T size; /* Size in bytes, including overhead. */ + struct malloc_chunk* fd; /* double links -- used only if free. */ + struct malloc_chunk* bk; +}; + +typedef struct malloc_chunk* mchunkptr; + +/* + + malloc_chunk details: + + (The following includes lightly edited explanations by Colin Plumb.) + + Chunks of memory are maintained using a `boundary tag' method as + described in e.g., Knuth or Standish. (See the paper by Paul + Wilson ftp://ftp.cs.utexas.edu/pub/garbage/allocsrv.ps for a + survey of such techniques.) Sizes of free chunks are stored both + in the front of each chunk and at the end. This makes + consolidating fragmented chunks into bigger chunks very fast. The + size fields also hold bits representing whether chunks are free or + in use. + + An allocated chunk looks like this: + + + chunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Size of previous chunk, if allocated | | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Size of chunk, in bytes |P| + mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | User data starts here... . + . . + . (malloc_usable_space() bytes) . + . | +nextchunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Size of chunk | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + + Where "chunk" is the front of the chunk for the purpose of most of + the malloc code, but "mem" is the pointer that is returned to the + user. "Nextchunk" is the beginning of the next contiguous chunk. + + Chunks always begin on even word boundries, so the mem portion + (which is returned to the user) is also on an even word boundary, and + thus double-word aligned. + + Free chunks are stored in circular doubly-linked lists, and look like this: + + chunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Size of previous chunk | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + `head:' | Size of chunk, in bytes |P| + mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Forward pointer to next chunk in list | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Back pointer to previous chunk in list | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Unused space (may be 0 bytes long) . + . . + . | +nextchunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + `foot:' | Size of chunk, in bytes | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + The P (PREV_INUSE) bit, stored in the unused low-order bit of the + chunk size (which is always a multiple of two words), is an in-use + bit for the *previous* chunk. If that bit is *clear*, then the + word before the current chunk size contains the previous chunk + size, and can be used to find the front of the previous chunk. + (The very first chunk allocated always has this bit set, + preventing access to non-existent (or non-owned) memory.) + + Note that the `foot' of the current chunk is actually represented + as the prev_size of the NEXT chunk. (This makes it easier to + deal with alignments etc). + + The two exceptions to all this are + + 1. The special chunk `top', which doesn't bother using the + trailing size field since there is no + next contiguous chunk that would have to index off it. (After + initialization, `top' is forced to always exist. If it would + become less than MINSIZE bytes long, it is replenished via + malloc_extend_top.) + + 2. Chunks allocated via mmap, which have the second-lowest-order + bit (IS_MMAPPED) set in their size fields. Because they are + never merged or traversed from any other chunk, they have no + foot size or inuse information. + + Available chunks are kept in any of several places (all declared below): + + * `av': An array of chunks serving as bin headers for consolidated + chunks. Each bin is doubly linked. The bins are approximately + proportionally (log) spaced. There are a lot of these bins + (128). This may look excessive, but works very well in + practice. All procedures maintain the invariant that no + consolidated chunk physically borders another one. Chunks in + bins are kept in size order, with ties going to the + approximately least recently used chunk. + + The chunks in each bin are maintained in decreasing sorted order by + size. This is irrelevant for the small bins, which all contain + the same-sized chunks, but facilitates best-fit allocation for + larger chunks. (These lists are just sequential. Keeping them in + order almost never requires enough traversal to warrant using + fancier ordered data structures.) Chunks of the same size are + linked with the most recently freed at the front, and allocations + are taken from the back. This results in LRU or FIFO allocation + order, which tends to give each chunk an equal opportunity to be + consolidated with adjacent freed chunks, resulting in larger free + chunks and less fragmentation. + + * `top': The top-most available chunk (i.e., the one bordering the + end of available memory) is treated specially. It is never + included in any bin, is used only if no other chunk is + available, and is released back to the system if it is very + large (see M_TRIM_THRESHOLD). + + * `last_remainder': A bin holding only the remainder of the + most recently split (non-top) chunk. This bin is checked + before other non-fitting chunks, so as to provide better + locality for runs of sequentially allocated chunks. + + * Implicitly, through the host system's memory mapping tables. + If supported, requests greater than a threshold are usually + serviced via calls to mmap, and then later released via munmap. + +*/ + + + + + +/* sizes, alignments */ + +#define SIZE_SZ (sizeof(INTERNAL_SIZE_T)) +#define MALLOC_ALIGNMENT (SIZE_SZ + SIZE_SZ) +#define MALLOC_ALIGN_MASK (MALLOC_ALIGNMENT - 1) +#define MINSIZE (sizeof(struct malloc_chunk)) + +/* conversion from malloc headers to user pointers, and back */ + +#define chunk2mem(p) ((Void_t*)((char*)(p) + 2*SIZE_SZ)) +#define mem2chunk(mem) ((mchunkptr)((char*)(mem) - 2*SIZE_SZ)) + +/* pad request bytes into a usable size */ + +#define request2size(req) \ + (((long)((req) + (SIZE_SZ + MALLOC_ALIGN_MASK)) < \ + (long)(MINSIZE + MALLOC_ALIGN_MASK)) ? MINSIZE : \ + (((req) + (SIZE_SZ + MALLOC_ALIGN_MASK)) & ~(MALLOC_ALIGN_MASK))) + +/* Check if m has acceptable alignment */ + +#define aligned_OK(m) (((unsigned long)((m)) & (MALLOC_ALIGN_MASK)) == 0) + + + + +/* + Physical chunk operations +*/ + + +/* size field is or'ed with PREV_INUSE when previous adjacent chunk in use */ + +#define PREV_INUSE 0x1 + +/* size field is or'ed with IS_MMAPPED if the chunk was obtained with mmap() */ + +#define IS_MMAPPED 0x2 + +/* Bits to mask off when extracting size */ + +#define SIZE_BITS (PREV_INUSE|IS_MMAPPED) + + +/* Ptr to next physical malloc_chunk. */ + +#define next_chunk(p) ((mchunkptr)( ((char*)(p)) + ((p)->size & ~PREV_INUSE) )) + +/* Ptr to previous physical malloc_chunk */ + +#define prev_chunk(p)\ + ((mchunkptr)( ((char*)(p)) - ((p)->prev_size) )) + + +/* Treat space at ptr + offset as a chunk */ + +#define chunk_at_offset(p, s) ((mchunkptr)(((char*)(p)) + (s))) + + + + +/* + Dealing with use bits +*/ + +/* extract p's inuse bit */ + +#define inuse(p)\ +((((mchunkptr)(((char*)(p))+((p)->size & ~PREV_INUSE)))->size) & PREV_INUSE) + +/* extract inuse bit of previous chunk */ + +#define prev_inuse(p) ((p)->size & PREV_INUSE) + +/* check for mmap()'ed chunk */ + +#define chunk_is_mmapped(p) ((p)->size & IS_MMAPPED) + +/* set/clear chunk as in use without otherwise disturbing */ + +#define set_inuse(p)\ +((mchunkptr)(((char*)(p)) + ((p)->size & ~PREV_INUSE)))->size |= PREV_INUSE + +#define clear_inuse(p)\ +((mchunkptr)(((char*)(p)) + ((p)->size & ~PREV_INUSE)))->size &= ~(PREV_INUSE) + +/* check/set/clear inuse bits in known places */ + +#define inuse_bit_at_offset(p, s)\ + (((mchunkptr)(((char*)(p)) + (s)))->size & PREV_INUSE) + +#define set_inuse_bit_at_offset(p, s)\ + (((mchunkptr)(((char*)(p)) + (s)))->size |= PREV_INUSE) + +#define clear_inuse_bit_at_offset(p, s)\ + (((mchunkptr)(((char*)(p)) + (s)))->size &= ~(PREV_INUSE)) + + + + +/* + Dealing with size fields +*/ + +/* Get size, ignoring use bits */ + +#define chunksize(p) ((p)->size & ~(SIZE_BITS)) + +/* Set size at head, without disturbing its use bit */ + +#define set_head_size(p, s) ((p)->size = (((p)->size & PREV_INUSE) | (s))) + +/* Set size/use ignoring previous bits in header */ + +#define set_head(p, s) ((p)->size = (s)) + +/* Set size at footer (only when chunk is not in use) */ + +#define set_foot(p, s) (((mchunkptr)((char*)(p) + (s)))->prev_size = (s)) + + + + + +/* + Bins + + The bins, `av_' are an array of pairs of pointers serving as the + heads of (initially empty) doubly-linked lists of chunks, laid out + in a way so that each pair can be treated as if it were in a + malloc_chunk. (This way, the fd/bk offsets for linking bin heads + and chunks are the same). + + Bins for sizes < 512 bytes contain chunks of all the same size, spaced + 8 bytes apart. Larger bins are approximately logarithmically + spaced. (See the table below.) The `av_' array is never mentioned + directly in the code, but instead via bin access macros. + + Bin layout: + + 64 bins of size 8 + 32 bins of size 64 + 16 bins of size 512 + 8 bins of size 4096 + 4 bins of size 32768 + 2 bins of size 262144 + 1 bin of size what's left + + There is actually a little bit of slop in the numbers in bin_index + for the sake of speed. This makes no difference elsewhere. + + The special chunks `top' and `last_remainder' get their own bins, + (this is implemented via yet more trickery with the av_ array), + although `top' is never properly linked to its bin since it is + always handled specially. + +*/ + +#define NAV 128 /* number of bins */ + +typedef struct malloc_chunk* mbinptr; + +/* access macros */ + +#define bin_at(i) ((mbinptr)((char*)&(av_[2*(i) + 2]) - 2*SIZE_SZ)) +#define next_bin(b) ((mbinptr)((char*)(b) + 2 * sizeof(mbinptr))) +#define prev_bin(b) ((mbinptr)((char*)(b) - 2 * sizeof(mbinptr))) + +/* + The first 2 bins are never indexed. The corresponding av_ cells are instead + used for bookkeeping. This is not to save space, but to simplify + indexing, maintain locality, and avoid some initialization tests. +*/ + +#define top (bin_at(0)->fd) /* The topmost chunk */ +#define last_remainder (bin_at(1)) /* remainder from last split */ + + +/* + Because top initially points to its own bin with initial + zero size, thus forcing extension on the first malloc request, + we avoid having any special code in malloc to check whether + it even exists yet. But we still need to in malloc_extend_top. +*/ + +#define initial_top ((mchunkptr)(bin_at(0))) + +/* Helper macro to initialize bins */ + +#define IAV(i) bin_at(i), bin_at(i) + +static mbinptr av_[NAV * 2 + 2] = { + 0, 0, + IAV(0), IAV(1), IAV(2), IAV(3), IAV(4), IAV(5), IAV(6), IAV(7), + IAV(8), IAV(9), IAV(10), IAV(11), IAV(12), IAV(13), IAV(14), IAV(15), + IAV(16), IAV(17), IAV(18), IAV(19), IAV(20), IAV(21), IAV(22), IAV(23), + IAV(24), IAV(25), IAV(26), IAV(27), IAV(28), IAV(29), IAV(30), IAV(31), + IAV(32), IAV(33), IAV(34), IAV(35), IAV(36), IAV(37), IAV(38), IAV(39), + IAV(40), IAV(41), IAV(42), IAV(43), IAV(44), IAV(45), IAV(46), IAV(47), + IAV(48), IAV(49), IAV(50), IAV(51), IAV(52), IAV(53), IAV(54), IAV(55), + IAV(56), IAV(57), IAV(58), IAV(59), IAV(60), IAV(61), IAV(62), IAV(63), + IAV(64), IAV(65), IAV(66), IAV(67), IAV(68), IAV(69), IAV(70), IAV(71), + IAV(72), IAV(73), IAV(74), IAV(75), IAV(76), IAV(77), IAV(78), IAV(79), + IAV(80), IAV(81), IAV(82), IAV(83), IAV(84), IAV(85), IAV(86), IAV(87), + IAV(88), IAV(89), IAV(90), IAV(91), IAV(92), IAV(93), IAV(94), IAV(95), + IAV(96), IAV(97), IAV(98), IAV(99), IAV(100), IAV(101), IAV(102), IAV(103), + IAV(104), IAV(105), IAV(106), IAV(107), IAV(108), IAV(109), IAV(110), IAV(111), + IAV(112), IAV(113), IAV(114), IAV(115), IAV(116), IAV(117), IAV(118), IAV(119), + IAV(120), IAV(121), IAV(122), IAV(123), IAV(124), IAV(125), IAV(126), IAV(127) +}; + + + +/* field-extraction macros */ + +#define first(b) ((b)->fd) +#define last(b) ((b)->bk) + +/* + Indexing into bins +*/ + +#define bin_index(sz) \ +(((((unsigned long)(sz)) >> 9) == 0) ? (((unsigned long)(sz)) >> 3): \ + ((((unsigned long)(sz)) >> 9) <= 4) ? 56 + (((unsigned long)(sz)) >> 6): \ + ((((unsigned long)(sz)) >> 9) <= 20) ? 91 + (((unsigned long)(sz)) >> 9): \ + ((((unsigned long)(sz)) >> 9) <= 84) ? 110 + (((unsigned long)(sz)) >> 12): \ + ((((unsigned long)(sz)) >> 9) <= 340) ? 119 + (((unsigned long)(sz)) >> 15): \ + ((((unsigned long)(sz)) >> 9) <= 1364) ? 124 + (((unsigned long)(sz)) >> 18): \ + 126) +/* + bins for chunks < 512 are all spaced 8 bytes apart, and hold + identically sized chunks. This is exploited in malloc. +*/ + +#define MAX_SMALLBIN 63 +#define MAX_SMALLBIN_SIZE 512 +#define SMALLBIN_WIDTH 8 + +#define smallbin_index(sz) (((unsigned long)(sz)) >> 3) + +/* + Requests are `small' if both the corresponding and the next bin are small +*/ + +#define is_small_request(nb) (nb < MAX_SMALLBIN_SIZE - SMALLBIN_WIDTH) + + + +/* + To help compensate for the large number of bins, a one-level index + structure is used for bin-by-bin searching. `binblocks' is a + one-word bitvector recording whether groups of BINBLOCKWIDTH bins + have any (possibly) non-empty bins, so they can be skipped over + all at once during during traversals. The bits are NOT always + cleared as soon as all bins in a block are empty, but instead only + when all are noticed to be empty during traversal in malloc. +*/ + +#define BINBLOCKWIDTH 4 /* bins per block */ + +#define binblocks (bin_at(0)->size) /* bitvector of nonempty blocks */ + +/* bin<->block macros */ + +#define idx2binblock(ix) ((unsigned)1 << (ix / BINBLOCKWIDTH)) +#define mark_binblock(ii) (binblocks |= idx2binblock(ii)) +#define clear_binblock(ii) (binblocks &= ~(idx2binblock(ii))) + + + + + +/* Other static bookkeeping data */ + +/* variables holding tunable values */ + +static unsigned long trim_threshold = DEFAULT_TRIM_THRESHOLD; +static unsigned long top_pad = DEFAULT_TOP_PAD; +static unsigned int n_mmaps_max = DEFAULT_MMAP_MAX; +static unsigned long mmap_threshold = DEFAULT_MMAP_THRESHOLD; + +/* The first value returned from sbrk */ +static char* sbrk_base = (char*)(-1); + +/* The maximum memory obtained from system via sbrk */ +static unsigned long max_sbrked_mem = 0; + +/* The maximum via either sbrk or mmap */ +static unsigned long max_total_mem = 0; + +/* internal working copy of mallinfo */ +static struct mallinfo current_mallinfo = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + +/* The total memory obtained from system via sbrk */ +#define sbrked_mem (current_mallinfo.arena) + +/* Tracking mmaps */ + +static unsigned int n_mmaps = 0; +static unsigned int max_n_mmaps = 0; +static unsigned long mmapped_mem = 0; +static unsigned long max_mmapped_mem = 0; + + + +/* + Debugging support +*/ + +#if DEBUG + + +/* + These routines make a number of assertions about the states + of data structures that should be true at all times. If any + are not true, it's very likely that a user program has somehow + trashed memory. (It's also possible that there is a coding error + in malloc. In which case, please report it!) +*/ + +#if __STD_C +static void do_check_chunk(mchunkptr p) +#else +static void do_check_chunk(p) mchunkptr p; +#endif +{ + INTERNAL_SIZE_T sz = p->size & ~PREV_INUSE; + + /* No checkable chunk is mmapped */ + assert(!chunk_is_mmapped(p)); + + /* Check for legal address ... */ + assert((char*)p >= sbrk_base); + if (p != top) + assert((char*)p + sz <= (char*)top); + else + assert((char*)p + sz <= sbrk_base + sbrked_mem); + +} + + +#if __STD_C +static void do_check_free_chunk(mchunkptr p) +#else +static void do_check_free_chunk(p) mchunkptr p; +#endif +{ + INTERNAL_SIZE_T sz = p->size & ~PREV_INUSE; + mchunkptr next = chunk_at_offset(p, sz); + + do_check_chunk(p); + + /* Check whether it claims to be free ... */ + assert(!inuse(p)); + + /* Unless a special marker, must have OK fields */ + if ((long)sz >= (long)MINSIZE) + { + assert((sz & MALLOC_ALIGN_MASK) == 0); + assert(aligned_OK(chunk2mem(p))); + /* ... matching footer field */ + assert(next->prev_size == sz); + /* ... and is fully consolidated */ + assert(prev_inuse(p)); + assert (next == top || inuse(next)); + + /* ... and has minimally sane links */ + assert(p->fd->bk == p); + assert(p->bk->fd == p); + } + else /* markers are always of size SIZE_SZ */ + assert(sz == SIZE_SZ); +} + +#if __STD_C +static void do_check_inuse_chunk(mchunkptr p) +#else +static void do_check_inuse_chunk(p) mchunkptr p; +#endif +{ + mchunkptr next = next_chunk(p); + do_check_chunk(p); + + /* Check whether it claims to be in use ... */ + assert(inuse(p)); + + /* ... and is surrounded by OK chunks. + Since more things can be checked with free chunks than inuse ones, + if an inuse chunk borders them and debug is on, it's worth doing them. + */ + if (!prev_inuse(p)) + { + mchunkptr prv = prev_chunk(p); + assert(next_chunk(prv) == p); + do_check_free_chunk(prv); + } + if (next == top) + { + assert(prev_inuse(next)); + assert(chunksize(next) >= MINSIZE); + } + else if (!inuse(next)) + do_check_free_chunk(next); + +} + +#if __STD_C +static void do_check_malloced_chunk(mchunkptr p, INTERNAL_SIZE_T s) +#else +static void do_check_malloced_chunk(p, s) mchunkptr p; INTERNAL_SIZE_T s; +#endif +{ + INTERNAL_SIZE_T sz = p->size & ~PREV_INUSE; + long room = sz - s; + + do_check_inuse_chunk(p); + + /* Legal size ... */ + assert((long)sz >= (long)MINSIZE); + assert((sz & MALLOC_ALIGN_MASK) == 0); + assert(room >= 0); + assert(room < (long)MINSIZE); + + /* ... and alignment */ + assert(aligned_OK(chunk2mem(p))); + + + /* ... and was allocated at front of an available chunk */ + assert(prev_inuse(p)); + +} + + +#define check_free_chunk(P) do_check_free_chunk(P) +#define check_inuse_chunk(P) do_check_inuse_chunk(P) +#define check_chunk(P) do_check_chunk(P) +#define check_malloced_chunk(P,N) do_check_malloced_chunk(P,N) +#else +#define check_free_chunk(P) +#define check_inuse_chunk(P) +#define check_chunk(P) +#define check_malloced_chunk(P,N) +#endif + + + +/* + Macro-based internal utilities +*/ + + +/* + Linking chunks in bin lists. + Call these only with variables, not arbitrary expressions, as arguments. +*/ + +/* + Place chunk p of size s in its bin, in size order, + putting it ahead of others of same size. +*/ + + +#define frontlink(P, S, IDX, BK, FD) \ +{ \ + if (S < MAX_SMALLBIN_SIZE) \ + { \ + IDX = smallbin_index(S); \ + mark_binblock(IDX); \ + BK = bin_at(IDX); \ + FD = BK->fd; \ + P->bk = BK; \ + P->fd = FD; \ + FD->bk = BK->fd = P; \ + } \ + else \ + { \ + IDX = bin_index(S); \ + BK = bin_at(IDX); \ + FD = BK->fd; \ + if (FD == BK) mark_binblock(IDX); \ + else \ + { \ + while (FD != BK && S < chunksize(FD)) FD = FD->fd; \ + BK = FD->bk; \ + } \ + P->bk = BK; \ + P->fd = FD; \ + FD->bk = BK->fd = P; \ + } \ +} + + +/* take a chunk off a list */ + +#define unlink(P, BK, FD) \ +{ \ + BK = P->bk; \ + FD = P->fd; \ + FD->bk = BK; \ + BK->fd = FD; \ +} \ + +/* Place p as the last remainder */ + +#define link_last_remainder(P) \ +{ \ + last_remainder->fd = last_remainder->bk = P; \ + P->fd = P->bk = last_remainder; \ +} + +/* Clear the last_remainder bin */ + +#define clear_last_remainder \ + (last_remainder->fd = last_remainder->bk = last_remainder) + + + + + +/* Routines dealing with mmap(). */ + +#if HAVE_MMAP + +#if __STD_C +static mchunkptr mmap_chunk(size_t size) +#else +static mchunkptr mmap_chunk(size) size_t size; +#endif +{ + size_t page_mask = malloc_getpagesize - 1; + mchunkptr p; + +#ifndef MAP_ANONYMOUS + static int fd = -1; +#endif + + if(n_mmaps >= n_mmaps_max) return 0; /* too many regions */ + + /* For mmapped chunks, the overhead is one SIZE_SZ unit larger, because + * there is no following chunk whose prev_size field could be used. + */ + size = (size + SIZE_SZ + page_mask) & ~page_mask; + +#ifdef MAP_ANONYMOUS + p = (mchunkptr)mmap(0, size, PROT_READ|PROT_WRITE, + MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); +#else /* !MAP_ANONYMOUS */ + if (fd < 0) + { + fd = open("/dev/zero", O_RDWR); + if(fd < 0) return 0; + } + p = (mchunkptr)mmap(0, size, PROT_READ|PROT_WRITE, MAP_PRIVATE, fd, 0); +#endif + + if(p == (mchunkptr)-1) return 0; + + n_mmaps++; + if (n_mmaps > max_n_mmaps) max_n_mmaps = n_mmaps; + + /* We demand that eight bytes into a page must be 8-byte aligned. */ + assert(aligned_OK(chunk2mem(p))); + + /* The offset to the start of the mmapped region is stored + * in the prev_size field of the chunk; normally it is zero, + * but that can be changed in memalign(). + */ + p->prev_size = 0; + set_head(p, size|IS_MMAPPED); + + mmapped_mem += size; + if ((unsigned long)mmapped_mem > (unsigned long)max_mmapped_mem) + max_mmapped_mem = mmapped_mem; + if ((unsigned long)(mmapped_mem + sbrked_mem) > (unsigned long)max_total_mem) + max_total_mem = mmapped_mem + sbrked_mem; + return p; +} + +#if __STD_C +static void munmap_chunk(mchunkptr p) +#else +static void munmap_chunk(p) mchunkptr p; +#endif +{ + INTERNAL_SIZE_T size = chunksize(p); + int ret; + + assert (chunk_is_mmapped(p)); + assert(! ((char*)p >= sbrk_base && (char*)p < sbrk_base + sbrked_mem)); + assert((n_mmaps > 0)); + assert(((p->prev_size + size) & (malloc_getpagesize-1)) == 0); + + n_mmaps--; + mmapped_mem -= (size + p->prev_size); + + ret = munmap((char *)p - p->prev_size, size + p->prev_size); + + /* munmap returns non-zero on failure */ + assert(ret == 0); +} + +#if HAVE_MREMAP + +#if __STD_C +static mchunkptr mremap_chunk(mchunkptr p, size_t new_size) +#else +static mchunkptr mremap_chunk(p, new_size) mchunkptr p; size_t new_size; +#endif +{ + size_t page_mask = malloc_getpagesize - 1; + INTERNAL_SIZE_T offset = p->prev_size; + INTERNAL_SIZE_T size = chunksize(p); + char *cp; + + assert (chunk_is_mmapped(p)); + assert(! ((char*)p >= sbrk_base && (char*)p < sbrk_base + sbrked_mem)); + assert((n_mmaps > 0)); + assert(((size + offset) & (malloc_getpagesize-1)) == 0); + + /* Note the extra SIZE_SZ overhead as in mmap_chunk(). */ + new_size = (new_size + offset + SIZE_SZ + page_mask) & ~page_mask; + + cp = (char *)mremap((char *)p - offset, size + offset, new_size, 1); + + if (cp == (char *)-1) return 0; + + p = (mchunkptr)(cp + offset); + + assert(aligned_OK(chunk2mem(p))); + + assert((p->prev_size == offset)); + set_head(p, (new_size - offset)|IS_MMAPPED); + + mmapped_mem -= size + offset; + mmapped_mem += new_size; + if ((unsigned long)mmapped_mem > (unsigned long)max_mmapped_mem) + max_mmapped_mem = mmapped_mem; + if ((unsigned long)(mmapped_mem + sbrked_mem) > (unsigned long)max_total_mem) + max_total_mem = mmapped_mem + sbrked_mem; + return p; +} + +#endif /* HAVE_MREMAP */ + +#endif /* HAVE_MMAP */ + + + + +/* + Extend the top-most chunk by obtaining memory from system. + Main interface to sbrk (but see also malloc_trim). +*/ + +#if __STD_C +static void malloc_extend_top(INTERNAL_SIZE_T nb) +#else +static void malloc_extend_top(nb) INTERNAL_SIZE_T nb; +#endif +{ + char* brk; /* return value from sbrk */ + INTERNAL_SIZE_T front_misalign; /* unusable bytes at front of sbrked space */ + INTERNAL_SIZE_T correction; /* bytes for 2nd sbrk call */ + char* new_brk; /* return of 2nd sbrk call */ + INTERNAL_SIZE_T top_size; /* new size of top chunk */ + + mchunkptr old_top = top; /* Record state of old top */ + INTERNAL_SIZE_T old_top_size = chunksize(old_top); + char* old_end = (char*)(chunk_at_offset(old_top, old_top_size)); + + /* Pad request with top_pad plus minimal overhead */ + + INTERNAL_SIZE_T sbrk_size = nb + top_pad + MINSIZE; + unsigned long pagesz = malloc_getpagesize; + + /* If not the first time through, round to preserve page boundary */ + /* Otherwise, we need to correct to a page size below anyway. */ + /* (We also correct below if an intervening foreign sbrk call.) */ + + if (sbrk_base != (char*)(-1)) + sbrk_size = (sbrk_size + (pagesz - 1)) & ~(pagesz - 1); + + brk = (char*)(MORECORE (sbrk_size)); + + /* Fail if sbrk failed or if a foreign sbrk call killed our space */ + if (brk == (char*)(MORECORE_FAILURE) || + (brk < old_end && old_top != initial_top)) + return; + + sbrked_mem += sbrk_size; + + if (brk == old_end) /* can just add bytes to current top */ + { + top_size = sbrk_size + old_top_size; + set_head(top, top_size | PREV_INUSE); + } + else + { + if (sbrk_base == (char*)(-1)) /* First time through. Record base */ + sbrk_base = brk; + else /* Someone else called sbrk(). Count those bytes as sbrked_mem. */ + sbrked_mem += brk - (char*)old_end; + + /* Guarantee alignment of first new chunk made from this space */ + front_misalign = (unsigned long)chunk2mem(brk) & MALLOC_ALIGN_MASK; + if (front_misalign > 0) + { + correction = (MALLOC_ALIGNMENT) - front_misalign; + brk += correction; + } + else + correction = 0; + + /* Guarantee the next brk will be at a page boundary */ + + correction += ((((unsigned long)(brk + sbrk_size))+(pagesz-1)) & + ~(pagesz - 1)) - ((unsigned long)(brk + sbrk_size)); + + /* Allocate correction */ + new_brk = (char*)(MORECORE (correction)); + if (new_brk == (char*)(MORECORE_FAILURE)) return; + + sbrked_mem += correction; + + top = (mchunkptr)brk; + top_size = new_brk - brk + correction; + set_head(top, top_size | PREV_INUSE); + + if (old_top != initial_top) + { + + /* There must have been an intervening foreign sbrk call. */ + /* A double fencepost is necessary to prevent consolidation */ + + /* If not enough space to do this, then user did something very wrong */ + if (old_top_size < MINSIZE) + { + set_head(top, PREV_INUSE); /* will force null return from malloc */ + return; + } + + /* Also keep size a multiple of MALLOC_ALIGNMENT */ + old_top_size = (old_top_size - 3*SIZE_SZ) & ~MALLOC_ALIGN_MASK; + set_head_size(old_top, old_top_size); + chunk_at_offset(old_top, old_top_size )->size = + SIZE_SZ|PREV_INUSE; + chunk_at_offset(old_top, old_top_size + SIZE_SZ)->size = + SIZE_SZ|PREV_INUSE; + /* If possible, release the rest. */ + if (old_top_size >= MINSIZE) + fREe(chunk2mem(old_top)); + } + } + + if ((unsigned long)sbrked_mem > (unsigned long)max_sbrked_mem) + max_sbrked_mem = sbrked_mem; + if ((unsigned long)(mmapped_mem + sbrked_mem) > (unsigned long)max_total_mem) + max_total_mem = mmapped_mem + sbrked_mem; + + /* We always land on a page boundary */ + assert(((unsigned long)((char*)top + top_size) & (pagesz - 1)) == 0); +} + + + + +/* Main public routines */ + + +/* + Malloc Algorthim: + + The requested size is first converted into a usable form, `nb'. + This currently means to add 4 bytes overhead plus possibly more to + obtain 8-byte alignment and/or to obtain a size of at least + MINSIZE (currently 16 bytes), the smallest allocatable size. + (All fits are considered `exact' if they are within MINSIZE bytes.) + + From there, the first successful of the following steps is taken: + + 1. The bin corresponding to the request size is scanned, and if + a chunk of exactly the right size is found, it is taken. + + 2. The most recently remaindered chunk is used if it is big + enough. This is a form of (roving) first fit, used only in + the absence of exact fits. Runs of consecutive requests use + the remainder of the chunk used for the previous such request + whenever possible. This limited use of a first-fit style + allocation strategy tends to give contiguous chunks + coextensive lifetimes, which improves locality and can reduce + fragmentation in the long run. + + 3. Other bins are scanned in increasing size order, using a + chunk big enough to fulfill the request, and splitting off + any remainder. This search is strictly by best-fit; i.e., + the smallest (with ties going to approximately the least + recently used) chunk that fits is selected. + + 4. If large enough, the chunk bordering the end of memory + (`top') is split off. (This use of `top' is in accord with + the best-fit search rule. In effect, `top' is treated as + larger (and thus less well fitting) than any other available + chunk since it can be extended to be as large as necessary + (up to system limitations). + + 5. If the request size meets the mmap threshold and the + system supports mmap, and there are few enough currently + allocated mmapped regions, and a call to mmap succeeds, + the request is allocated via direct memory mapping. + + 6. Otherwise, the top of memory is extended by + obtaining more space from the system (normally using sbrk, + but definable to anything else via the MORECORE macro). + Memory is gathered from the system (in system page-sized + units) in a way that allows chunks obtained across different + sbrk calls to be consolidated, but does not require + contiguous memory. Thus, it should be safe to intersperse + mallocs with other sbrk calls. + + + All allocations are made from the the `lowest' part of any found + chunk. (The implementation invariant is that prev_inuse is + always true of any allocated chunk; i.e., that each allocated + chunk borders either a previously allocated and still in-use chunk, + or the base of its memory arena.) + +*/ + +#if __STD_C +Void_t* mALLOc(size_t bytes) +#else +Void_t* mALLOc(bytes) size_t bytes; +#endif +{ + mchunkptr victim; /* inspected/selected chunk */ + INTERNAL_SIZE_T victim_size; /* its size */ + int idx; /* index for bin traversal */ + mbinptr bin; /* associated bin */ + mchunkptr remainder; /* remainder from a split */ + long remainder_size; /* its size */ + int remainder_index; /* its bin index */ + unsigned long block; /* block traverser bit */ + int startidx; /* first bin of a traversed block */ + mchunkptr fwd; /* misc temp for linking */ + mchunkptr bck; /* misc temp for linking */ + mbinptr q; /* misc temp */ + + INTERNAL_SIZE_T nb; + + if ((long)bytes < 0) return 0; + + nb = request2size(bytes); /* padded request size; */ + + /* Check for exact match in a bin */ + + if (is_small_request(nb)) /* Faster version for small requests */ + { + idx = smallbin_index(nb); + + /* No traversal or size check necessary for small bins. */ + + q = bin_at(idx); + victim = last(q); + + /* Also scan the next one, since it would have a remainder < MINSIZE */ + if (victim == q) + { + q = next_bin(q); + victim = last(q); + } + if (victim != q) + { + victim_size = chunksize(victim); + unlink(victim, bck, fwd); + set_inuse_bit_at_offset(victim, victim_size); + check_malloced_chunk(victim, nb); + return chunk2mem(victim); + } + + idx += 2; /* Set for bin scan below. We've already scanned 2 bins. */ + + } + else + { + idx = bin_index(nb); + bin = bin_at(idx); + + for (victim = last(bin); victim != bin; victim = victim->bk) + { + victim_size = chunksize(victim); + remainder_size = victim_size - nb; + + if (remainder_size >= (long)MINSIZE) /* too big */ + { + --idx; /* adjust to rescan below after checking last remainder */ + break; + } + + else if (remainder_size >= 0) /* exact fit */ + { + unlink(victim, bck, fwd); + set_inuse_bit_at_offset(victim, victim_size); + check_malloced_chunk(victim, nb); + return chunk2mem(victim); + } + } + + ++idx; + + } + + /* Try to use the last split-off remainder */ + + if ( (victim = last_remainder->fd) != last_remainder) + { + victim_size = chunksize(victim); + remainder_size = victim_size - nb; + + if (remainder_size >= (long)MINSIZE) /* re-split */ + { + remainder = chunk_at_offset(victim, nb); + set_head(victim, nb | PREV_INUSE); + link_last_remainder(remainder); + set_head(remainder, remainder_size | PREV_INUSE); + set_foot(remainder, remainder_size); + check_malloced_chunk(victim, nb); + return chunk2mem(victim); + } + + clear_last_remainder; + + if (remainder_size >= 0) /* exhaust */ + { + set_inuse_bit_at_offset(victim, victim_size); + check_malloced_chunk(victim, nb); + return chunk2mem(victim); + } + + /* Else place in bin */ + + frontlink(victim, victim_size, remainder_index, bck, fwd); + } + + /* + If there are any possibly nonempty big-enough blocks, + search for best fitting chunk by scanning bins in blockwidth units. + */ + + if ( (block = idx2binblock(idx)) <= binblocks) + { + + /* Get to the first marked block */ + + if ( (block & binblocks) == 0) + { + /* force to an even block boundary */ + idx = (idx & ~(BINBLOCKWIDTH - 1)) + BINBLOCKWIDTH; + block <<= 1; + while ((block & binblocks) == 0) + { + idx += BINBLOCKWIDTH; + block <<= 1; + } + } + + /* For each possibly nonempty block ... */ + for (;;) + { + startidx = idx; /* (track incomplete blocks) */ + q = bin = bin_at(idx); + + /* For each bin in this block ... */ + do + { + /* Find and use first big enough chunk ... */ + + for (victim = last(bin); victim != bin; victim = victim->bk) + { + victim_size = chunksize(victim); + remainder_size = victim_size - nb; + + if (remainder_size >= (long)MINSIZE) /* split */ + { + remainder = chunk_at_offset(victim, nb); + set_head(victim, nb | PREV_INUSE); + unlink(victim, bck, fwd); + link_last_remainder(remainder); + set_head(remainder, remainder_size | PREV_INUSE); + set_foot(remainder, remainder_size); + check_malloced_chunk(victim, nb); + return chunk2mem(victim); + } + + else if (remainder_size >= 0) /* take */ + { + set_inuse_bit_at_offset(victim, victim_size); + unlink(victim, bck, fwd); + check_malloced_chunk(victim, nb); + return chunk2mem(victim); + } + + } + + bin = next_bin(bin); + + } while ((++idx & (BINBLOCKWIDTH - 1)) != 0); + + /* Clear out the block bit. */ + + do /* Possibly backtrack to try to clear a partial block */ + { + if ((startidx & (BINBLOCKWIDTH - 1)) == 0) + { + binblocks &= ~block; + break; + } + --startidx; + q = prev_bin(q); + } while (first(q) == q); + + /* Get to the next possibly nonempty block */ + + if ( (block <<= 1) <= binblocks && (block != 0) ) + { + while ((block & binblocks) == 0) + { + idx += BINBLOCKWIDTH; + block <<= 1; + } + } + else + break; + } + } + + + /* Try to use top chunk */ + + /* Require that there be a remainder, ensuring top always exists */ + if ( (remainder_size = chunksize(top) - nb) < (long)MINSIZE) + { + +#if HAVE_MMAP + /* If big and would otherwise need to extend, try to use mmap instead */ + if ((unsigned long)nb >= (unsigned long)mmap_threshold && + (victim = mmap_chunk(nb)) != 0) + return chunk2mem(victim); +#endif + + /* Try to extend */ + malloc_extend_top(nb); + if ( (remainder_size = chunksize(top) - nb) < (long)MINSIZE) + return 0; /* propagate failure */ + } + + victim = top; + set_head(victim, nb | PREV_INUSE); + top = chunk_at_offset(victim, nb); + set_head(top, remainder_size | PREV_INUSE); + check_malloced_chunk(victim, nb); + return chunk2mem(victim); + +} + + + + +/* + + free() algorithm : + + cases: + + 1. free(0) has no effect. + + 2. If the chunk was allocated via mmap, it is release via munmap(). + + 3. If a returned chunk borders the current high end of memory, + it is consolidated into the top, and if the total unused + topmost memory exceeds the trim threshold, malloc_trim is + called. + + 4. Other chunks are consolidated as they arrive, and + placed in corresponding bins. (This includes the case of + consolidating with the current `last_remainder'). + +*/ + + +#if __STD_C +void fREe(Void_t* mem) +#else +void fREe(mem) Void_t* mem; +#endif +{ + mchunkptr p; /* chunk corresponding to mem */ + INTERNAL_SIZE_T hd; /* its head field */ + INTERNAL_SIZE_T sz; /* its size */ + int idx; /* its bin index */ + mchunkptr next; /* next contiguous chunk */ + INTERNAL_SIZE_T nextsz; /* its size */ + INTERNAL_SIZE_T prevsz; /* size of previous contiguous chunk */ + mchunkptr bck; /* misc temp for linking */ + mchunkptr fwd; /* misc temp for linking */ + int islr; /* track whether merging with last_remainder */ + + if (mem == 0) /* free(0) has no effect */ + return; + + p = mem2chunk(mem); + hd = p->size; + +#if HAVE_MMAP + if (hd & IS_MMAPPED) /* release mmapped memory. */ + { + munmap_chunk(p); + return; + } +#endif + + check_inuse_chunk(p); + + sz = hd & ~PREV_INUSE; + next = chunk_at_offset(p, sz); + nextsz = chunksize(next); + + if (next == top) /* merge with top */ + { + sz += nextsz; + + if (!(hd & PREV_INUSE)) /* consolidate backward */ + { + prevsz = p->prev_size; + p = chunk_at_offset(p, -((long) prevsz)); + sz += prevsz; + unlink(p, bck, fwd); + } + + set_head(p, sz | PREV_INUSE); + top = p; + if ((unsigned long)(sz) >= (unsigned long)trim_threshold) + malloc_trim(top_pad); + return; + } + + set_head(next, nextsz); /* clear inuse bit */ + + islr = 0; + + if (!(hd & PREV_INUSE)) /* consolidate backward */ + { + prevsz = p->prev_size; + p = chunk_at_offset(p, -((long) prevsz)); + sz += prevsz; + + if (p->fd == last_remainder) /* keep as last_remainder */ + islr = 1; + else + unlink(p, bck, fwd); + } + + if (!(inuse_bit_at_offset(next, nextsz))) /* consolidate forward */ + { + sz += nextsz; + + if (!islr && next->fd == last_remainder) /* re-insert last_remainder */ + { + islr = 1; + link_last_remainder(p); + } + else + unlink(next, bck, fwd); + } + + + set_head(p, sz | PREV_INUSE); + set_foot(p, sz); + if (!islr) + frontlink(p, sz, idx, bck, fwd); +} + + + + + +/* + + Realloc algorithm: + + Chunks that were obtained via mmap cannot be extended or shrunk + unless HAVE_MREMAP is defined, in which case mremap is used. + Otherwise, if their reallocation is for additional space, they are + copied. If for less, they are just left alone. + + Otherwise, if the reallocation is for additional space, and the + chunk can be extended, it is, else a malloc-copy-free sequence is + taken. There are several different ways that a chunk could be + extended. All are tried: + + * Extending forward into following adjacent free chunk. + * Shifting backwards, joining preceding adjacent space + * Both shifting backwards and extending forward. + * Extending into newly sbrked space + + Unless the #define REALLOC_ZERO_BYTES_FREES is set, realloc with a + size argument of zero (re)allocates a minimum-sized chunk. + + If the reallocation is for less space, and the new request is for + a `small' (<512 bytes) size, then the newly unused space is lopped + off and freed. + + The old unix realloc convention of allowing the last-free'd chunk + to be used as an argument to realloc is no longer supported. + I don't know of any programs still relying on this feature, + and allowing it would also allow too many other incorrect + usages of realloc to be sensible. + + +*/ + + +#if __STD_C +Void_t* rEALLOc(Void_t* oldmem, size_t bytes) +#else +Void_t* rEALLOc(oldmem, bytes) Void_t* oldmem; size_t bytes; +#endif +{ + INTERNAL_SIZE_T nb; /* padded request size */ + + mchunkptr oldp; /* chunk corresponding to oldmem */ + INTERNAL_SIZE_T oldsize; /* its size */ + + mchunkptr newp; /* chunk to return */ + INTERNAL_SIZE_T newsize; /* its size */ + Void_t* newmem; /* corresponding user mem */ + + mchunkptr next; /* next contiguous chunk after oldp */ + INTERNAL_SIZE_T nextsize; /* its size */ + + mchunkptr prev; /* previous contiguous chunk before oldp */ + INTERNAL_SIZE_T prevsize; /* its size */ + + mchunkptr remainder; /* holds split off extra space from newp */ + INTERNAL_SIZE_T remainder_size; /* its size */ + + mchunkptr bck; /* misc temp for linking */ + mchunkptr fwd; /* misc temp for linking */ + +#ifdef REALLOC_ZERO_BYTES_FREES + if (bytes == 0) { fREe(oldmem); return 0; } +#endif + + if ((long)bytes < 0) return 0; + + /* realloc of null is supposed to be same as malloc */ + if (oldmem == 0) return mALLOc(bytes); + + newp = oldp = mem2chunk(oldmem); + newsize = oldsize = chunksize(oldp); + + + nb = request2size(bytes); + +#if HAVE_MMAP + if (chunk_is_mmapped(oldp)) + { +#if HAVE_MREMAP + newp = mremap_chunk(oldp, nb); + if(newp) return chunk2mem(newp); +#endif + /* Note the extra SIZE_SZ overhead. */ + if(oldsize - SIZE_SZ >= nb) return oldmem; /* do nothing */ + /* Must alloc, copy, free. */ + newmem = mALLOc(bytes); + if (newmem == 0) return 0; /* propagate failure */ + MALLOC_COPY(newmem, oldmem, oldsize - 2*SIZE_SZ); + munmap_chunk(oldp); + return newmem; + } +#endif + + check_inuse_chunk(oldp); + + if ((long)(oldsize) < (long)(nb)) + { + + /* Try expanding forward */ + + next = chunk_at_offset(oldp, oldsize); + if (next == top || !inuse(next)) + { + nextsize = chunksize(next); + + /* Forward into top only if a remainder */ + if (next == top) + { + if ((long)(nextsize + newsize) >= (long)(nb + MINSIZE)) + { + newsize += nextsize; + top = chunk_at_offset(oldp, nb); + set_head(top, (newsize - nb) | PREV_INUSE); + set_head_size(oldp, nb); + return chunk2mem(oldp); + } + } + + /* Forward into next chunk */ + else if (((long)(nextsize + newsize) >= (long)(nb))) + { + unlink(next, bck, fwd); + newsize += nextsize; + goto split; + } + } + else + { + next = 0; + nextsize = 0; + } + + /* Try shifting backwards. */ + + if (!prev_inuse(oldp)) + { + prev = prev_chunk(oldp); + prevsize = chunksize(prev); + + /* try forward + backward first to save a later consolidation */ + + if (next != 0) + { + /* into top */ + if (next == top) + { + if ((long)(nextsize + prevsize + newsize) >= (long)(nb + MINSIZE)) + { + unlink(prev, bck, fwd); + newp = prev; + newsize += prevsize + nextsize; + newmem = chunk2mem(newp); + MALLOC_COPY(newmem, oldmem, oldsize - SIZE_SZ); + top = chunk_at_offset(newp, nb); + set_head(top, (newsize - nb) | PREV_INUSE); + set_head_size(newp, nb); + return newmem; + } + } + + /* into next chunk */ + else if (((long)(nextsize + prevsize + newsize) >= (long)(nb))) + { + unlink(next, bck, fwd); + unlink(prev, bck, fwd); + newp = prev; + newsize += nextsize + prevsize; + newmem = chunk2mem(newp); + MALLOC_COPY(newmem, oldmem, oldsize - SIZE_SZ); + goto split; + } + } + + /* backward only */ + if (prev != 0 && (long)(prevsize + newsize) >= (long)nb) + { + unlink(prev, bck, fwd); + newp = prev; + newsize += prevsize; + newmem = chunk2mem(newp); + MALLOC_COPY(newmem, oldmem, oldsize - SIZE_SZ); + goto split; + } + } + + /* Must allocate */ + + newmem = mALLOc (bytes); + + if (newmem == 0) /* propagate failure */ + return 0; + + /* Avoid copy if newp is next chunk after oldp. */ + /* (This can only happen when new chunk is sbrk'ed.) */ + + if ( (newp = mem2chunk(newmem)) == next_chunk(oldp)) + { + newsize += chunksize(newp); + newp = oldp; + goto split; + } + + /* Otherwise copy, free, and exit */ + MALLOC_COPY(newmem, oldmem, oldsize - SIZE_SZ); + fREe(oldmem); + return newmem; + } + + + split: /* split off extra room in old or expanded chunk */ + + if (newsize - nb >= MINSIZE) /* split off remainder */ + { + remainder = chunk_at_offset(newp, nb); + remainder_size = newsize - nb; + set_head_size(newp, nb); + set_head(remainder, remainder_size | PREV_INUSE); + set_inuse_bit_at_offset(remainder, remainder_size); + fREe(chunk2mem(remainder)); /* let free() deal with it */ + } + else + { + set_head_size(newp, newsize); + set_inuse_bit_at_offset(newp, newsize); + } + + check_inuse_chunk(newp); + return chunk2mem(newp); +} + + + + +/* + + memalign algorithm: + + memalign requests more than enough space from malloc, finds a spot + within that chunk that meets the alignment request, and then + possibly frees the leading and trailing space. + + The alignment argument must be a power of two. This property is not + checked by memalign, so misuse may result in random runtime errors. + + 8-byte alignment is guaranteed by normal malloc calls, so don't + bother calling memalign with an argument of 8 or less. + + Overreliance on memalign is a sure way to fragment space. + +*/ + + +#if __STD_C +Void_t* mEMALIGn(size_t alignment, size_t bytes) +#else +Void_t* mEMALIGn(alignment, bytes) size_t alignment; size_t bytes; +#endif +{ + INTERNAL_SIZE_T nb; /* padded request size */ + char* m; /* memory returned by malloc call */ + mchunkptr p; /* corresponding chunk */ + char* brk; /* alignment point within p */ + mchunkptr newp; /* chunk to return */ + INTERNAL_SIZE_T newsize; /* its size */ + INTERNAL_SIZE_T leadsize; /* leading space befor alignment point */ + mchunkptr remainder; /* spare room at end to split off */ + long remainder_size; /* its size */ + + if ((long)bytes < 0) return 0; + + /* If need less alignment than we give anyway, just relay to malloc */ + + if (alignment <= MALLOC_ALIGNMENT) return mALLOc(bytes); + + /* Otherwise, ensure that it is at least a minimum chunk size */ + + if (alignment < MINSIZE) alignment = MINSIZE; + + /* Call malloc with worst case padding to hit alignment. */ + + nb = request2size(bytes); + m = (char*)(mALLOc(nb + alignment + MINSIZE)); + + if (m == 0) return 0; /* propagate failure */ + + p = mem2chunk(m); + + if ((((unsigned long)(m)) % alignment) == 0) /* aligned */ + { +#if HAVE_MMAP + if(chunk_is_mmapped(p)) + return chunk2mem(p); /* nothing more to do */ +#endif + } + else /* misaligned */ + { + /* + Find an aligned spot inside chunk. + Since we need to give back leading space in a chunk of at + least MINSIZE, if the first calculation places us at + a spot with less than MINSIZE leader, we can move to the + next aligned spot -- we've allocated enough total room so that + this is always possible. + */ + + brk = (char*)mem2chunk(((unsigned long)(m + alignment - 1)) & -((signed) alignment)); + if ((long)(brk - (char*)(p)) < MINSIZE) brk = brk + alignment; + + newp = (mchunkptr)brk; + leadsize = brk - (char*)(p); + newsize = chunksize(p) - leadsize; + +#if HAVE_MMAP + if(chunk_is_mmapped(p)) + { + newp->prev_size = p->prev_size + leadsize; + set_head(newp, newsize|IS_MMAPPED); + return chunk2mem(newp); + } +#endif + + /* give back leader, use the rest */ + + set_head(newp, newsize | PREV_INUSE); + set_inuse_bit_at_offset(newp, newsize); + set_head_size(p, leadsize); + fREe(chunk2mem(p)); + p = newp; + + assert (newsize >= nb && (((unsigned long)(chunk2mem(p))) % alignment) == 0); + } + + /* Also give back spare room at the end */ + + remainder_size = chunksize(p) - nb; + + if (remainder_size >= (long)MINSIZE) + { + remainder = chunk_at_offset(p, nb); + set_head(remainder, remainder_size | PREV_INUSE); + set_head_size(p, nb); + fREe(chunk2mem(remainder)); + } + + check_inuse_chunk(p); + return chunk2mem(p); + +} + + + + +/* + valloc just invokes memalign with alignment argument equal + to the page size of the system (or as near to this as can + be figured out from all the includes/defines above.) +*/ + +#if __STD_C +Void_t* vALLOc(size_t bytes) +#else +Void_t* vALLOc(bytes) size_t bytes; +#endif +{ + return mEMALIGn (malloc_getpagesize, bytes); +} + +/* + pvalloc just invokes valloc for the nearest pagesize + that will accommodate request +*/ + + +#if __STD_C +Void_t* pvALLOc(size_t bytes) +#else +Void_t* pvALLOc(bytes) size_t bytes; +#endif +{ + size_t pagesize = malloc_getpagesize; + return mEMALIGn (pagesize, (bytes + pagesize - 1) & ~(pagesize - 1)); +} + +/* + + calloc calls malloc, then zeroes out the allocated chunk. + +*/ + +#if __STD_C +Void_t* cALLOc(size_t n, size_t elem_size) +#else +Void_t* cALLOc(n, elem_size) size_t n; size_t elem_size; +#endif +{ + mchunkptr p; + INTERNAL_SIZE_T csz; + + INTERNAL_SIZE_T sz = n * elem_size; + + + /* check if expand_top called, in which case don't need to clear */ +#if MORECORE_CLEARS + mchunkptr oldtop = top; + INTERNAL_SIZE_T oldtopsize = chunksize(top); +#endif + Void_t* mem = mALLOc (sz); + + if ((long)n < 0) return 0; + + if (mem == 0) + return 0; + else + { + p = mem2chunk(mem); + + /* Two optional cases in which clearing not necessary */ + + +#if HAVE_MMAP + if (chunk_is_mmapped(p)) return mem; +#endif + + csz = chunksize(p); + +#if MORECORE_CLEARS + if (p == oldtop && csz > oldtopsize) + { + /* clear only the bytes from non-freshly-sbrked memory */ + csz = oldtopsize; + } +#endif + + MALLOC_ZERO(mem, csz - SIZE_SZ); + return mem; + } +} + +/* + + cfree just calls free. It is needed/defined on some systems + that pair it with calloc, presumably for odd historical reasons. + +*/ + +#if !defined(INTERNAL_LINUX_C_LIB) || !defined(__ELF__) +#if __STD_C +void cfree(Void_t *mem) +#else +void cfree(mem) Void_t *mem; +#endif +{ + fREe(mem); +} +#endif + + + +/* + + Malloc_trim gives memory back to the system (via negative + arguments to sbrk) if there is unused memory at the `high' end of + the malloc pool. You can call this after freeing large blocks of + memory to potentially reduce the system-level memory requirements + of a program. However, it cannot guarantee to reduce memory. Under + some allocation patterns, some large free blocks of memory will be + locked between two used chunks, so they cannot be given back to + the system. + + The `pad' argument to malloc_trim represents the amount of free + trailing space to leave untrimmed. If this argument is zero, + only the minimum amount of memory to maintain internal data + structures will be left (one page or less). Non-zero arguments + can be supplied to maintain enough trailing space to service + future expected allocations without having to re-obtain memory + from the system. + + Malloc_trim returns 1 if it actually released any memory, else 0. + +*/ + +#if __STD_C +int malloc_trim(size_t pad) +#else +int malloc_trim(pad) size_t pad; +#endif +{ + long top_size; /* Amount of top-most memory */ + long extra; /* Amount to release */ + char* current_brk; /* address returned by pre-check sbrk call */ + char* new_brk; /* address returned by negative sbrk call */ + + unsigned long pagesz = malloc_getpagesize; + + top_size = chunksize(top); + extra = ((top_size - pad - MINSIZE + (pagesz-1)) / pagesz - 1) * pagesz; + + if (extra < (long)pagesz) /* Not enough memory to release */ + return 0; + + else + { + /* Test to make sure no one else called sbrk */ + current_brk = (char*)(MORECORE (0)); + if (current_brk != (char*)(top) + top_size) + return 0; /* Apparently we don't own memory; must fail */ + + else + { + new_brk = (char*)(MORECORE (-extra)); + + if (new_brk == (char*)(MORECORE_FAILURE)) /* sbrk failed? */ + { + /* Try to figure out what we have */ + current_brk = (char*)(MORECORE (0)); + top_size = current_brk - (char*)top; + if (top_size >= (long)MINSIZE) /* if not, we are very very dead! */ + { + sbrked_mem = current_brk - sbrk_base; + set_head(top, top_size | PREV_INUSE); + } + check_chunk(top); + return 0; + } + + else + { + /* Success. Adjust top accordingly. */ + set_head(top, (top_size - extra) | PREV_INUSE); + sbrked_mem -= extra; + check_chunk(top); + return 1; + } + } + } +} + + + +/* + malloc_usable_size: + + This routine tells you how many bytes you can actually use in an + allocated chunk, which may be more than you requested (although + often not). You can use this many bytes without worrying about + overwriting other allocated objects. Not a particularly great + programming practice, but still sometimes useful. + +*/ + +#if __STD_C +size_t malloc_usable_size(Void_t* mem) +#else +size_t malloc_usable_size(mem) Void_t* mem; +#endif +{ + mchunkptr p; + if (mem == 0) + return 0; + else + { + p = mem2chunk(mem); + if(!chunk_is_mmapped(p)) + { + if (!inuse(p)) return 0; + check_inuse_chunk(p); + return chunksize(p) - SIZE_SZ; + } + return chunksize(p) - 2*SIZE_SZ; + } +} + + + + +/* Utility to update current_mallinfo for malloc_stats and mallinfo() */ + +static void malloc_update_mallinfo() +{ + int i; + mbinptr b; + mchunkptr p; +#if DEBUG + mchunkptr q; +#endif + + INTERNAL_SIZE_T avail = chunksize(top); + int navail = ((long)(avail) >= (long)MINSIZE)? 1 : 0; + + for (i = 1; i < NAV; ++i) + { + b = bin_at(i); + for (p = last(b); p != b; p = p->bk) + { +#if DEBUG + check_free_chunk(p); + for (q = next_chunk(p); + q < top && inuse(q) && (long)(chunksize(q)) >= (long)MINSIZE; + q = next_chunk(q)) + check_inuse_chunk(q); +#endif + avail += chunksize(p); + navail++; + } + } + + current_mallinfo.ordblks = navail; + current_mallinfo.uordblks = sbrked_mem - avail; + current_mallinfo.fordblks = avail; + current_mallinfo.hblks = n_mmaps; + current_mallinfo.hblkhd = mmapped_mem; + current_mallinfo.keepcost = chunksize(top); + +} + + + +/* + + malloc_stats: + + Prints on stderr the amount of space obtain from the system (both + via sbrk and mmap), the maximum amount (which may be more than + current if malloc_trim and/or munmap got called), the maximum + number of simultaneous mmap regions used, and the current number + of bytes allocated via malloc (or realloc, etc) but not yet + freed. (Note that this is the number of bytes allocated, not the + number requested. It will be larger than the number requested + because of alignment and bookkeeping overhead.) + +*/ + +void malloc_stats() +{ + malloc_update_mallinfo(); + fprintf(stderr, "max system bytes = %10u\n", + (unsigned int)(max_total_mem)); + fprintf(stderr, "system bytes = %10u\n", + (unsigned int)(sbrked_mem + mmapped_mem)); + fprintf(stderr, "in use bytes = %10u\n", + (unsigned int)(current_mallinfo.uordblks + mmapped_mem)); +#if HAVE_MMAP + fprintf(stderr, "max mmap regions = %10u\n", + (unsigned int)max_n_mmaps); +#endif +} + +/* + mallinfo returns a copy of updated current mallinfo. +*/ + +struct mallinfo mALLINFo() +{ + malloc_update_mallinfo(); + return current_mallinfo; +} + + + + +/* + mallopt: + + mallopt is the general SVID/XPG interface to tunable parameters. + The format is to provide a (parameter-number, parameter-value) pair. + mallopt then sets the corresponding parameter to the argument + value if it can (i.e., so long as the value is meaningful), + and returns 1 if successful else 0. + + See descriptions of tunable parameters above. + +*/ + +#if __STD_C +int mALLOPt(int param_number, int value) +#else +int mALLOPt(param_number, value) int param_number; int value; +#endif +{ + switch(param_number) + { + case M_TRIM_THRESHOLD: + trim_threshold = value; return 1; + case M_TOP_PAD: + top_pad = value; return 1; + case M_MMAP_THRESHOLD: + mmap_threshold = value; return 1; + case M_MMAP_MAX: +#if HAVE_MMAP + n_mmaps_max = value; return 1; +#else + if (value != 0) return 0; else n_mmaps_max = value; return 1; +#endif + + default: + return 0; + } +} + +/* + +History: + + V2.6.6 Sun Dec 5 07:42:19 1999 Doug Lea (dl at gee) + * return null for negative arguments + * Added Several WIN32 cleanups from Martin C. Fong <mcfong@yahoo.com> + * Add 'LACKS_SYS_PARAM_H' for those systems without 'sys/param.h' + (e.g. WIN32 platforms) + * Cleanup up header file inclusion for WIN32 platforms + * Cleanup code to avoid Microsoft Visual C++ compiler complaints + * Add 'USE_DL_PREFIX' to quickly allow co-existence with existing + memory allocation routines + * Set 'malloc_getpagesize' for WIN32 platforms (needs more work) + * Use 'assert' rather than 'ASSERT' in WIN32 code to conform to + usage of 'assert' in non-WIN32 code + * Improve WIN32 'sbrk()' emulation's 'findRegion()' routine to + avoid infinite loop + * Always call 'fREe()' rather than 'free()' + + V2.6.5 Wed Jun 17 15:57:31 1998 Doug Lea (dl at gee) + * Fixed ordering problem with boundary-stamping + + V2.6.3 Sun May 19 08:17:58 1996 Doug Lea (dl at gee) + * Added pvalloc, as recommended by H.J. Liu + * Added 64bit pointer support mainly from Wolfram Gloger + * Added anonymously donated WIN32 sbrk emulation + * Malloc, calloc, getpagesize: add optimizations from Raymond Nijssen + * malloc_extend_top: fix mask error that caused wastage after + foreign sbrks + * Add linux mremap support code from HJ Liu + + V2.6.2 Tue Dec 5 06:52:55 1995 Doug Lea (dl at gee) + * Integrated most documentation with the code. + * Add support for mmap, with help from + Wolfram Gloger (Gloger@lrz.uni-muenchen.de). + * Use last_remainder in more cases. + * Pack bins using idea from colin@nyx10.cs.du.edu + * Use ordered bins instead of best-fit threshhold + * Eliminate block-local decls to simplify tracing and debugging. + * Support another case of realloc via move into top + * Fix error occuring when initial sbrk_base not word-aligned. + * Rely on page size for units instead of SBRK_UNIT to + avoid surprises about sbrk alignment conventions. + * Add mallinfo, mallopt. Thanks to Raymond Nijssen + (raymond@es.ele.tue.nl) for the suggestion. + * Add `pad' argument to malloc_trim and top_pad mallopt parameter. + * More precautions for cases where other routines call sbrk, + courtesy of Wolfram Gloger (Gloger@lrz.uni-muenchen.de). + * Added macros etc., allowing use in linux libc from + H.J. Lu (hjl@gnu.ai.mit.edu) + * Inverted this history list + + V2.6.1 Sat Dec 2 14:10:57 1995 Doug Lea (dl at gee) + * Re-tuned and fixed to behave more nicely with V2.6.0 changes. + * Removed all preallocation code since under current scheme + the work required to undo bad preallocations exceeds + the work saved in good cases for most test programs. + * No longer use return list or unconsolidated bins since + no scheme using them consistently outperforms those that don't + given above changes. + * Use best fit for very large chunks to prevent some worst-cases. + * Added some support for debugging + + V2.6.0 Sat Nov 4 07:05:23 1995 Doug Lea (dl at gee) + * Removed footers when chunks are in use. Thanks to + Paul Wilson (wilson@cs.texas.edu) for the suggestion. + + V2.5.4 Wed Nov 1 07:54:51 1995 Doug Lea (dl at gee) + * Added malloc_trim, with help from Wolfram Gloger + (wmglo@Dent.MED.Uni-Muenchen.DE). + + V2.5.3 Tue Apr 26 10:16:01 1994 Doug Lea (dl at g) + + V2.5.2 Tue Apr 5 16:20:40 1994 Doug Lea (dl at g) + * realloc: try to expand in both directions + * malloc: swap order of clean-bin strategy; + * realloc: only conditionally expand backwards + * Try not to scavenge used bins + * Use bin counts as a guide to preallocation + * Occasionally bin return list chunks in first scan + * Add a few optimizations from colin@nyx10.cs.du.edu + + V2.5.1 Sat Aug 14 15:40:43 1993 Doug Lea (dl at g) + * faster bin computation & slightly different binning + * merged all consolidations to one part of malloc proper + (eliminating old malloc_find_space & malloc_clean_bin) + * Scan 2 returns chunks (not just 1) + * Propagate failure in realloc if malloc returns 0 + * Add stuff to allow compilation on non-ANSI compilers + from kpv@research.att.com + + V2.5 Sat Aug 7 07:41:59 1993 Doug Lea (dl at g.oswego.edu) + * removed potential for odd address access in prev_chunk + * removed dependency on getpagesize.h + * misc cosmetics and a bit more internal documentation + * anticosmetics: mangled names in macros to evade debugger strangeness + * tested on sparc, hp-700, dec-mips, rs6000 + with gcc & native cc (hp, dec only) allowing + Detlefs & Zorn comparison study (in SIGPLAN Notices.) + + Trial version Fri Aug 28 13:14:29 1992 Doug Lea (dl at g.oswego.edu) + * Based loosely on libg++-1.2X malloc. (It retains some of the overall + structure of old version, but most details differ.) + +*/ diff --git a/qemu/roms/u-boot/common/edid.c b/qemu/roms/u-boot/common/edid.c new file mode 100644 index 000000000..e66108f24 --- /dev/null +++ b/qemu/roms/u-boot/common/edid.c @@ -0,0 +1,290 @@ +/* + * Copyright (c) 2012 The Chromium OS Authors. + * + * (C) Copyright 2010 + * Petr Stetiar <ynezz@true.cz> + * + * SPDX-License-Identifier: GPL-2.0+ + * + * Contains stolen code from ddcprobe project which is: + * Copyright (C) Nalin Dahyabhai <bigfun@pobox.com> + */ + +#include <common.h> +#include <edid.h> +#include <linux/ctype.h> +#include <linux/string.h> + +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/qemu/roms/u-boot/common/env_attr.c b/qemu/roms/u-boot/common/env_attr.c new file mode 100644 index 000000000..64baca5a1 --- /dev/null +++ b/qemu/roms/u-boot/common/env_attr.c @@ -0,0 +1,213 @@ +/* + * (C) Copyright 2012 + * Joe Hershberger, National Instruments, joe.hershberger@ni.com + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifdef USE_HOSTCC /* Eliminate "ANSI does not permit..." warnings */ +#include <stdint.h> +#include <stdio.h> +#include <linux/linux_string.h> +#else +#include <common.h> +#endif + +#include <env_attr.h> +#include <errno.h> +#include <linux/string.h> +#include <malloc.h> + +/* + * 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/qemu/roms/u-boot/common/env_callback.c b/qemu/roms/u-boot/common/env_callback.c new file mode 100644 index 000000000..d03fa03a4 --- /dev/null +++ b/qemu/roms/u-boot/common/env_callback.c @@ -0,0 +1,135 @@ +/* + * (C) Copyright 2012 + * Joe Hershberger, National Instruments, joe.hershberger@ni.com + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <environment.h> + +#if defined(CONFIG_NEEDS_MANUAL_RELOC) +DECLARE_GLOBAL_DATA_PTR; +#endif + +/* + * Look up a callback function pointer by name + */ +static 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; +} + +static int first_call = 1; +static const char *callback_list; + +/* + * 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; + char callback_name[256] = ""; + struct env_clbk_tbl *clbkp; + int ret = 1; + + if (first_call) { + callback_list = getenv(ENV_CALLBACK_VAR); + first_call = 0; + } + + /* 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/qemu/roms/u-boot/common/env_common.c b/qemu/roms/u-boot/common/env_common.c new file mode 100644 index 000000000..cd7b4cd1d --- /dev/null +++ b/qemu/roms/u-boot/common/env_common.c @@ -0,0 +1,303 @@ +/* + * (C) Copyright 2000-2010 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * (C) Copyright 2001 Sysgo Real-Time Solutions, GmbH <www.elinos.com> + * Andreas Heppel <aheppel@sysgo.de> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <command.h> +#include <environment.h> +#include <linux/stddef.h> +#include <search.h> +#include <errno.h> +#include <malloc.h> + +DECLARE_GLOBAL_DATA_PTR; + +/************************************************************************ + * Default settings to be used when no valid environment is found + */ +#include <env_default.h> + +struct hsearch_data env_htab = { + .change_ok = env_flags_validate, +}; + +static uchar __env_get_char_spec(int index) +{ + return *((uchar *)(gd->env_addr + index)); +} +uchar env_get_char_spec(int) + __attribute__((weak, alias("__env_get_char_spec"))); + +static uchar env_get_char_init(int index) +{ + /* if crc was bad, use the default environment */ + if (gd->env_valid) + return env_get_char_spec(index); + else + return default_environment[index]; +} + +uchar env_get_char_memory(int index) +{ + return *env_get_addr(index); +} + +uchar env_get_char(int index) +{ + /* if relocated to RAM */ + if (gd->flags & GD_FLG_RELOC) + return env_get_char_memory(index); + else + return env_get_char_init(index); +} + +const uchar *env_get_addr(int index) +{ + if (gd->env_valid) + return (uchar *)(gd->env_addr + index); + else + 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; +} + +/* + * 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; + + if (sizeof(default_environment) > ENV_SIZE) { + puts("*** Error - default environment is too large\n\n"); + return; + } + + if (s) { + if (*s == '!') { + printf("*** Warning - %s, " + "using default environment\n\n", + s + 1); + } else { + flags = H_INTERACTIVE; + puts(s); + } + } else { + puts("Using default environment\n\n"); + } + + if (himport_r(&env_htab, (char *)default_environment, + sizeof(default_environment), '\0', flags, + 0, NULL) == 0) + error("Environment import failed: errno = %d\n", errno); + + gd->flags |= GD_FLG_ENV_READY; +} + + +/* [re]set individual variables to their value in the default environment */ +int set_default_vars(int nvars, char * const vars[]) +{ + /* + * Special use-case: import from default environment + * (and use \0 as a separator) + */ + return himport_r(&env_htab, (const char *)default_environment, + sizeof(default_environment), '\0', + H_NOCLEAR | H_INTERACTIVE, nvars, vars); +} + +#ifdef CONFIG_ENV_AES +#include <aes.h> +/** + * env_aes_cbc_get_key() - Get AES-128-CBC key for the environment + * + * This function shall return 16-byte array containing AES-128 key used + * to encrypt and decrypt the environment. This function must be overriden + * by the implementer as otherwise the environment encryption will not + * work. + */ +__weak uint8_t *env_aes_cbc_get_key(void) +{ + return NULL; +} + +static int env_aes_cbc_crypt(env_t *env, const int enc) +{ + unsigned char *data = env->data; + uint8_t *key; + uint8_t key_exp[AES_EXPAND_KEY_LENGTH]; + uint32_t aes_blocks; + + key = env_aes_cbc_get_key(); + if (!key) + return -EINVAL; + + /* First we expand the key. */ + aes_expand_key(key, key_exp); + + /* Calculate the number of AES blocks to encrypt. */ + aes_blocks = ENV_SIZE / AES_KEY_LENGTH; + + if (enc) + aes_cbc_encrypt_blocks(key_exp, data, data, aes_blocks); + else + aes_cbc_decrypt_blocks(key_exp, data, data, aes_blocks); + + return 0; +} +#else +static inline int env_aes_cbc_crypt(env_t *env, const int enc) +{ + return 0; +} +#endif + +/* + * Check if CRC is valid and (if yes) import the environment. + * Note that "buf" may or may not be aligned. + */ +int env_import(const char *buf, int check) +{ + env_t *ep = (env_t *)buf; + int ret; + + if (check) { + uint32_t crc; + + memcpy(&crc, &ep->crc, sizeof(crc)); + + if (crc32(0, ep->data, ENV_SIZE) != crc) { + set_default_env("!bad CRC"); + return 0; + } + } + + /* Decrypt the env if desired. */ + ret = env_aes_cbc_crypt(ep, 0); + if (ret) { + error("Failed to decrypt env!\n"); + set_default_env("!import failed"); + return ret; + } + + if (himport_r(&env_htab, (char *)ep->data, ENV_SIZE, '\0', 0, + 0, NULL)) { + gd->flags |= GD_FLG_ENV_READY; + return 1; + } + + error("Cannot import environment: errno = %d\n", errno); + + set_default_env("!import failed"); + + return 0; +} + +/* Emport the environment and generate CRC for it. */ +int env_export(env_t *env_out) +{ + char *res; + ssize_t len; + int ret; + + res = (char *)env_out->data; + 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; + } + + /* Encrypt the env if desired. */ + ret = env_aes_cbc_crypt(env_out, 1); + if (ret) + return ret; + + env_out->crc = crc32(0, env_out->data, ENV_SIZE); + + return 0; +} + +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) + /* Environment not changable */ + set_default_env(NULL); +#else + bootstage_error(BOOTSTAGE_ID_NET_CHECKSUM); + set_default_env("!bad CRC"); +#endif + } else { + env_relocate_spec(); + } +} + +#if defined(CONFIG_AUTO_COMPLETE) && !defined(CONFIG_SPL_BUILD) +int env_complete(char *var, int maxv, char *cmdv[], int bufsz, char *buf) +{ + ENTRY *match; + int found, idx; + + idx = 0; + found = 0; + cmdv[0] = NULL; + + while ((idx = hmatch_r(var, idx, &match, &env_htab))) { + int vallen = strlen(match->key) + 1; + + if (found >= maxv - 2 || bufsz < vallen) + break; + + cmdv[found++] = buf; + memcpy(buf, match->key, vallen); + buf += vallen; + bufsz -= vallen; + } + + qsort(cmdv, found, sizeof(cmdv[0]), strcmp_compar); + + if (idx) + cmdv[found++] = "..."; + + cmdv[found] = NULL; + return found; +} +#endif diff --git a/qemu/roms/u-boot/common/env_dataflash.c b/qemu/roms/u-boot/common/env_dataflash.c new file mode 100644 index 000000000..034e32316 --- /dev/null +++ b/qemu/roms/u-boot/common/env_dataflash.c @@ -0,0 +1,84 @@ +/* + * LowLevel function for DataFlash environment support + * Author : Gilles Gastaldi (Atmel) + * + * SPDX-License-Identifier: GPL-2.0+ + */ +#include <common.h> +#include <command.h> +#include <environment.h> +#include <linux/stddef.h> +#include <dataflash.h> +#include <search.h> +#include <errno.h> + +DECLARE_GLOBAL_DATA_PTR; + +env_t *env_ptr; + +char *env_name_spec = "dataflash"; + +uchar env_get_char_spec(int index) +{ + uchar c; + + read_dataflash(CONFIG_ENV_ADDR + index + offsetof(env_t, data), + 1, (char *)&c); + return c; +} + +void env_relocate_spec(void) +{ + ulong crc, new = 0; + unsigned off; + char buf[CONFIG_ENV_SIZE]; + + /* Read old CRC */ + read_dataflash(CONFIG_ENV_ADDR + offsetof(env_t, crc), + sizeof(ulong), (char *)&crc); + + /* Read whole environment */ + read_dataflash(CONFIG_ENV_ADDR, CONFIG_ENV_SIZE, buf); + + /* Calculate the CRC */ + off = offsetof(env_t, data); + new = crc32(new, (unsigned char *)(buf + off), ENV_SIZE); + + if (crc == new) + env_import(buf, 1); + else + set_default_env("!bad CRC"); +} + +#ifdef CONFIG_ENV_OFFSET_REDUND +#error No support for redundant environment on dataflash yet! +#endif + +int saveenv(void) +{ + env_t env_new; + int ret; + + ret = env_export(&env_new); + if (ret) + return ret; + + return write_dataflash(CONFIG_ENV_ADDR, + (unsigned long)&env_new, + CONFIG_ENV_SIZE); +} + +/* + * Initialize environment use + * + * We are still running from ROM, so data use is limited. + * Use a (moderately small) buffer on the stack + */ +int env_init(void) +{ + /* use default */ + gd->env_addr = (ulong)&default_environment[0]; + gd->env_valid = 1; + + return 0; +} diff --git a/qemu/roms/u-boot/common/env_eeprom.c b/qemu/roms/u-boot/common/env_eeprom.c new file mode 100644 index 000000000..490ac731b --- /dev/null +++ b/qemu/roms/u-boot/common/env_eeprom.c @@ -0,0 +1,256 @@ +/* + * (C) Copyright 2000-2010 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * (C) Copyright 2001 Sysgo Real-Time Solutions, GmbH <www.elinos.com> + * Andreas Heppel <aheppel@sysgo.de> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <command.h> +#include <environment.h> +#include <linux/stddef.h> +#if defined(CONFIG_I2C_ENV_EEPROM_BUS) +#include <i2c.h> +#endif +#include <search.h> +#include <errno.h> +#include <linux/compiler.h> /* for BUG_ON */ + +DECLARE_GLOBAL_DATA_PTR; + +env_t *env_ptr; + +char *env_name_spec = "EEPROM"; + +static int eeprom_bus_read(unsigned dev_addr, unsigned offset, + uchar *buffer, unsigned cnt) +{ + int rcode; +#if defined(CONFIG_I2C_ENV_EEPROM_BUS) + int old_bus = i2c_get_bus_num(); + + if (old_bus != CONFIG_I2C_ENV_EEPROM_BUS) + i2c_set_bus_num(CONFIG_I2C_ENV_EEPROM_BUS); +#endif + + rcode = eeprom_read(dev_addr, offset, buffer, cnt); + +#if defined(CONFIG_I2C_ENV_EEPROM_BUS) + i2c_set_bus_num(old_bus); +#endif + + return rcode; +} + +static int eeprom_bus_write(unsigned dev_addr, unsigned offset, + uchar *buffer, unsigned cnt) +{ + int rcode; +#if defined(CONFIG_I2C_ENV_EEPROM_BUS) + int old_bus = i2c_get_bus_num(); + + if (old_bus != CONFIG_I2C_ENV_EEPROM_BUS) + i2c_set_bus_num(CONFIG_I2C_ENV_EEPROM_BUS); +#endif + + rcode = eeprom_write(dev_addr, offset, buffer, cnt); + +#if defined(CONFIG_I2C_ENV_EEPROM_BUS) + i2c_set_bus_num(old_bus); +#endif + + return rcode; +} + +uchar env_get_char_spec(int index) +{ + uchar c; + unsigned int off = CONFIG_ENV_OFFSET; + +#ifdef CONFIG_ENV_OFFSET_REDUND + if (gd->env_valid == 2) + off = CONFIG_ENV_OFFSET_REDUND; +#endif + eeprom_bus_read(CONFIG_SYS_DEF_EEPROM_ADDR, + off + index + offsetof(env_t, data), &c, 1); + + return c; +} + +void env_relocate_spec(void) +{ + char buf[CONFIG_ENV_SIZE]; + unsigned int off = CONFIG_ENV_OFFSET; + +#ifdef CONFIG_ENV_OFFSET_REDUND + if (gd->env_valid == 2) + off = CONFIG_ENV_OFFSET_REDUND; +#endif + eeprom_bus_read(CONFIG_SYS_DEF_EEPROM_ADDR, + off, (uchar *)buf, CONFIG_ENV_SIZE); + + env_import(buf, 1); +} + +int saveenv(void) +{ + env_t env_new; + int rc; + unsigned int off = CONFIG_ENV_OFFSET; +#ifdef CONFIG_ENV_OFFSET_REDUND + unsigned int off_red = CONFIG_ENV_OFFSET_REDUND; + char flag_obsolete = OBSOLETE_FLAG; +#endif + + BUG_ON(env_ptr != NULL); + + rc = env_export(&env_new); + if (rc) + return rc; + +#ifdef CONFIG_ENV_OFFSET_REDUND + if (gd->env_valid == 1) { + off = CONFIG_ENV_OFFSET_REDUND; + off_red = CONFIG_ENV_OFFSET; + } + + env_new.flags = ACTIVE_FLAG; +#endif + + rc = eeprom_bus_write(CONFIG_SYS_DEF_EEPROM_ADDR, + off, (uchar *)&env_new, CONFIG_ENV_SIZE); + +#ifdef CONFIG_ENV_OFFSET_REDUND + if (rc == 0) { + eeprom_bus_write(CONFIG_SYS_DEF_EEPROM_ADDR, + off_red + offsetof(env_t, flags), + (uchar *)&flag_obsolete, 1); + + if (gd->env_valid == 1) + gd->env_valid = 2; + else + gd->env_valid = 1; + } +#endif + return rc; +} + +/* + * Initialize Environment use + * + * We are still running from ROM, so data use is limited. + * Use a (moderately small) buffer on the stack + */ +#ifdef CONFIG_ENV_OFFSET_REDUND +int env_init(void) +{ + ulong len, crc[2], crc_tmp; + unsigned int off, off_env[2]; + uchar buf[64], flags[2]; + int i, crc_ok[2] = {0, 0}; + + eeprom_init(); /* prepare for EEPROM read/write */ + + off_env[0] = CONFIG_ENV_OFFSET; + off_env[1] = CONFIG_ENV_OFFSET_REDUND; + + for (i = 0; i < 2; i++) { + /* read CRC */ + eeprom_bus_read(CONFIG_SYS_DEF_EEPROM_ADDR, + off_env[i] + offsetof(env_t, crc), + (uchar *)&crc[i], sizeof(ulong)); + /* read FLAGS */ + eeprom_bus_read(CONFIG_SYS_DEF_EEPROM_ADDR, + off_env[i] + offsetof(env_t, flags), + (uchar *)&flags[i], sizeof(uchar)); + + crc_tmp = 0; + len = ENV_SIZE; + off = off_env[i] + offsetof(env_t, data); + while (len > 0) { + int n = (len > sizeof(buf)) ? sizeof(buf) : len; + + eeprom_bus_read(CONFIG_SYS_DEF_EEPROM_ADDR, off, + buf, n); + + crc_tmp = crc32(crc_tmp, buf, n); + len -= n; + off += n; + } + + if (crc_tmp == crc[i]) + crc_ok[i] = 1; + } + + if (!crc_ok[0] && !crc_ok[1]) { + gd->env_addr = 0; + gd->env_valid = 0; + + return 0; + } else if (crc_ok[0] && !crc_ok[1]) { + gd->env_valid = 1; + } else if (!crc_ok[0] && crc_ok[1]) { + gd->env_valid = 2; + } else { + /* both ok - check serial */ + if (flags[0] == ACTIVE_FLAG && flags[1] == OBSOLETE_FLAG) + gd->env_valid = 1; + else if (flags[0] == OBSOLETE_FLAG && flags[1] == ACTIVE_FLAG) + gd->env_valid = 2; + else if (flags[0] == 0xFF && flags[1] == 0) + gd->env_valid = 2; + else if (flags[1] == 0xFF && flags[0] == 0) + gd->env_valid = 1; + else /* flags are equal - almost impossible */ + gd->env_valid = 1; + } + + if (gd->env_valid == 2) + gd->env_addr = off_env[1] + offsetof(env_t, data); + else if (gd->env_valid == 1) + gd->env_addr = off_env[0] + offsetof(env_t, data); + + return 0; +} +#else +int env_init(void) +{ + ulong crc, len, new; + unsigned off; + uchar buf[64]; + + eeprom_init(); /* prepare for EEPROM read/write */ + + /* read old CRC */ + eeprom_bus_read(CONFIG_SYS_DEF_EEPROM_ADDR, + CONFIG_ENV_OFFSET + offsetof(env_t, crc), + (uchar *)&crc, sizeof(ulong)); + + new = 0; + len = ENV_SIZE; + off = offsetof(env_t, data); + + while (len > 0) { + int n = (len > sizeof(buf)) ? sizeof(buf) : len; + + eeprom_bus_read(CONFIG_SYS_DEF_EEPROM_ADDR, + CONFIG_ENV_OFFSET + off, buf, n); + new = crc32(new, buf, n); + len -= n; + off += n; + } + + if (crc == new) { + gd->env_addr = offsetof(env_t, data); + gd->env_valid = 1; + } else { + gd->env_addr = 0; + gd->env_valid = 0; + } + + return 0; +} +#endif diff --git a/qemu/roms/u-boot/common/env_embedded.c b/qemu/roms/u-boot/common/env_embedded.c new file mode 100644 index 000000000..1c4f915b7 --- /dev/null +++ b/qemu/roms/u-boot/common/env_embedded.c @@ -0,0 +1,104 @@ +/* + * (C) Copyright 2001 + * Erik Theisen, Wave 7 Optics, etheisen@mindspring.com. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef __ASSEMBLY__ +#define __ASSEMBLY__ /* Dirty trick to get only #defines */ +#endif +#define __ASM_STUB_PROCESSOR_H__ /* don't include asm/processor. */ +#include <config.h> +#undef __ASSEMBLY__ +#include <environment.h> +#include <linux/stringify.h> + +/* Handle HOSTS that have prepended crap on symbol names, not TARGETS. */ +#if defined(__APPLE__) +/* Leading underscore on symbols */ +# define SYM_CHAR "_" +#else /* No leading character on symbols */ +# define SYM_CHAR +#endif + +/* + * Generate embedded environment table + * inside U-Boot image, if needed. + */ +#if defined(ENV_IS_EMBEDDED) || defined(CONFIG_BUILD_ENVCRC) +/* + * Only put the environment in it's own section when we are building + * U-Boot proper. The host based program "tools/envcrc" does not need + * a seperate section. Note that ENV_CRC is only defined when building + * U-Boot itself. + */ +#if (defined(CONFIG_SYS_USE_PPCENV) || defined(CONFIG_NAND_U_BOOT)) && \ + defined(ENV_CRC) /* Environment embedded in U-Boot .ppcenv section */ +/* XXX - This only works with GNU C */ +# define __PPCENV__ __attribute__ ((section(".ppcenv"))) +# define __PPCTEXT__ __attribute__ ((section(".text"))) + +#elif defined(USE_HOSTCC) /* Native for 'tools/envcrc' */ +# define __PPCENV__ /*XXX DO_NOT_DEL_THIS_COMMENT*/ +# define __PPCTEXT__ /*XXX DO_NOT_DEL_THIS_COMMENT*/ + +#else /* Environment is embedded in U-Boot's .text section */ +/* XXX - This only works with GNU C */ +# define __PPCENV__ __attribute__ ((section(".text"))) +# define __PPCTEXT__ __attribute__ ((section(".text"))) +#endif + +/* + * Macros to generate global absolutes. + */ +#if defined(__bfin__) +# define GEN_SET_VALUE(name, value) \ + asm(".set " GEN_SYMNAME(name) ", " GEN_VALUE(value)) +#else +# define GEN_SET_VALUE(name, value) \ + asm(GEN_SYMNAME(name) " = " GEN_VALUE(value)) +#endif +#define GEN_SYMNAME(str) SYM_CHAR #str +#define GEN_VALUE(str) #str +#define GEN_ABS(name, value) \ + asm(".globl " GEN_SYMNAME(name)); \ + GEN_SET_VALUE(name, value) + +/* + * Check to see if we are building with a + * computed CRC. Otherwise define it as ~0. + */ +#if !defined(ENV_CRC) +# define ENV_CRC (~0) +#endif + +#define DEFAULT_ENV_INSTANCE_EMBEDDED +#include <env_default.h> + +#ifdef CONFIG_ENV_ADDR_REDUND +env_t redundand_environment __PPCENV__ = { + 0, /* CRC Sum: invalid */ + 0, /* Flags: invalid */ + { + "\0" + } +}; +#endif /* CONFIG_ENV_ADDR_REDUND */ + +/* + * These will end up in the .text section + * if the environment strings are embedded + * in the image. When this is used for + * tools/envcrc, they are placed in the + * .data/.sdata section. + * + */ +unsigned long env_size __PPCTEXT__ = sizeof(env_t); + +/* + * Add in absolutes. + */ +GEN_ABS(env_offset, CONFIG_ENV_OFFSET); + +#endif /* ENV_IS_EMBEDDED */ diff --git a/qemu/roms/u-boot/common/env_fat.c b/qemu/roms/u-boot/common/env_fat.c new file mode 100644 index 000000000..aad0487c3 --- /dev/null +++ b/qemu/roms/u-boot/common/env_fat.c @@ -0,0 +1,137 @@ +/* + * (c) Copyright 2011 by Tigris Elektronik GmbH + * + * Author: + * Maximilian Schwerin <mvs@tigris.de> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> + +#include <command.h> +#include <environment.h> +#include <linux/stddef.h> +#include <malloc.h> +#include <search.h> +#include <errno.h> +#include <fat.h> +#include <mmc.h> + +char *env_name_spec = "FAT"; + +env_t *env_ptr; + +DECLARE_GLOBAL_DATA_PTR; + +int env_init(void) +{ + /* use default */ + gd->env_addr = (ulong)&default_environment[0]; + gd->env_valid = 1; + + return 0; +} + +#ifdef CONFIG_CMD_SAVEENV +int saveenv(void) +{ + env_t env_new; + block_dev_desc_t *dev_desc = NULL; + int dev = FAT_ENV_DEVICE; + int part = FAT_ENV_PART; + int err; + + err = env_export(&env_new); + if (err) + return err; + +#ifdef CONFIG_MMC + if (strcmp(FAT_ENV_INTERFACE, "mmc") == 0) { + struct mmc *mmc = find_mmc_device(dev); + + if (!mmc) { + printf("no mmc device at slot %x\n", dev); + return 1; + } + + mmc->has_init = 0; + mmc_init(mmc); + } +#endif /* CONFIG_MMC */ + + dev_desc = get_dev(FAT_ENV_INTERFACE, dev); + if (dev_desc == NULL) { + printf("Failed to find %s%d\n", + FAT_ENV_INTERFACE, dev); + return 1; + } + + err = fat_register_device(dev_desc, part); + if (err) { + printf("Failed to register %s%d:%d\n", + FAT_ENV_INTERFACE, dev, part); + return 1; + } + + err = file_fat_write(FAT_ENV_FILE, (void *)&env_new, sizeof(env_t)); + if (err == -1) { + printf("\n** Unable to write \"%s\" from %s%d:%d **\n", + FAT_ENV_FILE, FAT_ENV_INTERFACE, dev, part); + return 1; + } + + puts("done\n"); + return 0; +} +#endif /* CONFIG_CMD_SAVEENV */ + +void env_relocate_spec(void) +{ + char buf[CONFIG_ENV_SIZE]; + block_dev_desc_t *dev_desc = NULL; + int dev = FAT_ENV_DEVICE; + int part = FAT_ENV_PART; + int err; + +#ifdef CONFIG_MMC + if (strcmp(FAT_ENV_INTERFACE, "mmc") == 0) { + struct mmc *mmc = find_mmc_device(dev); + + if (!mmc) { + printf("no mmc device at slot %x\n", dev); + set_default_env(NULL); + return; + } + + mmc->has_init = 0; + mmc_init(mmc); + } +#endif /* CONFIG_MMC */ + + dev_desc = get_dev(FAT_ENV_INTERFACE, dev); + if (dev_desc == NULL) { + printf("Failed to find %s%d\n", + FAT_ENV_INTERFACE, dev); + set_default_env(NULL); + return; + } + + err = fat_register_device(dev_desc, part); + if (err) { + printf("Failed to register %s%d:%d\n", + FAT_ENV_INTERFACE, dev, part); + set_default_env(NULL); + return; + } + + err = file_fat_read(FAT_ENV_FILE, (uchar *)&buf, CONFIG_ENV_SIZE); + if (err == -1) { + printf("\n** Unable to read \"%s\" from %s%d:%d **\n", + FAT_ENV_FILE, FAT_ENV_INTERFACE, dev, part); + set_default_env(NULL); + return; + } + + env_import(buf, 1); +} diff --git a/qemu/roms/u-boot/common/env_flags.c b/qemu/roms/u-boot/common/env_flags.c new file mode 100644 index 000000000..985f92e50 --- /dev/null +++ b/qemu/roms/u-boot/common/env_flags.c @@ -0,0 +1,550 @@ +/* + * (C) Copyright 2012 + * Joe Hershberger, National Instruments, joe.hershberger@ni.com + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <linux/string.h> +#include <linux/ctype.h> + +#ifdef USE_HOSTCC /* Eliminate "ANSI does not permit..." warnings */ +#include <stdint.h> +#include <stdio.h> +#include "fw_env.h" +#include <env_attr.h> +#include <env_flags.h> +#define getenv fw_getenv +#else +#include <common.h> +#include <environment.h> +#endif + +#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; +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", + "decimal", + "hexadecimal", + "boolean", +#ifdef CONFIG_CMD_NET + "IP address", + "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. + */ +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++; + } +} + +/* + * 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 */ + +/* + * 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; +} + +/* + * 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'); +} + +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; +} + +#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); +} + +/* + * 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. + */ +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 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[]) +{ + 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. + */ +static int env_parse_flags_to_bin(const char *flags) +{ + 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; +} + +static int first_call = 1; +static const char *flags_list; + +/* + * 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; + char flags[ENV_FLAGS_ATTR_MAX_LEN + 1] = ""; + int ret = 1; + + if (first_call) { + flags_list = getenv(ENV_FLAGS_VAR); + first_call = 0; + } + /* 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; + const char *oldval = NULL; + + if (op != env_op_create) + oldval = item->data; + + name = item->key; + + /* Default value for NULL to protect string-manipulating functions */ + newval = newval ? : ""; + + /* 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; + } + } + + /* 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; +} + +#endif diff --git a/qemu/roms/u-boot/common/env_flash.c b/qemu/roms/u-boot/common/env_flash.c new file mode 100644 index 000000000..004e8849a --- /dev/null +++ b/qemu/roms/u-boot/common/env_flash.c @@ -0,0 +1,338 @@ +/* + * (C) Copyright 2000-2010 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * (C) Copyright 2001 Sysgo Real-Time Solutions, GmbH <www.elinos.com> + * Andreas Heppel <aheppel@sysgo.de> + + * SPDX-License-Identifier: GPL-2.0+ + */ + +/* #define DEBUG */ + +#include <common.h> +#include <command.h> +#include <environment.h> +#include <linux/stddef.h> +#include <malloc.h> +#include <search.h> +#include <errno.h> + +DECLARE_GLOBAL_DATA_PTR; + +#if defined(CONFIG_CMD_SAVEENV) && defined(CONFIG_CMD_FLASH) +#define CMD_SAVEENV +#elif defined(CONFIG_ENV_ADDR_REDUND) +#error CONFIG_ENV_ADDR_REDUND must have CONFIG_CMD_SAVEENV & CONFIG_CMD_FLASH +#endif + +#if defined(CONFIG_ENV_SIZE_REDUND) && \ + (CONFIG_ENV_SIZE_REDUND < CONFIG_ENV_SIZE) +#error CONFIG_ENV_SIZE_REDUND should not be less then CONFIG_ENV_SIZE +#endif + +char *env_name_spec = "Flash"; + +#ifdef ENV_IS_EMBEDDED +env_t *env_ptr = &environment; + +static env_t *flash_addr = (env_t *)CONFIG_ENV_ADDR; + +#else /* ! ENV_IS_EMBEDDED */ + +env_t *env_ptr = (env_t *)CONFIG_ENV_ADDR; +static env_t *flash_addr = (env_t *)CONFIG_ENV_ADDR; +#endif /* ENV_IS_EMBEDDED */ + +#if defined(CMD_SAVEENV) || defined(CONFIG_ENV_ADDR_REDUND) +/* CONFIG_ENV_ADDR is supposed to be on sector boundary */ +static ulong end_addr = CONFIG_ENV_ADDR + CONFIG_ENV_SECT_SIZE - 1; +#endif + +#ifdef CONFIG_ENV_ADDR_REDUND +static env_t *flash_addr_new = (env_t *)CONFIG_ENV_ADDR_REDUND; + +/* CONFIG_ENV_ADDR_REDUND is supposed to be on sector boundary */ +static ulong end_addr_new = CONFIG_ENV_ADDR_REDUND + CONFIG_ENV_SECT_SIZE - 1; +#endif /* CONFIG_ENV_ADDR_REDUND */ + + +#ifdef CONFIG_ENV_ADDR_REDUND +int env_init(void) +{ + int crc1_ok = 0, crc2_ok = 0; + + uchar flag1 = flash_addr->flags; + uchar flag2 = flash_addr_new->flags; + + ulong addr_default = (ulong)&default_environment[0]; + ulong addr1 = (ulong)&(flash_addr->data); + ulong addr2 = (ulong)&(flash_addr_new->data); + + crc1_ok = crc32(0, flash_addr->data, ENV_SIZE) == flash_addr->crc; + crc2_ok = + crc32(0, flash_addr_new->data, ENV_SIZE) == flash_addr_new->crc; + + if (crc1_ok && !crc2_ok) { + gd->env_addr = addr1; + gd->env_valid = 1; + } else if (!crc1_ok && crc2_ok) { + gd->env_addr = addr2; + gd->env_valid = 1; + } else if (!crc1_ok && !crc2_ok) { + gd->env_addr = addr_default; + gd->env_valid = 0; + } else if (flag1 == ACTIVE_FLAG && flag2 == OBSOLETE_FLAG) { + gd->env_addr = addr1; + gd->env_valid = 1; + } else if (flag1 == OBSOLETE_FLAG && flag2 == ACTIVE_FLAG) { + gd->env_addr = addr2; + gd->env_valid = 1; + } else if (flag1 == flag2) { + gd->env_addr = addr1; + gd->env_valid = 2; + } else if (flag1 == 0xFF) { + gd->env_addr = addr1; + gd->env_valid = 2; + } else if (flag2 == 0xFF) { + gd->env_addr = addr2; + gd->env_valid = 2; + } + + return 0; +} + +#ifdef CMD_SAVEENV +int saveenv(void) +{ + env_t env_new; + char *saved_data = NULL; + char flag = OBSOLETE_FLAG, new_flag = ACTIVE_FLAG; + int rc = 1; +#if CONFIG_ENV_SECT_SIZE > CONFIG_ENV_SIZE + ulong up_data = 0; +#endif + + debug("Protect off %08lX ... %08lX\n", (ulong)flash_addr, end_addr); + + if (flash_sect_protect(0, (ulong)flash_addr, end_addr)) + goto done; + + debug("Protect off %08lX ... %08lX\n", + (ulong)flash_addr_new, end_addr_new); + + if (flash_sect_protect(0, (ulong)flash_addr_new, end_addr_new)) + goto done; + + rc = env_export(&env_new); + if (rc) + return rc; + env_new.flags = new_flag; + +#if CONFIG_ENV_SECT_SIZE > CONFIG_ENV_SIZE + up_data = end_addr_new + 1 - ((long)flash_addr_new + CONFIG_ENV_SIZE); + debug("Data to save 0x%lX\n", up_data); + if (up_data) { + saved_data = malloc(up_data); + if (saved_data == NULL) { + printf("Unable to save the rest of sector (%ld)\n", + up_data); + goto done; + } + memcpy(saved_data, + (void *)((long)flash_addr_new + CONFIG_ENV_SIZE), + up_data); + debug("Data (start 0x%lX, len 0x%lX) saved at 0x%p\n", + (long)flash_addr_new + CONFIG_ENV_SIZE, + up_data, saved_data); + } +#endif + puts("Erasing Flash..."); + debug(" %08lX ... %08lX ...", (ulong)flash_addr_new, end_addr_new); + + if (flash_sect_erase((ulong)flash_addr_new, end_addr_new)) + goto done; + + puts("Writing to Flash... "); + debug(" %08lX ... %08lX ...", + (ulong)&(flash_addr_new->data), + sizeof(env_ptr->data) + (ulong)&(flash_addr_new->data)); + rc = flash_write((char *)&env_new, (ulong)flash_addr_new, + sizeof(env_new)); + if (rc) + goto perror; + + rc = flash_write(&flag, (ulong)&(flash_addr->flags), + sizeof(flash_addr->flags)); + if (rc) + goto perror; + +#if CONFIG_ENV_SECT_SIZE > CONFIG_ENV_SIZE + if (up_data) { /* restore the rest of sector */ + debug("Restoring the rest of data to 0x%lX len 0x%lX\n", + (long)flash_addr_new + CONFIG_ENV_SIZE, up_data); + if (flash_write(saved_data, + (long)flash_addr_new + CONFIG_ENV_SIZE, + up_data)) + goto perror; + } +#endif + puts("done\n"); + + { + env_t *etmp = flash_addr; + ulong ltmp = end_addr; + + flash_addr = flash_addr_new; + flash_addr_new = etmp; + + end_addr = end_addr_new; + end_addr_new = ltmp; + } + + rc = 0; + goto done; +perror: + flash_perror(rc); +done: + if (saved_data) + free(saved_data); + /* try to re-protect */ + flash_sect_protect(1, (ulong)flash_addr, end_addr); + flash_sect_protect(1, (ulong)flash_addr_new, end_addr_new); + + return rc; +} +#endif /* CMD_SAVEENV */ + +#else /* ! CONFIG_ENV_ADDR_REDUND */ + +int env_init(void) +{ + if (crc32(0, env_ptr->data, ENV_SIZE) == env_ptr->crc) { + gd->env_addr = (ulong)&(env_ptr->data); + gd->env_valid = 1; + return 0; + } + + gd->env_addr = (ulong)&default_environment[0]; + gd->env_valid = 0; + return 0; +} + +#ifdef CMD_SAVEENV +int saveenv(void) +{ + env_t env_new; + int rc = 1; + char *saved_data = NULL; +#if CONFIG_ENV_SECT_SIZE > CONFIG_ENV_SIZE + ulong up_data = 0; + + up_data = end_addr + 1 - ((long)flash_addr + CONFIG_ENV_SIZE); + debug("Data to save 0x%lx\n", up_data); + if (up_data) { + saved_data = malloc(up_data); + if (saved_data == NULL) { + printf("Unable to save the rest of sector (%ld)\n", + up_data); + goto done; + } + memcpy(saved_data, + (void *)((long)flash_addr + CONFIG_ENV_SIZE), up_data); + debug("Data (start 0x%lx, len 0x%lx) saved at 0x%lx\n", + (ulong)flash_addr + CONFIG_ENV_SIZE, + up_data, + (ulong)saved_data); + } +#endif /* CONFIG_ENV_SECT_SIZE */ + + debug("Protect off %08lX ... %08lX\n", (ulong)flash_addr, end_addr); + + if (flash_sect_protect(0, (long)flash_addr, end_addr)) + goto done; + + rc = env_export(&env_new); + if (rc) + goto done; + + puts("Erasing Flash..."); + if (flash_sect_erase((long)flash_addr, end_addr)) + goto done; + + puts("Writing to Flash... "); + rc = flash_write((char *)&env_new, (long)flash_addr, CONFIG_ENV_SIZE); + if (rc != 0) + goto perror; + +#if CONFIG_ENV_SECT_SIZE > CONFIG_ENV_SIZE + if (up_data) { /* restore the rest of sector */ + debug("Restoring the rest of data to 0x%lx len 0x%lx\n", + (ulong)flash_addr + CONFIG_ENV_SIZE, up_data); + if (flash_write(saved_data, + (long)flash_addr + CONFIG_ENV_SIZE, + up_data)) + goto perror; + } +#endif + puts("done\n"); + rc = 0; + goto done; +perror: + flash_perror(rc); +done: + if (saved_data) + free(saved_data); + /* try to re-protect */ + flash_sect_protect(1, (long)flash_addr, end_addr); + return rc; +} +#endif /* CMD_SAVEENV */ + +#endif /* CONFIG_ENV_ADDR_REDUND */ + +void env_relocate_spec(void) +{ +#ifdef CONFIG_ENV_ADDR_REDUND + if (gd->env_addr != (ulong)&(flash_addr->data)) { + env_t *etmp = flash_addr; + ulong ltmp = end_addr; + + flash_addr = flash_addr_new; + flash_addr_new = etmp; + + end_addr = end_addr_new; + end_addr_new = ltmp; + } + + if (flash_addr_new->flags != OBSOLETE_FLAG && + crc32(0, flash_addr_new->data, ENV_SIZE) == flash_addr_new->crc) { + char flag = OBSOLETE_FLAG; + + gd->env_valid = 2; + flash_sect_protect(0, (ulong)flash_addr_new, end_addr_new); + flash_write(&flag, + (ulong)&(flash_addr_new->flags), + sizeof(flash_addr_new->flags)); + flash_sect_protect(1, (ulong)flash_addr_new, end_addr_new); + } + + if (flash_addr->flags != ACTIVE_FLAG && + (flash_addr->flags & ACTIVE_FLAG) == ACTIVE_FLAG) { + char flag = ACTIVE_FLAG; + + gd->env_valid = 2; + flash_sect_protect(0, (ulong)flash_addr, end_addr); + flash_write(&flag, + (ulong)&(flash_addr->flags), + sizeof(flash_addr->flags)); + flash_sect_protect(1, (ulong)flash_addr, end_addr); + } + + if (gd->env_valid == 2) + puts("*** Warning - some problems detected " + "reading environment; recovered successfully\n\n"); +#endif /* CONFIG_ENV_ADDR_REDUND */ + + env_import((char *)flash_addr, 1); +} diff --git a/qemu/roms/u-boot/common/env_mmc.c b/qemu/roms/u-boot/common/env_mmc.c new file mode 100644 index 000000000..7da10e61a --- /dev/null +++ b/qemu/roms/u-boot/common/env_mmc.c @@ -0,0 +1,321 @@ +/* + * (C) Copyright 2008-2011 Freescale Semiconductor, Inc. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/* #define DEBUG */ + +#include <common.h> + +#include <command.h> +#include <environment.h> +#include <linux/stddef.h> +#include <malloc.h> +#include <mmc.h> +#include <search.h> +#include <errno.h> + +#if defined(CONFIG_ENV_SIZE_REDUND) && \ + (CONFIG_ENV_SIZE_REDUND != CONFIG_ENV_SIZE) +#error CONFIG_ENV_SIZE_REDUND should be the same as CONFIG_ENV_SIZE +#endif + +char *env_name_spec = "MMC"; + +#ifdef ENV_IS_EMBEDDED +env_t *env_ptr = &environment; +#else /* ! ENV_IS_EMBEDDED */ +env_t *env_ptr; +#endif /* ENV_IS_EMBEDDED */ + +DECLARE_GLOBAL_DATA_PTR; + +#if !defined(CONFIG_ENV_OFFSET) +#define CONFIG_ENV_OFFSET 0 +#endif + +__weak int mmc_get_env_addr(struct mmc *mmc, int copy, u32 *env_addr) +{ + s64 offset; + + offset = CONFIG_ENV_OFFSET; +#ifdef CONFIG_ENV_OFFSET_REDUND + if (copy) + offset = CONFIG_ENV_OFFSET_REDUND; +#endif + + if (offset < 0) + offset += mmc->capacity; + + *env_addr = offset; + + return 0; +} + +int env_init(void) +{ + /* use default */ + gd->env_addr = (ulong)&default_environment[0]; + gd->env_valid = 1; + + return 0; +} + +static int init_mmc_for_env(struct mmc *mmc) +{ +#ifdef CONFIG_SYS_MMC_ENV_PART + int dev = CONFIG_SYS_MMC_ENV_DEV; + +#ifdef CONFIG_SPL_BUILD + dev = 0; +#endif +#endif + + if (!mmc) { + puts("No MMC card found\n"); + return -1; + } + + if (mmc_init(mmc)) { + puts("MMC init failed\n"); + return -1; + } + +#ifdef CONFIG_SYS_MMC_ENV_PART + if (CONFIG_SYS_MMC_ENV_PART != mmc->part_num) { + if (mmc_switch_part(dev, CONFIG_SYS_MMC_ENV_PART)) { + puts("MMC partition switch failed\n"); + return -1; + } + } +#endif + + return 0; +} + +static void fini_mmc_for_env(struct mmc *mmc) +{ +#ifdef CONFIG_SYS_MMC_ENV_PART + int dev = CONFIG_SYS_MMC_ENV_DEV; + +#ifdef CONFIG_SPL_BUILD + dev = 0; +#endif + if (CONFIG_SYS_MMC_ENV_PART != mmc->part_num) + mmc_switch_part(dev, mmc->part_num); +#endif +} + +#ifdef CONFIG_CMD_SAVEENV +static inline int write_env(struct mmc *mmc, unsigned long size, + unsigned long offset, const void *buffer) +{ + uint blk_start, blk_cnt, n; + + blk_start = ALIGN(offset, mmc->write_bl_len) / mmc->write_bl_len; + blk_cnt = ALIGN(size, mmc->write_bl_len) / mmc->write_bl_len; + + n = mmc->block_dev.block_write(CONFIG_SYS_MMC_ENV_DEV, blk_start, + blk_cnt, (u_char *)buffer); + + return (n == blk_cnt) ? 0 : -1; +} + +#ifdef CONFIG_ENV_OFFSET_REDUND +static unsigned char env_flags; +#endif + +int saveenv(void) +{ + ALLOC_CACHE_ALIGN_BUFFER(env_t, env_new, 1); + struct mmc *mmc = find_mmc_device(CONFIG_SYS_MMC_ENV_DEV); + u32 offset; + int ret, copy = 0; + + if (init_mmc_for_env(mmc)) + return 1; + + ret = env_export(env_new); + if (ret) + goto fini; + +#ifdef CONFIG_ENV_OFFSET_REDUND + env_new->flags = ++env_flags; /* increase the serial */ + + if (gd->env_valid == 1) + copy = 1; +#endif + + if (mmc_get_env_addr(mmc, copy, &offset)) { + ret = 1; + goto fini; + } + + printf("Writing to %sMMC(%d)... ", copy ? "redundant " : "", + CONFIG_SYS_MMC_ENV_DEV); + if (write_env(mmc, CONFIG_ENV_SIZE, offset, (u_char *)env_new)) { + puts("failed\n"); + ret = 1; + goto fini; + } + + puts("done\n"); + ret = 0; + +#ifdef CONFIG_ENV_OFFSET_REDUND + gd->env_valid = gd->env_valid == 2 ? 1 : 2; +#endif + +fini: + fini_mmc_for_env(mmc); + return ret; +} +#endif /* CONFIG_CMD_SAVEENV */ + +static inline int read_env(struct mmc *mmc, unsigned long size, + unsigned long offset, const void *buffer) +{ + uint blk_start, blk_cnt, n; + int dev = CONFIG_SYS_MMC_ENV_DEV; + +#ifdef CONFIG_SPL_BUILD + dev = 0; +#endif + + blk_start = ALIGN(offset, mmc->read_bl_len) / mmc->read_bl_len; + blk_cnt = ALIGN(size, mmc->read_bl_len) / mmc->read_bl_len; + + n = mmc->block_dev.block_read(dev, blk_start, blk_cnt, (uchar *)buffer); + + return (n == blk_cnt) ? 0 : -1; +} + +#ifdef CONFIG_ENV_OFFSET_REDUND +void env_relocate_spec(void) +{ +#if !defined(ENV_IS_EMBEDDED) + struct mmc *mmc; + u32 offset1, offset2; + int read1_fail = 0, read2_fail = 0; + int crc1_ok = 0, crc2_ok = 0; + env_t *ep; + int ret; + int dev = CONFIG_SYS_MMC_ENV_DEV; + + ALLOC_CACHE_ALIGN_BUFFER(env_t, tmp_env1, 1); + ALLOC_CACHE_ALIGN_BUFFER(env_t, tmp_env2, 1); + +#ifdef CONFIG_SPL_BUILD + dev = 0; +#endif + + mmc = find_mmc_device(dev); + + if (init_mmc_for_env(mmc)) { + ret = 1; + goto err; + } + + if (mmc_get_env_addr(mmc, 0, &offset1) || + mmc_get_env_addr(mmc, 1, &offset2)) { + ret = 1; + goto fini; + } + + read1_fail = read_env(mmc, CONFIG_ENV_SIZE, offset1, tmp_env1); + read2_fail = read_env(mmc, CONFIG_ENV_SIZE, offset2, tmp_env2); + + if (read1_fail && read2_fail) + puts("*** Error - No Valid Environment Area found\n"); + else if (read1_fail || read2_fail) + puts("*** Warning - some problems detected " + "reading environment; recovered successfully\n"); + + crc1_ok = !read1_fail && + (crc32(0, tmp_env1->data, ENV_SIZE) == tmp_env1->crc); + crc2_ok = !read2_fail && + (crc32(0, tmp_env2->data, ENV_SIZE) == tmp_env2->crc); + + if (!crc1_ok && !crc2_ok) { + ret = 1; + goto fini; + } else if (crc1_ok && !crc2_ok) { + gd->env_valid = 1; + } else if (!crc1_ok && crc2_ok) { + gd->env_valid = 2; + } else { + /* both ok - check serial */ + if (tmp_env1->flags == 255 && tmp_env2->flags == 0) + gd->env_valid = 2; + else if (tmp_env2->flags == 255 && tmp_env1->flags == 0) + gd->env_valid = 1; + else if (tmp_env1->flags > tmp_env2->flags) + gd->env_valid = 1; + else if (tmp_env2->flags > tmp_env1->flags) + gd->env_valid = 2; + else /* flags are equal - almost impossible */ + gd->env_valid = 1; + } + + free(env_ptr); + + if (gd->env_valid == 1) + ep = tmp_env1; + else + ep = tmp_env2; + + env_flags = ep->flags; + env_import((char *)ep, 0); + ret = 0; + +fini: + fini_mmc_for_env(mmc); +err: + if (ret) + set_default_env(NULL); + +#endif +} +#else /* ! CONFIG_ENV_OFFSET_REDUND */ +void env_relocate_spec(void) +{ +#if !defined(ENV_IS_EMBEDDED) + ALLOC_CACHE_ALIGN_BUFFER(char, buf, CONFIG_ENV_SIZE); + struct mmc *mmc; + u32 offset; + int ret; + int dev = CONFIG_SYS_MMC_ENV_DEV; + +#ifdef CONFIG_SPL_BUILD + dev = 0; +#endif + + mmc = find_mmc_device(dev); + + if (init_mmc_for_env(mmc)) { + ret = 1; + goto err; + } + + if (mmc_get_env_addr(mmc, 0, &offset)) { + ret = 1; + goto fini; + } + + if (read_env(mmc, CONFIG_ENV_SIZE, offset, buf)) { + ret = 1; + goto fini; + } + + env_import(buf, 1); + ret = 0; + +fini: + fini_mmc_for_env(mmc); +err: + if (ret) + set_default_env(NULL); +#endif +} +#endif /* CONFIG_ENV_OFFSET_REDUND */ diff --git a/qemu/roms/u-boot/common/env_nand.c b/qemu/roms/u-boot/common/env_nand.c new file mode 100644 index 000000000..5a734a932 --- /dev/null +++ b/qemu/roms/u-boot/common/env_nand.c @@ -0,0 +1,404 @@ +/* + * (C) Copyright 2000-2010 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * (C) Copyright 2008 + * Stuart Wood, Lab X Technologies <stuart.wood@labxtechnologies.com> + * + * (C) Copyright 2004 + * Jian Zhang, Texas Instruments, jzhang@ti.com. + * + * (C) Copyright 2001 Sysgo Real-Time Solutions, GmbH <www.elinos.com> + * Andreas Heppel <aheppel@sysgo.de> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <command.h> +#include <environment.h> +#include <linux/stddef.h> +#include <malloc.h> +#include <nand.h> +#include <search.h> +#include <errno.h> + +#if defined(CONFIG_CMD_SAVEENV) && defined(CONFIG_CMD_NAND) +#define CMD_SAVEENV +#elif defined(CONFIG_ENV_OFFSET_REDUND) +#error CONFIG_ENV_OFFSET_REDUND must have CONFIG_CMD_SAVEENV & CONFIG_CMD_NAND +#endif + +#if defined(CONFIG_ENV_SIZE_REDUND) && \ + (CONFIG_ENV_SIZE_REDUND != CONFIG_ENV_SIZE) +#error CONFIG_ENV_SIZE_REDUND should be the same as CONFIG_ENV_SIZE +#endif + +#ifndef CONFIG_ENV_RANGE +#define CONFIG_ENV_RANGE CONFIG_ENV_SIZE +#endif + +char *env_name_spec = "NAND"; + +#if defined(ENV_IS_EMBEDDED) +env_t *env_ptr = &environment; +#elif defined(CONFIG_NAND_ENV_DST) +env_t *env_ptr = (env_t *)CONFIG_NAND_ENV_DST; +#else /* ! ENV_IS_EMBEDDED */ +env_t *env_ptr; +#endif /* ENV_IS_EMBEDDED */ + +DECLARE_GLOBAL_DATA_PTR; + +/* + * This is called before nand_init() so we can't read NAND to + * validate env data. + * + * Mark it OK for now. env_relocate() in env_common.c will call our + * relocate function which does the real validation. + * + * When using a NAND boot image (like sequoia_nand), the environment + * can be embedded or attached to the U-Boot image in NAND flash. + * This way the SPL loads not only the U-Boot image from NAND but + * also the environment. + */ +int env_init(void) +{ +#if defined(ENV_IS_EMBEDDED) || defined(CONFIG_NAND_ENV_DST) + int crc1_ok = 0, crc2_ok = 0; + env_t *tmp_env1; + +#ifdef CONFIG_ENV_OFFSET_REDUND + env_t *tmp_env2; + + tmp_env2 = (env_t *)((ulong)env_ptr + CONFIG_ENV_SIZE); + crc2_ok = crc32(0, tmp_env2->data, ENV_SIZE) == tmp_env2->crc; +#endif + tmp_env1 = env_ptr; + crc1_ok = crc32(0, tmp_env1->data, ENV_SIZE) == tmp_env1->crc; + + if (!crc1_ok && !crc2_ok) { + gd->env_addr = 0; + gd->env_valid = 0; + + return 0; + } else if (crc1_ok && !crc2_ok) { + gd->env_valid = 1; + } +#ifdef CONFIG_ENV_OFFSET_REDUND + else if (!crc1_ok && crc2_ok) { + gd->env_valid = 2; + } else { + /* both ok - check serial */ + if (tmp_env1->flags == 255 && tmp_env2->flags == 0) + gd->env_valid = 2; + else if (tmp_env2->flags == 255 && tmp_env1->flags == 0) + gd->env_valid = 1; + else if (tmp_env1->flags > tmp_env2->flags) + gd->env_valid = 1; + else if (tmp_env2->flags > tmp_env1->flags) + gd->env_valid = 2; + else /* flags are equal - almost impossible */ + gd->env_valid = 1; + } + + if (gd->env_valid == 2) + env_ptr = tmp_env2; + else +#endif + if (gd->env_valid == 1) + env_ptr = tmp_env1; + + gd->env_addr = (ulong)env_ptr->data; + +#else /* ENV_IS_EMBEDDED || CONFIG_NAND_ENV_DST */ + gd->env_addr = (ulong)&default_environment[0]; + gd->env_valid = 1; +#endif /* ENV_IS_EMBEDDED || CONFIG_NAND_ENV_DST */ + + return 0; +} + +#ifdef CMD_SAVEENV +/* + * The legacy NAND code saved the environment in the first NAND device i.e., + * nand_dev_desc + 0. This is also the behaviour using the new NAND code. + */ +int writeenv(size_t offset, u_char *buf) +{ + size_t end = offset + CONFIG_ENV_RANGE; + size_t amount_saved = 0; + size_t blocksize, len; + u_char *char_ptr; + + blocksize = nand_info[0].erasesize; + len = min(blocksize, CONFIG_ENV_SIZE); + + while (amount_saved < CONFIG_ENV_SIZE && offset < end) { + if (nand_block_isbad(&nand_info[0], offset)) { + offset += blocksize; + } else { + char_ptr = &buf[amount_saved]; + if (nand_write(&nand_info[0], offset, &len, char_ptr)) + return 1; + + offset += blocksize; + amount_saved += len; + } + } + if (amount_saved != CONFIG_ENV_SIZE) + return 1; + + return 0; +} + +struct env_location { + const char *name; + const nand_erase_options_t erase_opts; +}; + +static int erase_and_write_env(const struct env_location *location, + u_char *env_new) +{ + int ret = 0; + + printf("Erasing %s...\n", location->name); + if (nand_erase_opts(&nand_info[0], &location->erase_opts)) + return 1; + + printf("Writing to %s... ", location->name); + ret = writeenv(location->erase_opts.offset, env_new); + puts(ret ? "FAILED!\n" : "OK\n"); + + return ret; +} + +#ifdef CONFIG_ENV_OFFSET_REDUND +static unsigned char env_flags; +#endif + +int saveenv(void) +{ + int ret = 0; + ALLOC_CACHE_ALIGN_BUFFER(env_t, env_new, 1); + int env_idx = 0; + static const struct env_location location[] = { + { + .name = "NAND", + .erase_opts = { + .length = CONFIG_ENV_RANGE, + .offset = CONFIG_ENV_OFFSET, + }, + }, +#ifdef CONFIG_ENV_OFFSET_REDUND + { + .name = "redundant NAND", + .erase_opts = { + .length = CONFIG_ENV_RANGE, + .offset = CONFIG_ENV_OFFSET_REDUND, + }, + }, +#endif + }; + + + if (CONFIG_ENV_RANGE < CONFIG_ENV_SIZE) + return 1; + + ret = env_export(env_new); + if (ret) + return ret; + +#ifdef CONFIG_ENV_OFFSET_REDUND + env_new->flags = ++env_flags; /* increase the serial */ + env_idx = (gd->env_valid == 1); +#endif + + ret = erase_and_write_env(&location[env_idx], (u_char *)env_new); +#ifdef CONFIG_ENV_OFFSET_REDUND + if (!ret) { + /* preset other copy for next write */ + gd->env_valid = gd->env_valid == 2 ? 1 : 2; + return ret; + } + + env_idx = (env_idx + 1) & 1; + ret = erase_and_write_env(&location[env_idx], (u_char *)env_new); + if (!ret) + printf("Warning: primary env write failed," + " redundancy is lost!\n"); +#endif + + return ret; +} +#endif /* CMD_SAVEENV */ + +int readenv(size_t offset, u_char *buf) +{ + size_t end = offset + CONFIG_ENV_RANGE; + size_t amount_loaded = 0; + size_t blocksize, len; + u_char *char_ptr; + + blocksize = nand_info[0].erasesize; + if (!blocksize) + return 1; + + len = min(blocksize, CONFIG_ENV_SIZE); + + while (amount_loaded < CONFIG_ENV_SIZE && offset < end) { + if (nand_block_isbad(&nand_info[0], offset)) { + offset += blocksize; + } else { + char_ptr = &buf[amount_loaded]; + if (nand_read_skip_bad(&nand_info[0], offset, + &len, NULL, + nand_info[0].size, char_ptr)) + return 1; + + offset += blocksize; + amount_loaded += len; + } + } + + if (amount_loaded != CONFIG_ENV_SIZE) + return 1; + + return 0; +} + +#ifdef CONFIG_ENV_OFFSET_OOB +int get_nand_env_oob(nand_info_t *nand, unsigned long *result) +{ + struct mtd_oob_ops ops; + uint32_t oob_buf[ENV_OFFSET_SIZE / sizeof(uint32_t)]; + int ret; + + ops.datbuf = NULL; + ops.mode = MTD_OOB_AUTO; + ops.ooboffs = 0; + ops.ooblen = ENV_OFFSET_SIZE; + ops.oobbuf = (void *)oob_buf; + + ret = nand->read_oob(nand, ENV_OFFSET_SIZE, &ops); + if (ret) { + printf("error reading OOB block 0\n"); + return ret; + } + + if (oob_buf[0] == ENV_OOB_MARKER) { + *result = oob_buf[1] * nand->erasesize; + } else if (oob_buf[0] == ENV_OOB_MARKER_OLD) { + *result = oob_buf[1]; + } else { + printf("No dynamic environment marker in OOB block 0\n"); + return -ENOENT; + } + + return 0; +} +#endif + +#ifdef CONFIG_ENV_OFFSET_REDUND +void env_relocate_spec(void) +{ +#if !defined(ENV_IS_EMBEDDED) + int read1_fail = 0, read2_fail = 0; + int crc1_ok = 0, crc2_ok = 0; + env_t *ep, *tmp_env1, *tmp_env2; + + tmp_env1 = (env_t *)malloc(CONFIG_ENV_SIZE); + tmp_env2 = (env_t *)malloc(CONFIG_ENV_SIZE); + if (tmp_env1 == NULL || tmp_env2 == NULL) { + puts("Can't allocate buffers for environment\n"); + set_default_env("!malloc() failed"); + goto done; + } + + read1_fail = readenv(CONFIG_ENV_OFFSET, (u_char *) tmp_env1); + read2_fail = readenv(CONFIG_ENV_OFFSET_REDUND, (u_char *) tmp_env2); + + if (read1_fail && read2_fail) + puts("*** Error - No Valid Environment Area found\n"); + else if (read1_fail || read2_fail) + puts("*** Warning - some problems detected " + "reading environment; recovered successfully\n"); + + crc1_ok = !read1_fail && + (crc32(0, tmp_env1->data, ENV_SIZE) == tmp_env1->crc); + crc2_ok = !read2_fail && + (crc32(0, tmp_env2->data, ENV_SIZE) == tmp_env2->crc); + + if (!crc1_ok && !crc2_ok) { + set_default_env("!bad CRC"); + goto done; + } else if (crc1_ok && !crc2_ok) { + gd->env_valid = 1; + } else if (!crc1_ok && crc2_ok) { + gd->env_valid = 2; + } else { + /* both ok - check serial */ + if (tmp_env1->flags == 255 && tmp_env2->flags == 0) + gd->env_valid = 2; + else if (tmp_env2->flags == 255 && tmp_env1->flags == 0) + gd->env_valid = 1; + else if (tmp_env1->flags > tmp_env2->flags) + gd->env_valid = 1; + else if (tmp_env2->flags > tmp_env1->flags) + gd->env_valid = 2; + else /* flags are equal - almost impossible */ + gd->env_valid = 1; + } + + free(env_ptr); + + if (gd->env_valid == 1) + ep = tmp_env1; + else + ep = tmp_env2; + + env_flags = ep->flags; + env_import((char *)ep, 0); + +done: + free(tmp_env1); + free(tmp_env2); + +#endif /* ! ENV_IS_EMBEDDED */ +} +#else /* ! CONFIG_ENV_OFFSET_REDUND */ +/* + * The legacy NAND code saved the environment in the first NAND + * device i.e., nand_dev_desc + 0. This is also the behaviour using + * the new NAND code. + */ +void env_relocate_spec(void) +{ +#if !defined(ENV_IS_EMBEDDED) + int ret; + ALLOC_CACHE_ALIGN_BUFFER(char, buf, CONFIG_ENV_SIZE); + +#if defined(CONFIG_ENV_OFFSET_OOB) + ret = get_nand_env_oob(&nand_info[0], &nand_env_oob_offset); + /* + * If unable to read environment offset from NAND OOB then fall through + * to the normal environment reading code below + */ + if (!ret) { + printf("Found Environment offset in OOB..\n"); + } else { + set_default_env("!no env offset in OOB"); + return; + } +#endif + + ret = readenv(CONFIG_ENV_OFFSET, (u_char *)buf); + if (ret) { + set_default_env("!readenv() failed"); + return; + } + + env_import(buf, 1); +#endif /* ! ENV_IS_EMBEDDED */ +} +#endif /* CONFIG_ENV_OFFSET_REDUND */ diff --git a/qemu/roms/u-boot/common/env_nowhere.c b/qemu/roms/u-boot/common/env_nowhere.c new file mode 100644 index 000000000..bdc1ed5e6 --- /dev/null +++ b/qemu/roms/u-boot/common/env_nowhere.c @@ -0,0 +1,35 @@ +/* + * (C) Copyright 2000-2010 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * (C) Copyright 2001 Sysgo Real-Time Solutions, GmbH <www.elinos.com> + * Andreas Heppel <aheppel@sysgo.de> + + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <command.h> +#include <environment.h> +#include <linux/stddef.h> + +DECLARE_GLOBAL_DATA_PTR; + +env_t *env_ptr; + +void env_relocate_spec(void) +{ +} + +/* + * Initialize Environment use + * + * We are still running from ROM, so data use is limited + */ +int env_init(void) +{ + gd->env_addr = (ulong)&default_environment[0]; + gd->env_valid = 0; + + return 0; +} diff --git a/qemu/roms/u-boot/common/env_nvram.c b/qemu/roms/u-boot/common/env_nvram.c new file mode 100644 index 000000000..524f07d5f --- /dev/null +++ b/qemu/roms/u-boot/common/env_nvram.c @@ -0,0 +1,114 @@ +/* + * (C) Copyright 2000-2010 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * (C) Copyright 2001 Sysgo Real-Time Solutions, GmbH <www.elinos.com> + * Andreas Heppel <aheppel@sysgo.de> + + * SPDX-License-Identifier: GPL-2.0+ + */ + +/* + * 09-18-2001 Andreas Heppel, Sysgo RTS GmbH <aheppel@sysgo.de> + * + * It might not be possible in all cases to use 'memcpy()' to copy + * the environment to NVRAM, as the NVRAM might not be mapped into + * the memory space. (I.e. this is the case for the BAB750). In those + * cases it might be possible to access the NVRAM using a different + * method. For example, the RTC on the BAB750 is accessible in IO + * space using its address and data registers. To enable usage of + * NVRAM in those cases I invented the functions 'nvram_read()' and + * 'nvram_write()', which will be activated upon the configuration + * #define CONFIG_SYS_NVRAM_ACCESS_ROUTINE. Note, that those functions are + * strongly dependent on the used HW, and must be redefined for each + * board that wants to use them. + */ + +#include <common.h> +#include <command.h> +#include <environment.h> +#include <linux/stddef.h> +#include <search.h> +#include <errno.h> + +DECLARE_GLOBAL_DATA_PTR; + +#ifdef CONFIG_SYS_NVRAM_ACCESS_ROUTINE +extern void *nvram_read(void *dest, const long src, size_t count); +extern void nvram_write(long dest, const void *src, size_t count); +env_t *env_ptr; +#else +env_t *env_ptr = (env_t *)CONFIG_ENV_ADDR; +#endif + +char *env_name_spec = "NVRAM"; + +#ifdef CONFIG_SYS_NVRAM_ACCESS_ROUTINE +uchar env_get_char_spec(int index) +{ + uchar c; + + nvram_read(&c, CONFIG_ENV_ADDR + index, 1); + + return c; +} +#endif + +void env_relocate_spec(void) +{ + char buf[CONFIG_ENV_SIZE]; + +#if defined(CONFIG_SYS_NVRAM_ACCESS_ROUTINE) + nvram_read(buf, CONFIG_ENV_ADDR, CONFIG_ENV_SIZE); +#else + memcpy(buf, (void *)CONFIG_ENV_ADDR, CONFIG_ENV_SIZE); +#endif + env_import(buf, 1); +} + +int saveenv(void) +{ + env_t env_new; + int rcode = 0; + + rcode = env_export(&env_new); + if (rcode) + return rcode; + +#ifdef CONFIG_SYS_NVRAM_ACCESS_ROUTINE + nvram_write(CONFIG_ENV_ADDR, &env_new, CONFIG_ENV_SIZE); +#else + if (memcpy((char *)CONFIG_ENV_ADDR, &env_new, CONFIG_ENV_SIZE) == NULL) + rcode = 1; +#endif + return rcode; +} + +/* + * Initialize Environment use + * + * We are still running from ROM, so data use is limited + */ +int env_init(void) +{ +#if defined(CONFIG_SYS_NVRAM_ACCESS_ROUTINE) + ulong crc; + uchar data[ENV_SIZE]; + + nvram_read(&crc, CONFIG_ENV_ADDR, sizeof(ulong)); + nvram_read(data, CONFIG_ENV_ADDR + sizeof(ulong), ENV_SIZE); + + if (crc32(0, data, ENV_SIZE) == crc) { + gd->env_addr = (ulong)CONFIG_ENV_ADDR + sizeof(long); +#else + if (crc32(0, env_ptr->data, ENV_SIZE) == env_ptr->crc) { + gd->env_addr = (ulong)&env_ptr->data; +#endif + gd->env_valid = 1; + } else { + gd->env_addr = (ulong)&default_environment[0]; + gd->env_valid = 0; + } + + return 0; +} diff --git a/qemu/roms/u-boot/common/env_onenand.c b/qemu/roms/u-boot/common/env_onenand.c new file mode 100644 index 000000000..cc3d670de --- /dev/null +++ b/qemu/roms/u-boot/common/env_onenand.c @@ -0,0 +1,116 @@ +/* + * (C) Copyright 2010 DENX Software Engineering + * Wolfgang Denk <wd@denx.de> + * + * (C) Copyright 2005-2009 Samsung Electronics + * Kyungmin Park <kyungmin.park@samsung.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <command.h> +#include <environment.h> +#include <linux/stddef.h> +#include <malloc.h> +#include <search.h> +#include <errno.h> +#include <onenand_uboot.h> + +#include <linux/compat.h> +#include <linux/mtd/mtd.h> +#include <linux/mtd/onenand.h> + +char *env_name_spec = "OneNAND"; + +#define ONENAND_MAX_ENV_SIZE CONFIG_ENV_SIZE +#define ONENAND_ENV_SIZE(mtd) (ONENAND_MAX_ENV_SIZE - ENV_HEADER_SIZE) + +DECLARE_GLOBAL_DATA_PTR; + +void env_relocate_spec(void) +{ + struct mtd_info *mtd = &onenand_mtd; +#ifdef CONFIG_ENV_ADDR_FLEX + struct onenand_chip *this = &onenand_chip; +#endif + int rc; + size_t retlen; +#ifdef ENV_IS_EMBEDDED + char *buf = (char *)&environment; +#else + loff_t env_addr = CONFIG_ENV_ADDR; + char onenand_env[ONENAND_MAX_ENV_SIZE]; + char *buf = (char *)&onenand_env[0]; +#endif /* ENV_IS_EMBEDDED */ + +#ifndef ENV_IS_EMBEDDED +# ifdef CONFIG_ENV_ADDR_FLEX + if (FLEXONENAND(this)) + env_addr = CONFIG_ENV_ADDR_FLEX; +# endif + /* Check OneNAND exist */ + if (mtd->writesize) + /* Ignore read fail */ + mtd_read(mtd, env_addr, ONENAND_MAX_ENV_SIZE, + &retlen, (u_char *)buf); + else + mtd->writesize = MAX_ONENAND_PAGESIZE; +#endif /* !ENV_IS_EMBEDDED */ + + rc = env_import(buf, 1); + if (rc) + gd->env_valid = 1; +} + +int saveenv(void) +{ + env_t env_new; + int ret; + struct mtd_info *mtd = &onenand_mtd; +#ifdef CONFIG_ENV_ADDR_FLEX + struct onenand_chip *this = &onenand_chip; +#endif + loff_t env_addr = CONFIG_ENV_ADDR; + size_t retlen; + struct erase_info instr = { + .callback = NULL, + }; + + ret = env_export(&env_new); + if (ret) + return ret; + + instr.len = CONFIG_ENV_SIZE; +#ifdef CONFIG_ENV_ADDR_FLEX + if (FLEXONENAND(this)) { + env_addr = CONFIG_ENV_ADDR_FLEX; + instr.len = CONFIG_ENV_SIZE_FLEX; + instr.len <<= onenand_mtd.eraseregions[0].numblocks == 1 ? + 1 : 0; + } +#endif + instr.addr = env_addr; + instr.mtd = mtd; + if (mtd_erase(mtd, &instr)) { + printf("OneNAND: erase failed at 0x%08llx\n", env_addr); + return 1; + } + + if (mtd_write(mtd, env_addr, ONENAND_MAX_ENV_SIZE, &retlen, + (u_char *)&env_new)) { + printf("OneNAND: write failed at 0x%llx\n", instr.addr); + return 2; + } + + return 0; +} + +int env_init(void) +{ + /* use default */ + gd->env_addr = (ulong)&default_environment[0]; + gd->env_valid = 1; + + return 0; +} diff --git a/qemu/roms/u-boot/common/env_remote.c b/qemu/roms/u-boot/common/env_remote.c new file mode 100644 index 000000000..eb977ee1f --- /dev/null +++ b/qemu/roms/u-boot/common/env_remote.c @@ -0,0 +1,58 @@ +/* + * (C) Copyright 2011-2012 Freescale Semiconductor, Inc. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/* #define DEBUG */ + +#include <common.h> +#include <command.h> +#include <environment.h> +#include <linux/stddef.h> + +char *env_name_spec = "Remote"; + +#ifdef ENV_IS_EMBEDDED +env_t *env_ptr = &environment; +#else /* ! ENV_IS_EMBEDDED */ +env_t *env_ptr = (env_t *)CONFIG_ENV_ADDR; +#endif /* ENV_IS_EMBEDDED */ + +DECLARE_GLOBAL_DATA_PTR; + +#if !defined(CONFIG_ENV_OFFSET) +#define CONFIG_ENV_OFFSET 0 +#endif + +int env_init(void) +{ + if (crc32(0, env_ptr->data, ENV_SIZE) == env_ptr->crc) { + gd->env_addr = (ulong)&(env_ptr->data); + gd->env_valid = 1; + return 0; + } + + gd->env_addr = (ulong)default_environment; + gd->env_valid = 0; + return 0; +} + +#ifdef CONFIG_CMD_SAVEENV +int saveenv(void) +{ +#ifdef CONFIG_SRIO_PCIE_BOOT_SLAVE + printf("Can not support the 'saveenv' when boot from SRIO or PCIE!\n"); + return 1; +#else + return 0; +#endif +} +#endif /* CONFIG_CMD_SAVEENV */ + +void env_relocate_spec(void) +{ +#ifndef ENV_IS_EMBEDDED + env_import((char *)env_ptr, 1); +#endif +} diff --git a/qemu/roms/u-boot/common/env_sf.c b/qemu/roms/u-boot/common/env_sf.c new file mode 100644 index 000000000..37ab13ae1 --- /dev/null +++ b/qemu/roms/u-boot/common/env_sf.c @@ -0,0 +1,330 @@ +/* + * (C) Copyright 2000-2010 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * (C) Copyright 2001 Sysgo Real-Time Solutions, GmbH <www.elinos.com> + * Andreas Heppel <aheppel@sysgo.de> + * + * (C) Copyright 2008 Atmel Corporation + * + * SPDX-License-Identifier: GPL-2.0+ + */ +#include <common.h> +#include <environment.h> +#include <malloc.h> +#include <spi_flash.h> +#include <search.h> +#include <errno.h> + +#ifndef CONFIG_ENV_SPI_BUS +# define CONFIG_ENV_SPI_BUS 0 +#endif +#ifndef CONFIG_ENV_SPI_CS +# define CONFIG_ENV_SPI_CS 0 +#endif +#ifndef CONFIG_ENV_SPI_MAX_HZ +# define CONFIG_ENV_SPI_MAX_HZ 1000000 +#endif +#ifndef CONFIG_ENV_SPI_MODE +# define CONFIG_ENV_SPI_MODE SPI_MODE_3 +#endif + +#ifdef CONFIG_ENV_OFFSET_REDUND +static ulong env_offset = CONFIG_ENV_OFFSET; +static ulong env_new_offset = CONFIG_ENV_OFFSET_REDUND; + +#define ACTIVE_FLAG 1 +#define OBSOLETE_FLAG 0 +#endif /* CONFIG_ENV_OFFSET_REDUND */ + +DECLARE_GLOBAL_DATA_PTR; + +char *env_name_spec = "SPI Flash"; + +static struct spi_flash *env_flash; + +#if defined(CONFIG_ENV_OFFSET_REDUND) +int saveenv(void) +{ + env_t env_new; + char *saved_buffer = NULL, flag = OBSOLETE_FLAG; + u32 saved_size, saved_offset, sector = 1; + int ret; + + if (!env_flash) { + env_flash = spi_flash_probe(CONFIG_ENV_SPI_BUS, + CONFIG_ENV_SPI_CS, + CONFIG_ENV_SPI_MAX_HZ, CONFIG_ENV_SPI_MODE); + if (!env_flash) { + set_default_env("!spi_flash_probe() failed"); + return 1; + } + } + + ret = env_export(&env_new); + if (ret) + return ret; + env_new.flags = ACTIVE_FLAG; + + if (gd->env_valid == 1) { + env_new_offset = CONFIG_ENV_OFFSET_REDUND; + env_offset = CONFIG_ENV_OFFSET; + } else { + env_new_offset = CONFIG_ENV_OFFSET; + env_offset = CONFIG_ENV_OFFSET_REDUND; + } + + /* Is the sector larger than the env (i.e. embedded) */ + if (CONFIG_ENV_SECT_SIZE > CONFIG_ENV_SIZE) { + saved_size = CONFIG_ENV_SECT_SIZE - CONFIG_ENV_SIZE; + saved_offset = env_new_offset + CONFIG_ENV_SIZE; + saved_buffer = malloc(saved_size); + if (!saved_buffer) { + ret = 1; + goto done; + } + ret = spi_flash_read(env_flash, saved_offset, + saved_size, saved_buffer); + if (ret) + goto done; + } + + if (CONFIG_ENV_SIZE > CONFIG_ENV_SECT_SIZE) { + sector = CONFIG_ENV_SIZE / CONFIG_ENV_SECT_SIZE; + if (CONFIG_ENV_SIZE % CONFIG_ENV_SECT_SIZE) + sector++; + } + + puts("Erasing SPI flash..."); + ret = spi_flash_erase(env_flash, env_new_offset, + sector * CONFIG_ENV_SECT_SIZE); + if (ret) + goto done; + + puts("Writing to SPI flash..."); + + ret = spi_flash_write(env_flash, env_new_offset, + CONFIG_ENV_SIZE, &env_new); + if (ret) + goto done; + + if (CONFIG_ENV_SECT_SIZE > CONFIG_ENV_SIZE) { + ret = spi_flash_write(env_flash, saved_offset, + saved_size, saved_buffer); + if (ret) + goto done; + } + + ret = spi_flash_write(env_flash, env_offset + offsetof(env_t, flags), + sizeof(env_new.flags), &flag); + if (ret) + goto done; + + puts("done\n"); + + gd->env_valid = gd->env_valid == 2 ? 1 : 2; + + printf("Valid environment: %d\n", (int)gd->env_valid); + + done: + if (saved_buffer) + free(saved_buffer); + + return ret; +} + +void env_relocate_spec(void) +{ + int ret; + int crc1_ok = 0, crc2_ok = 0; + env_t *tmp_env1 = NULL; + env_t *tmp_env2 = NULL; + env_t *ep = NULL; + + tmp_env1 = (env_t *)malloc(CONFIG_ENV_SIZE); + tmp_env2 = (env_t *)malloc(CONFIG_ENV_SIZE); + + if (!tmp_env1 || !tmp_env2) { + set_default_env("!malloc() failed"); + goto out; + } + + env_flash = spi_flash_probe(CONFIG_ENV_SPI_BUS, CONFIG_ENV_SPI_CS, + CONFIG_ENV_SPI_MAX_HZ, CONFIG_ENV_SPI_MODE); + if (!env_flash) { + set_default_env("!spi_flash_probe() failed"); + goto out; + } + + ret = spi_flash_read(env_flash, CONFIG_ENV_OFFSET, + CONFIG_ENV_SIZE, tmp_env1); + if (ret) { + set_default_env("!spi_flash_read() failed"); + goto err_read; + } + + if (crc32(0, tmp_env1->data, ENV_SIZE) == tmp_env1->crc) + crc1_ok = 1; + + ret = spi_flash_read(env_flash, CONFIG_ENV_OFFSET_REDUND, + CONFIG_ENV_SIZE, tmp_env2); + if (!ret) { + if (crc32(0, tmp_env2->data, ENV_SIZE) == tmp_env2->crc) + crc2_ok = 1; + } + + if (!crc1_ok && !crc2_ok) { + set_default_env("!bad CRC"); + goto err_read; + } else if (crc1_ok && !crc2_ok) { + gd->env_valid = 1; + } else if (!crc1_ok && crc2_ok) { + gd->env_valid = 2; + } else if (tmp_env1->flags == ACTIVE_FLAG && + tmp_env2->flags == OBSOLETE_FLAG) { + gd->env_valid = 1; + } else if (tmp_env1->flags == OBSOLETE_FLAG && + tmp_env2->flags == ACTIVE_FLAG) { + gd->env_valid = 2; + } else if (tmp_env1->flags == tmp_env2->flags) { + gd->env_valid = 2; + } else if (tmp_env1->flags == 0xFF) { + gd->env_valid = 2; + } else { + /* + * this differs from code in env_flash.c, but I think a sane + * default path is desirable. + */ + gd->env_valid = 2; + } + + if (gd->env_valid == 1) + ep = tmp_env1; + else + ep = tmp_env2; + + ret = env_import((char *)ep, 0); + if (!ret) { + error("Cannot import environment: errno = %d\n", errno); + set_default_env("env_import failed"); + } + +err_read: + spi_flash_free(env_flash); + env_flash = NULL; +out: + free(tmp_env1); + free(tmp_env2); +} +#else +int saveenv(void) +{ + u32 saved_size, saved_offset, sector = 1; + char *saved_buffer = NULL; + int ret = 1; + env_t env_new; + + if (!env_flash) { + env_flash = spi_flash_probe(CONFIG_ENV_SPI_BUS, + CONFIG_ENV_SPI_CS, + CONFIG_ENV_SPI_MAX_HZ, CONFIG_ENV_SPI_MODE); + if (!env_flash) { + set_default_env("!spi_flash_probe() failed"); + return 1; + } + } + + /* Is the sector larger than the env (i.e. embedded) */ + if (CONFIG_ENV_SECT_SIZE > CONFIG_ENV_SIZE) { + saved_size = CONFIG_ENV_SECT_SIZE - CONFIG_ENV_SIZE; + saved_offset = CONFIG_ENV_OFFSET + CONFIG_ENV_SIZE; + saved_buffer = malloc(saved_size); + if (!saved_buffer) + goto done; + + ret = spi_flash_read(env_flash, saved_offset, + saved_size, saved_buffer); + if (ret) + goto done; + } + + if (CONFIG_ENV_SIZE > CONFIG_ENV_SECT_SIZE) { + sector = CONFIG_ENV_SIZE / CONFIG_ENV_SECT_SIZE; + if (CONFIG_ENV_SIZE % CONFIG_ENV_SECT_SIZE) + sector++; + } + + ret = env_export(&env_new); + if (ret) + goto done; + + puts("Erasing SPI flash..."); + ret = spi_flash_erase(env_flash, CONFIG_ENV_OFFSET, + sector * CONFIG_ENV_SECT_SIZE); + if (ret) + goto done; + + puts("Writing to SPI flash..."); + ret = spi_flash_write(env_flash, CONFIG_ENV_OFFSET, + CONFIG_ENV_SIZE, &env_new); + if (ret) + goto done; + + if (CONFIG_ENV_SECT_SIZE > CONFIG_ENV_SIZE) { + ret = spi_flash_write(env_flash, saved_offset, + saved_size, saved_buffer); + if (ret) + goto done; + } + + ret = 0; + puts("done\n"); + + done: + if (saved_buffer) + free(saved_buffer); + + return ret; +} + +void env_relocate_spec(void) +{ + int ret; + char *buf = NULL; + + buf = (char *)malloc(CONFIG_ENV_SIZE); + env_flash = spi_flash_probe(CONFIG_ENV_SPI_BUS, CONFIG_ENV_SPI_CS, + CONFIG_ENV_SPI_MAX_HZ, CONFIG_ENV_SPI_MODE); + if (!env_flash) { + set_default_env("!spi_flash_probe() failed"); + if (buf) + free(buf); + return; + } + + ret = spi_flash_read(env_flash, + CONFIG_ENV_OFFSET, CONFIG_ENV_SIZE, buf); + if (ret) { + set_default_env("!spi_flash_read() failed"); + goto out; + } + + ret = env_import(buf, 1); + if (ret) + gd->env_valid = 1; +out: + spi_flash_free(env_flash); + if (buf) + free(buf); + env_flash = NULL; +} +#endif + +int env_init(void) +{ + /* SPI flash isn't usable before relocation */ + gd->env_addr = (ulong)&default_environment[0]; + gd->env_valid = 1; + + return 0; +} diff --git a/qemu/roms/u-boot/common/env_ubi.c b/qemu/roms/u-boot/common/env_ubi.c new file mode 100644 index 000000000..77bbfa6ef --- /dev/null +++ b/qemu/roms/u-boot/common/env_ubi.c @@ -0,0 +1,193 @@ +/* + * (c) Copyright 2012 by National Instruments, + * Joe Hershberger <joe.hershberger@ni.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> + +#include <command.h> +#include <environment.h> +#include <errno.h> +#include <malloc.h> +#include <search.h> +#include <ubi_uboot.h> +#undef crc32 + +char *env_name_spec = "UBI"; + +env_t *env_ptr; + +DECLARE_GLOBAL_DATA_PTR; + +int env_init(void) +{ + /* use default */ + gd->env_addr = (ulong)&default_environment[0]; + gd->env_valid = 1; + + return 0; +} + +#ifdef CONFIG_CMD_SAVEENV +#ifdef CONFIG_SYS_REDUNDAND_ENVIRONMENT +static unsigned char env_flags; + +int saveenv(void) +{ + ALLOC_CACHE_ALIGN_BUFFER(env_t, env_new, 1); + int ret; + + ret = env_export(env_new); + if (ret) + return ret; + + if (ubi_part(CONFIG_ENV_UBI_PART, NULL)) { + printf("\n** Cannot find mtd partition \"%s\"\n", + CONFIG_ENV_UBI_PART); + return 1; + } + + env_new->flags = ++env_flags; /* increase the serial */ + + if (gd->env_valid == 1) { + puts("Writing to redundant UBI... "); + if (ubi_volume_write(CONFIG_ENV_UBI_VOLUME_REDUND, + (void *)env_new, CONFIG_ENV_SIZE)) { + printf("\n** Unable to write env to %s:%s **\n", + CONFIG_ENV_UBI_PART, + CONFIG_ENV_UBI_VOLUME_REDUND); + return 1; + } + } else { + puts("Writing to UBI... "); + if (ubi_volume_write(CONFIG_ENV_UBI_VOLUME, + (void *)env_new, CONFIG_ENV_SIZE)) { + printf("\n** Unable to write env to %s:%s **\n", + CONFIG_ENV_UBI_PART, + CONFIG_ENV_UBI_VOLUME); + return 1; + } + } + + puts("done\n"); + + gd->env_valid = gd->env_valid == 2 ? 1 : 2; + + return 0; +} +#else /* ! CONFIG_SYS_REDUNDAND_ENVIRONMENT */ +int saveenv(void) +{ + ALLOC_CACHE_ALIGN_BUFFER(env_t, env_new, 1); + int ret; + + ret = env_export(env_new); + if (ret) + return ret; + + if (ubi_part(CONFIG_ENV_UBI_PART, NULL)) { + printf("\n** Cannot find mtd partition \"%s\"\n", + CONFIG_ENV_UBI_PART); + return 1; + } + + if (ubi_volume_write(CONFIG_ENV_UBI_VOLUME, (void *)env_new, + CONFIG_ENV_SIZE)) { + printf("\n** Unable to write env to %s:%s **\n", + CONFIG_ENV_UBI_PART, CONFIG_ENV_UBI_VOLUME); + return 1; + } + + puts("done\n"); + return 0; +} +#endif /* CONFIG_SYS_REDUNDAND_ENVIRONMENT */ +#endif /* CONFIG_CMD_SAVEENV */ + +#ifdef CONFIG_SYS_REDUNDAND_ENVIRONMENT +void env_relocate_spec(void) +{ + ALLOC_CACHE_ALIGN_BUFFER(char, env1_buf, CONFIG_ENV_SIZE); + ALLOC_CACHE_ALIGN_BUFFER(char, env2_buf, CONFIG_ENV_SIZE); + int crc1_ok = 0, crc2_ok = 0; + env_t *ep, *tmp_env1, *tmp_env2; + + tmp_env1 = (env_t *)env1_buf; + tmp_env2 = (env_t *)env2_buf; + + if (ubi_part(CONFIG_ENV_UBI_PART, NULL)) { + printf("\n** Cannot find mtd partition \"%s\"\n", + CONFIG_ENV_UBI_PART); + set_default_env(NULL); + return; + } + + if (ubi_volume_read(CONFIG_ENV_UBI_VOLUME, (void *)tmp_env1, + CONFIG_ENV_SIZE)) { + printf("\n** Unable to read env from %s:%s **\n", + CONFIG_ENV_UBI_PART, CONFIG_ENV_UBI_VOLUME); + } + + if (ubi_volume_read(CONFIG_ENV_UBI_VOLUME_REDUND, (void *)tmp_env2, + CONFIG_ENV_SIZE)) { + printf("\n** Unable to read redundant env from %s:%s **\n", + CONFIG_ENV_UBI_PART, CONFIG_ENV_UBI_VOLUME_REDUND); + } + + crc1_ok = crc32(0, tmp_env1->data, ENV_SIZE) == tmp_env1->crc; + crc2_ok = crc32(0, tmp_env2->data, ENV_SIZE) == tmp_env2->crc; + + if (!crc1_ok && !crc2_ok) { + set_default_env("!bad CRC"); + return; + } else if (crc1_ok && !crc2_ok) { + gd->env_valid = 1; + } else if (!crc1_ok && crc2_ok) { + gd->env_valid = 2; + } else { + /* both ok - check serial */ + if (tmp_env1->flags == 255 && tmp_env2->flags == 0) + gd->env_valid = 2; + else if (tmp_env2->flags == 255 && tmp_env1->flags == 0) + gd->env_valid = 1; + else if (tmp_env1->flags > tmp_env2->flags) + gd->env_valid = 1; + else if (tmp_env2->flags > tmp_env1->flags) + gd->env_valid = 2; + else /* flags are equal - almost impossible */ + gd->env_valid = 1; + } + + if (gd->env_valid == 1) + ep = tmp_env1; + else + ep = tmp_env2; + + env_flags = ep->flags; + env_import((char *)ep, 0); +} +#else /* ! CONFIG_SYS_REDUNDAND_ENVIRONMENT */ +void env_relocate_spec(void) +{ + ALLOC_CACHE_ALIGN_BUFFER(char, buf, CONFIG_ENV_SIZE); + + if (ubi_part(CONFIG_ENV_UBI_PART, NULL)) { + printf("\n** Cannot find mtd partition \"%s\"\n", + CONFIG_ENV_UBI_PART); + set_default_env(NULL); + return; + } + + if (ubi_volume_read(CONFIG_ENV_UBI_VOLUME, (void *)&buf, + CONFIG_ENV_SIZE)) { + printf("\n** Unable to read env from %s:%s **\n", + CONFIG_ENV_UBI_PART, CONFIG_ENV_UBI_VOLUME); + set_default_env(NULL); + return; + } + + env_import(buf, 1); +} +#endif /* CONFIG_SYS_REDUNDAND_ENVIRONMENT */ diff --git a/qemu/roms/u-boot/common/exports.c b/qemu/roms/u-boot/common/exports.c new file mode 100644 index 000000000..b97ca4830 --- /dev/null +++ b/qemu/roms/u-boot/common/exports.c @@ -0,0 +1,43 @@ +#include <common.h> +#include <exports.h> +#include <spi.h> + +DECLARE_GLOBAL_DATA_PTR; + +__attribute__((unused)) static void dummy(void) +{ +} + +unsigned long get_version(void) +{ + return XF_VERSION; +} + +/* Reuse _exports.h with a little trickery to avoid bitrot */ +#define EXPORT_FUNC(sym) gd->jt[XF_##sym] = (void *)sym; + +#if !defined(CONFIG_X86) && !defined(CONFIG_PPC) +# define install_hdlr dummy +# define free_hdlr dummy +#else /* kludge for non-standard function naming */ +# define install_hdlr irq_install_handler +# define free_hdlr irq_free_handler +#endif +#ifndef CONFIG_CMD_I2C +# define i2c_write dummy +# define i2c_read dummy +#endif +#ifndef CONFIG_CMD_SPI +# define spi_init dummy +# define spi_setup_slave dummy +# define spi_free_slave dummy +# define spi_claim_bus dummy +# define spi_release_bus dummy +# define spi_xfer dummy +#endif + +void jumptable_init(void) +{ + gd->jt = malloc(XF_MAX * sizeof(void *)); +#include <_exports.h> +} diff --git a/qemu/roms/u-boot/common/fdt_support.c b/qemu/roms/u-boot/common/fdt_support.c new file mode 100644 index 000000000..fcd252336 --- /dev/null +++ b/qemu/roms/u-boot/common/fdt_support.c @@ -0,0 +1,1531 @@ +/* + * (C) Copyright 2007 + * Gerald Van Baren, Custom IDEAS, vanbaren@cideas.com + * + * Copyright 2010-2011 Freescale Semiconductor, Inc. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <stdio_dev.h> +#include <linux/ctype.h> +#include <linux/types.h> +#include <asm/global_data.h> +#include <libfdt.h> +#include <fdt_support.h> +#include <exports.h> + +/* + * Global data (for the gd->bd) + */ +DECLARE_GLOBAL_DATA_PTR; + +/* + * Get cells len in bytes + * if #NNNN-cells property is 2 then len is 8 + * otherwise len is 4 + */ +static int get_cells_len(void *blob, char *nr_cells_name) +{ + const fdt32_t *cell; + + cell = fdt_getprop(blob, 0, nr_cells_name, NULL); + if (cell && fdt32_to_cpu(*cell) == 2) + return 8; + + return 4; +} + +/* + * Write a 4 or 8 byte big endian cell + */ +static void write_cell(u8 *addr, u64 val, int size) +{ + int shift = (size - 1) * 8; + while (size-- > 0) { + *addr++ = (val >> shift) & 0xff; + shift -= 8; + } +} + +/** + * fdt_getprop_u32_default_node - Return a node's property or a default + * + * @fdt: ptr to device tree + * @off: offset of node + * @cell: cell offset in property + * @prop: property name + * @dflt: default value if the property isn't found + * + * Convenience function to return a node's property or a default value if + * the property doesn't exist. + */ +u32 fdt_getprop_u32_default_node(const void *fdt, int off, int cell, + const char *prop, const u32 dflt) +{ + const fdt32_t *val; + int len; + + val = fdt_getprop(fdt, off, prop, &len); + + /* Check if property exists */ + if (!val) + return dflt; + + /* Check if property is long enough */ + if (len < ((cell + 1) * sizeof(uint32_t))) + return dflt; + + return fdt32_to_cpu(*val); +} + +/** + * fdt_getprop_u32_default - Find a node and return it's property or a default + * + * @fdt: ptr to device tree + * @path: path of node + * @prop: property name + * @dflt: default value if the property isn't found + * + * Convenience function to find a node and return it's property or a + * default value if it doesn't exist. + */ +u32 fdt_getprop_u32_default(const void *fdt, const char *path, + const char *prop, const u32 dflt) +{ + int off; + + off = fdt_path_offset(fdt, path); + if (off < 0) + return dflt; + + return fdt_getprop_u32_default_node(fdt, off, 0, prop, dflt); +} + +/** + * fdt_find_and_setprop: Find a node and set it's property + * + * @fdt: ptr to device tree + * @node: path of node + * @prop: property name + * @val: ptr to new value + * @len: length of new property value + * @create: flag to create the property if it doesn't exist + * + * Convenience function to directly set a property given the path to the node. + */ +int fdt_find_and_setprop(void *fdt, const char *node, const char *prop, + const void *val, int len, int create) +{ + int nodeoff = fdt_path_offset(fdt, node); + + if (nodeoff < 0) + return nodeoff; + + if ((!create) && (fdt_get_property(fdt, nodeoff, prop, NULL) == NULL)) + return 0; /* create flag not set; so exit quietly */ + + return fdt_setprop(fdt, nodeoff, prop, val, len); +} + +#ifdef CONFIG_OF_STDOUT_VIA_ALIAS + +#ifdef CONFIG_CONS_INDEX +static void fdt_fill_multisername(char *sername, size_t maxlen) +{ + const char *outname = stdio_devices[stdout]->name; + + if (strcmp(outname, "serial") > 0) + strncpy(sername, outname, maxlen); + + /* eserial? */ + if (strcmp(outname + 1, "serial") > 0) + strncpy(sername, outname + 1, maxlen); +} +#endif + +static int fdt_fixup_stdout(void *fdt, int chosenoff) +{ + int err = 0; +#ifdef CONFIG_CONS_INDEX + int node; + char sername[9] = { 0 }; + const char *path; + + fdt_fill_multisername(sername, sizeof(sername) - 1); + if (!sername[0]) + sprintf(sername, "serial%d", CONFIG_CONS_INDEX - 1); + + err = node = fdt_path_offset(fdt, "/aliases"); + if (node >= 0) { + int len; + path = fdt_getprop(fdt, node, sername, &len); + if (path) { + char *p = malloc(len); + err = -FDT_ERR_NOSPACE; + if (p) { + memcpy(p, path, len); + err = fdt_setprop(fdt, chosenoff, + "linux,stdout-path", p, len); + free(p); + } + } else { + err = len; + } + } +#endif + if (err < 0) + printf("WARNING: could not set linux,stdout-path %s.\n", + fdt_strerror(err)); + + return err; +} +#endif + +int fdt_initrd(void *fdt, ulong initrd_start, ulong initrd_end, int force) +{ + int nodeoffset, addr_cell_len; + int err, j, total; + fdt64_t tmp; + const char *path; + uint64_t addr, size; + + /* Find the "chosen" node. */ + nodeoffset = fdt_path_offset (fdt, "/chosen"); + + /* If there is no "chosen" node in the blob return */ + if (nodeoffset < 0) { + printf("fdt_initrd: %s\n", fdt_strerror(nodeoffset)); + return nodeoffset; + } + + /* just return if initrd_start/end aren't valid */ + if ((initrd_start == 0) || (initrd_end == 0)) + return 0; + + total = fdt_num_mem_rsv(fdt); + + /* + * Look for an existing entry and update it. If we don't find + * the entry, we will j be the next available slot. + */ + for (j = 0; j < total; j++) { + err = fdt_get_mem_rsv(fdt, j, &addr, &size); + if (addr == initrd_start) { + fdt_del_mem_rsv(fdt, j); + break; + } + } + + err = fdt_add_mem_rsv(fdt, initrd_start, initrd_end - initrd_start); + if (err < 0) { + printf("fdt_initrd: %s\n", fdt_strerror(err)); + return err; + } + + addr_cell_len = get_cells_len(fdt, "#address-cells"); + + path = fdt_getprop(fdt, nodeoffset, "linux,initrd-start", NULL); + if ((path == NULL) || force) { + write_cell((u8 *)&tmp, initrd_start, addr_cell_len); + err = fdt_setprop(fdt, nodeoffset, + "linux,initrd-start", &tmp, addr_cell_len); + if (err < 0) { + printf("WARNING: " + "could not set linux,initrd-start %s.\n", + fdt_strerror(err)); + return err; + } + write_cell((u8 *)&tmp, initrd_end, addr_cell_len); + err = fdt_setprop(fdt, nodeoffset, + "linux,initrd-end", &tmp, addr_cell_len); + if (err < 0) { + printf("WARNING: could not set linux,initrd-end %s.\n", + fdt_strerror(err)); + + return err; + } + } + + return 0; +} + +int fdt_chosen(void *fdt, int force) +{ + int nodeoffset; + int err; + char *str; /* used to set string properties */ + const char *path; + + err = fdt_check_header(fdt); + if (err < 0) { + printf("fdt_chosen: %s\n", fdt_strerror(err)); + return err; + } + + /* + * Find the "chosen" node. + */ + nodeoffset = fdt_path_offset (fdt, "/chosen"); + + /* + * If there is no "chosen" node in the blob, create it. + */ + if (nodeoffset < 0) { + /* + * Create a new node "/chosen" (offset 0 is root level) + */ + nodeoffset = fdt_add_subnode(fdt, 0, "chosen"); + if (nodeoffset < 0) { + printf("WARNING: could not create /chosen %s.\n", + fdt_strerror(nodeoffset)); + return nodeoffset; + } + } + + /* + * Create /chosen properites that don't exist in the fdt. + * If the property exists, update it only if the "force" parameter + * is true. + */ + str = getenv("bootargs"); + if (str != NULL) { + path = fdt_getprop(fdt, nodeoffset, "bootargs", NULL); + if ((path == NULL) || force) { + err = fdt_setprop(fdt, nodeoffset, + "bootargs", str, strlen(str)+1); + if (err < 0) + printf("WARNING: could not set bootargs %s.\n", + fdt_strerror(err)); + } + } + +#ifdef CONFIG_OF_STDOUT_VIA_ALIAS + path = fdt_getprop(fdt, nodeoffset, "linux,stdout-path", NULL); + if ((path == NULL) || force) + err = fdt_fixup_stdout(fdt, nodeoffset); +#endif + +#ifdef OF_STDOUT_PATH + path = fdt_getprop(fdt, nodeoffset, "linux,stdout-path", NULL); + if ((path == NULL) || force) { + err = fdt_setprop(fdt, nodeoffset, + "linux,stdout-path", OF_STDOUT_PATH, strlen(OF_STDOUT_PATH)+1); + if (err < 0) + printf("WARNING: could not set linux,stdout-path %s.\n", + fdt_strerror(err)); + } +#endif + + return err; +} + +void do_fixup_by_path(void *fdt, const char *path, const char *prop, + const void *val, int len, int create) +{ +#if defined(DEBUG) + int i; + debug("Updating property '%s/%s' = ", path, prop); + for (i = 0; i < len; i++) + debug(" %.2x", *(u8*)(val+i)); + debug("\n"); +#endif + int rc = fdt_find_and_setprop(fdt, path, prop, val, len, create); + if (rc) + printf("Unable to update property %s:%s, err=%s\n", + path, prop, fdt_strerror(rc)); +} + +void do_fixup_by_path_u32(void *fdt, const char *path, const char *prop, + u32 val, int create) +{ + fdt32_t tmp = cpu_to_fdt32(val); + do_fixup_by_path(fdt, path, prop, &tmp, sizeof(tmp), create); +} + +void do_fixup_by_prop(void *fdt, + const char *pname, const void *pval, int plen, + const char *prop, const void *val, int len, + int create) +{ + int off; +#if defined(DEBUG) + int i; + debug("Updating property '%s' = ", prop); + for (i = 0; i < len; i++) + debug(" %.2x", *(u8*)(val+i)); + debug("\n"); +#endif + off = fdt_node_offset_by_prop_value(fdt, -1, pname, pval, plen); + while (off != -FDT_ERR_NOTFOUND) { + if (create || (fdt_get_property(fdt, off, prop, NULL) != NULL)) + fdt_setprop(fdt, off, prop, val, len); + off = fdt_node_offset_by_prop_value(fdt, off, pname, pval, plen); + } +} + +void do_fixup_by_prop_u32(void *fdt, + const char *pname, const void *pval, int plen, + const char *prop, u32 val, int create) +{ + fdt32_t tmp = cpu_to_fdt32(val); + do_fixup_by_prop(fdt, pname, pval, plen, prop, &tmp, 4, create); +} + +void do_fixup_by_compat(void *fdt, const char *compat, + const char *prop, const void *val, int len, int create) +{ + int off = -1; +#if defined(DEBUG) + int i; + debug("Updating property '%s' = ", prop); + for (i = 0; i < len; i++) + debug(" %.2x", *(u8*)(val+i)); + debug("\n"); +#endif + off = fdt_node_offset_by_compatible(fdt, -1, compat); + while (off != -FDT_ERR_NOTFOUND) { + if (create || (fdt_get_property(fdt, off, prop, NULL) != NULL)) + fdt_setprop(fdt, off, prop, val, len); + off = fdt_node_offset_by_compatible(fdt, off, compat); + } +} + +void do_fixup_by_compat_u32(void *fdt, const char *compat, + const char *prop, u32 val, int create) +{ + fdt32_t tmp = cpu_to_fdt32(val); + do_fixup_by_compat(fdt, compat, prop, &tmp, 4, create); +} + +#ifdef CONFIG_NR_DRAM_BANKS +#define MEMORY_BANKS_MAX CONFIG_NR_DRAM_BANKS +#else +#define MEMORY_BANKS_MAX 4 +#endif +int fdt_fixup_memory_banks(void *blob, u64 start[], u64 size[], int banks) +{ + int err, nodeoffset; + int addr_cell_len, size_cell_len, len; + u8 tmp[MEMORY_BANKS_MAX * 16]; /* Up to 64-bit address + 64-bit size */ + int bank; + + if (banks > MEMORY_BANKS_MAX) { + printf("%s: num banks %d exceeds hardcoded limit %d." + " Recompile with higher MEMORY_BANKS_MAX?\n", + __FUNCTION__, banks, MEMORY_BANKS_MAX); + return -1; + } + + err = fdt_check_header(blob); + if (err < 0) { + printf("%s: %s\n", __FUNCTION__, fdt_strerror(err)); + return err; + } + + /* update, or add and update /memory node */ + nodeoffset = fdt_path_offset(blob, "/memory"); + if (nodeoffset < 0) { + nodeoffset = fdt_add_subnode(blob, 0, "memory"); + if (nodeoffset < 0) { + printf("WARNING: could not create /memory: %s.\n", + fdt_strerror(nodeoffset)); + return nodeoffset; + } + } + err = fdt_setprop(blob, nodeoffset, "device_type", "memory", + sizeof("memory")); + if (err < 0) { + printf("WARNING: could not set %s %s.\n", "device_type", + fdt_strerror(err)); + return err; + } + + addr_cell_len = get_cells_len(blob, "#address-cells"); + size_cell_len = get_cells_len(blob, "#size-cells"); + + for (bank = 0, len = 0; bank < banks; bank++) { + write_cell(tmp + len, start[bank], addr_cell_len); + len += addr_cell_len; + + write_cell(tmp + len, size[bank], size_cell_len); + len += size_cell_len; + } + + err = fdt_setprop(blob, nodeoffset, "reg", tmp, len); + if (err < 0) { + printf("WARNING: could not set %s %s.\n", + "reg", fdt_strerror(err)); + return err; + } + return 0; +} + +int fdt_fixup_memory(void *blob, u64 start, u64 size) +{ + return fdt_fixup_memory_banks(blob, &start, &size, 1); +} + +void fdt_fixup_ethernet(void *fdt) +{ + int node, i, j; + char enet[16], *tmp, *end; + char mac[16]; + const char *path; + unsigned char mac_addr[6]; + + node = fdt_path_offset(fdt, "/aliases"); + if (node < 0) + return; + + i = 0; + strcpy(mac, "ethaddr"); + while ((tmp = getenv(mac)) != NULL) { + sprintf(enet, "ethernet%d", i); + path = fdt_getprop(fdt, node, enet, NULL); + if (!path) { + debug("No alias for %s\n", enet); + sprintf(mac, "eth%daddr", ++i); + continue; + } + + for (j = 0; j < 6; j++) { + mac_addr[j] = tmp ? simple_strtoul(tmp, &end, 16) : 0; + if (tmp) + tmp = (*end) ? end+1 : end; + } + + do_fixup_by_path(fdt, path, "mac-address", &mac_addr, 6, 0); + do_fixup_by_path(fdt, path, "local-mac-address", + &mac_addr, 6, 1); + + sprintf(mac, "eth%daddr", ++i); + } +} + +/* Resize the fdt to its actual size + a bit of padding */ +int fdt_resize(void *blob) +{ + int i; + uint64_t addr, size; + int total, ret; + uint actualsize; + + if (!blob) + return 0; + + total = fdt_num_mem_rsv(blob); + for (i = 0; i < total; i++) { + fdt_get_mem_rsv(blob, i, &addr, &size); + if (addr == (uintptr_t)blob) { + fdt_del_mem_rsv(blob, i); + break; + } + } + + /* + * Calculate the actual size of the fdt + * plus the size needed for 5 fdt_add_mem_rsv, one + * for the fdt itself and 4 for a possible initrd + * ((initrd-start + initrd-end) * 2 (name & value)) + */ + actualsize = fdt_off_dt_strings(blob) + + fdt_size_dt_strings(blob) + 5 * sizeof(struct fdt_reserve_entry); + + /* Make it so the fdt ends on a page boundary */ + actualsize = ALIGN(actualsize + ((uintptr_t)blob & 0xfff), 0x1000); + actualsize = actualsize - ((uintptr_t)blob & 0xfff); + + /* Change the fdt header to reflect the correct size */ + fdt_set_totalsize(blob, actualsize); + + /* Add the new reservation */ + ret = fdt_add_mem_rsv(blob, (uintptr_t)blob, actualsize); + if (ret < 0) + return ret; + + return actualsize; +} + +#ifdef CONFIG_PCI +#define CONFIG_SYS_PCI_NR_INBOUND_WIN 4 + +#define FDT_PCI_PREFETCH (0x40000000) +#define FDT_PCI_MEM32 (0x02000000) +#define FDT_PCI_IO (0x01000000) +#define FDT_PCI_MEM64 (0x03000000) + +int fdt_pci_dma_ranges(void *blob, int phb_off, struct pci_controller *hose) { + + int addrcell, sizecell, len, r; + u32 *dma_range; + /* sized based on pci addr cells, size-cells, & address-cells */ + u32 dma_ranges[(3 + 2 + 2) * CONFIG_SYS_PCI_NR_INBOUND_WIN]; + + addrcell = fdt_getprop_u32_default(blob, "/", "#address-cells", 1); + sizecell = fdt_getprop_u32_default(blob, "/", "#size-cells", 1); + + dma_range = &dma_ranges[0]; + for (r = 0; r < hose->region_count; r++) { + u64 bus_start, phys_start, size; + + /* skip if !PCI_REGION_SYS_MEMORY */ + if (!(hose->regions[r].flags & PCI_REGION_SYS_MEMORY)) + continue; + + bus_start = (u64)hose->regions[r].bus_start; + phys_start = (u64)hose->regions[r].phys_start; + size = (u64)hose->regions[r].size; + + dma_range[0] = 0; + if (size >= 0x100000000ull) + dma_range[0] |= FDT_PCI_MEM64; + else + dma_range[0] |= FDT_PCI_MEM32; + if (hose->regions[r].flags & PCI_REGION_PREFETCH) + dma_range[0] |= FDT_PCI_PREFETCH; +#ifdef CONFIG_SYS_PCI_64BIT + dma_range[1] = bus_start >> 32; +#else + dma_range[1] = 0; +#endif + dma_range[2] = bus_start & 0xffffffff; + + if (addrcell == 2) { + dma_range[3] = phys_start >> 32; + dma_range[4] = phys_start & 0xffffffff; + } else { + dma_range[3] = phys_start & 0xffffffff; + } + + if (sizecell == 2) { + dma_range[3 + addrcell + 0] = size >> 32; + dma_range[3 + addrcell + 1] = size & 0xffffffff; + } else { + dma_range[3 + addrcell + 0] = size & 0xffffffff; + } + + dma_range += (3 + addrcell + sizecell); + } + + len = dma_range - &dma_ranges[0]; + if (len) + fdt_setprop(blob, phb_off, "dma-ranges", &dma_ranges[0], len*4); + + return 0; +} +#endif + +#ifdef CONFIG_FDT_FIXUP_NOR_FLASH_SIZE +/* + * Provide a weak default function to return the flash bank size. + * There might be multiple non-identical flash chips connected to one + * chip-select, so we need to pass an index as well. + */ +u32 __flash_get_bank_size(int cs, int idx) +{ + extern flash_info_t flash_info[]; + + /* + * As default, a simple 1:1 mapping is provided. Boards with + * a different mapping need to supply a board specific mapping + * routine. + */ + return flash_info[cs].size; +} +u32 flash_get_bank_size(int cs, int idx) + __attribute__((weak, alias("__flash_get_bank_size"))); + +/* + * This function can be used to update the size in the "reg" property + * of all NOR FLASH device nodes. This is necessary for boards with + * non-fixed NOR FLASH sizes. + */ +int fdt_fixup_nor_flash_size(void *blob) +{ + char compat[][16] = { "cfi-flash", "jedec-flash" }; + int off; + int len; + struct fdt_property *prop; + u32 *reg, *reg2; + int i; + + for (i = 0; i < 2; i++) { + off = fdt_node_offset_by_compatible(blob, -1, compat[i]); + while (off != -FDT_ERR_NOTFOUND) { + int idx; + + /* + * Found one compatible node, so fixup the size + * int its reg properties + */ + prop = fdt_get_property_w(blob, off, "reg", &len); + if (prop) { + int tuple_size = 3 * sizeof(reg); + + /* + * There might be multiple reg-tuples, + * so loop through them all + */ + reg = reg2 = (u32 *)&prop->data[0]; + for (idx = 0; idx < (len / tuple_size); idx++) { + /* + * Update size in reg property + */ + reg[2] = flash_get_bank_size(reg[0], + idx); + + /* + * Point to next reg tuple + */ + reg += 3; + } + + fdt_setprop(blob, off, "reg", reg2, len); + } + + /* Move to next compatible node */ + off = fdt_node_offset_by_compatible(blob, off, + compat[i]); + } + } + + return 0; +} +#endif + +int fdt_increase_size(void *fdt, int add_len) +{ + int newlen; + + newlen = fdt_totalsize(fdt) + add_len; + + /* Open in place with a new len */ + return fdt_open_into(fdt, fdt, newlen); +} + +#ifdef CONFIG_FDT_FIXUP_PARTITIONS +#include <jffs2/load_kernel.h> +#include <mtd_node.h> + +struct reg_cell { + unsigned int r0; + unsigned int r1; +}; + +int fdt_del_subnodes(const void *blob, int parent_offset) +{ + int off, ndepth; + int ret; + + for (ndepth = 0, off = fdt_next_node(blob, parent_offset, &ndepth); + (off >= 0) && (ndepth > 0); + off = fdt_next_node(blob, off, &ndepth)) { + if (ndepth == 1) { + debug("delete %s: offset: %x\n", + fdt_get_name(blob, off, 0), off); + ret = fdt_del_node((void *)blob, off); + if (ret < 0) { + printf("Can't delete node: %s\n", + fdt_strerror(ret)); + return ret; + } else { + ndepth = 0; + off = parent_offset; + } + } + } + return 0; +} + +int fdt_del_partitions(void *blob, int parent_offset) +{ + const void *prop; + int ndepth = 0; + int off; + int ret; + + off = fdt_next_node(blob, parent_offset, &ndepth); + if (off > 0 && ndepth == 1) { + prop = fdt_getprop(blob, off, "label", NULL); + if (prop == NULL) { + /* + * Could not find label property, nand {}; node? + * Check subnode, delete partitions there if any. + */ + return fdt_del_partitions(blob, off); + } else { + ret = fdt_del_subnodes(blob, parent_offset); + if (ret < 0) { + printf("Can't remove subnodes: %s\n", + fdt_strerror(ret)); + return ret; + } + } + } + return 0; +} + +int fdt_node_set_part_info(void *blob, int parent_offset, + struct mtd_device *dev) +{ + struct list_head *pentry; + struct part_info *part; + struct reg_cell cell; + int off, ndepth = 0; + int part_num, ret; + char buf[64]; + + ret = fdt_del_partitions(blob, parent_offset); + if (ret < 0) + return ret; + + /* + * Check if it is nand {}; subnode, adjust + * the offset in this case + */ + off = fdt_next_node(blob, parent_offset, &ndepth); + if (off > 0 && ndepth == 1) + parent_offset = off; + + part_num = 0; + list_for_each_prev(pentry, &dev->parts) { + int newoff; + + part = list_entry(pentry, struct part_info, link); + + debug("%2d: %-20s0x%08llx\t0x%08llx\t%d\n", + part_num, part->name, part->size, + part->offset, part->mask_flags); + + sprintf(buf, "partition@%llx", part->offset); +add_sub: + ret = fdt_add_subnode(blob, parent_offset, buf); + if (ret == -FDT_ERR_NOSPACE) { + ret = fdt_increase_size(blob, 512); + if (!ret) + goto add_sub; + else + goto err_size; + } else if (ret < 0) { + printf("Can't add partition node: %s\n", + fdt_strerror(ret)); + return ret; + } + newoff = ret; + + /* Check MTD_WRITEABLE_CMD flag */ + if (part->mask_flags & 1) { +add_ro: + ret = fdt_setprop(blob, newoff, "read_only", NULL, 0); + if (ret == -FDT_ERR_NOSPACE) { + ret = fdt_increase_size(blob, 512); + if (!ret) + goto add_ro; + else + goto err_size; + } else if (ret < 0) + goto err_prop; + } + + cell.r0 = cpu_to_fdt32(part->offset); + cell.r1 = cpu_to_fdt32(part->size); +add_reg: + ret = fdt_setprop(blob, newoff, "reg", &cell, sizeof(cell)); + if (ret == -FDT_ERR_NOSPACE) { + ret = fdt_increase_size(blob, 512); + if (!ret) + goto add_reg; + else + goto err_size; + } else if (ret < 0) + goto err_prop; + +add_label: + ret = fdt_setprop_string(blob, newoff, "label", part->name); + if (ret == -FDT_ERR_NOSPACE) { + ret = fdt_increase_size(blob, 512); + if (!ret) + goto add_label; + else + goto err_size; + } else if (ret < 0) + goto err_prop; + + part_num++; + } + return 0; +err_size: + printf("Can't increase blob size: %s\n", fdt_strerror(ret)); + return ret; +err_prop: + printf("Can't add property: %s\n", fdt_strerror(ret)); + return ret; +} + +/* + * Update partitions in nor/nand nodes using info from + * mtdparts environment variable. The nodes to update are + * specified by node_info structure which contains mtd device + * type and compatible string: E. g. the board code in + * ft_board_setup() could use: + * + * struct node_info nodes[] = { + * { "fsl,mpc5121-nfc", MTD_DEV_TYPE_NAND, }, + * { "cfi-flash", MTD_DEV_TYPE_NOR, }, + * }; + * + * fdt_fixup_mtdparts(blob, nodes, ARRAY_SIZE(nodes)); + */ +void fdt_fixup_mtdparts(void *blob, void *node_info, int node_info_size) +{ + struct node_info *ni = node_info; + struct mtd_device *dev; + char *parts; + int i, idx; + int noff; + + parts = getenv("mtdparts"); + if (!parts) + return; + + if (mtdparts_init() != 0) + return; + + for (i = 0; i < node_info_size; i++) { + idx = 0; + noff = fdt_node_offset_by_compatible(blob, -1, ni[i].compat); + while (noff != -FDT_ERR_NOTFOUND) { + debug("%s: %s, mtd dev type %d\n", + fdt_get_name(blob, noff, 0), + ni[i].compat, ni[i].type); + dev = device_find(ni[i].type, idx++); + if (dev) { + if (fdt_node_set_part_info(blob, noff, dev)) + return; /* return on error */ + } + + /* Jump to next flash node */ + noff = fdt_node_offset_by_compatible(blob, noff, + ni[i].compat); + } + } +} +#endif + +void fdt_del_node_and_alias(void *blob, const char *alias) +{ + int off = fdt_path_offset(blob, alias); + + if (off < 0) + return; + + fdt_del_node(blob, off); + + off = fdt_path_offset(blob, "/aliases"); + fdt_delprop(blob, off, alias); +} + +/* Helper to read a big number; size is in cells (not bytes) */ +static inline u64 of_read_number(const fdt32_t *cell, int size) +{ + u64 r = 0; + while (size--) + r = (r << 32) | fdt32_to_cpu(*(cell++)); + return r; +} + +#define PRu64 "%llx" + +/* Max address size we deal with */ +#define OF_MAX_ADDR_CELLS 4 +#define OF_BAD_ADDR ((u64)-1) +#define OF_CHECK_COUNTS(na, ns) ((na) > 0 && (na) <= OF_MAX_ADDR_CELLS && \ + (ns) > 0) + +/* Debug utility */ +#ifdef DEBUG +static void of_dump_addr(const char *s, const fdt32_t *addr, int na) +{ + printf("%s", s); + while(na--) + printf(" %08x", *(addr++)); + printf("\n"); +} +#else +static void of_dump_addr(const char *s, const fdt32_t *addr, int na) { } +#endif + +/* Callbacks for bus specific translators */ +struct of_bus { + const char *name; + const char *addresses; + void (*count_cells)(void *blob, int parentoffset, + int *addrc, int *sizec); + u64 (*map)(fdt32_t *addr, const fdt32_t *range, + int na, int ns, int pna); + int (*translate)(fdt32_t *addr, u64 offset, int na); +}; + +/* Default translator (generic bus) */ +static void of_bus_default_count_cells(void *blob, int parentoffset, + int *addrc, int *sizec) +{ + const fdt32_t *prop; + + if (addrc) { + prop = fdt_getprop(blob, parentoffset, "#address-cells", NULL); + if (prop) + *addrc = be32_to_cpup(prop); + else + *addrc = 2; + } + + if (sizec) { + prop = fdt_getprop(blob, parentoffset, "#size-cells", NULL); + if (prop) + *sizec = be32_to_cpup(prop); + else + *sizec = 1; + } +} + +static u64 of_bus_default_map(fdt32_t *addr, const fdt32_t *range, + int na, int ns, int pna) +{ + u64 cp, s, da; + + cp = of_read_number(range, na); + s = of_read_number(range + na + pna, ns); + da = of_read_number(addr, na); + + debug("OF: default map, cp="PRu64", s="PRu64", da="PRu64"\n", + cp, s, da); + + if (da < cp || da >= (cp + s)) + return OF_BAD_ADDR; + return da - cp; +} + +static int of_bus_default_translate(fdt32_t *addr, u64 offset, int na) +{ + u64 a = of_read_number(addr, na); + memset(addr, 0, na * 4); + a += offset; + if (na > 1) + addr[na - 2] = cpu_to_fdt32(a >> 32); + addr[na - 1] = cpu_to_fdt32(a & 0xffffffffu); + + return 0; +} + +/* Array of bus specific translators */ +static struct of_bus of_busses[] = { + /* Default */ + { + .name = "default", + .addresses = "reg", + .count_cells = of_bus_default_count_cells, + .map = of_bus_default_map, + .translate = of_bus_default_translate, + }, +}; + +static int of_translate_one(void * blob, int parent, struct of_bus *bus, + struct of_bus *pbus, fdt32_t *addr, + int na, int ns, int pna, const char *rprop) +{ + const fdt32_t *ranges; + int rlen; + int rone; + u64 offset = OF_BAD_ADDR; + + /* Normally, an absence of a "ranges" property means we are + * crossing a non-translatable boundary, and thus the addresses + * below the current not cannot be converted to CPU physical ones. + * Unfortunately, while this is very clear in the spec, it's not + * what Apple understood, and they do have things like /uni-n or + * /ht nodes with no "ranges" property and a lot of perfectly + * useable mapped devices below them. Thus we treat the absence of + * "ranges" as equivalent to an empty "ranges" property which means + * a 1:1 translation at that level. It's up to the caller not to try + * to translate addresses that aren't supposed to be translated in + * the first place. --BenH. + */ + ranges = fdt_getprop(blob, parent, rprop, &rlen); + if (ranges == NULL || rlen == 0) { + offset = of_read_number(addr, na); + memset(addr, 0, pna * 4); + debug("OF: no ranges, 1:1 translation\n"); + goto finish; + } + + debug("OF: walking ranges...\n"); + + /* Now walk through the ranges */ + rlen /= 4; + rone = na + pna + ns; + for (; rlen >= rone; rlen -= rone, ranges += rone) { + offset = bus->map(addr, ranges, na, ns, pna); + if (offset != OF_BAD_ADDR) + break; + } + if (offset == OF_BAD_ADDR) { + debug("OF: not found !\n"); + return 1; + } + memcpy(addr, ranges + na, 4 * pna); + + finish: + of_dump_addr("OF: parent translation for:", addr, pna); + debug("OF: with offset: "PRu64"\n", offset); + + /* Translate it into parent bus space */ + return pbus->translate(addr, offset, pna); +} + +/* + * Translate an address from the device-tree into a CPU physical address, + * this walks up the tree and applies the various bus mappings on the + * way. + * + * Note: We consider that crossing any level with #size-cells == 0 to mean + * that translation is impossible (that is we are not dealing with a value + * that can be mapped to a cpu physical address). This is not really specified + * that way, but this is traditionally the way IBM at least do things + */ +static u64 __of_translate_address(void *blob, int node_offset, const fdt32_t *in_addr, + const char *rprop) +{ + int parent; + struct of_bus *bus, *pbus; + fdt32_t addr[OF_MAX_ADDR_CELLS]; + int na, ns, pna, pns; + u64 result = OF_BAD_ADDR; + + debug("OF: ** translation for device %s **\n", + fdt_get_name(blob, node_offset, NULL)); + + /* Get parent & match bus type */ + parent = fdt_parent_offset(blob, node_offset); + if (parent < 0) + goto bail; + bus = &of_busses[0]; + + /* Cound address cells & copy address locally */ + bus->count_cells(blob, parent, &na, &ns); + if (!OF_CHECK_COUNTS(na, ns)) { + printf("%s: Bad cell count for %s\n", __FUNCTION__, + fdt_get_name(blob, node_offset, NULL)); + goto bail; + } + memcpy(addr, in_addr, na * 4); + + debug("OF: bus is %s (na=%d, ns=%d) on %s\n", + bus->name, na, ns, fdt_get_name(blob, parent, NULL)); + of_dump_addr("OF: translating address:", addr, na); + + /* Translate */ + for (;;) { + /* Switch to parent bus */ + node_offset = parent; + parent = fdt_parent_offset(blob, node_offset); + + /* If root, we have finished */ + if (parent < 0) { + debug("OF: reached root node\n"); + result = of_read_number(addr, na); + break; + } + + /* Get new parent bus and counts */ + pbus = &of_busses[0]; + pbus->count_cells(blob, parent, &pna, &pns); + if (!OF_CHECK_COUNTS(pna, pns)) { + printf("%s: Bad cell count for %s\n", __FUNCTION__, + fdt_get_name(blob, node_offset, NULL)); + break; + } + + debug("OF: parent bus is %s (na=%d, ns=%d) on %s\n", + pbus->name, pna, pns, fdt_get_name(blob, parent, NULL)); + + /* Apply bus translation */ + if (of_translate_one(blob, node_offset, bus, pbus, + addr, na, ns, pna, rprop)) + break; + + /* Complete the move up one level */ + na = pna; + ns = pns; + bus = pbus; + + of_dump_addr("OF: one level translation:", addr, na); + } + bail: + + return result; +} + +u64 fdt_translate_address(void *blob, int node_offset, const fdt32_t *in_addr) +{ + return __of_translate_address(blob, node_offset, in_addr, "ranges"); +} + +/** + * fdt_node_offset_by_compat_reg: Find a node that matches compatiable and + * who's reg property matches a physical cpu address + * + * @blob: ptr to device tree + * @compat: compatiable string to match + * @compat_off: property name + * + */ +int fdt_node_offset_by_compat_reg(void *blob, const char *compat, + phys_addr_t compat_off) +{ + int len, off = fdt_node_offset_by_compatible(blob, -1, compat); + while (off != -FDT_ERR_NOTFOUND) { + const fdt32_t *reg = fdt_getprop(blob, off, "reg", &len); + if (reg) { + if (compat_off == fdt_translate_address(blob, off, reg)) + return off; + } + off = fdt_node_offset_by_compatible(blob, off, compat); + } + + return -FDT_ERR_NOTFOUND; +} + +/** + * fdt_alloc_phandle: Return next free phandle value + * + * @blob: ptr to device tree + */ +int fdt_alloc_phandle(void *blob) +{ + int offset, phandle = 0; + + for (offset = fdt_next_node(blob, -1, NULL); offset >= 0; + offset = fdt_next_node(blob, offset, NULL)) { + phandle = max(phandle, fdt_get_phandle(blob, offset)); + } + + return phandle + 1; +} + +/* + * fdt_set_phandle: Create a phandle property for the given node + * + * @fdt: ptr to device tree + * @nodeoffset: node to update + * @phandle: phandle value to set (must be unique) + */ +int fdt_set_phandle(void *fdt, int nodeoffset, uint32_t phandle) +{ + int ret; + +#ifdef DEBUG + int off = fdt_node_offset_by_phandle(fdt, phandle); + + if ((off >= 0) && (off != nodeoffset)) { + char buf[64]; + + fdt_get_path(fdt, nodeoffset, buf, sizeof(buf)); + printf("Trying to update node %s with phandle %u ", + buf, phandle); + + fdt_get_path(fdt, off, buf, sizeof(buf)); + printf("that already exists in node %s.\n", buf); + return -FDT_ERR_BADPHANDLE; + } +#endif + + ret = fdt_setprop_cell(fdt, nodeoffset, "phandle", phandle); + if (ret < 0) + return ret; + + /* + * For now, also set the deprecated "linux,phandle" property, so that we + * don't break older kernels. + */ + ret = fdt_setprop_cell(fdt, nodeoffset, "linux,phandle", phandle); + + return ret; +} + +/* + * fdt_create_phandle: Create a phandle property for the given node + * + * @fdt: ptr to device tree + * @nodeoffset: node to update + */ +unsigned int fdt_create_phandle(void *fdt, int nodeoffset) +{ + /* see if there is a phandle already */ + int phandle = fdt_get_phandle(fdt, nodeoffset); + + /* if we got 0, means no phandle so create one */ + if (phandle == 0) { + int ret; + + phandle = fdt_alloc_phandle(fdt); + ret = fdt_set_phandle(fdt, nodeoffset, phandle); + if (ret < 0) { + printf("Can't set phandle %u: %s\n", phandle, + fdt_strerror(ret)); + return 0; + } + } + + return phandle; +} + +/* + * fdt_set_node_status: Set status for the given node + * + * @fdt: ptr to device tree + * @nodeoffset: node to update + * @status: FDT_STATUS_OKAY, FDT_STATUS_DISABLED, + * FDT_STATUS_FAIL, FDT_STATUS_FAIL_ERROR_CODE + * @error_code: optional, only used if status is FDT_STATUS_FAIL_ERROR_CODE + */ +int fdt_set_node_status(void *fdt, int nodeoffset, + enum fdt_status status, unsigned int error_code) +{ + char buf[16]; + int ret = 0; + + if (nodeoffset < 0) + return nodeoffset; + + switch (status) { + case FDT_STATUS_OKAY: + ret = fdt_setprop_string(fdt, nodeoffset, "status", "okay"); + break; + case FDT_STATUS_DISABLED: + ret = fdt_setprop_string(fdt, nodeoffset, "status", "disabled"); + break; + case FDT_STATUS_FAIL: + ret = fdt_setprop_string(fdt, nodeoffset, "status", "fail"); + break; + case FDT_STATUS_FAIL_ERROR_CODE: + sprintf(buf, "fail-%d", error_code); + ret = fdt_setprop_string(fdt, nodeoffset, "status", buf); + break; + default: + printf("Invalid fdt status: %x\n", status); + ret = -1; + break; + } + + return ret; +} + +/* + * fdt_set_status_by_alias: Set status for the given node given an alias + * + * @fdt: ptr to device tree + * @alias: alias of node to update + * @status: FDT_STATUS_OKAY, FDT_STATUS_DISABLED, + * FDT_STATUS_FAIL, FDT_STATUS_FAIL_ERROR_CODE + * @error_code: optional, only used if status is FDT_STATUS_FAIL_ERROR_CODE + */ +int fdt_set_status_by_alias(void *fdt, const char* alias, + enum fdt_status status, unsigned int error_code) +{ + int offset = fdt_path_offset(fdt, alias); + + return fdt_set_node_status(fdt, offset, status, error_code); +} + +#if defined(CONFIG_VIDEO) || defined(CONFIG_LCD) +int fdt_add_edid(void *blob, const char *compat, unsigned char *edid_buf) +{ + int noff; + int ret; + + noff = fdt_node_offset_by_compatible(blob, -1, compat); + if (noff != -FDT_ERR_NOTFOUND) { + debug("%s: %s\n", fdt_get_name(blob, noff, 0), compat); +add_edid: + ret = fdt_setprop(blob, noff, "edid", edid_buf, 128); + if (ret == -FDT_ERR_NOSPACE) { + ret = fdt_increase_size(blob, 512); + if (!ret) + goto add_edid; + else + goto err_size; + } else if (ret < 0) { + printf("Can't add property: %s\n", fdt_strerror(ret)); + return ret; + } + } + return 0; +err_size: + printf("Can't increase blob size: %s\n", fdt_strerror(ret)); + return ret; +} +#endif + +/* + * Verify the physical address of device tree node for a given alias + * + * This function locates the device tree node of a given alias, and then + * verifies that the physical address of that device matches the given + * parameter. It displays a message if there is a mismatch. + * + * Returns 1 on success, 0 on failure + */ +int fdt_verify_alias_address(void *fdt, int anode, const char *alias, u64 addr) +{ + const char *path; + const fdt32_t *reg; + int node, len; + u64 dt_addr; + + path = fdt_getprop(fdt, anode, alias, NULL); + if (!path) { + /* If there's no such alias, then it's not a failure */ + return 1; + } + + node = fdt_path_offset(fdt, path); + if (node < 0) { + printf("Warning: device tree alias '%s' points to invalid " + "node %s.\n", alias, path); + return 0; + } + + reg = fdt_getprop(fdt, node, "reg", &len); + if (!reg) { + printf("Warning: device tree node '%s' has no address.\n", + path); + return 0; + } + + dt_addr = fdt_translate_address(fdt, node, reg); + if (addr != dt_addr) { + printf("Warning: U-Boot configured device %s at address %llx,\n" + " but the device tree has it address %llx.\n", + alias, addr, dt_addr); + return 0; + } + + return 1; +} + +/* + * Returns the base address of an SOC or PCI node + */ +u64 fdt_get_base_address(void *fdt, int node) +{ + int size; + u32 naddr; + const fdt32_t *prop; + + prop = fdt_getprop(fdt, node, "#address-cells", &size); + if (prop && size == 4) + naddr = be32_to_cpup(prop); + else + naddr = 2; + + prop = fdt_getprop(fdt, node, "ranges", &size); + + return prop ? fdt_translate_address(fdt, node, prop + naddr) : 0; +} + +/* + * Read a property of size <prop_len>. Currently only supports 1 or 2 cells. + */ +static int fdt_read_prop(const fdt32_t *prop, int prop_len, int cell_off, + uint64_t *val, int cells) +{ + const fdt32_t *prop32 = &prop[cell_off]; + const fdt64_t *prop64 = (const fdt64_t *)&prop[cell_off]; + + if ((cell_off + cells) > prop_len) + return -FDT_ERR_NOSPACE; + + switch (cells) { + case 1: + *val = fdt32_to_cpu(*prop32); + break; + case 2: + *val = fdt64_to_cpu(*prop64); + break; + default: + return -FDT_ERR_NOSPACE; + } + + return 0; +} + +/** + * fdt_read_range - Read a node's n'th range property + * + * @fdt: ptr to device tree + * @node: offset of node + * @n: range index + * @child_addr: pointer to storage for the "child address" field + * @addr: pointer to storage for the CPU view translated physical start + * @len: pointer to storage for the range length + * + * Convenience function that reads and interprets a specific range out of + * a number of the "ranges" property array. + */ +int fdt_read_range(void *fdt, int node, int n, uint64_t *child_addr, + uint64_t *addr, uint64_t *len) +{ + int pnode = fdt_parent_offset(fdt, node); + const fdt32_t *ranges; + int pacells; + int acells; + int scells; + int ranges_len; + int cell = 0; + int r = 0; + + /* + * The "ranges" property is an array of + * { <child address> <parent address> <size in child address space> } + * + * All 3 elements can span a diffent number of cells. Fetch their size. + */ + pacells = fdt_getprop_u32_default_node(fdt, pnode, 0, "#address-cells", 1); + acells = fdt_getprop_u32_default_node(fdt, node, 0, "#address-cells", 1); + scells = fdt_getprop_u32_default_node(fdt, node, 0, "#size-cells", 1); + + /* Now try to get the ranges property */ + ranges = fdt_getprop(fdt, node, "ranges", &ranges_len); + if (!ranges) + return -FDT_ERR_NOTFOUND; + ranges_len /= sizeof(uint32_t); + + /* Jump to the n'th entry */ + cell = n * (pacells + acells + scells); + + /* Read <child address> */ + if (child_addr) { + r = fdt_read_prop(ranges, ranges_len, cell, child_addr, + acells); + if (r) + return r; + } + cell += acells; + + /* Read <parent address> */ + if (addr) + *addr = fdt_translate_address(fdt, node, ranges + cell); + cell += pacells; + + /* Read <size in child address space> */ + if (len) { + r = fdt_read_prop(ranges, ranges_len, cell, len, scells); + if (r) + return r; + } + + return 0; +} diff --git a/qemu/roms/u-boot/common/flash.c b/qemu/roms/u-boot/common/flash.c new file mode 100644 index 000000000..f1440c2c1 --- /dev/null +++ b/qemu/roms/u-boot/common/flash.c @@ -0,0 +1,230 @@ +/* + * (C) Copyright 2000 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/* #define DEBUG */ + +#include <common.h> +#include <flash.h> + +#if !defined(CONFIG_SYS_NO_FLASH) +#include <mtd/cfi_flash.h> + +extern flash_info_t flash_info[]; /* info for FLASH chips */ + +/*----------------------------------------------------------------------- + * Functions + */ + +/*----------------------------------------------------------------------- + * Set protection status for monitor sectors + * + * The monitor is always located in the _first_ Flash bank. + * If necessary you have to map the second bank at lower addresses. + */ +void +flash_protect (int flag, ulong from, ulong to, flash_info_t *info) +{ + ulong b_end; + short s_end; + int i; + + /* Do nothing if input data is bad. */ + if (!info || info->sector_count == 0 || info->size == 0 || to < from) { + return; + } + + s_end = info->sector_count - 1; /* index of last sector */ + b_end = info->start[0] + info->size - 1; /* bank end address */ + + debug ("flash_protect %s: from 0x%08lX to 0x%08lX\n", + (flag & FLAG_PROTECT_SET) ? "ON" : + (flag & FLAG_PROTECT_CLEAR) ? "OFF" : "???", + from, to); + + /* There is nothing to do if we have no data about the flash + * or the protect range and flash range don't overlap. + */ + if (info->flash_id == FLASH_UNKNOWN || + to < info->start[0] || from > b_end) { + return; + } + + for (i=0; i<info->sector_count; ++i) { + ulong end; /* last address in current sect */ + + end = (i == s_end) ? b_end : info->start[i + 1] - 1; + + /* Update protection if any part of the sector + * is in the specified range. + */ + if (from <= end && to >= info->start[i]) { + if (flag & FLAG_PROTECT_CLEAR) { +#if defined(CONFIG_SYS_FLASH_PROTECTION) + flash_real_protect(info, i, 0); +#else + info->protect[i] = 0; +#endif /* CONFIG_SYS_FLASH_PROTECTION */ + debug ("protect off %d\n", i); + } + else if (flag & FLAG_PROTECT_SET) { +#if defined(CONFIG_SYS_FLASH_PROTECTION) + flash_real_protect(info, i, 1); +#else + info->protect[i] = 1; +#endif /* CONFIG_SYS_FLASH_PROTECTION */ + debug ("protect on %d\n", i); + } + } + } +} + +/*----------------------------------------------------------------------- + */ + +flash_info_t * +addr2info (ulong addr) +{ +#ifndef CONFIG_SPD823TS + flash_info_t *info; + int i; + + for (i=0, info = &flash_info[0]; i<CONFIG_SYS_MAX_FLASH_BANKS; ++i, ++info) { + if (info->flash_id != FLASH_UNKNOWN && + addr >= info->start[0] && + /* WARNING - The '- 1' is needed if the flash + * is at the end of the address space, since + * info->start[0] + info->size wraps back to 0. + * Please don't change this unless you understand this. + */ + addr <= info->start[0] + info->size - 1) { + return (info); + } + } +#endif /* CONFIG_SPD823TS */ + + return (NULL); +} + +/*----------------------------------------------------------------------- + * Copy memory to flash. + * Make sure all target addresses are within Flash bounds, + * and no protected sectors are hit. + * Returns: + * ERR_OK 0 - OK + * ERR_TIMOUT 1 - write timeout + * ERR_NOT_ERASED 2 - Flash not erased + * ERR_PROTECTED 4 - target range includes protected sectors + * ERR_INVAL 8 - target address not in Flash memory + * ERR_ALIGN 16 - target address not aligned on boundary + * (only some targets require alignment) + */ +int +flash_write (char *src, ulong addr, ulong cnt) +{ +#ifdef CONFIG_SPD823TS + return (ERR_TIMOUT); /* any other error codes are possible as well */ +#else + int i; + ulong end = addr + cnt - 1; + flash_info_t *info_first = addr2info (addr); + flash_info_t *info_last = addr2info (end ); + flash_info_t *info; + __maybe_unused char *src_orig = src; + __maybe_unused char *addr_orig = (char *)addr; + __maybe_unused ulong cnt_orig = cnt; + + if (cnt == 0) { + return (ERR_OK); + } + + if (!info_first || !info_last) { + return (ERR_INVAL); + } + + for (info = info_first; info <= info_last; ++info) { + ulong b_end = info->start[0] + info->size; /* bank end addr */ + short s_end = info->sector_count - 1; + for (i=0; i<info->sector_count; ++i) { + ulong e_addr = (i == s_end) ? b_end : info->start[i + 1]; + + if ((end >= info->start[i]) && (addr < e_addr) && + (info->protect[i] != 0) ) { + return (ERR_PROTECTED); + } + } + } + + /* finally write data to flash */ + for (info = info_first; info <= info_last && cnt>0; ++info) { + ulong len; + + len = info->start[0] + info->size - addr; + if (len > cnt) + len = cnt; + if ((i = write_buff(info, (uchar *)src, addr, len)) != 0) { + return (i); + } + cnt -= len; + addr += len; + src += len; + } + +#if defined(CONFIG_FLASH_VERIFY) + if (memcmp(src_orig, addr_orig, cnt_orig)) { + printf("\nVerify failed!\n"); + return ERR_PROG_ERROR; + } +#endif /* CONFIG_SYS_FLASH_VERIFY_AFTER_WRITE */ + + return (ERR_OK); +#endif /* CONFIG_SPD823TS */ +} + +/*----------------------------------------------------------------------- + */ + +void flash_perror (int err) +{ + switch (err) { + case ERR_OK: + break; + case ERR_TIMOUT: + puts ("Timeout writing to Flash\n"); + break; + case ERR_NOT_ERASED: + puts ("Flash not Erased\n"); + break; + case ERR_PROTECTED: + puts ("Can't write to protected Flash sectors\n"); + break; + case ERR_INVAL: + puts ("Outside available Flash\n"); + break; + case ERR_ALIGN: + puts ("Start and/or end address not on sector boundary\n"); + break; + case ERR_UNKNOWN_FLASH_VENDOR: + puts ("Unknown Vendor of Flash\n"); + break; + case ERR_UNKNOWN_FLASH_TYPE: + puts ("Unknown Type of Flash\n"); + break; + case ERR_PROG_ERROR: + puts ("General Flash Programming Error\n"); + break; + case ERR_ABORTED: + puts("Flash Programming Aborted\n"); + break; + default: + printf ("%s[%d] FIXME: rc=%d\n", __FILE__, __LINE__, err); + break; + } +} + +/*----------------------------------------------------------------------- + */ +#endif /* !CONFIG_SYS_NO_FLASH */ diff --git a/qemu/roms/u-boot/common/hash.c b/qemu/roms/u-boot/common/hash.c new file mode 100644 index 000000000..7627b84b4 --- /dev/null +++ b/qemu/roms/u-boot/common/hash.c @@ -0,0 +1,429 @@ +/* + * 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. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <command.h> +#include <malloc.h> +#include <hw_sha.h> +#include <hash.h> +#include <sha1.h> +#include <sha256.h> +#include <asm/io.h> +#include <asm/errno.h> + +#ifdef CONFIG_CMD_SHA1SUM +static int hash_init_sha1(struct hash_algo *algo, void **ctxp) +{ + sha1_context *ctx = malloc(sizeof(sha1_context)); + sha1_starts(ctx); + *ctxp = ctx; + return 0; +} + +static int hash_update_sha1(struct hash_algo *algo, void *ctx, const void *buf, + unsigned int size, int is_last) +{ + sha1_update((sha1_context *)ctx, buf, size); + return 0; +} + +static int hash_finish_sha1(struct hash_algo *algo, void *ctx, void *dest_buf, + int size) +{ + if (size < algo->digest_size) + return -1; + + sha1_finish((sha1_context *)ctx, dest_buf); + free(ctx); + return 0; +} +#endif + +#ifdef CONFIG_SHA256 +static int hash_init_sha256(struct hash_algo *algo, void **ctxp) +{ + sha256_context *ctx = malloc(sizeof(sha256_context)); + sha256_starts(ctx); + *ctxp = ctx; + return 0; +} + +static int hash_update_sha256(struct hash_algo *algo, void *ctx, + const void *buf, unsigned int size, int is_last) +{ + sha256_update((sha256_context *)ctx, buf, size); + return 0; +} + +static int hash_finish_sha256(struct hash_algo *algo, void *ctx, void + *dest_buf, int size) +{ + if (size < algo->digest_size) + return -1; + + sha256_finish((sha256_context *)ctx, dest_buf); + free(ctx); + return 0; +} +#endif + +static int hash_init_crc32(struct hash_algo *algo, void **ctxp) +{ + uint32_t *ctx = malloc(sizeof(uint32_t)); + *ctx = 0; + *ctxp = ctx; + return 0; +} + +static int hash_update_crc32(struct hash_algo *algo, void *ctx, + const void *buf, unsigned int size, int is_last) +{ + *((uint32_t *)ctx) = crc32(*((uint32_t *)ctx), buf, size); + return 0; +} + +static int hash_finish_crc32(struct hash_algo *algo, void *ctx, void *dest_buf, + int size) +{ + if (size < algo->digest_size) + return -1; + + *((uint32_t *)dest_buf) = *((uint32_t *)ctx); + free(ctx); + return 0; +} + +/* + * These are the hash algorithms we support. Chips which support accelerated + * crypto could perhaps add named version of these algorithms here. Note that + * algorithm names must be in lower case. + */ +static struct hash_algo hash_algo[] = { + /* + * CONFIG_SHA_HW_ACCEL is defined if hardware acceleration is + * available. + */ +#ifdef CONFIG_SHA_HW_ACCEL + { + "sha1", + SHA1_SUM_LEN, + hw_sha1, + CHUNKSZ_SHA1, + }, { + "sha256", + SHA256_SUM_LEN, + hw_sha256, + CHUNKSZ_SHA256, + }, +#endif + /* + * This is CONFIG_CMD_SHA1SUM instead of CONFIG_SHA1 since otherwise + * it bloats the code for boards which use SHA1 but not the 'hash' + * or 'sha1sum' commands. + */ +#ifdef CONFIG_CMD_SHA1SUM + { + "sha1", + SHA1_SUM_LEN, + sha1_csum_wd, + CHUNKSZ_SHA1, + hash_init_sha1, + hash_update_sha1, + hash_finish_sha1, + }, +#define MULTI_HASH +#endif +#ifdef CONFIG_SHA256 + { + "sha256", + SHA256_SUM_LEN, + sha256_csum_wd, + CHUNKSZ_SHA256, + hash_init_sha256, + hash_update_sha256, + hash_finish_sha256, + }, +#define MULTI_HASH +#endif + { + "crc32", + 4, + crc32_wd_buf, + CHUNKSZ_CRC32, + hash_init_crc32, + hash_update_crc32, + hash_finish_crc32, + }, +}; + +#if defined(CONFIG_HASH_VERIFY) || defined(CONFIG_CMD_HASH) +#define MULTI_HASH +#endif + +/* Try to minimize code size for boards that don't want much hashing */ +#ifdef MULTI_HASH +#define multi_hash() 1 +#else +#define multi_hash() 0 +#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 allow_env_vars is 0) or otherwise as an + * environment variable. + * @allow_env_vars: non-zero to permit storing the result to an + * variable environment + */ +static void store_result(struct hash_algo *algo, const u8 *sum, + const char *dest, int allow_env_vars) +{ + unsigned int i; + int env_var = 0; + + /* + * If environment variables are allowed, then we assume that 'dest' + * is an environment variable, unless it starts with *, in which + * case we assume it is an address. If not allowed, it is always an + * address. This is to support the crc32 command. + */ + if (allow_env_vars) { + if (*dest == '*') + dest++; + else + env_var = 1; + } + + if (env_var) { + 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); + } else { + ulong addr; + void *buf; + + addr = simple_strtoul(dest, NULL, 16); + buf = map_sysmem(addr, algo->digest_size); + memcpy(buf, sum, algo->digest_size); + unmap_sysmem(buf); + } +} + +/** + * 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) + * @allow_env_vars: non-zero to permit storing the result to an environment + * variable. If 0 then verify_str is assumed to be an + * address, and the * prefix is not expected. + * @return 0 if ok, non-zero on error + */ +static int parse_verify_sum(struct hash_algo *algo, char *verify_str, u8 *vsum, + int allow_env_vars) +{ + int env_var = 0; + + /* See comment above in store_result() */ + if (allow_env_vars) { + if (*verify_str == '*') + verify_str++; + else + env_var = 1; + } + + if (env_var) { + ulong addr; + void *buf; + + addr = simple_strtoul(verify_str, NULL, 16); + buf = map_sysmem(addr, algo->digest_size); + memcpy(vsum, buf, 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; +} + +int hash_lookup_algo(const char *algo_name, struct hash_algo **algop) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(hash_algo); i++) { + if (!strcmp(algo_name, hash_algo[i].name)) { + *algop = &hash_algo[i]; + return 0; + } + } + + debug("Unknown hash algorithm '%s'\n", algo_name); + return -EPROTONOSUPPORT; +} + +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_block(const char *algo_name, const void *data, unsigned int len, + uint8_t *output, int *output_size) +{ + struct hash_algo *algo; + int ret; + + ret = hash_lookup_algo(algo_name, &algo); + if (ret) + return ret; + + if (output_size && *output_size < algo->digest_size) { + debug("Output buffer size %d too small (need %d bytes)", + *output_size, algo->digest_size); + return -ENOSPC; + } + if (output_size) + *output_size = algo->digest_size; + algo->hash_func_ws(data, len, output, algo->chunk_size); + + return 0; +} + +int hash_command(const char *algo_name, int flags, cmd_tbl_t *cmdtp, int flag, + int argc, char * const argv[]) +{ + ulong addr, len; + + if (argc < 2) + return CMD_RET_USAGE; + + addr = simple_strtoul(*argv++, NULL, 16); + len = simple_strtoul(*argv++, NULL, 16); + + if (multi_hash()) { + struct hash_algo *algo; + u8 output[HASH_MAX_DIGEST_SIZE]; + u8 vsum[HASH_MAX_DIGEST_SIZE]; + void *buf; + + if (hash_lookup_algo(algo_name, &algo)) { + printf("Unknown hash algorithm '%s'\n", algo_name); + return CMD_RET_USAGE; + } + argc -= 2; + + if (algo->digest_size > HASH_MAX_DIGEST_SIZE) { + puts("HASH_MAX_DIGEST_SIZE exceeded\n"); + return 1; + } + + buf = map_sysmem(addr, len); + algo->hash_func_ws(buf, len, output, algo->chunk_size); + unmap_sysmem(buf); + + /* Try to avoid code bloat when verify is not needed */ +#ifdef CONFIG_HASH_VERIFY + if (flags & HASH_FLAG_VERIFY) { +#else + if (0) { +#endif + if (!argc) + return CMD_RET_USAGE; + if (parse_verify_sum(algo, *argv, vsum, + flags & HASH_FLAG_ENV)) { + 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, + flags & HASH_FLAG_ENV); + } + } + + /* Horrible code size hack for boards that just want crc32 */ + } else { + ulong crc; + ulong *ptr; + + crc = crc32_wd(0, (const uchar *)addr, len, CHUNKSZ_CRC32); + + printf("CRC32 for %08lx ... %08lx ==> %08lx\n", + addr, addr + len - 1, crc); + + if (argc >= 3) { + ptr = (ulong *)simple_strtoul(argv[0], NULL, 16); + *ptr = crc; + } + } + + return 0; +} diff --git a/qemu/roms/u-boot/common/hush.c b/qemu/roms/u-boot/common/hush.c new file mode 100644 index 000000000..5b4322475 --- /dev/null +++ b/qemu/roms/u-boot/common/hush.c @@ -0,0 +1,3687 @@ +/* + * sh.c -- a prototype Bourne shell grammar parser + * Intended to follow the original Thompson and Ritchie + * "small and simple is beautiful" philosophy, which + * incidentally is a good match to today's BusyBox. + * + * Copyright (C) 2000,2001 Larry Doolittle <larry@doolittle.boa.org> + * + * Credits: + * The parser routines proper are all original material, first + * written Dec 2000 and Jan 2001 by Larry Doolittle. + * The execution engine, the builtins, and much of the underlying + * support has been adapted from busybox-0.49pre's lash, + * which is Copyright (C) 2000 by Lineo, Inc., and + * written by Erik Andersen <andersen@lineo.com>, <andersee@debian.org>. + * That, in turn, is based in part on ladsh.c, by Michael K. Johnson and + * Erik W. Troan, which they placed in the public domain. I don't know + * how much of the Johnson/Troan code has survived the repeated rewrites. + * Other credits: + * b_addchr() derived from similar w_addchar function in glibc-2.2 + * setup_redirect(), redirect_opt_num(), and big chunks of main() + * and many builtins derived from contributions by Erik Andersen + * miscellaneous bugfixes from Matt Kraai + * + * There are two big (and related) architecture differences between + * this parser and the lash parser. One is that this version is + * actually designed from the ground up to understand nearly all + * of the Bourne grammar. The second, consequential change is that + * the parser and input reader have been turned inside out. Now, + * the parser is in control, and asks for input as needed. The old + * way had the input reader in control, and it asked for parsing to + * take place as needed. The new way makes it much easier to properly + * handle the recursion implicit in the various substitutions, especially + * across continuation lines. + * + * Bash grammar not implemented: (how many of these were in original sh?) + * $@ (those sure look like weird quoting rules) + * $_ + * ! negation operator for pipes + * &> and >& redirection of stdout+stderr + * Brace Expansion + * Tilde Expansion + * fancy forms of Parameter Expansion + * aliases + * Arithmetic Expansion + * <(list) and >(list) Process Substitution + * reserved words: case, esac, select, function + * Here Documents ( << word ) + * Functions + * Major bugs: + * job handling woefully incomplete and buggy + * reserved word execution woefully incomplete and buggy + * to-do: + * port selected bugfixes from post-0.49 busybox lash - done? + * finish implementing reserved words: for, while, until, do, done + * change { and } from special chars to reserved words + * builtins: break, continue, eval, return, set, trap, ulimit + * test magic exec + * handle children going into background + * clean up recognition of null pipes + * check setting of global_argc and global_argv + * control-C handling, probably with longjmp + * follow IFS rules more precisely, including update semantics + * figure out what to do with backslash-newline + * explain why we use signal instead of sigaction + * propagate syntax errors, die on resource errors? + * continuation lines, both explicit and implicit - done? + * memory leak finding and plugging - done? + * more testing, especially quoting rules and redirection + * document how quoting rules not precisely followed for variable assignments + * maybe change map[] to use 2-bit entries + * (eventually) remove all the printf's + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#define __U_BOOT__ +#ifdef __U_BOOT__ +#include <malloc.h> /* malloc, free, realloc*/ +#include <linux/ctype.h> /* isalpha, isdigit */ +#include <common.h> /* readline */ +#include <hush.h> +#include <command.h> /* find_cmd */ +#ifndef CONFIG_SYS_PROMPT_HUSH_PS2 +#define CONFIG_SYS_PROMPT_HUSH_PS2 "> " +#endif +#endif +#ifndef __U_BOOT__ +#include <ctype.h> /* isalpha, isdigit */ +#include <unistd.h> /* getpid */ +#include <stdlib.h> /* getenv, atoi */ +#include <string.h> /* strchr */ +#include <stdio.h> /* popen etc. */ +#include <glob.h> /* glob, of course */ +#include <stdarg.h> /* va_list */ +#include <errno.h> +#include <fcntl.h> +#include <getopt.h> /* should be pretty obvious */ + +#include <sys/stat.h> /* ulimit */ +#include <sys/types.h> +#include <sys/wait.h> +#include <signal.h> + +/* #include <dmalloc.h> */ + +#if 1 +#include "busybox.h" +#include "cmdedit.h" +#else +#define applet_name "hush" +#include "standalone.h" +#define hush_main main +#undef CONFIG_FEATURE_SH_FANCY_PROMPT +#define BB_BANNER +#endif +#endif +#define SPECIAL_VAR_SYMBOL 03 +#define SUBSTED_VAR_SYMBOL 04 +#ifndef __U_BOOT__ +#define FLAG_EXIT_FROM_LOOP 1 +#define FLAG_PARSE_SEMICOLON (1 << 1) /* symbol ';' is special for parser */ +#define FLAG_REPARSING (1 << 2) /* >= 2nd pass */ + +#endif + +#ifdef __U_BOOT__ +DECLARE_GLOBAL_DATA_PTR; + +#define EXIT_SUCCESS 0 +#define EOF -1 +#define syntax() syntax_err() +#define xstrdup strdup +#define error_msg printf +#else +typedef enum { + REDIRECT_INPUT = 1, + REDIRECT_OVERWRITE = 2, + REDIRECT_APPEND = 3, + REDIRECT_HEREIS = 4, + REDIRECT_IO = 5 +} redir_type; + +/* The descrip member of this structure is only used to make debugging + * output pretty */ +struct {int mode; int default_fd; char *descrip;} redir_table[] = { + { 0, 0, "()" }, + { O_RDONLY, 0, "<" }, + { O_CREAT|O_TRUNC|O_WRONLY, 1, ">" }, + { O_CREAT|O_APPEND|O_WRONLY, 1, ">>" }, + { O_RDONLY, -1, "<<" }, + { O_RDWR, 1, "<>" } +}; +#endif + +typedef enum { + PIPE_SEQ = 1, + PIPE_AND = 2, + PIPE_OR = 3, + PIPE_BG = 4, +} pipe_style; + +/* might eventually control execution */ +typedef enum { + RES_NONE = 0, + RES_IF = 1, + RES_THEN = 2, + RES_ELIF = 3, + RES_ELSE = 4, + RES_FI = 5, + RES_FOR = 6, + RES_WHILE = 7, + RES_UNTIL = 8, + RES_DO = 9, + RES_DONE = 10, + RES_XXXX = 11, + RES_IN = 12, + RES_SNTX = 13 +} reserved_style; +#define FLAG_END (1<<RES_NONE) +#define FLAG_IF (1<<RES_IF) +#define FLAG_THEN (1<<RES_THEN) +#define FLAG_ELIF (1<<RES_ELIF) +#define FLAG_ELSE (1<<RES_ELSE) +#define FLAG_FI (1<<RES_FI) +#define FLAG_FOR (1<<RES_FOR) +#define FLAG_WHILE (1<<RES_WHILE) +#define FLAG_UNTIL (1<<RES_UNTIL) +#define FLAG_DO (1<<RES_DO) +#define FLAG_DONE (1<<RES_DONE) +#define FLAG_IN (1<<RES_IN) +#define FLAG_START (1<<RES_XXXX) + +/* This holds pointers to the various results of parsing */ +struct p_context { + struct child_prog *child; + struct pipe *list_head; + struct pipe *pipe; +#ifndef __U_BOOT__ + struct redir_struct *pending_redirect; +#endif + reserved_style w; + int old_flag; /* for figuring out valid reserved words */ + struct p_context *stack; + int type; /* define type of parser : ";$" common or special symbol */ + /* How about quoting status? */ +}; + +#ifndef __U_BOOT__ +struct redir_struct { + redir_type type; /* type of redirection */ + int fd; /* file descriptor being redirected */ + int dup; /* -1, or file descriptor being duplicated */ + struct redir_struct *next; /* pointer to the next redirect in the list */ + glob_t word; /* *word.gl_pathv is the filename */ +}; +#endif + +struct child_prog { +#ifndef __U_BOOT__ + pid_t pid; /* 0 if exited */ +#endif + char **argv; /* program name and arguments */ + /* was quoted when parsed; copy of struct o_string.nonnull field */ + int *argv_nonnull; +#ifdef __U_BOOT__ + int argc; /* number of program arguments */ +#endif + struct pipe *group; /* if non-NULL, first in group or subshell */ +#ifndef __U_BOOT__ + int subshell; /* flag, non-zero if group must be forked */ + struct redir_struct *redirects; /* I/O redirections */ + glob_t glob_result; /* result of parameter globbing */ + int is_stopped; /* is the program currently running? */ + struct pipe *family; /* pointer back to the child's parent pipe */ +#endif + int sp; /* number of SPECIAL_VAR_SYMBOL */ + int type; +}; + +struct pipe { +#ifndef __U_BOOT__ + int jobid; /* job number */ +#endif + int num_progs; /* total number of programs in job */ +#ifndef __U_BOOT__ + int running_progs; /* number of programs running */ + char *text; /* name of job */ + char *cmdbuf; /* buffer various argv's point into */ + pid_t pgrp; /* process group ID for the job */ +#endif + struct child_prog *progs; /* array of commands in pipe */ + struct pipe *next; /* to track background commands */ +#ifndef __U_BOOT__ + int stopped_progs; /* number of programs alive, but stopped */ + int job_context; /* bitmask defining current context */ +#endif + pipe_style followup; /* PIPE_BG, PIPE_SEQ, PIPE_OR, PIPE_AND */ + reserved_style r_mode; /* supports if, for, while, until */ +}; + +#ifndef __U_BOOT__ +struct close_me { + int fd; + struct close_me *next; +}; +#endif + +struct variables { + char *name; + char *value; + int flg_export; + int flg_read_only; + struct variables *next; +}; + +/* globals, connect us to the outside world + * the first three support $?, $#, and $1 */ +#ifndef __U_BOOT__ +char **global_argv; +unsigned int global_argc; +#endif +static unsigned int last_return_code; +#ifndef __U_BOOT__ +extern char **environ; /* This is in <unistd.h>, but protected with __USE_GNU */ +#endif + +/* "globals" within this file */ +static uchar *ifs; +static char map[256]; +#ifndef __U_BOOT__ +static int fake_mode; +static int interactive; +static struct close_me *close_me_head; +static const char *cwd; +static struct pipe *job_list; +static unsigned int last_bg_pid; +static unsigned int last_jobid; +static unsigned int shell_terminal; +static char *PS1; +static char *PS2; +struct variables shell_ver = { "HUSH_VERSION", "0.01", 1, 1, 0 }; +struct variables *top_vars = &shell_ver; +#else +static int flag_repeat = 0; +static int do_repeat = 0; +static struct variables *top_vars = NULL ; +#endif /*__U_BOOT__ */ + +#define B_CHUNK (100) +#define B_NOSPAC 1 + +typedef struct { + char *data; + int length; + int maxlen; + int quote; + int nonnull; +} o_string; +#define NULL_O_STRING {NULL,0,0,0,0} +/* used for initialization: + o_string foo = NULL_O_STRING; */ + +/* I can almost use ordinary FILE *. Is open_memstream() universally + * available? Where is it documented? */ +struct in_str { + const char *p; +#ifndef __U_BOOT__ + char peek_buf[2]; +#endif + int __promptme; + int promptmode; +#ifndef __U_BOOT__ + FILE *file; +#endif + int (*get) (struct in_str *); + int (*peek) (struct in_str *); +}; +#define b_getch(input) ((input)->get(input)) +#define b_peek(input) ((input)->peek(input)) + +#ifndef __U_BOOT__ +#define JOB_STATUS_FORMAT "[%d] %-22s %.40s\n" + +struct built_in_command { + char *cmd; /* name */ + char *descr; /* description */ + int (*function) (struct child_prog *); /* function ptr */ +}; +#endif + +/* define DEBUG_SHELL for debugging output (obviously ;-)) */ +#if 0 +#define DEBUG_SHELL +#endif + +/* This should be in utility.c */ +#ifdef DEBUG_SHELL +#ifndef __U_BOOT__ +static void debug_printf(const char *format, ...) +{ + va_list args; + va_start(args, format); + vfprintf(stderr, format, args); + va_end(args); +} +#else +#define debug_printf(fmt,args...) printf (fmt ,##args) +#endif +#else +static inline void debug_printf(const char *format, ...) { } +#endif +#define final_printf debug_printf + +#ifdef __U_BOOT__ +static void syntax_err(void) { + printf("syntax error\n"); +} +#else +static void __syntax(char *file, int line) { + error_msg("syntax error %s:%d", file, line); +} +#define syntax() __syntax(__FILE__, __LINE__) +#endif + +#ifdef __U_BOOT__ +static void *xmalloc(size_t size); +static void *xrealloc(void *ptr, size_t size); +#else +/* Index of subroutines: */ +/* function prototypes for builtins */ +static int builtin_cd(struct child_prog *child); +static int builtin_env(struct child_prog *child); +static int builtin_eval(struct child_prog *child); +static int builtin_exec(struct child_prog *child); +static int builtin_exit(struct child_prog *child); +static int builtin_export(struct child_prog *child); +static int builtin_fg_bg(struct child_prog *child); +static int builtin_help(struct child_prog *child); +static int builtin_jobs(struct child_prog *child); +static int builtin_pwd(struct child_prog *child); +static int builtin_read(struct child_prog *child); +static int builtin_set(struct child_prog *child); +static int builtin_shift(struct child_prog *child); +static int builtin_source(struct child_prog *child); +static int builtin_umask(struct child_prog *child); +static int builtin_unset(struct child_prog *child); +static int builtin_not_written(struct child_prog *child); +#endif +/* o_string manipulation: */ +static int b_check_space(o_string *o, int len); +static int b_addchr(o_string *o, int ch); +static void b_reset(o_string *o); +static int b_addqchr(o_string *o, int ch, int quote); +#ifndef __U_BOOT__ +static int b_adduint(o_string *o, unsigned int i); +#endif +/* in_str manipulations: */ +static int static_get(struct in_str *i); +static int static_peek(struct in_str *i); +static int file_get(struct in_str *i); +static int file_peek(struct in_str *i); +#ifndef __U_BOOT__ +static void setup_file_in_str(struct in_str *i, FILE *f); +#else +static void setup_file_in_str(struct in_str *i); +#endif +static void setup_string_in_str(struct in_str *i, const char *s); +#ifndef __U_BOOT__ +/* close_me manipulations: */ +static void mark_open(int fd); +static void mark_closed(int fd); +static void close_all(void); +#endif +/* "run" the final data structures: */ +static char *indenter(int i); +static int free_pipe_list(struct pipe *head, int indent); +static int free_pipe(struct pipe *pi, int indent); +/* really run the final data structures: */ +#ifndef __U_BOOT__ +static int setup_redirects(struct child_prog *prog, int squirrel[]); +#endif +static int run_list_real(struct pipe *pi); +#ifndef __U_BOOT__ +static void pseudo_exec(struct child_prog *child) __attribute__ ((noreturn)); +#endif +static int run_pipe_real(struct pipe *pi); +/* extended glob support: */ +#ifndef __U_BOOT__ +static int globhack(const char *src, int flags, glob_t *pglob); +static int glob_needed(const char *s); +static int xglob(o_string *dest, int flags, glob_t *pglob); +#endif +/* variable assignment: */ +static int is_assignment(const char *s); +/* data structure manipulation: */ +#ifndef __U_BOOT__ +static int setup_redirect(struct p_context *ctx, int fd, redir_type style, struct in_str *input); +#endif +static void initialize_context(struct p_context *ctx); +static int done_word(o_string *dest, struct p_context *ctx); +static int done_command(struct p_context *ctx); +static int done_pipe(struct p_context *ctx, pipe_style type); +/* primary string parsing: */ +#ifndef __U_BOOT__ +static int redirect_dup_num(struct in_str *input); +static int redirect_opt_num(o_string *o); +static int process_command_subs(o_string *dest, struct p_context *ctx, struct in_str *input, int subst_end); +static int parse_group(o_string *dest, struct p_context *ctx, struct in_str *input, int ch); +#endif +static char *lookup_param(char *src); +static char *make_string(char **inp, int *nonnull); +static int handle_dollar(o_string *dest, struct p_context *ctx, struct in_str *input); +#ifndef __U_BOOT__ +static int parse_string(o_string *dest, struct p_context *ctx, const char *src); +#endif +static int parse_stream(o_string *dest, struct p_context *ctx, struct in_str *input0, int end_trigger); +/* setup: */ +static int parse_stream_outer(struct in_str *inp, int flag); +#ifndef __U_BOOT__ +static int parse_string_outer(const char *s, int flag); +static int parse_file_outer(FILE *f); +#endif +#ifndef __U_BOOT__ +/* job management: */ +static int checkjobs(struct pipe* fg_pipe); +static void insert_bg_job(struct pipe *pi); +static void remove_bg_job(struct pipe *pi); +#endif +/* local variable support */ +static char **make_list_in(char **inp, char *name); +static char *insert_var_value(char *inp); +static char *insert_var_value_sub(char *inp, int tag_subst); + +#ifndef __U_BOOT__ +/* Table of built-in functions. They can be forked or not, depending on + * context: within pipes, they fork. As simple commands, they do not. + * When used in non-forking context, they can change global variables + * in the parent shell process. If forked, of course they can not. + * For example, 'unset foo | whatever' will parse and run, but foo will + * still be set at the end. */ +static struct built_in_command bltins[] = { + {"bg", "Resume a job in the background", builtin_fg_bg}, + {"break", "Exit for, while or until loop", builtin_not_written}, + {"cd", "Change working directory", builtin_cd}, + {"continue", "Continue for, while or until loop", builtin_not_written}, + {"env", "Print all environment variables", builtin_env}, + {"eval", "Construct and run shell command", builtin_eval}, + {"exec", "Exec command, replacing this shell with the exec'd process", + builtin_exec}, + {"exit", "Exit from shell()", builtin_exit}, + {"export", "Set environment variable", builtin_export}, + {"fg", "Bring job into the foreground", builtin_fg_bg}, + {"jobs", "Lists the active jobs", builtin_jobs}, + {"pwd", "Print current directory", builtin_pwd}, + {"read", "Input environment variable", builtin_read}, + {"return", "Return from a function", builtin_not_written}, + {"set", "Set/unset shell local variables", builtin_set}, + {"shift", "Shift positional parameters", builtin_shift}, + {"trap", "Trap signals", builtin_not_written}, + {"ulimit","Controls resource limits", builtin_not_written}, + {"umask","Sets file creation mask", builtin_umask}, + {"unset", "Unset environment variable", builtin_unset}, + {".", "Source-in and run commands in a file", builtin_source}, + {"help", "List shell built-in commands", builtin_help}, + {NULL, NULL, NULL} +}; + +static const char *set_cwd(void) +{ + if(cwd==unknown) + cwd = NULL; /* xgetcwd(arg) called free(arg) */ + cwd = xgetcwd((char *)cwd); + if (!cwd) + cwd = unknown; + return cwd; +} + +/* built-in 'eval' handler */ +static int builtin_eval(struct child_prog *child) +{ + char *str = NULL; + int rcode = EXIT_SUCCESS; + + if (child->argv[1]) { + str = make_string(child->argv + 1); + parse_string_outer(str, FLAG_EXIT_FROM_LOOP | + FLAG_PARSE_SEMICOLON); + free(str); + rcode = last_return_code; + } + return rcode; +} + +/* built-in 'cd <path>' handler */ +static int builtin_cd(struct child_prog *child) +{ + char *newdir; + if (child->argv[1] == NULL) + newdir = getenv("HOME"); + else + newdir = child->argv[1]; + if (chdir(newdir)) { + printf("cd: %s: %s\n", newdir, strerror(errno)); + return EXIT_FAILURE; + } + set_cwd(); + return EXIT_SUCCESS; +} + +/* built-in 'env' handler */ +static int builtin_env(struct child_prog *dummy) +{ + char **e = environ; + if (e == NULL) return EXIT_FAILURE; + for (; *e; e++) { + puts(*e); + } + return EXIT_SUCCESS; +} + +/* built-in 'exec' handler */ +static int builtin_exec(struct child_prog *child) +{ + if (child->argv[1] == NULL) + return EXIT_SUCCESS; /* Really? */ + child->argv++; + pseudo_exec(child); + /* never returns */ +} + +/* built-in 'exit' handler */ +static int builtin_exit(struct child_prog *child) +{ + if (child->argv[1] == NULL) + exit(last_return_code); + exit (atoi(child->argv[1])); +} + +/* built-in 'export VAR=value' handler */ +static int builtin_export(struct child_prog *child) +{ + int res = 0; + char *name = child->argv[1]; + + if (name == NULL) { + return (builtin_env(child)); + } + + name = strdup(name); + + if(name) { + char *value = strchr(name, '='); + + if (!value) { + char *tmp; + /* They are exporting something without an =VALUE */ + + value = get_local_var(name); + if (value) { + size_t ln = strlen(name); + + tmp = realloc(name, ln+strlen(value)+2); + if(tmp==NULL) + res = -1; + else { + sprintf(tmp+ln, "=%s", value); + name = tmp; + } + } else { + /* bash does not return an error when trying to export + * an undefined variable. Do likewise. */ + res = 1; + } + } + } + if (res<0) + perror_msg("export"); + else if(res==0) + res = set_local_var(name, 1); + else + res = 0; + free(name); + return res; +} + +/* built-in 'fg' and 'bg' handler */ +static int builtin_fg_bg(struct child_prog *child) +{ + int i, jobnum; + struct pipe *pi=NULL; + + if (!interactive) + return EXIT_FAILURE; + /* If they gave us no args, assume they want the last backgrounded task */ + if (!child->argv[1]) { + for (pi = job_list; pi; pi = pi->next) { + if (pi->jobid == last_jobid) { + break; + } + } + if (!pi) { + error_msg("%s: no current job", child->argv[0]); + return EXIT_FAILURE; + } + } else { + if (sscanf(child->argv[1], "%%%d", &jobnum) != 1) { + error_msg("%s: bad argument '%s'", child->argv[0], child->argv[1]); + return EXIT_FAILURE; + } + for (pi = job_list; pi; pi = pi->next) { + if (pi->jobid == jobnum) { + break; + } + } + if (!pi) { + error_msg("%s: %d: no such job", child->argv[0], jobnum); + return EXIT_FAILURE; + } + } + + if (*child->argv[0] == 'f') { + /* Put the job into the foreground. */ + tcsetpgrp(shell_terminal, pi->pgrp); + } + + /* Restart the processes in the job */ + for (i = 0; i < pi->num_progs; i++) + pi->progs[i].is_stopped = 0; + + if ( (i=kill(- pi->pgrp, SIGCONT)) < 0) { + if (i == ESRCH) { + remove_bg_job(pi); + } else { + perror_msg("kill (SIGCONT)"); + } + } + + pi->stopped_progs = 0; + return EXIT_SUCCESS; +} + +/* built-in 'help' handler */ +static int builtin_help(struct child_prog *dummy) +{ + struct built_in_command *x; + + printf("\nBuilt-in commands:\n"); + printf("-------------------\n"); + for (x = bltins; x->cmd; x++) { + if (x->descr==NULL) + continue; + printf("%s\t%s\n", x->cmd, x->descr); + } + printf("\n\n"); + return EXIT_SUCCESS; +} + +/* built-in 'jobs' handler */ +static int builtin_jobs(struct child_prog *child) +{ + struct pipe *job; + char *status_string; + + for (job = job_list; job; job = job->next) { + if (job->running_progs == job->stopped_progs) + status_string = "Stopped"; + else + status_string = "Running"; + + printf(JOB_STATUS_FORMAT, job->jobid, status_string, job->text); + } + return EXIT_SUCCESS; +} + + +/* built-in 'pwd' handler */ +static int builtin_pwd(struct child_prog *dummy) +{ + puts(set_cwd()); + return EXIT_SUCCESS; +} + +/* built-in 'read VAR' handler */ +static int builtin_read(struct child_prog *child) +{ + int res; + + if (child->argv[1]) { + char string[BUFSIZ]; + char *var = 0; + + string[0] = 0; /* In case stdin has only EOF */ + /* read string */ + fgets(string, sizeof(string), stdin); + chomp(string); + var = malloc(strlen(child->argv[1])+strlen(string)+2); + if(var) { + sprintf(var, "%s=%s", child->argv[1], string); + res = set_local_var(var, 0); + } else + res = -1; + if (res) + fprintf(stderr, "read: %m\n"); + free(var); /* So not move up to avoid breaking errno */ + return res; + } else { + do res=getchar(); while(res!='\n' && res!=EOF); + return 0; + } +} + +/* built-in 'set VAR=value' handler */ +static int builtin_set(struct child_prog *child) +{ + char *temp = child->argv[1]; + struct variables *e; + + if (temp == NULL) + for(e = top_vars; e; e=e->next) + printf("%s=%s\n", e->name, e->value); + else + set_local_var(temp, 0); + + return EXIT_SUCCESS; +} + + +/* Built-in 'shift' handler */ +static int builtin_shift(struct child_prog *child) +{ + int n=1; + if (child->argv[1]) { + n=atoi(child->argv[1]); + } + if (n>=0 && n<global_argc) { + /* XXX This probably breaks $0 */ + global_argc -= n; + global_argv += n; + return EXIT_SUCCESS; + } else { + return EXIT_FAILURE; + } +} + +/* Built-in '.' handler (read-in and execute commands from file) */ +static int builtin_source(struct child_prog *child) +{ + FILE *input; + int status; + + if (child->argv[1] == NULL) + return EXIT_FAILURE; + + /* XXX search through $PATH is missing */ + input = fopen(child->argv[1], "r"); + if (!input) { + error_msg("Couldn't open file '%s'", child->argv[1]); + return EXIT_FAILURE; + } + + /* Now run the file */ + /* XXX argv and argc are broken; need to save old global_argv + * (pointer only is OK!) on this stack frame, + * set global_argv=child->argv+1, recurse, and restore. */ + mark_open(fileno(input)); + status = parse_file_outer(input); + mark_closed(fileno(input)); + fclose(input); + return (status); +} + +static int builtin_umask(struct child_prog *child) +{ + mode_t new_umask; + const char *arg = child->argv[1]; + char *end; + if (arg) { + new_umask=strtoul(arg, &end, 8); + if (*end!='\0' || end == arg) { + return EXIT_FAILURE; + } + } else { + printf("%.3o\n", (unsigned int) (new_umask=umask(0))); + } + umask(new_umask); + return EXIT_SUCCESS; +} + +/* built-in 'unset VAR' handler */ +static int builtin_unset(struct child_prog *child) +{ + /* bash returned already true */ + unset_local_var(child->argv[1]); + return EXIT_SUCCESS; +} + +static int builtin_not_written(struct child_prog *child) +{ + printf("builtin_%s not written\n",child->argv[0]); + return EXIT_FAILURE; +} +#endif + +static int b_check_space(o_string *o, int len) +{ + /* It would be easy to drop a more restrictive policy + * in here, such as setting a maximum string length */ + if (o->length + len > o->maxlen) { + char *old_data = o->data; + /* assert (data == NULL || o->maxlen != 0); */ + o->maxlen += max(2*len, B_CHUNK); + o->data = realloc(o->data, 1 + o->maxlen); + if (o->data == NULL) { + free(old_data); + } + } + return o->data == NULL; +} + +static int b_addchr(o_string *o, int ch) +{ + debug_printf("b_addchr: %c %d %p\n", ch, o->length, o); + if (b_check_space(o, 1)) return B_NOSPAC; + o->data[o->length] = ch; + o->length++; + o->data[o->length] = '\0'; + return 0; +} + +static void b_reset(o_string *o) +{ + o->length = 0; + o->nonnull = 0; + if (o->data != NULL) *o->data = '\0'; +} + +static void b_free(o_string *o) +{ + b_reset(o); + free(o->data); + o->data = NULL; + o->maxlen = 0; +} + +/* My analysis of quoting semantics tells me that state information + * is associated with a destination, not a source. + */ +static int b_addqchr(o_string *o, int ch, int quote) +{ + if (quote && strchr("*?[\\",ch)) { + int rc; + rc = b_addchr(o, '\\'); + if (rc) return rc; + } + return b_addchr(o, ch); +} + +#ifndef __U_BOOT__ +static int b_adduint(o_string *o, unsigned int i) +{ + int r; + char *p = simple_itoa(i); + /* no escape checking necessary */ + do r=b_addchr(o, *p++); while (r==0 && *p); + return r; +} +#endif + +static int static_get(struct in_str *i) +{ + int ch = *i->p++; + if (ch=='\0') return EOF; + return ch; +} + +static int static_peek(struct in_str *i) +{ + return *i->p; +} + +#ifndef __U_BOOT__ +static inline void cmdedit_set_initial_prompt(void) +{ +#ifndef CONFIG_FEATURE_SH_FANCY_PROMPT + PS1 = NULL; +#else + PS1 = getenv("PS1"); + if(PS1==0) + PS1 = "\\w \\$ "; +#endif +} + +static inline void setup_prompt_string(int promptmode, char **prompt_str) +{ + debug_printf("setup_prompt_string %d ",promptmode); +#ifndef CONFIG_FEATURE_SH_FANCY_PROMPT + /* Set up the prompt */ + if (promptmode == 1) { + free(PS1); + PS1=xmalloc(strlen(cwd)+4); + sprintf(PS1, "%s %s", cwd, ( geteuid() != 0 ) ? "$ ":"# "); + *prompt_str = PS1; + } else { + *prompt_str = PS2; + } +#else + *prompt_str = (promptmode==1)? PS1 : PS2; +#endif + debug_printf("result %s\n",*prompt_str); +} +#endif + +static void get_user_input(struct in_str *i) +{ +#ifndef __U_BOOT__ + char *prompt_str; + static char the_command[BUFSIZ]; + + setup_prompt_string(i->promptmode, &prompt_str); +#ifdef CONFIG_FEATURE_COMMAND_EDITING + /* + ** enable command line editing only while a command line + ** is actually being read; otherwise, we'll end up bequeathing + ** atexit() handlers and other unwanted stuff to our + ** child processes (rob@sysgo.de) + */ + cmdedit_read_input(prompt_str, the_command); +#else + fputs(prompt_str, stdout); + fflush(stdout); + the_command[0]=fgetc(i->file); + the_command[1]='\0'; +#endif + fflush(stdout); + i->p = the_command; +#else + int n; + static char the_command[CONFIG_SYS_CBSIZE + 1]; + +#ifdef CONFIG_BOOT_RETRY_TIME +# ifndef CONFIG_RESET_TO_RETRY +# error "This currently only works with CONFIG_RESET_TO_RETRY enabled" +# endif + reset_cmd_timeout(); +#endif + i->__promptme = 1; + if (i->promptmode == 1) { + n = readline(CONFIG_SYS_PROMPT); + } else { + n = readline(CONFIG_SYS_PROMPT_HUSH_PS2); + } +#ifdef CONFIG_BOOT_RETRY_TIME + if (n == -2) { + puts("\nTimeout waiting for command\n"); +# ifdef CONFIG_RESET_TO_RETRY + do_reset(NULL, 0, 0, NULL); +# else +# error "This currently only works with CONFIG_RESET_TO_RETRY enabled" +# endif + } +#endif + if (n == -1 ) { + flag_repeat = 0; + i->__promptme = 0; + } + n = strlen(console_buffer); + console_buffer[n] = '\n'; + console_buffer[n+1]= '\0'; + if (had_ctrlc()) flag_repeat = 0; + clear_ctrlc(); + do_repeat = 0; + if (i->promptmode == 1) { + if (console_buffer[0] == '\n'&& flag_repeat == 0) { + strcpy(the_command,console_buffer); + } + else { + if (console_buffer[0] != '\n') { + strcpy(the_command,console_buffer); + flag_repeat = 1; + } + else { + do_repeat = 1; + } + } + i->p = the_command; + } + else { + if (console_buffer[0] != '\n') { + if (strlen(the_command) + strlen(console_buffer) + < CONFIG_SYS_CBSIZE) { + n = strlen(the_command); + the_command[n-1] = ' '; + strcpy(&the_command[n],console_buffer); + } + else { + the_command[0] = '\n'; + the_command[1] = '\0'; + flag_repeat = 0; + } + } + if (i->__promptme == 0) { + the_command[0] = '\n'; + the_command[1] = '\0'; + } + i->p = console_buffer; + } +#endif +} + +/* This is the magic location that prints prompts + * and gets data back from the user */ +static int file_get(struct in_str *i) +{ + int ch; + + ch = 0; + /* If there is data waiting, eat it up */ + if (i->p && *i->p) { + ch = *i->p++; + } else { + /* need to double check i->file because we might be doing something + * more complicated by now, like sourcing or substituting. */ +#ifndef __U_BOOT__ + if (i->__promptme && interactive && i->file == stdin) { + while(! i->p || (interactive && strlen(i->p)==0) ) { +#else + while(! i->p || strlen(i->p)==0 ) { +#endif + get_user_input(i); + } + i->promptmode=2; +#ifndef __U_BOOT__ + i->__promptme = 0; +#endif + if (i->p && *i->p) { + ch = *i->p++; + } +#ifndef __U_BOOT__ + } else { + ch = fgetc(i->file); + } + +#endif + debug_printf("b_getch: got a %d\n", ch); + } +#ifndef __U_BOOT__ + if (ch == '\n') i->__promptme=1; +#endif + return ch; +} + +/* All the callers guarantee this routine will never be + * used right after a newline, so prompting is not needed. + */ +static int file_peek(struct in_str *i) +{ +#ifndef __U_BOOT__ + if (i->p && *i->p) { +#endif + return *i->p; +#ifndef __U_BOOT__ + } else { + i->peek_buf[0] = fgetc(i->file); + i->peek_buf[1] = '\0'; + i->p = i->peek_buf; + debug_printf("b_peek: got a %d\n", *i->p); + return *i->p; + } +#endif +} + +#ifndef __U_BOOT__ +static void setup_file_in_str(struct in_str *i, FILE *f) +#else +static void setup_file_in_str(struct in_str *i) +#endif +{ + i->peek = file_peek; + i->get = file_get; + i->__promptme=1; + i->promptmode=1; +#ifndef __U_BOOT__ + i->file = f; +#endif + i->p = NULL; +} + +static void setup_string_in_str(struct in_str *i, const char *s) +{ + i->peek = static_peek; + i->get = static_get; + i->__promptme=1; + i->promptmode=1; + i->p = s; +} + +#ifndef __U_BOOT__ +static void mark_open(int fd) +{ + struct close_me *new = xmalloc(sizeof(struct close_me)); + new->fd = fd; + new->next = close_me_head; + close_me_head = new; +} + +static void mark_closed(int fd) +{ + struct close_me *tmp; + if (close_me_head == NULL || close_me_head->fd != fd) + error_msg_and_die("corrupt close_me"); + tmp = close_me_head; + close_me_head = close_me_head->next; + free(tmp); +} + +static void close_all(void) +{ + struct close_me *c; + for (c=close_me_head; c; c=c->next) { + close(c->fd); + } + close_me_head = NULL; +} + +/* squirrel != NULL means we squirrel away copies of stdin, stdout, + * and stderr if they are redirected. */ +static int setup_redirects(struct child_prog *prog, int squirrel[]) +{ + int openfd, mode; + struct redir_struct *redir; + + for (redir=prog->redirects; redir; redir=redir->next) { + if (redir->dup == -1 && redir->word.gl_pathv == NULL) { + /* something went wrong in the parse. Pretend it didn't happen */ + continue; + } + if (redir->dup == -1) { + mode=redir_table[redir->type].mode; + openfd = open(redir->word.gl_pathv[0], mode, 0666); + if (openfd < 0) { + /* this could get lost if stderr has been redirected, but + bash and ash both lose it as well (though zsh doesn't!) */ + perror_msg("error opening %s", redir->word.gl_pathv[0]); + return 1; + } + } else { + openfd = redir->dup; + } + + if (openfd != redir->fd) { + if (squirrel && redir->fd < 3) { + squirrel[redir->fd] = dup(redir->fd); + } + if (openfd == -3) { + close(openfd); + } else { + dup2(openfd, redir->fd); + if (redir->dup == -1) + close (openfd); + } + } + } + return 0; +} + +static void restore_redirects(int squirrel[]) +{ + int i, fd; + for (i=0; i<3; i++) { + fd = squirrel[i]; + if (fd != -1) { + /* No error checking. I sure wouldn't know what + * to do with an error if I found one! */ + dup2(fd, i); + close(fd); + } + } +} + +/* never returns */ +/* XXX no exit() here. If you don't exec, use _exit instead. + * The at_exit handlers apparently confuse the calling process, + * in particular stdin handling. Not sure why? */ +static void pseudo_exec(struct child_prog *child) +{ + int i, rcode; + char *p; + struct built_in_command *x; + if (child->argv) { + for (i=0; is_assignment(child->argv[i]); i++) { + debug_printf("pid %d environment modification: %s\n",getpid(),child->argv[i]); + p = insert_var_value(child->argv[i]); + putenv(strdup(p)); + if (p != child->argv[i]) free(p); + } + child->argv+=i; /* XXX this hack isn't so horrible, since we are about + to exit, and therefore don't need to keep data + structures consistent for free() use. */ + /* If a variable is assigned in a forest, and nobody listens, + * was it ever really set? + */ + if (child->argv[0] == NULL) { + _exit(EXIT_SUCCESS); + } + + /* + * Check if the command matches any of the builtins. + * Depending on context, this might be redundant. But it's + * easier to waste a few CPU cycles than it is to figure out + * if this is one of those cases. + */ + for (x = bltins; x->cmd; x++) { + if (strcmp(child->argv[0], x->cmd) == 0 ) { + debug_printf("builtin exec %s\n", child->argv[0]); + rcode = x->function(child); + fflush(stdout); + _exit(rcode); + } + } + + /* Check if the command matches any busybox internal commands + * ("applets") here. + * FIXME: This feature is not 100% safe, since + * BusyBox is not fully reentrant, so we have no guarantee the things + * from the .bss are still zeroed, or that things from .data are still + * at their defaults. We could exec ourself from /proc/self/exe, but I + * really dislike relying on /proc for things. We could exec ourself + * from global_argv[0], but if we are in a chroot, we may not be able + * to find ourself... */ +#ifdef CONFIG_FEATURE_SH_STANDALONE_SHELL + { + int argc_l; + char** argv_l=child->argv; + char *name = child->argv[0]; + +#ifdef CONFIG_FEATURE_SH_APPLETS_ALWAYS_WIN + /* Following discussions from November 2000 on the busybox mailing + * list, the default configuration, (without + * get_last_path_component()) lets the user force use of an + * external command by specifying the full (with slashes) filename. + * If you enable CONFIG_FEATURE_SH_APPLETS_ALWAYS_WIN then applets + * _aways_ override external commands, so if you want to run + * /bin/cat, it will use BusyBox cat even if /bin/cat exists on the + * filesystem and is _not_ busybox. Some systems may want this, + * most do not. */ + name = get_last_path_component(name); +#endif + /* Count argc for use in a second... */ + for(argc_l=0;*argv_l!=NULL; argv_l++, argc_l++); + optind = 1; + debug_printf("running applet %s\n", name); + run_applet_by_name(name, argc_l, child->argv); + } +#endif + debug_printf("exec of %s\n",child->argv[0]); + execvp(child->argv[0],child->argv); + perror_msg("couldn't exec: %s",child->argv[0]); + _exit(1); + } else if (child->group) { + debug_printf("runtime nesting to group\n"); + interactive=0; /* crucial!!!! */ + rcode = run_list_real(child->group); + /* OK to leak memory by not calling free_pipe_list, + * since this process is about to exit */ + _exit(rcode); + } else { + /* Can happen. See what bash does with ">foo" by itself. */ + debug_printf("trying to pseudo_exec null command\n"); + _exit(EXIT_SUCCESS); + } +} + +static void insert_bg_job(struct pipe *pi) +{ + struct pipe *thejob; + + /* Linear search for the ID of the job to use */ + pi->jobid = 1; + for (thejob = job_list; thejob; thejob = thejob->next) + if (thejob->jobid >= pi->jobid) + pi->jobid = thejob->jobid + 1; + + /* add thejob to the list of running jobs */ + if (!job_list) { + thejob = job_list = xmalloc(sizeof(*thejob)); + } else { + for (thejob = job_list; thejob->next; thejob = thejob->next) /* nothing */; + thejob->next = xmalloc(sizeof(*thejob)); + thejob = thejob->next; + } + + /* physically copy the struct job */ + memcpy(thejob, pi, sizeof(struct pipe)); + thejob->next = NULL; + thejob->running_progs = thejob->num_progs; + thejob->stopped_progs = 0; + thejob->text = xmalloc(BUFSIZ); /* cmdedit buffer size */ + + /*if (pi->progs[0] && pi->progs[0].argv && pi->progs[0].argv[0]) */ + { + char *bar=thejob->text; + char **foo=pi->progs[0].argv; + while(foo && *foo) { + bar += sprintf(bar, "%s ", *foo++); + } + } + + /* we don't wait for background thejobs to return -- append it + to the list of backgrounded thejobs and leave it alone */ + printf("[%d] %d\n", thejob->jobid, thejob->progs[0].pid); + last_bg_pid = thejob->progs[0].pid; + last_jobid = thejob->jobid; +} + +/* remove a backgrounded job */ +static void remove_bg_job(struct pipe *pi) +{ + struct pipe *prev_pipe; + + if (pi == job_list) { + job_list = pi->next; + } else { + prev_pipe = job_list; + while (prev_pipe->next != pi) + prev_pipe = prev_pipe->next; + prev_pipe->next = pi->next; + } + if (job_list) + last_jobid = job_list->jobid; + else + last_jobid = 0; + + pi->stopped_progs = 0; + free_pipe(pi, 0); + free(pi); +} + +/* Checks to see if any processes have exited -- if they + have, figure out why and see if a job has completed */ +static int checkjobs(struct pipe* fg_pipe) +{ + int attributes; + int status; + int prognum = 0; + struct pipe *pi; + pid_t childpid; + + attributes = WUNTRACED; + if (fg_pipe==NULL) { + attributes |= WNOHANG; + } + + while ((childpid = waitpid(-1, &status, attributes)) > 0) { + if (fg_pipe) { + int i, rcode = 0; + for (i=0; i < fg_pipe->num_progs; i++) { + if (fg_pipe->progs[i].pid == childpid) { + if (i==fg_pipe->num_progs-1) + rcode=WEXITSTATUS(status); + (fg_pipe->num_progs)--; + return(rcode); + } + } + } + + for (pi = job_list; pi; pi = pi->next) { + prognum = 0; + while (prognum < pi->num_progs && pi->progs[prognum].pid != childpid) { + prognum++; + } + if (prognum < pi->num_progs) + break; + } + + if(pi==NULL) { + debug_printf("checkjobs: pid %d was not in our list!\n", childpid); + continue; + } + + if (WIFEXITED(status) || WIFSIGNALED(status)) { + /* child exited */ + pi->running_progs--; + pi->progs[prognum].pid = 0; + + if (!pi->running_progs) { + printf(JOB_STATUS_FORMAT, pi->jobid, "Done", pi->text); + remove_bg_job(pi); + } + } else { + /* child stopped */ + pi->stopped_progs++; + pi->progs[prognum].is_stopped = 1; + +#if 0 + /* Printing this stuff is a pain, since it tends to + * overwrite the prompt an inconveinient moments. So + * don't do that. */ + if (pi->stopped_progs == pi->num_progs) { + printf("\n"JOB_STATUS_FORMAT, pi->jobid, "Stopped", pi->text); + } +#endif + } + } + + if (childpid == -1 && errno != ECHILD) + perror_msg("waitpid"); + + /* move the shell to the foreground */ + /*if (interactive && tcsetpgrp(shell_terminal, getpgid(0))) */ + /* perror_msg("tcsetpgrp-2"); */ + return -1; +} + +/* Figure out our controlling tty, checking in order stderr, + * stdin, and stdout. If check_pgrp is set, also check that + * we belong to the foreground process group associated with + * that tty. The value of shell_terminal is needed in order to call + * tcsetpgrp(shell_terminal, ...); */ +void controlling_tty(int check_pgrp) +{ + pid_t curpgrp; + + if ((curpgrp = tcgetpgrp(shell_terminal = 2)) < 0 + && (curpgrp = tcgetpgrp(shell_terminal = 0)) < 0 + && (curpgrp = tcgetpgrp(shell_terminal = 1)) < 0) + goto shell_terminal_error; + + if (check_pgrp && curpgrp != getpgid(0)) + goto shell_terminal_error; + + return; + +shell_terminal_error: + shell_terminal = -1; + return; +} +#endif + +/* run_pipe_real() starts all the jobs, but doesn't wait for anything + * to finish. See checkjobs(). + * + * return code is normally -1, when the caller has to wait for children + * to finish to determine the exit status of the pipe. If the pipe + * is a simple builtin command, however, the action is done by the + * time run_pipe_real returns, and the exit code is provided as the + * return value. + * + * The input of the pipe is always stdin, the output is always + * stdout. The outpipe[] mechanism in BusyBox-0.48 lash is bogus, + * because it tries to avoid running the command substitution in + * subshell, when that is in fact necessary. The subshell process + * now has its stdout directed to the input of the appropriate pipe, + * so this routine is noticeably simpler. + */ +static int run_pipe_real(struct pipe *pi) +{ + int i; +#ifndef __U_BOOT__ + int nextin, nextout; + int pipefds[2]; /* pipefds[0] is for reading */ + struct child_prog *child; + struct built_in_command *x; + char *p; +# if __GNUC__ + /* Avoid longjmp clobbering */ + (void) &i; + (void) &nextin; + (void) &nextout; + (void) &child; +# endif +#else + int nextin; + int flag = do_repeat ? CMD_FLAG_REPEAT : 0; + struct child_prog *child; + char *p; +# if __GNUC__ + /* Avoid longjmp clobbering */ + (void) &i; + (void) &nextin; + (void) &child; +# endif +#endif /* __U_BOOT__ */ + + nextin = 0; +#ifndef __U_BOOT__ + pi->pgrp = -1; +#endif + + /* Check if this is a simple builtin (not part of a pipe). + * Builtins within pipes have to fork anyway, and are handled in + * pseudo_exec. "echo foo | read bar" doesn't work on bash, either. + */ + if (pi->num_progs == 1) child = & (pi->progs[0]); +#ifndef __U_BOOT__ + if (pi->num_progs == 1 && child->group && child->subshell == 0) { + int squirrel[] = {-1, -1, -1}; + int rcode; + debug_printf("non-subshell grouping\n"); + setup_redirects(child, squirrel); + /* XXX could we merge code with following builtin case, + * by creating a pseudo builtin that calls run_list_real? */ + rcode = run_list_real(child->group); + restore_redirects(squirrel); +#else + if (pi->num_progs == 1 && child->group) { + int rcode; + debug_printf("non-subshell grouping\n"); + rcode = run_list_real(child->group); +#endif + return rcode; + } else if (pi->num_progs == 1 && pi->progs[0].argv != NULL) { + for (i=0; is_assignment(child->argv[i]); i++) { /* nothing */ } + if (i!=0 && child->argv[i]==NULL) { + /* assignments, but no command: set the local environment */ + for (i=0; child->argv[i]!=NULL; i++) { + + /* Ok, this case is tricky. We have to decide if this is a + * local variable, or an already exported variable. If it is + * already exported, we have to export the new value. If it is + * not exported, we need only set this as a local variable. + * This junk is all to decide whether or not to export this + * variable. */ + int export_me=0; + char *name, *value; + name = xstrdup(child->argv[i]); + debug_printf("Local environment set: %s\n", name); + value = strchr(name, '='); + if (value) + *value=0; +#ifndef __U_BOOT__ + if ( get_local_var(name)) { + export_me=1; + } +#endif + free(name); + p = insert_var_value(child->argv[i]); + set_local_var(p, export_me); + if (p != child->argv[i]) free(p); + } + return EXIT_SUCCESS; /* don't worry about errors in set_local_var() yet */ + } + for (i = 0; is_assignment(child->argv[i]); i++) { + p = insert_var_value(child->argv[i]); +#ifndef __U_BOOT__ + putenv(strdup(p)); +#else + set_local_var(p, 0); +#endif + if (p != child->argv[i]) { + child->sp--; + free(p); + } + } + if (child->sp) { + char * str = NULL; + + str = make_string(child->argv + i, + child->argv_nonnull + i); + parse_string_outer(str, FLAG_EXIT_FROM_LOOP | FLAG_REPARSING); + free(str); + return last_return_code; + } +#ifndef __U_BOOT__ + for (x = bltins; x->cmd; x++) { + if (strcmp(child->argv[i], x->cmd) == 0 ) { + int squirrel[] = {-1, -1, -1}; + int rcode; + if (x->function == builtin_exec && child->argv[i+1]==NULL) { + debug_printf("magic exec\n"); + setup_redirects(child,NULL); + return EXIT_SUCCESS; + } + debug_printf("builtin inline %s\n", child->argv[0]); + /* XXX setup_redirects acts on file descriptors, not FILEs. + * This is perfect for work that comes after exec(). + * Is it really safe for inline use? Experimentally, + * things seem to work with glibc. */ + setup_redirects(child, squirrel); + + child->argv += i; /* XXX horrible hack */ + rcode = x->function(child); + /* XXX restore hack so free() can work right */ + child->argv -= i; + restore_redirects(squirrel); + } + return rcode; + } +#else + /* check ";", because ,example , argv consist from + * "help;flinfo" must not execute + */ + if (strchr(child->argv[i], ';')) { + printf("Unknown command '%s' - try 'help' or use " + "'run' command\n", child->argv[i]); + return -1; + } + /* Process the command */ + return cmd_process(flag, child->argc, child->argv, + &flag_repeat, NULL); +#endif + } +#ifndef __U_BOOT__ + + for (i = 0; i < pi->num_progs; i++) { + child = & (pi->progs[i]); + + /* pipes are inserted between pairs of commands */ + if ((i + 1) < pi->num_progs) { + if (pipe(pipefds)<0) perror_msg_and_die("pipe"); + nextout = pipefds[1]; + } else { + nextout=1; + pipefds[0] = -1; + } + + /* XXX test for failed fork()? */ + if (!(child->pid = fork())) { + /* Set the handling for job control signals back to the default. */ + signal(SIGINT, SIG_DFL); + signal(SIGQUIT, SIG_DFL); + signal(SIGTERM, SIG_DFL); + signal(SIGTSTP, SIG_DFL); + signal(SIGTTIN, SIG_DFL); + signal(SIGTTOU, SIG_DFL); + signal(SIGCHLD, SIG_DFL); + + close_all(); + + if (nextin != 0) { + dup2(nextin, 0); + close(nextin); + } + if (nextout != 1) { + dup2(nextout, 1); + close(nextout); + } + if (pipefds[0]!=-1) { + close(pipefds[0]); /* opposite end of our output pipe */ + } + + /* Like bash, explicit redirects override pipes, + * and the pipe fd is available for dup'ing. */ + setup_redirects(child,NULL); + + if (interactive && pi->followup!=PIPE_BG) { + /* If we (the child) win the race, put ourselves in the process + * group whose leader is the first process in this pipe. */ + if (pi->pgrp < 0) { + pi->pgrp = getpid(); + } + if (setpgid(0, pi->pgrp) == 0) { + tcsetpgrp(2, pi->pgrp); + } + } + + pseudo_exec(child); + } + + + /* put our child in the process group whose leader is the + first process in this pipe */ + if (pi->pgrp < 0) { + pi->pgrp = child->pid; + } + /* Don't check for errors. The child may be dead already, + * in which case setpgid returns error code EACCES. */ + setpgid(child->pid, pi->pgrp); + + if (nextin != 0) + close(nextin); + if (nextout != 1) + close(nextout); + + /* If there isn't another process, nextin is garbage + but it doesn't matter */ + nextin = pipefds[0]; + } +#endif + return -1; +} + +static int run_list_real(struct pipe *pi) +{ + char *save_name = NULL; + char **list = NULL; + char **save_list = NULL; + struct pipe *rpipe; + int flag_rep = 0; +#ifndef __U_BOOT__ + int save_num_progs; +#endif + int rcode=0, flag_skip=1; + int flag_restore = 0; + int if_code=0, next_if_code=0; /* need double-buffer to handle elif */ + reserved_style rmode, skip_more_in_this_rmode=RES_XXXX; + /* check syntax for "for" */ + for (rpipe = pi; rpipe; rpipe = rpipe->next) { + if ((rpipe->r_mode == RES_IN || + rpipe->r_mode == RES_FOR) && + (rpipe->next == NULL)) { + syntax(); +#ifdef __U_BOOT__ + flag_repeat = 0; +#endif + return 1; + } + if ((rpipe->r_mode == RES_IN && + (rpipe->next->r_mode == RES_IN && + rpipe->next->progs->argv != NULL))|| + (rpipe->r_mode == RES_FOR && + rpipe->next->r_mode != RES_IN)) { + syntax(); +#ifdef __U_BOOT__ + flag_repeat = 0; +#endif + return 1; + } + } + for (; pi; pi = (flag_restore != 0) ? rpipe : pi->next) { + if (pi->r_mode == RES_WHILE || pi->r_mode == RES_UNTIL || + pi->r_mode == RES_FOR) { +#ifdef __U_BOOT__ + /* check Ctrl-C */ + ctrlc(); + if ((had_ctrlc())) { + return 1; + } +#endif + flag_restore = 0; + if (!rpipe) { + flag_rep = 0; + rpipe = pi; + } + } + rmode = pi->r_mode; + debug_printf("rmode=%d if_code=%d next_if_code=%d skip_more=%d\n", rmode, if_code, next_if_code, skip_more_in_this_rmode); + if (rmode == skip_more_in_this_rmode && flag_skip) { + if (pi->followup == PIPE_SEQ) flag_skip=0; + continue; + } + flag_skip = 1; + skip_more_in_this_rmode = RES_XXXX; + if (rmode == RES_THEN || rmode == RES_ELSE) if_code = next_if_code; + if (rmode == RES_THEN && if_code) continue; + if (rmode == RES_ELSE && !if_code) continue; + if (rmode == RES_ELIF && !if_code) break; + if (rmode == RES_FOR && pi->num_progs) { + if (!list) { + /* if no variable values after "in" we skip "for" */ + if (!pi->next->progs->argv) continue; + /* create list of variable values */ + list = make_list_in(pi->next->progs->argv, + pi->progs->argv[0]); + save_list = list; + save_name = pi->progs->argv[0]; + pi->progs->argv[0] = NULL; + flag_rep = 1; + } + if (!(*list)) { + free(pi->progs->argv[0]); + free(save_list); + list = NULL; + flag_rep = 0; + pi->progs->argv[0] = save_name; +#ifndef __U_BOOT__ + pi->progs->glob_result.gl_pathv[0] = + pi->progs->argv[0]; +#endif + continue; + } else { + /* insert new value from list for variable */ + if (pi->progs->argv[0]) + free(pi->progs->argv[0]); + pi->progs->argv[0] = *list++; +#ifndef __U_BOOT__ + pi->progs->glob_result.gl_pathv[0] = + pi->progs->argv[0]; +#endif + } + } + if (rmode == RES_IN) continue; + if (rmode == RES_DO) { + if (!flag_rep) continue; + } + if ((rmode == RES_DONE)) { + if (flag_rep) { + flag_restore = 1; + } else { + rpipe = NULL; + } + } + if (pi->num_progs == 0) continue; +#ifndef __U_BOOT__ + save_num_progs = pi->num_progs; /* save number of programs */ +#endif + rcode = run_pipe_real(pi); + debug_printf("run_pipe_real returned %d\n",rcode); +#ifndef __U_BOOT__ + if (rcode!=-1) { + /* We only ran a builtin: rcode was set by the return value + * of run_pipe_real(), and we don't need to wait for anything. */ + } else if (pi->followup==PIPE_BG) { + /* XXX check bash's behavior with nontrivial pipes */ + /* XXX compute jobid */ + /* XXX what does bash do with attempts to background builtins? */ + insert_bg_job(pi); + rcode = EXIT_SUCCESS; + } else { + if (interactive) { + /* move the new process group into the foreground */ + if (tcsetpgrp(shell_terminal, pi->pgrp) && errno != ENOTTY) + perror_msg("tcsetpgrp-3"); + rcode = checkjobs(pi); + /* move the shell to the foreground */ + if (tcsetpgrp(shell_terminal, getpgid(0)) && errno != ENOTTY) + perror_msg("tcsetpgrp-4"); + } else { + rcode = checkjobs(pi); + } + debug_printf("checkjobs returned %d\n",rcode); + } + last_return_code=rcode; +#else + if (rcode < -1) { + last_return_code = -rcode - 2; + return -2; /* exit */ + } + last_return_code=(rcode == 0) ? 0 : 1; +#endif +#ifndef __U_BOOT__ + pi->num_progs = save_num_progs; /* restore number of programs */ +#endif + if ( rmode == RES_IF || rmode == RES_ELIF ) + next_if_code=rcode; /* can be overwritten a number of times */ + if (rmode == RES_WHILE) + flag_rep = !last_return_code; + if (rmode == RES_UNTIL) + flag_rep = last_return_code; + if ( (rcode==EXIT_SUCCESS && pi->followup==PIPE_OR) || + (rcode!=EXIT_SUCCESS && pi->followup==PIPE_AND) ) + skip_more_in_this_rmode=rmode; +#ifndef __U_BOOT__ + checkjobs(NULL); +#endif + } + return rcode; +} + +/* broken, of course, but OK for testing */ +static char *indenter(int i) +{ + static char blanks[]=" "; + return &blanks[sizeof(blanks)-i-1]; +} + +/* return code is the exit status of the pipe */ +static int free_pipe(struct pipe *pi, int indent) +{ + char **p; + struct child_prog *child; +#ifndef __U_BOOT__ + struct redir_struct *r, *rnext; +#endif + int a, i, ret_code=0; + char *ind = indenter(indent); + +#ifndef __U_BOOT__ + if (pi->stopped_progs > 0) + return ret_code; + final_printf("%s run pipe: (pid %d)\n",ind,getpid()); +#endif + for (i=0; i<pi->num_progs; i++) { + child = &pi->progs[i]; + final_printf("%s command %d:\n",ind,i); + if (child->argv) { + for (a=0,p=child->argv; *p; a++,p++) { + final_printf("%s argv[%d] = %s\n",ind,a,*p); + } +#ifndef __U_BOOT__ + globfree(&child->glob_result); +#else + for (a = 0; a < child->argc; a++) { + free(child->argv[a]); + } + free(child->argv); + free(child->argv_nonnull); + child->argc = 0; +#endif + child->argv=NULL; + } else if (child->group) { +#ifndef __U_BOOT__ + final_printf("%s begin group (subshell:%d)\n",ind, child->subshell); +#endif + ret_code = free_pipe_list(child->group,indent+3); + final_printf("%s end group\n",ind); + } else { + final_printf("%s (nil)\n",ind); + } +#ifndef __U_BOOT__ + for (r=child->redirects; r; r=rnext) { + final_printf("%s redirect %d%s", ind, r->fd, redir_table[r->type].descrip); + if (r->dup == -1) { + /* guard against the case >$FOO, where foo is unset or blank */ + if (r->word.gl_pathv) { + final_printf(" %s\n", *r->word.gl_pathv); + globfree(&r->word); + } + } else { + final_printf("&%d\n", r->dup); + } + rnext=r->next; + free(r); + } + child->redirects=NULL; +#endif + } + free(pi->progs); /* children are an array, they get freed all at once */ + pi->progs=NULL; + return ret_code; +} + +static int free_pipe_list(struct pipe *head, int indent) +{ + int rcode=0; /* if list has no members */ + struct pipe *pi, *next; + char *ind = indenter(indent); + for (pi=head; pi; pi=next) { + final_printf("%s pipe reserved mode %d\n", ind, pi->r_mode); + rcode = free_pipe(pi, indent); + final_printf("%s pipe followup code %d\n", ind, pi->followup); + next=pi->next; + pi->next=NULL; + free(pi); + } + return rcode; +} + +/* Select which version we will use */ +static int run_list(struct pipe *pi) +{ + int rcode=0; +#ifndef __U_BOOT__ + if (fake_mode==0) { +#endif + rcode = run_list_real(pi); +#ifndef __U_BOOT__ + } +#endif + /* free_pipe_list has the side effect of clearing memory + * In the long run that function can be merged with run_list_real, + * but doing that now would hobble the debugging effort. */ + free_pipe_list(pi,0); + return rcode; +} + +/* The API for glob is arguably broken. This routine pushes a non-matching + * string into the output structure, removing non-backslashed backslashes. + * If someone can prove me wrong, by performing this function within the + * original glob(3) api, feel free to rewrite this routine into oblivion. + * Return code (0 vs. GLOB_NOSPACE) matches glob(3). + * XXX broken if the last character is '\\', check that before calling. + */ +#ifndef __U_BOOT__ +static int globhack(const char *src, int flags, glob_t *pglob) +{ + int cnt=0, pathc; + const char *s; + char *dest; + for (cnt=1, s=src; s && *s; s++) { + if (*s == '\\') s++; + cnt++; + } + dest = malloc(cnt); + if (!dest) return GLOB_NOSPACE; + if (!(flags & GLOB_APPEND)) { + pglob->gl_pathv=NULL; + pglob->gl_pathc=0; + pglob->gl_offs=0; + pglob->gl_offs=0; + } + pathc = ++pglob->gl_pathc; + pglob->gl_pathv = realloc(pglob->gl_pathv, (pathc+1)*sizeof(*pglob->gl_pathv)); + if (pglob->gl_pathv == NULL) return GLOB_NOSPACE; + pglob->gl_pathv[pathc-1]=dest; + pglob->gl_pathv[pathc]=NULL; + for (s=src; s && *s; s++, dest++) { + if (*s == '\\') s++; + *dest = *s; + } + *dest='\0'; + return 0; +} + +/* XXX broken if the last character is '\\', check that before calling */ +static int glob_needed(const char *s) +{ + for (; *s; s++) { + if (*s == '\\') s++; + if (strchr("*[?",*s)) return 1; + } + return 0; +} + +#if 0 +static void globprint(glob_t *pglob) +{ + int i; + debug_printf("glob_t at %p:\n", pglob); + debug_printf(" gl_pathc=%d gl_pathv=%p gl_offs=%d gl_flags=%d\n", + pglob->gl_pathc, pglob->gl_pathv, pglob->gl_offs, pglob->gl_flags); + for (i=0; i<pglob->gl_pathc; i++) + debug_printf("pglob->gl_pathv[%d] = %p = %s\n", i, + pglob->gl_pathv[i], pglob->gl_pathv[i]); +} +#endif + +static int xglob(o_string *dest, int flags, glob_t *pglob) +{ + int gr; + + /* short-circuit for null word */ + /* we can code this better when the debug_printf's are gone */ + if (dest->length == 0) { + if (dest->nonnull) { + /* bash man page calls this an "explicit" null */ + gr = globhack(dest->data, flags, pglob); + debug_printf("globhack returned %d\n",gr); + } else { + return 0; + } + } else if (glob_needed(dest->data)) { + gr = glob(dest->data, flags, NULL, pglob); + debug_printf("glob returned %d\n",gr); + if (gr == GLOB_NOMATCH) { + /* quote removal, or more accurately, backslash removal */ + gr = globhack(dest->data, flags, pglob); + debug_printf("globhack returned %d\n",gr); + } + } else { + gr = globhack(dest->data, flags, pglob); + debug_printf("globhack returned %d\n",gr); + } + if (gr == GLOB_NOSPACE) + error_msg_and_die("out of memory during glob"); + if (gr != 0) { /* GLOB_ABORTED ? */ + error_msg("glob(3) error %d",gr); + } + /* globprint(glob_target); */ + return gr; +} +#endif + +#ifdef __U_BOOT__ +static char *get_dollar_var(char ch); +#endif + +/* This is used to get/check local shell variables */ +char *get_local_var(const char *s) +{ + struct variables *cur; + + if (!s) + return NULL; + +#ifdef __U_BOOT__ + if (*s == '$') + return get_dollar_var(s[1]); +#endif + + for (cur = top_vars; cur; cur=cur->next) + if(strcmp(cur->name, s)==0) + return cur->value; + return NULL; +} + +/* This is used to set local shell variables + flg_export==0 if only local (not exporting) variable + flg_export==1 if "new" exporting environ + flg_export>1 if current startup environ (not call putenv()) */ +int set_local_var(const char *s, int flg_export) +{ + char *name, *value; + int result=0; + struct variables *cur; + +#ifdef __U_BOOT__ + /* might be possible! */ + if (!isalpha(*s)) + return -1; +#endif + + name=strdup(s); + +#ifdef __U_BOOT__ + if (getenv(name) != NULL) { + printf ("ERROR: " + "There is a global environment variable with the same name.\n"); + free(name); + return -1; + } +#endif + /* Assume when we enter this function that we are already in + * NAME=VALUE format. So the first order of business is to + * split 's' on the '=' into 'name' and 'value' */ + value = strchr(name, '='); + if (value == NULL && ++value == NULL) { + free(name); + return -1; + } + *value++ = 0; + + for(cur = top_vars; cur; cur = cur->next) { + if(strcmp(cur->name, name)==0) + break; + } + + if(cur) { + if(strcmp(cur->value, value)==0) { + if(flg_export>0 && cur->flg_export==0) + cur->flg_export=flg_export; + else + result++; + } else { + if(cur->flg_read_only) { + error_msg("%s: readonly variable", name); + result = -1; + } else { + if(flg_export>0 || cur->flg_export>1) + cur->flg_export=1; + free(cur->value); + + cur->value = strdup(value); + } + } + } else { + cur = malloc(sizeof(struct variables)); + if(!cur) { + result = -1; + } else { + cur->name = strdup(name); + if (cur->name == NULL) { + free(cur); + result = -1; + } else { + struct variables *bottom = top_vars; + cur->value = strdup(value); + cur->next = NULL; + cur->flg_export = flg_export; + cur->flg_read_only = 0; + while(bottom->next) bottom=bottom->next; + bottom->next = cur; + } + } + } + +#ifndef __U_BOOT__ + if(result==0 && cur->flg_export==1) { + *(value-1) = '='; + result = putenv(name); + } else { +#endif + free(name); +#ifndef __U_BOOT__ + if(result>0) /* equivalent to previous set */ + result = 0; + } +#endif + return result; +} + +void unset_local_var(const char *name) +{ + struct variables *cur; + + if (name) { + for (cur = top_vars; cur; cur=cur->next) { + if(strcmp(cur->name, name)==0) + break; + } + if (cur != NULL) { + struct variables *next = top_vars; + if(cur->flg_read_only) { + error_msg("%s: readonly variable", name); + return; + } else { +#ifndef __U_BOOT__ + if(cur->flg_export) + unsetenv(cur->name); +#endif + free(cur->name); + free(cur->value); + while (next->next != cur) + next = next->next; + next->next = cur->next; + } + free(cur); + } + } +} + +static int is_assignment(const char *s) +{ + if (s == NULL) + return 0; + + if (!isalpha(*s)) return 0; + ++s; + while(isalnum(*s) || *s=='_') ++s; + return *s=='='; +} + +#ifndef __U_BOOT__ +/* the src parameter allows us to peek forward to a possible &n syntax + * for file descriptor duplication, e.g., "2>&1". + * Return code is 0 normally, 1 if a syntax error is detected in src. + * Resource errors (in xmalloc) cause the process to exit */ +static int setup_redirect(struct p_context *ctx, int fd, redir_type style, + struct in_str *input) +{ + struct child_prog *child=ctx->child; + struct redir_struct *redir = child->redirects; + struct redir_struct *last_redir=NULL; + + /* Create a new redir_struct and drop it onto the end of the linked list */ + while(redir) { + last_redir=redir; + redir=redir->next; + } + redir = xmalloc(sizeof(struct redir_struct)); + redir->next=NULL; + redir->word.gl_pathv=NULL; + if (last_redir) { + last_redir->next=redir; + } else { + child->redirects=redir; + } + + redir->type=style; + redir->fd= (fd==-1) ? redir_table[style].default_fd : fd ; + + debug_printf("Redirect type %d%s\n", redir->fd, redir_table[style].descrip); + + /* Check for a '2>&1' type redirect */ + redir->dup = redirect_dup_num(input); + if (redir->dup == -2) return 1; /* syntax error */ + if (redir->dup != -1) { + /* Erik had a check here that the file descriptor in question + * is legit; I postpone that to "run time" + * A "-" representation of "close me" shows up as a -3 here */ + debug_printf("Duplicating redirect '%d>&%d'\n", redir->fd, redir->dup); + } else { + /* We do _not_ try to open the file that src points to, + * since we need to return and let src be expanded first. + * Set ctx->pending_redirect, so we know what to do at the + * end of the next parsed word. + */ + ctx->pending_redirect = redir; + } + return 0; +} +#endif + +static struct pipe *new_pipe(void) +{ + struct pipe *pi; + pi = xmalloc(sizeof(struct pipe)); + pi->num_progs = 0; + pi->progs = NULL; + pi->next = NULL; + pi->followup = 0; /* invalid */ + pi->r_mode = RES_NONE; + return pi; +} + +static void initialize_context(struct p_context *ctx) +{ + ctx->pipe=NULL; +#ifndef __U_BOOT__ + ctx->pending_redirect=NULL; +#endif + ctx->child=NULL; + ctx->list_head=new_pipe(); + ctx->pipe=ctx->list_head; + ctx->w=RES_NONE; + ctx->stack=NULL; +#ifdef __U_BOOT__ + ctx->old_flag=0; +#endif + done_command(ctx); /* creates the memory for working child */ +} + +/* normal return is 0 + * if a reserved word is found, and processed, return 1 + * should handle if, then, elif, else, fi, for, while, until, do, done. + * case, function, and select are obnoxious, save those for later. + */ +struct reserved_combo { + char *literal; + int code; + long flag; +}; +/* Mostly a list of accepted follow-up reserved words. + * FLAG_END means we are done with the sequence, and are ready + * to turn the compound list into a command. + * FLAG_START means the word must start a new compound list. + */ +static struct reserved_combo reserved_list[] = { + { "if", RES_IF, FLAG_THEN | FLAG_START }, + { "then", RES_THEN, FLAG_ELIF | FLAG_ELSE | FLAG_FI }, + { "elif", RES_ELIF, FLAG_THEN }, + { "else", RES_ELSE, FLAG_FI }, + { "fi", RES_FI, FLAG_END }, + { "for", RES_FOR, FLAG_IN | FLAG_START }, + { "while", RES_WHILE, FLAG_DO | FLAG_START }, + { "until", RES_UNTIL, FLAG_DO | FLAG_START }, + { "in", RES_IN, FLAG_DO }, + { "do", RES_DO, FLAG_DONE }, + { "done", RES_DONE, FLAG_END } +}; +#define NRES (sizeof(reserved_list)/sizeof(struct reserved_combo)) + +static int reserved_word(o_string *dest, struct p_context *ctx) +{ + struct reserved_combo *r; + for (r=reserved_list; + r<reserved_list+NRES; r++) { + if (strcmp(dest->data, r->literal) == 0) { + debug_printf("found reserved word %s, code %d\n",r->literal,r->code); + if (r->flag & FLAG_START) { + struct p_context *new = xmalloc(sizeof(struct p_context)); + debug_printf("push stack\n"); + if (ctx->w == RES_IN || ctx->w == RES_FOR) { + syntax(); + free(new); + ctx->w = RES_SNTX; + b_reset(dest); + return 1; + } + *new = *ctx; /* physical copy */ + initialize_context(ctx); + ctx->stack=new; + } else if ( ctx->w == RES_NONE || ! (ctx->old_flag & (1<<r->code))) { + syntax(); + ctx->w = RES_SNTX; + b_reset(dest); + return 1; + } + ctx->w=r->code; + ctx->old_flag = r->flag; + if (ctx->old_flag & FLAG_END) { + struct p_context *old; + debug_printf("pop stack\n"); + done_pipe(ctx,PIPE_SEQ); + old = ctx->stack; + old->child->group = ctx->list_head; +#ifndef __U_BOOT__ + old->child->subshell = 0; +#endif + *ctx = *old; /* physical copy */ + free(old); + } + b_reset (dest); + return 1; + } + } + return 0; +} + +/* normal return is 0. + * Syntax or xglob errors return 1. */ +static int done_word(o_string *dest, struct p_context *ctx) +{ + struct child_prog *child=ctx->child; +#ifndef __U_BOOT__ + glob_t *glob_target; + int gr, flags = 0; +#else + char *str, *s; + int argc, cnt; +#endif + + debug_printf("done_word: %s %p\n", dest->data, child); + if (dest->length == 0 && !dest->nonnull) { + debug_printf(" true null, ignored\n"); + return 0; + } +#ifndef __U_BOOT__ + if (ctx->pending_redirect) { + glob_target = &ctx->pending_redirect->word; + } else { +#endif + if (child->group) { + syntax(); + return 1; /* syntax error, groups and arglists don't mix */ + } + if (!child->argv && (ctx->type & FLAG_PARSE_SEMICOLON)) { + debug_printf("checking %s for reserved-ness\n",dest->data); + if (reserved_word(dest,ctx)) return ctx->w==RES_SNTX; + } +#ifndef __U_BOOT__ + glob_target = &child->glob_result; + if (child->argv) flags |= GLOB_APPEND; +#else + for (cnt = 1, s = dest->data; s && *s; s++) { + if (*s == '\\') s++; + cnt++; + } + str = malloc(cnt); + if (!str) return 1; + if ( child->argv == NULL) { + child->argc=0; + } + argc = ++child->argc; + child->argv = realloc(child->argv, (argc+1)*sizeof(*child->argv)); + if (child->argv == NULL) return 1; + child->argv_nonnull = realloc(child->argv_nonnull, + (argc+1)*sizeof(*child->argv_nonnull)); + if (child->argv_nonnull == NULL) + return 1; + child->argv[argc-1]=str; + child->argv_nonnull[argc-1] = dest->nonnull; + child->argv[argc]=NULL; + child->argv_nonnull[argc] = 0; + for (s = dest->data; s && *s; s++,str++) { + if (*s == '\\') s++; + *str = *s; + } + *str = '\0'; +#endif +#ifndef __U_BOOT__ + } + gr = xglob(dest, flags, glob_target); + if (gr != 0) return 1; +#endif + + b_reset(dest); +#ifndef __U_BOOT__ + if (ctx->pending_redirect) { + ctx->pending_redirect=NULL; + if (glob_target->gl_pathc != 1) { + error_msg("ambiguous redirect"); + return 1; + } + } else { + child->argv = glob_target->gl_pathv; + } +#endif + if (ctx->w == RES_FOR) { + done_word(dest,ctx); + done_pipe(ctx,PIPE_SEQ); + } + return 0; +} + +/* The only possible error here is out of memory, in which case + * xmalloc exits. */ +static int done_command(struct p_context *ctx) +{ + /* The child is really already in the pipe structure, so + * advance the pipe counter and make a new, null child. + * Only real trickiness here is that the uncommitted + * child structure, to which ctx->child points, is not + * counted in pi->num_progs. */ + struct pipe *pi=ctx->pipe; + struct child_prog *prog=ctx->child; + + if (prog && prog->group == NULL + && prog->argv == NULL +#ifndef __U_BOOT__ + && prog->redirects == NULL) { +#else + ) { +#endif + debug_printf("done_command: skipping null command\n"); + return 0; + } else if (prog) { + pi->num_progs++; + debug_printf("done_command: num_progs incremented to %d\n",pi->num_progs); + } else { + debug_printf("done_command: initializing\n"); + } + pi->progs = xrealloc(pi->progs, sizeof(*pi->progs) * (pi->num_progs+1)); + + prog = pi->progs + pi->num_progs; +#ifndef __U_BOOT__ + prog->redirects = NULL; +#endif + prog->argv = NULL; + prog->argv_nonnull = NULL; +#ifndef __U_BOOT__ + prog->is_stopped = 0; +#endif + prog->group = NULL; +#ifndef __U_BOOT__ + prog->glob_result.gl_pathv = NULL; + prog->family = pi; +#endif + prog->sp = 0; + ctx->child = prog; + prog->type = ctx->type; + + /* but ctx->pipe and ctx->list_head remain unchanged */ + return 0; +} + +static int done_pipe(struct p_context *ctx, pipe_style type) +{ + struct pipe *new_p; + done_command(ctx); /* implicit closure of previous command */ + debug_printf("done_pipe, type %d\n", type); + ctx->pipe->followup = type; + ctx->pipe->r_mode = ctx->w; + new_p=new_pipe(); + ctx->pipe->next = new_p; + ctx->pipe = new_p; + ctx->child = NULL; + done_command(ctx); /* set up new pipe to accept commands */ + return 0; +} + +#ifndef __U_BOOT__ +/* peek ahead in the in_str to find out if we have a "&n" construct, + * as in "2>&1", that represents duplicating a file descriptor. + * returns either -2 (syntax error), -1 (no &), or the number found. + */ +static int redirect_dup_num(struct in_str *input) +{ + int ch, d=0, ok=0; + ch = b_peek(input); + if (ch != '&') return -1; + + b_getch(input); /* get the & */ + ch=b_peek(input); + if (ch == '-') { + b_getch(input); + return -3; /* "-" represents "close me" */ + } + while (isdigit(ch)) { + d = d*10+(ch-'0'); + ok=1; + b_getch(input); + ch = b_peek(input); + } + if (ok) return d; + + error_msg("ambiguous redirect"); + return -2; +} + +/* If a redirect is immediately preceded by a number, that number is + * supposed to tell which file descriptor to redirect. This routine + * looks for such preceding numbers. In an ideal world this routine + * needs to handle all the following classes of redirects... + * echo 2>foo # redirects fd 2 to file "foo", nothing passed to echo + * echo 49>foo # redirects fd 49 to file "foo", nothing passed to echo + * echo -2>foo # redirects fd 1 to file "foo", "-2" passed to echo + * echo 49x>foo # redirects fd 1 to file "foo", "49x" passed to echo + * A -1 output from this program means no valid number was found, so the + * caller should use the appropriate default for this redirection. + */ +static int redirect_opt_num(o_string *o) +{ + int num; + + if (o->length==0) return -1; + for(num=0; num<o->length; num++) { + if (!isdigit(*(o->data+num))) { + return -1; + } + } + /* reuse num (and save an int) */ + num=atoi(o->data); + b_reset(o); + return num; +} + +FILE *generate_stream_from_list(struct pipe *head) +{ + FILE *pf; +#if 1 + int pid, channel[2]; + if (pipe(channel)<0) perror_msg_and_die("pipe"); + pid=fork(); + if (pid<0) { + perror_msg_and_die("fork"); + } else if (pid==0) { + close(channel[0]); + if (channel[1] != 1) { + dup2(channel[1],1); + close(channel[1]); + } +#if 0 +#define SURROGATE "surrogate response" + write(1,SURROGATE,sizeof(SURROGATE)); + _exit(run_list(head)); +#else + _exit(run_list_real(head)); /* leaks memory */ +#endif + } + debug_printf("forked child %d\n",pid); + close(channel[1]); + pf = fdopen(channel[0],"r"); + debug_printf("pipe on FILE *%p\n",pf); +#else + free_pipe_list(head,0); + pf=popen("echo surrogate response","r"); + debug_printf("started fake pipe on FILE *%p\n",pf); +#endif + return pf; +} + +/* this version hacked for testing purposes */ +/* return code is exit status of the process that is run. */ +static int process_command_subs(o_string *dest, struct p_context *ctx, struct in_str *input, int subst_end) +{ + int retcode; + o_string result=NULL_O_STRING; + struct p_context inner; + FILE *p; + struct in_str pipe_str; + initialize_context(&inner); + + /* recursion to generate command */ + retcode = parse_stream(&result, &inner, input, subst_end); + if (retcode != 0) return retcode; /* syntax error or EOF */ + done_word(&result, &inner); + done_pipe(&inner, PIPE_SEQ); + b_free(&result); + + p=generate_stream_from_list(inner.list_head); + if (p==NULL) return 1; + mark_open(fileno(p)); + setup_file_in_str(&pipe_str, p); + + /* now send results of command back into original context */ + retcode = parse_stream(dest, ctx, &pipe_str, '\0'); + /* XXX In case of a syntax error, should we try to kill the child? + * That would be tough to do right, so just read until EOF. */ + if (retcode == 1) { + while (b_getch(&pipe_str)!=EOF) { /* discard */ }; + } + + debug_printf("done reading from pipe, pclose()ing\n"); + /* This is the step that wait()s for the child. Should be pretty + * safe, since we just read an EOF from its stdout. We could try + * to better, by using wait(), and keeping track of background jobs + * at the same time. That would be a lot of work, and contrary + * to the KISS philosophy of this program. */ + mark_closed(fileno(p)); + retcode=pclose(p); + free_pipe_list(inner.list_head,0); + debug_printf("pclosed, retcode=%d\n",retcode); + /* XXX this process fails to trim a single trailing newline */ + return retcode; +} + +static int parse_group(o_string *dest, struct p_context *ctx, + struct in_str *input, int ch) +{ + int rcode, endch=0; + struct p_context sub; + struct child_prog *child = ctx->child; + if (child->argv) { + syntax(); + return 1; /* syntax error, groups and arglists don't mix */ + } + initialize_context(&sub); + switch(ch) { + case '(': endch=')'; child->subshell=1; break; + case '{': endch='}'; break; + default: syntax(); /* really logic error */ + } + rcode=parse_stream(dest,&sub,input,endch); + done_word(dest,&sub); /* finish off the final word in the subcontext */ + done_pipe(&sub, PIPE_SEQ); /* and the final command there, too */ + child->group = sub.list_head; + return rcode; + /* child remains "open", available for possible redirects */ +} +#endif + +/* basically useful version until someone wants to get fancier, + * see the bash man page under "Parameter Expansion" */ +static char *lookup_param(char *src) +{ + char *p; + char *sep; + char *default_val = NULL; + int assign = 0; + int expand_empty = 0; + + if (!src) + return NULL; + + sep = strchr(src, ':'); + + if (sep) { + *sep = '\0'; + if (*(sep + 1) == '-') + default_val = sep+2; + if (*(sep + 1) == '=') { + default_val = sep+2; + assign = 1; + } + if (*(sep + 1) == '+') { + default_val = sep+2; + expand_empty = 1; + } + } + + p = getenv(src); + if (!p) + p = get_local_var(src); + + if (!p || strlen(p) == 0) { + p = default_val; + if (assign) { + char *var = malloc(strlen(src)+strlen(default_val)+2); + if (var) { + sprintf(var, "%s=%s", src, default_val); + set_local_var(var, 0); + } + free(var); + } + } else if (expand_empty) { + p += strlen(p); + } + + if (sep) + *sep = ':'; + + return p; +} + +#ifdef __U_BOOT__ +static char *get_dollar_var(char ch) +{ + static char buf[40]; + + buf[0] = '\0'; + switch (ch) { + case '?': + sprintf(buf, "%u", (unsigned int)last_return_code); + break; + default: + return NULL; + } + return buf; +} +#endif + +/* return code: 0 for OK, 1 for syntax error */ +static int handle_dollar(o_string *dest, struct p_context *ctx, struct in_str *input) +{ +#ifndef __U_BOOT__ + int i, advance=0; +#else + int advance=0; +#endif +#ifndef __U_BOOT__ + char sep[]=" "; +#endif + int ch = input->peek(input); /* first character after the $ */ + debug_printf("handle_dollar: ch=%c\n",ch); + if (isalpha(ch)) { + b_addchr(dest, SPECIAL_VAR_SYMBOL); + ctx->child->sp++; + while(ch=b_peek(input),isalnum(ch) || ch=='_') { + b_getch(input); + b_addchr(dest,ch); + } + b_addchr(dest, SPECIAL_VAR_SYMBOL); +#ifndef __U_BOOT__ + } else if (isdigit(ch)) { + i = ch-'0'; /* XXX is $0 special? */ + if (i<global_argc) { + parse_string(dest, ctx, global_argv[i]); /* recursion */ + } + advance = 1; +#endif + } else switch (ch) { +#ifndef __U_BOOT__ + case '$': + b_adduint(dest,getpid()); + advance = 1; + break; + case '!': + if (last_bg_pid > 0) b_adduint(dest, last_bg_pid); + advance = 1; + break; +#endif + case '?': +#ifndef __U_BOOT__ + b_adduint(dest,last_return_code); +#else + ctx->child->sp++; + b_addchr(dest, SPECIAL_VAR_SYMBOL); + b_addchr(dest, '$'); + b_addchr(dest, '?'); + b_addchr(dest, SPECIAL_VAR_SYMBOL); +#endif + advance = 1; + break; +#ifndef __U_BOOT__ + case '#': + b_adduint(dest,global_argc ? global_argc-1 : 0); + advance = 1; + break; +#endif + case '{': + b_addchr(dest, SPECIAL_VAR_SYMBOL); + ctx->child->sp++; + b_getch(input); + /* XXX maybe someone will try to escape the '}' */ + while(ch=b_getch(input),ch!=EOF && ch!='}') { + b_addchr(dest,ch); + } + if (ch != '}') { + syntax(); + return 1; + } + b_addchr(dest, SPECIAL_VAR_SYMBOL); + break; +#ifndef __U_BOOT__ + case '(': + b_getch(input); + process_command_subs(dest, ctx, input, ')'); + break; + case '*': + sep[0]=ifs[0]; + for (i=1; i<global_argc; i++) { + parse_string(dest, ctx, global_argv[i]); + if (i+1 < global_argc) parse_string(dest, ctx, sep); + } + break; + case '@': + case '-': + case '_': + /* still unhandled, but should be eventually */ + error_msg("unhandled syntax: $%c",ch); + return 1; + break; +#endif + default: + b_addqchr(dest,'$',dest->quote); + } + /* Eat the character if the flag was set. If the compiler + * is smart enough, we could substitute "b_getch(input);" + * for all the "advance = 1;" above, and also end up with + * a nice size-optimized program. Hah! That'll be the day. + */ + if (advance) b_getch(input); + return 0; +} + +#ifndef __U_BOOT__ +int parse_string(o_string *dest, struct p_context *ctx, const char *src) +{ + struct in_str foo; + setup_string_in_str(&foo, src); + return parse_stream(dest, ctx, &foo, '\0'); +} +#endif + +/* return code is 0 for normal exit, 1 for syntax error */ +static int parse_stream(o_string *dest, struct p_context *ctx, + struct in_str *input, int end_trigger) +{ + unsigned int ch, m; +#ifndef __U_BOOT__ + int redir_fd; + redir_type redir_style; +#endif + int next; + + /* Only double-quote state is handled in the state variable dest->quote. + * A single-quote triggers a bypass of the main loop until its mate is + * found. When recursing, quote state is passed in via dest->quote. */ + + debug_printf("parse_stream, end_trigger=%d\n",end_trigger); + while ((ch=b_getch(input))!=EOF) { + m = map[ch]; +#ifdef __U_BOOT__ + if (input->__promptme == 0) return 1; +#endif + next = (ch == '\n') ? 0 : b_peek(input); + + debug_printf("parse_stream: ch=%c (%d) m=%d quote=%d - %c\n", + ch >= ' ' ? ch : '.', ch, m, + dest->quote, ctx->stack == NULL ? '*' : '.'); + + if (m==0 || ((m==1 || m==2) && dest->quote)) { + b_addqchr(dest, ch, dest->quote); + } else { + if (m==2) { /* unquoted IFS */ + if (done_word(dest, ctx)) { + return 1; + } + /* If we aren't performing a substitution, treat a newline as a + * command separator. */ + if (end_trigger != '\0' && ch=='\n') + done_pipe(ctx,PIPE_SEQ); + } + if (ch == end_trigger && !dest->quote && ctx->w==RES_NONE) { + debug_printf("leaving parse_stream (triggered)\n"); + return 0; + } +#if 0 + if (ch=='\n') { + /* Yahoo! Time to run with it! */ + done_pipe(ctx,PIPE_SEQ); + run_list(ctx->list_head); + initialize_context(ctx); + } +#endif + if (m!=2) switch (ch) { + case '#': + if (dest->length == 0 && !dest->quote) { + while(ch=b_peek(input),ch!=EOF && ch!='\n') { b_getch(input); } + } else { + b_addqchr(dest, ch, dest->quote); + } + break; + case '\\': + if (next == EOF) { + syntax(); + return 1; + } + b_addqchr(dest, '\\', dest->quote); + b_addqchr(dest, b_getch(input), dest->quote); + break; + case '$': + if (handle_dollar(dest, ctx, input)!=0) return 1; + break; + case '\'': + dest->nonnull = 1; + while(ch=b_getch(input),ch!=EOF && ch!='\'') { +#ifdef __U_BOOT__ + if(input->__promptme == 0) return 1; +#endif + b_addchr(dest,ch); + } + if (ch==EOF) { + syntax(); + return 1; + } + break; + case '"': + dest->nonnull = 1; + dest->quote = !dest->quote; + break; +#ifndef __U_BOOT__ + case '`': + process_command_subs(dest, ctx, input, '`'); + break; + case '>': + redir_fd = redirect_opt_num(dest); + done_word(dest, ctx); + redir_style=REDIRECT_OVERWRITE; + if (next == '>') { + redir_style=REDIRECT_APPEND; + b_getch(input); + } else if (next == '(') { + syntax(); /* until we support >(list) Process Substitution */ + return 1; + } + setup_redirect(ctx, redir_fd, redir_style, input); + break; + case '<': + redir_fd = redirect_opt_num(dest); + done_word(dest, ctx); + redir_style=REDIRECT_INPUT; + if (next == '<') { + redir_style=REDIRECT_HEREIS; + b_getch(input); + } else if (next == '>') { + redir_style=REDIRECT_IO; + b_getch(input); + } else if (next == '(') { + syntax(); /* until we support <(list) Process Substitution */ + return 1; + } + setup_redirect(ctx, redir_fd, redir_style, input); + break; +#endif + case ';': + done_word(dest, ctx); + done_pipe(ctx,PIPE_SEQ); + break; + case '&': + done_word(dest, ctx); + if (next=='&') { + b_getch(input); + done_pipe(ctx,PIPE_AND); + } else { +#ifndef __U_BOOT__ + done_pipe(ctx,PIPE_BG); +#else + syntax_err(); + return 1; +#endif + } + break; + case '|': + done_word(dest, ctx); + if (next=='|') { + b_getch(input); + done_pipe(ctx,PIPE_OR); + } else { + /* we could pick up a file descriptor choice here + * with redirect_opt_num(), but bash doesn't do it. + * "echo foo 2| cat" yields "foo 2". */ +#ifndef __U_BOOT__ + done_command(ctx); +#else + syntax_err(); + return 1; +#endif + } + break; +#ifndef __U_BOOT__ + case '(': + case '{': + if (parse_group(dest, ctx, input, ch)!=0) return 1; + break; + case ')': + case '}': + syntax(); /* Proper use of this character caught by end_trigger */ + return 1; + break; +#endif + case SUBSTED_VAR_SYMBOL: + dest->nonnull = 1; + while (ch = b_getch(input), ch != EOF && + ch != SUBSTED_VAR_SYMBOL) { + debug_printf("subst, pass=%d\n", ch); + if (input->__promptme == 0) + return 1; + b_addchr(dest, ch); + } + debug_printf("subst, term=%d\n", ch); + if (ch == EOF) { + syntax(); + return 1; + } + break; + default: + syntax(); /* this is really an internal logic error */ + return 1; + } + } + } + /* complain if quote? No, maybe we just finished a command substitution + * that was quoted. Example: + * $ echo "`cat foo` plus more" + * and we just got the EOF generated by the subshell that ran "cat foo" + * The only real complaint is if we got an EOF when end_trigger != '\0', + * that is, we were really supposed to get end_trigger, and never got + * one before the EOF. Can't use the standard "syntax error" return code, + * so that parse_stream_outer can distinguish the EOF and exit smoothly. */ + debug_printf("leaving parse_stream (EOF)\n"); + if (end_trigger != '\0') return -1; + return 0; +} + +static void mapset(const unsigned char *set, int code) +{ + const unsigned char *s; + for (s=set; *s; s++) map[*s] = code; +} + +static void update_ifs_map(void) +{ + /* char *ifs and char map[256] are both globals. */ + ifs = (uchar *)getenv("IFS"); + if (ifs == NULL) ifs=(uchar *)" \t\n"; + /* Precompute a list of 'flow through' behavior so it can be treated + * quickly up front. Computation is necessary because of IFS. + * Special case handling of IFS == " \t\n" is not implemented. + * The map[] array only really needs two bits each, and on most machines + * that would be faster because of the reduced L1 cache footprint. + */ + memset(map,0,sizeof(map)); /* most characters flow through always */ +#ifndef __U_BOOT__ + mapset((uchar *)"\\$'\"`", 3); /* never flow through */ + mapset((uchar *)"<>;&|(){}#", 1); /* flow through if quoted */ +#else + { + uchar subst[2] = {SUBSTED_VAR_SYMBOL, 0}; + mapset(subst, 3); /* never flow through */ + } + mapset((uchar *)"\\$'\"", 3); /* never flow through */ + mapset((uchar *)";&|#", 1); /* flow through if quoted */ +#endif + mapset(ifs, 2); /* also flow through if quoted */ +} + +/* most recursion does not come through here, the exeception is + * from builtin_source() */ +static int parse_stream_outer(struct in_str *inp, int flag) +{ + + struct p_context ctx; + o_string temp=NULL_O_STRING; + int rcode; +#ifdef __U_BOOT__ + int code = 0; +#endif + do { + ctx.type = flag; + initialize_context(&ctx); + update_ifs_map(); + if (!(flag & FLAG_PARSE_SEMICOLON) || (flag & FLAG_REPARSING)) mapset((uchar *)";$&|", 0); + inp->promptmode=1; + rcode = parse_stream(&temp, &ctx, inp, '\n'); +#ifdef __U_BOOT__ + if (rcode == 1) flag_repeat = 0; +#endif + if (rcode != 1 && ctx.old_flag != 0) { + syntax(); +#ifdef __U_BOOT__ + flag_repeat = 0; +#endif + } + if (rcode != 1 && ctx.old_flag == 0) { + done_word(&temp, &ctx); + done_pipe(&ctx,PIPE_SEQ); +#ifndef __U_BOOT__ + run_list(ctx.list_head); +#else + code = run_list(ctx.list_head); + if (code == -2) { /* exit */ + b_free(&temp); + code = 0; + /* XXX hackish way to not allow exit from main loop */ + if (inp->peek == file_peek) { + printf("exit not allowed from main input shell.\n"); + continue; + } + break; + } + if (code == -1) + flag_repeat = 0; +#endif + } else { + if (ctx.old_flag != 0) { + free(ctx.stack); + b_reset(&temp); + } +#ifdef __U_BOOT__ + if (inp->__promptme == 0) printf("<INTERRUPT>\n"); + inp->__promptme = 1; +#endif + temp.nonnull = 0; + temp.quote = 0; + inp->p = NULL; + free_pipe_list(ctx.list_head,0); + } + b_free(&temp); + } while (rcode != -1 && !(flag & FLAG_EXIT_FROM_LOOP)); /* loop on syntax errors, return on EOF */ +#ifndef __U_BOOT__ + return 0; +#else + return (code != 0) ? 1 : 0; +#endif /* __U_BOOT__ */ +} + +#ifndef __U_BOOT__ +static int parse_string_outer(const char *s, int flag) +#else +int parse_string_outer(const char *s, int flag) +#endif /* __U_BOOT__ */ +{ + struct in_str input; +#ifdef __U_BOOT__ + char *p = NULL; + int rcode; + if ( !s || !*s) + return 1; + if (!(p = strchr(s, '\n')) || *++p) { + p = xmalloc(strlen(s) + 2); + strcpy(p, s); + strcat(p, "\n"); + setup_string_in_str(&input, p); + rcode = parse_stream_outer(&input, flag); + free(p); + return rcode; + } else { +#endif + setup_string_in_str(&input, s); + return parse_stream_outer(&input, flag); +#ifdef __U_BOOT__ + } +#endif +} + +#ifndef __U_BOOT__ +static int parse_file_outer(FILE *f) +#else +int parse_file_outer(void) +#endif +{ + int rcode; + struct in_str input; +#ifndef __U_BOOT__ + setup_file_in_str(&input, f); +#else + setup_file_in_str(&input); +#endif + rcode = parse_stream_outer(&input, FLAG_PARSE_SEMICOLON); + return rcode; +} + +#ifdef __U_BOOT__ +#ifdef CONFIG_NEEDS_MANUAL_RELOC +static void u_boot_hush_reloc(void) +{ + unsigned long addr; + struct reserved_combo *r; + + for (r=reserved_list; r<reserved_list+NRES; r++) { + addr = (ulong) (r->literal) + gd->reloc_off; + r->literal = (char *)addr; + } +} +#endif + +int u_boot_hush_start(void) +{ + if (top_vars == NULL) { + top_vars = malloc(sizeof(struct variables)); + top_vars->name = "HUSH_VERSION"; + top_vars->value = "0.01"; + top_vars->next = NULL; + top_vars->flg_export = 0; + top_vars->flg_read_only = 1; +#ifdef CONFIG_NEEDS_MANUAL_RELOC + u_boot_hush_reloc(); +#endif + } + return 0; +} + +static void *xmalloc(size_t size) +{ + void *p = NULL; + + if (!(p = malloc(size))) { + printf("ERROR : memory not allocated\n"); + for(;;); + } + return p; +} + +static void *xrealloc(void *ptr, size_t size) +{ + void *p = NULL; + + if (!(p = realloc(ptr, size))) { + printf("ERROR : memory not allocated\n"); + for(;;); + } + return p; +} +#endif /* __U_BOOT__ */ + +#ifndef __U_BOOT__ +/* Make sure we have a controlling tty. If we get started under a job + * aware app (like bash for example), make sure we are now in charge so + * we don't fight over who gets the foreground */ +static void setup_job_control(void) +{ + static pid_t shell_pgrp; + /* Loop until we are in the foreground. */ + while (tcgetpgrp (shell_terminal) != (shell_pgrp = getpgrp ())) + kill (- shell_pgrp, SIGTTIN); + + /* Ignore interactive and job-control signals. */ + signal(SIGINT, SIG_IGN); + signal(SIGQUIT, SIG_IGN); + signal(SIGTERM, SIG_IGN); + signal(SIGTSTP, SIG_IGN); + signal(SIGTTIN, SIG_IGN); + signal(SIGTTOU, SIG_IGN); + signal(SIGCHLD, SIG_IGN); + + /* Put ourselves in our own process group. */ + setsid(); + shell_pgrp = getpid (); + setpgid (shell_pgrp, shell_pgrp); + + /* Grab control of the terminal. */ + tcsetpgrp(shell_terminal, shell_pgrp); +} + +int hush_main(int argc, char * const *argv) +{ + int opt; + FILE *input; + char **e = environ; + + /* XXX what should these be while sourcing /etc/profile? */ + global_argc = argc; + global_argv = argv; + + /* (re?) initialize globals. Sometimes hush_main() ends up calling + * hush_main(), therefore we cannot rely on the BSS to zero out this + * stuff. Reset these to 0 every time. */ + ifs = NULL; + /* map[] is taken care of with call to update_ifs_map() */ + fake_mode = 0; + interactive = 0; + close_me_head = NULL; + last_bg_pid = 0; + job_list = NULL; + last_jobid = 0; + + /* Initialize some more globals to non-zero values */ + set_cwd(); +#ifdef CONFIG_FEATURE_COMMAND_EDITING + cmdedit_set_initial_prompt(); +#else + PS1 = NULL; +#endif + PS2 = "> "; + + /* initialize our shell local variables with the values + * currently living in the environment */ + if (e) { + for (; *e; e++) + set_local_var(*e, 2); /* without call putenv() */ + } + + last_return_code=EXIT_SUCCESS; + + + if (argv[0] && argv[0][0] == '-') { + debug_printf("\nsourcing /etc/profile\n"); + if ((input = fopen("/etc/profile", "r")) != NULL) { + mark_open(fileno(input)); + parse_file_outer(input); + mark_closed(fileno(input)); + fclose(input); + } + } + input=stdin; + + while ((opt = getopt(argc, argv, "c:xif")) > 0) { + switch (opt) { + case 'c': + { + global_argv = argv+optind; + global_argc = argc-optind; + opt = parse_string_outer(optarg, FLAG_PARSE_SEMICOLON); + goto final_return; + } + break; + case 'i': + interactive++; + break; + case 'f': + fake_mode++; + break; + default: +#ifndef BB_VER + fprintf(stderr, "Usage: sh [FILE]...\n" + " or: sh -c command [args]...\n\n"); + exit(EXIT_FAILURE); +#else + show_usage(); +#endif + } + } + /* A shell is interactive if the `-i' flag was given, or if all of + * the following conditions are met: + * no -c command + * no arguments remaining or the -s flag given + * standard input is a terminal + * standard output is a terminal + * Refer to Posix.2, the description of the `sh' utility. */ + if (argv[optind]==NULL && input==stdin && + isatty(fileno(stdin)) && isatty(fileno(stdout))) { + interactive++; + } + + debug_printf("\ninteractive=%d\n", interactive); + if (interactive) { + /* Looks like they want an interactive shell */ +#ifndef CONFIG_FEATURE_SH_EXTRA_QUIET + printf( "\n\n" BB_BANNER " hush - the humble shell v0.01 (testing)\n"); + printf( "Enter 'help' for a list of built-in commands.\n\n"); +#endif + setup_job_control(); + } + + if (argv[optind]==NULL) { + opt=parse_file_outer(stdin); + goto final_return; + } + + debug_printf("\nrunning script '%s'\n", argv[optind]); + global_argv = argv+optind; + global_argc = argc-optind; + input = xfopen(argv[optind], "r"); + opt = parse_file_outer(input); + +#ifdef CONFIG_FEATURE_CLEAN_UP + fclose(input); + if (cwd && cwd != unknown) + free((char*)cwd); + { + struct variables *cur, *tmp; + for(cur = top_vars; cur; cur = tmp) { + tmp = cur->next; + if (!cur->flg_read_only) { + free(cur->name); + free(cur->value); + free(cur); + } + } + } +#endif + +final_return: + return(opt?opt:last_return_code); +} +#endif + +static char *insert_var_value(char *inp) +{ + return insert_var_value_sub(inp, 0); +} + +static char *insert_var_value_sub(char *inp, int tag_subst) +{ + int res_str_len = 0; + int len; + int done = 0; + char *p, *p1, *res_str = NULL; + + while ((p = strchr(inp, SPECIAL_VAR_SYMBOL))) { + /* check the beginning of the string for normal charachters */ + if (p != inp) { + /* copy any charachters to the result string */ + len = p - inp; + res_str = xrealloc(res_str, (res_str_len + len)); + strncpy((res_str + res_str_len), inp, len); + res_str_len += len; + } + inp = ++p; + /* find the ending marker */ + p = strchr(inp, SPECIAL_VAR_SYMBOL); + *p = '\0'; + /* look up the value to substitute */ + if ((p1 = lookup_param(inp))) { + if (tag_subst) + len = res_str_len + strlen(p1) + 2; + else + len = res_str_len + strlen(p1); + res_str = xrealloc(res_str, (1 + len)); + if (tag_subst) { + /* + * copy the variable value to the result + * string + */ + strcpy((res_str + res_str_len + 1), p1); + + /* + * mark the replaced text to be accepted as + * is + */ + res_str[res_str_len] = SUBSTED_VAR_SYMBOL; + res_str[res_str_len + 1 + strlen(p1)] = + SUBSTED_VAR_SYMBOL; + } else + /* + * copy the variable value to the result + * string + */ + strcpy((res_str + res_str_len), p1); + + res_str_len = len; + } + *p = SPECIAL_VAR_SYMBOL; + inp = ++p; + done = 1; + } + if (done) { + res_str = xrealloc(res_str, (1 + res_str_len + strlen(inp))); + strcpy((res_str + res_str_len), inp); + while ((p = strchr(res_str, '\n'))) { + *p = ' '; + } + } + return (res_str == NULL) ? inp : res_str; +} + +static char **make_list_in(char **inp, char *name) +{ + int len, i; + int name_len = strlen(name); + int n = 0; + char **list; + char *p1, *p2, *p3; + + /* create list of variable values */ + list = xmalloc(sizeof(*list)); + for (i = 0; inp[i]; i++) { + p3 = insert_var_value(inp[i]); + p1 = p3; + while (*p1) { + if ((*p1 == ' ')) { + p1++; + continue; + } + if ((p2 = strchr(p1, ' '))) { + len = p2 - p1; + } else { + len = strlen(p1); + p2 = p1 + len; + } + /* we use n + 2 in realloc for list,because we add + * new element and then we will add NULL element */ + list = xrealloc(list, sizeof(*list) * (n + 2)); + list[n] = xmalloc(2 + name_len + len); + strcpy(list[n], name); + strcat(list[n], "="); + strncat(list[n], p1, len); + list[n++][name_len + len + 1] = '\0'; + p1 = p2; + } + if (p3 != inp[i]) free(p3); + } + list[n] = NULL; + return list; +} + +/* + * Make new string for parser + * inp - array of argument strings to flatten + * nonnull - indicates argument was quoted when originally parsed + */ +static char *make_string(char **inp, int *nonnull) +{ + char *p; + char *str = NULL; + int n; + int len = 2; + char *noeval_str; + int noeval = 0; + + noeval_str = get_local_var("HUSH_NO_EVAL"); + if (noeval_str != NULL && *noeval_str != '0' && *noeval_str != '\0') + noeval = 1; + for (n = 0; inp[n]; n++) { + p = insert_var_value_sub(inp[n], noeval); + str = xrealloc(str, (len + strlen(p) + (2 * nonnull[n]))); + if (n) { + strcat(str, " "); + } else { + *str = '\0'; + } + if (nonnull[n]) + strcat(str, "'"); + strcat(str, p); + if (nonnull[n]) + strcat(str, "'"); + len = strlen(str) + 3; + if (p != inp[n]) free(p); + } + len = strlen(str); + *(str + len) = '\n'; + *(str + len + 1) = '\0'; + return str; +} + +#ifdef __U_BOOT__ +static int do_showvar(cmd_tbl_t *cmdtp, int flag, int argc, + char * const argv[]) +{ + int i, k; + int rcode = 0; + struct variables *cur; + + if (argc == 1) { /* Print all env variables */ + for (cur = top_vars; cur; cur = cur->next) { + printf ("%s=%s\n", cur->name, cur->value); + if (ctrlc ()) { + puts ("\n ** Abort\n"); + return 1; + } + } + return 0; + } + for (i = 1; i < argc; ++i) { /* print single env variables */ + char *name = argv[i]; + + k = -1; + for (cur = top_vars; cur; cur = cur->next) { + if(strcmp (cur->name, name) == 0) { + k = 0; + printf ("%s=%s\n", cur->name, cur->value); + } + if (ctrlc ()) { + puts ("\n ** Abort\n"); + return 1; + } + } + if (k < 0) { + printf ("## Error: \"%s\" not defined\n", name); + rcode ++; + } + } + return rcode; +} + +U_BOOT_CMD( + showvar, CONFIG_SYS_MAXARGS, 1, do_showvar, + "print local hushshell variables", + "\n - print values of all hushshell variables\n" + "showvar name ...\n" + " - print value of hushshell variable 'name'" +); + +#endif +/****************************************************************************/ diff --git a/qemu/roms/u-boot/common/hwconfig.c b/qemu/roms/u-boot/common/hwconfig.c new file mode 100644 index 000000000..4ae042ee3 --- /dev/null +++ b/qemu/roms/u-boot/common/hwconfig.c @@ -0,0 +1,285 @@ +/* + * An inteface for configuring a hardware via u-boot environment. + * + * Copyright (c) 2009 MontaVista Software, Inc. + * Copyright 2011 Freescale Semiconductor, Inc. + * + * Author: Anton Vorontsov <avorontsov@ru.mvista.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef HWCONFIG_TEST +#include <config.h> +#include <common.h> +#include <exports.h> +#include <hwconfig.h> +#include <linux/types.h> +#include <linux/string.h> +#else +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <assert.h> +#define min(a, b) (((a) < (b)) ? (a) : (b)) +#endif /* HWCONFIG_TEST */ + +DECLARE_GLOBAL_DATA_PTR; + +static const char *hwconfig_parse(const char *opts, size_t maxlen, + const char *opt, char *stopchs, char eqch, + size_t *arglen) +{ + size_t optlen = strlen(opt); + char *str; + const char *start = opts; + const char *end; + +next: + str = strstr(opts, opt); + end = str + optlen; + if (end - start > maxlen) + return NULL; + + if (str && (str == opts || strpbrk(str - 1, stopchs) == str - 1) && + (strpbrk(end, stopchs) == end || *end == eqch || + *end == '\0')) { + const char *arg_end; + + if (!arglen) + return str; + + if (*end != eqch) + return NULL; + + arg_end = strpbrk(str, stopchs); + if (!arg_end) + *arglen = min(maxlen, strlen(str)) - optlen - 1; + else + *arglen = arg_end - end - 1; + + return end + 1; + } else if (str) { + opts = end; + goto next; + } + return NULL; +} + +const char cpu_hwconfig[] __attribute__((weak)) = ""; +const char board_hwconfig[] __attribute__((weak)) = ""; + +static const char *__hwconfig(const char *opt, size_t *arglen, + const char *env_hwconfig) +{ + const char *ret; + + /* if we are passed a buffer use it, otherwise try the environment */ + if (!env_hwconfig) { + if (!(gd->flags & GD_FLG_ENV_READY)) { + printf("WARNING: Calling __hwconfig without a buffer " + "and before environment is ready\n"); + return NULL; + } + env_hwconfig = getenv("hwconfig"); + } + + if (env_hwconfig) { + ret = hwconfig_parse(env_hwconfig, strlen(env_hwconfig), + opt, ";", ':', arglen); + if (ret) + return ret; + } + + ret = hwconfig_parse(board_hwconfig, strlen(board_hwconfig), + opt, ";", ':', arglen); + if (ret) + return ret; + + return hwconfig_parse(cpu_hwconfig, strlen(cpu_hwconfig), + opt, ";", ':', arglen); +} + +/* + * hwconfig_f - query if a particular hwconfig option is specified + * @opt: a string representing an option + * @buf: if non-NULL use this buffer to parse, otherwise try env + * + * This call can be used to find out whether U-Boot should configure + * a particular hardware option. + * + * Returns non-zero value if the hardware option can be used and thus + * should be configured, 0 otherwise. + * + * This function also returns non-zero value if CONFIG_HWCONFIG is + * undefined. + * + * Returning non-zero value without CONFIG_HWCONFIG has its crucial + * purpose: the hwconfig() call should be a "transparent" interface, + * e.g. if a board doesn't need hwconfig facility, then we assume + * that the board file only calls things that are actually used, so + * hwconfig() will always return true result. + */ +int hwconfig_f(const char *opt, char *buf) +{ + return !!__hwconfig(opt, NULL, buf); +} + +/* + * hwconfig_arg_f - get hwconfig option's argument + * @opt: a string representing an option + * @arglen: a pointer to an allocated size_t variable + * @buf: if non-NULL use this buffer to parse, otherwise try env + * + * Unlike hwconfig_f() function, this function returns a pointer to the + * start of the hwconfig arguments, if option is not found or it has + * no specified arguments, the function returns NULL pointer. + * + * If CONFIG_HWCONFIG is undefined, the function returns "", and + * arglen is set to 0. + */ +const char *hwconfig_arg_f(const char *opt, size_t *arglen, char *buf) +{ + return __hwconfig(opt, arglen, buf); +} + +/* + * hwconfig_arg_cmp_f - compare hwconfig option's argument + * @opt: a string representing an option + * @arg: a string for comparing an option's argument + * @buf: if non-NULL use this buffer to parse, otherwise try env + * + * This call is similar to hwconfig_arg_f, but instead of returning + * hwconfig argument and its length, it is comparing it to @arg. + * + * Returns non-zero value if @arg matches, 0 otherwise. + * + * If CONFIG_HWCONFIG is undefined, the function returns a non-zero + * value, i.e. the argument matches. + */ +int hwconfig_arg_cmp_f(const char *opt, const char *arg, char *buf) +{ + const char *argstr; + size_t arglen; + + argstr = hwconfig_arg_f(opt, &arglen, buf); + if (!argstr || arglen != strlen(arg)) + return 0; + + return !strncmp(argstr, arg, arglen); +} + +/* + * hwconfig_sub_f - query if a particular hwconfig sub-option is specified + * @opt: a string representing an option + * @subopt: a string representing a sub-option + * @buf: if non-NULL use this buffer to parse, otherwise try env + * + * This call is similar to hwconfig_f(), except that it takes additional + * argument @subopt. In this example: + * "dr_usb:mode=peripheral" + * "dr_usb" is an option, "mode" is a sub-option, and "peripheral" is its + * argument. + */ +int hwconfig_sub_f(const char *opt, const char *subopt, char *buf) +{ + size_t arglen; + const char *arg; + + arg = __hwconfig(opt, &arglen, buf); + if (!arg) + return 0; + return !!hwconfig_parse(arg, arglen, subopt, ",;", '=', NULL); +} + +/* + * hwconfig_subarg_f - get hwconfig sub-option's argument + * @opt: a string representing an option + * @subopt: a string representing a sub-option + * @subarglen: a pointer to an allocated size_t variable + * @buf: if non-NULL use this buffer to parse, otherwise try env + * + * This call is similar to hwconfig_arg_f(), except that it takes an + * additional argument @subopt, and so works with sub-options. + */ +const char *hwconfig_subarg_f(const char *opt, const char *subopt, + size_t *subarglen, char *buf) +{ + size_t arglen; + const char *arg; + + arg = __hwconfig(opt, &arglen, buf); + if (!arg) + return NULL; + return hwconfig_parse(arg, arglen, subopt, ",;", '=', subarglen); +} + +/* + * hwconfig_arg_cmp_f - compare hwconfig sub-option's argument + * @opt: a string representing an option + * @subopt: a string representing a sub-option + * @subarg: a string for comparing an sub-option's argument + * @buf: if non-NULL use this buffer to parse, otherwise try env + * + * This call is similar to hwconfig_arg_cmp_f, except that it takes an + * additional argument @subopt, and so works with sub-options. + */ +int hwconfig_subarg_cmp_f(const char *opt, const char *subopt, + const char *subarg, char *buf) +{ + const char *argstr; + size_t arglen; + + argstr = hwconfig_subarg_f(opt, subopt, &arglen, buf); + if (!argstr || arglen != strlen(subarg)) + return 0; + + return !strncmp(argstr, subarg, arglen); +} + +#ifdef HWCONFIG_TEST +int main() +{ + const char *ret; + size_t len; + + setenv("hwconfig", "key1:subkey1=value1,subkey2=value2;key2:value3;;;;" + "key3;:,:=;key4", 1); + + ret = hwconfig_arg("key1", &len); + printf("%zd %.*s\n", len, (int)len, ret); + assert(len == 29); + assert(hwconfig_arg_cmp("key1", "subkey1=value1,subkey2=value2")); + assert(!strncmp(ret, "subkey1=value1,subkey2=value2", len)); + + ret = hwconfig_subarg("key1", "subkey1", &len); + printf("%zd %.*s\n", len, (int)len, ret); + assert(len == 6); + assert(hwconfig_subarg_cmp("key1", "subkey1", "value1")); + assert(!strncmp(ret, "value1", len)); + + ret = hwconfig_subarg("key1", "subkey2", &len); + printf("%zd %.*s\n", len, (int)len, ret); + assert(len == 6); + assert(hwconfig_subarg_cmp("key1", "subkey2", "value2")); + assert(!strncmp(ret, "value2", len)); + + ret = hwconfig_arg("key2", &len); + printf("%zd %.*s\n", len, (int)len, ret); + assert(len == 6); + assert(hwconfig_arg_cmp("key2", "value3")); + assert(!strncmp(ret, "value3", len)); + + assert(hwconfig("key3")); + assert(hwconfig_arg("key4", &len) == NULL); + assert(hwconfig_arg("bogus", &len) == NULL); + + unsetenv("hwconfig"); + + assert(hwconfig(NULL) == 0); + assert(hwconfig("") == 0); + assert(hwconfig("key3") == 0); + + return 0; +} +#endif /* HWCONFIG_TEST */ diff --git a/qemu/roms/u-boot/common/image-fdt.c b/qemu/roms/u-boot/common/image-fdt.c new file mode 100644 index 000000000..5d64009df --- /dev/null +++ b/qemu/roms/u-boot/common/image-fdt.c @@ -0,0 +1,496 @@ +/* + * Copyright (c) 2013, Google Inc. + * + * (C) Copyright 2008 Semihalf + * + * (C) Copyright 2000-2006 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <fdt_support.h> +#include <errno.h> +#include <image.h> +#include <libfdt.h> +#include <asm/io.h> + +#ifndef CONFIG_SYS_FDT_PAD +#define CONFIG_SYS_FDT_PAD 0x3000 +#endif + +DECLARE_GLOBAL_DATA_PTR; + +static void fdt_error(const char *msg) +{ + puts("ERROR: "); + puts(msg); + puts(" - must RESET the board to recover.\n"); +} + +static const image_header_t *image_get_fdt(ulong fdt_addr) +{ + const image_header_t *fdt_hdr = map_sysmem(fdt_addr, 0); + + image_print_contents(fdt_hdr); + + puts(" Verifying Checksum ... "); + if (!image_check_hcrc(fdt_hdr)) { + fdt_error("fdt header checksum invalid"); + return NULL; + } + + if (!image_check_dcrc(fdt_hdr)) { + fdt_error("fdt checksum invalid"); + return NULL; + } + puts("OK\n"); + + if (!image_check_type(fdt_hdr, IH_TYPE_FLATDT)) { + fdt_error("uImage is not a fdt"); + return NULL; + } + if (image_get_comp(fdt_hdr) != IH_COMP_NONE) { + fdt_error("uImage is compressed"); + return NULL; + } + if (fdt_check_header((void *)image_get_data(fdt_hdr)) != 0) { + fdt_error("uImage data is not a fdt"); + return NULL; + } + return fdt_hdr; +} + +/** + * boot_fdt_add_mem_rsv_regions - Mark the memreserve sections as unusable + * @lmb: pointer to lmb handle, will be used for memory mgmt + * @fdt_blob: pointer to fdt blob base address + * + * Adds the memreserve regions in the dtb to the lmb block. Adding the + * memreserve regions prevents u-boot from using them to store the initrd + * or the fdt blob. + */ +void boot_fdt_add_mem_rsv_regions(struct lmb *lmb, void *fdt_blob) +{ + uint64_t addr, size; + int i, total; + + if (fdt_check_header(fdt_blob) != 0) + return; + + total = fdt_num_mem_rsv(fdt_blob); + for (i = 0; i < total; i++) { + if (fdt_get_mem_rsv(fdt_blob, i, &addr, &size) != 0) + continue; + printf(" reserving fdt memory region: addr=%llx size=%llx\n", + (unsigned long long)addr, (unsigned long long)size); + lmb_reserve(lmb, addr, size); + } +} + +/** + * boot_relocate_fdt - relocate flat device tree + * @lmb: pointer to lmb handle, will be used for memory mgmt + * @of_flat_tree: pointer to a char* variable, will hold fdt start address + * @of_size: pointer to a ulong variable, will hold fdt length + * + * boot_relocate_fdt() allocates a region of memory within the bootmap and + * relocates the of_flat_tree into that region, even if the fdt is already in + * the bootmap. It also expands the size of the fdt by CONFIG_SYS_FDT_PAD + * bytes. + * + * of_flat_tree and of_size are set to final (after relocation) values + * + * returns: + * 0 - success + * 1 - failure + */ +int boot_relocate_fdt(struct lmb *lmb, char **of_flat_tree, ulong *of_size) +{ + void *fdt_blob = *of_flat_tree; + void *of_start = NULL; + char *fdt_high; + ulong of_len = 0; + int err; + int disable_relocation = 0; + + /* nothing to do */ + if (*of_size == 0) + return 0; + + if (fdt_check_header(fdt_blob) != 0) { + fdt_error("image is not a fdt"); + goto error; + } + + /* position on a 4K boundary before the alloc_current */ + /* Pad the FDT by a specified amount */ + of_len = *of_size + CONFIG_SYS_FDT_PAD; + + /* If fdt_high is set use it to select the relocation address */ + fdt_high = getenv("fdt_high"); + if (fdt_high) { + void *desired_addr = (void *)simple_strtoul(fdt_high, NULL, 16); + + if (((ulong) desired_addr) == ~0UL) { + /* All ones means use fdt in place */ + of_start = fdt_blob; + lmb_reserve(lmb, (ulong)of_start, of_len); + disable_relocation = 1; + } else if (desired_addr) { + of_start = + (void *)(ulong) lmb_alloc_base(lmb, of_len, 0x1000, + (ulong)desired_addr); + if (of_start == NULL) { + puts("Failed using fdt_high value for Device Tree"); + goto error; + } + } else { + of_start = + (void *)(ulong) lmb_alloc(lmb, of_len, 0x1000); + } + } else { + of_start = + (void *)(ulong) lmb_alloc_base(lmb, of_len, 0x1000, + getenv_bootm_mapsize() + + getenv_bootm_low()); + } + + if (of_start == NULL) { + puts("device tree - allocation error\n"); + goto error; + } + + if (disable_relocation) { + /* + * We assume there is space after the existing fdt to use + * for padding + */ + fdt_set_totalsize(of_start, of_len); + printf(" Using Device Tree in place at %p, end %p\n", + of_start, of_start + of_len - 1); + } else { + debug("## device tree at %p ... %p (len=%ld [0x%lX])\n", + fdt_blob, fdt_blob + *of_size - 1, of_len, of_len); + + printf(" Loading Device Tree to %p, end %p ... ", + of_start, of_start + of_len - 1); + + err = fdt_open_into(fdt_blob, of_start, of_len); + if (err != 0) { + fdt_error("fdt move failed"); + goto error; + } + puts("OK\n"); + } + + *of_flat_tree = of_start; + *of_size = of_len; + + set_working_fdt_addr(*of_flat_tree); + return 0; + +error: + return 1; +} + +/** + * boot_get_fdt - main fdt handling routine + * @argc: command argument count + * @argv: command argument list + * @arch: architecture (IH_ARCH_...) + * @images: pointer to the bootm images structure + * @of_flat_tree: pointer to a char* variable, will hold fdt start address + * @of_size: pointer to a ulong variable, will hold fdt length + * + * boot_get_fdt() is responsible for finding a valid flat device tree image. + * Curently supported are the following ramdisk sources: + * - multicomponent kernel/ramdisk image, + * - commandline provided address of decicated ramdisk image. + * + * returns: + * 0, if fdt image was found and valid, or skipped + * of_flat_tree and of_size are set to fdt start address and length if + * fdt image is found and valid + * + * 1, if fdt image is found but corrupted + * of_flat_tree and of_size are set to 0 if no fdt exists + */ +int boot_get_fdt(int flag, int argc, char * const argv[], uint8_t arch, + bootm_headers_t *images, char **of_flat_tree, ulong *of_size) +{ + const image_header_t *fdt_hdr; + ulong fdt_addr; + char *fdt_blob = NULL; + ulong image_start, image_data, image_end; + ulong load, load_end; + void *buf; +#if defined(CONFIG_FIT) + const char *fit_uname_config = images->fit_uname_cfg; + const char *fit_uname_fdt = NULL; + ulong default_addr; + int fdt_noffset; +#endif + const char *select = NULL; + + *of_flat_tree = NULL; + *of_size = 0; + + if (argc > 2) + select = argv[2]; + if (select || genimg_has_config(images)) { +#if defined(CONFIG_FIT) + if (select) { + /* + * If the FDT blob comes from the FIT image and the + * FIT image address is omitted in the command line + * argument, try to use ramdisk or os FIT image + * address or default load address. + */ + if (images->fit_uname_rd) + default_addr = (ulong)images->fit_hdr_rd; + else if (images->fit_uname_os) + default_addr = (ulong)images->fit_hdr_os; + else + default_addr = load_addr; + + if (fit_parse_conf(select, default_addr, + &fdt_addr, &fit_uname_config)) { + debug("* fdt: config '%s' from image at 0x%08lx\n", + fit_uname_config, fdt_addr); + } else if (fit_parse_subimage(select, default_addr, + &fdt_addr, &fit_uname_fdt)) { + debug("* fdt: subimage '%s' from image at 0x%08lx\n", + fit_uname_fdt, fdt_addr); + } else +#endif + { + fdt_addr = simple_strtoul(select, NULL, 16); + debug("* fdt: cmdline image address = 0x%08lx\n", + fdt_addr); + } +#if defined(CONFIG_FIT) + } else { + /* use FIT configuration provided in first bootm + * command argument + */ + fdt_addr = map_to_sysmem(images->fit_hdr_os); + fdt_noffset = fit_get_node_from_config(images, + FIT_FDT_PROP, + fdt_addr); + if (fdt_noffset == -ENOLINK) + return 0; + else if (fdt_noffset < 0) + return 1; + } +#endif + debug("## Checking for 'FDT'/'FDT Image' at %08lx\n", + fdt_addr); + + /* copy from dataflash if needed */ + fdt_addr = genimg_get_image(fdt_addr); + + /* + * Check if there is an FDT image at the + * address provided in the second bootm argument + * check image type, for FIT images get a FIT node. + */ + buf = map_sysmem(fdt_addr, 0); + switch (genimg_get_format(buf)) { + case IMAGE_FORMAT_LEGACY: + /* verify fdt_addr points to a valid image header */ + printf("## Flattened Device Tree from Legacy Image at %08lx\n", + fdt_addr); + fdt_hdr = image_get_fdt(fdt_addr); + if (!fdt_hdr) + goto error; + + /* + * move image data to the load address, + * make sure we don't overwrite initial image + */ + image_start = (ulong)fdt_hdr; + image_data = (ulong)image_get_data(fdt_hdr); + image_end = image_get_image_end(fdt_hdr); + + load = image_get_load(fdt_hdr); + load_end = load + image_get_data_size(fdt_hdr); + + if (load == image_start || + load == image_data) { + fdt_blob = (char *)image_data; + break; + } + + if ((load < image_end) && (load_end > image_start)) { + fdt_error("fdt overwritten"); + goto error; + } + + debug(" Loading FDT from 0x%08lx to 0x%08lx\n", + image_data, load); + + memmove((void *)load, + (void *)image_data, + image_get_data_size(fdt_hdr)); + + fdt_addr = load; + break; + case IMAGE_FORMAT_FIT: + /* + * This case will catch both: new uImage format + * (libfdt based) and raw FDT blob (also libfdt + * based). + */ +#if defined(CONFIG_FIT) + /* check FDT blob vs FIT blob */ + if (fit_check_format(buf)) { + ulong load, len; + + fdt_noffset = fit_image_load(images, + FIT_FDT_PROP, + fdt_addr, &fit_uname_fdt, + &fit_uname_config, + arch, IH_TYPE_FLATDT, + BOOTSTAGE_ID_FIT_FDT_START, + FIT_LOAD_OPTIONAL, &load, &len); + + images->fit_hdr_fdt = map_sysmem(fdt_addr, 0); + images->fit_uname_fdt = fit_uname_fdt; + images->fit_noffset_fdt = fdt_noffset; + fdt_addr = load; + break; + } else +#endif + { + /* + * FDT blob + */ + debug("* fdt: raw FDT blob\n"); + printf("## Flattened Device Tree blob at %08lx\n", + (long)fdt_addr); + } + break; + default: + puts("ERROR: Did not find a cmdline Flattened Device Tree\n"); + goto error; + } + + printf(" Booting using the fdt blob at %#08lx\n", fdt_addr); + fdt_blob = map_sysmem(fdt_addr, 0); + } else if (images->legacy_hdr_valid && + image_check_type(&images->legacy_hdr_os_copy, + IH_TYPE_MULTI)) { + ulong fdt_data, fdt_len; + + /* + * Now check if we have a legacy multi-component image, + * get second entry data start address and len. + */ + printf("## Flattened Device Tree from multi component Image at %08lX\n", + (ulong)images->legacy_hdr_os); + + image_multi_getimg(images->legacy_hdr_os, 2, &fdt_data, + &fdt_len); + if (fdt_len) { + fdt_blob = (char *)fdt_data; + printf(" Booting using the fdt at 0x%p\n", fdt_blob); + + if (fdt_check_header(fdt_blob) != 0) { + fdt_error("image is not a fdt"); + goto error; + } + + if (fdt_totalsize(fdt_blob) != fdt_len) { + fdt_error("fdt size != image size"); + goto error; + } + } else { + debug("## No Flattened Device Tree\n"); + return 0; + } + } else { + debug("## No Flattened Device Tree\n"); + return 0; + } + + *of_flat_tree = fdt_blob; + *of_size = fdt_totalsize(fdt_blob); + debug(" of_flat_tree at 0x%08lx size 0x%08lx\n", + (ulong)*of_flat_tree, *of_size); + + return 0; + +error: + *of_flat_tree = NULL; + *of_size = 0; + return 1; +} + +/* + * Verify the device tree. + * + * This function is called after all device tree fix-ups have been enacted, + * so that the final device tree can be verified. The definition of "verified" + * is up to the specific implementation. However, it generally means that the + * addresses of some of the devices in the device tree are compared with the + * actual addresses at which U-Boot has placed them. + * + * Returns 1 on success, 0 on failure. If 0 is returned, U-boot will halt the + * boot process. + */ +__weak int ft_verify_fdt(void *fdt) +{ + return 1; +} + +__weak int arch_fixup_memory_node(void *blob) +{ + return 0; +} + +int image_setup_libfdt(bootm_headers_t *images, void *blob, + int of_size, struct lmb *lmb) +{ + ulong *initrd_start = &images->initrd_start; + ulong *initrd_end = &images->initrd_end; + int ret; + + if (fdt_chosen(blob, 1) < 0) { + puts("ERROR: /chosen node create failed"); + puts(" - must RESET the board to recover.\n"); + return -1; + } + arch_fixup_memory_node(blob); + if (IMAGE_OF_BOARD_SETUP) + ft_board_setup(blob, gd->bd); + fdt_fixup_ethernet(blob); + + /* Delete the old LMB reservation */ + lmb_free(lmb, (phys_addr_t)(u32)(uintptr_t)blob, + (phys_size_t)fdt_totalsize(blob)); + + ret = fdt_resize(blob); + if (ret < 0) + return ret; + of_size = ret; + + if (*initrd_start && *initrd_end) { + of_size += FDT_RAMDISK_OVERHEAD; + fdt_set_totalsize(blob, of_size); + } + /* Create a new LMB reservation */ + lmb_reserve(lmb, (ulong)blob, of_size); + + fdt_initrd(blob, *initrd_start, *initrd_end, 1); + if (!ft_verify_fdt(blob)) + return -1; + +#ifdef CONFIG_SOC_K2HK + if (IMAGE_OF_BOARD_SETUP) + ft_board_setup_ex(blob, gd->bd); +#endif + + return 0; +} diff --git a/qemu/roms/u-boot/common/image-fit.c b/qemu/roms/u-boot/common/image-fit.c new file mode 100644 index 000000000..77f32bce3 --- /dev/null +++ b/qemu/roms/u-boot/common/image-fit.c @@ -0,0 +1,1667 @@ +/* + * Copyright (c) 2013, Google Inc. + * + * (C) Copyright 2008 Semihalf + * + * (C) Copyright 2000-2006 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifdef USE_HOSTCC +#include "mkimage.h" +#include <image.h> +#include <time.h> +#else +#include <common.h> +#include <errno.h> +#include <asm/io.h> +DECLARE_GLOBAL_DATA_PTR; +#endif /* !USE_HOSTCC*/ + +#include <bootstage.h> +#include <sha1.h> +#include <sha256.h> +#include <u-boot/crc.h> +#include <u-boot/md5.h> + +/*****************************************************************************/ +/* New uImage format routines */ +/*****************************************************************************/ +#ifndef USE_HOSTCC +static int fit_parse_spec(const char *spec, char sepc, ulong addr_curr, + ulong *addr, const char **name) +{ + const char *sep; + + *addr = addr_curr; + *name = NULL; + + sep = strchr(spec, sepc); + if (sep) { + if (sep - spec > 0) + *addr = simple_strtoul(spec, NULL, 16); + + *name = sep + 1; + return 1; + } + + return 0; +} + +/** + * fit_parse_conf - parse FIT configuration spec + * @spec: input string, containing configuration spec + * @add_curr: current image address (to be used as a possible default) + * @addr: pointer to a ulong variable, will hold FIT image address of a given + * configuration + * @conf_name double pointer to a char, will hold pointer to a configuration + * unit name + * + * fit_parse_conf() expects configuration spec in the form of [<addr>]#<conf>, + * where <addr> is a FIT image address that contains configuration + * with a <conf> unit name. + * + * Address part is optional, and if omitted default add_curr will + * be used instead. + * + * returns: + * 1 if spec is a valid configuration string, + * addr and conf_name are set accordingly + * 0 otherwise + */ +int fit_parse_conf(const char *spec, ulong addr_curr, + ulong *addr, const char **conf_name) +{ + return fit_parse_spec(spec, '#', addr_curr, addr, conf_name); +} + +/** + * fit_parse_subimage - parse FIT subimage spec + * @spec: input string, containing subimage spec + * @add_curr: current image address (to be used as a possible default) + * @addr: pointer to a ulong variable, will hold FIT image address of a given + * subimage + * @image_name: double pointer to a char, will hold pointer to a subimage name + * + * fit_parse_subimage() expects subimage spec in the form of + * [<addr>]:<subimage>, where <addr> is a FIT image address that contains + * subimage with a <subimg> unit name. + * + * Address part is optional, and if omitted default add_curr will + * be used instead. + * + * returns: + * 1 if spec is a valid subimage string, + * addr and image_name are set accordingly + * 0 otherwise + */ +int fit_parse_subimage(const char *spec, ulong addr_curr, + ulong *addr, const char **image_name) +{ + return fit_parse_spec(spec, ':', addr_curr, addr, image_name); +} +#endif /* !USE_HOSTCC */ + +static void fit_get_debug(const void *fit, int noffset, + char *prop_name, int err) +{ + debug("Can't get '%s' property from FIT 0x%08lx, node: offset %d, name %s (%s)\n", + prop_name, (ulong)fit, noffset, fit_get_name(fit, noffset, NULL), + fdt_strerror(err)); +} + +#if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_FIT_SPL_PRINT) +/** + * fit_print_contents - prints out the contents of the FIT format image + * @fit: pointer to the FIT format image header + * @p: pointer to prefix string + * + * fit_print_contents() formats a multi line FIT image contents description. + * The routine prints out FIT image properties (root node level) follwed by + * the details of each component image. + * + * returns: + * no returned results + */ +void fit_print_contents(const void *fit) +{ + char *desc; + char *uname; + int images_noffset; + int confs_noffset; + int noffset; + int ndepth; + int count = 0; + int ret; + const char *p; + time_t timestamp; + + /* Indent string is defined in header image.h */ + p = IMAGE_INDENT_STRING; + + /* Root node properties */ + ret = fit_get_desc(fit, 0, &desc); + printf("%sFIT description: ", p); + if (ret) + printf("unavailable\n"); + else + printf("%s\n", desc); + + if (IMAGE_ENABLE_TIMESTAMP) { + ret = fit_get_timestamp(fit, 0, ×tamp); + printf("%sCreated: ", p); + if (ret) + printf("unavailable\n"); + else + genimg_print_time(timestamp); + } + + /* Find images parent node offset */ + images_noffset = fdt_path_offset(fit, FIT_IMAGES_PATH); + if (images_noffset < 0) { + printf("Can't find images parent node '%s' (%s)\n", + FIT_IMAGES_PATH, fdt_strerror(images_noffset)); + return; + } + + /* Process its subnodes, print out component images details */ + for (ndepth = 0, count = 0, + noffset = fdt_next_node(fit, images_noffset, &ndepth); + (noffset >= 0) && (ndepth > 0); + noffset = fdt_next_node(fit, noffset, &ndepth)) { + if (ndepth == 1) { + /* + * Direct child node of the images parent node, + * i.e. component image node. + */ + printf("%s Image %u (%s)\n", p, count++, + fit_get_name(fit, noffset, NULL)); + + fit_image_print(fit, noffset, p); + } + } + + /* Find configurations parent node offset */ + confs_noffset = fdt_path_offset(fit, FIT_CONFS_PATH); + if (confs_noffset < 0) { + debug("Can't get configurations parent node '%s' (%s)\n", + FIT_CONFS_PATH, fdt_strerror(confs_noffset)); + return; + } + + /* get default configuration unit name from default property */ + uname = (char *)fdt_getprop(fit, noffset, FIT_DEFAULT_PROP, NULL); + if (uname) + printf("%s Default Configuration: '%s'\n", p, uname); + + /* Process its subnodes, print out configurations details */ + for (ndepth = 0, count = 0, + noffset = fdt_next_node(fit, confs_noffset, &ndepth); + (noffset >= 0) && (ndepth > 0); + noffset = fdt_next_node(fit, noffset, &ndepth)) { + if (ndepth == 1) { + /* + * Direct child node of the configurations parent node, + * i.e. configuration node. + */ + printf("%s Configuration %u (%s)\n", p, count++, + fit_get_name(fit, noffset, NULL)); + + fit_conf_print(fit, noffset, p); + } + } +} + +/** + * fit_image_print_data() - prints out the hash node details + * @fit: pointer to the FIT format image header + * @noffset: offset of the hash node + * @p: pointer to prefix string + * @type: Type of information to print ("hash" or "sign") + * + * fit_image_print_data() lists properies for the processed hash node + * + * This function avoid using puts() since it prints a newline on the host + * but does not in U-Boot. + * + * returns: + * no returned results + */ +static void fit_image_print_data(const void *fit, int noffset, const char *p, + const char *type) +{ + const char *keyname; + uint8_t *value; + int value_len; + char *algo; + int required; + int ret, i; + + debug("%s %s node: '%s'\n", p, type, + fit_get_name(fit, noffset, NULL)); + printf("%s %s algo: ", p, type); + if (fit_image_hash_get_algo(fit, noffset, &algo)) { + printf("invalid/unsupported\n"); + return; + } + printf("%s", algo); + keyname = fdt_getprop(fit, noffset, "key-name-hint", NULL); + required = fdt_getprop(fit, noffset, "required", NULL) != NULL; + if (keyname) + printf(":%s", keyname); + if (required) + printf(" (required)"); + printf("\n"); + + ret = fit_image_hash_get_value(fit, noffset, &value, + &value_len); + printf("%s %s value: ", p, type); + if (ret) { + printf("unavailable\n"); + } else { + for (i = 0; i < value_len; i++) + printf("%02x", value[i]); + printf("\n"); + } + + debug("%s %s len: %d\n", p, type, value_len); + + /* Signatures have a time stamp */ + if (IMAGE_ENABLE_TIMESTAMP && keyname) { + time_t timestamp; + + printf("%s Timestamp: ", p); + if (fit_get_timestamp(fit, noffset, ×tamp)) + printf("unavailable\n"); + else + genimg_print_time(timestamp); + } +} + +/** + * fit_image_print_verification_data() - prints out the hash/signature details + * @fit: pointer to the FIT format image header + * @noffset: offset of the hash or signature node + * @p: pointer to prefix string + * + * This lists properies for the processed hash node + * + * returns: + * no returned results + */ +static void fit_image_print_verification_data(const void *fit, int noffset, + const char *p) +{ + const char *name; + + /* + * Check subnode name, must be equal to "hash" or "signature". + * Multiple hash/signature nodes require unique unit node + * names, e.g. hash@1, hash@2, signature@1, signature@2, etc. + */ + name = fit_get_name(fit, noffset, NULL); + if (!strncmp(name, FIT_HASH_NODENAME, strlen(FIT_HASH_NODENAME))) { + fit_image_print_data(fit, noffset, p, "Hash"); + } else if (!strncmp(name, FIT_SIG_NODENAME, + strlen(FIT_SIG_NODENAME))) { + fit_image_print_data(fit, noffset, p, "Sign"); + } +} + +/** + * fit_image_print - prints out the FIT component image details + * @fit: pointer to the FIT format image header + * @image_noffset: offset of the component image node + * @p: pointer to prefix string + * + * fit_image_print() lists all mandatory properies for the processed component + * image. If present, hash nodes are printed out as well. Load + * address for images of type firmware is also printed out. Since the load + * address is not mandatory for firmware images, it will be output as + * "unavailable" when not present. + * + * returns: + * no returned results + */ +void fit_image_print(const void *fit, int image_noffset, const char *p) +{ + char *desc; + uint8_t type, arch, os, comp; + size_t size; + ulong load, entry; + const void *data; + int noffset; + int ndepth; + int ret; + + /* Mandatory properties */ + ret = fit_get_desc(fit, image_noffset, &desc); + printf("%s Description: ", p); + if (ret) + printf("unavailable\n"); + else + printf("%s\n", desc); + + if (IMAGE_ENABLE_TIMESTAMP) { + time_t timestamp; + + ret = fit_get_timestamp(fit, 0, ×tamp); + printf("%s Created: ", p); + if (ret) + printf("unavailable\n"); + else + genimg_print_time(timestamp); + } + + fit_image_get_type(fit, image_noffset, &type); + printf("%s Type: %s\n", p, genimg_get_type_name(type)); + + fit_image_get_comp(fit, image_noffset, &comp); + printf("%s Compression: %s\n", p, genimg_get_comp_name(comp)); + + ret = fit_image_get_data(fit, image_noffset, &data, &size); + +#ifndef USE_HOSTCC + printf("%s Data Start: ", p); + if (ret) { + printf("unavailable\n"); + } else { + void *vdata = (void *)data; + + printf("0x%08lx\n", (ulong)map_to_sysmem(vdata)); + } +#endif + + printf("%s Data Size: ", p); + if (ret) + printf("unavailable\n"); + else + genimg_print_size(size); + + /* Remaining, type dependent properties */ + if ((type == IH_TYPE_KERNEL) || (type == IH_TYPE_STANDALONE) || + (type == IH_TYPE_RAMDISK) || (type == IH_TYPE_FIRMWARE) || + (type == IH_TYPE_FLATDT)) { + fit_image_get_arch(fit, image_noffset, &arch); + printf("%s Architecture: %s\n", p, genimg_get_arch_name(arch)); + } + + if ((type == IH_TYPE_KERNEL) || (type == IH_TYPE_RAMDISK)) { + fit_image_get_os(fit, image_noffset, &os); + printf("%s OS: %s\n", p, genimg_get_os_name(os)); + } + + if ((type == IH_TYPE_KERNEL) || (type == IH_TYPE_STANDALONE) || + (type == IH_TYPE_FIRMWARE) || (type == IH_TYPE_RAMDISK)) { + ret = fit_image_get_load(fit, image_noffset, &load); + printf("%s Load Address: ", p); + if (ret) + printf("unavailable\n"); + else + printf("0x%08lx\n", load); + } + + if ((type == IH_TYPE_KERNEL) || (type == IH_TYPE_STANDALONE) || + (type == IH_TYPE_RAMDISK)) { + fit_image_get_entry(fit, image_noffset, &entry); + printf("%s Entry Point: ", p); + if (ret) + printf("unavailable\n"); + else + printf("0x%08lx\n", entry); + } + + /* Process all hash subnodes of the component image node */ + for (ndepth = 0, noffset = fdt_next_node(fit, image_noffset, &ndepth); + (noffset >= 0) && (ndepth > 0); + noffset = fdt_next_node(fit, noffset, &ndepth)) { + if (ndepth == 1) { + /* Direct child node of the component image node */ + fit_image_print_verification_data(fit, noffset, p); + } + } +} +#endif + +/** + * fit_get_desc - get node description property + * @fit: pointer to the FIT format image header + * @noffset: node offset + * @desc: double pointer to the char, will hold pointer to the descrption + * + * fit_get_desc() reads description property from a given node, if + * description is found pointer to it is returened in third call argument. + * + * returns: + * 0, on success + * -1, on failure + */ +int fit_get_desc(const void *fit, int noffset, char **desc) +{ + int len; + + *desc = (char *)fdt_getprop(fit, noffset, FIT_DESC_PROP, &len); + if (*desc == NULL) { + fit_get_debug(fit, noffset, FIT_DESC_PROP, len); + return -1; + } + + return 0; +} + +/** + * fit_get_timestamp - get node timestamp property + * @fit: pointer to the FIT format image header + * @noffset: node offset + * @timestamp: pointer to the time_t, will hold read timestamp + * + * fit_get_timestamp() reads timestamp poperty from given node, if timestamp + * is found and has a correct size its value is retured in third call + * argument. + * + * returns: + * 0, on success + * -1, on property read failure + * -2, on wrong timestamp size + */ +int fit_get_timestamp(const void *fit, int noffset, time_t *timestamp) +{ + int len; + const void *data; + + data = fdt_getprop(fit, noffset, FIT_TIMESTAMP_PROP, &len); + if (data == NULL) { + fit_get_debug(fit, noffset, FIT_TIMESTAMP_PROP, len); + return -1; + } + if (len != sizeof(uint32_t)) { + debug("FIT timestamp with incorrect size of (%u)\n", len); + return -2; + } + + *timestamp = uimage_to_cpu(*((uint32_t *)data)); + return 0; +} + +/** + * fit_image_get_node - get node offset for component image of a given unit name + * @fit: pointer to the FIT format image header + * @image_uname: component image node unit name + * + * fit_image_get_node() finds a component image (withing the '/images' + * node) of a provided unit name. If image is found its node offset is + * returned to the caller. + * + * returns: + * image node offset when found (>=0) + * negative number on failure (FDT_ERR_* code) + */ +int fit_image_get_node(const void *fit, const char *image_uname) +{ + int noffset, images_noffset; + + images_noffset = fdt_path_offset(fit, FIT_IMAGES_PATH); + if (images_noffset < 0) { + debug("Can't find images parent node '%s' (%s)\n", + FIT_IMAGES_PATH, fdt_strerror(images_noffset)); + return images_noffset; + } + + noffset = fdt_subnode_offset(fit, images_noffset, image_uname); + if (noffset < 0) { + debug("Can't get node offset for image unit name: '%s' (%s)\n", + image_uname, fdt_strerror(noffset)); + } + + return noffset; +} + +/** + * fit_image_get_os - get os id for a given component image node + * @fit: pointer to the FIT format image header + * @noffset: component image node offset + * @os: pointer to the uint8_t, will hold os numeric id + * + * fit_image_get_os() finds os property in a given component image node. + * If the property is found, its (string) value is translated to the numeric + * id which is returned to the caller. + * + * returns: + * 0, on success + * -1, on failure + */ +int fit_image_get_os(const void *fit, int noffset, uint8_t *os) +{ + int len; + const void *data; + + /* Get OS name from property data */ + data = fdt_getprop(fit, noffset, FIT_OS_PROP, &len); + if (data == NULL) { + fit_get_debug(fit, noffset, FIT_OS_PROP, len); + *os = -1; + return -1; + } + + /* Translate OS name to id */ + *os = genimg_get_os_id(data); + return 0; +} + +/** + * fit_image_get_arch - get arch id for a given component image node + * @fit: pointer to the FIT format image header + * @noffset: component image node offset + * @arch: pointer to the uint8_t, will hold arch numeric id + * + * fit_image_get_arch() finds arch property in a given component image node. + * If the property is found, its (string) value is translated to the numeric + * id which is returned to the caller. + * + * returns: + * 0, on success + * -1, on failure + */ +int fit_image_get_arch(const void *fit, int noffset, uint8_t *arch) +{ + int len; + const void *data; + + /* Get architecture name from property data */ + data = fdt_getprop(fit, noffset, FIT_ARCH_PROP, &len); + if (data == NULL) { + fit_get_debug(fit, noffset, FIT_ARCH_PROP, len); + *arch = -1; + return -1; + } + + /* Translate architecture name to id */ + *arch = genimg_get_arch_id(data); + return 0; +} + +/** + * fit_image_get_type - get type id for a given component image node + * @fit: pointer to the FIT format image header + * @noffset: component image node offset + * @type: pointer to the uint8_t, will hold type numeric id + * + * fit_image_get_type() finds type property in a given component image node. + * If the property is found, its (string) value is translated to the numeric + * id which is returned to the caller. + * + * returns: + * 0, on success + * -1, on failure + */ +int fit_image_get_type(const void *fit, int noffset, uint8_t *type) +{ + int len; + const void *data; + + /* Get image type name from property data */ + data = fdt_getprop(fit, noffset, FIT_TYPE_PROP, &len); + if (data == NULL) { + fit_get_debug(fit, noffset, FIT_TYPE_PROP, len); + *type = -1; + return -1; + } + + /* Translate image type name to id */ + *type = genimg_get_type_id(data); + return 0; +} + +/** + * fit_image_get_comp - get comp id for a given component image node + * @fit: pointer to the FIT format image header + * @noffset: component image node offset + * @comp: pointer to the uint8_t, will hold comp numeric id + * + * fit_image_get_comp() finds comp property in a given component image node. + * If the property is found, its (string) value is translated to the numeric + * id which is returned to the caller. + * + * returns: + * 0, on success + * -1, on failure + */ +int fit_image_get_comp(const void *fit, int noffset, uint8_t *comp) +{ + int len; + const void *data; + + /* Get compression name from property data */ + data = fdt_getprop(fit, noffset, FIT_COMP_PROP, &len); + if (data == NULL) { + fit_get_debug(fit, noffset, FIT_COMP_PROP, len); + *comp = -1; + return -1; + } + + /* Translate compression name to id */ + *comp = genimg_get_comp_id(data); + return 0; +} + +/** + * fit_image_get_load() - get load addr property for given component image node + * @fit: pointer to the FIT format image header + * @noffset: component image node offset + * @load: pointer to the uint32_t, will hold load address + * + * fit_image_get_load() finds load address property in a given component + * image node. If the property is found, its value is returned to the caller. + * + * returns: + * 0, on success + * -1, on failure + */ +int fit_image_get_load(const void *fit, int noffset, ulong *load) +{ + int len; + const uint32_t *data; + + data = fdt_getprop(fit, noffset, FIT_LOAD_PROP, &len); + if (data == NULL) { + fit_get_debug(fit, noffset, FIT_LOAD_PROP, len); + return -1; + } + + *load = uimage_to_cpu(*data); + return 0; +} + +/** + * fit_image_get_entry() - get entry point address property + * @fit: pointer to the FIT format image header + * @noffset: component image node offset + * @entry: pointer to the uint32_t, will hold entry point address + * + * This gets the entry point address property for a given component image + * node. + * + * fit_image_get_entry() finds entry point address property in a given + * component image node. If the property is found, its value is returned + * to the caller. + * + * returns: + * 0, on success + * -1, on failure + */ +int fit_image_get_entry(const void *fit, int noffset, ulong *entry) +{ + int len; + const uint32_t *data; + + data = fdt_getprop(fit, noffset, FIT_ENTRY_PROP, &len); + if (data == NULL) { + fit_get_debug(fit, noffset, FIT_ENTRY_PROP, len); + return -1; + } + + *entry = uimage_to_cpu(*data); + return 0; +} + +/** + * fit_image_get_data - get data property and its size for a given component image node + * @fit: pointer to the FIT format image header + * @noffset: component image node offset + * @data: double pointer to void, will hold data property's data address + * @size: pointer to size_t, will hold data property's data size + * + * fit_image_get_data() finds data property in a given component image node. + * If the property is found its data start address and size are returned to + * the caller. + * + * returns: + * 0, on success + * -1, on failure + */ +int fit_image_get_data(const void *fit, int noffset, + const void **data, size_t *size) +{ + int len; + + *data = fdt_getprop(fit, noffset, FIT_DATA_PROP, &len); + if (*data == NULL) { + fit_get_debug(fit, noffset, FIT_DATA_PROP, len); + *size = 0; + return -1; + } + + *size = len; + return 0; +} + +/** + * fit_image_hash_get_algo - get hash algorithm name + * @fit: pointer to the FIT format image header + * @noffset: hash node offset + * @algo: double pointer to char, will hold pointer to the algorithm name + * + * fit_image_hash_get_algo() finds hash algorithm property in a given hash node. + * If the property is found its data start address is returned to the caller. + * + * returns: + * 0, on success + * -1, on failure + */ +int fit_image_hash_get_algo(const void *fit, int noffset, char **algo) +{ + int len; + + *algo = (char *)fdt_getprop(fit, noffset, FIT_ALGO_PROP, &len); + if (*algo == NULL) { + fit_get_debug(fit, noffset, FIT_ALGO_PROP, len); + return -1; + } + + return 0; +} + +/** + * fit_image_hash_get_value - get hash value and length + * @fit: pointer to the FIT format image header + * @noffset: hash node offset + * @value: double pointer to uint8_t, will hold address of a hash value data + * @value_len: pointer to an int, will hold hash data length + * + * fit_image_hash_get_value() finds hash value property in a given hash node. + * If the property is found its data start address and size are returned to + * the caller. + * + * returns: + * 0, on success + * -1, on failure + */ +int fit_image_hash_get_value(const void *fit, int noffset, uint8_t **value, + int *value_len) +{ + int len; + + *value = (uint8_t *)fdt_getprop(fit, noffset, FIT_VALUE_PROP, &len); + if (*value == NULL) { + fit_get_debug(fit, noffset, FIT_VALUE_PROP, len); + *value_len = 0; + return -1; + } + + *value_len = len; + return 0; +} + +/** + * fit_image_hash_get_ignore - get hash ignore flag + * @fit: pointer to the FIT format image header + * @noffset: hash node offset + * @ignore: pointer to an int, will hold hash ignore flag + * + * fit_image_hash_get_ignore() finds hash ignore property in a given hash node. + * If the property is found and non-zero, the hash algorithm is not verified by + * u-boot automatically. + * + * returns: + * 0, on ignore not found + * value, on ignore found + */ +static int fit_image_hash_get_ignore(const void *fit, int noffset, int *ignore) +{ + int len; + int *value; + + value = (int *)fdt_getprop(fit, noffset, FIT_IGNORE_PROP, &len); + if (value == NULL || len != sizeof(int)) + *ignore = 0; + else + *ignore = *value; + + return 0; +} + +/** + * fit_set_timestamp - set node timestamp property + * @fit: pointer to the FIT format image header + * @noffset: node offset + * @timestamp: timestamp value to be set + * + * fit_set_timestamp() attempts to set timestamp property in the requested + * node and returns operation status to the caller. + * + * returns: + * 0, on success + * -1, on property read failure + */ +int fit_set_timestamp(void *fit, int noffset, time_t timestamp) +{ + uint32_t t; + int ret; + + t = cpu_to_uimage(timestamp); + ret = fdt_setprop(fit, noffset, FIT_TIMESTAMP_PROP, &t, + sizeof(uint32_t)); + if (ret) { + printf("Can't set '%s' property for '%s' node (%s)\n", + FIT_TIMESTAMP_PROP, fit_get_name(fit, noffset, NULL), + fdt_strerror(ret)); + return -1; + } + + return 0; +} + +/** + * calculate_hash - calculate and return hash for provided input data + * @data: pointer to the input data + * @data_len: data length + * @algo: requested hash algorithm + * @value: pointer to the char, will hold hash value data (caller must + * allocate enough free space) + * value_len: length of the calculated hash + * + * calculate_hash() computes input data hash according to the requested + * algorithm. + * Resulting hash value is placed in caller provided 'value' buffer, length + * of the calculated hash is returned via value_len pointer argument. + * + * returns: + * 0, on success + * -1, when algo is unsupported + */ +int calculate_hash(const void *data, int data_len, const char *algo, + uint8_t *value, int *value_len) +{ + if (IMAGE_ENABLE_CRC32 && strcmp(algo, "crc32") == 0) { + *((uint32_t *)value) = crc32_wd(0, data, data_len, + CHUNKSZ_CRC32); + *((uint32_t *)value) = cpu_to_uimage(*((uint32_t *)value)); + *value_len = 4; + } else if (IMAGE_ENABLE_SHA1 && strcmp(algo, "sha1") == 0) { + sha1_csum_wd((unsigned char *)data, data_len, + (unsigned char *)value, CHUNKSZ_SHA1); + *value_len = 20; + } else if (IMAGE_ENABLE_SHA256 && strcmp(algo, "sha256") == 0) { + sha256_csum_wd((unsigned char *)data, data_len, + (unsigned char *)value, CHUNKSZ_SHA256); + *value_len = SHA256_SUM_LEN; + } else if (IMAGE_ENABLE_MD5 && strcmp(algo, "md5") == 0) { + md5_wd((unsigned char *)data, data_len, value, CHUNKSZ_MD5); + *value_len = 16; + } else { + debug("Unsupported hash alogrithm\n"); + return -1; + } + return 0; +} + +static int fit_image_check_hash(const void *fit, int noffset, const void *data, + size_t size, char **err_msgp) +{ + uint8_t value[FIT_MAX_HASH_LEN]; + int value_len; + char *algo; + uint8_t *fit_value; + int fit_value_len; + int ignore; + + *err_msgp = NULL; + + if (fit_image_hash_get_algo(fit, noffset, &algo)) { + *err_msgp = "Can't get hash algo property"; + return -1; + } + printf("%s", algo); + + if (IMAGE_ENABLE_IGNORE) { + fit_image_hash_get_ignore(fit, noffset, &ignore); + if (ignore) { + printf("-skipped "); + return 0; + } + } + + if (fit_image_hash_get_value(fit, noffset, &fit_value, + &fit_value_len)) { + *err_msgp = "Can't get hash value property"; + return -1; + } + + if (calculate_hash(data, size, algo, value, &value_len)) { + *err_msgp = "Unsupported hash algorithm"; + return -1; + } + + if (value_len != fit_value_len) { + *err_msgp = "Bad hash value len"; + return -1; + } else if (memcmp(value, fit_value, value_len) != 0) { + *err_msgp = "Bad hash value"; + return -1; + } + + return 0; +} + +/** + * fit_image_verify - verify data intergity + * @fit: pointer to the FIT format image header + * @image_noffset: component image node offset + * + * fit_image_verify() goes over component image hash nodes, + * re-calculates each data hash and compares with the value stored in hash + * node. + * + * returns: + * 1, if all hashes are valid + * 0, otherwise (or on error) + */ +int fit_image_verify(const void *fit, int image_noffset) +{ + const void *data; + size_t size; + int noffset = 0; + char *err_msg = ""; + int verify_all = 1; + int ret; + + /* Get image data and data length */ + if (fit_image_get_data(fit, image_noffset, &data, &size)) { + err_msg = "Can't get image data/size"; + goto error; + } + + /* Verify all required signatures */ + if (IMAGE_ENABLE_VERIFY && + fit_image_verify_required_sigs(fit, image_noffset, data, size, + gd_fdt_blob(), &verify_all)) { + err_msg = "Unable to verify required signature"; + goto error; + } + + /* Process all hash subnodes of the component image node */ + for (noffset = fdt_first_subnode(fit, image_noffset); + noffset >= 0; + noffset = fdt_next_subnode(fit, noffset)) { + const char *name = fit_get_name(fit, noffset, NULL); + + /* + * Check subnode name, must be equal to "hash". + * Multiple hash nodes require unique unit node + * names, e.g. hash@1, hash@2, etc. + */ + if (!strncmp(name, FIT_HASH_NODENAME, + strlen(FIT_HASH_NODENAME))) { + if (fit_image_check_hash(fit, noffset, data, size, + &err_msg)) + goto error; + puts("+ "); + } else if (IMAGE_ENABLE_VERIFY && verify_all && + !strncmp(name, FIT_SIG_NODENAME, + strlen(FIT_SIG_NODENAME))) { + ret = fit_image_check_sig(fit, noffset, data, + size, -1, &err_msg); + if (ret) + puts("- "); + else + puts("+ "); + } + } + + if (noffset == -FDT_ERR_TRUNCATED || noffset == -FDT_ERR_BADSTRUCTURE) { + err_msg = "Corrupted or truncated tree"; + goto error; + } + + return 1; + +error: + printf(" error!\n%s for '%s' hash node in '%s' image node\n", + err_msg, fit_get_name(fit, noffset, NULL), + fit_get_name(fit, image_noffset, NULL)); + return 0; +} + +/** + * fit_all_image_verify - verify data intergity for all images + * @fit: pointer to the FIT format image header + * + * fit_all_image_verify() goes over all images in the FIT and + * for every images checks if all it's hashes are valid. + * + * returns: + * 1, if all hashes of all images are valid + * 0, otherwise (or on error) + */ +int fit_all_image_verify(const void *fit) +{ + int images_noffset; + int noffset; + int ndepth; + int count; + + /* Find images parent node offset */ + images_noffset = fdt_path_offset(fit, FIT_IMAGES_PATH); + if (images_noffset < 0) { + printf("Can't find images parent node '%s' (%s)\n", + FIT_IMAGES_PATH, fdt_strerror(images_noffset)); + return 0; + } + + /* Process all image subnodes, check hashes for each */ + printf("## Checking hash(es) for FIT Image at %08lx ...\n", + (ulong)fit); + for (ndepth = 0, count = 0, + noffset = fdt_next_node(fit, images_noffset, &ndepth); + (noffset >= 0) && (ndepth > 0); + noffset = fdt_next_node(fit, noffset, &ndepth)) { + if (ndepth == 1) { + /* + * Direct child node of the images parent node, + * i.e. component image node. + */ + printf(" Hash(es) for Image %u (%s): ", count++, + fit_get_name(fit, noffset, NULL)); + + if (!fit_image_verify(fit, noffset)) + return 0; + printf("\n"); + } + } + return 1; +} + +/** + * fit_image_check_os - check whether image node is of a given os type + * @fit: pointer to the FIT format image header + * @noffset: component image node offset + * @os: requested image os + * + * fit_image_check_os() reads image os property and compares its numeric + * id with the requested os. Comparison result is returned to the caller. + * + * returns: + * 1 if image is of given os type + * 0 otherwise (or on error) + */ +int fit_image_check_os(const void *fit, int noffset, uint8_t os) +{ + uint8_t image_os; + + if (fit_image_get_os(fit, noffset, &image_os)) + return 0; + return (os == image_os); +} + +/** + * fit_image_check_arch - check whether image node is of a given arch + * @fit: pointer to the FIT format image header + * @noffset: component image node offset + * @arch: requested imagearch + * + * fit_image_check_arch() reads image arch property and compares its numeric + * id with the requested arch. Comparison result is returned to the caller. + * + * returns: + * 1 if image is of given arch + * 0 otherwise (or on error) + */ +int fit_image_check_arch(const void *fit, int noffset, uint8_t arch) +{ + uint8_t image_arch; + + if (fit_image_get_arch(fit, noffset, &image_arch)) + return 0; + return (arch == image_arch); +} + +/** + * fit_image_check_type - check whether image node is of a given type + * @fit: pointer to the FIT format image header + * @noffset: component image node offset + * @type: requested image type + * + * fit_image_check_type() reads image type property and compares its numeric + * id with the requested type. Comparison result is returned to the caller. + * + * returns: + * 1 if image is of given type + * 0 otherwise (or on error) + */ +int fit_image_check_type(const void *fit, int noffset, uint8_t type) +{ + uint8_t image_type; + + if (fit_image_get_type(fit, noffset, &image_type)) + return 0; + return (type == image_type); +} + +/** + * fit_image_check_comp - check whether image node uses given compression + * @fit: pointer to the FIT format image header + * @noffset: component image node offset + * @comp: requested image compression type + * + * fit_image_check_comp() reads image compression property and compares its + * numeric id with the requested compression type. Comparison result is + * returned to the caller. + * + * returns: + * 1 if image uses requested compression + * 0 otherwise (or on error) + */ +int fit_image_check_comp(const void *fit, int noffset, uint8_t comp) +{ + uint8_t image_comp; + + if (fit_image_get_comp(fit, noffset, &image_comp)) + return 0; + return (comp == image_comp); +} + +/** + * fit_check_format - sanity check FIT image format + * @fit: pointer to the FIT format image header + * + * fit_check_format() runs a basic sanity FIT image verification. + * Routine checks for mandatory properties, nodes, etc. + * + * returns: + * 1, on success + * 0, on failure + */ +int fit_check_format(const void *fit) +{ + /* mandatory / node 'description' property */ + if (fdt_getprop(fit, 0, FIT_DESC_PROP, NULL) == NULL) { + debug("Wrong FIT format: no description\n"); + return 0; + } + + if (IMAGE_ENABLE_TIMESTAMP) { + /* mandatory / node 'timestamp' property */ + if (fdt_getprop(fit, 0, FIT_TIMESTAMP_PROP, NULL) == NULL) { + debug("Wrong FIT format: no timestamp\n"); + return 0; + } + } + + /* mandatory subimages parent '/images' node */ + if (fdt_path_offset(fit, FIT_IMAGES_PATH) < 0) { + debug("Wrong FIT format: no images parent node\n"); + return 0; + } + + return 1; +} + + +/** + * fit_conf_find_compat + * @fit: pointer to the FIT format image header + * @fdt: pointer to the device tree to compare against + * + * fit_conf_find_compat() attempts to find the configuration whose fdt is the + * most compatible with the passed in device tree. + * + * Example: + * + * / o image-tree + * |-o images + * | |-o fdt@1 + * | |-o fdt@2 + * | + * |-o configurations + * |-o config@1 + * | |-fdt = fdt@1 + * | + * |-o config@2 + * |-fdt = fdt@2 + * + * / o U-Boot fdt + * |-compatible = "foo,bar", "bim,bam" + * + * / o kernel fdt1 + * |-compatible = "foo,bar", + * + * / o kernel fdt2 + * |-compatible = "bim,bam", "baz,biz" + * + * Configuration 1 would be picked because the first string in U-Boot's + * compatible list, "foo,bar", matches a compatible string in the root of fdt1. + * "bim,bam" in fdt2 matches the second string which isn't as good as fdt1. + * + * returns: + * offset to the configuration to use if one was found + * -1 otherwise + */ +int fit_conf_find_compat(const void *fit, const void *fdt) +{ + int ndepth = 0; + int noffset, confs_noffset, images_noffset; + const void *fdt_compat; + int fdt_compat_len; + int best_match_offset = 0; + int best_match_pos = 0; + + confs_noffset = fdt_path_offset(fit, FIT_CONFS_PATH); + images_noffset = fdt_path_offset(fit, FIT_IMAGES_PATH); + if (confs_noffset < 0 || images_noffset < 0) { + debug("Can't find configurations or images nodes.\n"); + return -1; + } + + fdt_compat = fdt_getprop(fdt, 0, "compatible", &fdt_compat_len); + if (!fdt_compat) { + debug("Fdt for comparison has no \"compatible\" property.\n"); + return -1; + } + + /* + * Loop over the configurations in the FIT image. + */ + for (noffset = fdt_next_node(fit, confs_noffset, &ndepth); + (noffset >= 0) && (ndepth > 0); + noffset = fdt_next_node(fit, noffset, &ndepth)) { + const void *kfdt; + const char *kfdt_name; + int kfdt_noffset; + const char *cur_fdt_compat; + int len; + size_t size; + int i; + + if (ndepth > 1) + continue; + + kfdt_name = fdt_getprop(fit, noffset, "fdt", &len); + if (!kfdt_name) { + debug("No fdt property found.\n"); + continue; + } + kfdt_noffset = fdt_subnode_offset(fit, images_noffset, + kfdt_name); + if (kfdt_noffset < 0) { + debug("No image node named \"%s\" found.\n", + kfdt_name); + continue; + } + /* + * Get a pointer to this configuration's fdt. + */ + if (fit_image_get_data(fit, kfdt_noffset, &kfdt, &size)) { + debug("Failed to get fdt \"%s\".\n", kfdt_name); + continue; + } + + len = fdt_compat_len; + cur_fdt_compat = fdt_compat; + /* + * Look for a match for each U-Boot compatibility string in + * turn in this configuration's fdt. + */ + for (i = 0; len > 0 && + (!best_match_offset || best_match_pos > i); i++) { + int cur_len = strlen(cur_fdt_compat) + 1; + + if (!fdt_node_check_compatible(kfdt, 0, + cur_fdt_compat)) { + best_match_offset = noffset; + best_match_pos = i; + break; + } + len -= cur_len; + cur_fdt_compat += cur_len; + } + } + if (!best_match_offset) { + debug("No match found.\n"); + return -1; + } + + return best_match_offset; +} + +/** + * fit_conf_get_node - get node offset for configuration of a given unit name + * @fit: pointer to the FIT format image header + * @conf_uname: configuration node unit name + * + * fit_conf_get_node() finds a configuration (withing the '/configurations' + * parant node) of a provided unit name. If configuration is found its node + * offset is returned to the caller. + * + * When NULL is provided in second argument fit_conf_get_node() will search + * for a default configuration node instead. Default configuration node unit + * name is retrieved from FIT_DEFAULT_PROP property of the '/configurations' + * node. + * + * returns: + * configuration node offset when found (>=0) + * negative number on failure (FDT_ERR_* code) + */ +int fit_conf_get_node(const void *fit, const char *conf_uname) +{ + int noffset, confs_noffset; + int len; + + confs_noffset = fdt_path_offset(fit, FIT_CONFS_PATH); + if (confs_noffset < 0) { + debug("Can't find configurations parent node '%s' (%s)\n", + FIT_CONFS_PATH, fdt_strerror(confs_noffset)); + return confs_noffset; + } + + if (conf_uname == NULL) { + /* get configuration unit name from the default property */ + debug("No configuration specified, trying default...\n"); + conf_uname = (char *)fdt_getprop(fit, confs_noffset, + FIT_DEFAULT_PROP, &len); + if (conf_uname == NULL) { + fit_get_debug(fit, confs_noffset, FIT_DEFAULT_PROP, + len); + return len; + } + debug("Found default configuration: '%s'\n", conf_uname); + } + + noffset = fdt_subnode_offset(fit, confs_noffset, conf_uname); + if (noffset < 0) { + debug("Can't get node offset for configuration unit name: '%s' (%s)\n", + conf_uname, fdt_strerror(noffset)); + } + + return noffset; +} + +int fit_conf_get_prop_node(const void *fit, int noffset, + const char *prop_name) +{ + char *uname; + int len; + + /* get kernel image unit name from configuration kernel property */ + uname = (char *)fdt_getprop(fit, noffset, prop_name, &len); + if (uname == NULL) + return len; + + return fit_image_get_node(fit, uname); +} + +/** + * fit_conf_print - prints out the FIT configuration details + * @fit: pointer to the FIT format image header + * @noffset: offset of the configuration node + * @p: pointer to prefix string + * + * fit_conf_print() lists all mandatory properies for the processed + * configuration node. + * + * returns: + * no returned results + */ +void fit_conf_print(const void *fit, int noffset, const char *p) +{ + char *desc; + char *uname; + int ret; + + /* Mandatory properties */ + ret = fit_get_desc(fit, noffset, &desc); + printf("%s Description: ", p); + if (ret) + printf("unavailable\n"); + else + printf("%s\n", desc); + + uname = (char *)fdt_getprop(fit, noffset, FIT_KERNEL_PROP, NULL); + printf("%s Kernel: ", p); + if (uname == NULL) + printf("unavailable\n"); + else + printf("%s\n", uname); + + /* Optional properties */ + uname = (char *)fdt_getprop(fit, noffset, FIT_RAMDISK_PROP, NULL); + if (uname) + printf("%s Init Ramdisk: %s\n", p, uname); + + uname = (char *)fdt_getprop(fit, noffset, FIT_FDT_PROP, NULL); + if (uname) + printf("%s FDT: %s\n", p, uname); +} + +int fit_image_select(const void *fit, int rd_noffset, int verify) +{ + fit_image_print(fit, rd_noffset, " "); + + if (verify) { + puts(" Verifying Hash Integrity ... "); + if (!fit_image_verify(fit, rd_noffset)) { + puts("Bad Data Hash\n"); + return -EACCES; + } + puts("OK\n"); + } + + return 0; +} + +int fit_get_node_from_config(bootm_headers_t *images, const char *prop_name, + ulong addr) +{ + int cfg_noffset; + void *fit_hdr; + int noffset; + + debug("* %s: using config '%s' from image at 0x%08lx\n", + prop_name, images->fit_uname_cfg, addr); + + /* Check whether configuration has this property defined */ + fit_hdr = map_sysmem(addr, 0); + cfg_noffset = fit_conf_get_node(fit_hdr, images->fit_uname_cfg); + if (cfg_noffset < 0) { + debug("* %s: no such config\n", prop_name); + return -ENOENT; + } + + noffset = fit_conf_get_prop_node(fit_hdr, cfg_noffset, prop_name); + if (noffset < 0) { + debug("* %s: no '%s' in config\n", prop_name, prop_name); + return -ENOLINK; + } + + return noffset; +} + +int fit_image_load(bootm_headers_t *images, const char *prop_name, ulong addr, + const char **fit_unamep, const char **fit_uname_configp, + int arch, int image_type, int bootstage_id, + enum fit_load_op load_op, ulong *datap, ulong *lenp) +{ + int cfg_noffset, noffset; + const char *fit_uname; + const char *fit_uname_config; + const void *fit; + const void *buf; + size_t size; + int type_ok, os_ok; + ulong load, data, len; + int ret; + + fit = map_sysmem(addr, 0); + fit_uname = fit_unamep ? *fit_unamep : NULL; + fit_uname_config = fit_uname_configp ? *fit_uname_configp : NULL; + printf("## Loading %s from FIT Image at %08lx ...\n", prop_name, addr); + + bootstage_mark(bootstage_id + BOOTSTAGE_SUB_FORMAT); + if (!fit_check_format(fit)) { + printf("Bad FIT %s image format!\n", prop_name); + bootstage_error(bootstage_id + BOOTSTAGE_SUB_FORMAT); + return -ENOEXEC; + } + bootstage_mark(bootstage_id + BOOTSTAGE_SUB_FORMAT_OK); + if (fit_uname) { + /* get FIT component image node offset */ + bootstage_mark(bootstage_id + BOOTSTAGE_SUB_UNIT_NAME); + noffset = fit_image_get_node(fit, fit_uname); + } else { + /* + * no image node unit name, try to get config + * node first. If config unit node name is NULL + * fit_conf_get_node() will try to find default config node + */ + bootstage_mark(bootstage_id + BOOTSTAGE_SUB_NO_UNIT_NAME); + if (IMAGE_ENABLE_BEST_MATCH && !fit_uname_config) { + cfg_noffset = fit_conf_find_compat(fit, gd_fdt_blob()); + } else { + cfg_noffset = fit_conf_get_node(fit, + fit_uname_config); + } + if (cfg_noffset < 0) { + puts("Could not find configuration node\n"); + bootstage_error(bootstage_id + + BOOTSTAGE_SUB_NO_UNIT_NAME); + return -ENOENT; + } + fit_uname_config = fdt_get_name(fit, cfg_noffset, NULL); + printf(" Using '%s' configuration\n", fit_uname_config); + if (image_type == IH_TYPE_KERNEL) { + /* Remember (and possibly verify) this config */ + images->fit_uname_cfg = fit_uname_config; + if (IMAGE_ENABLE_VERIFY && images->verify) { + puts(" Verifying Hash Integrity ... "); + if (!fit_config_verify(fit, cfg_noffset)) { + puts("Bad Data Hash\n"); + bootstage_error(bootstage_id + + BOOTSTAGE_SUB_HASH); + return -EACCES; + } + puts("OK\n"); + } + bootstage_mark(BOOTSTAGE_ID_FIT_CONFIG); + } + + noffset = fit_conf_get_prop_node(fit, cfg_noffset, + prop_name); + fit_uname = fit_get_name(fit, noffset, NULL); + } + if (noffset < 0) { + puts("Could not find subimage node\n"); + bootstage_error(bootstage_id + BOOTSTAGE_SUB_SUBNODE); + return -ENOENT; + } + + printf(" Trying '%s' %s subimage\n", fit_uname, prop_name); + + ret = fit_image_select(fit, noffset, images->verify); + if (ret) { + bootstage_error(bootstage_id + BOOTSTAGE_SUB_HASH); + return ret; + } + + bootstage_mark(bootstage_id + BOOTSTAGE_SUB_CHECK_ARCH); + if (!fit_image_check_target_arch(fit, noffset)) { + puts("Unsupported Architecture\n"); + bootstage_error(bootstage_id + BOOTSTAGE_SUB_CHECK_ARCH); + return -ENOEXEC; + } + + if (image_type == IH_TYPE_FLATDT && + !fit_image_check_comp(fit, noffset, IH_COMP_NONE)) { + puts("FDT image is compressed"); + return -EPROTONOSUPPORT; + } + + bootstage_mark(bootstage_id + BOOTSTAGE_SUB_CHECK_ALL); + type_ok = fit_image_check_type(fit, noffset, image_type) || + (image_type == IH_TYPE_KERNEL && + fit_image_check_type(fit, noffset, + IH_TYPE_KERNEL_NOLOAD)); + os_ok = image_type == IH_TYPE_FLATDT || + fit_image_check_os(fit, noffset, IH_OS_LINUX); + if (!type_ok || !os_ok) { + printf("No Linux %s %s Image\n", genimg_get_arch_name(arch), + genimg_get_type_name(image_type)); + bootstage_error(bootstage_id + BOOTSTAGE_SUB_CHECK_ALL); + return -EIO; + } + + bootstage_mark(bootstage_id + BOOTSTAGE_SUB_CHECK_ALL_OK); + + /* get image data address and length */ + if (fit_image_get_data(fit, noffset, &buf, &size)) { + printf("Could not find %s subimage data!\n", prop_name); + bootstage_error(bootstage_id + BOOTSTAGE_SUB_GET_DATA); + return -ENOENT; + } + len = (ulong)size; + + /* verify that image data is a proper FDT blob */ + if (image_type == IH_TYPE_FLATDT && fdt_check_header(buf)) { + puts("Subimage data is not a FDT"); + return -ENOEXEC; + } + + bootstage_mark(bootstage_id + BOOTSTAGE_SUB_GET_DATA_OK); + + /* + * Work-around for eldk-4.2 which gives this warning if we try to + * case in the unmap_sysmem() call: + * warning: initialization discards qualifiers from pointer target type + */ + { + void *vbuf = (void *)buf; + + data = map_to_sysmem(vbuf); + } + + if (load_op == FIT_LOAD_IGNORED) { + /* Don't load */ + } else if (fit_image_get_load(fit, noffset, &load)) { + if (load_op == FIT_LOAD_REQUIRED) { + printf("Can't get %s subimage load address!\n", + prop_name); + bootstage_error(bootstage_id + BOOTSTAGE_SUB_LOAD); + return -EBADF; + } + } else { + ulong image_start, image_end; + ulong load_end; + void *dst; + + /* + * move image data to the load address, + * make sure we don't overwrite initial image + */ + image_start = addr; + image_end = addr + fit_get_size(fit); + + load_end = load + len; + if (image_type != IH_TYPE_KERNEL && + load < image_end && load_end > image_start) { + printf("Error: %s overwritten\n", prop_name); + return -EXDEV; + } + + printf(" Loading %s from 0x%08lx to 0x%08lx\n", + prop_name, data, load); + + dst = map_sysmem(load, len); + memmove(dst, buf, len); + data = load; + } + bootstage_mark(bootstage_id + BOOTSTAGE_SUB_LOAD); + + *datap = data; + *lenp = len; + if (fit_unamep) + *fit_unamep = (char *)fit_uname; + if (fit_uname_configp) + *fit_uname_configp = (char *)fit_uname_config; + + return noffset; +} diff --git a/qemu/roms/u-boot/common/image-sig.c b/qemu/roms/u-boot/common/image-sig.c new file mode 100644 index 000000000..72284eb1d --- /dev/null +++ b/qemu/roms/u-boot/common/image-sig.c @@ -0,0 +1,472 @@ +/* + * Copyright (c) 2013, Google Inc. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifdef USE_HOSTCC +#include "mkimage.h" +#include <time.h> +#else +#include <common.h> +#include <malloc.h> +DECLARE_GLOBAL_DATA_PTR; +#endif /* !USE_HOSTCC*/ +#include <image.h> +#include <rsa.h> +#include <rsa-checksum.h> + +#define IMAGE_MAX_HASHED_NODES 100 + +#ifdef USE_HOSTCC +void *host_blob; +void image_set_host_blob(void *blob) +{ + host_blob = blob; +} +void *image_get_host_blob(void) +{ + return host_blob; +} +#endif + +struct checksum_algo checksum_algos[] = { + { + "sha1", + SHA1_SUM_LEN, + RSA2048_BYTES, +#if IMAGE_ENABLE_SIGN + EVP_sha1, +#endif + sha1_calculate, + padding_sha1_rsa2048, + }, + { + "sha256", + SHA256_SUM_LEN, + RSA2048_BYTES, +#if IMAGE_ENABLE_SIGN + EVP_sha256, +#endif + sha256_calculate, + padding_sha256_rsa2048, + }, + { + "sha256", + SHA256_SUM_LEN, + RSA4096_BYTES, +#if IMAGE_ENABLE_SIGN + EVP_sha256, +#endif + sha256_calculate, + padding_sha256_rsa4096, + } + +}; + +struct image_sig_algo image_sig_algos[] = { + { + "sha1,rsa2048", + rsa_sign, + rsa_add_verify_data, + rsa_verify, + &checksum_algos[0], + }, + { + "sha256,rsa2048", + rsa_sign, + rsa_add_verify_data, + rsa_verify, + &checksum_algos[1], + }, + { + "sha256,rsa4096", + rsa_sign, + rsa_add_verify_data, + rsa_verify, + &checksum_algos[2], + } + +}; + +struct image_sig_algo *image_get_sig_algo(const char *name) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(image_sig_algos); i++) { + if (!strcmp(image_sig_algos[i].name, name)) + return &image_sig_algos[i]; + } + + return NULL; +} + +/** + * fit_region_make_list() - Make a list of image regions + * + * Given a list of fdt_regions, create a list of image_regions. This is a + * simple conversion routine since the FDT and image code use different + * structures. + * + * @fit: FIT image + * @fdt_regions: Pointer to FDT regions + * @count: Number of FDT regions + * @region: Pointer to image regions, which must hold @count records. If + * region is NULL, then (except for an SPL build) the array will be + * allocated. + * @return: Pointer to image regions + */ +struct image_region *fit_region_make_list(const void *fit, + struct fdt_region *fdt_regions, int count, + struct image_region *region) +{ + int i; + + debug("Hash regions:\n"); + debug("%10s %10s\n", "Offset", "Size"); + + /* + * Use malloc() except in SPL (to save code size). In SPL the caller + * must allocate the array. + */ +#ifndef CONFIG_SPL_BUILD + if (!region) + region = calloc(sizeof(*region), count); +#endif + if (!region) + return NULL; + for (i = 0; i < count; i++) { + debug("%10x %10x\n", fdt_regions[i].offset, + fdt_regions[i].size); + region[i].data = fit + fdt_regions[i].offset; + region[i].size = fdt_regions[i].size; + } + + return region; +} + +static int fit_image_setup_verify(struct image_sign_info *info, + const void *fit, int noffset, int required_keynode, + char **err_msgp) +{ + char *algo_name; + + if (fit_image_hash_get_algo(fit, noffset, &algo_name)) { + *err_msgp = "Can't get hash algo property"; + return -1; + } + memset(info, '\0', sizeof(*info)); + info->keyname = fdt_getprop(fit, noffset, "key-name-hint", NULL); + info->fit = (void *)fit; + info->node_offset = noffset; + info->algo = image_get_sig_algo(algo_name); + info->fdt_blob = gd_fdt_blob(); + info->required_keynode = required_keynode; + printf("%s:%s", algo_name, info->keyname); + + if (!info->algo) { + *err_msgp = "Unknown signature algorithm"; + return -1; + } + + return 0; +} + +int fit_image_check_sig(const void *fit, int noffset, const void *data, + size_t size, int required_keynode, char **err_msgp) +{ + struct image_sign_info info; + struct image_region region; + uint8_t *fit_value; + int fit_value_len; + + *err_msgp = NULL; + if (fit_image_setup_verify(&info, fit, noffset, required_keynode, + err_msgp)) + return -1; + + if (fit_image_hash_get_value(fit, noffset, &fit_value, + &fit_value_len)) { + *err_msgp = "Can't get hash value property"; + return -1; + } + + region.data = data; + region.size = size; + + if (info.algo->verify(&info, ®ion, 1, fit_value, fit_value_len)) { + *err_msgp = "Verification failed"; + return -1; + } + + return 0; +} + +static int fit_image_verify_sig(const void *fit, int image_noffset, + const char *data, size_t size, const void *sig_blob, + int sig_offset) +{ + int noffset; + char *err_msg = ""; + int verified = 0; + int ret; + + /* Process all hash subnodes of the component image node */ + for (noffset = fdt_first_subnode(fit, image_noffset); + noffset >= 0; + noffset = fdt_next_subnode(fit, noffset)) { + const char *name = fit_get_name(fit, noffset, NULL); + + if (!strncmp(name, FIT_SIG_NODENAME, + strlen(FIT_SIG_NODENAME))) { + ret = fit_image_check_sig(fit, noffset, data, + size, -1, &err_msg); + if (ret) { + puts("- "); + } else { + puts("+ "); + verified = 1; + break; + } + } + } + + if (noffset == -FDT_ERR_TRUNCATED || noffset == -FDT_ERR_BADSTRUCTURE) { + err_msg = "Corrupted or truncated tree"; + goto error; + } + + return verified ? 0 : -EPERM; + +error: + printf(" error!\n%s for '%s' hash node in '%s' image node\n", + err_msg, fit_get_name(fit, noffset, NULL), + fit_get_name(fit, image_noffset, NULL)); + return -1; +} + +int fit_image_verify_required_sigs(const void *fit, int image_noffset, + const char *data, size_t size, const void *sig_blob, + int *no_sigsp) +{ + int verify_count = 0; + int noffset; + int sig_node; + + /* Work out what we need to verify */ + *no_sigsp = 1; + sig_node = fdt_subnode_offset(sig_blob, 0, FIT_SIG_NODENAME); + if (sig_node < 0) { + debug("%s: No signature node found: %s\n", __func__, + fdt_strerror(sig_node)); + return 0; + } + + for (noffset = fdt_first_subnode(sig_blob, sig_node); + noffset >= 0; + noffset = fdt_next_subnode(sig_blob, noffset)) { + const char *required; + int ret; + + required = fdt_getprop(sig_blob, noffset, "required", NULL); + if (!required || strcmp(required, "image")) + continue; + ret = fit_image_verify_sig(fit, image_noffset, data, size, + sig_blob, noffset); + if (ret) { + printf("Failed to verify required signature '%s'\n", + fit_get_name(sig_blob, noffset, NULL)); + return ret; + } + verify_count++; + } + + if (verify_count) + *no_sigsp = 0; + + return 0; +} + +int fit_config_check_sig(const void *fit, int noffset, int required_keynode, + char **err_msgp) +{ + char * const exc_prop[] = {"data"}; + const char *prop, *end, *name; + struct image_sign_info info; + const uint32_t *strings; + uint8_t *fit_value; + int fit_value_len; + int max_regions; + int i, prop_len; + char path[200]; + int count; + + debug("%s: fdt=%p, conf='%s', sig='%s'\n", __func__, gd_fdt_blob(), + fit_get_name(fit, noffset, NULL), + fit_get_name(gd_fdt_blob(), required_keynode, NULL)); + *err_msgp = NULL; + if (fit_image_setup_verify(&info, fit, noffset, required_keynode, + err_msgp)) + return -1; + + if (fit_image_hash_get_value(fit, noffset, &fit_value, + &fit_value_len)) { + *err_msgp = "Can't get hash value property"; + return -1; + } + + /* Count the number of strings in the property */ + prop = fdt_getprop(fit, noffset, "hashed-nodes", &prop_len); + end = prop ? prop + prop_len : prop; + for (name = prop, count = 0; name < end; name++) + if (!*name) + count++; + if (!count) { + *err_msgp = "Can't get hashed-nodes property"; + return -1; + } + + /* Add a sanity check here since we are using the stack */ + if (count > IMAGE_MAX_HASHED_NODES) { + *err_msgp = "Number of hashed nodes exceeds maximum"; + return -1; + } + + /* Create a list of node names from those strings */ + char *node_inc[count]; + + debug("Hash nodes (%d):\n", count); + for (name = prop, i = 0; name < end; name += strlen(name) + 1, i++) { + debug(" '%s'\n", name); + node_inc[i] = (char *)name; + } + + /* + * Each node can generate one region for each sub-node. Allow for + * 7 sub-nodes (hash@1, signature@1, etc.) and some extra. + */ + max_regions = 20 + count * 7; + struct fdt_region fdt_regions[max_regions]; + + /* Get a list of regions to hash */ + count = fdt_find_regions(fit, node_inc, count, + exc_prop, ARRAY_SIZE(exc_prop), + fdt_regions, max_regions - 1, + path, sizeof(path), 0); + if (count < 0) { + *err_msgp = "Failed to hash configuration"; + return -1; + } + if (count == 0) { + *err_msgp = "No data to hash"; + return -1; + } + if (count >= max_regions - 1) { + *err_msgp = "Too many hash regions"; + return -1; + } + + /* Add the strings */ + strings = fdt_getprop(fit, noffset, "hashed-strings", NULL); + if (strings) { + fdt_regions[count].offset = fdt_off_dt_strings(fit) + + fdt32_to_cpu(strings[0]); + fdt_regions[count].size = fdt32_to_cpu(strings[1]); + count++; + } + + /* Allocate the region list on the stack */ + struct image_region region[count]; + + fit_region_make_list(fit, fdt_regions, count, region); + if (info.algo->verify(&info, region, count, fit_value, + fit_value_len)) { + *err_msgp = "Verification failed"; + return -1; + } + + return 0; +} + +static int fit_config_verify_sig(const void *fit, int conf_noffset, + const void *sig_blob, int sig_offset) +{ + int noffset; + char *err_msg = ""; + int verified = 0; + int ret; + + /* Process all hash subnodes of the component conf node */ + for (noffset = fdt_first_subnode(fit, conf_noffset); + noffset >= 0; + noffset = fdt_next_subnode(fit, noffset)) { + const char *name = fit_get_name(fit, noffset, NULL); + + if (!strncmp(name, FIT_SIG_NODENAME, + strlen(FIT_SIG_NODENAME))) { + ret = fit_config_check_sig(fit, noffset, sig_offset, + &err_msg); + if (ret) { + puts("- "); + } else { + puts("+ "); + verified = 1; + break; + } + } + } + + if (noffset == -FDT_ERR_TRUNCATED || noffset == -FDT_ERR_BADSTRUCTURE) { + err_msg = "Corrupted or truncated tree"; + goto error; + } + + return verified ? 0 : -EPERM; + +error: + printf(" error!\n%s for '%s' hash node in '%s' config node\n", + err_msg, fit_get_name(fit, noffset, NULL), + fit_get_name(fit, conf_noffset, NULL)); + return -1; +} + +int fit_config_verify_required_sigs(const void *fit, int conf_noffset, + const void *sig_blob) +{ + int noffset; + int sig_node; + + /* Work out what we need to verify */ + sig_node = fdt_subnode_offset(sig_blob, 0, FIT_SIG_NODENAME); + if (sig_node < 0) { + debug("%s: No signature node found: %s\n", __func__, + fdt_strerror(sig_node)); + return 0; + } + + for (noffset = fdt_first_subnode(sig_blob, sig_node); + noffset >= 0; + noffset = fdt_next_subnode(sig_blob, noffset)) { + const char *required; + int ret; + + required = fdt_getprop(sig_blob, noffset, "required", NULL); + if (!required || strcmp(required, "conf")) + continue; + ret = fit_config_verify_sig(fit, conf_noffset, sig_blob, + noffset); + if (ret) { + printf("Failed to verify required signature '%s'\n", + fit_get_name(sig_blob, noffset, NULL)); + return ret; + } + } + + return 0; +} + +int fit_config_verify(const void *fit, int conf_noffset) +{ + return !fit_config_verify_required_sigs(fit, conf_noffset, + gd_fdt_blob()); +} diff --git a/qemu/roms/u-boot/common/image.c b/qemu/roms/u-boot/common/image.c new file mode 100644 index 000000000..fcc5a9c3e --- /dev/null +++ b/qemu/roms/u-boot/common/image.c @@ -0,0 +1,1176 @@ +/* + * (C) Copyright 2008 Semihalf + * + * (C) Copyright 2000-2006 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef USE_HOSTCC +#include <common.h> +#include <watchdog.h> + +#ifdef CONFIG_SHOW_BOOT_PROGRESS +#include <status_led.h> +#endif + +#ifdef CONFIG_HAS_DATAFLASH +#include <dataflash.h> +#endif + +#ifdef CONFIG_LOGBUFFER +#include <logbuff.h> +#endif + +#include <rtc.h> + +#include <environment.h> +#include <image.h> + +#if defined(CONFIG_FIT) || defined(CONFIG_OF_LIBFDT) +#include <libfdt.h> +#include <fdt_support.h> +#endif + +#include <u-boot/md5.h> +#include <sha1.h> +#include <asm/errno.h> +#include <asm/io.h> + +#ifdef CONFIG_CMD_BDI +extern int do_bdinfo(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]); +#endif + +DECLARE_GLOBAL_DATA_PTR; + +static const image_header_t *image_get_ramdisk(ulong rd_addr, uint8_t arch, + int verify); +#else +#include "mkimage.h" +#include <u-boot/md5.h> +#include <time.h> +#include <image.h> +#endif /* !USE_HOSTCC*/ + +#include <u-boot/crc.h> + +#ifndef CONFIG_SYS_BARGSIZE +#define CONFIG_SYS_BARGSIZE 512 +#endif + +static const table_entry_t uimage_arch[] = { + { IH_ARCH_INVALID, NULL, "Invalid ARCH", }, + { IH_ARCH_ALPHA, "alpha", "Alpha", }, + { IH_ARCH_ARM, "arm", "ARM", }, + { IH_ARCH_I386, "x86", "Intel x86", }, + { IH_ARCH_IA64, "ia64", "IA64", }, + { IH_ARCH_M68K, "m68k", "M68K", }, + { IH_ARCH_MICROBLAZE, "microblaze", "MicroBlaze", }, + { IH_ARCH_MIPS, "mips", "MIPS", }, + { IH_ARCH_MIPS64, "mips64", "MIPS 64 Bit", }, + { IH_ARCH_NIOS2, "nios2", "NIOS II", }, + { IH_ARCH_PPC, "powerpc", "PowerPC", }, + { IH_ARCH_PPC, "ppc", "PowerPC", }, + { IH_ARCH_S390, "s390", "IBM S390", }, + { IH_ARCH_SH, "sh", "SuperH", }, + { IH_ARCH_SPARC, "sparc", "SPARC", }, + { IH_ARCH_SPARC64, "sparc64", "SPARC 64 Bit", }, + { IH_ARCH_BLACKFIN, "blackfin", "Blackfin", }, + { IH_ARCH_AVR32, "avr32", "AVR32", }, + { IH_ARCH_NDS32, "nds32", "NDS32", }, + { IH_ARCH_OPENRISC, "or1k", "OpenRISC 1000",}, + { IH_ARCH_SANDBOX, "sandbox", "Sandbox", }, + { IH_ARCH_ARM64, "arm64", "AArch64", }, + { IH_ARCH_ARC, "arc", "ARC", }, + { -1, "", "", }, +}; + +static const table_entry_t uimage_os[] = { + { IH_OS_INVALID, NULL, "Invalid OS", }, + { IH_OS_LINUX, "linux", "Linux", }, +#if defined(CONFIG_LYNXKDI) || defined(USE_HOSTCC) + { IH_OS_LYNXOS, "lynxos", "LynxOS", }, +#endif + { IH_OS_NETBSD, "netbsd", "NetBSD", }, + { IH_OS_OSE, "ose", "Enea OSE", }, + { IH_OS_PLAN9, "plan9", "Plan 9", }, + { IH_OS_RTEMS, "rtems", "RTEMS", }, + { IH_OS_U_BOOT, "u-boot", "U-Boot", }, + { IH_OS_VXWORKS, "vxworks", "VxWorks", }, +#if defined(CONFIG_CMD_ELF) || defined(USE_HOSTCC) + { IH_OS_QNX, "qnx", "QNX", }, +#endif +#if defined(CONFIG_INTEGRITY) || defined(USE_HOSTCC) + { IH_OS_INTEGRITY,"integrity", "INTEGRITY", }, +#endif +#ifdef USE_HOSTCC + { IH_OS_4_4BSD, "4_4bsd", "4_4BSD", }, + { IH_OS_DELL, "dell", "Dell", }, + { IH_OS_ESIX, "esix", "Esix", }, + { IH_OS_FREEBSD, "freebsd", "FreeBSD", }, + { IH_OS_IRIX, "irix", "Irix", }, + { IH_OS_NCR, "ncr", "NCR", }, + { IH_OS_OPENBSD, "openbsd", "OpenBSD", }, + { IH_OS_PSOS, "psos", "pSOS", }, + { IH_OS_SCO, "sco", "SCO", }, + { IH_OS_SOLARIS, "solaris", "Solaris", }, + { IH_OS_SVR4, "svr4", "SVR4", }, +#endif + { -1, "", "", }, +}; + +static const table_entry_t uimage_type[] = { + { IH_TYPE_AISIMAGE, "aisimage", "Davinci AIS image",}, + { IH_TYPE_FILESYSTEM, "filesystem", "Filesystem Image", }, + { IH_TYPE_FIRMWARE, "firmware", "Firmware", }, + { IH_TYPE_FLATDT, "flat_dt", "Flat Device Tree", }, + { IH_TYPE_GPIMAGE, "gpimage", "TI Keystone SPL Image",}, + { IH_TYPE_KERNEL, "kernel", "Kernel Image", }, + { IH_TYPE_KERNEL_NOLOAD, "kernel_noload", "Kernel Image (no loading done)", }, + { IH_TYPE_KWBIMAGE, "kwbimage", "Kirkwood Boot Image",}, + { IH_TYPE_IMXIMAGE, "imximage", "Freescale i.MX Boot Image",}, + { IH_TYPE_INVALID, NULL, "Invalid Image", }, + { IH_TYPE_MULTI, "multi", "Multi-File Image", }, + { IH_TYPE_OMAPIMAGE, "omapimage", "TI OMAP SPL With GP CH",}, + { IH_TYPE_PBLIMAGE, "pblimage", "Freescale PBL Boot Image",}, + { IH_TYPE_RAMDISK, "ramdisk", "RAMDisk Image", }, + { IH_TYPE_SCRIPT, "script", "Script", }, + { IH_TYPE_STANDALONE, "standalone", "Standalone Program", }, + { IH_TYPE_UBLIMAGE, "ublimage", "Davinci UBL image",}, + { IH_TYPE_MXSIMAGE, "mxsimage", "Freescale MXS Boot Image",}, + { -1, "", "", }, +}; + +static const table_entry_t uimage_comp[] = { + { IH_COMP_NONE, "none", "uncompressed", }, + { IH_COMP_BZIP2, "bzip2", "bzip2 compressed", }, + { IH_COMP_GZIP, "gzip", "gzip compressed", }, + { IH_COMP_LZMA, "lzma", "lzma compressed", }, + { IH_COMP_LZO, "lzo", "lzo compressed", }, + { -1, "", "", }, +}; + +/*****************************************************************************/ +/* Legacy format routines */ +/*****************************************************************************/ +int image_check_hcrc(const image_header_t *hdr) +{ + ulong hcrc; + ulong len = image_get_header_size(); + image_header_t header; + + /* Copy header so we can blank CRC field for re-calculation */ + memmove(&header, (char *)hdr, image_get_header_size()); + image_set_hcrc(&header, 0); + + hcrc = crc32(0, (unsigned char *)&header, len); + + return (hcrc == image_get_hcrc(hdr)); +} + +int image_check_dcrc(const image_header_t *hdr) +{ + ulong data = image_get_data(hdr); + ulong len = image_get_data_size(hdr); + ulong dcrc = crc32_wd(0, (unsigned char *)data, len, CHUNKSZ_CRC32); + + return (dcrc == image_get_dcrc(hdr)); +} + +/** + * image_multi_count - get component (sub-image) count + * @hdr: pointer to the header of the multi component image + * + * image_multi_count() returns number of components in a multi + * component image. + * + * Note: no checking of the image type is done, caller must pass + * a valid multi component image. + * + * returns: + * number of components + */ +ulong image_multi_count(const image_header_t *hdr) +{ + ulong i, count = 0; + uint32_t *size; + + /* get start of the image payload, which in case of multi + * component images that points to a table of component sizes */ + size = (uint32_t *)image_get_data(hdr); + + /* count non empty slots */ + for (i = 0; size[i]; ++i) + count++; + + return count; +} + +/** + * image_multi_getimg - get component data address and size + * @hdr: pointer to the header of the multi component image + * @idx: index of the requested component + * @data: pointer to a ulong variable, will hold component data address + * @len: pointer to a ulong variable, will hold component size + * + * image_multi_getimg() returns size and data address for the requested + * component in a multi component image. + * + * Note: no checking of the image type is done, caller must pass + * a valid multi component image. + * + * returns: + * data address and size of the component, if idx is valid + * 0 in data and len, if idx is out of range + */ +void image_multi_getimg(const image_header_t *hdr, ulong idx, + ulong *data, ulong *len) +{ + int i; + uint32_t *size; + ulong offset, count, img_data; + + /* get number of component */ + count = image_multi_count(hdr); + + /* get start of the image payload, which in case of multi + * component images that points to a table of component sizes */ + size = (uint32_t *)image_get_data(hdr); + + /* get address of the proper component data start, which means + * skipping sizes table (add 1 for last, null entry) */ + img_data = image_get_data(hdr) + (count + 1) * sizeof(uint32_t); + + if (idx < count) { + *len = uimage_to_cpu(size[idx]); + offset = 0; + + /* go over all indices preceding requested component idx */ + for (i = 0; i < idx; i++) { + /* add up i-th component size, rounding up to 4 bytes */ + offset += (uimage_to_cpu(size[i]) + 3) & ~3 ; + } + + /* calculate idx-th component data address */ + *data = img_data + offset; + } else { + *len = 0; + *data = 0; + } +} + +static void image_print_type(const image_header_t *hdr) +{ + const char *os, *arch, *type, *comp; + + os = genimg_get_os_name(image_get_os(hdr)); + arch = genimg_get_arch_name(image_get_arch(hdr)); + type = genimg_get_type_name(image_get_type(hdr)); + comp = genimg_get_comp_name(image_get_comp(hdr)); + + printf("%s %s %s (%s)\n", arch, os, type, comp); +} + +/** + * image_print_contents - prints out the contents of the legacy format image + * @ptr: pointer to the legacy format image header + * @p: pointer to prefix string + * + * image_print_contents() formats a multi line legacy image contents description. + * The routine prints out all header fields followed by the size/offset data + * for MULTI/SCRIPT images. + * + * returns: + * no returned results + */ +void image_print_contents(const void *ptr) +{ + const image_header_t *hdr = (const image_header_t *)ptr; + const char *p; + + p = IMAGE_INDENT_STRING; + printf("%sImage Name: %.*s\n", p, IH_NMLEN, image_get_name(hdr)); + if (IMAGE_ENABLE_TIMESTAMP) { + printf("%sCreated: ", p); + genimg_print_time((time_t)image_get_time(hdr)); + } + printf("%sImage Type: ", p); + image_print_type(hdr); + printf("%sData Size: ", p); + genimg_print_size(image_get_data_size(hdr)); + printf("%sLoad Address: %08x\n", p, image_get_load(hdr)); + printf("%sEntry Point: %08x\n", p, image_get_ep(hdr)); + + if (image_check_type(hdr, IH_TYPE_MULTI) || + image_check_type(hdr, IH_TYPE_SCRIPT)) { + int i; + ulong data, len; + ulong count = image_multi_count(hdr); + + printf("%sContents:\n", p); + for (i = 0; i < count; i++) { + image_multi_getimg(hdr, i, &data, &len); + + printf("%s Image %d: ", p, i); + genimg_print_size(len); + + if (image_check_type(hdr, IH_TYPE_SCRIPT) && i > 0) { + /* + * the user may need to know offsets + * if planning to do something with + * multiple files + */ + printf("%s Offset = 0x%08lx\n", p, data); + } + } + } +} + + +#ifndef USE_HOSTCC +/** + * image_get_ramdisk - get and verify ramdisk image + * @rd_addr: ramdisk image start address + * @arch: expected ramdisk architecture + * @verify: checksum verification flag + * + * image_get_ramdisk() returns a pointer to the verified ramdisk image + * header. Routine receives image start address and expected architecture + * flag. Verification done covers data and header integrity and os/type/arch + * fields checking. + * + * If dataflash support is enabled routine checks for dataflash addresses + * and handles required dataflash reads. + * + * returns: + * pointer to a ramdisk image header, if image was found and valid + * otherwise, return NULL + */ +static const image_header_t *image_get_ramdisk(ulong rd_addr, uint8_t arch, + int verify) +{ + const image_header_t *rd_hdr = (const image_header_t *)rd_addr; + + if (!image_check_magic(rd_hdr)) { + puts("Bad Magic Number\n"); + bootstage_error(BOOTSTAGE_ID_RD_MAGIC); + return NULL; + } + + if (!image_check_hcrc(rd_hdr)) { + puts("Bad Header Checksum\n"); + bootstage_error(BOOTSTAGE_ID_RD_HDR_CHECKSUM); + return NULL; + } + + bootstage_mark(BOOTSTAGE_ID_RD_MAGIC); + image_print_contents(rd_hdr); + + if (verify) { + puts(" Verifying Checksum ... "); + if (!image_check_dcrc(rd_hdr)) { + puts("Bad Data CRC\n"); + bootstage_error(BOOTSTAGE_ID_RD_CHECKSUM); + return NULL; + } + puts("OK\n"); + } + + bootstage_mark(BOOTSTAGE_ID_RD_HDR_CHECKSUM); + + if (!image_check_os(rd_hdr, IH_OS_LINUX) || + !image_check_arch(rd_hdr, arch) || + !image_check_type(rd_hdr, IH_TYPE_RAMDISK)) { + printf("No Linux %s Ramdisk Image\n", + genimg_get_arch_name(arch)); + bootstage_error(BOOTSTAGE_ID_RAMDISK); + return NULL; + } + + return rd_hdr; +} +#endif /* !USE_HOSTCC */ + +/*****************************************************************************/ +/* 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"); + if (s) { + ulong tmp = simple_strtoul(s, NULL, 16); + return tmp; + } + +#if defined(CONFIG_SYS_SDRAM_BASE) + return CONFIG_SYS_SDRAM_BASE; +#elif defined(CONFIG_ARM) + return gd->bd->bi_dram[0].start; +#else + return 0; +#endif +} + +phys_size_t getenv_bootm_size(void) +{ + phys_size_t tmp; + char *s = getenv("bootm_size"); + if (s) { + tmp = (phys_size_t)simple_strtoull(s, NULL, 16); + return tmp; + } + s = getenv("bootm_low"); + if (s) + tmp = (phys_size_t)simple_strtoull(s, NULL, 16); + else + tmp = 0; + + +#if defined(CONFIG_ARM) + return gd->bd->bi_dram[0].size - tmp; +#else + return gd->bd->bi_memsize - tmp; +#endif +} + +phys_size_t getenv_bootm_mapsize(void) +{ + phys_size_t tmp; + char *s = getenv("bootm_mapsize"); + if (s) { + tmp = (phys_size_t)simple_strtoull(s, NULL, 16); + return tmp; + } + +#if defined(CONFIG_SYS_BOOTMAPSZ) + return CONFIG_SYS_BOOTMAPSZ; +#else + return getenv_bootm_size(); +#endif +} + +void memmove_wd(void *to, void *from, size_t len, ulong chunksz) +{ + if (to == from) + return; + +#if defined(CONFIG_HW_WATCHDOG) || defined(CONFIG_WATCHDOG) + while (len > 0) { + size_t tail = (len > chunksz) ? chunksz : len; + WATCHDOG_RESET(); + memmove(to, from, tail); + to += tail; + from += tail; + len -= tail; + } +#else /* !(CONFIG_HW_WATCHDOG || CONFIG_WATCHDOG) */ + memmove(to, from, len); +#endif /* CONFIG_HW_WATCHDOG || CONFIG_WATCHDOG */ +} +#endif /* !USE_HOSTCC */ + +void genimg_print_size(uint32_t size) +{ +#ifndef USE_HOSTCC + printf("%d Bytes = ", size); + print_size(size, "\n"); +#else + printf("%d Bytes = %.2f kB = %.2f MB\n", + size, (double)size / 1.024e3, + (double)size / 1.048576e6); +#endif +} + +#if IMAGE_ENABLE_TIMESTAMP +void genimg_print_time(time_t timestamp) +{ +#ifndef USE_HOSTCC + struct rtc_time tm; + + to_tm(timestamp, &tm); + printf("%4d-%02d-%02d %2d:%02d:%02d UTC\n", + tm.tm_year, tm.tm_mon, tm.tm_mday, + tm.tm_hour, tm.tm_min, tm.tm_sec); +#else + printf("%s", ctime(×tamp)); +#endif +} +#endif + +/** + * get_table_entry_name - translate entry id to long name + * @table: pointer to a translation table for entries of a specific type + * @msg: message to be returned when translation fails + * @id: entry id to be translated + * + * get_table_entry_name() will go over translation table trying to find + * entry that matches given id. If matching entry is found, its long + * name is returned to the caller. + * + * returns: + * long entry name if translation succeeds + * msg otherwise + */ +char *get_table_entry_name(const table_entry_t *table, char *msg, int id) +{ + for (; table->id >= 0; ++table) { + if (table->id == id) +#if defined(USE_HOSTCC) || !defined(CONFIG_NEEDS_MANUAL_RELOC) + return table->lname; +#else + return table->lname + gd->reloc_off; +#endif + } + return (msg); +} + +const char *genimg_get_os_name(uint8_t os) +{ + return (get_table_entry_name(uimage_os, "Unknown OS", os)); +} + +const char *genimg_get_arch_name(uint8_t arch) +{ + return (get_table_entry_name(uimage_arch, "Unknown Architecture", + arch)); +} + +const char *genimg_get_type_name(uint8_t type) +{ + return (get_table_entry_name(uimage_type, "Unknown Image", type)); +} + +const char *genimg_get_comp_name(uint8_t comp) +{ + return (get_table_entry_name(uimage_comp, "Unknown Compression", + comp)); +} + +/** + * get_table_entry_id - translate short entry name to id + * @table: pointer to a translation table for entries of a specific type + * @table_name: to be used in case of error + * @name: entry short name to be translated + * + * get_table_entry_id() will go over translation table trying to find + * entry that matches given short name. If matching entry is found, + * its id returned to the caller. + * + * returns: + * entry id if translation succeeds + * -1 otherwise + */ +int get_table_entry_id(const table_entry_t *table, + const char *table_name, const char *name) +{ + const table_entry_t *t; +#ifdef USE_HOSTCC + int first = 1; + + for (t = table; t->id >= 0; ++t) { + if (t->sname && strcasecmp(t->sname, name) == 0) + return(t->id); + } + + fprintf(stderr, "\nInvalid %s Type - valid names are", table_name); + for (t = table; t->id >= 0; ++t) { + if (t->sname == NULL) + continue; + fprintf(stderr, "%c %s", (first) ? ':' : ',', t->sname); + first = 0; + } + fprintf(stderr, "\n"); +#else + for (t = table; t->id >= 0; ++t) { +#ifdef CONFIG_NEEDS_MANUAL_RELOC + if (t->sname && strcmp(t->sname + gd->reloc_off, name) == 0) +#else + if (t->sname && strcmp(t->sname, name) == 0) +#endif + return (t->id); + } + debug("Invalid %s Type: %s\n", table_name, name); +#endif /* USE_HOSTCC */ + return (-1); +} + +int genimg_get_os_id(const char *name) +{ + return (get_table_entry_id(uimage_os, "OS", name)); +} + +int genimg_get_arch_id(const char *name) +{ + return (get_table_entry_id(uimage_arch, "CPU", name)); +} + +int genimg_get_type_id(const char *name) +{ + return (get_table_entry_id(uimage_type, "Image", name)); +} + +int genimg_get_comp_id(const char *name) +{ + return (get_table_entry_id(uimage_comp, "Compression", name)); +} + +#ifndef USE_HOSTCC +/** + * genimg_get_format - get image format type + * @img_addr: image start address + * + * genimg_get_format() checks whether provided address points to a valid + * legacy or FIT image. + * + * New uImage format and FDT blob are based on a libfdt. FDT blob + * may be passed directly or embedded in a FIT image. In both situations + * genimg_get_format() must be able to dectect libfdt header. + * + * returns: + * image format type or IMAGE_FORMAT_INVALID if no image is present + */ +int genimg_get_format(const void *img_addr) +{ + ulong format = IMAGE_FORMAT_INVALID; + const image_header_t *hdr; + + hdr = (const image_header_t *)img_addr; + if (image_check_magic(hdr)) + format = IMAGE_FORMAT_LEGACY; +#if defined(CONFIG_FIT) || defined(CONFIG_OF_LIBFDT) + else { + if (fdt_check_header(img_addr) == 0) + format = IMAGE_FORMAT_FIT; + } +#endif + + return format; +} + +/** + * genimg_get_image - get image from special storage (if necessary) + * @img_addr: image start address + * + * genimg_get_image() checks if provided image start adddress is located + * in a dataflash storage. If so, image is moved to a system RAM memory. + * + * returns: + * image start address after possible relocation from special storage + */ +ulong genimg_get_image(ulong img_addr) +{ + ulong ram_addr = img_addr; + +#ifdef CONFIG_HAS_DATAFLASH + ulong h_size, d_size; + + if (addr_dataflash(img_addr)) { + void *buf; + + /* ger RAM address */ + ram_addr = CONFIG_SYS_LOAD_ADDR; + + /* get header size */ + h_size = image_get_header_size(); +#if defined(CONFIG_FIT) + if (sizeof(struct fdt_header) > h_size) + h_size = sizeof(struct fdt_header); +#endif + + /* read in header */ + debug(" Reading image header from dataflash address " + "%08lx to RAM address %08lx\n", img_addr, ram_addr); + + buf = map_sysmem(ram_addr, 0); + read_dataflash(img_addr, h_size, buf); + + /* get data size */ + switch (genimg_get_format(buf)) { + case IMAGE_FORMAT_LEGACY: + d_size = image_get_data_size(buf); + debug(" Legacy format image found at 0x%08lx, " + "size 0x%08lx\n", + ram_addr, d_size); + break; +#if defined(CONFIG_FIT) + case IMAGE_FORMAT_FIT: + d_size = fit_get_size(buf) - h_size; + debug(" FIT/FDT format image found at 0x%08lx, " + "size 0x%08lx\n", + ram_addr, d_size); + break; +#endif + default: + printf(" No valid image found at 0x%08lx\n", + img_addr); + return ram_addr; + } + + /* read in image data */ + debug(" Reading image remaining data from dataflash address " + "%08lx to RAM address %08lx\n", img_addr + h_size, + ram_addr + h_size); + + read_dataflash(img_addr + h_size, d_size, + (char *)(buf + h_size)); + + } +#endif /* CONFIG_HAS_DATAFLASH */ + + return ram_addr; +} + +/** + * fit_has_config - check if there is a valid FIT configuration + * @images: pointer to the bootm command headers structure + * + * fit_has_config() checks if there is a FIT configuration in use + * (if FTI support is present). + * + * returns: + * 0, no FIT support or no configuration found + * 1, configuration found + */ +int genimg_has_config(bootm_headers_t *images) +{ +#if defined(CONFIG_FIT) + if (images->fit_uname_cfg) + return 1; +#endif + return 0; +} + +/** + * boot_get_ramdisk - main ramdisk handling routine + * @argc: command argument count + * @argv: command argument list + * @images: pointer to the bootm images structure + * @arch: expected ramdisk architecture + * @rd_start: pointer to a ulong variable, will hold ramdisk start address + * @rd_end: pointer to a ulong variable, will hold ramdisk end + * + * boot_get_ramdisk() is responsible for finding a valid ramdisk image. + * Curently supported are the following ramdisk sources: + * - multicomponent kernel/ramdisk image, + * - commandline provided address of decicated ramdisk image. + * + * returns: + * 0, if ramdisk image was found and valid, or skiped + * rd_start and rd_end are set to ramdisk start/end addresses if + * ramdisk image is found and valid + * + * 1, if ramdisk image is found but corrupted, or invalid + * rd_start and rd_end are set to 0 if no ramdisk exists + */ +int boot_get_ramdisk(int argc, char * const argv[], bootm_headers_t *images, + uint8_t arch, ulong *rd_start, ulong *rd_end) +{ + ulong rd_addr, rd_load; + ulong rd_data, rd_len; + const image_header_t *rd_hdr; + void *buf; +#ifdef CONFIG_SUPPORT_RAW_INITRD + char *end; +#endif +#if defined(CONFIG_FIT) + const char *fit_uname_config = images->fit_uname_cfg; + const char *fit_uname_ramdisk = NULL; + ulong default_addr; + int rd_noffset; +#endif + const char *select = NULL; + + *rd_start = 0; + *rd_end = 0; + + if (argc >= 2) + select = argv[1]; + /* + * Look for a '-' which indicates to ignore the + * ramdisk argument + */ + if (select && strcmp(select, "-") == 0) { + debug("## Skipping init Ramdisk\n"); + rd_len = rd_data = 0; + } else if (select || genimg_has_config(images)) { +#if defined(CONFIG_FIT) + if (select) { + /* + * If the init ramdisk comes from the FIT image and + * the FIT image address is omitted in the command + * line argument, try to use os FIT image address or + * default load address. + */ + if (images->fit_uname_os) + default_addr = (ulong)images->fit_hdr_os; + else + default_addr = load_addr; + + if (fit_parse_conf(select, default_addr, + &rd_addr, &fit_uname_config)) { + debug("* ramdisk: config '%s' from image at " + "0x%08lx\n", + fit_uname_config, rd_addr); + } else if (fit_parse_subimage(select, default_addr, + &rd_addr, &fit_uname_ramdisk)) { + debug("* ramdisk: subimage '%s' from image at " + "0x%08lx\n", + fit_uname_ramdisk, rd_addr); + } else +#endif + { + rd_addr = simple_strtoul(select, NULL, 16); + debug("* ramdisk: cmdline image address = " + "0x%08lx\n", + rd_addr); + } +#if defined(CONFIG_FIT) + } else { + /* use FIT configuration provided in first bootm + * command argument. If the property is not defined, + * quit silently. + */ + rd_addr = map_to_sysmem(images->fit_hdr_os); + rd_noffset = fit_get_node_from_config(images, + FIT_RAMDISK_PROP, rd_addr); + if (rd_noffset == -ENOLINK) + return 0; + else if (rd_noffset < 0) + return 1; + } +#endif + + /* copy from dataflash if needed */ + rd_addr = genimg_get_image(rd_addr); + + /* + * Check if there is an initrd image at the + * address provided in the second bootm argument + * check image type, for FIT images get FIT node. + */ + buf = map_sysmem(rd_addr, 0); + switch (genimg_get_format(buf)) { + case IMAGE_FORMAT_LEGACY: + printf("## Loading init Ramdisk from Legacy " + "Image at %08lx ...\n", rd_addr); + + bootstage_mark(BOOTSTAGE_ID_CHECK_RAMDISK); + rd_hdr = image_get_ramdisk(rd_addr, arch, + images->verify); + + if (rd_hdr == NULL) + return 1; + + rd_data = image_get_data(rd_hdr); + rd_len = image_get_data_size(rd_hdr); + rd_load = image_get_load(rd_hdr); + break; +#if defined(CONFIG_FIT) + case IMAGE_FORMAT_FIT: + rd_noffset = fit_image_load(images, FIT_RAMDISK_PROP, + rd_addr, &fit_uname_ramdisk, + &fit_uname_config, arch, + IH_TYPE_RAMDISK, + BOOTSTAGE_ID_FIT_RD_START, + FIT_LOAD_IGNORED, &rd_data, &rd_len); + if (rd_noffset < 0) + return 1; + + images->fit_hdr_rd = map_sysmem(rd_addr, 0); + images->fit_uname_rd = fit_uname_ramdisk; + images->fit_noffset_rd = rd_noffset; + break; +#endif + default: +#ifdef CONFIG_SUPPORT_RAW_INITRD + end = NULL; + if (select) + end = strchr(select, ':'); + if (end) { + rd_len = simple_strtoul(++end, NULL, 16); + rd_data = rd_addr; + } else +#endif + { + puts("Wrong Ramdisk Image Format\n"); + rd_data = rd_len = rd_load = 0; + return 1; + } + } + } else if (images->legacy_hdr_valid && + image_check_type(&images->legacy_hdr_os_copy, + IH_TYPE_MULTI)) { + + /* + * Now check if we have a legacy mult-component image, + * get second entry data start address and len. + */ + bootstage_mark(BOOTSTAGE_ID_RAMDISK); + printf("## Loading init Ramdisk from multi component " + "Legacy Image at %08lx ...\n", + (ulong)images->legacy_hdr_os); + + image_multi_getimg(images->legacy_hdr_os, 1, &rd_data, &rd_len); + } else { + /* + * no initrd image + */ + bootstage_mark(BOOTSTAGE_ID_NO_RAMDISK); + rd_len = rd_data = 0; + } + + if (!rd_data) { + debug("## No init Ramdisk\n"); + } else { + *rd_start = rd_data; + *rd_end = rd_data + rd_len; + } + debug(" ramdisk start = 0x%08lx, ramdisk end = 0x%08lx\n", + *rd_start, *rd_end); + + return 0; +} + +#ifdef CONFIG_SYS_BOOT_RAMDISK_HIGH +/** + * boot_ramdisk_high - relocate init ramdisk + * @lmb: pointer to lmb handle, will be used for memory mgmt + * @rd_data: ramdisk data start address + * @rd_len: ramdisk data length + * @initrd_start: pointer to a ulong variable, will hold final init ramdisk + * start address (after possible relocation) + * @initrd_end: pointer to a ulong variable, will hold final init ramdisk + * end address (after possible relocation) + * + * boot_ramdisk_high() takes a relocation hint from "initrd_high" environment + * variable and if requested ramdisk data is moved to a specified location. + * + * Initrd_start and initrd_end are set to final (after relocation) ramdisk + * start/end addresses if ramdisk image start and len were provided, + * otherwise set initrd_start and initrd_end set to zeros. + * + * returns: + * 0 - success + * -1 - failure + */ +int boot_ramdisk_high(struct lmb *lmb, ulong rd_data, ulong rd_len, + ulong *initrd_start, ulong *initrd_end) +{ + char *s; + ulong initrd_high; + int initrd_copy_to_ram = 1; + + if ((s = getenv("initrd_high")) != NULL) { + /* a value of "no" or a similar string will act like 0, + * turning the "load high" feature off. This is intentional. + */ + initrd_high = simple_strtoul(s, NULL, 16); + if (initrd_high == ~0) + initrd_copy_to_ram = 0; + } else { + /* not set, no restrictions to load high */ + initrd_high = ~0; + } + + +#ifdef CONFIG_LOGBUFFER + /* Prevent initrd from overwriting logbuffer */ + lmb_reserve(lmb, logbuffer_base() - LOGBUFF_OVERHEAD, LOGBUFF_RESERVE); +#endif + + debug("## initrd_high = 0x%08lx, copy_to_ram = %d\n", + initrd_high, initrd_copy_to_ram); + + if (rd_data) { + if (!initrd_copy_to_ram) { /* zero-copy ramdisk support */ + debug(" in-place initrd\n"); + *initrd_start = rd_data; + *initrd_end = rd_data + rd_len; + lmb_reserve(lmb, rd_data, rd_len); + } else { + if (initrd_high) + *initrd_start = (ulong)lmb_alloc_base(lmb, + rd_len, 0x1000, initrd_high); + else + *initrd_start = (ulong)lmb_alloc(lmb, rd_len, + 0x1000); + + if (*initrd_start == 0) { + puts("ramdisk - allocation error\n"); + goto error; + } + bootstage_mark(BOOTSTAGE_ID_COPY_RAMDISK); + + *initrd_end = *initrd_start + rd_len; + printf(" Loading Ramdisk to %08lx, end %08lx ... ", + *initrd_start, *initrd_end); + + memmove_wd((void *)*initrd_start, + (void *)rd_data, rd_len, CHUNKSZ); + +#ifdef CONFIG_MP + /* + * Ensure the image is flushed to memory to handle + * AMP boot scenarios in which we might not be + * HW cache coherent + */ + flush_cache((unsigned long)*initrd_start, rd_len); +#endif + puts("OK\n"); + } + } else { + *initrd_start = 0; + *initrd_end = 0; + } + debug(" ramdisk load start = 0x%08lx, ramdisk load end = 0x%08lx\n", + *initrd_start, *initrd_end); + + return 0; + +error: + return -1; +} +#endif /* CONFIG_SYS_BOOT_RAMDISK_HIGH */ + +#ifdef CONFIG_SYS_BOOT_GET_CMDLINE +/** + * boot_get_cmdline - allocate and initialize kernel cmdline + * @lmb: pointer to lmb handle, will be used for memory mgmt + * @cmd_start: pointer to a ulong variable, will hold cmdline start + * @cmd_end: pointer to a ulong variable, will hold cmdline end + * + * boot_get_cmdline() allocates space for kernel command line below + * BOOTMAPSZ + getenv_bootm_low() address. If "bootargs" U-boot environemnt + * variable is present its contents is copied to allocated kernel + * command line. + * + * returns: + * 0 - success + * -1 - failure + */ +int boot_get_cmdline(struct lmb *lmb, ulong *cmd_start, ulong *cmd_end) +{ + char *cmdline; + char *s; + + cmdline = (char *)(ulong)lmb_alloc_base(lmb, CONFIG_SYS_BARGSIZE, 0xf, + getenv_bootm_mapsize() + getenv_bootm_low()); + + if (cmdline == NULL) + return -1; + + if ((s = getenv("bootargs")) == NULL) + s = ""; + + strcpy(cmdline, s); + + *cmd_start = (ulong) & cmdline[0]; + *cmd_end = *cmd_start + strlen(cmdline); + + debug("## cmdline at 0x%08lx ... 0x%08lx\n", *cmd_start, *cmd_end); + + return 0; +} +#endif /* CONFIG_SYS_BOOT_GET_CMDLINE */ + +#ifdef CONFIG_SYS_BOOT_GET_KBD +/** + * boot_get_kbd - allocate and initialize kernel copy of board info + * @lmb: pointer to lmb handle, will be used for memory mgmt + * @kbd: double pointer to board info data + * + * boot_get_kbd() allocates space for kernel copy of board info data below + * BOOTMAPSZ + getenv_bootm_low() address and kernel board info is initialized + * with the current u-boot board info data. + * + * returns: + * 0 - success + * -1 - failure + */ +int boot_get_kbd(struct lmb *lmb, bd_t **kbd) +{ + *kbd = (bd_t *)(ulong)lmb_alloc_base(lmb, sizeof(bd_t), 0xf, + getenv_bootm_mapsize() + getenv_bootm_low()); + if (*kbd == NULL) + return -1; + + **kbd = *(gd->bd); + + debug("## kernel board info at 0x%08lx\n", (ulong)*kbd); + +#if defined(DEBUG) && defined(CONFIG_CMD_BDI) + do_bdinfo(NULL, 0, 0, NULL); +#endif + + return 0; +} +#endif /* CONFIG_SYS_BOOT_GET_KBD */ + +#ifdef CONFIG_LMB +int image_setup_linux(bootm_headers_t *images) +{ + ulong of_size = images->ft_len; + char **of_flat_tree = &images->ft_addr; + ulong *initrd_start = &images->initrd_start; + ulong *initrd_end = &images->initrd_end; + struct lmb *lmb = &images->lmb; + ulong rd_len; + int ret; + + if (IMAGE_ENABLE_OF_LIBFDT) + boot_fdt_add_mem_rsv_regions(lmb, *of_flat_tree); + + if (IMAGE_BOOT_GET_CMDLINE) { + ret = boot_get_cmdline(lmb, &images->cmdline_start, + &images->cmdline_end); + if (ret) { + puts("ERROR with allocation of cmdline\n"); + return ret; + } + } + if (IMAGE_ENABLE_RAMDISK_HIGH) { + rd_len = images->rd_end - images->rd_start; + ret = boot_ramdisk_high(lmb, images->rd_start, rd_len, + initrd_start, initrd_end); + if (ret) + return ret; + } + + if (IMAGE_ENABLE_OF_LIBFDT) { + ret = boot_relocate_fdt(lmb, of_flat_tree, &of_size); + if (ret) + return ret; + } + + if (IMAGE_ENABLE_OF_LIBFDT && of_size) { + ret = image_setup_libfdt(images, *of_flat_tree, of_size, lmb); + if (ret) + return ret; + } + + return 0; +} +#endif /* CONFIG_LMB */ +#endif /* !USE_HOSTCC */ diff --git a/qemu/roms/u-boot/common/iomux.c b/qemu/roms/u-boot/common/iomux.c new file mode 100644 index 000000000..62bdec61a --- /dev/null +++ b/qemu/roms/u-boot/common/iomux.c @@ -0,0 +1,147 @@ +/* + * (C) Copyright 2008 + * Gary Jennejohn, DENX Software Engineering GmbH, garyj@denx.de. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <serial.h> +#include <malloc.h> + +#ifdef CONFIG_CONSOLE_MUX +void iomux_printdevs(const int console) +{ + int i; + struct stdio_dev *dev; + + for (i = 0; i < cd_count[console]; i++) { + dev = console_devices[console][i]; + printf("%s ", dev->name); + } + printf("\n"); +} + +/* This tries to preserve the old list if an error occurs. */ +int iomux_doenv(const int console, const char *arg) +{ + char *console_args, *temp, **start; + int i, j, k, io_flag, cs_idx, repeat; + struct stdio_dev *dev; + struct stdio_dev **cons_set; + + console_args = strdup(arg); + if (console_args == NULL) + return 1; + /* + * Check whether a comma separated list of devices was + * entered and count how many devices were entered. + * The array start[] has pointers to the beginning of + * each device name (up to MAX_CONSARGS devices). + * + * Have to do this twice - once to count the number of + * commas and then again to populate start. + */ + i = 0; + temp = console_args; + for (;;) { + temp = strchr(temp, ','); + if (temp != NULL) { + i++; + temp++; + continue; + } + /* There's always one entry more than the number of commas. */ + i++; + break; + } + start = (char **)malloc(i * sizeof(char *)); + if (start == NULL) { + free(console_args); + return 1; + } + i = 0; + start[0] = console_args; + for (;;) { + temp = strchr(start[i++], ','); + if (temp == NULL) + break; + *temp = '\0'; + start[i] = temp + 1; + } + cons_set = (struct stdio_dev **)calloc(i, sizeof(struct stdio_dev *)); + if (cons_set == NULL) { + free(start); + free(console_args); + return 1; + } + + switch (console) { + case stdin: + io_flag = DEV_FLAGS_INPUT; + break; + case stdout: + case stderr: + io_flag = DEV_FLAGS_OUTPUT; + break; + default: + free(start); + free(console_args); + free(cons_set); + return 1; + } + + cs_idx = 0; + for (j = 0; j < i; j++) { + /* + * Check whether the device exists and is valid. + * console_assign() also calls search_device(), + * but I need the pointer to the device. + */ + dev = search_device(io_flag, start[j]); + if (dev == NULL) + continue; + /* + * Prevent multiple entries for a device. + */ + repeat = 0; + for (k = 0; k < cs_idx; k++) { + if (dev == cons_set[k]) { + repeat++; + break; + } + } + if (repeat) + continue; + /* + * Try assigning the specified device. + * This could screw up the console settings for apps. + */ + if (console_assign(console, start[j]) < 0) + continue; + cons_set[cs_idx++] = dev; + } + free(console_args); + free(start); + /* failed to set any console */ + if (cs_idx == 0) { + free(cons_set); + return 1; + } else { + /* Works even if console_devices[console] is NULL. */ + console_devices[console] = + (struct stdio_dev **)realloc(console_devices[console], + cs_idx * sizeof(struct stdio_dev *)); + if (console_devices[console] == NULL) { + free(cons_set); + return 1; + } + memcpy(console_devices[console], cons_set, cs_idx * + sizeof(struct stdio_dev *)); + + cd_count[console] = cs_idx; + } + free(cons_set); + return 0; +} +#endif /* CONFIG_CONSOLE_MUX */ diff --git a/qemu/roms/u-boot/common/kallsyms.c b/qemu/roms/u-boot/common/kallsyms.c new file mode 100644 index 000000000..ce42a932b --- /dev/null +++ b/qemu/roms/u-boot/common/kallsyms.c @@ -0,0 +1,44 @@ +/* + * Helper functions for working with the builtin symbol table + * + * Copyright (c) 2008-2009 Analog Devices Inc. + * Licensed under the GPL-2 or later. + */ + +#include <common.h> + +/* We need the weak marking as this symbol is provided specially */ +extern const char system_map[] __attribute__((weak)); + +/* Given an address, return a pointer to the symbol name and store + * the base address in caddr. So if the symbol map had an entry: + * 03fb9b7c_spi_cs_deactivate + * Then the following call: + * unsigned long base; + * const char *sym = symbol_lookup(0x03fb9b80, &base); + * Would end up setting the variables like so: + * base = 0x03fb9b7c; + * sym = "_spi_cs_deactivate"; + */ +const char *symbol_lookup(unsigned long addr, unsigned long *caddr) +{ + const char *sym, *csym; + char *esym; + unsigned long sym_addr; + + sym = system_map; + csym = NULL; + *caddr = 0; + + while (*sym) { + sym_addr = simple_strtoul(sym, &esym, 16); + sym = esym; + if (sym_addr > addr) + break; + *caddr = sym_addr; + csym = sym; + sym += strlen(sym) + 1; + } + + return csym; +} diff --git a/qemu/roms/u-boot/common/kgdb.c b/qemu/roms/u-boot/common/kgdb.c new file mode 100644 index 000000000..8a621ad1f --- /dev/null +++ b/qemu/roms/u-boot/common/kgdb.c @@ -0,0 +1,608 @@ +/* taken from arch/powerpc/kernel/ppc-stub.c */ + +/**************************************************************************** + + THIS SOFTWARE IS NOT COPYRIGHTED + + HP offers the following for use in the public domain. HP makes no + warranty with regard to the software or its performance and the + user accepts the software "AS IS" with all faults. + + HP DISCLAIMS ANY WARRANTIES, EXPRESS OR IMPLIED, WITH REGARD + TO THIS SOFTWARE INCLUDING BUT NOT LIMITED TO THE WARRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + +****************************************************************************/ + +/**************************************************************************** + * Header: remcom.c,v 1.34 91/03/09 12:29:49 glenne Exp $ + * + * Module name: remcom.c $ + * Revision: 1.34 $ + * Date: 91/03/09 12:29:49 $ + * Contributor: Lake Stevens Instrument Division$ + * + * Description: low level support for gdb debugger. $ + * + * Considerations: only works on target hardware $ + * + * Written by: Glenn Engel $ + * ModuleState: Experimental $ + * + * NOTES: See Below $ + * + * Modified for SPARC by Stu Grossman, Cygnus Support. + * + * This code has been extensively tested on the Fujitsu SPARClite demo board. + * + * To enable debugger support, two things need to happen. One, a + * call to set_debug_traps() is necessary in order to allow any breakpoints + * or error conditions to be properly intercepted and reported to gdb. + * Two, a breakpoint needs to be generated to begin communication. This + * is most easily accomplished by a call to breakpoint(). Breakpoint() + * simulates a breakpoint by executing a trap #1. + * + ************* + * + * The following gdb commands are supported: + * + * command function Return value + * + * g return the value of the CPU registers hex data or ENN + * G set the value of the CPU registers OK or ENN + * qOffsets Get section offsets. Reply is Text=xxx;Data=yyy;Bss=zzz + * + * mAA..AA,LLLL Read LLLL bytes at address AA..AA hex data or ENN + * MAA..AA,LLLL: Write LLLL bytes at address AA.AA OK or ENN + * + * c Resume at current address SNN ( signal NN) + * cAA..AA Continue at address AA..AA SNN + * + * s Step one instruction SNN + * sAA..AA Step one instruction from AA..AA SNN + * + * k kill + * + * ? What was the last sigval ? SNN (signal NN) + * + * bBB..BB Set baud rate to BB..BB OK or BNN, then sets + * baud rate + * + * All commands and responses are sent with a packet which includes a + * checksum. A packet consists of + * + * $<packet info>#<checksum>. + * + * where + * <packet info> :: <characters representing the command or response> + * <checksum> :: <two hex digits computed as modulo 256 sum of <packetinfo>> + * + * When a packet is received, it is first acknowledged with either '+' or '-'. + * '+' indicates a successful transfer. '-' indicates a failed transfer. + * + * Example: + * + * Host: Reply: + * $m0,10#2a +$00010203040506070809101112131415#42 + * + ****************************************************************************/ + +#include <common.h> + +#include <kgdb.h> +#include <command.h> + +#undef KGDB_DEBUG + +/* + * BUFMAX defines the maximum number of characters in inbound/outbound buffers + */ +#define BUFMAX 1024 +static char remcomInBuffer[BUFMAX]; +static char remcomOutBuffer[BUFMAX]; +static char remcomRegBuffer[BUFMAX]; + +static int initialized = 0; +static int kgdb_active = 0, first_entry = 1; +static struct pt_regs entry_regs; +static long error_jmp_buf[BUFMAX/2]; +static int longjmp_on_fault = 0; +#ifdef KGDB_DEBUG +static int kdebug = 1; +#endif + +static const char hexchars[]="0123456789abcdef"; + +/* Convert ch from a hex digit to an int */ +static int +hex(unsigned char ch) +{ + if (ch >= 'a' && ch <= 'f') + return ch-'a'+10; + if (ch >= '0' && ch <= '9') + return ch-'0'; + if (ch >= 'A' && ch <= 'F') + return ch-'A'+10; + return -1; +} + +/* Convert the memory pointed to by mem into hex, placing result in buf. + * Return a pointer to the last char put in buf (null). + */ +static unsigned char * +mem2hex(char *mem, char *buf, int count) +{ + char *tmp; + unsigned char ch; + + /* + * We use the upper half of buf as an intermediate buffer for the + * raw memory copy. Hex conversion will work against this one. + */ + tmp = buf + count; + longjmp_on_fault = 1; + + memcpy(tmp, mem, count); + + while (count-- > 0) { + ch = *tmp++; + *buf++ = hexchars[ch >> 4]; + *buf++ = hexchars[ch & 0xf]; + } + *buf = 0; + longjmp_on_fault = 0; + return (unsigned char *)buf; +} + +/* convert the hex array pointed to by buf into binary to be placed in mem + * return a pointer to the character AFTER the last byte fetched from buf. +*/ +static char * +hex2mem(char *buf, char *mem, int count) +{ + int hexValue; + char *tmp_raw, *tmp_hex; + + /* + * We use the upper half of buf as an intermediate buffer for the + * raw memory that is converted from hex. + */ + tmp_raw = buf + count * 2; + tmp_hex = tmp_raw - 1; + + longjmp_on_fault = 1; + while (tmp_hex >= buf) { + tmp_raw--; + hexValue = hex(*tmp_hex--); + if (hexValue < 0) + kgdb_error(KGDBERR_NOTHEXDIG); + *tmp_raw = hexValue; + hexValue = hex(*tmp_hex--); + if (hexValue < 0) + kgdb_error(KGDBERR_NOTHEXDIG); + *tmp_raw |= hexValue << 4; + + } + + memcpy(mem, tmp_raw, count); + + kgdb_flush_cache_range((void *)mem, (void *)(mem+count)); + longjmp_on_fault = 0; + + return buf; +} + +/* + * While we find nice hex chars, build an int. + * Return number of chars processed. + */ +static int +hexToInt(char **ptr, int *intValue) +{ + int numChars = 0; + int hexValue; + + *intValue = 0; + + longjmp_on_fault = 1; + while (**ptr) { + hexValue = hex(**ptr); + if (hexValue < 0) + break; + + *intValue = (*intValue << 4) | hexValue; + numChars ++; + + (*ptr)++; + } + longjmp_on_fault = 0; + + return (numChars); +} + +/* scan for the sequence $<data>#<checksum> */ +static void +getpacket(char *buffer) +{ + unsigned char checksum; + unsigned char xmitcsum; + int i; + int count; + unsigned char ch; + + do { + /* wait around for the start character, ignore all other + * characters */ + while ((ch = (getDebugChar() & 0x7f)) != '$') { +#ifdef KGDB_DEBUG + if (kdebug) + putc(ch); +#endif + ; + } + + checksum = 0; + xmitcsum = -1; + + count = 0; + + /* now, read until a # or end of buffer is found */ + while (count < BUFMAX) { + ch = getDebugChar() & 0x7f; + if (ch == '#') + break; + checksum = checksum + ch; + buffer[count] = ch; + count = count + 1; + } + + if (count >= BUFMAX) + continue; + + buffer[count] = 0; + + if (ch == '#') { + xmitcsum = hex(getDebugChar() & 0x7f) << 4; + xmitcsum |= hex(getDebugChar() & 0x7f); + if (checksum != xmitcsum) + putDebugChar('-'); /* failed checksum */ + else { + putDebugChar('+'); /* successful transfer */ + /* if a sequence char is present, reply the ID */ + if (buffer[2] == ':') { + putDebugChar(buffer[0]); + putDebugChar(buffer[1]); + /* remove sequence chars from buffer */ + count = strlen(buffer); + for (i=3; i <= count; i++) + buffer[i-3] = buffer[i]; + } + } + } + } while (checksum != xmitcsum); +} + +/* send the packet in buffer. */ +static void +putpacket(unsigned char *buffer) +{ + unsigned char checksum; + int count; + unsigned char ch, recv; + + /* $<packet info>#<checksum>. */ + do { + putDebugChar('$'); + checksum = 0; + count = 0; + + while ((ch = buffer[count])) { + putDebugChar(ch); + checksum += ch; + count += 1; + } + + putDebugChar('#'); + putDebugChar(hexchars[checksum >> 4]); + putDebugChar(hexchars[checksum & 0xf]); + recv = getDebugChar(); + } while ((recv & 0x7f) != '+'); +} + +/* + * This function does all command processing for interfacing to gdb. + */ +static int +handle_exception (struct pt_regs *regs) +{ + int addr; + int length; + char *ptr; + kgdb_data kd; + int i; + + if (!initialized) { + printf("kgdb: exception before kgdb is initialized! huh?\n"); + return (0); + } + + /* probably should check which exception occured as well */ + if (longjmp_on_fault) { + longjmp_on_fault = 0; + kgdb_longjmp(error_jmp_buf, KGDBERR_MEMFAULT); + panic("kgdb longjump failed!\n"); + } + + if (kgdb_active) { + printf("kgdb: unexpected exception from within kgdb\n"); + return (0); + } + kgdb_active = 1; + + kgdb_interruptible(0); + + printf("kgdb: handle_exception; trap [0x%x]\n", kgdb_trap(regs)); + + if (kgdb_setjmp(error_jmp_buf) != 0) + panic("kgdb: error or fault in entry init!\n"); + + kgdb_enter(regs, &kd); + + if (first_entry) { + /* + * the first time we enter kgdb, we save the processor + * state so that we can return to the monitor if the + * remote end quits gdb (or at least, tells us to quit + * with the 'k' packet) + */ + entry_regs = *regs; + first_entry = 0; + } + + ptr = remcomOutBuffer; + + *ptr++ = 'T'; + + *ptr++ = hexchars[kd.sigval >> 4]; + *ptr++ = hexchars[kd.sigval & 0xf]; + + for (i = 0; i < kd.nregs; i++) { + kgdb_reg *rp = &kd.regs[i]; + + *ptr++ = hexchars[rp->num >> 4]; + *ptr++ = hexchars[rp->num & 0xf]; + *ptr++ = ':'; + ptr = (char *)mem2hex((char *)&rp->val, ptr, 4); + *ptr++ = ';'; + } + + *ptr = 0; + +#ifdef KGDB_DEBUG + if (kdebug) + printf("kgdb: remcomOutBuffer: %s\n", remcomOutBuffer); +#endif + + putpacket((unsigned char *)&remcomOutBuffer); + + while (1) { + volatile int errnum; + + remcomOutBuffer[0] = 0; + + getpacket(remcomInBuffer); + ptr = &remcomInBuffer[1]; + +#ifdef KGDB_DEBUG + if (kdebug) + printf("kgdb: remcomInBuffer: %s\n", remcomInBuffer); +#endif + + errnum = kgdb_setjmp(error_jmp_buf); + + if (errnum == 0) switch (remcomInBuffer[0]) { + + case '?': /* report most recent signal */ + remcomOutBuffer[0] = 'S'; + remcomOutBuffer[1] = hexchars[kd.sigval >> 4]; + remcomOutBuffer[2] = hexchars[kd.sigval & 0xf]; + remcomOutBuffer[3] = 0; + break; + +#ifdef KGDB_DEBUG + case 'd': + /* toggle debug flag */ + kdebug ^= 1; + break; +#endif + + case 'g': /* return the value of the CPU registers. */ + length = kgdb_getregs(regs, remcomRegBuffer, BUFMAX); + mem2hex(remcomRegBuffer, remcomOutBuffer, length); + break; + + case 'G': /* set the value of the CPU registers */ + length = strlen(ptr); + if ((length & 1) != 0) kgdb_error(KGDBERR_BADPARAMS); + hex2mem(ptr, remcomRegBuffer, length/2); + kgdb_putregs(regs, remcomRegBuffer, length/2); + strcpy(remcomOutBuffer,"OK"); + break; + + case 'm': /* mAA..AA,LLLL Read LLLL bytes at address AA..AA */ + /* Try to read %x,%x. */ + + if (hexToInt(&ptr, &addr) + && *ptr++ == ',' + && hexToInt(&ptr, &length)) { + mem2hex((char *)addr, remcomOutBuffer, length); + } else { + kgdb_error(KGDBERR_BADPARAMS); + } + break; + + case 'M': /* MAA..AA,LLLL: Write LLLL bytes at address AA.AA return OK */ + /* Try to read '%x,%x:'. */ + + if (hexToInt(&ptr, &addr) + && *ptr++ == ',' + && hexToInt(&ptr, &length) + && *ptr++ == ':') { + hex2mem(ptr, (char *)addr, length); + strcpy(remcomOutBuffer, "OK"); + } else { + kgdb_error(KGDBERR_BADPARAMS); + } + break; + + + case 'k': /* kill the program, actually return to monitor */ + kd.extype = KGDBEXIT_KILL; + *regs = entry_regs; + first_entry = 1; + goto doexit; + + case 'C': /* CSS continue with signal SS */ + *ptr = '\0'; /* ignore the signal number for now */ + /* fall through */ + + case 'c': /* cAA..AA Continue; address AA..AA optional */ + /* try to read optional parameter, pc unchanged if no parm */ + kd.extype = KGDBEXIT_CONTINUE; + + if (hexToInt(&ptr, &addr)) { + kd.exaddr = addr; + kd.extype |= KGDBEXIT_WITHADDR; + } + + goto doexit; + + case 'S': /* SSS single step with signal SS */ + *ptr = '\0'; /* ignore the signal number for now */ + /* fall through */ + + case 's': + kd.extype = KGDBEXIT_SINGLE; + + if (hexToInt(&ptr, &addr)) { + kd.exaddr = addr; + kd.extype |= KGDBEXIT_WITHADDR; + } + + doexit: +/* Need to flush the instruction cache here, as we may have deposited a + * breakpoint, and the icache probably has no way of knowing that a data ref to + * some location may have changed something that is in the instruction cache. + */ + kgdb_flush_cache_all(); + kgdb_exit(regs, &kd); + kgdb_active = 0; + kgdb_interruptible(1); + return (1); + + case 'r': /* Reset (if user process..exit ???)*/ + panic("kgdb reset."); + break; + + case 'P': /* Pr=v set reg r to value v (r and v are hex) */ + if (hexToInt(&ptr, &addr) + && *ptr++ == '=' + && ((length = strlen(ptr)) & 1) == 0) { + hex2mem(ptr, remcomRegBuffer, length/2); + kgdb_putreg(regs, addr, + remcomRegBuffer, length/2); + strcpy(remcomOutBuffer,"OK"); + } else { + kgdb_error(KGDBERR_BADPARAMS); + } + break; + } /* switch */ + + if (errnum != 0) + sprintf(remcomOutBuffer, "E%02d", errnum); + +#ifdef KGDB_DEBUG + if (kdebug) + printf("kgdb: remcomOutBuffer: %s\n", remcomOutBuffer); +#endif + + /* reply to the request */ + putpacket((unsigned char *)&remcomOutBuffer); + + } /* while(1) */ +} + +/* + * kgdb_init must be called *after* the + * monitor is relocated into ram + */ +void +kgdb_init(void) +{ + kgdb_serial_init(); + debugger_exception_handler = handle_exception; + initialized = 1; + + putDebugStr("kgdb ready\n"); + puts("ready\n"); +} + +void +kgdb_error(int errnum) +{ + longjmp_on_fault = 0; + kgdb_longjmp(error_jmp_buf, errnum); + panic("kgdb_error: longjmp failed!\n"); +} + +/* Output string in GDB O-packet format if GDB has connected. If nothing + output, returns 0 (caller must then handle output). */ +int +kgdb_output_string (const char* s, unsigned int count) +{ + char buffer[512]; + + count = (count <= (sizeof(buffer) / 2 - 2)) + ? count : (sizeof(buffer) / 2 - 2); + + buffer[0] = 'O'; + mem2hex ((char *)s, &buffer[1], count); + putpacket((unsigned char *)&buffer); + + return 1; +} + +void +breakpoint(void) +{ + if (!initialized) { + printf("breakpoint() called b4 kgdb init\n"); + return; + } + + kgdb_breakpoint(0, 0); +} + +int +do_kgdb(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + printf("Entering KGDB mode via exception handler...\n\n"); + kgdb_breakpoint(argc - 1, argv + 1); + printf("\nReturned from KGDB mode\n"); + return 0; +} + +U_BOOT_CMD( + kgdb, CONFIG_SYS_MAXARGS, 1, do_kgdb, + "enter gdb remote debug mode", + "[arg0 arg1 .. argN]\n" + " - executes a breakpoint so that kgdb mode is\n" + " entered via the exception handler. To return\n" + " to the monitor, the remote gdb debugger must\n" + " execute a \"continue\" or \"quit\" command.\n" + "\n" + " if a program is loaded by the remote gdb, any args\n" + " passed to the kgdb command are given to the loaded\n" + " program if it is executed (see the \"hello_world\"\n" + " example program in the U-Boot examples directory)." +); diff --git a/qemu/roms/u-boot/common/kgdb_stubs.c b/qemu/roms/u-boot/common/kgdb_stubs.c new file mode 100644 index 000000000..19b0c1824 --- /dev/null +++ b/qemu/roms/u-boot/common/kgdb_stubs.c @@ -0,0 +1,64 @@ +/* + * U-boot - stub functions for common kgdb code, + * can be overridden in board specific files + * + * Copyright 2009 Analog Devices Inc. + * + * Licensed under the GPL-2 or later. + */ + +#include <common.h> +#include <kgdb.h> + +int (*debugger_exception_handler)(struct pt_regs *); + +__attribute__((weak)) +void kgdb_serial_init(void) +{ + puts("[on serial] "); +} + +__attribute__((weak)) +void putDebugChar(int c) +{ + serial_putc(c); +} + +__attribute__((weak)) +void putDebugStr(const char *str) +{ +#ifdef DEBUG + serial_puts(str); +#endif +} + +__attribute__((weak)) +int getDebugChar(void) +{ + return serial_getc(); +} + +__attribute__((weak)) +void kgdb_interruptible(int yes) +{ + return; +} + +__attribute__((weak)) +void kgdb_flush_cache_range(void *from, void *to) +{ + flush_cache((unsigned long)from, (unsigned long)(to - from)); +} + +__attribute__((weak)) +void kgdb_flush_cache_all(void) +{ + if (dcache_status()) { + dcache_disable(); + dcache_enable(); + } + if (icache_status()) { + icache_disable(); + icache_enable(); + } +} diff --git a/qemu/roms/u-boot/common/lcd.c b/qemu/roms/u-boot/common/lcd.c new file mode 100644 index 000000000..19b86b7c5 --- /dev/null +++ b/qemu/roms/u-boot/common/lcd.c @@ -0,0 +1,1245 @@ +/* + * Common LCD routines for supported CPUs + * + * (C) Copyright 2001-2002 + * Wolfgang Denk, DENX Software Engineering -- wd@denx.de + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/************************************************************************/ +/* ** HEADER FILES */ +/************************************************************************/ + +/* #define DEBUG */ + +#include <config.h> +#include <common.h> +#include <command.h> +#include <stdarg.h> +#include <search.h> +#include <env_callback.h> +#include <linux/types.h> +#include <stdio_dev.h> +#if defined(CONFIG_POST) +#include <post.h> +#endif +#include <lcd.h> +#include <watchdog.h> +#include <asm/unaligned.h> +#include <splash.h> +#include <asm/io.h> +#include <asm/unaligned.h> + +#if defined(CONFIG_CPU_PXA25X) || defined(CONFIG_CPU_PXA27X) || \ + defined(CONFIG_CPU_MONAHANS) +#define CONFIG_CPU_PXA +#include <asm/byteorder.h> +#endif + +#if defined(CONFIG_MPC823) +#include <lcdvideo.h> +#endif + +#if defined(CONFIG_ATMEL_LCD) +#include <atmel_lcdc.h> +#endif + +#if defined(CONFIG_LCD_DT_SIMPLEFB) +#include <libfdt.h> +#endif + +/************************************************************************/ +/* ** FONT DATA */ +/************************************************************************/ +#include <video_font.h> /* Get font data, width and height */ + +/************************************************************************/ +/* ** LOGO DATA */ +/************************************************************************/ +#ifdef CONFIG_LCD_LOGO +# include <bmp_logo.h> /* Get logo data, width and height */ +# include <bmp_logo_data.h> +# if (CONSOLE_COLOR_WHITE >= BMP_LOGO_OFFSET) && (LCD_BPP != LCD_COLOR16) +# error Default Color Map overlaps with Logo Color Map +# endif +#endif + +#ifdef CONFIG_SANDBOX +#include <asm/sdl.h> +#endif + +#ifndef CONFIG_LCD_ALIGNMENT +#define CONFIG_LCD_ALIGNMENT PAGE_SIZE +#endif + +/* By default we scroll by a single line */ +#ifndef CONFIG_CONSOLE_SCROLL_LINES +#define CONFIG_CONSOLE_SCROLL_LINES 1 +#endif + +/************************************************************************/ +/* ** CONSOLE DEFINITIONS & FUNCTIONS */ +/************************************************************************/ +#if defined(CONFIG_LCD_LOGO) && !defined(CONFIG_LCD_INFO_BELOW_LOGO) +# define CONSOLE_ROWS ((panel_info.vl_row-BMP_LOGO_HEIGHT) \ + / VIDEO_FONT_HEIGHT) +#else +# define CONSOLE_ROWS (panel_info.vl_row / VIDEO_FONT_HEIGHT) +#endif + +#define CONSOLE_COLS (panel_info.vl_col / VIDEO_FONT_WIDTH) +#define CONSOLE_ROW_SIZE (VIDEO_FONT_HEIGHT * lcd_line_length) +#define CONSOLE_ROW_FIRST lcd_console_address +#define CONSOLE_ROW_SECOND (lcd_console_address + CONSOLE_ROW_SIZE) +#define CONSOLE_ROW_LAST (lcd_console_address + CONSOLE_SIZE \ + - CONSOLE_ROW_SIZE) +#define CONSOLE_SIZE (CONSOLE_ROW_SIZE * CONSOLE_ROWS) +#define CONSOLE_SCROLL_SIZE (CONSOLE_SIZE - CONSOLE_ROW_SIZE) + +#if LCD_BPP == LCD_MONOCHROME +# define COLOR_MASK(c) ((c) | (c) << 1 | (c) << 2 | (c) << 3 | \ + (c) << 4 | (c) << 5 | (c) << 6 | (c) << 7) +#elif (LCD_BPP == LCD_COLOR8) || (LCD_BPP == LCD_COLOR16) +# define COLOR_MASK(c) (c) +#else +# error Unsupported LCD BPP. +#endif + +DECLARE_GLOBAL_DATA_PTR; + +static void lcd_drawchars(ushort x, ushort y, uchar *str, int count); +static inline void lcd_puts_xy(ushort x, ushort y, uchar *s); +static inline void lcd_putc_xy(ushort x, ushort y, uchar c); + +static int lcd_init(void *lcdbase); + +static void *lcd_logo(void); + +static int lcd_getbgcolor(void); +static void lcd_setfgcolor(int color); +static void lcd_setbgcolor(int color); + +static int lcd_color_fg; +static int lcd_color_bg; +int lcd_line_length; + +char lcd_is_enabled = 0; + +static short console_col; +static short console_row; + +static void *lcd_console_address; +static void *lcd_base; /* Start of framebuffer memory */ + +static char lcd_flush_dcache; /* 1 to flush dcache after each lcd update */ + +/************************************************************************/ + +/* Flush LCD activity to the caches */ +void lcd_sync(void) +{ + /* + * flush_dcache_range() is declared in common.h but it seems that some + * architectures do not actually implement it. Is there a way to find + * out whether it exists? For now, ARM is safe. + */ +#if defined(CONFIG_ARM) && !defined(CONFIG_SYS_DCACHE_OFF) + int line_length; + + if (lcd_flush_dcache) + flush_dcache_range((u32)lcd_base, + (u32)(lcd_base + lcd_get_size(&line_length))); +#elif defined(CONFIG_SANDBOX) && defined(CONFIG_VIDEO_SANDBOX_SDL) + static ulong last_sync; + + if (get_timer(last_sync) > 10) { + sandbox_sdl_sync(lcd_base); + last_sync = get_timer(0); + } +#endif +} + +void lcd_set_flush_dcache(int flush) +{ + lcd_flush_dcache = (flush != 0); +} + +/*----------------------------------------------------------------------*/ + +static void console_scrollup(void) +{ + const int rows = CONFIG_CONSOLE_SCROLL_LINES; + + /* Copy up rows ignoring those that will be overwritten */ + memcpy(CONSOLE_ROW_FIRST, + lcd_console_address + CONSOLE_ROW_SIZE * rows, + CONSOLE_SIZE - CONSOLE_ROW_SIZE * rows); + + /* Clear the last rows */ + memset(lcd_console_address + CONSOLE_SIZE - CONSOLE_ROW_SIZE * rows, + COLOR_MASK(lcd_color_bg), + CONSOLE_ROW_SIZE * rows); + + lcd_sync(); + console_row -= rows; +} + +/*----------------------------------------------------------------------*/ + +static inline void console_back(void) +{ + if (--console_col < 0) { + console_col = CONSOLE_COLS-1 ; + if (--console_row < 0) + console_row = 0; + } + + lcd_putc_xy(console_col * VIDEO_FONT_WIDTH, + console_row * VIDEO_FONT_HEIGHT, ' '); +} + +/*----------------------------------------------------------------------*/ + +static inline void console_newline(void) +{ + console_col = 0; + + /* Check if we need to scroll the terminal */ + if (++console_row >= CONSOLE_ROWS) + console_scrollup(); + else + lcd_sync(); +} + +/*----------------------------------------------------------------------*/ + +void lcd_putc(const char c) +{ + if (!lcd_is_enabled) { + serial_putc(c); + + return; + } + + switch (c) { + case '\r': + console_col = 0; + + return; + case '\n': + console_newline(); + + return; + case '\t': /* Tab (8 chars alignment) */ + console_col += 8; + console_col &= ~7; + + if (console_col >= CONSOLE_COLS) + console_newline(); + + return; + case '\b': + console_back(); + + return; + default: + lcd_putc_xy(console_col * VIDEO_FONT_WIDTH, + console_row * VIDEO_FONT_HEIGHT, c); + if (++console_col >= CONSOLE_COLS) + console_newline(); + } +} + +/*----------------------------------------------------------------------*/ + +void lcd_puts(const char *s) +{ + if (!lcd_is_enabled) { + serial_puts(s); + + return; + } + + while (*s) + lcd_putc(*s++); + + lcd_sync(); +} + +/*----------------------------------------------------------------------*/ + +void lcd_printf(const char *fmt, ...) +{ + va_list args; + char buf[CONFIG_SYS_PBSIZE]; + + va_start(args, fmt); + vsprintf(buf, fmt, args); + va_end(args); + + lcd_puts(buf); +} + +/************************************************************************/ +/* ** Low-Level Graphics Routines */ +/************************************************************************/ + +static void lcd_drawchars(ushort x, ushort y, uchar *str, int count) +{ + uchar *dest; + ushort row; + +#if defined(CONFIG_LCD_LOGO) && !defined(CONFIG_LCD_INFO_BELOW_LOGO) + y += BMP_LOGO_HEIGHT; +#endif + +#if LCD_BPP == LCD_MONOCHROME + ushort off = x * (1 << LCD_BPP) % 8; +#endif + + dest = (uchar *)(lcd_base + y * lcd_line_length + x * (1 << LCD_BPP) / 8); + + for (row = 0; row < VIDEO_FONT_HEIGHT; ++row, dest += lcd_line_length) { + uchar *s = str; + int i; +#if LCD_BPP == LCD_COLOR16 + ushort *d = (ushort *)dest; +#else + uchar *d = dest; +#endif + +#if LCD_BPP == LCD_MONOCHROME + uchar rest = *d & -(1 << (8 - off)); + uchar sym; +#endif + for (i = 0; i < count; ++i) { + uchar c, bits; + + c = *s++; + bits = video_fontdata[c * VIDEO_FONT_HEIGHT + row]; + +#if LCD_BPP == LCD_MONOCHROME + sym = (COLOR_MASK(lcd_color_fg) & bits) | + (COLOR_MASK(lcd_color_bg) & ~bits); + + *d++ = rest | (sym >> off); + rest = sym << (8-off); +#elif LCD_BPP == LCD_COLOR8 + for (c = 0; c < 8; ++c) { + *d++ = (bits & 0x80) ? + lcd_color_fg : lcd_color_bg; + bits <<= 1; + } +#elif LCD_BPP == LCD_COLOR16 + for (c = 0; c < 8; ++c) { + *d++ = (bits & 0x80) ? + lcd_color_fg : lcd_color_bg; + bits <<= 1; + } +#endif + } +#if LCD_BPP == LCD_MONOCHROME + *d = rest | (*d & ((1 << (8 - off)) - 1)); +#endif + } +} + +/*----------------------------------------------------------------------*/ + +static inline void lcd_puts_xy(ushort x, ushort y, uchar *s) +{ + lcd_drawchars(x, y, s, strlen((char *)s)); +} + +/*----------------------------------------------------------------------*/ + +static inline void lcd_putc_xy(ushort x, ushort y, uchar c) +{ + lcd_drawchars(x, y, &c, 1); +} + +/************************************************************************/ +/** Small utility to check that you got the colours right */ +/************************************************************************/ +#ifdef LCD_TEST_PATTERN + +#define N_BLK_VERT 2 +#define N_BLK_HOR 3 + +static int test_colors[N_BLK_HOR * N_BLK_VERT] = { + CONSOLE_COLOR_RED, CONSOLE_COLOR_GREEN, CONSOLE_COLOR_YELLOW, + CONSOLE_COLOR_BLUE, CONSOLE_COLOR_MAGENTA, CONSOLE_COLOR_CYAN, +}; + +static void test_pattern(void) +{ + ushort v_max = panel_info.vl_row; + ushort h_max = panel_info.vl_col; + ushort v_step = (v_max + N_BLK_VERT - 1) / N_BLK_VERT; + ushort h_step = (h_max + N_BLK_HOR - 1) / N_BLK_HOR; + ushort v, h; + uchar *pix = (uchar *)lcd_base; + + printf("[LCD] Test Pattern: %d x %d [%d x %d]\n", + h_max, v_max, h_step, v_step); + + /* WARNING: Code silently assumes 8bit/pixel */ + for (v = 0; v < v_max; ++v) { + uchar iy = v / v_step; + for (h = 0; h < h_max; ++h) { + uchar ix = N_BLK_HOR * iy + h / h_step; + *pix++ = test_colors[ix]; + } + } +} +#endif /* LCD_TEST_PATTERN */ + + +/************************************************************************/ +/* ** GENERIC Initialization Routines */ +/************************************************************************/ +/* + * With most lcd drivers the line length is set up + * by calculating it from panel_info parameters. Some + * drivers need to calculate the line length differently, + * so make the function weak to allow overriding it. + */ +__weak int lcd_get_size(int *line_length) +{ + *line_length = (panel_info.vl_col * NBITS(panel_info.vl_bpix)) / 8; + return *line_length * panel_info.vl_row; +} + +int drv_lcd_init(void) +{ + struct stdio_dev lcddev; + int rc; + + lcd_base = map_sysmem(gd->fb_base, 0); + + lcd_init(lcd_base); /* LCD initialization */ + + /* Device initialization */ + memset(&lcddev, 0, sizeof(lcddev)); + + strcpy(lcddev.name, "lcd"); + lcddev.ext = 0; /* No extensions */ + lcddev.flags = DEV_FLAGS_OUTPUT; /* Output only */ + lcddev.putc = lcd_putc; /* 'putc' function */ + lcddev.puts = lcd_puts; /* 'puts' function */ + + rc = stdio_register(&lcddev); + + return (rc == 0) ? 1 : rc; +} + +/*----------------------------------------------------------------------*/ +void lcd_clear(void) +{ +#if LCD_BPP == LCD_MONOCHROME + /* Setting the palette */ + lcd_initcolregs(); + +#elif LCD_BPP == LCD_COLOR8 + /* Setting the palette */ + lcd_setcolreg(CONSOLE_COLOR_BLACK, 0, 0, 0); + lcd_setcolreg(CONSOLE_COLOR_RED, 0xFF, 0, 0); + lcd_setcolreg(CONSOLE_COLOR_GREEN, 0, 0xFF, 0); + lcd_setcolreg(CONSOLE_COLOR_YELLOW, 0xFF, 0xFF, 0); + lcd_setcolreg(CONSOLE_COLOR_BLUE, 0, 0, 0xFF); + lcd_setcolreg(CONSOLE_COLOR_MAGENTA, 0xFF, 0, 0xFF); + lcd_setcolreg(CONSOLE_COLOR_CYAN, 0, 0xFF, 0xFF); + lcd_setcolreg(CONSOLE_COLOR_GREY, 0xAA, 0xAA, 0xAA); + lcd_setcolreg(CONSOLE_COLOR_WHITE, 0xFF, 0xFF, 0xFF); +#endif + +#ifndef CONFIG_SYS_WHITE_ON_BLACK + lcd_setfgcolor(CONSOLE_COLOR_BLACK); + lcd_setbgcolor(CONSOLE_COLOR_WHITE); +#else + lcd_setfgcolor(CONSOLE_COLOR_WHITE); + lcd_setbgcolor(CONSOLE_COLOR_BLACK); +#endif /* CONFIG_SYS_WHITE_ON_BLACK */ + +#ifdef LCD_TEST_PATTERN + test_pattern(); +#else + /* set framebuffer to background color */ + memset((char *)lcd_base, + COLOR_MASK(lcd_getbgcolor()), + lcd_line_length * panel_info.vl_row); +#endif + /* Paint the logo and retrieve LCD base address */ + debug("[LCD] Drawing the logo...\n"); + lcd_console_address = lcd_logo(); + + console_col = 0; + console_row = 0; + lcd_sync(); +} + +static int do_lcd_clear(cmd_tbl_t *cmdtp, int flag, int argc, + char *const argv[]) +{ + lcd_clear(); + return 0; +} + +U_BOOT_CMD( + cls, 1, 1, do_lcd_clear, + "clear screen", + "" +); + +/*----------------------------------------------------------------------*/ + +static int lcd_init(void *lcdbase) +{ + /* Initialize the lcd controller */ + debug("[LCD] Initializing LCD frambuffer at %p\n", lcdbase); + + lcd_ctrl_init(lcdbase); + + /* + * lcd_ctrl_init() of some drivers (i.e. bcm2835 on rpi_b) ignores + * the 'lcdbase' argument and uses custom lcd base address + * by setting up gd->fb_base. Check for this condition and fixup + * 'lcd_base' address. + */ + if (map_to_sysmem(lcdbase) != gd->fb_base) + lcd_base = map_sysmem(gd->fb_base, 0); + + debug("[LCD] Using LCD frambuffer at %p\n", lcd_base); + + lcd_get_size(&lcd_line_length); + lcd_is_enabled = 1; + lcd_clear(); + lcd_enable(); + + /* Initialize the console */ + console_col = 0; +#ifdef CONFIG_LCD_INFO_BELOW_LOGO + console_row = 7 + BMP_LOGO_HEIGHT / VIDEO_FONT_HEIGHT; +#else + console_row = 1; /* leave 1 blank line below logo */ +#endif + + return 0; +} + + +/************************************************************************/ +/* ** ROM capable initialization part - needed to reserve FB memory */ +/************************************************************************/ +/* + * This is called early in the system initialization to grab memory + * for the LCD controller. + * Returns new address for monitor, after reserving LCD buffer memory + * + * Note that this is running from ROM, so no write access to global data. + */ +ulong lcd_setmem(ulong addr) +{ + ulong size; + int line_length; + + debug("LCD panel info: %d x %d, %d bit/pix\n", panel_info.vl_col, + panel_info.vl_row, NBITS(panel_info.vl_bpix)); + + size = lcd_get_size(&line_length); + + /* Round up to nearest full page, or MMU section if defined */ + size = ALIGN(size, CONFIG_LCD_ALIGNMENT); + addr = ALIGN(addr - CONFIG_LCD_ALIGNMENT + 1, CONFIG_LCD_ALIGNMENT); + + /* Allocate pages for the frame buffer. */ + addr -= size; + + debug("Reserving %ldk for LCD Framebuffer at: %08lx\n", + size >> 10, addr); + + return addr; +} + +/*----------------------------------------------------------------------*/ + +static void lcd_setfgcolor(int color) +{ + lcd_color_fg = color; +} + +/*----------------------------------------------------------------------*/ + +static void lcd_setbgcolor(int color) +{ + lcd_color_bg = color; +} + +/*----------------------------------------------------------------------*/ + +int lcd_getfgcolor(void) +{ + return lcd_color_fg; +} + +/*----------------------------------------------------------------------*/ + +static int lcd_getbgcolor(void) +{ + return lcd_color_bg; +} + +/************************************************************************/ +/* ** Chipset depending Bitmap / Logo stuff... */ +/************************************************************************/ +static inline ushort *configuration_get_cmap(void) +{ +#if defined CONFIG_CPU_PXA + struct pxafb_info *fbi = &panel_info.pxa; + return (ushort *)fbi->palette; +#elif defined(CONFIG_MPC823) + immap_t *immr = (immap_t *) CONFIG_SYS_IMMR; + cpm8xx_t *cp = &(immr->im_cpm); + return (ushort *)&(cp->lcd_cmap[255 * sizeof(ushort)]); +#elif defined(CONFIG_ATMEL_LCD) + return (ushort *)(panel_info.mmio + ATMEL_LCDC_LUT(0)); +#elif !defined(CONFIG_ATMEL_HLCD) && !defined(CONFIG_EXYNOS_FB) + return panel_info.cmap; +#elif defined(CONFIG_LCD_LOGO) + return bmp_logo_palette; +#else + return NULL; +#endif +} + +#ifdef CONFIG_LCD_LOGO +void bitmap_plot(int x, int y) +{ +#ifdef CONFIG_ATMEL_LCD + uint *cmap = (uint *)bmp_logo_palette; +#else + ushort *cmap = (ushort *)bmp_logo_palette; +#endif + ushort i, j; + uchar *bmap; + uchar *fb; + ushort *fb16; +#if defined(CONFIG_MPC823) + immap_t *immr = (immap_t *) CONFIG_SYS_IMMR; + cpm8xx_t *cp = &(immr->im_cpm); +#endif + unsigned bpix = NBITS(panel_info.vl_bpix); + + debug("Logo: width %d height %d colors %d cmap %d\n", + BMP_LOGO_WIDTH, BMP_LOGO_HEIGHT, BMP_LOGO_COLORS, + ARRAY_SIZE(bmp_logo_palette)); + + bmap = &bmp_logo_bitmap[0]; + fb = (uchar *)(lcd_base + y * lcd_line_length + x * bpix / 8); + + if (bpix < 12) { + /* Leave room for default color map + * default case: generic system with no cmap (most likely 16bpp) + * cmap was set to the source palette, so no change is done. + * This avoids even more ifdefs in the next stanza + */ +#if defined(CONFIG_MPC823) + cmap = (ushort *) &(cp->lcd_cmap[BMP_LOGO_OFFSET * sizeof(ushort)]); +#elif defined(CONFIG_ATMEL_LCD) + cmap = (uint *)configuration_get_cmap(); +#else + cmap = configuration_get_cmap(); +#endif + + WATCHDOG_RESET(); + + /* Set color map */ + for (i = 0; i < ARRAY_SIZE(bmp_logo_palette); ++i) { + ushort colreg = bmp_logo_palette[i]; +#ifdef CONFIG_ATMEL_LCD + uint lut_entry; +#ifdef CONFIG_ATMEL_LCD_BGR555 + lut_entry = ((colreg & 0x000F) << 11) | + ((colreg & 0x00F0) << 2) | + ((colreg & 0x0F00) >> 7); +#else /* CONFIG_ATMEL_LCD_RGB565 */ + lut_entry = ((colreg & 0x000F) << 1) | + ((colreg & 0x00F0) << 3) | + ((colreg & 0x0F00) << 4); +#endif + *(cmap + BMP_LOGO_OFFSET) = lut_entry; + cmap++; +#else /* !CONFIG_ATMEL_LCD */ +#ifdef CONFIG_SYS_INVERT_COLORS + *cmap++ = 0xffff - colreg; +#else + *cmap++ = colreg; +#endif +#endif /* CONFIG_ATMEL_LCD */ + } + + WATCHDOG_RESET(); + + for (i = 0; i < BMP_LOGO_HEIGHT; ++i) { + memcpy(fb, bmap, BMP_LOGO_WIDTH); + bmap += BMP_LOGO_WIDTH; + fb += panel_info.vl_col; + } + } + else { /* true color mode */ + u16 col16; + fb16 = (ushort *)fb; + for (i = 0; i < BMP_LOGO_HEIGHT; ++i) { + for (j = 0; j < BMP_LOGO_WIDTH; j++) { + col16 = bmp_logo_palette[(bmap[j]-16)]; + fb16[j] = + ((col16 & 0x000F) << 1) | + ((col16 & 0x00F0) << 3) | + ((col16 & 0x0F00) << 4); + } + bmap += BMP_LOGO_WIDTH; + fb16 += panel_info.vl_col; + } + } + + WATCHDOG_RESET(); + lcd_sync(); +} +#else +static inline void bitmap_plot(int x, int y) {} +#endif /* CONFIG_LCD_LOGO */ + +/*----------------------------------------------------------------------*/ +#if defined(CONFIG_CMD_BMP) || defined(CONFIG_SPLASH_SCREEN) +/* + * Display the BMP file located at address bmp_image. + * Only uncompressed. + */ + +#ifdef CONFIG_SPLASH_SCREEN_ALIGN +#define BMP_ALIGN_CENTER 0x7FFF + +static void splash_align_axis(int *axis, unsigned long panel_size, + unsigned long picture_size) +{ + unsigned long panel_picture_delta = panel_size - picture_size; + unsigned long axis_alignment; + + if (*axis == BMP_ALIGN_CENTER) + axis_alignment = panel_picture_delta / 2; + else if (*axis < 0) + axis_alignment = panel_picture_delta + *axis + 1; + else + return; + + *axis = max(0, axis_alignment); +} +#endif + + +#ifdef CONFIG_LCD_BMP_RLE8 + +#define BMP_RLE8_ESCAPE 0 +#define BMP_RLE8_EOL 0 +#define BMP_RLE8_EOBMP 1 +#define BMP_RLE8_DELTA 2 + +static void draw_unencoded_bitmap(ushort **fbp, uchar *bmap, ushort *cmap, + int cnt) +{ + while (cnt > 0) { + *(*fbp)++ = cmap[*bmap++]; + cnt--; + } +} + +static void draw_encoded_bitmap(ushort **fbp, ushort c, int cnt) +{ + ushort *fb = *fbp; + int cnt_8copy = cnt >> 3; + + cnt -= cnt_8copy << 3; + while (cnt_8copy > 0) { + *fb++ = c; + *fb++ = c; + *fb++ = c; + *fb++ = c; + *fb++ = c; + *fb++ = c; + *fb++ = c; + *fb++ = c; + cnt_8copy--; + } + while (cnt > 0) { + *fb++ = c; + cnt--; + } + *fbp = fb; +} + +/* + * Do not call this function directly, must be called from lcd_display_bitmap. + */ +static void lcd_display_rle8_bitmap(bmp_image_t *bmp, ushort *cmap, uchar *fb, + int x_off, int y_off) +{ + uchar *bmap; + ulong width, height; + ulong cnt, runlen; + int x, y; + int decode = 1; + + width = get_unaligned_le32(&bmp->header.width); + height = get_unaligned_le32(&bmp->header.height); + bmap = (uchar *)bmp + get_unaligned_le32(&bmp->header.data_offset); + + x = 0; + y = height - 1; + + while (decode) { + if (bmap[0] == BMP_RLE8_ESCAPE) { + switch (bmap[1]) { + case BMP_RLE8_EOL: + /* end of line */ + bmap += 2; + x = 0; + y--; + /* 16bpix, 2-byte per pixel, width should *2 */ + fb -= (width * 2 + lcd_line_length); + break; + case BMP_RLE8_EOBMP: + /* end of bitmap */ + decode = 0; + break; + case BMP_RLE8_DELTA: + /* delta run */ + x += bmap[2]; + y -= bmap[3]; + /* 16bpix, 2-byte per pixel, x should *2 */ + fb = (uchar *) (lcd_base + (y + y_off - 1) + * lcd_line_length + (x + x_off) * 2); + bmap += 4; + break; + default: + /* unencoded run */ + runlen = bmap[1]; + bmap += 2; + if (y < height) { + if (x < width) { + if (x + runlen > width) + cnt = width - x; + else + cnt = runlen; + draw_unencoded_bitmap( + (ushort **)&fb, + bmap, cmap, cnt); + } + x += runlen; + } + bmap += runlen; + if (runlen & 1) + bmap++; + } + } else { + /* encoded run */ + if (y < height) { + runlen = bmap[0]; + if (x < width) { + /* aggregate the same code */ + while (bmap[0] == 0xff && + bmap[2] != BMP_RLE8_ESCAPE && + bmap[1] == bmap[3]) { + runlen += bmap[2]; + bmap += 2; + } + if (x + runlen > width) + cnt = width - x; + else + cnt = runlen; + draw_encoded_bitmap((ushort **)&fb, + cmap[bmap[1]], cnt); + } + x += runlen; + } + bmap += 2; + } + } +} +#endif + +#if defined(CONFIG_MPC823) || defined(CONFIG_MCC200) +#define FB_PUT_BYTE(fb, from) *(fb)++ = (255 - *(from)++) +#else +#define FB_PUT_BYTE(fb, from) *(fb)++ = *(from)++ +#endif + +#if defined(CONFIG_BMP_16BPP) +#if defined(CONFIG_ATMEL_LCD_BGR555) +static inline void fb_put_word(uchar **fb, uchar **from) +{ + *(*fb)++ = (((*from)[0] & 0x1f) << 2) | ((*from)[1] & 0x03); + *(*fb)++ = ((*from)[0] & 0xe0) | (((*from)[1] & 0x7c) >> 2); + *from += 2; +} +#else +static inline void fb_put_word(uchar **fb, uchar **from) +{ + *(*fb)++ = *(*from)++; + *(*fb)++ = *(*from)++; +} +#endif +#endif /* CONFIG_BMP_16BPP */ + +int lcd_display_bitmap(ulong bmp_image, int x, int y) +{ +#if !defined(CONFIG_MCC200) + ushort *cmap = NULL; +#endif + ushort *cmap_base = NULL; + ushort i, j; + uchar *fb; + bmp_image_t *bmp = (bmp_image_t *)map_sysmem(bmp_image, 0); + uchar *bmap; + ushort padded_width; + unsigned long width, height, byte_width; + unsigned long pwidth = panel_info.vl_col; + unsigned colors, bpix, bmp_bpix; + + if (!bmp || !(bmp->header.signature[0] == 'B' && + bmp->header.signature[1] == 'M')) { + printf("Error: no valid bmp image at %lx\n", bmp_image); + + return 1; + } + + width = get_unaligned_le32(&bmp->header.width); + height = get_unaligned_le32(&bmp->header.height); + bmp_bpix = get_unaligned_le16(&bmp->header.bit_count); + + colors = 1 << bmp_bpix; + + bpix = NBITS(panel_info.vl_bpix); + + if (bpix != 1 && bpix != 8 && bpix != 16 && bpix != 32) { + printf ("Error: %d bit/pixel mode, but BMP has %d bit/pixel\n", + bpix, bmp_bpix); + + return 1; + } + + /* We support displaying 8bpp BMPs on 16bpp LCDs */ + if (bpix != bmp_bpix && !(bmp_bpix == 8 && bpix == 16)) { + printf ("Error: %d bit/pixel mode, but BMP has %d bit/pixel\n", + bpix, get_unaligned_le16(&bmp->header.bit_count)); + return 1; + } + + debug("Display-bmp: %d x %d with %d colors\n", + (int)width, (int)height, (int)colors); + +#if !defined(CONFIG_MCC200) + /* MCC200 LCD doesn't need CMAP, supports 1bpp b&w only */ + if (bmp_bpix == 8) { + cmap = configuration_get_cmap(); + cmap_base = cmap; + + /* Set color map */ + for (i = 0; i < colors; ++i) { + bmp_color_table_entry_t cte = bmp->color_table[i]; +#if !defined(CONFIG_ATMEL_LCD) + ushort colreg = + ( ((cte.red) << 8) & 0xf800) | + ( ((cte.green) << 3) & 0x07e0) | + ( ((cte.blue) >> 3) & 0x001f) ; +#ifdef CONFIG_SYS_INVERT_COLORS + *cmap = 0xffff - colreg; +#else + *cmap = colreg; +#endif +#if defined(CONFIG_MPC823) + cmap--; +#else + cmap++; +#endif +#else /* CONFIG_ATMEL_LCD */ + lcd_setcolreg(i, cte.red, cte.green, cte.blue); +#endif + } + } +#endif + /* + * BMP format for Monochrome assumes that the state of a + * pixel is described on a per Bit basis, not per Byte. + * So, in case of Monochrome BMP we should align widths + * on a byte boundary and convert them from Bit to Byte + * units. + * Probably, PXA250 and MPC823 process 1bpp BMP images in + * their own ways, so make the converting to be MCC200 + * specific. + */ +#if defined(CONFIG_MCC200) + if (bpix == 1) { + width = ((width + 7) & ~7) >> 3; + x = ((x + 7) & ~7) >> 3; + pwidth= ((pwidth + 7) & ~7) >> 3; + } +#endif + + padded_width = (width & 0x3 ? (width & ~0x3) + 4 : width); + +#ifdef CONFIG_SPLASH_SCREEN_ALIGN + splash_align_axis(&x, pwidth, width); + splash_align_axis(&y, panel_info.vl_row, height); +#endif /* CONFIG_SPLASH_SCREEN_ALIGN */ + + if ((x + width) > pwidth) + width = pwidth - x; + if ((y + height) > panel_info.vl_row) + height = panel_info.vl_row - y; + + bmap = (uchar *)bmp + get_unaligned_le32(&bmp->header.data_offset); + fb = (uchar *)(lcd_base + + (y + height - 1) * lcd_line_length + x * bpix / 8); + + switch (bmp_bpix) { + case 1: /* pass through */ + case 8: +#ifdef CONFIG_LCD_BMP_RLE8 + u32 compression = get_unaligned_le32(&bmp->header.compression); + if (compression == BMP_BI_RLE8) { + if (bpix != 16) { + /* TODO implement render code for bpix != 16 */ + printf("Error: only support 16 bpix"); + return 1; + } + lcd_display_rle8_bitmap(bmp, cmap_base, fb, x, y); + break; + } +#endif + + if (bpix != 16) + byte_width = width; + else + byte_width = width * 2; + + for (i = 0; i < height; ++i) { + WATCHDOG_RESET(); + for (j = 0; j < width; j++) { + if (bpix != 16) { + FB_PUT_BYTE(fb, bmap); + } else { + *(uint16_t *)fb = cmap_base[*(bmap++)]; + fb += sizeof(uint16_t) / sizeof(*fb); + } + } + bmap += (padded_width - width); + fb -= byte_width + lcd_line_length; + } + break; + +#if defined(CONFIG_BMP_16BPP) + case 16: + for (i = 0; i < height; ++i) { + WATCHDOG_RESET(); + for (j = 0; j < width; j++) + fb_put_word(&fb, &bmap); + + bmap += (padded_width - width) * 2; + fb -= width * 2 + lcd_line_length; + } + break; +#endif /* CONFIG_BMP_16BPP */ + +#if defined(CONFIG_BMP_32BPP) + case 32: + for (i = 0; i < height; ++i) { + for (j = 0; j < width; j++) { + *(fb++) = *(bmap++); + *(fb++) = *(bmap++); + *(fb++) = *(bmap++); + *(fb++) = *(bmap++); + } + fb -= lcd_line_length + width * (bpix / 8); + } + break; +#endif /* CONFIG_BMP_32BPP */ + default: + break; + }; + + lcd_sync(); + return 0; +} +#endif + +static void *lcd_logo(void) +{ +#ifdef CONFIG_SPLASH_SCREEN + char *s; + ulong addr; + static int do_splash = 1; + + if (do_splash && (s = getenv("splashimage")) != NULL) { + int x = 0, y = 0; + do_splash = 0; + + if (splash_screen_prepare()) + return (void *)lcd_base; + + addr = simple_strtoul (s, NULL, 16); + + splash_get_pos(&x, &y); + + if (bmp_display(addr, x, y) == 0) + return (void *)lcd_base; + } +#endif /* CONFIG_SPLASH_SCREEN */ + + bitmap_plot(0, 0); + +#ifdef CONFIG_LCD_INFO + console_col = LCD_INFO_X / VIDEO_FONT_WIDTH; + console_row = LCD_INFO_Y / VIDEO_FONT_HEIGHT; + lcd_show_board_info(); +#endif /* CONFIG_LCD_INFO */ + +#if defined(CONFIG_LCD_LOGO) && !defined(CONFIG_LCD_INFO_BELOW_LOGO) + return (void *)((ulong)lcd_base + BMP_LOGO_HEIGHT * lcd_line_length); +#else + return (void *)lcd_base; +#endif /* CONFIG_LCD_LOGO && !defined(CONFIG_LCD_INFO_BELOW_LOGO) */ +} + +#ifdef CONFIG_SPLASHIMAGE_GUARD +static int on_splashimage(const char *name, const char *value, enum env_op op, + int flags) +{ + ulong addr; + int aligned; + + if (op == env_op_delete) + return 0; + + addr = simple_strtoul(value, NULL, 16); + /* See README.displaying-bmps */ + aligned = (addr % 4 == 2); + if (!aligned) { + printf("Invalid splashimage value. Value must be 16 bit aligned, but not 32 bit aligned\n"); + return -1; + } + + return 0; +} + +U_BOOT_ENV_CALLBACK(splashimage, on_splashimage); +#endif + +void lcd_position_cursor(unsigned col, unsigned row) +{ + console_col = min(col, CONSOLE_COLS - 1); + console_row = min(row, CONSOLE_ROWS - 1); +} + +int lcd_get_pixel_width(void) +{ + return panel_info.vl_col; +} + +int lcd_get_pixel_height(void) +{ + return panel_info.vl_row; +} + +int lcd_get_screen_rows(void) +{ + return CONSOLE_ROWS; +} + +int lcd_get_screen_columns(void) +{ + return CONSOLE_COLS; +} + +#if defined(CONFIG_LCD_DT_SIMPLEFB) +static int lcd_dt_simplefb_configure_node(void *blob, int off) +{ + u32 stride; + fdt32_t cells[2]; + int ret; + static const char format[] = +#if LCD_BPP == LCD_COLOR16 + "r5g6b5"; +#else + ""; +#endif + + if (!format[0]) + return -1; + + stride = panel_info.vl_col * 2; + + cells[0] = cpu_to_fdt32(gd->fb_base); + cells[1] = cpu_to_fdt32(stride * panel_info.vl_row); + ret = fdt_setprop(blob, off, "reg", cells, sizeof(cells[0]) * 2); + if (ret < 0) + return -1; + + cells[0] = cpu_to_fdt32(panel_info.vl_col); + ret = fdt_setprop(blob, off, "width", cells, sizeof(cells[0])); + if (ret < 0) + return -1; + + cells[0] = cpu_to_fdt32(panel_info.vl_row); + ret = fdt_setprop(blob, off, "height", cells, sizeof(cells[0])); + if (ret < 0) + return -1; + + cells[0] = cpu_to_fdt32(stride); + ret = fdt_setprop(blob, off, "stride", cells, sizeof(cells[0])); + if (ret < 0) + return -1; + + ret = fdt_setprop(blob, off, "format", format, strlen(format) + 1); + if (ret < 0) + return -1; + + ret = fdt_delprop(blob, off, "status"); + if (ret < 0) + return -1; + + return 0; +} + +int lcd_dt_simplefb_add_node(void *blob) +{ + static const char compat[] = "simple-framebuffer"; + static const char disabled[] = "disabled"; + int off, ret; + + off = fdt_add_subnode(blob, 0, "framebuffer"); + if (off < 0) + return -1; + + ret = fdt_setprop(blob, off, "status", disabled, sizeof(disabled)); + if (ret < 0) + return -1; + + ret = fdt_setprop(blob, off, "compatible", compat, sizeof(compat)); + if (ret < 0) + return -1; + + return lcd_dt_simplefb_configure_node(blob, off); +} + +int lcd_dt_simplefb_enable_existing_node(void *blob) +{ + int off; + + off = fdt_node_offset_by_compatible(blob, -1, "simple-framebuffer"); + if (off < 0) + return -1; + + return lcd_dt_simplefb_configure_node(blob, off); +} +#endif diff --git a/qemu/roms/u-boot/common/lynxkdi.c b/qemu/roms/u-boot/common/lynxkdi.c new file mode 100644 index 000000000..22ad384ef --- /dev/null +++ b/qemu/roms/u-boot/common/lynxkdi.c @@ -0,0 +1,70 @@ +/* + * Copyright (c) Orbacom Systems, Inc <www.orbacom.com> + * All rights reserved. + * + * Redistribution and use in source and binary forms are freely + * permitted provided that the above copyright notice and this + * paragraph and the following disclaimer are duplicated in all + * such forms. + * + * This software is provided "AS IS" and without any express or + * implied warranties, including, without limitation, the implied + * warranties of merchantability and fitness for a particular + * purpose. + */ + +#include <common.h> +#include <asm/processor.h> +#include <image.h> +#include <net.h> + +#include <lynxkdi.h> + +DECLARE_GLOBAL_DATA_PTR; + +#if defined(CONFIG_MPC8260) || defined(CONFIG_440EP) || defined(CONFIG_440GR) +void lynxkdi_boot(image_header_t *hdr) +{ + void (*lynxkdi)(void) = (void(*)(void))image_get_ep(hdr); + lynxos_bootparms_t *parms = (lynxos_bootparms_t *)0x0020; + bd_t *kbd; + u32 *psz = (u32 *)(image_get_load(hdr) + 0x0204); + + memset(parms, 0, sizeof(*parms)); + kbd = gd->bd; + parms->clock_ref = kbd->bi_busfreq; + parms->dramsz = kbd->bi_memsize; + eth_getenv_enetaddr("ethaddr", parms->ethaddr); + mtspr(SPRN_SPRG2, 0x0020); + + /* Do a simple check for Bluecat so we can pass the + * kernel command line parameters. + */ + /* FIXME: NOT SURE HERE ! */ + if (le32_to_cpu(*psz) == image_get_data_size(hdr)) { + char *args; + char *cmdline = (char *)(image_get_load(hdr) + 0x020c); + int len; + + printf("Booting Bluecat KDI ...\n"); + udelay(200*1000); /* Allow serial port to flush */ + if ((args = getenv("bootargs")) == NULL) + args = ""; + /* Prepend the cmdline */ + len = strlen(args); + if (len && (len + strlen(cmdline) + 2 < (0x0400 - 0x020c))) { + memmove(cmdline + strlen(args) + 1, cmdline, + strlen(cmdline)); + strcpy(cmdline, args); + cmdline[len] = ' '; + } + } + else { + printf("Booting LynxOS KDI ...\n"); + } + + lynxkdi(); +} +#else +#error "Lynx KDI support not implemented for configured CPU" +#endif diff --git a/qemu/roms/u-boot/common/main.c b/qemu/roms/u-boot/common/main.c new file mode 100644 index 000000000..9bee7bdc6 --- /dev/null +++ b/qemu/roms/u-boot/common/main.c @@ -0,0 +1,1558 @@ +/* + * (C) Copyright 2000 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * Add to readline cmdline-editing by + * (C) Copyright 2005 + * JinHua Luo, GuangDong Linux Center, <luo.jinhua@gd-linux.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/* #define DEBUG */ + +#include <common.h> +#include <command.h> +#include <fdtdec.h> +#include <hush.h> +#include <malloc.h> +#include <menu.h> +#include <post.h> +#include <version.h> +#include <watchdog.h> +#include <linux/ctype.h> + +DECLARE_GLOBAL_DATA_PTR; + +/* + * Board-specific Platform code can reimplement show_boot_progress () if needed + */ +void inline __show_boot_progress (int val) {} +void show_boot_progress (int val) __attribute__((weak, alias("__show_boot_progress"))); + +#define MAX_DELAY_STOP_STR 32 + +#define DEBUG_PARSER 0 /* set to 1 to debug */ + +#define debug_parser(fmt, args...) \ + debug_cond(DEBUG_PARSER, fmt, ##args) + +#ifndef DEBUG_BOOTKEYS +#define DEBUG_BOOTKEYS 0 +#endif +#define debug_bootkeys(fmt, args...) \ + debug_cond(DEBUG_BOOTKEYS, fmt, ##args) + +char console_buffer[CONFIG_SYS_CBSIZE + 1]; /* console I/O buffer */ + +static char * delete_char (char *buffer, char *p, int *colp, int *np, int plen); +static const char erase_seq[] = "\b \b"; /* erase sequence */ +static const char tab_seq[] = " "; /* used to expand TABs */ + +#ifdef CONFIG_BOOT_RETRY_TIME +static uint64_t endtime = 0; /* must be set, default is instant timeout */ +static int retry_time = -1; /* -1 so can call readline before main_loop */ +#endif + +#define endtick(seconds) (get_ticks() + (uint64_t)(seconds) * get_tbclk()) + +#ifndef CONFIG_BOOT_RETRY_MIN +#define CONFIG_BOOT_RETRY_MIN CONFIG_BOOT_RETRY_TIME +#endif + +#ifdef CONFIG_MODEM_SUPPORT +int do_mdm_init = 0; +extern void mdm_init(void); /* defined in board.c */ +#endif + +/*************************************************************************** + * Watch for 'delay' seconds for autoboot stop or autoboot delay string. + * returns: 0 - no key string, allow autoboot 1 - got key string, abort + */ +#if defined(CONFIG_BOOTDELAY) +# if defined(CONFIG_AUTOBOOT_KEYED) +static int abortboot_keyed(int bootdelay) +{ + int abort = 0; + uint64_t etime = endtick(bootdelay); + struct { + char* str; + u_int len; + int retry; + } + delaykey [] = { + { str: getenv ("bootdelaykey"), retry: 1 }, + { str: getenv ("bootdelaykey2"), retry: 1 }, + { str: getenv ("bootstopkey"), retry: 0 }, + { str: getenv ("bootstopkey2"), retry: 0 }, + }; + + char presskey [MAX_DELAY_STOP_STR]; + u_int presskey_len = 0; + u_int presskey_max = 0; + u_int i; + +#ifndef CONFIG_ZERO_BOOTDELAY_CHECK + if (bootdelay == 0) + return 0; +#endif + +# ifdef CONFIG_AUTOBOOT_PROMPT + printf(CONFIG_AUTOBOOT_PROMPT); +# endif + +# ifdef CONFIG_AUTOBOOT_DELAY_STR + if (delaykey[0].str == NULL) + delaykey[0].str = CONFIG_AUTOBOOT_DELAY_STR; +# endif +# ifdef CONFIG_AUTOBOOT_DELAY_STR2 + if (delaykey[1].str == NULL) + delaykey[1].str = CONFIG_AUTOBOOT_DELAY_STR2; +# endif +# ifdef CONFIG_AUTOBOOT_STOP_STR + if (delaykey[2].str == NULL) + delaykey[2].str = CONFIG_AUTOBOOT_STOP_STR; +# endif +# ifdef CONFIG_AUTOBOOT_STOP_STR2 + if (delaykey[3].str == NULL) + delaykey[3].str = CONFIG_AUTOBOOT_STOP_STR2; +# endif + + for (i = 0; i < sizeof(delaykey) / sizeof(delaykey[0]); i ++) { + delaykey[i].len = delaykey[i].str == NULL ? + 0 : strlen (delaykey[i].str); + delaykey[i].len = delaykey[i].len > MAX_DELAY_STOP_STR ? + MAX_DELAY_STOP_STR : delaykey[i].len; + + presskey_max = presskey_max > delaykey[i].len ? + presskey_max : delaykey[i].len; + + debug_bootkeys("%s key:<%s>\n", + delaykey[i].retry ? "delay" : "stop", + delaykey[i].str ? delaykey[i].str : "NULL"); + } + + /* In order to keep up with incoming data, check timeout only + * when catch up. + */ + do { + if (tstc()) { + if (presskey_len < presskey_max) { + presskey [presskey_len ++] = getc(); + } + else { + for (i = 0; i < presskey_max - 1; i ++) + presskey [i] = presskey [i + 1]; + + presskey [i] = getc(); + } + } + + for (i = 0; i < sizeof(delaykey) / sizeof(delaykey[0]); i ++) { + if (delaykey[i].len > 0 && + presskey_len >= delaykey[i].len && + memcmp (presskey + presskey_len - delaykey[i].len, + delaykey[i].str, + delaykey[i].len) == 0) { + debug_bootkeys("got %skey\n", + delaykey[i].retry ? "delay" : + "stop"); + +# ifdef CONFIG_BOOT_RETRY_TIME + /* don't retry auto boot */ + if (! delaykey[i].retry) + retry_time = -1; +# endif + abort = 1; + } + } + } while (!abort && get_ticks() <= etime); + + if (!abort) + debug_bootkeys("key timeout\n"); + +#ifdef CONFIG_SILENT_CONSOLE + if (abort) + gd->flags &= ~GD_FLG_SILENT; +#endif + + return abort; +} + +# else /* !defined(CONFIG_AUTOBOOT_KEYED) */ + +#ifdef CONFIG_MENUKEY +static int menukey = 0; +#endif + +static int abortboot_normal(int bootdelay) +{ + int abort = 0; + unsigned long ts; + +#ifdef CONFIG_MENUPROMPT + printf(CONFIG_MENUPROMPT); +#else + if (bootdelay >= 0) + printf("Hit any key to stop autoboot: %2d ", bootdelay); +#endif + +#if defined CONFIG_ZERO_BOOTDELAY_CHECK + /* + * Check if key already pressed + * Don't check if bootdelay < 0 + */ + if (bootdelay >= 0) { + if (tstc()) { /* we got a key press */ + (void) getc(); /* consume input */ + puts ("\b\b\b 0"); + abort = 1; /* don't auto boot */ + } + } +#endif + + while ((bootdelay > 0) && (!abort)) { + --bootdelay; + /* delay 1000 ms */ + ts = get_timer(0); + do { + if (tstc()) { /* we got a key press */ + abort = 1; /* don't auto boot */ + bootdelay = 0; /* no more delay */ +# ifdef CONFIG_MENUKEY + menukey = getc(); +# else + (void) getc(); /* consume input */ +# endif + break; + } + udelay(10000); + } while (!abort && get_timer(ts) < 1000); + + printf("\b\b\b%2d ", bootdelay); + } + + putc('\n'); + +#ifdef CONFIG_SILENT_CONSOLE + if (abort) + gd->flags &= ~GD_FLG_SILENT; +#endif + + return abort; +} +# endif /* CONFIG_AUTOBOOT_KEYED */ + +static int abortboot(int bootdelay) +{ +#ifdef CONFIG_AUTOBOOT_KEYED + return abortboot_keyed(bootdelay); +#else + return abortboot_normal(bootdelay); +#endif +} +#endif /* CONFIG_BOOTDELAY */ + +/* + * Runs the given boot command securely. Specifically: + * - Doesn't run the command with the shell (run_command or parse_string_outer), + * since that's a lot of code surface that an attacker might exploit. + * Because of this, we don't do any argument parsing--the secure boot command + * has to be a full-fledged u-boot command. + * - Doesn't check for keypresses before booting, since that could be a + * security hole; also disables Ctrl-C. + * - Doesn't allow the command to return. + * + * Upon any failures, this function will drop into an infinite loop after + * printing the error message to console. + */ + +#if defined(CONFIG_BOOTDELAY) && defined(CONFIG_OF_CONTROL) +static void secure_boot_cmd(char *cmd) +{ + cmd_tbl_t *cmdtp; + int rc; + + if (!cmd) { + printf("## Error: Secure boot command not specified\n"); + goto err; + } + + /* Disable Ctrl-C just in case some command is used that checks it. */ + disable_ctrlc(1); + + /* Find the command directly. */ + cmdtp = find_cmd(cmd); + if (!cmdtp) { + printf("## Error: \"%s\" not defined\n", cmd); + goto err; + } + + /* Run the command, forcing no flags and faking argc and argv. */ + rc = (cmdtp->cmd)(cmdtp, 0, 1, &cmd); + + /* Shouldn't ever return from boot command. */ + printf("## Error: \"%s\" returned (code %d)\n", cmd, rc); + +err: + /* + * Not a whole lot to do here. Rebooting won't help much, since we'll + * just end up right back here. Just loop. + */ + hang(); +} + +static void process_fdt_options(const void *blob) +{ + ulong addr; + + /* Add an env variable to point to a kernel payload, if available */ + addr = fdtdec_get_config_int(gd->fdt_blob, "kernel-offset", 0); + if (addr) + setenv_addr("kernaddr", (void *)(CONFIG_SYS_TEXT_BASE + addr)); + + /* Add an env variable to point to a root disk, if available */ + addr = fdtdec_get_config_int(gd->fdt_blob, "rootdisk-offset", 0); + if (addr) + setenv_addr("rootaddr", (void *)(CONFIG_SYS_TEXT_BASE + addr)); +} +#endif /* CONFIG_OF_CONTROL */ + +#ifdef CONFIG_BOOTDELAY +static void process_boot_delay(void) +{ +#ifdef CONFIG_OF_CONTROL + char *env; +#endif + char *s; + int bootdelay; +#ifdef CONFIG_BOOTCOUNT_LIMIT + unsigned long bootcount = 0; + unsigned long bootlimit = 0; +#endif /* CONFIG_BOOTCOUNT_LIMIT */ + +#ifdef CONFIG_BOOTCOUNT_LIMIT + bootcount = bootcount_load(); + bootcount++; + bootcount_store (bootcount); + setenv_ulong("bootcount", bootcount); + bootlimit = getenv_ulong("bootlimit", 10, 0); +#endif /* CONFIG_BOOTCOUNT_LIMIT */ + + s = getenv ("bootdelay"); + bootdelay = s ? (int)simple_strtol(s, NULL, 10) : CONFIG_BOOTDELAY; + +#ifdef CONFIG_OF_CONTROL + bootdelay = fdtdec_get_config_int(gd->fdt_blob, "bootdelay", + bootdelay); +#endif + + debug ("### main_loop entered: bootdelay=%d\n\n", bootdelay); + +#if defined(CONFIG_MENU_SHOW) + bootdelay = menu_show(bootdelay); +#endif +# ifdef CONFIG_BOOT_RETRY_TIME + init_cmd_timeout (); +# endif /* CONFIG_BOOT_RETRY_TIME */ + +#ifdef CONFIG_POST + if (gd->flags & GD_FLG_POSTFAIL) { + s = getenv("failbootcmd"); + } + else +#endif /* CONFIG_POST */ +#ifdef CONFIG_BOOTCOUNT_LIMIT + if (bootlimit && (bootcount > bootlimit)) { + printf ("Warning: Bootlimit (%u) exceeded. Using altbootcmd.\n", + (unsigned)bootlimit); + s = getenv ("altbootcmd"); + } + else +#endif /* CONFIG_BOOTCOUNT_LIMIT */ + s = getenv ("bootcmd"); +#ifdef CONFIG_OF_CONTROL + /* Allow the fdt to override the boot command */ + env = fdtdec_get_config_string(gd->fdt_blob, "bootcmd"); + if (env) + s = env; + + process_fdt_options(gd->fdt_blob); + + /* + * If the bootsecure option was chosen, use secure_boot_cmd(). + * Always use 'env' in this case, since bootsecure requres that the + * bootcmd was specified in the FDT too. + */ + if (fdtdec_get_config_int(gd->fdt_blob, "bootsecure", 0)) + secure_boot_cmd(env); + +#endif /* CONFIG_OF_CONTROL */ + + debug ("### main_loop: bootcmd=\"%s\"\n", s ? s : "<UNDEFINED>"); + + if (bootdelay != -1 && s && !abortboot(bootdelay)) { +#if defined(CONFIG_AUTOBOOT_KEYED) && !defined(CONFIG_AUTOBOOT_KEYED_CTRLC) + int prev = disable_ctrlc(1); /* disable Control C checking */ +#endif + + run_command_list(s, -1, 0); + +#if defined(CONFIG_AUTOBOOT_KEYED) && !defined(CONFIG_AUTOBOOT_KEYED_CTRLC) + disable_ctrlc(prev); /* restore Control C checking */ +#endif + } + +#ifdef CONFIG_MENUKEY + if (menukey == CONFIG_MENUKEY) { + s = getenv("menucmd"); + if (s) + run_command_list(s, -1, 0); + } +#endif /* CONFIG_MENUKEY */ +} +#endif /* CONFIG_BOOTDELAY */ + +void main_loop(void) +{ +#ifndef CONFIG_SYS_HUSH_PARSER + static char lastcommand[CONFIG_SYS_CBSIZE] = { 0, }; + int len; + int rc = 1; + int flag; +#endif +#ifdef CONFIG_PREBOOT + char *p; +#endif + + bootstage_mark_name(BOOTSTAGE_ID_MAIN_LOOP, "main_loop"); + +#ifndef CONFIG_SYS_GENERIC_BOARD + puts("Warning: Your board does not use generic board. Please read\n"); + puts("doc/README.generic-board and take action. Boards not\n"); + puts("upgraded by the late 2014 may break or be removed.\n"); +#endif + +#ifdef CONFIG_MODEM_SUPPORT + debug("DEBUG: main_loop: do_mdm_init=%d\n", do_mdm_init); + if (do_mdm_init) { + char *str = strdup(getenv("mdm_cmd")); + setenv("preboot", str); /* set or delete definition */ + if (str != NULL) + free(str); + mdm_init(); /* wait for modem connection */ + } +#endif /* CONFIG_MODEM_SUPPORT */ + +#ifdef CONFIG_VERSION_VARIABLE + { + setenv("ver", version_string); /* set version variable */ + } +#endif /* CONFIG_VERSION_VARIABLE */ + +#ifdef CONFIG_SYS_HUSH_PARSER + u_boot_hush_start(); +#endif + +#if defined(CONFIG_HUSH_INIT_VAR) + hush_init_var(); +#endif + +#ifdef CONFIG_PREBOOT + p = getenv("preboot"); + if (p != NULL) { +# ifdef CONFIG_AUTOBOOT_KEYED + int prev = disable_ctrlc(1); /* disable Control C checking */ +# endif + + run_command_list(p, -1, 0); + +# ifdef CONFIG_AUTOBOOT_KEYED + disable_ctrlc(prev); /* restore Control C checking */ +# endif + } +#endif /* CONFIG_PREBOOT */ + +#if defined(CONFIG_UPDATE_TFTP) + update_tftp(0UL); +#endif /* CONFIG_UPDATE_TFTP */ + +#ifdef CONFIG_BOOTDELAY + process_boot_delay(); +#endif + /* + * Main Loop for Monitor Command Processing + */ +#ifdef CONFIG_SYS_HUSH_PARSER + parse_file_outer(); + /* This point is never reached */ + for (;;); +#else + for (;;) { +#ifdef CONFIG_BOOT_RETRY_TIME + if (rc >= 0) { + /* Saw enough of a valid command to + * restart the timeout. + */ + reset_cmd_timeout(); + } +#endif + len = readline (CONFIG_SYS_PROMPT); + + flag = 0; /* assume no special flags for now */ + if (len > 0) + strcpy (lastcommand, console_buffer); + else if (len == 0) + flag |= CMD_FLAG_REPEAT; +#ifdef CONFIG_BOOT_RETRY_TIME + else if (len == -2) { + /* -2 means timed out, retry autoboot + */ + puts ("\nTimed out waiting for command\n"); +# ifdef CONFIG_RESET_TO_RETRY + /* Reinit board to run initialization code again */ + do_reset (NULL, 0, 0, NULL); +# else + return; /* retry autoboot */ +# endif + } +#endif + + if (len == -1) + puts ("<INTERRUPT>\n"); + else + rc = run_command(lastcommand, flag); + + if (rc <= 0) { + /* invalid command or not repeatable, forget it */ + lastcommand[0] = 0; + } + } +#endif /*CONFIG_SYS_HUSH_PARSER*/ +} + +#ifdef CONFIG_BOOT_RETRY_TIME +/*************************************************************************** + * initialize command line timeout + */ +void init_cmd_timeout(void) +{ + char *s = getenv ("bootretry"); + + if (s != NULL) + retry_time = (int)simple_strtol(s, NULL, 10); + else + retry_time = CONFIG_BOOT_RETRY_TIME; + + if (retry_time >= 0 && retry_time < CONFIG_BOOT_RETRY_MIN) + retry_time = CONFIG_BOOT_RETRY_MIN; +} + +/*************************************************************************** + * reset command line timeout to retry_time seconds + */ +void reset_cmd_timeout(void) +{ + endtime = endtick(retry_time); +} +#endif + +#ifdef CONFIG_CMDLINE_EDITING + +/* + * cmdline-editing related codes from vivi. + * Author: Janghoon Lyu <nandy@mizi.com> + */ + +#define putnstr(str,n) do { \ + printf ("%.*s", (int)n, str); \ + } while (0) + +#define CTL_CH(c) ((c) - 'a' + 1) +#define CTL_BACKSPACE ('\b') +#define DEL ((char)255) +#define DEL7 ((char)127) +#define CREAD_HIST_CHAR ('!') + +#define getcmd_putch(ch) putc(ch) +#define getcmd_getch() getc() +#define getcmd_cbeep() getcmd_putch('\a') + +#define HIST_MAX 20 +#define HIST_SIZE CONFIG_SYS_CBSIZE + +static int hist_max; +static int hist_add_idx; +static int hist_cur = -1; +static unsigned hist_num; + +static char *hist_list[HIST_MAX]; +static char hist_lines[HIST_MAX][HIST_SIZE + 1]; /* Save room for NULL */ + +#define add_idx_minus_one() ((hist_add_idx == 0) ? hist_max : hist_add_idx-1) + +static void hist_init(void) +{ + int i; + + hist_max = 0; + hist_add_idx = 0; + hist_cur = -1; + hist_num = 0; + + for (i = 0; i < HIST_MAX; i++) { + hist_list[i] = hist_lines[i]; + hist_list[i][0] = '\0'; + } +} + +static void cread_add_to_hist(char *line) +{ + strcpy(hist_list[hist_add_idx], line); + + if (++hist_add_idx >= HIST_MAX) + hist_add_idx = 0; + + if (hist_add_idx > hist_max) + hist_max = hist_add_idx; + + hist_num++; +} + +static char* hist_prev(void) +{ + char *ret; + int old_cur; + + if (hist_cur < 0) + return NULL; + + old_cur = hist_cur; + if (--hist_cur < 0) + hist_cur = hist_max; + + if (hist_cur == hist_add_idx) { + hist_cur = old_cur; + ret = NULL; + } else + ret = hist_list[hist_cur]; + + return (ret); +} + +static char* hist_next(void) +{ + char *ret; + + if (hist_cur < 0) + return NULL; + + if (hist_cur == hist_add_idx) + return NULL; + + if (++hist_cur > hist_max) + hist_cur = 0; + + if (hist_cur == hist_add_idx) { + ret = ""; + } else + ret = hist_list[hist_cur]; + + return (ret); +} + +#ifndef CONFIG_CMDLINE_EDITING +static void cread_print_hist_list(void) +{ + int i; + unsigned long n; + + n = hist_num - hist_max; + + i = hist_add_idx + 1; + while (1) { + if (i > hist_max) + i = 0; + if (i == hist_add_idx) + break; + printf("%s\n", hist_list[i]); + n++; + i++; + } +} +#endif /* CONFIG_CMDLINE_EDITING */ + +#define BEGINNING_OF_LINE() { \ + while (num) { \ + getcmd_putch(CTL_BACKSPACE); \ + num--; \ + } \ +} + +#define ERASE_TO_EOL() { \ + if (num < eol_num) { \ + printf("%*s", (int)(eol_num - num), ""); \ + do { \ + getcmd_putch(CTL_BACKSPACE); \ + } while (--eol_num > num); \ + } \ +} + +#define REFRESH_TO_EOL() { \ + if (num < eol_num) { \ + wlen = eol_num - num; \ + putnstr(buf + num, wlen); \ + num = eol_num; \ + } \ +} + +static void cread_add_char(char ichar, int insert, unsigned long *num, + unsigned long *eol_num, char *buf, unsigned long len) +{ + unsigned long wlen; + + /* room ??? */ + if (insert || *num == *eol_num) { + if (*eol_num > len - 1) { + getcmd_cbeep(); + return; + } + (*eol_num)++; + } + + if (insert) { + wlen = *eol_num - *num; + if (wlen > 1) { + memmove(&buf[*num+1], &buf[*num], wlen-1); + } + + buf[*num] = ichar; + putnstr(buf + *num, wlen); + (*num)++; + while (--wlen) { + getcmd_putch(CTL_BACKSPACE); + } + } else { + /* echo the character */ + wlen = 1; + buf[*num] = ichar; + putnstr(buf + *num, wlen); + (*num)++; + } +} + +static void cread_add_str(char *str, int strsize, int insert, unsigned long *num, + unsigned long *eol_num, char *buf, unsigned long len) +{ + while (strsize--) { + cread_add_char(*str, insert, num, eol_num, buf, len); + str++; + } +} + +static int cread_line(const char *const prompt, char *buf, unsigned int *len, + int timeout) +{ + unsigned long num = 0; + unsigned long eol_num = 0; + unsigned long wlen; + char ichar; + int insert = 1; + int esc_len = 0; + char esc_save[8]; + int init_len = strlen(buf); + int first = 1; + + if (init_len) + cread_add_str(buf, init_len, 1, &num, &eol_num, buf, *len); + + while (1) { +#ifdef CONFIG_BOOT_RETRY_TIME + while (!tstc()) { /* while no incoming data */ + if (retry_time >= 0 && get_ticks() > endtime) + return (-2); /* timed out */ + WATCHDOG_RESET(); + } +#endif + if (first && timeout) { + uint64_t etime = endtick(timeout); + + while (!tstc()) { /* while no incoming data */ + if (get_ticks() >= etime) + return -2; /* timed out */ + WATCHDOG_RESET(); + } + first = 0; + } + + ichar = getcmd_getch(); + + if ((ichar == '\n') || (ichar == '\r')) { + putc('\n'); + break; + } + + /* + * handle standard linux xterm esc sequences for arrow key, etc. + */ + if (esc_len != 0) { + if (esc_len == 1) { + if (ichar == '[') { + esc_save[esc_len] = ichar; + esc_len = 2; + } else { + cread_add_str(esc_save, esc_len, insert, + &num, &eol_num, buf, *len); + esc_len = 0; + } + continue; + } + + switch (ichar) { + + case 'D': /* <- key */ + ichar = CTL_CH('b'); + esc_len = 0; + break; + case 'C': /* -> key */ + ichar = CTL_CH('f'); + esc_len = 0; + break; /* pass off to ^F handler */ + case 'H': /* Home key */ + ichar = CTL_CH('a'); + esc_len = 0; + break; /* pass off to ^A handler */ + case 'A': /* up arrow */ + ichar = CTL_CH('p'); + esc_len = 0; + break; /* pass off to ^P handler */ + case 'B': /* down arrow */ + ichar = CTL_CH('n'); + esc_len = 0; + break; /* pass off to ^N handler */ + default: + esc_save[esc_len++] = ichar; + cread_add_str(esc_save, esc_len, insert, + &num, &eol_num, buf, *len); + esc_len = 0; + continue; + } + } + + switch (ichar) { + case 0x1b: + if (esc_len == 0) { + esc_save[esc_len] = ichar; + esc_len = 1; + } else { + puts("impossible condition #876\n"); + esc_len = 0; + } + break; + + case CTL_CH('a'): + BEGINNING_OF_LINE(); + break; + case CTL_CH('c'): /* ^C - break */ + *buf = '\0'; /* discard input */ + return (-1); + case CTL_CH('f'): + if (num < eol_num) { + getcmd_putch(buf[num]); + num++; + } + break; + case CTL_CH('b'): + if (num) { + getcmd_putch(CTL_BACKSPACE); + num--; + } + break; + case CTL_CH('d'): + if (num < eol_num) { + wlen = eol_num - num - 1; + if (wlen) { + memmove(&buf[num], &buf[num+1], wlen); + putnstr(buf + num, wlen); + } + + getcmd_putch(' '); + do { + getcmd_putch(CTL_BACKSPACE); + } while (wlen--); + eol_num--; + } + break; + case CTL_CH('k'): + ERASE_TO_EOL(); + break; + case CTL_CH('e'): + REFRESH_TO_EOL(); + break; + case CTL_CH('o'): + insert = !insert; + break; + case CTL_CH('x'): + case CTL_CH('u'): + BEGINNING_OF_LINE(); + ERASE_TO_EOL(); + break; + case DEL: + case DEL7: + case 8: + if (num) { + wlen = eol_num - num; + num--; + memmove(&buf[num], &buf[num+1], wlen); + getcmd_putch(CTL_BACKSPACE); + putnstr(buf + num, wlen); + getcmd_putch(' '); + do { + getcmd_putch(CTL_BACKSPACE); + } while (wlen--); + eol_num--; + } + break; + case CTL_CH('p'): + case CTL_CH('n'): + { + char * hline; + + esc_len = 0; + + if (ichar == CTL_CH('p')) + hline = hist_prev(); + else + hline = hist_next(); + + if (!hline) { + getcmd_cbeep(); + continue; + } + + /* nuke the current line */ + /* first, go home */ + BEGINNING_OF_LINE(); + + /* erase to end of line */ + ERASE_TO_EOL(); + + /* copy new line into place and display */ + strcpy(buf, hline); + eol_num = strlen(buf); + REFRESH_TO_EOL(); + continue; + } +#ifdef CONFIG_AUTO_COMPLETE + case '\t': { + int num2, col; + + /* do not autocomplete when in the middle */ + if (num < eol_num) { + getcmd_cbeep(); + break; + } + + buf[num] = '\0'; + col = strlen(prompt) + eol_num; + num2 = num; + if (cmd_auto_complete(prompt, buf, &num2, &col)) { + col = num2 - num; + num += col; + eol_num += col; + } + break; + } +#endif + default: + cread_add_char(ichar, insert, &num, &eol_num, buf, *len); + break; + } + } + *len = eol_num; + buf[eol_num] = '\0'; /* lose the newline */ + + if (buf[0] && buf[0] != CREAD_HIST_CHAR) + cread_add_to_hist(buf); + hist_cur = hist_add_idx; + + return 0; +} + +#endif /* CONFIG_CMDLINE_EDITING */ + +/****************************************************************************/ + +/* + * Prompt for input and read a line. + * If CONFIG_BOOT_RETRY_TIME is defined and retry_time >= 0, + * time out when time goes past endtime (timebase time in ticks). + * Return: number of read characters + * -1 if break + * -2 if timed out + */ +int readline (const char *const prompt) +{ + /* + * If console_buffer isn't 0-length the user will be prompted to modify + * it instead of entering it from scratch as desired. + */ + console_buffer[0] = '\0'; + + return readline_into_buffer(prompt, console_buffer, 0); +} + + +int readline_into_buffer(const char *const prompt, char *buffer, int timeout) +{ + char *p = buffer; +#ifdef CONFIG_CMDLINE_EDITING + unsigned int len = CONFIG_SYS_CBSIZE; + int rc; + static int initted = 0; + + /* + * History uses a global array which is not + * writable until after relocation to RAM. + * Revert to non-history version if still + * running from flash. + */ + if (gd->flags & GD_FLG_RELOC) { + if (!initted) { + hist_init(); + initted = 1; + } + + if (prompt) + puts (prompt); + + rc = cread_line(prompt, p, &len, timeout); + return rc < 0 ? rc : len; + + } else { +#endif /* CONFIG_CMDLINE_EDITING */ + char * p_buf = p; + int n = 0; /* buffer index */ + int plen = 0; /* prompt length */ + int col; /* output column cnt */ + char c; + + /* print prompt */ + if (prompt) { + plen = strlen (prompt); + puts (prompt); + } + col = plen; + + for (;;) { +#ifdef CONFIG_BOOT_RETRY_TIME + while (!tstc()) { /* while no incoming data */ + if (retry_time >= 0 && get_ticks() > endtime) + return (-2); /* timed out */ + WATCHDOG_RESET(); + } +#endif + WATCHDOG_RESET(); /* Trigger watchdog, if needed */ + +#ifdef CONFIG_SHOW_ACTIVITY + while (!tstc()) { + show_activity(0); + WATCHDOG_RESET(); + } +#endif + c = getc(); + + /* + * Special character handling + */ + switch (c) { + case '\r': /* Enter */ + case '\n': + *p = '\0'; + puts ("\r\n"); + return p - p_buf; + + case '\0': /* nul */ + continue; + + case 0x03: /* ^C - break */ + p_buf[0] = '\0'; /* discard input */ + return -1; + + case 0x15: /* ^U - erase line */ + while (col > plen) { + puts (erase_seq); + --col; + } + p = p_buf; + n = 0; + continue; + + case 0x17: /* ^W - erase word */ + p=delete_char(p_buf, p, &col, &n, plen); + while ((n > 0) && (*p != ' ')) { + p=delete_char(p_buf, p, &col, &n, plen); + } + continue; + + case 0x08: /* ^H - backspace */ + case 0x7F: /* DEL - backspace */ + p=delete_char(p_buf, p, &col, &n, plen); + continue; + + default: + /* + * Must be a normal character then + */ + if (n < CONFIG_SYS_CBSIZE-2) { + if (c == '\t') { /* expand TABs */ +#ifdef CONFIG_AUTO_COMPLETE + /* if auto completion triggered just continue */ + *p = '\0'; + if (cmd_auto_complete(prompt, console_buffer, &n, &col)) { + p = p_buf + n; /* reset */ + continue; + } +#endif + puts (tab_seq+(col&07)); + col += 8 - (col&07); + } else { + char buf[2]; + + /* + * Echo input using puts() to force an + * LCD flush if we are using an LCD + */ + ++col; + buf[0] = c; + buf[1] = '\0'; + puts(buf); + } + *p++ = c; + ++n; + } else { /* Buffer full */ + putc ('\a'); + } + } + } +#ifdef CONFIG_CMDLINE_EDITING + } +#endif +} + +/****************************************************************************/ + +static char * delete_char (char *buffer, char *p, int *colp, int *np, int plen) +{ + char *s; + + if (*np == 0) { + return (p); + } + + if (*(--p) == '\t') { /* will retype the whole line */ + while (*colp > plen) { + puts (erase_seq); + (*colp)--; + } + for (s=buffer; s<p; ++s) { + if (*s == '\t') { + puts (tab_seq+((*colp) & 07)); + *colp += 8 - ((*colp) & 07); + } else { + ++(*colp); + putc (*s); + } + } + } else { + puts (erase_seq); + (*colp)--; + } + (*np)--; + return (p); +} + +/****************************************************************************/ + +int parse_line (char *line, char *argv[]) +{ + int nargs = 0; + + debug_parser("parse_line: \"%s\"\n", line); + while (nargs < CONFIG_SYS_MAXARGS) { + + /* skip any white space */ + while (isblank(*line)) + ++line; + + if (*line == '\0') { /* end of line, no more args */ + argv[nargs] = NULL; + debug_parser("parse_line: nargs=%d\n", nargs); + return nargs; + } + + argv[nargs++] = line; /* begin of argument string */ + + /* find end of string */ + while (*line && !isblank(*line)) + ++line; + + if (*line == '\0') { /* end of line, no more args */ + argv[nargs] = NULL; + debug_parser("parse_line: nargs=%d\n", nargs); + return nargs; + } + + *line++ = '\0'; /* terminate current arg */ + } + + printf ("** Too many args (max. %d) **\n", CONFIG_SYS_MAXARGS); + + debug_parser("parse_line: nargs=%d\n", nargs); + return (nargs); +} + +/****************************************************************************/ + +#ifndef CONFIG_SYS_HUSH_PARSER +static void process_macros (const char *input, char *output) +{ + char c, prev; + const char *varname_start = NULL; + int inputcnt = strlen (input); + int outputcnt = CONFIG_SYS_CBSIZE; + int state = 0; /* 0 = waiting for '$' */ + + /* 1 = waiting for '(' or '{' */ + /* 2 = waiting for ')' or '}' */ + /* 3 = waiting for ''' */ + char *output_start = output; + + debug_parser("[PROCESS_MACROS] INPUT len %zd: \"%s\"\n", strlen(input), + input); + + prev = '\0'; /* previous character */ + + while (inputcnt && outputcnt) { + c = *input++; + inputcnt--; + + if (state != 3) { + /* remove one level of escape characters */ + if ((c == '\\') && (prev != '\\')) { + if (inputcnt-- == 0) + break; + prev = c; + c = *input++; + } + } + + switch (state) { + case 0: /* Waiting for (unescaped) $ */ + if ((c == '\'') && (prev != '\\')) { + state = 3; + break; + } + if ((c == '$') && (prev != '\\')) { + state++; + } else { + *(output++) = c; + outputcnt--; + } + break; + case 1: /* Waiting for ( */ + if (c == '(' || c == '{') { + state++; + varname_start = input; + } else { + state = 0; + *(output++) = '$'; + outputcnt--; + + if (outputcnt) { + *(output++) = c; + outputcnt--; + } + } + break; + case 2: /* Waiting for ) */ + if (c == ')' || c == '}') { + int i; + char envname[CONFIG_SYS_CBSIZE], *envval; + int envcnt = input - varname_start - 1; /* Varname # of chars */ + + /* Get the varname */ + for (i = 0; i < envcnt; i++) { + envname[i] = varname_start[i]; + } + envname[i] = 0; + + /* Get its value */ + envval = getenv (envname); + + /* Copy into the line if it exists */ + if (envval != NULL) + while ((*envval) && outputcnt) { + *(output++) = *(envval++); + outputcnt--; + } + /* Look for another '$' */ + state = 0; + } + break; + case 3: /* Waiting for ' */ + if ((c == '\'') && (prev != '\\')) { + state = 0; + } else { + *(output++) = c; + outputcnt--; + } + break; + } + prev = c; + } + + if (outputcnt) + *output = 0; + else + *(output - 1) = 0; + + debug_parser("[PROCESS_MACROS] OUTPUT len %zd: \"%s\"\n", + strlen(output_start), output_start); +} + +/**************************************************************************** + * returns: + * 1 - command executed, repeatable + * 0 - command executed but not repeatable, interrupted commands are + * always considered not repeatable + * -1 - not executed (unrecognized, bootd recursion or too many args) + * (If cmd is NULL or "" or longer than CONFIG_SYS_CBSIZE-1 it is + * considered unrecognized) + * + * WARNING: + * + * We must create a temporary copy of the command since the command we get + * may be the result from getenv(), which returns a pointer directly to + * the environment data, which may change magicly when the command we run + * creates or modifies environment variables (like "bootp" does). + */ +static int builtin_run_command(const char *cmd, int flag) +{ + char cmdbuf[CONFIG_SYS_CBSIZE]; /* working copy of cmd */ + char *token; /* start of token in cmdbuf */ + char *sep; /* end of token (separator) in cmdbuf */ + char finaltoken[CONFIG_SYS_CBSIZE]; + char *str = cmdbuf; + char *argv[CONFIG_SYS_MAXARGS + 1]; /* NULL terminated */ + int argc, inquotes; + int repeatable = 1; + int rc = 0; + + debug_parser("[RUN_COMMAND] cmd[%p]=\"", cmd); + if (DEBUG_PARSER) { + /* use puts - string may be loooong */ + puts(cmd ? cmd : "NULL"); + puts("\"\n"); + } + clear_ctrlc(); /* forget any previous Control C */ + + if (!cmd || !*cmd) { + return -1; /* empty command */ + } + + if (strlen(cmd) >= CONFIG_SYS_CBSIZE) { + puts ("## Command too long!\n"); + return -1; + } + + strcpy (cmdbuf, cmd); + + /* Process separators and check for invalid + * repeatable commands + */ + + debug_parser("[PROCESS_SEPARATORS] %s\n", cmd); + while (*str) { + + /* + * Find separator, or string end + * Allow simple escape of ';' by writing "\;" + */ + for (inquotes = 0, sep = str; *sep; sep++) { + if ((*sep=='\'') && + (*(sep-1) != '\\')) + inquotes=!inquotes; + + if (!inquotes && + (*sep == ';') && /* separator */ + ( sep != str) && /* past string start */ + (*(sep-1) != '\\')) /* and NOT escaped */ + break; + } + + /* + * Limit the token to data between separators + */ + token = str; + if (*sep) { + str = sep + 1; /* start of command for next pass */ + *sep = '\0'; + } + else + str = sep; /* no more commands for next pass */ + debug_parser("token: \"%s\"\n", token); + + /* find macros in this token and replace them */ + process_macros (token, finaltoken); + + /* Extract arguments */ + if ((argc = parse_line (finaltoken, argv)) == 0) { + rc = -1; /* no command at all */ + continue; + } + + if (cmd_process(flag, argc, argv, &repeatable, NULL)) + rc = -1; + + /* Did the user stop this? */ + if (had_ctrlc ()) + return -1; /* if stopped then not repeatable */ + } + + return rc ? rc : repeatable; +} +#endif + +/* + * Run a command using the selected parser. + * + * @param cmd Command to run + * @param flag Execution flags (CMD_FLAG_...) + * @return 0 on success, or != 0 on error. + */ +int run_command(const char *cmd, int flag) +{ +#ifndef CONFIG_SYS_HUSH_PARSER + /* + * builtin_run_command can return 0 or 1 for success, so clean up + * its result. + */ + if (builtin_run_command(cmd, flag) == -1) + return 1; + + return 0; +#else + return parse_string_outer(cmd, + FLAG_PARSE_SEMICOLON | FLAG_EXIT_FROM_LOOP); +#endif +} + +#ifndef CONFIG_SYS_HUSH_PARSER +/** + * Execute a list of command separated by ; or \n using the built-in parser. + * + * This function cannot take a const char * for the command, since if it + * finds newlines in the string, it replaces them with \0. + * + * @param cmd String containing list of commands + * @param flag Execution flags (CMD_FLAG_...) + * @return 0 on success, or != 0 on error. + */ +static int builtin_run_command_list(char *cmd, int flag) +{ + char *line, *next; + int rcode = 0; + + /* + * Break into individual lines, and execute each line; terminate on + * error. + */ + line = next = cmd; + while (*next) { + if (*next == '\n') { + *next = '\0'; + /* run only non-empty commands */ + if (*line) { + debug("** exec: \"%s\"\n", line); + if (builtin_run_command(line, 0) < 0) { + rcode = 1; + break; + } + } + line = next + 1; + } + ++next; + } + if (rcode == 0 && *line) + rcode = (builtin_run_command(line, 0) >= 0); + + return rcode; +} +#endif + +int run_command_list(const char *cmd, int len, int flag) +{ + int need_buff = 1; + char *buff = (char *)cmd; /* cast away const */ + int rcode = 0; + + if (len == -1) { + len = strlen(cmd); +#ifdef CONFIG_SYS_HUSH_PARSER + /* hush will never change our string */ + need_buff = 0; +#else + /* the built-in parser will change our string if it sees \n */ + need_buff = strchr(cmd, '\n') != NULL; +#endif + } + if (need_buff) { + buff = malloc(len + 1); + if (!buff) + return 1; + memcpy(buff, cmd, len); + buff[len] = '\0'; + } +#ifdef CONFIG_SYS_HUSH_PARSER + rcode = parse_string_outer(buff, FLAG_PARSE_SEMICOLON); +#else + /* + * This function will overwrite any \n it sees with a \0, which + * is why it can't work with a const char *. Here we are making + * using of internal knowledge of this function, to avoid always + * doing a malloc() which is actually required only in a case that + * is pretty rare. + */ + rcode = builtin_run_command_list(buff, flag); + if (need_buff) + free(buff); +#endif + + return rcode; +} + +/****************************************************************************/ + +#if defined(CONFIG_CMD_RUN) +int do_run (cmd_tbl_t * cmdtp, int flag, int argc, char * const argv[]) +{ + int i; + + if (argc < 2) + return CMD_RET_USAGE; + + for (i=1; i<argc; ++i) { + char *arg; + + if ((arg = getenv (argv[i])) == NULL) { + printf ("## Error: \"%s\" not defined\n", argv[i]); + return 1; + } + + if (run_command_list(arg, -1, flag) != 0) + return 1; + } + return 0; +} +#endif diff --git a/qemu/roms/u-boot/common/memsize.c b/qemu/roms/u-boot/common/memsize.c new file mode 100644 index 000000000..589400d3b --- /dev/null +++ b/qemu/roms/u-boot/common/memsize.c @@ -0,0 +1,92 @@ +/* + * (C) Copyright 2004 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> + +DECLARE_GLOBAL_DATA_PTR; + +#ifdef __PPC__ +/* + * At least on G2 PowerPC cores, sequential accesses to non-existent + * memory must be synchronized. + */ +# include <asm/io.h> /* for sync() */ +#else +# define sync() /* nothing */ +#endif + +/* + * Check memory range for valid RAM. A simple memory test determines + * the actually available RAM size between addresses `base' and + * `base + maxsize'. + */ +long get_ram_size(long *base, long maxsize) +{ + volatile long *addr; + long save[32]; + long cnt; + long val; + long size; + int i = 0; + + for (cnt = (maxsize / sizeof (long)) >> 1; cnt > 0; cnt >>= 1) { + addr = base + cnt; /* pointer arith! */ + sync (); + save[i++] = *addr; + sync (); + *addr = ~cnt; + } + + addr = base; + sync (); + save[i] = *addr; + sync (); + *addr = 0; + + sync (); + if ((val = *addr) != 0) { + /* Restore the original data before leaving the function. + */ + sync (); + *addr = save[i]; + for (cnt = 1; cnt < maxsize / sizeof(long); cnt <<= 1) { + addr = base + cnt; + sync (); + *addr = save[--i]; + } + return (0); + } + + for (cnt = 1; cnt < maxsize / sizeof (long); cnt <<= 1) { + addr = base + cnt; /* pointer arith! */ + val = *addr; + *addr = save[--i]; + if (val != ~cnt) { + size = cnt * sizeof (long); + /* Restore the original data before leaving the function. + */ + for (cnt <<= 1; cnt < maxsize / sizeof (long); cnt <<= 1) { + addr = base + cnt; + *addr = save[--i]; + } + return (size); + } + } + + return (maxsize); +} + +phys_size_t __weak get_effective_memsize(void) +{ +#ifndef CONFIG_VERY_BIG_RAM + return gd->ram_size; +#else + /* limit stack to what we can reasonable map */ + return ((gd->ram_size > CONFIG_MAX_MEM_MAPPED) ? + CONFIG_MAX_MEM_MAPPED : gd->ram_size); +#endif +} diff --git a/qemu/roms/u-boot/common/menu.c b/qemu/roms/u-boot/common/menu.c new file mode 100644 index 000000000..ba393adc3 --- /dev/null +++ b/qemu/roms/u-boot/common/menu.c @@ -0,0 +1,413 @@ +/* + * Copyright 2010-2011 Calxeda, Inc. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <malloc.h> +#include <errno.h> +#include <linux/list.h> + +#include "menu.h" + +/* + * Internally, each item in a menu is represented by a struct menu_item. + * + * These items will be alloc'd and initialized by menu_item_add and destroyed + * by menu_item_destroy, and the consumer of the interface never sees that + * this struct is used at all. + */ +struct menu_item { + char *key; + void *data; + struct list_head list; +}; + +/* + * The menu is composed of a list of items along with settings and callbacks + * provided by the user. An incomplete definition of this struct is available + * in menu.h, but the full definition is here to prevent consumers from + * relying on its contents. + */ +struct menu { + struct menu_item *default_item; + int timeout; + char *title; + int prompt; + void (*item_data_print)(void *); + char *(*item_choice)(void *); + void *item_choice_data; + struct list_head items; +}; + +/* + * An iterator function for menu items. callback will be called for each item + * in m, with m, a pointer to the item, and extra being passed to callback. If + * callback returns a value other than NULL, iteration stops and the value + * return by callback is returned from menu_items_iter. This allows it to be + * used for search type operations. It is also safe for callback to remove the + * item from the list of items. + */ +static inline void *menu_items_iter(struct menu *m, + void *(*callback)(struct menu *, struct menu_item *, void *), + void *extra) +{ + struct list_head *pos, *n; + struct menu_item *item; + void *ret; + + list_for_each_safe(pos, n, &m->items) { + item = list_entry(pos, struct menu_item, list); + + ret = callback(m, item, extra); + + if (ret) + return ret; + } + + return NULL; +} + +/* + * Print a menu_item. If the consumer provided an item_data_print function + * when creating the menu, call it with a pointer to the item's private data. + * Otherwise, print the key of the item. + */ +static inline void *menu_item_print(struct menu *m, + struct menu_item *item, + void *extra) +{ + if (!m->item_data_print) { + puts(item->key); + putc('\n'); + } else { + m->item_data_print(item->data); + } + + return NULL; +} + +/* + * Free the memory used by a menu item. This includes the memory used by its + * key. + */ +static inline void *menu_item_destroy(struct menu *m, + struct menu_item *item, + void *extra) +{ + if (item->key) + free(item->key); + + free(item); + + return NULL; +} + +void __menu_display_statusline(struct menu *m) +{ + return; +} +void menu_display_statusline(struct menu *m) + __attribute__ ((weak, alias("__menu_display_statusline"))); + +/* + * Display a menu so the user can make a choice of an item. First display its + * title, if any, and then each item in the menu. + */ +static inline void menu_display(struct menu *m) +{ + if (m->title) { + puts(m->title); + putc('\n'); + } + menu_display_statusline(m); + + menu_items_iter(m, menu_item_print, NULL); +} + +/* + * Check if an item's key matches a provided string, pointed to by extra. If + * extra is NULL, an item with a NULL key will match. Otherwise, the item's + * key has to match according to strcmp. + * + * This is called via menu_items_iter, so it returns a pointer to the item if + * the key matches, and returns NULL otherwise. + */ +static inline void *menu_item_key_match(struct menu *m, + struct menu_item *item, void *extra) +{ + char *item_key = extra; + + if (!item_key || !item->key) { + if (item_key == item->key) + return item; + + return NULL; + } + + if (strcmp(item->key, item_key) == 0) + return item; + + return NULL; +} + +/* + * Find the first item with a key matching item_key, if any exists. + */ +static inline struct menu_item *menu_item_by_key(struct menu *m, + char *item_key) +{ + return menu_items_iter(m, menu_item_key_match, item_key); +} + +/* + * Set *choice to point to the default item's data, if any default item was + * set, and returns 1. If no default item was set, returns -ENOENT. + */ +int menu_default_choice(struct menu *m, void **choice) +{ + if (m->default_item) { + *choice = m->default_item->data; + return 1; + } + + return -ENOENT; +} + +/* + * Displays the menu and asks the user to choose an item. *choice will point + * to the private data of the item the user chooses. The user makes a choice + * by inputting a string matching the key of an item. Invalid choices will + * cause the user to be prompted again, repeatedly, until the user makes a + * valid choice. The user can exit the menu without making a choice via ^c. + * + * Returns 1 if the user made a choice, or -EINTR if they bail via ^c. + */ +static inline int menu_interactive_choice(struct menu *m, void **choice) +{ + char cbuf[CONFIG_SYS_CBSIZE]; + struct menu_item *choice_item = NULL; + int readret; + + while (!choice_item) { + cbuf[0] = '\0'; + + menu_display(m); + + if (!m->item_choice) { + readret = readline_into_buffer("Enter choice: ", cbuf, + m->timeout / 10); + + if (readret >= 0) { + choice_item = menu_item_by_key(m, cbuf); + if (!choice_item) + printf("%s not found\n", cbuf); + } else { + return menu_default_choice(m, choice); + } + } else { + char *key = m->item_choice(m->item_choice_data); + + if (key) + choice_item = menu_item_by_key(m, key); + } + + if (!choice_item) + m->timeout = 0; + } + + *choice = choice_item->data; + + return 1; +} + +/* + * menu_default_set() - Sets the default choice for the menu. This is safe to + * call more than once on a menu. + * + * m - Points to a menu created by menu_create(). + * + * item_key - Points to a string that, when compared using strcmp, matches the + * key for an existing item in the menu. + * + * Returns 1 if successful, -EINVAL if m is NULL, or -ENOENT if no item with a + * key matching item_key is found. + */ +int menu_default_set(struct menu *m, char *item_key) +{ + struct menu_item *item; + + if (!m) + return -EINVAL; + + item = menu_item_by_key(m, item_key); + + if (!item) + return -ENOENT; + + m->default_item = item; + + return 1; +} + +/* + * menu_get_choice() - Returns the user's selected menu entry, or the default + * if the menu is set to not prompt or the timeout expires. This is safe to + * call more than once. + * + * m - Points to a menu created by menu_create(). + * + * choice - Points to a location that will store a pointer to the selected + * menu item. If no item is selected or there is an error, no value will be + * written at the location it points to. + * + * Returns 1 if successful, -EINVAL if m or choice is NULL, -ENOENT if no + * default has been set and the menu is set to not prompt or the timeout + * expires, or -EINTR if the user exits the menu via ^c. + */ +int menu_get_choice(struct menu *m, void **choice) +{ + if (!m || !choice) + return -EINVAL; + + if (!m->prompt) + return menu_default_choice(m, choice); + + return menu_interactive_choice(m, choice); +} + +/* + * menu_item_add() - Adds or replaces a menu item. Note that this replaces the + * data of an item if it already exists, but doesn't change the order of the + * item. + * + * m - Points to a menu created by menu_create(). + * + * item_key - Points to a string that will uniquely identify the item. The + * string will be copied to internal storage, and is safe to discard after + * passing to menu_item_add. + * + * item_data - An opaque pointer associated with an item. It is never + * dereferenced internally, but will be passed to the item_data_print, and + * will be returned from menu_get_choice if the menu item is selected. + * + * Returns 1 if successful, -EINVAL if m is NULL, or -ENOMEM if there is + * insufficient memory to add the menu item. + */ +int menu_item_add(struct menu *m, char *item_key, void *item_data) +{ + struct menu_item *item; + + if (!m) + return -EINVAL; + + item = menu_item_by_key(m, item_key); + + if (item) { + item->data = item_data; + return 1; + } + + item = malloc(sizeof *item); + if (!item) + return -ENOMEM; + + item->key = strdup(item_key); + + if (!item->key) { + free(item); + return -ENOMEM; + } + + item->data = item_data; + + list_add_tail(&item->list, &m->items); + + return 1; +} + +/* + * menu_create() - Creates a menu handle with default settings + * + * title - If not NULL, points to a string that will be displayed before the + * list of menu items. It will be copied to internal storage, and is safe to + * discard after passing to menu_create(). + * + * timeout - A delay in seconds to wait for user input. If 0, timeout is + * disabled, and the default choice will be returned unless prompt is 1. + * + * prompt - If 0, don't ask for user input unless there is an interrupted + * timeout. If 1, the user will be prompted for input regardless of the value + * of timeout. + * + * item_data_print - If not NULL, will be called for each item when the menu + * is displayed, with the pointer to the item's data passed as the argument. + * If NULL, each item's key will be printed instead. Since an item's key is + * what must be entered to select an item, the item_data_print function should + * make it obvious what the key for each entry is. + * + * item_choice - If not NULL, will be called when asking the user to choose an + * item. Returns a key string corresponding to the choosen item or NULL if + * no item has been selected. + * + * item_choice_data - Will be passed as the argument to the item_choice function + * + * Returns a pointer to the menu if successful, or NULL if there is + * insufficient memory available to create the menu. + */ +struct menu *menu_create(char *title, int timeout, int prompt, + void (*item_data_print)(void *), + char *(*item_choice)(void *), + void *item_choice_data) +{ + struct menu *m; + + m = malloc(sizeof *m); + + if (!m) + return NULL; + + m->default_item = NULL; + m->prompt = prompt; + m->timeout = timeout; + m->item_data_print = item_data_print; + m->item_choice = item_choice; + m->item_choice_data = item_choice_data; + + if (title) { + m->title = strdup(title); + if (!m->title) { + free(m); + return NULL; + } + } else + m->title = NULL; + + + INIT_LIST_HEAD(&m->items); + + return m; +} + +/* + * menu_destroy() - frees the memory used by a menu and its items. + * + * m - Points to a menu created by menu_create(). + * + * Returns 1 if successful, or -EINVAL if m is NULL. + */ +int menu_destroy(struct menu *m) +{ + if (!m) + return -EINVAL; + + menu_items_iter(m, menu_item_destroy, NULL); + + if (m->title) + free(m->title); + + free(m); + + return 1; +} diff --git a/qemu/roms/u-boot/common/miiphyutil.c b/qemu/roms/u-boot/common/miiphyutil.c new file mode 100644 index 000000000..74812e6e1 --- /dev/null +++ b/qemu/roms/u-boot/common/miiphyutil.c @@ -0,0 +1,584 @@ +/* + * (C) Copyright 2001 + * Gerald Van Baren, Custom IDEAS, vanbaren@cideas.com. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/* + * This provides a bit-banged interface to the ethernet MII management + * channel. + */ + +#include <common.h> +#include <miiphy.h> +#include <phy.h> + +#include <asm/types.h> +#include <linux/list.h> +#include <malloc.h> +#include <net.h> + +/* local debug macro */ +#undef MII_DEBUG + +#undef debug +#ifdef MII_DEBUG +#define debug(fmt, args...) printf(fmt, ##args) +#else +#define debug(fmt, args...) +#endif /* MII_DEBUG */ + +static struct list_head mii_devs; +static struct mii_dev *current_mii; + +/* + * Lookup the mii_dev struct by the registered device name. + */ +struct mii_dev *miiphy_get_dev_by_name(const char *devname) +{ + struct list_head *entry; + struct mii_dev *dev; + + if (!devname) { + printf("NULL device name!\n"); + return NULL; + } + + list_for_each(entry, &mii_devs) { + dev = list_entry(entry, struct mii_dev, link); + if (strcmp(dev->name, devname) == 0) + return dev; + } + + return NULL; +} + +/***************************************************************************** + * + * Initialize global data. Need to be called before any other miiphy routine. + */ +void miiphy_init(void) +{ + INIT_LIST_HEAD(&mii_devs); + current_mii = NULL; +} + +static int legacy_miiphy_read(struct mii_dev *bus, int addr, int devad, int reg) +{ + unsigned short val; + int ret; + struct legacy_mii_dev *ldev = bus->priv; + + ret = ldev->read(bus->name, addr, reg, &val); + + return ret ? -1 : (int)val; +} + +static int legacy_miiphy_write(struct mii_dev *bus, int addr, int devad, + int reg, u16 val) +{ + struct legacy_mii_dev *ldev = bus->priv; + + return ldev->write(bus->name, addr, reg, val); +} + +/***************************************************************************** + * + * Register read and write MII access routines for the device <name>. + * This API is now deprecated. Please use mdio_alloc and mdio_register, instead. + */ +void miiphy_register(const char *name, + int (*read)(const char *devname, unsigned char addr, + unsigned char reg, unsigned short *value), + int (*write)(const char *devname, unsigned char addr, + unsigned char reg, unsigned short value)) +{ + struct mii_dev *new_dev; + struct legacy_mii_dev *ldev; + + BUG_ON(strlen(name) >= MDIO_NAME_LEN); + + /* check if we have unique name */ + new_dev = miiphy_get_dev_by_name(name); + if (new_dev) { + printf("miiphy_register: non unique device name '%s'\n", name); + return; + } + + /* allocate memory */ + new_dev = mdio_alloc(); + ldev = malloc(sizeof(*ldev)); + + if (new_dev == NULL || ldev == NULL) { + printf("miiphy_register: cannot allocate memory for '%s'\n", + name); + return; + } + + /* initalize mii_dev struct fields */ + new_dev->read = legacy_miiphy_read; + new_dev->write = legacy_miiphy_write; + strncpy(new_dev->name, name, MDIO_NAME_LEN); + new_dev->name[MDIO_NAME_LEN - 1] = 0; + ldev->read = read; + ldev->write = write; + new_dev->priv = ldev; + + debug("miiphy_register: added '%s', read=0x%08lx, write=0x%08lx\n", + new_dev->name, ldev->read, ldev->write); + + /* add it to the list */ + list_add_tail(&new_dev->link, &mii_devs); + + if (!current_mii) + current_mii = new_dev; +} + +struct mii_dev *mdio_alloc(void) +{ + struct mii_dev *bus; + + bus = malloc(sizeof(*bus)); + if (!bus) + return bus; + + memset(bus, 0, sizeof(*bus)); + + /* initalize mii_dev struct fields */ + INIT_LIST_HEAD(&bus->link); + + return bus; +} + +int mdio_register(struct mii_dev *bus) +{ + if (!bus || !bus->name || !bus->read || !bus->write) + return -1; + + /* check if we have unique name */ + if (miiphy_get_dev_by_name(bus->name)) { + printf("mdio_register: non unique device name '%s'\n", + bus->name); + return -1; + } + + /* add it to the list */ + list_add_tail(&bus->link, &mii_devs); + + if (!current_mii) + current_mii = bus; + + return 0; +} + +void mdio_list_devices(void) +{ + struct list_head *entry; + + list_for_each(entry, &mii_devs) { + int i; + struct mii_dev *bus = list_entry(entry, struct mii_dev, link); + + printf("%s:\n", bus->name); + + for (i = 0; i < PHY_MAX_ADDR; i++) { + struct phy_device *phydev = bus->phymap[i]; + + if (phydev) { + printf("%d - %s", i, phydev->drv->name); + + if (phydev->dev) + printf(" <--> %s\n", phydev->dev->name); + else + printf("\n"); + } + } + } +} + +int miiphy_set_current_dev(const char *devname) +{ + struct mii_dev *dev; + + dev = miiphy_get_dev_by_name(devname); + if (dev) { + current_mii = dev; + return 0; + } + + printf("No such device: %s\n", devname); + + return 1; +} + +struct mii_dev *mdio_get_current_dev(void) +{ + return current_mii; +} + +struct phy_device *mdio_phydev_for_ethname(const char *ethname) +{ + struct list_head *entry; + struct mii_dev *bus; + + list_for_each(entry, &mii_devs) { + int i; + bus = list_entry(entry, struct mii_dev, link); + + for (i = 0; i < PHY_MAX_ADDR; i++) { + if (!bus->phymap[i] || !bus->phymap[i]->dev) + continue; + + if (strcmp(bus->phymap[i]->dev->name, ethname) == 0) + return bus->phymap[i]; + } + } + + printf("%s is not a known ethernet\n", ethname); + return NULL; +} + +const char *miiphy_get_current_dev(void) +{ + if (current_mii) + return current_mii->name; + + return NULL; +} + +static struct mii_dev *miiphy_get_active_dev(const char *devname) +{ + /* If the current mii is the one we want, return it */ + if (current_mii) + if (strcmp(current_mii->name, devname) == 0) + return current_mii; + + /* Otherwise, set the active one to the one we want */ + if (miiphy_set_current_dev(devname)) + return NULL; + else + return current_mii; +} + +/***************************************************************************** + * + * Read to variable <value> from the PHY attached to device <devname>, + * use PHY address <addr> and register <reg>. + * + * This API is deprecated. Use phy_read on a phy_device found via phy_connect + * + * Returns: + * 0 on success + */ +int miiphy_read(const char *devname, unsigned char addr, unsigned char reg, + unsigned short *value) +{ + struct mii_dev *bus; + int ret; + + bus = miiphy_get_active_dev(devname); + if (!bus) + return 1; + + ret = bus->read(bus, addr, MDIO_DEVAD_NONE, reg); + if (ret < 0) + return 1; + + *value = (unsigned short)ret; + return 0; +} + +/***************************************************************************** + * + * Write <value> to the PHY attached to device <devname>, + * use PHY address <addr> and register <reg>. + * + * This API is deprecated. Use phy_write on a phy_device found by phy_connect + * + * Returns: + * 0 on success + */ +int miiphy_write(const char *devname, unsigned char addr, unsigned char reg, + unsigned short value) +{ + struct mii_dev *bus; + + bus = miiphy_get_active_dev(devname); + if (bus) + return bus->write(bus, addr, MDIO_DEVAD_NONE, reg, value); + + return 1; +} + +/***************************************************************************** + * + * Print out list of registered MII capable devices. + */ +void miiphy_listdev(void) +{ + struct list_head *entry; + struct mii_dev *dev; + + puts("MII devices: "); + list_for_each(entry, &mii_devs) { + dev = list_entry(entry, struct mii_dev, link); + printf("'%s' ", dev->name); + } + puts("\n"); + + if (current_mii) + printf("Current device: '%s'\n", current_mii->name); +} + +/***************************************************************************** + * + * Read the OUI, manufacture's model number, and revision number. + * + * OUI: 22 bits (unsigned int) + * Model: 6 bits (unsigned char) + * Revision: 4 bits (unsigned char) + * + * This API is deprecated. + * + * Returns: + * 0 on success + */ +int miiphy_info(const char *devname, unsigned char addr, unsigned int *oui, + unsigned char *model, unsigned char *rev) +{ + unsigned int reg = 0; + unsigned short tmp; + + if (miiphy_read(devname, addr, MII_PHYSID2, &tmp) != 0) { + debug("PHY ID register 2 read failed\n"); + return -1; + } + reg = tmp; + + debug("MII_PHYSID2 @ 0x%x = 0x%04x\n", addr, reg); + + if (reg == 0xFFFF) { + /* No physical device present at this address */ + return -1; + } + + if (miiphy_read(devname, addr, MII_PHYSID1, &tmp) != 0) { + debug("PHY ID register 1 read failed\n"); + return -1; + } + reg |= tmp << 16; + debug("PHY_PHYIDR[1,2] @ 0x%x = 0x%08x\n", addr, reg); + + *oui = (reg >> 10); + *model = (unsigned char)((reg >> 4) & 0x0000003F); + *rev = (unsigned char)(reg & 0x0000000F); + return 0; +} + +#ifndef CONFIG_PHYLIB +/***************************************************************************** + * + * Reset the PHY. + * + * This API is deprecated. Use PHYLIB. + * + * Returns: + * 0 on success + */ +int miiphy_reset(const char *devname, unsigned char addr) +{ + unsigned short reg; + int timeout = 500; + + if (miiphy_read(devname, addr, MII_BMCR, ®) != 0) { + debug("PHY status read failed\n"); + return -1; + } + if (miiphy_write(devname, addr, MII_BMCR, reg | BMCR_RESET) != 0) { + debug("PHY reset failed\n"); + return -1; + } +#ifdef CONFIG_PHY_RESET_DELAY + udelay(CONFIG_PHY_RESET_DELAY); /* Intel LXT971A needs this */ +#endif + /* + * Poll the control register for the reset bit to go to 0 (it is + * auto-clearing). This should happen within 0.5 seconds per the + * IEEE spec. + */ + reg = 0x8000; + while (((reg & 0x8000) != 0) && timeout--) { + if (miiphy_read(devname, addr, MII_BMCR, ®) != 0) { + debug("PHY status read failed\n"); + return -1; + } + udelay(1000); + } + if ((reg & 0x8000) == 0) { + return 0; + } else { + puts("PHY reset timed out\n"); + return -1; + } + return 0; +} +#endif /* !PHYLIB */ + +/***************************************************************************** + * + * Determine the ethernet speed (10/100/1000). Return 10 on error. + */ +int miiphy_speed(const char *devname, unsigned char addr) +{ + u16 bmcr, anlpar; + +#if defined(CONFIG_PHY_GIGE) + u16 btsr; + + /* + * Check for 1000BASE-X. If it is supported, then assume that the speed + * is 1000. + */ + if (miiphy_is_1000base_x(devname, addr)) + return _1000BASET; + + /* + * No 1000BASE-X, so assume 1000BASE-T/100BASE-TX/10BASE-T register set. + */ + /* Check for 1000BASE-T. */ + if (miiphy_read(devname, addr, MII_STAT1000, &btsr)) { + printf("PHY 1000BT status"); + goto miiphy_read_failed; + } + if (btsr != 0xFFFF && + (btsr & (PHY_1000BTSR_1000FD | PHY_1000BTSR_1000HD))) + return _1000BASET; +#endif /* CONFIG_PHY_GIGE */ + + /* Check Basic Management Control Register first. */ + if (miiphy_read(devname, addr, MII_BMCR, &bmcr)) { + printf("PHY speed"); + goto miiphy_read_failed; + } + /* Check if auto-negotiation is on. */ + if (bmcr & BMCR_ANENABLE) { + /* Get auto-negotiation results. */ + if (miiphy_read(devname, addr, MII_LPA, &anlpar)) { + printf("PHY AN speed"); + goto miiphy_read_failed; + } + return (anlpar & LPA_100) ? _100BASET : _10BASET; + } + /* Get speed from basic control settings. */ + return (bmcr & BMCR_SPEED100) ? _100BASET : _10BASET; + +miiphy_read_failed: + printf(" read failed, assuming 10BASE-T\n"); + return _10BASET; +} + +/***************************************************************************** + * + * Determine full/half duplex. Return half on error. + */ +int miiphy_duplex(const char *devname, unsigned char addr) +{ + u16 bmcr, anlpar; + +#if defined(CONFIG_PHY_GIGE) + u16 btsr; + + /* Check for 1000BASE-X. */ + if (miiphy_is_1000base_x(devname, addr)) { + /* 1000BASE-X */ + if (miiphy_read(devname, addr, MII_LPA, &anlpar)) { + printf("1000BASE-X PHY AN duplex"); + goto miiphy_read_failed; + } + } + /* + * No 1000BASE-X, so assume 1000BASE-T/100BASE-TX/10BASE-T register set. + */ + /* Check for 1000BASE-T. */ + if (miiphy_read(devname, addr, MII_STAT1000, &btsr)) { + printf("PHY 1000BT status"); + goto miiphy_read_failed; + } + if (btsr != 0xFFFF) { + if (btsr & PHY_1000BTSR_1000FD) { + return FULL; + } else if (btsr & PHY_1000BTSR_1000HD) { + return HALF; + } + } +#endif /* CONFIG_PHY_GIGE */ + + /* Check Basic Management Control Register first. */ + if (miiphy_read(devname, addr, MII_BMCR, &bmcr)) { + puts("PHY duplex"); + goto miiphy_read_failed; + } + /* Check if auto-negotiation is on. */ + if (bmcr & BMCR_ANENABLE) { + /* Get auto-negotiation results. */ + if (miiphy_read(devname, addr, MII_LPA, &anlpar)) { + puts("PHY AN duplex"); + goto miiphy_read_failed; + } + return (anlpar & (LPA_10FULL | LPA_100FULL)) ? + FULL : HALF; + } + /* Get speed from basic control settings. */ + return (bmcr & BMCR_FULLDPLX) ? FULL : HALF; + +miiphy_read_failed: + printf(" read failed, assuming half duplex\n"); + return HALF; +} + +/***************************************************************************** + * + * Return 1 if PHY supports 1000BASE-X, 0 if PHY supports 10BASE-T/100BASE-TX/ + * 1000BASE-T, or on error. + */ +int miiphy_is_1000base_x(const char *devname, unsigned char addr) +{ +#if defined(CONFIG_PHY_GIGE) + u16 exsr; + + if (miiphy_read(devname, addr, MII_ESTATUS, &exsr)) { + printf("PHY extended status read failed, assuming no " + "1000BASE-X\n"); + return 0; + } + return 0 != (exsr & (ESTATUS_1000XF | ESTATUS_1000XH)); +#else + return 0; +#endif +} + +#ifdef CONFIG_SYS_FAULT_ECHO_LINK_DOWN +/***************************************************************************** + * + * Determine link status + */ +int miiphy_link(const char *devname, unsigned char addr) +{ + unsigned short reg; + + /* dummy read; needed to latch some phys */ + (void)miiphy_read(devname, addr, MII_BMSR, ®); + if (miiphy_read(devname, addr, MII_BMSR, ®)) { + puts("MII_BMSR read failed, assuming no link\n"); + return 0; + } + + /* Determine if a link is active */ + if ((reg & BMSR_LSTATUS) != 0) { + return 1; + } else { + return 0; + } +} +#endif diff --git a/qemu/roms/u-boot/common/modem.c b/qemu/roms/u-boot/common/modem.c new file mode 100644 index 000000000..be54b1011 --- /dev/null +++ b/qemu/roms/u-boot/common/modem.c @@ -0,0 +1,101 @@ +/* + * (C) Copyright 2002-2009 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> + +/* 'inline' - We have to do it fast */ +static inline void mdm_readline(char *buf, int bufsiz) +{ + char c; + char *p; + int n; + + n = 0; + p = buf; + for(;;) { + c = serial_getc(); + + /* dbg("(%c)", c); */ + + switch(c) { + case '\r': + break; + case '\n': + *p = '\0'; + return; + + default: + if(n++ > bufsiz) { + *p = '\0'; + return; /* sanity check */ + } + *p = c; + p++; + break; + } + } +} + +extern void dbg(const char *fmt, ...); +int mdm_init (void) +{ + char env_str[16]; + char *init_str; + int i; + extern void enable_putc(void); + extern int hwflow_onoff(int); + + enable_putc(); /* enable serial_putc() */ + +#ifdef CONFIG_HWFLOW + init_str = getenv("mdm_flow_control"); + if (init_str && (strcmp(init_str, "rts/cts") == 0)) + hwflow_onoff (1); + else + hwflow_onoff(-1); +#endif + + for (i = 1;;i++) { + sprintf(env_str, "mdm_init%d", i); + if ((init_str = getenv(env_str)) != NULL) { + serial_puts(init_str); + serial_puts("\n"); + for(;;) { + mdm_readline(console_buffer, CONFIG_SYS_CBSIZE); + dbg("ini%d: [%s]", i, console_buffer); + + if ((strcmp(console_buffer, "OK") == 0) || + (strcmp(console_buffer, "ERROR") == 0)) { + dbg("ini%d: cmd done", i); + break; + } else /* in case we are originating call ... */ + if (strncmp(console_buffer, "CONNECT", 7) == 0) { + dbg("ini%d: connect", i); + return 0; + } + } + } else + break; /* no init string - stop modem init */ + + udelay(100000); + } + + udelay(100000); + + /* final stage - wait for connect */ + for(;i > 1;) { /* if 'i' > 1 - wait for connection + message from modem */ + mdm_readline(console_buffer, CONFIG_SYS_CBSIZE); + dbg("ini_f: [%s]", console_buffer); + if (strncmp(console_buffer, "CONNECT", 7) == 0) { + dbg("ini_f: connected"); + return 0; + } + } + + return 0; +} diff --git a/qemu/roms/u-boot/common/s_record.c b/qemu/roms/u-boot/common/s_record.c new file mode 100644 index 000000000..aa82668a0 --- /dev/null +++ b/qemu/roms/u-boot/common/s_record.c @@ -0,0 +1,179 @@ +/* + * (C) Copyright 2000 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <s_record.h> + +static int hex1_bin (char c); +static int hex2_bin (char *s); + +int srec_decode (char *input, int *count, ulong *addr, char *data) +{ + int i; + int v; /* conversion buffer */ + int srec_type; /* S-Record type */ + unsigned char chksum; /* buffer for checksum */ + + chksum = 0; + + /* skip anything before 'S', and the 'S' itself. + * Return error if not found + */ + + for (; *input; ++input) { + if (*input == 'S') { /* skip 'S' */ + ++input; + break; + } + } + if (*input == '\0') { /* no more data? */ + return (SREC_EMPTY); + } + + v = *input++; /* record type */ + + if ((*count = hex2_bin(input)) < 0) { + return (SREC_E_NOSREC); + } + + chksum += *count; + input += 2; + + switch (v) { /* record type */ + + case '0': /* start record */ + srec_type = SREC_START; /* 2 byte addr field */ + *count -= 3; /* - checksum and addr */ + break; + case '1': + srec_type = SREC_DATA2; /* 2 byte addr field */ + *count -= 3; /* - checksum and addr */ + break; + case '2': + srec_type = SREC_DATA3; /* 3 byte addr field */ + *count -= 4; /* - checksum and addr */ + break; + case '3': /* data record with a */ + srec_type = SREC_DATA4; /* 4 byte addr field */ + *count -= 5; /* - checksum and addr */ + break; +/*** case '4' ***/ + case '5': /* count record, addr field contains */ + srec_type = SREC_COUNT; /* a 2 byte record counter */ + *count = 0; /* no data */ + break; +/*** case '6' -- not used ***/ + case '7': /* end record with a */ + srec_type = SREC_END4; /* 4 byte addr field */ + *count -= 5; /* - checksum and addr */ + break; + case '8': /* end record with a */ + srec_type = SREC_END3; /* 3 byte addr field */ + *count -= 4; /* - checksum and addr */ + break; + case '9': /* end record with a */ + srec_type = SREC_END2; /* 2 byte addr field */ + *count -= 3; /* - checksum and addr */ + break; + default: + return (SREC_E_BADTYPE); + } + + /* read address field */ + *addr = 0; + + switch (v) { + case '3': /* 4 byte addr field */ + case '7': + if ((v = hex2_bin(input)) < 0) { + return (SREC_E_NOSREC); + } + *addr += v; + chksum += v; + input += 2; + /* FALL THRU */ + case '2': /* 3 byte addr field */ + case '8': + if ((v = hex2_bin(input)) < 0) { + return (SREC_E_NOSREC); + } + *addr <<= 8; + *addr += v; + chksum += v; + input += 2; + /* FALL THRU */ + case '0': /* 2 byte addr field */ + case '1': + case '5': + case '9': + if ((v = hex2_bin(input)) < 0) { + return (SREC_E_NOSREC); + } + *addr <<= 8; + *addr += v; + chksum += v; + input += 2; + + if ((v = hex2_bin(input)) < 0) { + return (SREC_E_NOSREC); + } + *addr <<= 8; + *addr += v; + chksum += v; + input += 2; + + break; + default: + return (SREC_E_BADTYPE); + } + + /* convert data and calculate checksum */ + for (i=0; i < *count; ++i) { + if ((v = hex2_bin(input)) < 0) { + return (SREC_E_NOSREC); + } + data[i] = v; + chksum += v; + input += 2; + } + + /* read anc check checksum */ + if ((v = hex2_bin(input)) < 0) { + return (SREC_E_NOSREC); + } + + if ((unsigned char)v != (unsigned char)~chksum) { + return (SREC_E_BADCHKS); + } + + return (srec_type); +} + +static int hex1_bin (char c) +{ + if (c >= '0' && c <= '9') + return (c - '0'); + if (c >= 'a' && c <= 'f') + return (c + 10 - 'a'); + if (c >= 'A' && c <= 'F') + return (c + 10 - 'A'); + return (-1); +} + +static int hex2_bin (char *s) +{ + int i, j; + + if ((i = hex1_bin(*s++)) < 0) { + return (-1); + } + if ((j = hex1_bin(*s)) < 0) { + return (-1); + } + + return ((i<<4) + j); +} diff --git a/qemu/roms/u-boot/common/spl/Makefile b/qemu/roms/u-boot/common/spl/Makefile new file mode 100644 index 000000000..64569c2cc --- /dev/null +++ b/qemu/roms/u-boot/common/spl/Makefile @@ -0,0 +1,22 @@ +# +# (C) Copyright 2012 +# Texas Instruments Incorporated - http://www.ti.com/ +# Aneesh V <aneesh@ti.com> +# +# SPDX-License-Identifier: GPL-2.0+ +# +# Based on common/Makefile. +# + +ifdef CONFIG_SPL_BUILD +obj-$(CONFIG_SPL_FRAMEWORK) += spl.o +obj-$(CONFIG_SPL_NOR_SUPPORT) += spl_nor.o +obj-$(CONFIG_SPL_YMODEM_SUPPORT) += spl_ymodem.o +obj-$(CONFIG_SPL_NAND_SUPPORT) += spl_nand.o +obj-$(CONFIG_SPL_ONENAND_SUPPORT) += spl_onenand.o +obj-$(CONFIG_SPL_NET_SUPPORT) += spl_net.o +obj-$(CONFIG_SPL_MMC_SUPPORT) += spl_mmc.o +obj-$(CONFIG_SPL_USB_SUPPORT) += spl_usb.o +obj-$(CONFIG_SPL_FAT_SUPPORT) += spl_fat.o +obj-$(CONFIG_SPL_SATA_SUPPORT) += spl_sata.o +endif diff --git a/qemu/roms/u-boot/common/spl/spl.c b/qemu/roms/u-boot/common/spl/spl.c new file mode 100644 index 000000000..774fdad25 --- /dev/null +++ b/qemu/roms/u-boot/common/spl/spl.c @@ -0,0 +1,257 @@ +/* + * (C) Copyright 2010 + * Texas Instruments, <www.ti.com> + * + * Aneesh V <aneesh@ti.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ +#include <common.h> +#include <spl.h> +#include <asm/u-boot.h> +#include <nand.h> +#include <fat.h> +#include <version.h> +#include <i2c.h> +#include <image.h> +#include <malloc.h> +#include <linux/compiler.h> + +DECLARE_GLOBAL_DATA_PTR; + +#ifndef CONFIG_SYS_UBOOT_START +#define CONFIG_SYS_UBOOT_START CONFIG_SYS_TEXT_BASE +#endif +#ifndef CONFIG_SYS_MONITOR_LEN +#define CONFIG_SYS_MONITOR_LEN (200 * 1024) +#endif + +u32 *boot_params_ptr = NULL; +struct spl_image_info spl_image; + +/* Define board data structure */ +static bd_t bdata __attribute__ ((section(".data"))); + +/* + * Default function to determine if u-boot or the OS should + * be started. This implementation always returns 1. + * + * Please implement your own board specific funcion to do this. + * + * RETURN + * 0 to not start u-boot + * positive if u-boot should start + */ +#ifdef CONFIG_SPL_OS_BOOT +__weak int spl_start_uboot(void) +{ + puts("SPL: Please implement spl_start_uboot() for your board\n"); + puts("SPL: Direct Linux boot not active!\n"); + return 1; +} +#endif + +/* + * Weak default function for board specific cleanup/preparation before + * Linux boot. Some boards/platforms might not need it, so just provide + * an empty stub here. + */ +__weak void spl_board_prepare_for_linux(void) +{ + /* Nothing to do! */ +} + +void spl_parse_image_header(const struct image_header *header) +{ + u32 header_size = sizeof(struct image_header); + + if (image_get_magic(header) == IH_MAGIC) { + if (spl_image.flags & SPL_COPY_PAYLOAD_ONLY) { + /* + * On some system (e.g. powerpc), the load-address and + * entry-point is located at address 0. We can't load + * to 0-0x40. So skip header in this case. + */ + spl_image.load_addr = image_get_load(header); + spl_image.entry_point = image_get_ep(header); + spl_image.size = image_get_data_size(header); + } else { + spl_image.entry_point = image_get_load(header); + /* Load including the header */ + spl_image.load_addr = spl_image.entry_point - + header_size; + spl_image.size = image_get_data_size(header) + + header_size; + } + spl_image.os = image_get_os(header); + spl_image.name = image_get_name(header); + debug("spl: payload image: %.*s load addr: 0x%x size: %d\n", + sizeof(spl_image.name), spl_image.name, + spl_image.load_addr, spl_image.size); + } else { + /* Signature not found - assume u-boot.bin */ + debug("mkimage signature not found - ih_magic = %x\n", + header->ih_magic); + /* Let's assume U-Boot will not be more than 200 KB */ + spl_image.size = CONFIG_SYS_MONITOR_LEN; + spl_image.entry_point = CONFIG_SYS_UBOOT_START; + spl_image.load_addr = CONFIG_SYS_TEXT_BASE; + spl_image.os = IH_OS_U_BOOT; + spl_image.name = "U-Boot"; + } +} + +__weak void __noreturn jump_to_image_no_args(struct spl_image_info *spl_image) +{ + typedef void __noreturn (*image_entry_noargs_t)(void); + + image_entry_noargs_t image_entry = + (image_entry_noargs_t) spl_image->entry_point; + + debug("image entry point: 0x%X\n", spl_image->entry_point); + image_entry(); +} + +#ifdef CONFIG_SPL_RAM_DEVICE +static void spl_ram_load_image(void) +{ + const struct image_header *header; + + /* + * Get the header. It will point to an address defined by handoff + * which will tell where the image located inside the flash. For + * now, it will temporary fixed to address pointed by U-Boot. + */ + header = (struct image_header *) + (CONFIG_SYS_TEXT_BASE - sizeof(struct image_header)); + + spl_parse_image_header(header); +} +#endif + +void board_init_r(gd_t *dummy1, ulong dummy2) +{ + u32 boot_device; + debug(">>spl:board_init_r()\n"); + +#ifdef CONFIG_SYS_SPL_MALLOC_START + mem_malloc_init(CONFIG_SYS_SPL_MALLOC_START, + CONFIG_SYS_SPL_MALLOC_SIZE); +#endif + +#ifndef CONFIG_PPC + /* + * timer_init() does not exist on PPC systems. The timer is initialized + * and enabled (decrementer) in interrupt_init() here. + */ + timer_init(); +#endif + +#ifdef CONFIG_SPL_BOARD_INIT + spl_board_init(); +#endif + + boot_device = spl_boot_device(); + debug("boot device - %d\n", boot_device); + switch (boot_device) { +#ifdef CONFIG_SPL_RAM_DEVICE + case BOOT_DEVICE_RAM: + spl_ram_load_image(); + break; +#endif +#ifdef CONFIG_SPL_MMC_SUPPORT + case BOOT_DEVICE_MMC1: + case BOOT_DEVICE_MMC2: + case BOOT_DEVICE_MMC2_2: + spl_mmc_load_image(); + break; +#endif +#ifdef CONFIG_SPL_NAND_SUPPORT + case BOOT_DEVICE_NAND: + spl_nand_load_image(); + break; +#endif +#ifdef CONFIG_SPL_ONENAND_SUPPORT + case BOOT_DEVICE_ONENAND: + spl_onenand_load_image(); + break; +#endif +#ifdef CONFIG_SPL_NOR_SUPPORT + case BOOT_DEVICE_NOR: + spl_nor_load_image(); + break; +#endif +#ifdef CONFIG_SPL_YMODEM_SUPPORT + case BOOT_DEVICE_UART: + spl_ymodem_load_image(); + break; +#endif +#ifdef CONFIG_SPL_SPI_SUPPORT + case BOOT_DEVICE_SPI: + spl_spi_load_image(); + break; +#endif +#ifdef CONFIG_SPL_ETH_SUPPORT + case BOOT_DEVICE_CPGMAC: +#ifdef CONFIG_SPL_ETH_DEVICE + spl_net_load_image(CONFIG_SPL_ETH_DEVICE); +#else + spl_net_load_image(NULL); +#endif + break; +#endif +#ifdef CONFIG_SPL_USBETH_SUPPORT + case BOOT_DEVICE_USBETH: + spl_net_load_image("usb_ether"); + break; +#endif +#ifdef CONFIG_SPL_USB_SUPPORT + case BOOT_DEVICE_USB: + spl_usb_load_image(); + break; +#endif +#ifdef CONFIG_SPL_SATA_SUPPORT + case BOOT_DEVICE_SATA: + spl_sata_load_image(); + break; +#endif + default: + debug("SPL: Un-supported Boot Device\n"); + hang(); + } + + switch (spl_image.os) { + case IH_OS_U_BOOT: + debug("Jumping to U-Boot\n"); + break; +#ifdef CONFIG_SPL_OS_BOOT + case IH_OS_LINUX: + debug("Jumping to Linux\n"); + spl_board_prepare_for_linux(); + jump_to_image_linux((void *)CONFIG_SYS_SPL_ARGS_ADDR); +#endif + default: + debug("Unsupported OS image.. Jumping nevertheless..\n"); + } + jump_to_image_no_args(&spl_image); +} + +/* + * This requires UART clocks to be enabled. In order for this to work the + * caller must ensure that the gd pointer is valid. + */ +void preloader_console_init(void) +{ + gd->bd = &bdata; + gd->baudrate = CONFIG_BAUDRATE; + + serial_init(); /* serial communications setup */ + + gd->have_console = 1; + + puts("\nU-Boot SPL " PLAIN_VERSION " (" U_BOOT_DATE " - " \ + U_BOOT_TIME ")\n"); +#ifdef CONFIG_SPL_DISPLAY_PRINT + spl_display_print(); +#endif +} diff --git a/qemu/roms/u-boot/common/spl/spl_fat.c b/qemu/roms/u-boot/common/spl/spl_fat.c new file mode 100644 index 000000000..56be94388 --- /dev/null +++ b/qemu/roms/u-boot/common/spl/spl_fat.c @@ -0,0 +1,123 @@ +/* + * (C) Copyright 2014 + * Texas Instruments, <www.ti.com> + * + * Dan Murphy <dmurphy@ti.com> + * + * SPDX-License-Identifier: GPL-2.0+ + * + * FAT Image Functions copied from spl_mmc.c + */ + +#include <common.h> +#include <spl.h> +#include <asm/u-boot.h> +#include <fat.h> +#include <image.h> + +static int fat_registered; + +#ifdef CONFIG_SPL_FAT_SUPPORT +static int spl_register_fat_device(block_dev_desc_t *block_dev, int partition) +{ + int err = 0; + + if (fat_registered) + return err; + + err = fat_register_device(block_dev, partition); + if (err) { +#ifdef CONFIG_SPL_LIBCOMMON_SUPPORT + printf("%s: fat register err - %d\n", __func__, err); +#endif + hang(); + } + + fat_registered = 1; + + return err; +} + +int spl_load_image_fat(block_dev_desc_t *block_dev, + int partition, + const char *filename) +{ + int err; + struct image_header *header; + + err = spl_register_fat_device(block_dev, partition); + if (err) + goto end; + + header = (struct image_header *)(CONFIG_SYS_TEXT_BASE - + sizeof(struct image_header)); + + err = file_fat_read(filename, header, sizeof(struct image_header)); + if (err <= 0) + goto end; + + spl_parse_image_header(header); + + err = file_fat_read(filename, (u8 *)spl_image.load_addr, 0); + +end: +#ifdef CONFIG_SPL_LIBCOMMON_SUPPORT + if (err <= 0) + printf("%s: error reading image %s, err - %d\n", + __func__, filename, err); +#endif + + return (err <= 0); +} + +#ifdef CONFIG_SPL_OS_BOOT +int spl_load_image_fat_os(block_dev_desc_t *block_dev, int partition) +{ + int err; + __maybe_unused char *file; + + err = spl_register_fat_device(block_dev, partition); + if (err) + return err; + +#if defined(CONFIG_SPL_ENV_SUPPORT) && defined(CONFIG_SPL_OS_BOOT) + file = getenv("falcon_args_file"); + if (file) { + err = file_fat_read(file, (void *)CONFIG_SYS_SPL_ARGS_ADDR, 0); + if (err <= 0) { + printf("spl: error reading image %s, err - %d, falling back to default\n", + file, err); + goto defaults; + } + file = getenv("falcon_image_file"); + if (file) { + err = spl_load_image_fat(block_dev, partition, file); + if (err != 0) { + puts("spl: falling back to default\n"); + goto defaults; + } + + return 0; + } else + puts("spl: falcon_image_file not set in environment, falling back to default\n"); + } else + puts("spl: falcon_args_file not set in environment, falling back to default\n"); + +defaults: +#endif + + err = file_fat_read(CONFIG_SPL_FAT_LOAD_ARGS_NAME, + (void *)CONFIG_SYS_SPL_ARGS_ADDR, 0); + if (err <= 0) { +#ifdef CONFIG_SPL_LIBCOMMON_SUPPORT + printf("%s: error reading image %s, err - %d\n", + __func__, CONFIG_SPL_FAT_LOAD_ARGS_NAME, err); +#endif + return -1; + } + + return spl_load_image_fat(block_dev, partition, + CONFIG_SPL_FAT_LOAD_KERNEL_NAME); +} +#endif +#endif diff --git a/qemu/roms/u-boot/common/spl/spl_mmc.c b/qemu/roms/u-boot/common/spl/spl_mmc.c new file mode 100644 index 000000000..fa6f891bc --- /dev/null +++ b/qemu/roms/u-boot/common/spl/spl_mmc.c @@ -0,0 +1,147 @@ +/* + * (C) Copyright 2010 + * Texas Instruments, <www.ti.com> + * + * Aneesh V <aneesh@ti.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ +#include <common.h> +#include <spl.h> +#include <asm/u-boot.h> +#include <mmc.h> +#include <version.h> +#include <image.h> + +DECLARE_GLOBAL_DATA_PTR; + +static int mmc_load_image_raw(struct mmc *mmc, unsigned long sector) +{ + unsigned long err; + u32 image_size_sectors; + struct image_header *header; + + header = (struct image_header *)(CONFIG_SYS_TEXT_BASE - + sizeof(struct image_header)); + + /* read image header to find the image size & load address */ + err = mmc->block_dev.block_read(0, sector, 1, header); + if (err == 0) + goto end; + + if (image_get_magic(header) != IH_MAGIC) + return -1; + + spl_parse_image_header(header); + + /* convert size to sectors - round up */ + image_size_sectors = (spl_image.size + mmc->read_bl_len - 1) / + mmc->read_bl_len; + + /* Read the header too to avoid extra memcpy */ + err = mmc->block_dev.block_read(0, sector, image_size_sectors, + (void *)spl_image.load_addr); + +end: +#ifdef CONFIG_SPL_LIBCOMMON_SUPPORT + if (err == 0) + printf("spl: mmc blk read err - %lu\n", err); +#endif + + return (err == 0); +} + +#ifdef CONFIG_SPL_OS_BOOT +static int mmc_load_image_raw_os(struct mmc *mmc) +{ + if (!mmc->block_dev.block_read(0, + CONFIG_SYS_MMCSD_RAW_MODE_ARGS_SECTOR, + CONFIG_SYS_MMCSD_RAW_MODE_ARGS_SECTORS, + (void *)CONFIG_SYS_SPL_ARGS_ADDR)) { +#ifdef CONFIG_SPL_LIBCOMMON_SUPPORT + printf("mmc args blk read error\n"); +#endif + return -1; + } + + return mmc_load_image_raw(mmc, CONFIG_SYS_MMCSD_RAW_MODE_KERNEL_SECTOR); +} +#endif + +void spl_mmc_load_image(void) +{ + struct mmc *mmc; + int err; + u32 boot_mode; + + mmc_initialize(gd->bd); + /* We register only one device. So, the dev id is always 0 */ + mmc = find_mmc_device(0); + if (!mmc) { +#ifdef CONFIG_SPL_LIBCOMMON_SUPPORT + puts("spl: mmc device not found!!\n"); +#endif + hang(); + } + + err = mmc_init(mmc); + if (err) { +#ifdef CONFIG_SPL_LIBCOMMON_SUPPORT + printf("spl: mmc init failed: err - %d\n", err); +#endif + hang(); + } + + boot_mode = spl_boot_mode(); + if (boot_mode == MMCSD_MODE_RAW) { + debug("boot mode - RAW\n"); +#ifdef CONFIG_SPL_OS_BOOT + if (spl_start_uboot() || mmc_load_image_raw_os(mmc)) +#endif + err = mmc_load_image_raw(mmc, + CONFIG_SYS_MMCSD_RAW_MODE_U_BOOT_SECTOR); +#ifdef CONFIG_SPL_FAT_SUPPORT + } else if (boot_mode == MMCSD_MODE_FAT) { + debug("boot mode - FAT\n"); +#ifdef CONFIG_SPL_OS_BOOT + if (spl_start_uboot() || spl_load_image_fat_os(&mmc->block_dev, + CONFIG_SYS_MMC_SD_FAT_BOOT_PARTITION)) +#endif + err = spl_load_image_fat(&mmc->block_dev, + CONFIG_SYS_MMC_SD_FAT_BOOT_PARTITION, + CONFIG_SPL_FAT_LOAD_PAYLOAD_NAME); +#endif +#ifdef CONFIG_SUPPORT_EMMC_BOOT + } else if (boot_mode == MMCSD_MODE_EMMCBOOT) { + /* + * We need to check what the partition is configured to. + * 1 and 2 match up to boot0 / boot1 and 7 is user data + * which is the first physical partition (0). + */ + int part = (mmc->part_config >> 3) & PART_ACCESS_MASK; + + if (part == 7) + part = 0; + + if (mmc_switch_part(0, part)) { +#ifdef CONFIG_SPL_LIBCOMMON_SUPPORT + puts("MMC partition switch failed\n"); +#endif + hang(); + } +#ifdef CONFIG_SPL_OS_BOOT + if (spl_start_uboot() || mmc_load_image_raw_os(mmc)) +#endif + err = mmc_load_image_raw(mmc, + CONFIG_SYS_MMCSD_RAW_MODE_U_BOOT_SECTOR); +#endif + } else { +#ifdef CONFIG_SPL_LIBCOMMON_SUPPORT + puts("spl: wrong MMC boot mode\n"); +#endif + hang(); + } + + if (err) + hang(); +} diff --git a/qemu/roms/u-boot/common/spl/spl_nand.c b/qemu/roms/u-boot/common/spl/spl_nand.c new file mode 100644 index 000000000..9da021862 --- /dev/null +++ b/qemu/roms/u-boot/common/spl/spl_nand.c @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2011 + * Corscience GmbH & Co. KG - Simon Schwarz <schwarz@corscience.de> + * + * SPDX-License-Identifier: GPL-2.0+ + */ +#include <common.h> +#include <config.h> +#include <spl.h> +#include <asm/io.h> +#include <nand.h> + +void spl_nand_load_image(void) +{ + struct image_header *header; + int *src __attribute__((unused)); + int *dst __attribute__((unused)); + + debug("spl: nand - using hw ecc\n"); + nand_init(); + + /*use CONFIG_SYS_TEXT_BASE as temporary storage area */ + header = (struct image_header *)(CONFIG_SYS_TEXT_BASE); +#ifdef CONFIG_SPL_OS_BOOT + if (!spl_start_uboot()) { + /* + * load parameter image + * load to temp position since nand_spl_load_image reads + * a whole block which is typically larger than + * CONFIG_CMD_SPL_WRITE_SIZE therefore may overwrite + * following sections like BSS + */ + nand_spl_load_image(CONFIG_CMD_SPL_NAND_OFS, + CONFIG_CMD_SPL_WRITE_SIZE, + (void *)CONFIG_SYS_TEXT_BASE); + /* copy to destintion */ + for (dst = (int *)CONFIG_SYS_SPL_ARGS_ADDR, + src = (int *)CONFIG_SYS_TEXT_BASE; + src < (int *)(CONFIG_SYS_TEXT_BASE + + CONFIG_CMD_SPL_WRITE_SIZE); + src++, dst++) { + writel(readl(src), dst); + } + + /* load linux */ + nand_spl_load_image(CONFIG_SYS_NAND_SPL_KERNEL_OFFS, + CONFIG_SYS_NAND_PAGE_SIZE, (void *)header); + spl_parse_image_header(header); + if (header->ih_os == IH_OS_LINUX) { + /* happy - was a linux */ + nand_spl_load_image(CONFIG_SYS_NAND_SPL_KERNEL_OFFS, + spl_image.size, (void *)spl_image.load_addr); + nand_deselect(); + return; + } else { + puts("The Expected Linux image was not " + "found. Please check your NAND " + "configuration.\n"); + puts("Trying to start u-boot now...\n"); + } + } +#endif +#ifdef CONFIG_NAND_ENV_DST + nand_spl_load_image(CONFIG_ENV_OFFSET, + CONFIG_SYS_NAND_PAGE_SIZE, (void *)header); + spl_parse_image_header(header); + nand_spl_load_image(CONFIG_ENV_OFFSET, spl_image.size, + (void *)spl_image.load_addr); +#ifdef CONFIG_ENV_OFFSET_REDUND + nand_spl_load_image(CONFIG_ENV_OFFSET_REDUND, + CONFIG_SYS_NAND_PAGE_SIZE, (void *)header); + spl_parse_image_header(header); + nand_spl_load_image(CONFIG_ENV_OFFSET_REDUND, spl_image.size, + (void *)spl_image.load_addr); +#endif +#endif + /* Load u-boot */ + nand_spl_load_image(CONFIG_SYS_NAND_U_BOOT_OFFS, + CONFIG_SYS_NAND_PAGE_SIZE, (void *)header); + spl_parse_image_header(header); + nand_spl_load_image(CONFIG_SYS_NAND_U_BOOT_OFFS, + spl_image.size, (void *)spl_image.load_addr); + nand_deselect(); +} diff --git a/qemu/roms/u-boot/common/spl/spl_net.c b/qemu/roms/u-boot/common/spl/spl_net.c new file mode 100644 index 000000000..ff5370592 --- /dev/null +++ b/qemu/roms/u-boot/common/spl/spl_net.c @@ -0,0 +1,37 @@ +/* + * (C) Copyright 2000-2004 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * (C) Copyright 2012 + * Ilya Yanok <ilya.yanok@gmail.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ +#include <common.h> +#include <spl.h> +#include <net.h> + +DECLARE_GLOBAL_DATA_PTR; + +void spl_net_load_image(const char *device) +{ + int rv; + + env_init(); + env_relocate(); + setenv("autoload", "yes"); + load_addr = CONFIG_SYS_TEXT_BASE - sizeof(struct image_header); + rv = eth_initialize(gd->bd); + if (rv == 0) { + printf("No Ethernet devices found\n"); + hang(); + } + if (device) + setenv("ethact", device); + rv = NetLoop(BOOTP); + if (rv < 0) { + printf("Problem booting with BOOTP\n"); + hang(); + } + spl_parse_image_header((struct image_header *)load_addr); +} diff --git a/qemu/roms/u-boot/common/spl/spl_nor.c b/qemu/roms/u-boot/common/spl/spl_nor.c new file mode 100644 index 000000000..b444a3ea2 --- /dev/null +++ b/qemu/roms/u-boot/common/spl/spl_nor.c @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2012 Stefan Roese <sr@denx.de> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <spl.h> + +void spl_nor_load_image(void) +{ + /* + * Loading of the payload to SDRAM is done with skipping of + * the mkimage header in this SPL NOR driver + */ + spl_image.flags |= SPL_COPY_PAYLOAD_ONLY; + + if (spl_start_uboot()) { + /* + * Load real U-Boot from its location in NOR flash to its + * defined location in SDRAM + */ + spl_parse_image_header( + (const struct image_header *)CONFIG_SYS_UBOOT_BASE); + + memcpy((void *)spl_image.load_addr, + (void *)(CONFIG_SYS_UBOOT_BASE + + sizeof(struct image_header)), + spl_image.size); + } else { + /* + * Load Linux from its location in NOR flash to its defined + * location in SDRAM + */ + spl_parse_image_header( + (const struct image_header *)CONFIG_SYS_OS_BASE); + + memcpy((void *)spl_image.load_addr, + (void *)(CONFIG_SYS_OS_BASE + + sizeof(struct image_header)), + spl_image.size); + + /* + * Copy DT blob (fdt) to SDRAM. Passing pointer to flash + * doesn't work (16 KiB should be enough for DT) + */ + memcpy((void *)CONFIG_SYS_SPL_ARGS_ADDR, + (void *)(CONFIG_SYS_FDT_BASE), + (16 << 10)); + } +} diff --git a/qemu/roms/u-boot/common/spl/spl_onenand.c b/qemu/roms/u-boot/common/spl/spl_onenand.c new file mode 100644 index 000000000..d8d80974f --- /dev/null +++ b/qemu/roms/u-boot/common/spl/spl_onenand.c @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2013 + * ISEE 2007 SL - Enric Balletbo i Serra <eballetbo@iseebcn.com> + * + * Based on common/spl/spl_nand.c + * Copyright (C) 2011 + * Corscience GmbH & Co. KG - Simon Schwarz <schwarz@corscience.de> + * + * SPDX-License-Identifier: GPL-2.0+ + */ +#include <common.h> +#include <config.h> +#include <spl.h> +#include <asm/io.h> +#include <onenand_uboot.h> + +void spl_onenand_load_image(void) +{ + struct image_header *header; + + debug("spl: onenand\n"); + + /*use CONFIG_SYS_TEXT_BASE as temporary storage area */ + header = (struct image_header *)(CONFIG_SYS_TEXT_BASE); + /* Load u-boot */ + onenand_spl_load_image(CONFIG_SYS_ONENAND_U_BOOT_OFFS, + CONFIG_SYS_ONENAND_PAGE_SIZE, (void *)header); + spl_parse_image_header(header); + onenand_spl_load_image(CONFIG_SYS_ONENAND_U_BOOT_OFFS, + spl_image.size, (void *)spl_image.load_addr); +} diff --git a/qemu/roms/u-boot/common/spl/spl_sata.c b/qemu/roms/u-boot/common/spl/spl_sata.c new file mode 100644 index 000000000..2e7adca0c --- /dev/null +++ b/qemu/roms/u-boot/common/spl/spl_sata.c @@ -0,0 +1,49 @@ +/* + * (C) Copyright 2013 + * Texas Instruments, <www.ti.com> + * + * Dan Murphy <dmurphy@ti.com> + * + * SPDX-License-Identifier: GPL-2.0+ + * + * Derived work from spl_usb.c + */ + +#include <common.h> +#include <spl.h> +#include <asm/u-boot.h> +#include <sata.h> +#include <fat.h> +#include <version.h> +#include <image.h> + +DECLARE_GLOBAL_DATA_PTR; + +void spl_sata_load_image(void) +{ + int err; + block_dev_desc_t *stor_dev; + + err = init_sata(CONFIG_SPL_SATA_BOOT_DEVICE); + if (err) { +#ifdef CONFIG_SPL_LIBCOMMON_SUPPORT + printf("spl: sata init failed: err - %d\n", err); +#endif + hang(); + } else { + /* try to recognize storage devices immediately */ + stor_dev = scsi_get_dev(0); + } + +#ifdef CONFIG_SPL_OS_BOOT + if (spl_start_uboot() || spl_load_image_fat_os(stor_dev, + CONFIG_SYS_SATA_FAT_BOOT_PARTITION)) +#endif + err = spl_load_image_fat(stor_dev, + CONFIG_SYS_SATA_FAT_BOOT_PARTITION, + CONFIG_SPL_FAT_LOAD_PAYLOAD_NAME); + if (err) { + puts("Error loading sata device\n"); + hang(); + } +} diff --git a/qemu/roms/u-boot/common/spl/spl_usb.c b/qemu/roms/u-boot/common/spl/spl_usb.c new file mode 100644 index 000000000..53a904379 --- /dev/null +++ b/qemu/roms/u-boot/common/spl/spl_usb.c @@ -0,0 +1,58 @@ +/* + * (C) Copyright 2014 + * Texas Instruments, <www.ti.com> + * + * Dan Murphy <dmurphy@ti.com> + * + * SPDX-License-Identifier: GPL-2.0+ + * + * Derived work from spl_mmc.c + */ + +#include <common.h> +#include <spl.h> +#include <asm/u-boot.h> +#include <usb.h> +#include <fat.h> + +DECLARE_GLOBAL_DATA_PTR; + +#ifdef CONFIG_USB_STORAGE +static int usb_stor_curr_dev = -1; /* current device */ +#endif + +void spl_usb_load_image(void) +{ + int err; + block_dev_desc_t *stor_dev; + + usb_stop(); + err = usb_init(); + if (err) { +#ifdef CONFIG_SPL_LIBCOMMON_SUPPORT + printf("%s: usb init failed: err - %d\n", __func__, err); +#endif + hang(); + } + +#ifdef CONFIG_USB_STORAGE + /* try to recognize storage devices immediately */ + usb_stor_curr_dev = usb_stor_scan(1); + stor_dev = usb_stor_get_dev(usb_stor_curr_dev); +#endif + + debug("boot mode - FAT\n"); + +#ifdef CONFIG_SPL_OS_BOOT + if (spl_start_uboot() || spl_load_image_fat_os(stor_dev, + CONFIG_SYS_USB_FAT_BOOT_PARTITION)) +#endif + err = spl_load_image_fat(stor_dev, + CONFIG_SYS_USB_FAT_BOOT_PARTITION, + CONFIG_SPL_FAT_LOAD_PAYLOAD_NAME); + + if (err) { + puts("Error loading USB device\n"); + hang(); + } +} diff --git a/qemu/roms/u-boot/common/spl/spl_ymodem.c b/qemu/roms/u-boot/common/spl/spl_ymodem.c new file mode 100644 index 000000000..0f1e99707 --- /dev/null +++ b/qemu/roms/u-boot/common/spl/spl_ymodem.c @@ -0,0 +1,59 @@ +/* + * (C) Copyright 2000-2004 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * (C) Copyright 2011 + * Texas Instruments, <www.ti.com> + * + * Matt Porter <mporter@ti.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ +#include <common.h> +#include <spl.h> +#include <xyzModem.h> +#include <asm/u-boot.h> +#include <asm/utils.h> + +#define BUF_SIZE 1024 + +static int getcymodem(void) { + if (tstc()) + return (getc()); + return -1; +} + +void spl_ymodem_load_image(void) +{ + int size = 0; + int err; + int res; + int ret; + connection_info_t info; + char buf[BUF_SIZE]; + ulong store_addr = ~0; + ulong addr = 0; + + info.mode = xyzModem_ymodem; + ret = xyzModem_stream_open(&info, &err); + + if (!ret) { + while ((res = + xyzModem_stream_read(buf, BUF_SIZE, &err)) > 0) { + if (addr == 0) + spl_parse_image_header((struct image_header *)buf); + store_addr = addr + spl_image.load_addr; + size += res; + addr += res; + memcpy((char *)(store_addr), buf, res); + } + } else { + printf("spl: ymodem err - %s\n", xyzModem_error(err)); + hang(); + } + + xyzModem_stream_close(&err); + xyzModem_stream_terminate(false, &getcymodem); + + printf("Loaded %d bytes\n", size); +} diff --git a/qemu/roms/u-boot/common/splash.c b/qemu/roms/u-boot/common/splash.c new file mode 100644 index 000000000..18885f1bf --- /dev/null +++ b/qemu/roms/u-boot/common/splash.c @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2013, Boundary Devices <info@boundarydevices.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., http://www.fsf.org/about/contact/ + * + */ + +#include <common.h> +#include <splash.h> + +int __splash_screen_prepare(void) +{ + return 0; +} + +int splash_screen_prepare(void) + __attribute__ ((weak, alias("__splash_screen_prepare"))); + + +#ifdef CONFIG_SPLASH_SCREEN_ALIGN +void splash_get_pos(int *x, int *y) +{ + char *s = getenv("splashpos"); + + if (!s) + return; + + if (s[0] == 'm') + *x = BMP_ALIGN_CENTER; + else + *x = simple_strtol(s, NULL, 0); + + s = strchr(s + 1, ','); + if (s != NULL) { + if (s[1] == 'm') + *y = BMP_ALIGN_CENTER; + else + *y = simple_strtol(s + 1, NULL, 0); + } +} +#endif /* CONFIG_SPLASH_SCREEN_ALIGN */ diff --git a/qemu/roms/u-boot/common/stdio.c b/qemu/roms/u-boot/common/stdio.c new file mode 100644 index 000000000..844f98c18 --- /dev/null +++ b/qemu/roms/u-boot/common/stdio.c @@ -0,0 +1,233 @@ +/* + * Copyright (C) 2009 Sergey Kubushyn <ksi@koi8.net> + * + * Changes for multibus/multiadapter I2C support. + * + * (C) Copyright 2000 + * Paolo Scaffardi, AIRVENT SAM s.p.a - RIMINI(ITALY), arsenio@tin.it + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <config.h> +#include <common.h> +#include <stdarg.h> +#include <malloc.h> +#include <stdio_dev.h> +#include <serial.h> +#ifdef CONFIG_LOGBUFFER +#include <logbuff.h> +#endif + +#if defined(CONFIG_HARD_I2C) || defined(CONFIG_SYS_I2C) +#include <i2c.h> +#endif + +DECLARE_GLOBAL_DATA_PTR; + +static struct stdio_dev devs; +struct stdio_dev *stdio_devices[] = { NULL, NULL, NULL }; +char *stdio_names[MAX_FILES] = { "stdin", "stdout", "stderr" }; + +#if defined(CONFIG_SPLASH_SCREEN) && !defined(CONFIG_SYS_DEVICE_NULLDEV) +#define CONFIG_SYS_DEVICE_NULLDEV 1 +#endif + + +#ifdef CONFIG_SYS_DEVICE_NULLDEV +void nulldev_putc(const char c) +{ + /* nulldev is empty! */ +} + +void nulldev_puts(const char *s) +{ + /* nulldev is empty! */ +} + +int nulldev_input(void) +{ + /* nulldev is empty! */ + return 0; +} +#endif + +/************************************************************************** + * SYSTEM DRIVERS + ************************************************************************** + */ + +static void drv_system_init (void) +{ + struct stdio_dev dev; + + memset (&dev, 0, sizeof (dev)); + + strcpy (dev.name, "serial"); + dev.flags = DEV_FLAGS_OUTPUT | DEV_FLAGS_INPUT | DEV_FLAGS_SYSTEM; + dev.putc = serial_putc; + dev.puts = serial_puts; + dev.getc = serial_getc; + dev.tstc = serial_tstc; + stdio_register (&dev); + +#ifdef CONFIG_SYS_DEVICE_NULLDEV + memset (&dev, 0, sizeof (dev)); + + strcpy (dev.name, "nulldev"); + dev.flags = DEV_FLAGS_OUTPUT | DEV_FLAGS_INPUT | DEV_FLAGS_SYSTEM; + dev.putc = nulldev_putc; + dev.puts = nulldev_puts; + dev.getc = nulldev_input; + dev.tstc = nulldev_input; + + stdio_register (&dev); +#endif +} + +/************************************************************************** + * DEVICES + ************************************************************************** + */ +struct list_head* stdio_get_list(void) +{ + return &(devs.list); +} + +struct stdio_dev* stdio_get_by_name(const char *name) +{ + struct list_head *pos; + struct stdio_dev *dev; + + if(!name) + return NULL; + + list_for_each(pos, &(devs.list)) { + dev = list_entry(pos, struct stdio_dev, list); + if(strcmp(dev->name, name) == 0) + return dev; + } + + return NULL; +} + +struct stdio_dev* stdio_clone(struct stdio_dev *dev) +{ + struct stdio_dev *_dev; + + if(!dev) + return NULL; + + _dev = calloc(1, sizeof(struct stdio_dev)); + + if(!_dev) + return NULL; + + memcpy(_dev, dev, sizeof(struct stdio_dev)); + + return _dev; +} + +int stdio_register (struct stdio_dev * dev) +{ + struct stdio_dev *_dev; + + _dev = stdio_clone(dev); + if(!_dev) + return -1; + list_add_tail(&(_dev->list), &(devs.list)); + return 0; +} + +/* deregister the device "devname". + * returns 0 if success, -1 if device is assigned and 1 if devname not found + */ +#ifdef CONFIG_SYS_STDIO_DEREGISTER +int stdio_deregister(const char *devname) +{ + int l; + struct list_head *pos; + struct stdio_dev *dev; + char temp_names[3][16]; + + dev = stdio_get_by_name(devname); + + if(!dev) /* device not found */ + return -1; + /* get stdio devices (ListRemoveItem changes the dev list) */ + for (l=0 ; l< MAX_FILES; l++) { + if (stdio_devices[l] == dev) { + /* Device is assigned -> report error */ + return -1; + } + memcpy (&temp_names[l][0], + stdio_devices[l]->name, + sizeof(temp_names[l])); + } + + list_del(&(dev->list)); + + /* reassign Device list */ + list_for_each(pos, &(devs.list)) { + dev = list_entry(pos, struct stdio_dev, list); + for (l=0 ; l< MAX_FILES; l++) { + if(strcmp(dev->name, temp_names[l]) == 0) + stdio_devices[l] = dev; + } + } + return 0; +} +#endif /* CONFIG_SYS_STDIO_DEREGISTER */ + +int stdio_init (void) +{ +#if defined(CONFIG_NEEDS_MANUAL_RELOC) + /* already relocated for current ARM implementation */ + ulong relocation_offset = gd->reloc_off; + int i; + + /* relocate device name pointers */ + for (i = 0; i < (sizeof (stdio_names) / sizeof (char *)); ++i) { + stdio_names[i] = (char *) (((ulong) stdio_names[i]) + + relocation_offset); + } +#endif /* CONFIG_NEEDS_MANUAL_RELOC */ + + /* Initialize the list */ + INIT_LIST_HEAD(&(devs.list)); + +#ifdef CONFIG_SYS_I2C + i2c_init_all(); +#else +#if defined(CONFIG_HARD_I2C) + i2c_init (CONFIG_SYS_I2C_SPEED, CONFIG_SYS_I2C_SLAVE); +#endif +#endif +#ifdef CONFIG_LCD + drv_lcd_init (); +#endif +#if defined(CONFIG_VIDEO) || defined(CONFIG_CFB_CONSOLE) + drv_video_init (); +#endif +#ifdef CONFIG_KEYBOARD + drv_keyboard_init (); +#endif +#ifdef CONFIG_LOGBUFFER + drv_logbuff_init (); +#endif + drv_system_init (); + serial_stdio_init (); +#ifdef CONFIG_USB_TTY + drv_usbtty_init (); +#endif +#ifdef CONFIG_NETCONSOLE + drv_nc_init (); +#endif +#ifdef CONFIG_JTAG_CONSOLE + drv_jtag_console_init (); +#endif +#ifdef CONFIG_CBMEM_CONSOLE + cbmemc_init(); +#endif + return (0); +} diff --git a/qemu/roms/u-boot/common/system_map.c b/qemu/roms/u-boot/common/system_map.c new file mode 100644 index 000000000..8307293bf --- /dev/null +++ b/qemu/roms/u-boot/common/system_map.c @@ -0,0 +1,8 @@ +/* + * The builtin symbol table for use with kallsyms + * + * Copyright (c) 2008-2009 Analog Devices Inc. + * Licensed under the GPL-2 or later. + */ + +const char const system_map[] = SYSTEM_MAP; diff --git a/qemu/roms/u-boot/common/update.c b/qemu/roms/u-boot/common/update.c new file mode 100644 index 000000000..cc830a786 --- /dev/null +++ b/qemu/roms/u-boot/common/update.c @@ -0,0 +1,307 @@ +/* + * (C) Copyright 2008 Semihalf + * + * Written by: Rafal Czubak <rcz@semihalf.com> + * Bartlomiej Sieka <tur@semihalf.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> + +#if !(defined(CONFIG_FIT) && defined(CONFIG_OF_LIBFDT)) +#error "CONFIG_FIT and CONFIG_OF_LIBFDT are required for auto-update feature" +#endif + +#if defined(CONFIG_SYS_NO_FLASH) +#error "CONFIG_SYS_NO_FLASH defined, but FLASH is required for auto-update feature" +#endif + +#include <command.h> +#include <flash.h> +#include <net.h> +#include <net/tftp.h> +#include <malloc.h> + +/* env variable holding the location of the update file */ +#define UPDATE_FILE_ENV "updatefile" + +/* set configuration defaults if needed */ +#ifndef CONFIG_UPDATE_LOAD_ADDR +#define CONFIG_UPDATE_LOAD_ADDR 0x100000 +#endif + +#ifndef CONFIG_UPDATE_TFTP_MSEC_MAX +#define CONFIG_UPDATE_TFTP_MSEC_MAX 100 +#endif + +#ifndef CONFIG_UPDATE_TFTP_CNT_MAX +#define CONFIG_UPDATE_TFTP_CNT_MAX 0 +#endif + +extern ulong TftpRRQTimeoutMSecs; +extern int TftpRRQTimeoutCountMax; +extern flash_info_t flash_info[]; +extern ulong load_addr; + +static uchar *saved_prot_info; + +static int update_load(char *filename, ulong msec_max, int cnt_max, ulong addr) +{ + int size, rv; + ulong saved_timeout_msecs; + int saved_timeout_count; + char *saved_netretry, *saved_bootfile; + + rv = 0; + /* save used globals and env variable */ + saved_timeout_msecs = TftpRRQTimeoutMSecs; + saved_timeout_count = TftpRRQTimeoutCountMax; + saved_netretry = strdup(getenv("netretry")); + saved_bootfile = strdup(BootFile); + + /* set timeouts for auto-update */ + TftpRRQTimeoutMSecs = msec_max; + TftpRRQTimeoutCountMax = cnt_max; + + /* we don't want to retry the connection if errors occur */ + setenv("netretry", "no"); + + /* download the update file */ + load_addr = addr; + copy_filename(BootFile, filename, sizeof(BootFile)); + size = NetLoop(TFTPGET); + + if (size < 0) + rv = 1; + else if (size > 0) + flush_cache(addr, size); + + /* restore changed globals and env variable */ + TftpRRQTimeoutMSecs = saved_timeout_msecs; + TftpRRQTimeoutCountMax = saved_timeout_count; + + setenv("netretry", saved_netretry); + if (saved_netretry != NULL) + free(saved_netretry); + + if (saved_bootfile != NULL) { + copy_filename(BootFile, saved_bootfile, sizeof(BootFile)); + free(saved_bootfile); + } + + return rv; +} + +static int update_flash_protect(int prot, ulong addr_first, ulong addr_last) +{ + uchar *sp_info_ptr; + ulong s; + int i, bank, cnt; + flash_info_t *info; + + sp_info_ptr = NULL; + + if (prot == 0) { + saved_prot_info = + calloc(CONFIG_SYS_MAX_FLASH_BANKS * CONFIG_SYS_MAX_FLASH_SECT, 1); + if (!saved_prot_info) + return 1; + } + + for (bank = 0; bank < CONFIG_SYS_MAX_FLASH_BANKS; ++bank) { + cnt = 0; + info = &flash_info[bank]; + + /* Nothing to do if the bank doesn't exist */ + if (info->sector_count == 0) + return 0; + + /* Point to current bank protection information */ + sp_info_ptr = saved_prot_info + (bank * CONFIG_SYS_MAX_FLASH_SECT); + + /* + * Adjust addr_first or addr_last if we are on bank boundary. + * Address space between banks must be continuous for other + * flash functions (like flash_sect_erase or flash_write) to + * succeed. Banks must also be numbered in correct order, + * according to increasing addresses. + */ + if (addr_last > info->start[0] + info->size - 1) + addr_last = info->start[0] + info->size - 1; + if (addr_first < info->start[0]) + addr_first = info->start[0]; + + for (i = 0; i < info->sector_count; i++) { + /* Save current information about protected sectors */ + if (prot == 0) { + s = info->start[i]; + if ((s >= addr_first) && (s <= addr_last)) + sp_info_ptr[i] = info->protect[i]; + + } + + /* Protect/unprotect sectors */ + if (sp_info_ptr[i] == 1) { +#if defined(CONFIG_SYS_FLASH_PROTECTION) + if (flash_real_protect(info, i, prot)) + return 1; +#else + info->protect[i] = prot; +#endif + cnt++; + } + } + + if (cnt) { + printf("%sProtected %d sectors\n", + prot ? "": "Un-", cnt); + } + } + + if((prot == 1) && saved_prot_info) + free(saved_prot_info); + + return 0; +} + +static int update_flash(ulong addr_source, ulong addr_first, ulong size) +{ + ulong addr_last = addr_first + size - 1; + + /* round last address to the sector boundary */ + if (flash_sect_roundb(&addr_last) > 0) + return 1; + + if (addr_first >= addr_last) { + printf("Error: end address exceeds addressing space\n"); + return 1; + } + + /* remove protection on processed sectors */ + if (update_flash_protect(0, addr_first, addr_last) > 0) { + printf("Error: could not unprotect flash sectors\n"); + return 1; + } + + printf("Erasing 0x%08lx - 0x%08lx", addr_first, addr_last); + if (flash_sect_erase(addr_first, addr_last) > 0) { + printf("Error: could not erase flash\n"); + return 1; + } + + printf("Copying to flash..."); + if (flash_write((char *)addr_source, addr_first, size) > 0) { + printf("Error: could not copy to flash\n"); + return 1; + } + printf("done\n"); + + /* enable protection on processed sectors */ + if (update_flash_protect(1, addr_first, addr_last) > 0) { + printf("Error: could not protect flash sectors\n"); + return 1; + } + + return 0; +} + +static int update_fit_getparams(const void *fit, int noffset, ulong *addr, + ulong *fladdr, ulong *size) +{ + const void *data; + + if (fit_image_get_data(fit, noffset, &data, (size_t *)size)) + return 1; + + if (fit_image_get_load(fit, noffset, (ulong *)fladdr)) + return 1; + + *addr = (ulong)data; + + return 0; +} + +int update_tftp(ulong addr) +{ + char *filename, *env_addr; + int images_noffset, ndepth, noffset; + ulong update_addr, update_fladdr, update_size; + void *fit; + int ret = 0; + + /* use already present image */ + if (addr) + goto got_update_file; + + printf("Auto-update from TFTP: "); + + /* get the file name of the update file */ + filename = getenv(UPDATE_FILE_ENV); + if (filename == NULL) { + printf("failed, env. variable '%s' not found\n", + UPDATE_FILE_ENV); + return 1; + } + + printf("trying update file '%s'\n", filename); + + /* get load address of downloaded update file */ + if ((env_addr = getenv("loadaddr")) != NULL) + addr = simple_strtoul(env_addr, NULL, 16); + else + addr = CONFIG_UPDATE_LOAD_ADDR; + + + if (update_load(filename, CONFIG_UPDATE_TFTP_MSEC_MAX, + CONFIG_UPDATE_TFTP_CNT_MAX, addr)) { + printf("Can't load update file, aborting auto-update\n"); + return 1; + } + +got_update_file: + fit = (void *)addr; + + if (!fit_check_format((void *)fit)) { + printf("Bad FIT format of the update file, aborting " + "auto-update\n"); + return 1; + } + + /* process updates */ + images_noffset = fdt_path_offset(fit, FIT_IMAGES_PATH); + + ndepth = 0; + noffset = fdt_next_node(fit, images_noffset, &ndepth); + while (noffset >= 0 && ndepth > 0) { + if (ndepth != 1) + goto next_node; + + printf("Processing update '%s' :", + fit_get_name(fit, noffset, NULL)); + + if (!fit_image_verify(fit, noffset)) { + printf("Error: invalid update hash, aborting\n"); + ret = 1; + goto next_node; + } + + printf("\n"); + if (update_fit_getparams(fit, noffset, &update_addr, + &update_fladdr, &update_size)) { + printf("Error: can't get update parameteres, " + "aborting\n"); + ret = 1; + goto next_node; + } + if (update_flash(update_addr, update_fladdr, update_size)) { + printf("Error: can't flash update, aborting\n"); + ret = 1; + goto next_node; + } +next_node: + noffset = fdt_next_node(fit, noffset, &ndepth); + } + + return ret; +} diff --git a/qemu/roms/u-boot/common/usb.c b/qemu/roms/u-boot/common/usb.c new file mode 100644 index 000000000..60daa1005 --- /dev/null +++ b/qemu/roms/u-boot/common/usb.c @@ -0,0 +1,1075 @@ +/* + * Most of this source has been derived from the Linux USB + * project: + * (C) Copyright Linus Torvalds 1999 + * (C) Copyright Johannes Erdfelt 1999-2001 + * (C) Copyright Andreas Gal 1999 + * (C) Copyright Gregory P. Smith 1999 + * (C) Copyright Deti Fliegl 1999 (new USB architecture) + * (C) Copyright Randy Dunlap 2000 + * (C) Copyright David Brownell 2000 (kernel hotplug, usb_device_id) + * (C) Copyright Yggdrasil Computing, Inc. 2000 + * (usb_device_id matching changes by Adam J. Richter) + * + * Adapted for U-Boot: + * (C) Copyright 2001 Denis Peter, MPL AG Switzerland + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/* + * How it works: + * + * Since this is a bootloader, the devices will not be automatic + * (re)configured on hotplug, but after a restart of the USB the + * device should work. + * + * For each transfer (except "Interrupt") we wait for completion. + */ +#include <common.h> +#include <command.h> +#include <asm/processor.h> +#include <linux/compiler.h> +#include <linux/ctype.h> +#include <asm/byteorder.h> +#include <asm/unaligned.h> +#include <compiler.h> + +#include <usb.h> +#ifdef CONFIG_4xx +#include <asm/4xx_pci.h> +#endif + +#define USB_BUFSIZ 512 + +static struct usb_device usb_dev[USB_MAX_DEVICE]; +static int dev_index; +static int asynch_allowed; + +char usb_started; /* flag for the started/stopped USB status */ + +#ifndef CONFIG_USB_MAX_CONTROLLER_COUNT +#define CONFIG_USB_MAX_CONTROLLER_COUNT 1 +#endif + +/*************************************************************************** + * Init USB Device + */ +int usb_init(void) +{ + void *ctrl; + struct usb_device *dev; + int i, start_index = 0; + + dev_index = 0; + asynch_allowed = 1; + usb_hub_reset(); + + /* first make all devices unknown */ + for (i = 0; i < USB_MAX_DEVICE; i++) { + memset(&usb_dev[i], 0, sizeof(struct usb_device)); + usb_dev[i].devnum = -1; + } + + /* init low_level USB */ + for (i = 0; i < CONFIG_USB_MAX_CONTROLLER_COUNT; i++) { + /* init low_level USB */ + printf("USB%d: ", i); + if (usb_lowlevel_init(i, USB_INIT_HOST, &ctrl)) { + puts("lowlevel init failed\n"); + continue; + } + /* + * lowlevel init is OK, now scan the bus for devices + * i.e. search HUBs and configure them + */ + start_index = dev_index; + printf("scanning bus %d for devices... ", i); + dev = usb_alloc_new_device(ctrl); + /* + * device 0 is always present + * (root hub, so let it analyze) + */ + if (dev) + usb_new_device(dev); + + if (start_index == dev_index) + puts("No USB Device found\n"); + else + printf("%d USB Device(s) found\n", + dev_index - start_index); + + usb_started = 1; + } + + debug("scan end\n"); + /* if we were not able to find at least one working bus, bail out */ + if (!usb_started) { + puts("USB error: all controllers failed lowlevel init\n"); + return -1; + } + + return 0; +} + +/****************************************************************************** + * Stop USB this stops the LowLevel Part and deregisters USB devices. + */ +int usb_stop(void) +{ + int i; + + if (usb_started) { + asynch_allowed = 1; + usb_started = 0; + usb_hub_reset(); + + for (i = 0; i < CONFIG_USB_MAX_CONTROLLER_COUNT; i++) { + if (usb_lowlevel_stop(i)) + printf("failed to stop USB controller %d\n", i); + } + } + + return 0; +} + +/* + * disables the asynch behaviour of the control message. This is used for data + * transfers that uses the exclusiv access to the control and bulk messages. + * Returns the old value so it can be restored later. + */ +int usb_disable_asynch(int disable) +{ + int old_value = asynch_allowed; + + asynch_allowed = !disable; + return old_value; +} + + +/*------------------------------------------------------------------- + * Message wrappers. + * + */ + +/* + * submits an Interrupt Message + */ +int usb_submit_int_msg(struct usb_device *dev, unsigned long pipe, + void *buffer, int transfer_len, int interval) +{ + return submit_int_msg(dev, pipe, buffer, transfer_len, interval); +} + +/* + * submits a control message and waits for comletion (at least timeout * 1ms) + * If timeout is 0, we don't wait for completion (used as example to set and + * clear keyboards LEDs). For data transfers, (storage transfers) we don't + * allow control messages with 0 timeout, by previousely resetting the flag + * asynch_allowed (usb_disable_asynch(1)). + * returns the transfered length if OK or -1 if error. The transfered length + * and the current status are stored in the dev->act_len and dev->status. + */ +int usb_control_msg(struct usb_device *dev, unsigned int pipe, + unsigned char request, unsigned char requesttype, + unsigned short value, unsigned short index, + void *data, unsigned short size, int timeout) +{ + ALLOC_CACHE_ALIGN_BUFFER(struct devrequest, setup_packet, 1); + + if ((timeout == 0) && (!asynch_allowed)) { + /* request for a asynch control pipe is not allowed */ + return -1; + } + + /* set setup command */ + setup_packet->requesttype = requesttype; + setup_packet->request = request; + setup_packet->value = cpu_to_le16(value); + setup_packet->index = cpu_to_le16(index); + setup_packet->length = cpu_to_le16(size); + debug("usb_control_msg: request: 0x%X, requesttype: 0x%X, " \ + "value 0x%X index 0x%X length 0x%X\n", + request, requesttype, value, index, size); + dev->status = USB_ST_NOT_PROC; /*not yet processed */ + + if (submit_control_msg(dev, pipe, data, size, setup_packet) < 0) + return -1; + if (timeout == 0) + return (int)size; + + /* + * Wait for status to update until timeout expires, USB driver + * interrupt handler may set the status when the USB operation has + * been completed. + */ + while (timeout--) { + if (!((volatile unsigned long)dev->status & USB_ST_NOT_PROC)) + break; + mdelay(1); + } + if (dev->status) + return -1; + + return dev->act_len; + +} + +/*------------------------------------------------------------------- + * submits bulk message, and waits for completion. returns 0 if Ok or + * -1 if Error. + * synchronous behavior + */ +int usb_bulk_msg(struct usb_device *dev, unsigned int pipe, + void *data, int len, int *actual_length, int timeout) +{ + if (len < 0) + return -1; + dev->status = USB_ST_NOT_PROC; /*not yet processed */ + if (submit_bulk_msg(dev, pipe, data, len) < 0) + return -1; + while (timeout--) { + if (!((volatile unsigned long)dev->status & USB_ST_NOT_PROC)) + break; + mdelay(1); + } + *actual_length = dev->act_len; + if (dev->status == 0) + return 0; + else + return -1; +} + + +/*------------------------------------------------------------------- + * Max Packet stuff + */ + +/* + * returns the max packet size, depending on the pipe direction and + * the configurations values + */ +int usb_maxpacket(struct usb_device *dev, unsigned long pipe) +{ + /* direction is out -> use emaxpacket out */ + if ((pipe & USB_DIR_IN) == 0) + return dev->epmaxpacketout[((pipe>>15) & 0xf)]; + else + return dev->epmaxpacketin[((pipe>>15) & 0xf)]; +} + +/* + * The routine usb_set_maxpacket_ep() is extracted from the loop of routine + * usb_set_maxpacket(), because the optimizer of GCC 4.x chokes on this routine + * when it is inlined in 1 single routine. What happens is that the register r3 + * is used as loop-count 'i', but gets overwritten later on. + * This is clearly a compiler bug, but it is easier to workaround it here than + * to update the compiler (Occurs with at least several GCC 4.{1,2},x + * CodeSourcery compilers like e.g. 2007q3, 2008q1, 2008q3 lite editions on ARM) + * + * NOTE: Similar behaviour was observed with GCC4.6 on ARMv5. + */ +static void noinline +usb_set_maxpacket_ep(struct usb_device *dev, int if_idx, int ep_idx) +{ + int b; + struct usb_endpoint_descriptor *ep; + u16 ep_wMaxPacketSize; + + ep = &dev->config.if_desc[if_idx].ep_desc[ep_idx]; + + b = ep->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK; + ep_wMaxPacketSize = get_unaligned(&ep->wMaxPacketSize); + + if ((ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == + USB_ENDPOINT_XFER_CONTROL) { + /* Control => bidirectional */ + dev->epmaxpacketout[b] = ep_wMaxPacketSize; + dev->epmaxpacketin[b] = ep_wMaxPacketSize; + debug("##Control EP epmaxpacketout/in[%d] = %d\n", + b, dev->epmaxpacketin[b]); + } else { + if ((ep->bEndpointAddress & 0x80) == 0) { + /* OUT Endpoint */ + if (ep_wMaxPacketSize > dev->epmaxpacketout[b]) { + dev->epmaxpacketout[b] = ep_wMaxPacketSize; + debug("##EP epmaxpacketout[%d] = %d\n", + b, dev->epmaxpacketout[b]); + } + } else { + /* IN Endpoint */ + if (ep_wMaxPacketSize > dev->epmaxpacketin[b]) { + dev->epmaxpacketin[b] = ep_wMaxPacketSize; + debug("##EP epmaxpacketin[%d] = %d\n", + b, dev->epmaxpacketin[b]); + } + } /* if out */ + } /* if control */ +} + +/* + * set the max packed value of all endpoints in the given configuration + */ +static int usb_set_maxpacket(struct usb_device *dev) +{ + int i, ii; + + for (i = 0; i < dev->config.desc.bNumInterfaces; i++) + for (ii = 0; ii < dev->config.if_desc[i].desc.bNumEndpoints; ii++) + usb_set_maxpacket_ep(dev, i, ii); + + return 0; +} + +/******************************************************************************* + * Parse the config, located in buffer, and fills the dev->config structure. + * Note that all little/big endian swapping are done automatically. + * (wTotalLength has already been swapped and sanitized when it was read.) + */ +static int usb_parse_config(struct usb_device *dev, + unsigned char *buffer, int cfgno) +{ + struct usb_descriptor_header *head; + int index, ifno, epno, curr_if_num; + u16 ep_wMaxPacketSize; + struct usb_interface *if_desc = NULL; + + ifno = -1; + epno = -1; + curr_if_num = -1; + + dev->configno = cfgno; + head = (struct usb_descriptor_header *) &buffer[0]; + if (head->bDescriptorType != USB_DT_CONFIG) { + printf(" ERROR: NOT USB_CONFIG_DESC %x\n", + head->bDescriptorType); + return -1; + } + if (head->bLength != USB_DT_CONFIG_SIZE) { + printf("ERROR: Invalid USB CFG length (%d)\n", head->bLength); + return -1; + } + memcpy(&dev->config, head, USB_DT_CONFIG_SIZE); + dev->config.no_of_if = 0; + + index = dev->config.desc.bLength; + /* Ok the first entry must be a configuration entry, + * now process the others */ + head = (struct usb_descriptor_header *) &buffer[index]; + while (index + 1 < dev->config.desc.wTotalLength && head->bLength) { + switch (head->bDescriptorType) { + case USB_DT_INTERFACE: + if (head->bLength != USB_DT_INTERFACE_SIZE) { + printf("ERROR: Invalid USB IF length (%d)\n", + head->bLength); + break; + } + if (index + USB_DT_INTERFACE_SIZE > + dev->config.desc.wTotalLength) { + puts("USB IF descriptor overflowed buffer!\n"); + break; + } + if (((struct usb_interface_descriptor *) \ + head)->bInterfaceNumber != curr_if_num) { + /* this is a new interface, copy new desc */ + ifno = dev->config.no_of_if; + if (ifno >= USB_MAXINTERFACES) { + puts("Too many USB interfaces!\n"); + /* try to go on with what we have */ + return 1; + } + if_desc = &dev->config.if_desc[ifno]; + dev->config.no_of_if++; + memcpy(if_desc, head, + USB_DT_INTERFACE_SIZE); + if_desc->no_of_ep = 0; + if_desc->num_altsetting = 1; + curr_if_num = + if_desc->desc.bInterfaceNumber; + } else { + /* found alternate setting for the interface */ + if (ifno >= 0) { + if_desc = &dev->config.if_desc[ifno]; + if_desc->num_altsetting++; + } + } + break; + case USB_DT_ENDPOINT: + if (head->bLength != USB_DT_ENDPOINT_SIZE) { + printf("ERROR: Invalid USB EP length (%d)\n", + head->bLength); + break; + } + if (index + USB_DT_ENDPOINT_SIZE > + dev->config.desc.wTotalLength) { + puts("USB EP descriptor overflowed buffer!\n"); + break; + } + if (ifno < 0) { + puts("Endpoint descriptor out of order!\n"); + break; + } + epno = dev->config.if_desc[ifno].no_of_ep; + if_desc = &dev->config.if_desc[ifno]; + if (epno > USB_MAXENDPOINTS) { + printf("Interface %d has too many endpoints!\n", + if_desc->desc.bInterfaceNumber); + return 1; + } + /* found an endpoint */ + if_desc->no_of_ep++; + memcpy(&if_desc->ep_desc[epno], head, + USB_DT_ENDPOINT_SIZE); + ep_wMaxPacketSize = get_unaligned(&dev->config.\ + if_desc[ifno].\ + ep_desc[epno].\ + wMaxPacketSize); + put_unaligned(le16_to_cpu(ep_wMaxPacketSize), + &dev->config.\ + if_desc[ifno].\ + ep_desc[epno].\ + wMaxPacketSize); + debug("if %d, ep %d\n", ifno, epno); + break; + case USB_DT_SS_ENDPOINT_COMP: + if (head->bLength != USB_DT_SS_EP_COMP_SIZE) { + printf("ERROR: Invalid USB EPC length (%d)\n", + head->bLength); + break; + } + if (index + USB_DT_SS_EP_COMP_SIZE > + dev->config.desc.wTotalLength) { + puts("USB EPC descriptor overflowed buffer!\n"); + break; + } + if (ifno < 0 || epno < 0) { + puts("EPC descriptor out of order!\n"); + break; + } + if_desc = &dev->config.if_desc[ifno]; + memcpy(&if_desc->ss_ep_comp_desc[epno], head, + USB_DT_SS_EP_COMP_SIZE); + break; + default: + if (head->bLength == 0) + return 1; + + debug("unknown Description Type : %x\n", + head->bDescriptorType); + +#ifdef DEBUG + { + unsigned char *ch = (unsigned char *)head; + int i; + + for (i = 0; i < head->bLength; i++) + debug("%02X ", *ch++); + debug("\n\n\n"); + } +#endif + break; + } + index += head->bLength; + head = (struct usb_descriptor_header *)&buffer[index]; + } + return 1; +} + +/*********************************************************************** + * Clears an endpoint + * endp: endpoint number in bits 0-3; + * direction flag in bit 7 (1 = IN, 0 = OUT) + */ +int usb_clear_halt(struct usb_device *dev, int pipe) +{ + int result; + int endp = usb_pipeendpoint(pipe)|(usb_pipein(pipe)<<7); + + result = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), + USB_REQ_CLEAR_FEATURE, USB_RECIP_ENDPOINT, 0, + endp, NULL, 0, USB_CNTL_TIMEOUT * 3); + + /* don't clear if failed */ + if (result < 0) + return result; + + /* + * NOTE: we do not get status and verify reset was successful + * as some devices are reported to lock up upon this check.. + */ + + usb_endpoint_running(dev, usb_pipeendpoint(pipe), usb_pipeout(pipe)); + + /* toggle is reset on clear */ + usb_settoggle(dev, usb_pipeendpoint(pipe), usb_pipeout(pipe), 0); + return 0; +} + + +/********************************************************************** + * get_descriptor type + */ +static int usb_get_descriptor(struct usb_device *dev, unsigned char type, + unsigned char index, void *buf, int size) +{ + int res; + res = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), + USB_REQ_GET_DESCRIPTOR, USB_DIR_IN, + (type << 8) + index, 0, + buf, size, USB_CNTL_TIMEOUT); + return res; +} + +/********************************************************************** + * gets configuration cfgno and store it in the buffer + */ +int usb_get_configuration_no(struct usb_device *dev, + unsigned char *buffer, int cfgno) +{ + int result; + unsigned int length; + struct usb_config_descriptor *config; + + config = (struct usb_config_descriptor *)&buffer[0]; + result = usb_get_descriptor(dev, USB_DT_CONFIG, cfgno, buffer, 9); + if (result < 9) { + if (result < 0) + printf("unable to get descriptor, error %lX\n", + dev->status); + else + printf("config descriptor too short " \ + "(expected %i, got %i)\n", 9, result); + return -1; + } + length = le16_to_cpu(config->wTotalLength); + + if (length > USB_BUFSIZ) { + printf("%s: failed to get descriptor - too long: %d\n", + __func__, length); + return -1; + } + + result = usb_get_descriptor(dev, USB_DT_CONFIG, cfgno, buffer, length); + debug("get_conf_no %d Result %d, wLength %d\n", cfgno, result, length); + config->wTotalLength = length; /* validated, with CPU byte order */ + + return result; +} + +/******************************************************************** + * set address of a device to the value in dev->devnum. + * This can only be done by addressing the device via the default address (0) + */ +static int usb_set_address(struct usb_device *dev) +{ + int res; + + debug("set address %d\n", dev->devnum); + res = usb_control_msg(dev, usb_snddefctrl(dev), + USB_REQ_SET_ADDRESS, 0, + (dev->devnum), 0, + NULL, 0, USB_CNTL_TIMEOUT); + return res; +} + +/******************************************************************** + * set interface number to interface + */ +int usb_set_interface(struct usb_device *dev, int interface, int alternate) +{ + struct usb_interface *if_face = NULL; + int ret, i; + + for (i = 0; i < dev->config.desc.bNumInterfaces; i++) { + if (dev->config.if_desc[i].desc.bInterfaceNumber == interface) { + if_face = &dev->config.if_desc[i]; + break; + } + } + if (!if_face) { + printf("selecting invalid interface %d", interface); + return -1; + } + /* + * We should return now for devices with only one alternate setting. + * According to 9.4.10 of the Universal Serial Bus Specification + * Revision 2.0 such devices can return with a STALL. This results in + * some USB sticks timeouting during initialization and then being + * unusable in U-Boot. + */ + if (if_face->num_altsetting == 1) + return 0; + + ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), + USB_REQ_SET_INTERFACE, USB_RECIP_INTERFACE, + alternate, interface, NULL, 0, + USB_CNTL_TIMEOUT * 5); + if (ret < 0) + return ret; + + return 0; +} + +/******************************************************************** + * set configuration number to configuration + */ +static int usb_set_configuration(struct usb_device *dev, int configuration) +{ + int res; + debug("set configuration %d\n", configuration); + /* set setup command */ + res = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), + USB_REQ_SET_CONFIGURATION, 0, + configuration, 0, + NULL, 0, USB_CNTL_TIMEOUT); + if (res == 0) { + dev->toggle[0] = 0; + dev->toggle[1] = 0; + return 0; + } else + return -1; +} + +/******************************************************************** + * set protocol to protocol + */ +int usb_set_protocol(struct usb_device *dev, int ifnum, int protocol) +{ + return usb_control_msg(dev, usb_sndctrlpipe(dev, 0), + USB_REQ_SET_PROTOCOL, USB_TYPE_CLASS | USB_RECIP_INTERFACE, + protocol, ifnum, NULL, 0, USB_CNTL_TIMEOUT); +} + +/******************************************************************** + * set idle + */ +int usb_set_idle(struct usb_device *dev, int ifnum, int duration, int report_id) +{ + return usb_control_msg(dev, usb_sndctrlpipe(dev, 0), + USB_REQ_SET_IDLE, USB_TYPE_CLASS | USB_RECIP_INTERFACE, + (duration << 8) | report_id, ifnum, NULL, 0, USB_CNTL_TIMEOUT); +} + +/******************************************************************** + * get report + */ +int usb_get_report(struct usb_device *dev, int ifnum, unsigned char type, + unsigned char id, void *buf, int size) +{ + return usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), + USB_REQ_GET_REPORT, + USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE, + (type << 8) + id, ifnum, buf, size, USB_CNTL_TIMEOUT); +} + +/******************************************************************** + * get class descriptor + */ +int usb_get_class_descriptor(struct usb_device *dev, int ifnum, + unsigned char type, unsigned char id, void *buf, int size) +{ + return usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), + USB_REQ_GET_DESCRIPTOR, USB_RECIP_INTERFACE | USB_DIR_IN, + (type << 8) + id, ifnum, buf, size, USB_CNTL_TIMEOUT); +} + +/******************************************************************** + * get string index in buffer + */ +static int usb_get_string(struct usb_device *dev, unsigned short langid, + unsigned char index, void *buf, int size) +{ + int i; + int result; + + for (i = 0; i < 3; ++i) { + /* some devices are flaky */ + result = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), + USB_REQ_GET_DESCRIPTOR, USB_DIR_IN, + (USB_DT_STRING << 8) + index, langid, buf, size, + USB_CNTL_TIMEOUT); + + if (result > 0) + break; + } + + return result; +} + + +static void usb_try_string_workarounds(unsigned char *buf, int *length) +{ + int newlength, oldlength = *length; + + for (newlength = 2; newlength + 1 < oldlength; newlength += 2) + if (!isprint(buf[newlength]) || buf[newlength + 1]) + break; + + if (newlength > 2) { + buf[0] = newlength; + *length = newlength; + } +} + + +static int usb_string_sub(struct usb_device *dev, unsigned int langid, + unsigned int index, unsigned char *buf) +{ + int rc; + + /* Try to read the string descriptor by asking for the maximum + * possible number of bytes */ + rc = usb_get_string(dev, langid, index, buf, 255); + + /* If that failed try to read the descriptor length, then + * ask for just that many bytes */ + if (rc < 2) { + rc = usb_get_string(dev, langid, index, buf, 2); + if (rc == 2) + rc = usb_get_string(dev, langid, index, buf, buf[0]); + } + + if (rc >= 2) { + if (!buf[0] && !buf[1]) + usb_try_string_workarounds(buf, &rc); + + /* There might be extra junk at the end of the descriptor */ + if (buf[0] < rc) + rc = buf[0]; + + rc = rc - (rc & 1); /* force a multiple of two */ + } + + if (rc < 2) + rc = -1; + + return rc; +} + + +/******************************************************************** + * usb_string: + * Get string index and translate it to ascii. + * returns string length (> 0) or error (< 0) + */ +int usb_string(struct usb_device *dev, int index, char *buf, size_t size) +{ + ALLOC_CACHE_ALIGN_BUFFER(unsigned char, mybuf, USB_BUFSIZ); + unsigned char *tbuf; + int err; + unsigned int u, idx; + + if (size <= 0 || !buf || !index) + return -1; + buf[0] = 0; + tbuf = &mybuf[0]; + + /* get langid for strings if it's not yet known */ + if (!dev->have_langid) { + err = usb_string_sub(dev, 0, 0, tbuf); + if (err < 0) { + debug("error getting string descriptor 0 " \ + "(error=%lx)\n", dev->status); + return -1; + } else if (tbuf[0] < 4) { + debug("string descriptor 0 too short\n"); + return -1; + } else { + dev->have_langid = -1; + dev->string_langid = tbuf[2] | (tbuf[3] << 8); + /* always use the first langid listed */ + debug("USB device number %d default " \ + "language ID 0x%x\n", + dev->devnum, dev->string_langid); + } + } + + err = usb_string_sub(dev, dev->string_langid, index, tbuf); + if (err < 0) + return err; + + size--; /* leave room for trailing NULL char in output buffer */ + for (idx = 0, u = 2; u < err; u += 2) { + if (idx >= size) + break; + if (tbuf[u+1]) /* high byte */ + buf[idx++] = '?'; /* non-ASCII character */ + else + buf[idx++] = tbuf[u]; + } + buf[idx] = 0; + err = idx; + return err; +} + + +/******************************************************************** + * USB device handling: + * the USB device are static allocated [USB_MAX_DEVICE]. + */ + + +/* returns a pointer to the device with the index [index]. + * if the device is not assigned (dev->devnum==-1) returns NULL + */ +struct usb_device *usb_get_dev_index(int index) +{ + if (usb_dev[index].devnum == -1) + return NULL; + else + return &usb_dev[index]; +} + +/* returns a pointer of a new device structure or NULL, if + * no device struct is available + */ +struct usb_device *usb_alloc_new_device(void *controller) +{ + int i; + debug("New Device %d\n", dev_index); + if (dev_index == USB_MAX_DEVICE) { + printf("ERROR, too many USB Devices, max=%d\n", USB_MAX_DEVICE); + return NULL; + } + /* default Address is 0, real addresses start with 1 */ + usb_dev[dev_index].devnum = dev_index + 1; + usb_dev[dev_index].maxchild = 0; + for (i = 0; i < USB_MAXCHILDREN; i++) + usb_dev[dev_index].children[i] = NULL; + usb_dev[dev_index].parent = NULL; + usb_dev[dev_index].controller = controller; + dev_index++; + return &usb_dev[dev_index - 1]; +} + +/* + * Free the newly created device node. + * Called in error cases where configuring a newly attached + * device fails for some reason. + */ +void usb_free_device(void) +{ + dev_index--; + debug("Freeing device node: %d\n", dev_index); + memset(&usb_dev[dev_index], 0, sizeof(struct usb_device)); + usb_dev[dev_index].devnum = -1; +} + +/* + * XHCI issues Enable Slot command and thereafter + * allocates device contexts. Provide a weak alias + * function for the purpose, so that XHCI overrides it + * and EHCI/OHCI just work out of the box. + */ +__weak int usb_alloc_device(struct usb_device *udev) +{ + return 0; +} +/* + * By the time we get here, the device has gotten a new device ID + * and is in the default state. We need to identify the thing and + * get the ball rolling.. + * + * Returns 0 for success, != 0 for error. + */ +int usb_new_device(struct usb_device *dev) +{ + int addr, err; + int tmp; + ALLOC_CACHE_ALIGN_BUFFER(unsigned char, tmpbuf, USB_BUFSIZ); + + /* + * Allocate usb 3.0 device context. + * USB 3.0 (xHCI) protocol tries to allocate device slot + * and related data structures first. This call does that. + * Refer to sec 4.3.2 in xHCI spec rev1.0 + */ + if (usb_alloc_device(dev)) { + printf("Cannot allocate device context to get SLOT_ID\n"); + return -1; + } + + /* We still haven't set the Address yet */ + addr = dev->devnum; + dev->devnum = 0; + +#ifdef CONFIG_LEGACY_USB_INIT_SEQ + /* this is the old and known way of initializing devices, it is + * different than what Windows and Linux are doing. Windows and Linux + * both retrieve 64 bytes while reading the device descriptor + * Several USB stick devices report ERR: CTL_TIMEOUT, caused by an + * invalid header while reading 8 bytes as device descriptor. */ + dev->descriptor.bMaxPacketSize0 = 8; /* Start off at 8 bytes */ + dev->maxpacketsize = PACKET_SIZE_8; + dev->epmaxpacketin[0] = 8; + dev->epmaxpacketout[0] = 8; + + err = usb_get_descriptor(dev, USB_DT_DEVICE, 0, tmpbuf, 8); + if (err < 8) { + printf("\n USB device not responding, " \ + "giving up (status=%lX)\n", dev->status); + return 1; + } + memcpy(&dev->descriptor, tmpbuf, 8); +#else + /* This is a Windows scheme of initialization sequence, with double + * reset of the device (Linux uses the same sequence) + * Some equipment is said to work only with such init sequence; this + * patch is based on the work by Alan Stern: + * http://sourceforge.net/mailarchive/forum.php? + * thread_id=5729457&forum_id=5398 + */ + __maybe_unused struct usb_device_descriptor *desc; + int port = -1; + struct usb_device *parent = dev->parent; + unsigned short portstatus; + + /* send 64-byte GET-DEVICE-DESCRIPTOR request. Since the descriptor is + * only 18 bytes long, this will terminate with a short packet. But if + * the maxpacket size is 8 or 16 the device may be waiting to transmit + * some more, or keeps on retransmitting the 8 byte header. */ + + desc = (struct usb_device_descriptor *)tmpbuf; + dev->descriptor.bMaxPacketSize0 = 64; /* Start off at 64 bytes */ + /* Default to 64 byte max packet size */ + dev->maxpacketsize = PACKET_SIZE_64; + dev->epmaxpacketin[0] = 64; + dev->epmaxpacketout[0] = 64; + + /* + * XHCI needs to issue a Address device command to setup + * proper device context structures, before it can interact + * with the device. So a get_descriptor will fail before any + * of that is done for XHCI unlike EHCI. + */ +#ifndef CONFIG_USB_XHCI + err = usb_get_descriptor(dev, USB_DT_DEVICE, 0, desc, 64); + if (err < 0) { + debug("usb_new_device: usb_get_descriptor() failed\n"); + return 1; + } + + dev->descriptor.bMaxPacketSize0 = desc->bMaxPacketSize0; + /* + * Fetch the device class, driver can use this info + * to differentiate between HUB and DEVICE. + */ + dev->descriptor.bDeviceClass = desc->bDeviceClass; +#endif + + if (parent) { + int j; + + /* find the port number we're at */ + for (j = 0; j < parent->maxchild; j++) { + if (parent->children[j] == dev) { + port = j; + break; + } + } + if (port < 0) { + printf("usb_new_device:cannot locate device's port.\n"); + return 1; + } + + /* reset the port for the second time */ + err = hub_port_reset(dev->parent, port, &portstatus); + if (err < 0) { + printf("\n Couldn't reset port %i\n", port); + return 1; + } + } +#endif + + dev->epmaxpacketin[0] = dev->descriptor.bMaxPacketSize0; + dev->epmaxpacketout[0] = dev->descriptor.bMaxPacketSize0; + switch (dev->descriptor.bMaxPacketSize0) { + case 8: + dev->maxpacketsize = PACKET_SIZE_8; + break; + case 16: + dev->maxpacketsize = PACKET_SIZE_16; + break; + case 32: + dev->maxpacketsize = PACKET_SIZE_32; + break; + case 64: + dev->maxpacketsize = PACKET_SIZE_64; + break; + } + dev->devnum = addr; + + err = usb_set_address(dev); /* set address */ + + if (err < 0) { + printf("\n USB device not accepting new address " \ + "(error=%lX)\n", dev->status); + return 1; + } + + mdelay(10); /* Let the SET_ADDRESS settle */ + + tmp = sizeof(dev->descriptor); + + err = usb_get_descriptor(dev, USB_DT_DEVICE, 0, + tmpbuf, sizeof(dev->descriptor)); + if (err < tmp) { + if (err < 0) + printf("unable to get device descriptor (error=%d)\n", + err); + else + printf("USB device descriptor short read " \ + "(expected %i, got %i)\n", tmp, err); + return 1; + } + memcpy(&dev->descriptor, tmpbuf, sizeof(dev->descriptor)); + /* correct le values */ + le16_to_cpus(&dev->descriptor.bcdUSB); + le16_to_cpus(&dev->descriptor.idVendor); + le16_to_cpus(&dev->descriptor.idProduct); + le16_to_cpus(&dev->descriptor.bcdDevice); + /* only support for one config for now */ + err = usb_get_configuration_no(dev, tmpbuf, 0); + if (err < 0) { + printf("usb_new_device: Cannot read configuration, " \ + "skipping device %04x:%04x\n", + dev->descriptor.idVendor, dev->descriptor.idProduct); + return -1; + } + usb_parse_config(dev, tmpbuf, 0); + usb_set_maxpacket(dev); + /* we set the default configuration here */ + if (usb_set_configuration(dev, dev->config.desc.bConfigurationValue)) { + printf("failed to set default configuration " \ + "len %d, status %lX\n", dev->act_len, dev->status); + return -1; + } + debug("new device strings: Mfr=%d, Product=%d, SerialNumber=%d\n", + dev->descriptor.iManufacturer, dev->descriptor.iProduct, + dev->descriptor.iSerialNumber); + memset(dev->mf, 0, sizeof(dev->mf)); + memset(dev->prod, 0, sizeof(dev->prod)); + memset(dev->serial, 0, sizeof(dev->serial)); + if (dev->descriptor.iManufacturer) + usb_string(dev, dev->descriptor.iManufacturer, + dev->mf, sizeof(dev->mf)); + if (dev->descriptor.iProduct) + usb_string(dev, dev->descriptor.iProduct, + dev->prod, sizeof(dev->prod)); + if (dev->descriptor.iSerialNumber) + usb_string(dev, dev->descriptor.iSerialNumber, + dev->serial, sizeof(dev->serial)); + debug("Manufacturer %s\n", dev->mf); + debug("Product %s\n", dev->prod); + debug("SerialNumber %s\n", dev->serial); + /* now prode if the device is a hub */ + usb_hub_probe(dev, 0); + return 0; +} + +__weak +int board_usb_init(int index, enum usb_init_type init) +{ + return 0; +} +/* EOF */ diff --git a/qemu/roms/u-boot/common/usb_hub.c b/qemu/roms/u-boot/common/usb_hub.c new file mode 100644 index 000000000..ffac0e743 --- /dev/null +++ b/qemu/roms/u-boot/common/usb_hub.c @@ -0,0 +1,550 @@ +/* + * Most of this source has been derived from the Linux USB + * project: + * (C) Copyright Linus Torvalds 1999 + * (C) Copyright Johannes Erdfelt 1999-2001 + * (C) Copyright Andreas Gal 1999 + * (C) Copyright Gregory P. Smith 1999 + * (C) Copyright Deti Fliegl 1999 (new USB architecture) + * (C) Copyright Randy Dunlap 2000 + * (C) Copyright David Brownell 2000 (kernel hotplug, usb_device_id) + * (C) Copyright Yggdrasil Computing, Inc. 2000 + * (usb_device_id matching changes by Adam J. Richter) + * + * Adapted for U-Boot: + * (C) Copyright 2001 Denis Peter, MPL AG Switzerland + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/**************************************************************************** + * HUB "Driver" + * Probes device for being a hub and configurate it + */ + +#include <common.h> +#include <command.h> +#include <asm/processor.h> +#include <asm/unaligned.h> +#include <linux/ctype.h> +#include <asm/byteorder.h> +#include <asm/unaligned.h> + +#include <usb.h> +#ifdef CONFIG_4xx +#include <asm/4xx_pci.h> +#endif + +#ifndef CONFIG_USB_HUB_MIN_POWER_ON_DELAY +#define CONFIG_USB_HUB_MIN_POWER_ON_DELAY 100 +#endif + +#define USB_BUFSIZ 512 + +static struct usb_hub_device hub_dev[USB_MAX_HUB]; +static int usb_hub_index; + +__weak void usb_hub_reset_devices(int port) +{ + return; +} + +static int usb_get_hub_descriptor(struct usb_device *dev, void *data, int size) +{ + return usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), + USB_REQ_GET_DESCRIPTOR, USB_DIR_IN | USB_RT_HUB, + USB_DT_HUB << 8, 0, data, size, USB_CNTL_TIMEOUT); +} + +static int usb_clear_port_feature(struct usb_device *dev, int port, int feature) +{ + return usb_control_msg(dev, usb_sndctrlpipe(dev, 0), + USB_REQ_CLEAR_FEATURE, USB_RT_PORT, feature, + port, NULL, 0, USB_CNTL_TIMEOUT); +} + +static int usb_set_port_feature(struct usb_device *dev, int port, int feature) +{ + return usb_control_msg(dev, usb_sndctrlpipe(dev, 0), + USB_REQ_SET_FEATURE, USB_RT_PORT, feature, + port, NULL, 0, USB_CNTL_TIMEOUT); +} + +static int usb_get_hub_status(struct usb_device *dev, void *data) +{ + return usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), + USB_REQ_GET_STATUS, USB_DIR_IN | USB_RT_HUB, 0, 0, + data, sizeof(struct usb_hub_status), USB_CNTL_TIMEOUT); +} + +static int usb_get_port_status(struct usb_device *dev, int port, void *data) +{ + return usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), + USB_REQ_GET_STATUS, USB_DIR_IN | USB_RT_PORT, 0, port, + data, sizeof(struct usb_hub_status), USB_CNTL_TIMEOUT); +} + + +static void usb_hub_power_on(struct usb_hub_device *hub) +{ + int i; + struct usb_device *dev; + unsigned pgood_delay = hub->desc.bPwrOn2PwrGood * 2; + ALLOC_CACHE_ALIGN_BUFFER(struct usb_port_status, portsts, 1); + unsigned short portstatus; + int ret; + + dev = hub->pusb_dev; + + /* + * Enable power to the ports: + * Here we Power-cycle the ports: aka, + * turning them off and turning on again. + */ + debug("enabling power on all ports\n"); + for (i = 0; i < dev->maxchild; i++) { + usb_clear_port_feature(dev, i + 1, USB_PORT_FEAT_POWER); + debug("port %d returns %lX\n", i + 1, dev->status); + } + + /* Wait at least 2*bPwrOn2PwrGood for PP to change */ + mdelay(pgood_delay); + + for (i = 0; i < dev->maxchild; i++) { + ret = usb_get_port_status(dev, i + 1, portsts); + if (ret < 0) { + debug("port %d: get_port_status failed\n", i + 1); + continue; + } + + /* + * Check to confirm the state of Port Power: + * xHCI says "After modifying PP, s/w shall read + * PP and confirm that it has reached the desired state + * before modifying it again, undefined behavior may occur + * if this procedure is not followed". + * EHCI doesn't say anything like this, but no harm in keeping + * this. + */ + portstatus = le16_to_cpu(portsts->wPortStatus); + if (portstatus & (USB_PORT_STAT_POWER << 1)) { + debug("port %d: Port power change failed\n", i + 1); + continue; + } + } + + for (i = 0; i < dev->maxchild; i++) { + usb_set_port_feature(dev, i + 1, USB_PORT_FEAT_POWER); + debug("port %d returns %lX\n", i + 1, dev->status); + } + + /* Wait for power to become stable */ + mdelay(max(pgood_delay, CONFIG_USB_HUB_MIN_POWER_ON_DELAY)); +} + +void usb_hub_reset(void) +{ + usb_hub_index = 0; +} + +static struct usb_hub_device *usb_hub_allocate(void) +{ + if (usb_hub_index < USB_MAX_HUB) + return &hub_dev[usb_hub_index++]; + + printf("ERROR: USB_MAX_HUB (%d) reached\n", USB_MAX_HUB); + return NULL; +} + +#define MAX_TRIES 5 + +static inline char *portspeed(int portstatus) +{ + char *speed_str; + + switch (portstatus & USB_PORT_STAT_SPEED_MASK) { + case USB_PORT_STAT_SUPER_SPEED: + speed_str = "5 Gb/s"; + break; + case USB_PORT_STAT_HIGH_SPEED: + speed_str = "480 Mb/s"; + break; + case USB_PORT_STAT_LOW_SPEED: + speed_str = "1.5 Mb/s"; + break; + default: + speed_str = "12 Mb/s"; + break; + } + + return speed_str; +} + +int hub_port_reset(struct usb_device *dev, int port, + unsigned short *portstat) +{ + int tries; + ALLOC_CACHE_ALIGN_BUFFER(struct usb_port_status, portsts, 1); + unsigned short portstatus, portchange; + + debug("hub_port_reset: resetting port %d...\n", port); + for (tries = 0; tries < MAX_TRIES; tries++) { + + usb_set_port_feature(dev, port + 1, USB_PORT_FEAT_RESET); + mdelay(200); + + if (usb_get_port_status(dev, port + 1, portsts) < 0) { + debug("get_port_status failed status %lX\n", + dev->status); + return -1; + } + portstatus = le16_to_cpu(portsts->wPortStatus); + portchange = le16_to_cpu(portsts->wPortChange); + + debug("portstatus %x, change %x, %s\n", portstatus, portchange, + portspeed(portstatus)); + + debug("STAT_C_CONNECTION = %d STAT_CONNECTION = %d" \ + " USB_PORT_STAT_ENABLE %d\n", + (portchange & USB_PORT_STAT_C_CONNECTION) ? 1 : 0, + (portstatus & USB_PORT_STAT_CONNECTION) ? 1 : 0, + (portstatus & USB_PORT_STAT_ENABLE) ? 1 : 0); + + if ((portchange & USB_PORT_STAT_C_CONNECTION) || + !(portstatus & USB_PORT_STAT_CONNECTION)) + return -1; + + if (portstatus & USB_PORT_STAT_ENABLE) + break; + + mdelay(200); + } + + if (tries == MAX_TRIES) { + debug("Cannot enable port %i after %i retries, " \ + "disabling port.\n", port + 1, MAX_TRIES); + debug("Maybe the USB cable is bad?\n"); + return -1; + } + + usb_clear_port_feature(dev, port + 1, USB_PORT_FEAT_C_RESET); + *portstat = portstatus; + return 0; +} + + +void usb_hub_port_connect_change(struct usb_device *dev, int port) +{ + struct usb_device *usb; + ALLOC_CACHE_ALIGN_BUFFER(struct usb_port_status, portsts, 1); + unsigned short portstatus; + + /* Check status */ + if (usb_get_port_status(dev, port + 1, portsts) < 0) { + debug("get_port_status failed\n"); + return; + } + + portstatus = le16_to_cpu(portsts->wPortStatus); + debug("portstatus %x, change %x, %s\n", + portstatus, + le16_to_cpu(portsts->wPortChange), + portspeed(portstatus)); + + /* Clear the connection change status */ + usb_clear_port_feature(dev, port + 1, USB_PORT_FEAT_C_CONNECTION); + + /* Disconnect any existing devices under this port */ + if (((!(portstatus & USB_PORT_STAT_CONNECTION)) && + (!(portstatus & USB_PORT_STAT_ENABLE))) || (dev->children[port])) { + debug("usb_disconnect(&hub->children[port]);\n"); + /* Return now if nothing is connected */ + if (!(portstatus & USB_PORT_STAT_CONNECTION)) + return; + } + mdelay(200); + + /* Reset the port */ + if (hub_port_reset(dev, port, &portstatus) < 0) { + printf("cannot reset port %i!?\n", port + 1); + return; + } + + mdelay(200); + + /* Allocate a new device struct for it */ + usb = usb_alloc_new_device(dev->controller); + + switch (portstatus & USB_PORT_STAT_SPEED_MASK) { + case USB_PORT_STAT_SUPER_SPEED: + usb->speed = USB_SPEED_SUPER; + break; + case USB_PORT_STAT_HIGH_SPEED: + usb->speed = USB_SPEED_HIGH; + break; + case USB_PORT_STAT_LOW_SPEED: + usb->speed = USB_SPEED_LOW; + break; + default: + usb->speed = USB_SPEED_FULL; + break; + } + + dev->children[port] = usb; + usb->parent = dev; + usb->portnr = port + 1; + /* Run it through the hoops (find a driver, etc) */ + if (usb_new_device(usb)) { + /* Woops, disable the port */ + usb_free_device(); + dev->children[port] = NULL; + debug("hub: disabling port %d\n", port + 1); + usb_clear_port_feature(dev, port + 1, USB_PORT_FEAT_ENABLE); + } +} + + +static int usb_hub_configure(struct usb_device *dev) +{ + int i, length; + ALLOC_CACHE_ALIGN_BUFFER(unsigned char, buffer, USB_BUFSIZ); + unsigned char *bitmap; + short hubCharacteristics; + struct usb_hub_descriptor *descriptor; + struct usb_hub_device *hub; + __maybe_unused struct usb_hub_status *hubsts; + + /* "allocate" Hub device */ + hub = usb_hub_allocate(); + if (hub == NULL) + return -1; + hub->pusb_dev = dev; + /* Get the the hub descriptor */ + if (usb_get_hub_descriptor(dev, buffer, 4) < 0) { + debug("usb_hub_configure: failed to get hub " \ + "descriptor, giving up %lX\n", dev->status); + return -1; + } + descriptor = (struct usb_hub_descriptor *)buffer; + + length = min(descriptor->bLength, sizeof(struct usb_hub_descriptor)); + + if (usb_get_hub_descriptor(dev, buffer, length) < 0) { + debug("usb_hub_configure: failed to get hub " \ + "descriptor 2nd giving up %lX\n", dev->status); + return -1; + } + memcpy((unsigned char *)&hub->desc, buffer, length); + /* adjust 16bit values */ + put_unaligned(le16_to_cpu(get_unaligned( + &descriptor->wHubCharacteristics)), + &hub->desc.wHubCharacteristics); + /* set the bitmap */ + bitmap = (unsigned char *)&hub->desc.DeviceRemovable[0]; + /* devices not removable by default */ + memset(bitmap, 0xff, (USB_MAXCHILDREN+1+7)/8); + bitmap = (unsigned char *)&hub->desc.PortPowerCtrlMask[0]; + memset(bitmap, 0xff, (USB_MAXCHILDREN+1+7)/8); /* PowerMask = 1B */ + + for (i = 0; i < ((hub->desc.bNbrPorts + 1 + 7)/8); i++) + hub->desc.DeviceRemovable[i] = descriptor->DeviceRemovable[i]; + + for (i = 0; i < ((hub->desc.bNbrPorts + 1 + 7)/8); i++) + hub->desc.PortPowerCtrlMask[i] = descriptor->PortPowerCtrlMask[i]; + + dev->maxchild = descriptor->bNbrPorts; + debug("%d ports detected\n", dev->maxchild); + + hubCharacteristics = get_unaligned(&hub->desc.wHubCharacteristics); + switch (hubCharacteristics & HUB_CHAR_LPSM) { + case 0x00: + debug("ganged power switching\n"); + break; + case 0x01: + debug("individual port power switching\n"); + break; + case 0x02: + case 0x03: + debug("unknown reserved power switching mode\n"); + break; + } + + if (hubCharacteristics & HUB_CHAR_COMPOUND) + debug("part of a compound device\n"); + else + debug("standalone hub\n"); + + switch (hubCharacteristics & HUB_CHAR_OCPM) { + case 0x00: + debug("global over-current protection\n"); + break; + case 0x08: + debug("individual port over-current protection\n"); + break; + case 0x10: + case 0x18: + debug("no over-current protection\n"); + break; + } + + debug("power on to power good time: %dms\n", + descriptor->bPwrOn2PwrGood * 2); + debug("hub controller current requirement: %dmA\n", + descriptor->bHubContrCurrent); + + for (i = 0; i < dev->maxchild; i++) + debug("port %d is%s removable\n", i + 1, + hub->desc.DeviceRemovable[(i + 1) / 8] & \ + (1 << ((i + 1) % 8)) ? " not" : ""); + + if (sizeof(struct usb_hub_status) > USB_BUFSIZ) { + debug("usb_hub_configure: failed to get Status - " \ + "too long: %d\n", descriptor->bLength); + return -1; + } + + if (usb_get_hub_status(dev, buffer) < 0) { + debug("usb_hub_configure: failed to get Status %lX\n", + dev->status); + return -1; + } + +#ifdef DEBUG + hubsts = (struct usb_hub_status *)buffer; +#endif + + debug("get_hub_status returned status %X, change %X\n", + le16_to_cpu(hubsts->wHubStatus), + le16_to_cpu(hubsts->wHubChange)); + debug("local power source is %s\n", + (le16_to_cpu(hubsts->wHubStatus) & HUB_STATUS_LOCAL_POWER) ? \ + "lost (inactive)" : "good"); + debug("%sover-current condition exists\n", + (le16_to_cpu(hubsts->wHubStatus) & HUB_STATUS_OVERCURRENT) ? \ + "" : "no "); + usb_hub_power_on(hub); + + /* + * Reset any devices that may be in a bad state when applying + * the power. This is a __weak function. Resetting of the devices + * should occur in the board file of the device. + */ + for (i = 0; i < dev->maxchild; i++) + usb_hub_reset_devices(i + 1); + + for (i = 0; i < dev->maxchild; i++) { + ALLOC_CACHE_ALIGN_BUFFER(struct usb_port_status, portsts, 1); + unsigned short portstatus, portchange; + int ret; + ulong start = get_timer(0); + + /* + * Wait for (whichever finishes first) + * - A maximum of 10 seconds + * This is a purely observational value driven by connecting + * a few broken pen drives and taking the max * 1.5 approach + * - connection_change and connection state to report same + * state + */ + do { + ret = usb_get_port_status(dev, i + 1, portsts); + if (ret < 0) { + debug("get_port_status failed\n"); + break; + } + + portstatus = le16_to_cpu(portsts->wPortStatus); + portchange = le16_to_cpu(portsts->wPortChange); + + if ((portchange & USB_PORT_STAT_C_CONNECTION) == + (portstatus & USB_PORT_STAT_CONNECTION)) + break; + + } while (get_timer(start) < CONFIG_SYS_HZ * 10); + + if (ret < 0) + continue; + + debug("Port %d Status %X Change %X\n", + i + 1, portstatus, portchange); + + if (portchange & USB_PORT_STAT_C_CONNECTION) { + debug("port %d connection change\n", i + 1); + usb_hub_port_connect_change(dev, i); + } + if (portchange & USB_PORT_STAT_C_ENABLE) { + debug("port %d enable change, status %x\n", + i + 1, portstatus); + usb_clear_port_feature(dev, i + 1, + USB_PORT_FEAT_C_ENABLE); + /* + * The following hack causes a ghost device problem + * to Faraday EHCI + */ +#ifndef CONFIG_USB_EHCI_FARADAY + /* EM interference sometimes causes bad shielded USB + * devices to be shutdown by the hub, this hack enables + * them again. Works at least with mouse driver */ + if (!(portstatus & USB_PORT_STAT_ENABLE) && + (portstatus & USB_PORT_STAT_CONNECTION) && + ((dev->children[i]))) { + debug("already running port %i " \ + "disabled by hub (EMI?), " \ + "re-enabling...\n", i + 1); + usb_hub_port_connect_change(dev, i); + } +#endif + } + if (portstatus & USB_PORT_STAT_SUSPEND) { + debug("port %d suspend change\n", i + 1); + usb_clear_port_feature(dev, i + 1, + USB_PORT_FEAT_SUSPEND); + } + + if (portchange & USB_PORT_STAT_C_OVERCURRENT) { + debug("port %d over-current change\n", i + 1); + usb_clear_port_feature(dev, i + 1, + USB_PORT_FEAT_C_OVER_CURRENT); + usb_hub_power_on(hub); + } + + if (portchange & USB_PORT_STAT_C_RESET) { + debug("port %d reset change\n", i + 1); + usb_clear_port_feature(dev, i + 1, + USB_PORT_FEAT_C_RESET); + } + } /* end for i all ports */ + + return 0; +} + +int usb_hub_probe(struct usb_device *dev, int ifnum) +{ + struct usb_interface *iface; + struct usb_endpoint_descriptor *ep; + int ret; + + iface = &dev->config.if_desc[ifnum]; + /* Is it a hub? */ + if (iface->desc.bInterfaceClass != USB_CLASS_HUB) + return 0; + /* Some hubs have a subclass of 1, which AFAICT according to the */ + /* specs is not defined, but it works */ + if ((iface->desc.bInterfaceSubClass != 0) && + (iface->desc.bInterfaceSubClass != 1)) + return 0; + /* Multiple endpoints? What kind of mutant ninja-hub is this? */ + if (iface->desc.bNumEndpoints != 1) + return 0; + ep = &iface->ep_desc[0]; + /* Output endpoint? Curiousier and curiousier.. */ + if (!(ep->bEndpointAddress & USB_DIR_IN)) + return 0; + /* If it's not an interrupt endpoint, we'd better punt! */ + if ((ep->bmAttributes & 3) != 3) + return 0; + /* We found a hub */ + debug("USB hub found\n"); + ret = usb_hub_configure(dev); + return ret; +} diff --git a/qemu/roms/u-boot/common/usb_kbd.c b/qemu/roms/u-boot/common/usb_kbd.c new file mode 100644 index 000000000..0b77c16c5 --- /dev/null +++ b/qemu/roms/u-boot/common/usb_kbd.c @@ -0,0 +1,567 @@ +/* + * (C) Copyright 2001 + * Denis Peter, MPL AG Switzerland + * + * Part of this source has been derived from the Linux USB + * project. + * + * SPDX-License-Identifier: GPL-2.0+ + */ +#include <common.h> +#include <malloc.h> +#include <stdio_dev.h> +#include <asm/byteorder.h> + +#include <usb.h> + +/* + * If overwrite_console returns 1, the stdin, stderr and stdout + * are switched to the serial port, else the settings in the + * environment are used + */ +#ifdef CONFIG_SYS_CONSOLE_OVERWRITE_ROUTINE +extern int overwrite_console(void); +#else +int overwrite_console(void) +{ + return 0; +} +#endif + +/* Keyboard sampling rate */ +#define REPEAT_RATE (40 / 4) /* 40msec -> 25cps */ +#define REPEAT_DELAY 10 /* 10 x REPEAT_RATE = 400msec */ + +#define NUM_LOCK 0x53 +#define CAPS_LOCK 0x39 +#define SCROLL_LOCK 0x47 + +/* Modifier bits */ +#define LEFT_CNTR (1 << 0) +#define LEFT_SHIFT (1 << 1) +#define LEFT_ALT (1 << 2) +#define LEFT_GUI (1 << 3) +#define RIGHT_CNTR (1 << 4) +#define RIGHT_SHIFT (1 << 5) +#define RIGHT_ALT (1 << 6) +#define RIGHT_GUI (1 << 7) + +/* Size of the keyboard buffer */ +#define USB_KBD_BUFFER_LEN 0x20 + +/* Device name */ +#define DEVNAME "usbkbd" + +/* Keyboard maps */ +static const unsigned char usb_kbd_numkey[] = { + '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', + '\r', 0x1b, '\b', '\t', ' ', '-', '=', '[', ']', + '\\', '#', ';', '\'', '`', ',', '.', '/' +}; +static const unsigned char usb_kbd_numkey_shifted[] = { + '!', '@', '#', '$', '%', '^', '&', '*', '(', ')', + '\r', 0x1b, '\b', '\t', ' ', '_', '+', '{', '}', + '|', '~', ':', '"', '~', '<', '>', '?' +}; + +static const unsigned char usb_kbd_num_keypad[] = { + '/', '*', '-', '+', '\r', + '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', + '.', 0, 0, 0, '=' +}; + +/* + * map arrow keys to ^F/^B ^N/^P, can't really use the proper + * ANSI sequence for arrow keys because the queuing code breaks + * when a single keypress expands to 3 queue elements + */ +static const unsigned char usb_kbd_arrow[] = { + 0x6, 0x2, 0xe, 0x10 +}; + +/* + * NOTE: It's important for the NUM, CAPS, SCROLL-lock bits to be in this + * order. See usb_kbd_setled() function! + */ +#define USB_KBD_NUMLOCK (1 << 0) +#define USB_KBD_CAPSLOCK (1 << 1) +#define USB_KBD_SCROLLLOCK (1 << 2) +#define USB_KBD_CTRL (1 << 3) + +#define USB_KBD_LEDMASK \ + (USB_KBD_NUMLOCK | USB_KBD_CAPSLOCK | USB_KBD_SCROLLLOCK) + +/* + * USB Keyboard reports are 8 bytes in boot protocol. + * Appendix B of HID Device Class Definition 1.11 + */ +#define USB_KBD_BOOT_REPORT_SIZE 8 + +struct usb_kbd_pdata { + uint32_t repeat_delay; + + uint32_t usb_in_pointer; + uint32_t usb_out_pointer; + uint8_t usb_kbd_buffer[USB_KBD_BUFFER_LEN]; + + uint8_t *new; + uint8_t old[USB_KBD_BOOT_REPORT_SIZE]; + + uint8_t flags; +}; + +extern int __maybe_unused net_busy_flag; + +/* The period of time between two calls of usb_kbd_testc(). */ +static unsigned long __maybe_unused kbd_testc_tms; + +/* Generic keyboard event polling. */ +void usb_kbd_generic_poll(void) +{ + struct stdio_dev *dev; + struct usb_device *usb_kbd_dev; + struct usb_kbd_pdata *data; + struct usb_interface *iface; + struct usb_endpoint_descriptor *ep; + int pipe; + int maxp; + + /* Get the pointer to USB Keyboard device pointer */ + dev = stdio_get_by_name(DEVNAME); + usb_kbd_dev = (struct usb_device *)dev->priv; + data = usb_kbd_dev->privptr; + iface = &usb_kbd_dev->config.if_desc[0]; + ep = &iface->ep_desc[0]; + pipe = usb_rcvintpipe(usb_kbd_dev, ep->bEndpointAddress); + + /* Submit a interrupt transfer request */ + maxp = usb_maxpacket(usb_kbd_dev, pipe); + usb_submit_int_msg(usb_kbd_dev, pipe, data->new, + min(maxp, USB_KBD_BOOT_REPORT_SIZE), + ep->bInterval); +} + +/* Puts character in the queue and sets up the in and out pointer. */ +static void usb_kbd_put_queue(struct usb_kbd_pdata *data, char c) +{ + if (data->usb_in_pointer == USB_KBD_BUFFER_LEN - 1) { + /* Check for buffer full. */ + if (data->usb_out_pointer == 0) + return; + + data->usb_in_pointer = 0; + } else { + /* Check for buffer full. */ + if (data->usb_in_pointer == data->usb_out_pointer - 1) + return; + + data->usb_in_pointer++; + } + + data->usb_kbd_buffer[data->usb_in_pointer] = c; +} + +/* + * Set the LEDs. Since this is used in the irq routine, the control job is + * issued with a timeout of 0. This means, that the job is queued without + * waiting for job completion. + */ +static void usb_kbd_setled(struct usb_device *dev) +{ + struct usb_interface *iface = &dev->config.if_desc[0]; + struct usb_kbd_pdata *data = dev->privptr; + uint32_t leds = data->flags & USB_KBD_LEDMASK; + + usb_control_msg(dev, usb_sndctrlpipe(dev, 0), + USB_REQ_SET_REPORT, USB_TYPE_CLASS | USB_RECIP_INTERFACE, + 0x200, iface->desc.bInterfaceNumber, (void *)&leds, 1, 0); +} + +#define CAPITAL_MASK 0x20 +/* Translate the scancode in ASCII */ +static int usb_kbd_translate(struct usb_kbd_pdata *data, unsigned char scancode, + unsigned char modifier, int pressed) +{ + uint8_t keycode = 0; + + /* Key released */ + if (pressed == 0) { + data->repeat_delay = 0; + return 0; + } + + if (pressed == 2) { + data->repeat_delay++; + if (data->repeat_delay < REPEAT_DELAY) + return 0; + + data->repeat_delay = REPEAT_DELAY; + } + + /* Alphanumeric values */ + if ((scancode > 3) && (scancode <= 0x1d)) { + keycode = scancode - 4 + 'a'; + + if (data->flags & USB_KBD_CAPSLOCK) + keycode &= ~CAPITAL_MASK; + + if (modifier & (LEFT_SHIFT | RIGHT_SHIFT)) { + /* Handle CAPSLock + Shift pressed simultaneously */ + if (keycode & CAPITAL_MASK) + keycode &= ~CAPITAL_MASK; + else + keycode |= CAPITAL_MASK; + } + } + + if ((scancode > 0x1d) && (scancode < 0x3a)) { + /* Shift pressed */ + if (modifier & (LEFT_SHIFT | RIGHT_SHIFT)) + keycode = usb_kbd_numkey_shifted[scancode - 0x1e]; + else + keycode = usb_kbd_numkey[scancode - 0x1e]; + } + + /* Arrow keys */ + if ((scancode >= 0x4f) && (scancode <= 0x52)) + keycode = usb_kbd_arrow[scancode - 0x4f]; + + /* Numeric keypad */ + if ((scancode >= 0x54) && (scancode <= 0x67)) + keycode = usb_kbd_num_keypad[scancode - 0x54]; + + if (data->flags & USB_KBD_CTRL) + keycode = scancode - 0x3; + + if (pressed == 1) { + if (scancode == NUM_LOCK) { + data->flags ^= USB_KBD_NUMLOCK; + return 1; + } + + if (scancode == CAPS_LOCK) { + data->flags ^= USB_KBD_CAPSLOCK; + return 1; + } + if (scancode == SCROLL_LOCK) { + data->flags ^= USB_KBD_SCROLLLOCK; + return 1; + } + } + + /* Report keycode if any */ + if (keycode) { + debug("%c", keycode); + usb_kbd_put_queue(data, keycode); + } + + return 0; +} + +static uint32_t usb_kbd_service_key(struct usb_device *dev, int i, int up) +{ + uint32_t res = 0; + struct usb_kbd_pdata *data = dev->privptr; + uint8_t *new; + uint8_t *old; + + if (up) { + new = data->old; + old = data->new; + } else { + new = data->new; + old = data->old; + } + + if ((old[i] > 3) && + (memscan(new + 2, old[i], USB_KBD_BOOT_REPORT_SIZE - 2) == + new + USB_KBD_BOOT_REPORT_SIZE)) { + res |= usb_kbd_translate(data, old[i], data->new[0], up); + } + + return res; +} + +/* Interrupt service routine */ +static int usb_kbd_irq_worker(struct usb_device *dev) +{ + struct usb_kbd_pdata *data = dev->privptr; + int i, res = 0; + + /* No combo key pressed */ + if (data->new[0] == 0x00) + data->flags &= ~USB_KBD_CTRL; + /* Left or Right Ctrl pressed */ + else if ((data->new[0] == LEFT_CNTR) || (data->new[0] == RIGHT_CNTR)) + data->flags |= USB_KBD_CTRL; + + for (i = 2; i < USB_KBD_BOOT_REPORT_SIZE; i++) { + res |= usb_kbd_service_key(dev, i, 0); + res |= usb_kbd_service_key(dev, i, 1); + } + + /* Key is still pressed */ + if ((data->new[2] > 3) && (data->old[2] == data->new[2])) + res |= usb_kbd_translate(data, data->new[2], data->new[0], 2); + + if (res == 1) + usb_kbd_setled(dev); + + memcpy(data->old, data->new, USB_KBD_BOOT_REPORT_SIZE); + + return 1; +} + +/* Keyboard interrupt handler */ +static int usb_kbd_irq(struct usb_device *dev) +{ + if ((dev->irq_status != 0) || + (dev->irq_act_len != USB_KBD_BOOT_REPORT_SIZE)) { + debug("USB KBD: Error %lX, len %d\n", + dev->irq_status, dev->irq_act_len); + return 1; + } + + return usb_kbd_irq_worker(dev); +} + +/* Interrupt polling */ +static inline void usb_kbd_poll_for_event(struct usb_device *dev) +{ +#if defined(CONFIG_SYS_USB_EVENT_POLL) + struct usb_interface *iface; + struct usb_endpoint_descriptor *ep; + struct usb_kbd_pdata *data; + int pipe; + int maxp; + + /* Get the pointer to USB Keyboard device pointer */ + data = dev->privptr; + iface = &dev->config.if_desc[0]; + ep = &iface->ep_desc[0]; + pipe = usb_rcvintpipe(dev, ep->bEndpointAddress); + + /* Submit a interrupt transfer request */ + maxp = usb_maxpacket(dev, pipe); + usb_submit_int_msg(dev, pipe, &data->new[0], + min(maxp, USB_KBD_BOOT_REPORT_SIZE), + ep->bInterval); + + usb_kbd_irq_worker(dev); +#elif defined(CONFIG_SYS_USB_EVENT_POLL_VIA_CONTROL_EP) + struct usb_interface *iface; + struct usb_kbd_pdata *data = dev->privptr; + iface = &dev->config.if_desc[0]; + usb_get_report(dev, iface->desc.bInterfaceNumber, + 1, 0, data->new, USB_KBD_BOOT_REPORT_SIZE); + if (memcmp(data->old, data->new, USB_KBD_BOOT_REPORT_SIZE)) + usb_kbd_irq_worker(dev); +#endif +} + +/* test if a character is in the queue */ +static int usb_kbd_testc(void) +{ + struct stdio_dev *dev; + struct usb_device *usb_kbd_dev; + struct usb_kbd_pdata *data; + +#ifdef CONFIG_CMD_NET + /* + * If net_busy_flag is 1, NET transfer is running, + * then we check key-pressed every second (first check may be + * less than 1 second) to improve TFTP booting performance. + */ + if (net_busy_flag && (get_timer(kbd_testc_tms) < CONFIG_SYS_HZ)) + return 0; + kbd_testc_tms = get_timer(0); +#endif + dev = stdio_get_by_name(DEVNAME); + usb_kbd_dev = (struct usb_device *)dev->priv; + data = usb_kbd_dev->privptr; + + usb_kbd_poll_for_event(usb_kbd_dev); + + return !(data->usb_in_pointer == data->usb_out_pointer); +} + +/* gets the character from the queue */ +static int usb_kbd_getc(void) +{ + struct stdio_dev *dev; + struct usb_device *usb_kbd_dev; + struct usb_kbd_pdata *data; + + dev = stdio_get_by_name(DEVNAME); + usb_kbd_dev = (struct usb_device *)dev->priv; + data = usb_kbd_dev->privptr; + + while (data->usb_in_pointer == data->usb_out_pointer) + usb_kbd_poll_for_event(usb_kbd_dev); + + if (data->usb_out_pointer == USB_KBD_BUFFER_LEN - 1) + data->usb_out_pointer = 0; + else + data->usb_out_pointer++; + + return data->usb_kbd_buffer[data->usb_out_pointer]; +} + +/* probes the USB device dev for keyboard type. */ +static int usb_kbd_probe(struct usb_device *dev, unsigned int ifnum) +{ + struct usb_interface *iface; + struct usb_endpoint_descriptor *ep; + struct usb_kbd_pdata *data; + int pipe, maxp; + + if (dev->descriptor.bNumConfigurations != 1) + return 0; + + iface = &dev->config.if_desc[ifnum]; + + if (iface->desc.bInterfaceClass != 3) + return 0; + + if (iface->desc.bInterfaceSubClass != 1) + return 0; + + if (iface->desc.bInterfaceProtocol != 1) + return 0; + + if (iface->desc.bNumEndpoints != 1) + return 0; + + ep = &iface->ep_desc[0]; + + /* Check if endpoint 1 is interrupt endpoint */ + if (!(ep->bEndpointAddress & 0x80)) + return 0; + + if ((ep->bmAttributes & 3) != 3) + return 0; + + debug("USB KBD: found set protocol...\n"); + + data = malloc(sizeof(struct usb_kbd_pdata)); + if (!data) { + printf("USB KBD: Error allocating private data\n"); + return 0; + } + + /* Clear private data */ + memset(data, 0, sizeof(struct usb_kbd_pdata)); + + /* allocate input buffer aligned and sized to USB DMA alignment */ + data->new = memalign(USB_DMA_MINALIGN, + roundup(USB_KBD_BOOT_REPORT_SIZE, USB_DMA_MINALIGN)); + + /* Insert private data into USB device structure */ + dev->privptr = data; + + /* Set IRQ handler */ + dev->irq_handle = usb_kbd_irq; + + pipe = usb_rcvintpipe(dev, ep->bEndpointAddress); + maxp = usb_maxpacket(dev, pipe); + + /* We found a USB Keyboard, install it. */ + usb_set_protocol(dev, iface->desc.bInterfaceNumber, 0); + + debug("USB KBD: found set idle...\n"); + usb_set_idle(dev, iface->desc.bInterfaceNumber, REPEAT_RATE, 0); + + debug("USB KBD: enable interrupt pipe...\n"); + if (usb_submit_int_msg(dev, pipe, data->new, + min(maxp, USB_KBD_BOOT_REPORT_SIZE), + ep->bInterval) < 0) { + printf("Failed to get keyboard state from device %04x:%04x\n", + dev->descriptor.idVendor, dev->descriptor.idProduct); + /* Abort, we don't want to use that non-functional keyboard. */ + return 0; + } + + /* Success. */ + return 1; +} + +/* Search for keyboard and register it if found. */ +int drv_usb_kbd_init(void) +{ + struct stdio_dev usb_kbd_dev, *old_dev; + struct usb_device *dev; + char *stdinname = getenv("stdin"); + int error, i; + + /* Scan all USB Devices */ + for (i = 0; i < USB_MAX_DEVICE; i++) { + /* Get USB device. */ + dev = usb_get_dev_index(i); + if (!dev) + return -1; + + if (dev->devnum == -1) + continue; + + /* Try probing the keyboard */ + if (usb_kbd_probe(dev, 0) != 1) + continue; + + /* We found a keyboard, check if it is already registered. */ + debug("USB KBD: found set up device.\n"); + old_dev = stdio_get_by_name(DEVNAME); + if (old_dev) { + /* Already registered, just return ok. */ + debug("USB KBD: is already registered.\n"); + usb_kbd_deregister(); + return 1; + } + + /* Register the keyboard */ + debug("USB KBD: register.\n"); + memset(&usb_kbd_dev, 0, sizeof(struct stdio_dev)); + strcpy(usb_kbd_dev.name, DEVNAME); + usb_kbd_dev.flags = DEV_FLAGS_INPUT | DEV_FLAGS_SYSTEM; + usb_kbd_dev.putc = NULL; + usb_kbd_dev.puts = NULL; + usb_kbd_dev.getc = usb_kbd_getc; + usb_kbd_dev.tstc = usb_kbd_testc; + usb_kbd_dev.priv = (void *)dev; + error = stdio_register(&usb_kbd_dev); + if (error) + return error; + +#ifdef CONFIG_CONSOLE_MUX + error = iomux_doenv(stdin, stdinname); + if (error) + return error; +#else + /* Check if this is the standard input device. */ + if (strcmp(stdinname, DEVNAME)) + return 1; + + /* Reassign the console */ + if (overwrite_console()) + return 1; + + error = console_assign(stdin, DEVNAME); + if (error) + return error; +#endif + + return 1; + } + + /* No USB Keyboard found */ + return -1; +} + +/* Deregister the keyboard. */ +int usb_kbd_deregister(void) +{ +#ifdef CONFIG_SYS_STDIO_DEREGISTER + return stdio_deregister(DEVNAME); +#else + return 1; +#endif +} diff --git a/qemu/roms/u-boot/common/usb_storage.c b/qemu/roms/u-boot/common/usb_storage.c new file mode 100644 index 000000000..6ac358d5e --- /dev/null +++ b/qemu/roms/u-boot/common/usb_storage.c @@ -0,0 +1,1416 @@ +/* + * Most of this source has been derived from the Linux USB + * project: + * (c) 1999-2002 Matthew Dharm (mdharm-usb@one-eyed-alien.net) + * (c) 2000 David L. Brown, Jr. (usb-storage@davidb.org) + * (c) 1999 Michael Gee (michael@linuxspecific.com) + * (c) 2000 Yggdrasil Computing, Inc. + * + * + * Adapted for U-Boot: + * (C) Copyright 2001 Denis Peter, MPL AG Switzerland + * + * For BBB support (C) Copyright 2003 + * Gary Jennejohn, DENX Software Engineering <garyj@denx.de> + * + * BBB support based on /sys/dev/usb/umass.c from + * FreeBSD. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/* Note: + * Currently only the CBI transport protocoll has been implemented, and it + * is only tested with a TEAC USB Floppy. Other Massstorages with CBI or CB + * transport protocoll may work as well. + */ +/* + * New Note: + * Support for USB Mass Storage Devices (BBB) has been added. It has + * only been tested with USB memory sticks. + */ + + +#include <common.h> +#include <command.h> +#include <asm/byteorder.h> +#include <asm/processor.h> + +#include <part.h> +#include <usb.h> + +#undef BBB_COMDAT_TRACE +#undef BBB_XPORT_TRACE + +#include <scsi.h> +/* direction table -- this indicates the direction of the data + * transfer for each command code -- a 1 indicates input + */ +static const unsigned char us_direction[256/8] = { + 0x28, 0x81, 0x14, 0x14, 0x20, 0x01, 0x90, 0x77, + 0x0C, 0x20, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; +#define US_DIRECTION(x) ((us_direction[x>>3] >> (x & 7)) & 1) + +static ccb usb_ccb __attribute__((aligned(ARCH_DMA_MINALIGN))); + +/* + * CBI style + */ + +#define US_CBI_ADSC 0 + +/* + * BULK only + */ +#define US_BBB_RESET 0xff +#define US_BBB_GET_MAX_LUN 0xfe + +/* Command Block Wrapper */ +typedef struct { + __u32 dCBWSignature; +# define CBWSIGNATURE 0x43425355 + __u32 dCBWTag; + __u32 dCBWDataTransferLength; + __u8 bCBWFlags; +# define CBWFLAGS_OUT 0x00 +# define CBWFLAGS_IN 0x80 + __u8 bCBWLUN; + __u8 bCDBLength; +# define CBWCDBLENGTH 16 + __u8 CBWCDB[CBWCDBLENGTH]; +} umass_bbb_cbw_t; +#define UMASS_BBB_CBW_SIZE 31 +static __u32 CBWTag; + +/* Command Status Wrapper */ +typedef struct { + __u32 dCSWSignature; +# define CSWSIGNATURE 0x53425355 + __u32 dCSWTag; + __u32 dCSWDataResidue; + __u8 bCSWStatus; +# define CSWSTATUS_GOOD 0x0 +# define CSWSTATUS_FAILED 0x1 +# define CSWSTATUS_PHASE 0x2 +} umass_bbb_csw_t; +#define UMASS_BBB_CSW_SIZE 13 + +#define USB_MAX_STOR_DEV 5 +static int usb_max_devs; /* number of highest available usb device */ + +static block_dev_desc_t usb_dev_desc[USB_MAX_STOR_DEV]; + +struct us_data; +typedef int (*trans_cmnd)(ccb *cb, struct us_data *data); +typedef int (*trans_reset)(struct us_data *data); + +struct us_data { + struct usb_device *pusb_dev; /* this usb_device */ + + unsigned int flags; /* from filter initially */ +# define USB_READY (1 << 0) + unsigned char ifnum; /* interface number */ + unsigned char ep_in; /* in endpoint */ + unsigned char ep_out; /* out ....... */ + unsigned char ep_int; /* interrupt . */ + unsigned char subclass; /* as in overview */ + unsigned char protocol; /* .............. */ + unsigned char attention_done; /* force attn on first cmd */ + unsigned short ip_data; /* interrupt data */ + int action; /* what to do */ + int ip_wanted; /* needed */ + int *irq_handle; /* for USB int requests */ + unsigned int irqpipe; /* pipe for release_irq */ + unsigned char irqmaxp; /* max packed for irq Pipe */ + unsigned char irqinterval; /* Intervall for IRQ Pipe */ + ccb *srb; /* current srb */ + trans_reset transport_reset; /* reset routine */ + trans_cmnd transport; /* transport routine */ +}; + +#ifdef CONFIG_USB_EHCI +/* + * The U-Boot EHCI driver can handle any transfer length as long as there is + * enough free heap space left, but the SCSI READ(10) and WRITE(10) commands are + * limited to 65535 blocks. + */ +#define USB_MAX_XFER_BLK 65535 +#else +#define USB_MAX_XFER_BLK 20 +#endif + +static struct us_data usb_stor[USB_MAX_STOR_DEV]; + + +#define USB_STOR_TRANSPORT_GOOD 0 +#define USB_STOR_TRANSPORT_FAILED -1 +#define USB_STOR_TRANSPORT_ERROR -2 + +int usb_stor_get_info(struct usb_device *dev, struct us_data *us, + block_dev_desc_t *dev_desc); +int usb_storage_probe(struct usb_device *dev, unsigned int ifnum, + struct us_data *ss); +unsigned long usb_stor_read(int device, lbaint_t blknr, + lbaint_t blkcnt, void *buffer); +unsigned long usb_stor_write(int device, lbaint_t blknr, + lbaint_t blkcnt, const void *buffer); +struct usb_device * usb_get_dev_index(int index); +void uhci_show_temp_int_td(void); + +#ifdef CONFIG_PARTITIONS +block_dev_desc_t *usb_stor_get_dev(int index) +{ + return (index < usb_max_devs) ? &usb_dev_desc[index] : NULL; +} +#endif + +static void usb_show_progress(void) +{ + debug("."); +} + +/******************************************************************************* + * show info on storage devices; 'usb start/init' must be invoked earlier + * as we only retrieve structures populated during devices initialization + */ +int usb_stor_info(void) +{ + int i; + + if (usb_max_devs > 0) { + for (i = 0; i < usb_max_devs; i++) { + printf(" Device %d: ", i); + dev_print(&usb_dev_desc[i]); + } + return 0; + } + + printf("No storage devices, perhaps not 'usb start'ed..?\n"); + return 1; +} + +static unsigned int usb_get_max_lun(struct us_data *us) +{ + int len; + ALLOC_CACHE_ALIGN_BUFFER(unsigned char, result, 1); + len = usb_control_msg(us->pusb_dev, + usb_rcvctrlpipe(us->pusb_dev, 0), + US_BBB_GET_MAX_LUN, + USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN, + 0, us->ifnum, + result, sizeof(char), + USB_CNTL_TIMEOUT * 5); + debug("Get Max LUN -> len = %i, result = %i\n", len, (int) *result); + return (len > 0) ? *result : 0; +} + +/******************************************************************************* + * scan the usb and reports device info + * to the user if mode = 1 + * returns current device or -1 if no + */ +int usb_stor_scan(int mode) +{ + unsigned char i; + struct usb_device *dev; + + if (mode == 1) + printf(" scanning usb for storage devices... "); + + usb_disable_asynch(1); /* asynch transfer not allowed */ + + for (i = 0; i < USB_MAX_STOR_DEV; i++) { + memset(&usb_dev_desc[i], 0, sizeof(block_dev_desc_t)); + usb_dev_desc[i].if_type = IF_TYPE_USB; + usb_dev_desc[i].dev = i; + usb_dev_desc[i].part_type = PART_TYPE_UNKNOWN; + usb_dev_desc[i].target = 0xff; + usb_dev_desc[i].type = DEV_TYPE_UNKNOWN; + usb_dev_desc[i].block_read = usb_stor_read; + usb_dev_desc[i].block_write = usb_stor_write; + } + + usb_max_devs = 0; + for (i = 0; i < USB_MAX_DEVICE; i++) { + dev = usb_get_dev_index(i); /* get device */ + debug("i=%d\n", i); + if (dev == NULL) + break; /* no more devices available */ + + if (usb_storage_probe(dev, 0, &usb_stor[usb_max_devs])) { + /* OK, it's a storage device. Iterate over its LUNs + * and populate `usb_dev_desc'. + */ + int lun, max_lun, start = usb_max_devs; + + max_lun = usb_get_max_lun(&usb_stor[usb_max_devs]); + for (lun = 0; + lun <= max_lun && usb_max_devs < USB_MAX_STOR_DEV; + lun++) { + usb_dev_desc[usb_max_devs].lun = lun; + if (usb_stor_get_info(dev, &usb_stor[start], + &usb_dev_desc[usb_max_devs]) == 1) { + usb_max_devs++; + } + } + } + /* if storage device */ + if (usb_max_devs == USB_MAX_STOR_DEV) { + printf("max USB Storage Device reached: %d stopping\n", + usb_max_devs); + break; + } + } /* for */ + + usb_disable_asynch(0); /* asynch transfer allowed */ + printf("%d Storage Device(s) found\n", usb_max_devs); + if (usb_max_devs > 0) + return 0; + return -1; +} + +static int usb_stor_irq(struct usb_device *dev) +{ + struct us_data *us; + us = (struct us_data *)dev->privptr; + + if (us->ip_wanted) + us->ip_wanted = 0; + return 0; +} + + +#ifdef DEBUG + +static void usb_show_srb(ccb *pccb) +{ + int i; + printf("SRB: len %d datalen 0x%lX\n ", pccb->cmdlen, pccb->datalen); + for (i = 0; i < 12; i++) + printf("%02X ", pccb->cmd[i]); + printf("\n"); +} + +static void display_int_status(unsigned long tmp) +{ + printf("Status: %s %s %s %s %s %s %s\n", + (tmp & USB_ST_ACTIVE) ? "Active" : "", + (tmp & USB_ST_STALLED) ? "Stalled" : "", + (tmp & USB_ST_BUF_ERR) ? "Buffer Error" : "", + (tmp & USB_ST_BABBLE_DET) ? "Babble Det" : "", + (tmp & USB_ST_NAK_REC) ? "NAKed" : "", + (tmp & USB_ST_CRC_ERR) ? "CRC Error" : "", + (tmp & USB_ST_BIT_ERR) ? "Bitstuff Error" : ""); +} +#endif +/*********************************************************************** + * Data transfer routines + ***********************************************************************/ + +static int us_one_transfer(struct us_data *us, int pipe, char *buf, int length) +{ + int max_size; + int this_xfer; + int result; + int partial; + int maxtry; + int stat; + + /* determine the maximum packet size for these transfers */ + max_size = usb_maxpacket(us->pusb_dev, pipe) * 16; + + /* while we have data left to transfer */ + while (length) { + + /* calculate how long this will be -- maximum or a remainder */ + this_xfer = length > max_size ? max_size : length; + length -= this_xfer; + + /* setup the retry counter */ + maxtry = 10; + + /* set up the transfer loop */ + do { + /* transfer the data */ + debug("Bulk xfer 0x%x(%d) try #%d\n", + (unsigned int)buf, this_xfer, 11 - maxtry); + result = usb_bulk_msg(us->pusb_dev, pipe, buf, + this_xfer, &partial, + USB_CNTL_TIMEOUT * 5); + debug("bulk_msg returned %d xferred %d/%d\n", + result, partial, this_xfer); + if (us->pusb_dev->status != 0) { + /* if we stall, we need to clear it before + * we go on + */ +#ifdef DEBUG + display_int_status(us->pusb_dev->status); +#endif + if (us->pusb_dev->status & USB_ST_STALLED) { + debug("stalled ->clearing endpoint" \ + "halt for pipe 0x%x\n", pipe); + stat = us->pusb_dev->status; + usb_clear_halt(us->pusb_dev, pipe); + us->pusb_dev->status = stat; + if (this_xfer == partial) { + debug("bulk transferred" \ + "with error %lX," \ + " but data ok\n", + us->pusb_dev->status); + return 0; + } + else + return result; + } + if (us->pusb_dev->status & USB_ST_NAK_REC) { + debug("Device NAKed bulk_msg\n"); + return result; + } + debug("bulk transferred with error"); + if (this_xfer == partial) { + debug(" %ld, but data ok\n", + us->pusb_dev->status); + return 0; + } + /* if our try counter reaches 0, bail out */ + debug(" %ld, data %d\n", + us->pusb_dev->status, partial); + if (!maxtry--) + return result; + } + /* update to show what data was transferred */ + this_xfer -= partial; + buf += partial; + /* continue until this transfer is done */ + } while (this_xfer); + } + + /* if we get here, we're done and successful */ + return 0; +} + +static int usb_stor_BBB_reset(struct us_data *us) +{ + int result; + unsigned int pipe; + + /* + * Reset recovery (5.3.4 in Universal Serial Bus Mass Storage Class) + * + * For Reset Recovery the host shall issue in the following order: + * a) a Bulk-Only Mass Storage Reset + * b) a Clear Feature HALT to the Bulk-In endpoint + * c) a Clear Feature HALT to the Bulk-Out endpoint + * + * This is done in 3 steps. + * + * If the reset doesn't succeed, the device should be port reset. + * + * This comment stolen from FreeBSD's /sys/dev/usb/umass.c. + */ + debug("BBB_reset\n"); + result = usb_control_msg(us->pusb_dev, usb_sndctrlpipe(us->pusb_dev, 0), + US_BBB_RESET, + USB_TYPE_CLASS | USB_RECIP_INTERFACE, + 0, us->ifnum, NULL, 0, USB_CNTL_TIMEOUT * 5); + + if ((result < 0) && (us->pusb_dev->status & USB_ST_STALLED)) { + debug("RESET:stall\n"); + return -1; + } + + /* long wait for reset */ + mdelay(150); + debug("BBB_reset result %d: status %lX reset\n", + result, us->pusb_dev->status); + pipe = usb_rcvbulkpipe(us->pusb_dev, us->ep_in); + result = usb_clear_halt(us->pusb_dev, pipe); + /* long wait for reset */ + mdelay(150); + debug("BBB_reset result %d: status %lX clearing IN endpoint\n", + result, us->pusb_dev->status); + /* long wait for reset */ + pipe = usb_sndbulkpipe(us->pusb_dev, us->ep_out); + result = usb_clear_halt(us->pusb_dev, pipe); + mdelay(150); + debug("BBB_reset result %d: status %lX clearing OUT endpoint\n", + result, us->pusb_dev->status); + debug("BBB_reset done\n"); + return 0; +} + +/* FIXME: this reset function doesn't really reset the port, and it + * should. Actually it should probably do what it's doing here, and + * reset the port physically + */ +static int usb_stor_CB_reset(struct us_data *us) +{ + unsigned char cmd[12]; + int result; + + debug("CB_reset\n"); + memset(cmd, 0xff, sizeof(cmd)); + cmd[0] = SCSI_SEND_DIAG; + cmd[1] = 4; + result = usb_control_msg(us->pusb_dev, usb_sndctrlpipe(us->pusb_dev, 0), + US_CBI_ADSC, + USB_TYPE_CLASS | USB_RECIP_INTERFACE, + 0, us->ifnum, cmd, sizeof(cmd), + USB_CNTL_TIMEOUT * 5); + + /* long wait for reset */ + mdelay(1500); + debug("CB_reset result %d: status %lX clearing endpoint halt\n", + result, us->pusb_dev->status); + usb_clear_halt(us->pusb_dev, usb_rcvbulkpipe(us->pusb_dev, us->ep_in)); + usb_clear_halt(us->pusb_dev, usb_rcvbulkpipe(us->pusb_dev, us->ep_out)); + + debug("CB_reset done\n"); + return 0; +} + +/* + * Set up the command for a BBB device. Note that the actual SCSI + * command is copied into cbw.CBWCDB. + */ +static int usb_stor_BBB_comdat(ccb *srb, struct us_data *us) +{ + int result; + int actlen; + int dir_in; + unsigned int pipe; + ALLOC_CACHE_ALIGN_BUFFER(umass_bbb_cbw_t, cbw, 1); + + dir_in = US_DIRECTION(srb->cmd[0]); + +#ifdef BBB_COMDAT_TRACE + printf("dir %d lun %d cmdlen %d cmd %p datalen %lu pdata %p\n", + dir_in, srb->lun, srb->cmdlen, srb->cmd, srb->datalen, + srb->pdata); + if (srb->cmdlen) { + for (result = 0; result < srb->cmdlen; result++) + printf("cmd[%d] %#x ", result, srb->cmd[result]); + printf("\n"); + } +#endif + /* sanity checks */ + if (!(srb->cmdlen <= CBWCDBLENGTH)) { + debug("usb_stor_BBB_comdat:cmdlen too large\n"); + return -1; + } + + /* always OUT to the ep */ + pipe = usb_sndbulkpipe(us->pusb_dev, us->ep_out); + + cbw->dCBWSignature = cpu_to_le32(CBWSIGNATURE); + cbw->dCBWTag = cpu_to_le32(CBWTag++); + cbw->dCBWDataTransferLength = cpu_to_le32(srb->datalen); + cbw->bCBWFlags = (dir_in ? CBWFLAGS_IN : CBWFLAGS_OUT); + cbw->bCBWLUN = srb->lun; + cbw->bCDBLength = srb->cmdlen; + /* copy the command data into the CBW command data buffer */ + /* DST SRC LEN!!! */ + memcpy(cbw->CBWCDB, srb->cmd, srb->cmdlen); + result = usb_bulk_msg(us->pusb_dev, pipe, cbw, UMASS_BBB_CBW_SIZE, + &actlen, USB_CNTL_TIMEOUT * 5); + if (result < 0) + debug("usb_stor_BBB_comdat:usb_bulk_msg error\n"); + return result; +} + +/* FIXME: we also need a CBI_command which sets up the completion + * interrupt, and waits for it + */ +static int usb_stor_CB_comdat(ccb *srb, struct us_data *us) +{ + int result = 0; + int dir_in, retry; + unsigned int pipe; + unsigned long status; + + retry = 5; + dir_in = US_DIRECTION(srb->cmd[0]); + + if (dir_in) + pipe = usb_rcvbulkpipe(us->pusb_dev, us->ep_in); + else + pipe = usb_sndbulkpipe(us->pusb_dev, us->ep_out); + + while (retry--) { + debug("CBI gets a command: Try %d\n", 5 - retry); +#ifdef DEBUG + usb_show_srb(srb); +#endif + /* let's send the command via the control pipe */ + result = usb_control_msg(us->pusb_dev, + usb_sndctrlpipe(us->pusb_dev , 0), + US_CBI_ADSC, + USB_TYPE_CLASS | USB_RECIP_INTERFACE, + 0, us->ifnum, + srb->cmd, srb->cmdlen, + USB_CNTL_TIMEOUT * 5); + debug("CB_transport: control msg returned %d, status %lX\n", + result, us->pusb_dev->status); + /* check the return code for the command */ + if (result < 0) { + if (us->pusb_dev->status & USB_ST_STALLED) { + status = us->pusb_dev->status; + debug(" stall during command found," \ + " clear pipe\n"); + usb_clear_halt(us->pusb_dev, + usb_sndctrlpipe(us->pusb_dev, 0)); + us->pusb_dev->status = status; + } + debug(" error during command %02X" \ + " Stat = %lX\n", srb->cmd[0], + us->pusb_dev->status); + return result; + } + /* transfer the data payload for this command, if one exists*/ + + debug("CB_transport: control msg returned %d," \ + " direction is %s to go 0x%lx\n", result, + dir_in ? "IN" : "OUT", srb->datalen); + if (srb->datalen) { + result = us_one_transfer(us, pipe, (char *)srb->pdata, + srb->datalen); + debug("CBI attempted to transfer data," \ + " result is %d status %lX, len %d\n", + result, us->pusb_dev->status, + us->pusb_dev->act_len); + if (!(us->pusb_dev->status & USB_ST_NAK_REC)) + break; + } /* if (srb->datalen) */ + else + break; + } + /* return result */ + + return result; +} + + +static int usb_stor_CBI_get_status(ccb *srb, struct us_data *us) +{ + int timeout; + + us->ip_wanted = 1; + submit_int_msg(us->pusb_dev, us->irqpipe, + (void *) &us->ip_data, us->irqmaxp, us->irqinterval); + timeout = 1000; + while (timeout--) { + if ((volatile int *) us->ip_wanted == NULL) + break; + mdelay(10); + } + if (us->ip_wanted) { + printf(" Did not get interrupt on CBI\n"); + us->ip_wanted = 0; + return USB_STOR_TRANSPORT_ERROR; + } + debug("Got interrupt data 0x%x, transfered %d status 0x%lX\n", + us->ip_data, us->pusb_dev->irq_act_len, + us->pusb_dev->irq_status); + /* UFI gives us ASC and ASCQ, like a request sense */ + if (us->subclass == US_SC_UFI) { + if (srb->cmd[0] == SCSI_REQ_SENSE || + srb->cmd[0] == SCSI_INQUIRY) + return USB_STOR_TRANSPORT_GOOD; /* Good */ + else if (us->ip_data) + return USB_STOR_TRANSPORT_FAILED; + else + return USB_STOR_TRANSPORT_GOOD; + } + /* otherwise, we interpret the data normally */ + switch (us->ip_data) { + case 0x0001: + return USB_STOR_TRANSPORT_GOOD; + case 0x0002: + return USB_STOR_TRANSPORT_FAILED; + default: + return USB_STOR_TRANSPORT_ERROR; + } /* switch */ + return USB_STOR_TRANSPORT_ERROR; +} + +#define USB_TRANSPORT_UNKNOWN_RETRY 5 +#define USB_TRANSPORT_NOT_READY_RETRY 10 + +/* clear a stall on an endpoint - special for BBB devices */ +static int usb_stor_BBB_clear_endpt_stall(struct us_data *us, __u8 endpt) +{ + int result; + + /* ENDPOINT_HALT = 0, so set value to 0 */ + result = usb_control_msg(us->pusb_dev, usb_sndctrlpipe(us->pusb_dev, 0), + USB_REQ_CLEAR_FEATURE, USB_RECIP_ENDPOINT, + 0, endpt, NULL, 0, USB_CNTL_TIMEOUT * 5); + return result; +} + +static int usb_stor_BBB_transport(ccb *srb, struct us_data *us) +{ + int result, retry; + int dir_in; + int actlen, data_actlen; + unsigned int pipe, pipein, pipeout; + ALLOC_CACHE_ALIGN_BUFFER(umass_bbb_csw_t, csw, 1); +#ifdef BBB_XPORT_TRACE + unsigned char *ptr; + int index; +#endif + + dir_in = US_DIRECTION(srb->cmd[0]); + + /* COMMAND phase */ + debug("COMMAND phase\n"); + result = usb_stor_BBB_comdat(srb, us); + if (result < 0) { + debug("failed to send CBW status %ld\n", + us->pusb_dev->status); + usb_stor_BBB_reset(us); + return USB_STOR_TRANSPORT_FAILED; + } + if (!(us->flags & USB_READY)) + mdelay(5); + pipein = usb_rcvbulkpipe(us->pusb_dev, us->ep_in); + pipeout = usb_sndbulkpipe(us->pusb_dev, us->ep_out); + /* DATA phase + error handling */ + data_actlen = 0; + /* no data, go immediately to the STATUS phase */ + if (srb->datalen == 0) + goto st; + debug("DATA phase\n"); + if (dir_in) + pipe = pipein; + else + pipe = pipeout; + result = usb_bulk_msg(us->pusb_dev, pipe, srb->pdata, srb->datalen, + &data_actlen, USB_CNTL_TIMEOUT * 5); + /* special handling of STALL in DATA phase */ + if ((result < 0) && (us->pusb_dev->status & USB_ST_STALLED)) { + debug("DATA:stall\n"); + /* clear the STALL on the endpoint */ + result = usb_stor_BBB_clear_endpt_stall(us, + dir_in ? us->ep_in : us->ep_out); + if (result >= 0) + /* continue on to STATUS phase */ + goto st; + } + if (result < 0) { + debug("usb_bulk_msg error status %ld\n", + us->pusb_dev->status); + usb_stor_BBB_reset(us); + return USB_STOR_TRANSPORT_FAILED; + } +#ifdef BBB_XPORT_TRACE + for (index = 0; index < data_actlen; index++) + printf("pdata[%d] %#x ", index, srb->pdata[index]); + printf("\n"); +#endif + /* STATUS phase + error handling */ +st: + retry = 0; +again: + debug("STATUS phase\n"); + result = usb_bulk_msg(us->pusb_dev, pipein, csw, UMASS_BBB_CSW_SIZE, + &actlen, USB_CNTL_TIMEOUT*5); + + /* special handling of STALL in STATUS phase */ + if ((result < 0) && (retry < 1) && + (us->pusb_dev->status & USB_ST_STALLED)) { + debug("STATUS:stall\n"); + /* clear the STALL on the endpoint */ + result = usb_stor_BBB_clear_endpt_stall(us, us->ep_in); + if (result >= 0 && (retry++ < 1)) + /* do a retry */ + goto again; + } + if (result < 0) { + debug("usb_bulk_msg error status %ld\n", + us->pusb_dev->status); + usb_stor_BBB_reset(us); + return USB_STOR_TRANSPORT_FAILED; + } +#ifdef BBB_XPORT_TRACE + ptr = (unsigned char *)csw; + for (index = 0; index < UMASS_BBB_CSW_SIZE; index++) + printf("ptr[%d] %#x ", index, ptr[index]); + printf("\n"); +#endif + /* misuse pipe to get the residue */ + pipe = le32_to_cpu(csw->dCSWDataResidue); + if (pipe == 0 && srb->datalen != 0 && srb->datalen - data_actlen != 0) + pipe = srb->datalen - data_actlen; + if (CSWSIGNATURE != le32_to_cpu(csw->dCSWSignature)) { + debug("!CSWSIGNATURE\n"); + usb_stor_BBB_reset(us); + return USB_STOR_TRANSPORT_FAILED; + } else if ((CBWTag - 1) != le32_to_cpu(csw->dCSWTag)) { + debug("!Tag\n"); + usb_stor_BBB_reset(us); + return USB_STOR_TRANSPORT_FAILED; + } else if (csw->bCSWStatus > CSWSTATUS_PHASE) { + debug(">PHASE\n"); + usb_stor_BBB_reset(us); + return USB_STOR_TRANSPORT_FAILED; + } else if (csw->bCSWStatus == CSWSTATUS_PHASE) { + debug("=PHASE\n"); + usb_stor_BBB_reset(us); + return USB_STOR_TRANSPORT_FAILED; + } else if (data_actlen > srb->datalen) { + debug("transferred %dB instead of %ldB\n", + data_actlen, srb->datalen); + return USB_STOR_TRANSPORT_FAILED; + } else if (csw->bCSWStatus == CSWSTATUS_FAILED) { + debug("FAILED\n"); + return USB_STOR_TRANSPORT_FAILED; + } + + return result; +} + +static int usb_stor_CB_transport(ccb *srb, struct us_data *us) +{ + int result, status; + ccb *psrb; + ccb reqsrb; + int retry, notready; + + psrb = &reqsrb; + status = USB_STOR_TRANSPORT_GOOD; + retry = 0; + notready = 0; + /* issue the command */ +do_retry: + result = usb_stor_CB_comdat(srb, us); + debug("command / Data returned %d, status %lX\n", + result, us->pusb_dev->status); + /* if this is an CBI Protocol, get IRQ */ + if (us->protocol == US_PR_CBI) { + status = usb_stor_CBI_get_status(srb, us); + /* if the status is error, report it */ + if (status == USB_STOR_TRANSPORT_ERROR) { + debug(" USB CBI Command Error\n"); + return status; + } + srb->sense_buf[12] = (unsigned char)(us->ip_data >> 8); + srb->sense_buf[13] = (unsigned char)(us->ip_data & 0xff); + if (!us->ip_data) { + /* if the status is good, report it */ + if (status == USB_STOR_TRANSPORT_GOOD) { + debug(" USB CBI Command Good\n"); + return status; + } + } + } + /* do we have to issue an auto request? */ + /* HERE we have to check the result */ + if ((result < 0) && !(us->pusb_dev->status & USB_ST_STALLED)) { + debug("ERROR %lX\n", us->pusb_dev->status); + us->transport_reset(us); + return USB_STOR_TRANSPORT_ERROR; + } + if ((us->protocol == US_PR_CBI) && + ((srb->cmd[0] == SCSI_REQ_SENSE) || + (srb->cmd[0] == SCSI_INQUIRY))) { + /* do not issue an autorequest after request sense */ + debug("No auto request and good\n"); + return USB_STOR_TRANSPORT_GOOD; + } + /* issue an request_sense */ + memset(&psrb->cmd[0], 0, 12); + psrb->cmd[0] = SCSI_REQ_SENSE; + psrb->cmd[1] = srb->lun << 5; + psrb->cmd[4] = 18; + psrb->datalen = 18; + psrb->pdata = &srb->sense_buf[0]; + psrb->cmdlen = 12; + /* issue the command */ + result = usb_stor_CB_comdat(psrb, us); + debug("auto request returned %d\n", result); + /* if this is an CBI Protocol, get IRQ */ + if (us->protocol == US_PR_CBI) + status = usb_stor_CBI_get_status(psrb, us); + + if ((result < 0) && !(us->pusb_dev->status & USB_ST_STALLED)) { + debug(" AUTO REQUEST ERROR %ld\n", + us->pusb_dev->status); + return USB_STOR_TRANSPORT_ERROR; + } + debug("autorequest returned 0x%02X 0x%02X 0x%02X 0x%02X\n", + srb->sense_buf[0], srb->sense_buf[2], + srb->sense_buf[12], srb->sense_buf[13]); + /* Check the auto request result */ + if ((srb->sense_buf[2] == 0) && + (srb->sense_buf[12] == 0) && + (srb->sense_buf[13] == 0)) { + /* ok, no sense */ + return USB_STOR_TRANSPORT_GOOD; + } + + /* Check the auto request result */ + switch (srb->sense_buf[2]) { + case 0x01: + /* Recovered Error */ + return USB_STOR_TRANSPORT_GOOD; + break; + case 0x02: + /* Not Ready */ + if (notready++ > USB_TRANSPORT_NOT_READY_RETRY) { + printf("cmd 0x%02X returned 0x%02X 0x%02X 0x%02X" + " 0x%02X (NOT READY)\n", srb->cmd[0], + srb->sense_buf[0], srb->sense_buf[2], + srb->sense_buf[12], srb->sense_buf[13]); + return USB_STOR_TRANSPORT_FAILED; + } else { + mdelay(100); + goto do_retry; + } + break; + default: + if (retry++ > USB_TRANSPORT_UNKNOWN_RETRY) { + printf("cmd 0x%02X returned 0x%02X 0x%02X 0x%02X" + " 0x%02X\n", srb->cmd[0], srb->sense_buf[0], + srb->sense_buf[2], srb->sense_buf[12], + srb->sense_buf[13]); + return USB_STOR_TRANSPORT_FAILED; + } else + goto do_retry; + break; + } + return USB_STOR_TRANSPORT_FAILED; +} + + +static int usb_inquiry(ccb *srb, struct us_data *ss) +{ + int retry, i; + retry = 5; + do { + memset(&srb->cmd[0], 0, 12); + srb->cmd[0] = SCSI_INQUIRY; + srb->cmd[1] = srb->lun << 5; + srb->cmd[4] = 36; + srb->datalen = 36; + srb->cmdlen = 12; + i = ss->transport(srb, ss); + debug("inquiry returns %d\n", i); + if (i == 0) + break; + } while (--retry); + + if (!retry) { + printf("error in inquiry\n"); + return -1; + } + return 0; +} + +static int usb_request_sense(ccb *srb, struct us_data *ss) +{ + char *ptr; + + ptr = (char *)srb->pdata; + memset(&srb->cmd[0], 0, 12); + srb->cmd[0] = SCSI_REQ_SENSE; + srb->cmd[1] = srb->lun << 5; + srb->cmd[4] = 18; + srb->datalen = 18; + srb->pdata = &srb->sense_buf[0]; + srb->cmdlen = 12; + ss->transport(srb, ss); + debug("Request Sense returned %02X %02X %02X\n", + srb->sense_buf[2], srb->sense_buf[12], + srb->sense_buf[13]); + srb->pdata = (uchar *)ptr; + return 0; +} + +static int usb_test_unit_ready(ccb *srb, struct us_data *ss) +{ + int retries = 10; + + do { + memset(&srb->cmd[0], 0, 12); + srb->cmd[0] = SCSI_TST_U_RDY; + srb->cmd[1] = srb->lun << 5; + srb->datalen = 0; + srb->cmdlen = 12; + if (ss->transport(srb, ss) == USB_STOR_TRANSPORT_GOOD) { + ss->flags |= USB_READY; + return 0; + } + usb_request_sense(srb, ss); + /* + * Check the Key Code Qualifier, if it matches + * "Not Ready - medium not present" + * (the sense Key equals 0x2 and the ASC is 0x3a) + * return immediately as the medium being absent won't change + * unless there is a user action. + */ + if ((srb->sense_buf[2] == 0x02) && + (srb->sense_buf[12] == 0x3a)) + return -1; + mdelay(100); + } while (retries--); + + return -1; +} + +static int usb_read_capacity(ccb *srb, struct us_data *ss) +{ + int retry; + /* XXX retries */ + retry = 3; + do { + memset(&srb->cmd[0], 0, 12); + srb->cmd[0] = SCSI_RD_CAPAC; + srb->cmd[1] = srb->lun << 5; + srb->datalen = 8; + srb->cmdlen = 12; + if (ss->transport(srb, ss) == USB_STOR_TRANSPORT_GOOD) + return 0; + } while (retry--); + + return -1; +} + +static int usb_read_10(ccb *srb, struct us_data *ss, unsigned long start, + unsigned short blocks) +{ + memset(&srb->cmd[0], 0, 12); + srb->cmd[0] = SCSI_READ10; + srb->cmd[1] = srb->lun << 5; + srb->cmd[2] = ((unsigned char) (start >> 24)) & 0xff; + srb->cmd[3] = ((unsigned char) (start >> 16)) & 0xff; + srb->cmd[4] = ((unsigned char) (start >> 8)) & 0xff; + srb->cmd[5] = ((unsigned char) (start)) & 0xff; + srb->cmd[7] = ((unsigned char) (blocks >> 8)) & 0xff; + srb->cmd[8] = (unsigned char) blocks & 0xff; + srb->cmdlen = 12; + debug("read10: start %lx blocks %x\n", start, blocks); + return ss->transport(srb, ss); +} + +static int usb_write_10(ccb *srb, struct us_data *ss, unsigned long start, + unsigned short blocks) +{ + memset(&srb->cmd[0], 0, 12); + srb->cmd[0] = SCSI_WRITE10; + srb->cmd[1] = srb->lun << 5; + srb->cmd[2] = ((unsigned char) (start >> 24)) & 0xff; + srb->cmd[3] = ((unsigned char) (start >> 16)) & 0xff; + srb->cmd[4] = ((unsigned char) (start >> 8)) & 0xff; + srb->cmd[5] = ((unsigned char) (start)) & 0xff; + srb->cmd[7] = ((unsigned char) (blocks >> 8)) & 0xff; + srb->cmd[8] = (unsigned char) blocks & 0xff; + srb->cmdlen = 12; + debug("write10: start %lx blocks %x\n", start, blocks); + return ss->transport(srb, ss); +} + + +#ifdef CONFIG_USB_BIN_FIXUP +/* + * Some USB storage devices queried for SCSI identification data respond with + * binary strings, which if output to the console freeze the terminal. The + * workaround is to modify the vendor and product strings read from such + * device with proper values (as reported by 'usb info'). + * + * Vendor and product length limits are taken from the definition of + * block_dev_desc_t in include/part.h. + */ +static void usb_bin_fixup(struct usb_device_descriptor descriptor, + unsigned char vendor[], + unsigned char product[]) { + const unsigned char max_vendor_len = 40; + const unsigned char max_product_len = 20; + if (descriptor.idVendor == 0x0424 && descriptor.idProduct == 0x223a) { + strncpy((char *)vendor, "SMSC", max_vendor_len); + strncpy((char *)product, "Flash Media Cntrller", + max_product_len); + } +} +#endif /* CONFIG_USB_BIN_FIXUP */ + +unsigned long usb_stor_read(int device, lbaint_t blknr, + lbaint_t blkcnt, void *buffer) +{ + lbaint_t start, blks; + uintptr_t buf_addr; + unsigned short smallblks; + struct usb_device *dev; + struct us_data *ss; + int retry, i; + ccb *srb = &usb_ccb; + + if (blkcnt == 0) + return 0; + + device &= 0xff; + /* Setup device */ + debug("\nusb_read: dev %d \n", device); + dev = NULL; + for (i = 0; i < USB_MAX_DEVICE; i++) { + dev = usb_get_dev_index(i); + if (dev == NULL) + return 0; + if (dev->devnum == usb_dev_desc[device].target) + break; + } + ss = (struct us_data *)dev->privptr; + + usb_disable_asynch(1); /* asynch transfer not allowed */ + srb->lun = usb_dev_desc[device].lun; + buf_addr = (unsigned long)buffer; + start = blknr; + blks = blkcnt; + + debug("\nusb_read: dev %d startblk " LBAF ", blccnt " LBAF + " buffer %lx\n", device, start, blks, buf_addr); + + do { + /* XXX need some comment here */ + retry = 2; + srb->pdata = (unsigned char *)buf_addr; + if (blks > USB_MAX_XFER_BLK) + smallblks = USB_MAX_XFER_BLK; + else + smallblks = (unsigned short) blks; +retry_it: + if (smallblks == USB_MAX_XFER_BLK) + usb_show_progress(); + srb->datalen = usb_dev_desc[device].blksz * smallblks; + srb->pdata = (unsigned char *)buf_addr; + if (usb_read_10(srb, ss, start, smallblks)) { + debug("Read ERROR\n"); + usb_request_sense(srb, ss); + if (retry--) + goto retry_it; + blkcnt -= blks; + break; + } + start += smallblks; + blks -= smallblks; + buf_addr += srb->datalen; + } while (blks != 0); + ss->flags &= ~USB_READY; + + debug("usb_read: end startblk " LBAF + ", blccnt %x buffer %lx\n", + start, smallblks, buf_addr); + + usb_disable_asynch(0); /* asynch transfer allowed */ + if (blkcnt >= USB_MAX_XFER_BLK) + debug("\n"); + return blkcnt; +} + +unsigned long usb_stor_write(int device, lbaint_t blknr, + lbaint_t blkcnt, const void *buffer) +{ + lbaint_t start, blks; + uintptr_t buf_addr; + unsigned short smallblks; + struct usb_device *dev; + struct us_data *ss; + int retry, i; + ccb *srb = &usb_ccb; + + if (blkcnt == 0) + return 0; + + device &= 0xff; + /* Setup device */ + debug("\nusb_write: dev %d \n", device); + dev = NULL; + for (i = 0; i < USB_MAX_DEVICE; i++) { + dev = usb_get_dev_index(i); + if (dev == NULL) + return 0; + if (dev->devnum == usb_dev_desc[device].target) + break; + } + ss = (struct us_data *)dev->privptr; + + usb_disable_asynch(1); /* asynch transfer not allowed */ + + srb->lun = usb_dev_desc[device].lun; + buf_addr = (unsigned long)buffer; + start = blknr; + blks = blkcnt; + + debug("\nusb_write: dev %d startblk " LBAF ", blccnt " LBAF + " buffer %lx\n", device, start, blks, buf_addr); + + do { + /* If write fails retry for max retry count else + * return with number of blocks written successfully. + */ + retry = 2; + srb->pdata = (unsigned char *)buf_addr; + if (blks > USB_MAX_XFER_BLK) + smallblks = USB_MAX_XFER_BLK; + else + smallblks = (unsigned short) blks; +retry_it: + if (smallblks == USB_MAX_XFER_BLK) + usb_show_progress(); + srb->datalen = usb_dev_desc[device].blksz * smallblks; + srb->pdata = (unsigned char *)buf_addr; + if (usb_write_10(srb, ss, start, smallblks)) { + debug("Write ERROR\n"); + usb_request_sense(srb, ss); + if (retry--) + goto retry_it; + blkcnt -= blks; + break; + } + start += smallblks; + blks -= smallblks; + buf_addr += srb->datalen; + } while (blks != 0); + ss->flags &= ~USB_READY; + + debug("usb_write: end startblk " LBAF ", blccnt %x buffer %lx\n", + start, smallblks, buf_addr); + + usb_disable_asynch(0); /* asynch transfer allowed */ + if (blkcnt >= USB_MAX_XFER_BLK) + debug("\n"); + return blkcnt; + +} + +/* Probe to see if a new device is actually a Storage device */ +int usb_storage_probe(struct usb_device *dev, unsigned int ifnum, + struct us_data *ss) +{ + struct usb_interface *iface; + int i; + struct usb_endpoint_descriptor *ep_desc; + unsigned int flags = 0; + + int protocol = 0; + int subclass = 0; + + /* let's examine the device now */ + iface = &dev->config.if_desc[ifnum]; + +#if 0 + /* this is the place to patch some storage devices */ + debug("iVendor %X iProduct %X\n", dev->descriptor.idVendor, + dev->descriptor.idProduct); + + if ((dev->descriptor.idVendor) == 0x066b && + (dev->descriptor.idProduct) == 0x0103) { + debug("patched for E-USB\n"); + protocol = US_PR_CB; + subclass = US_SC_UFI; /* an assumption */ + } +#endif + + if (dev->descriptor.bDeviceClass != 0 || + iface->desc.bInterfaceClass != USB_CLASS_MASS_STORAGE || + iface->desc.bInterfaceSubClass < US_SC_MIN || + iface->desc.bInterfaceSubClass > US_SC_MAX) { + /* if it's not a mass storage, we go no further */ + return 0; + } + + memset(ss, 0, sizeof(struct us_data)); + + /* At this point, we know we've got a live one */ + debug("\n\nUSB Mass Storage device detected\n"); + + /* Initialize the us_data structure with some useful info */ + ss->flags = flags; + ss->ifnum = ifnum; + ss->pusb_dev = dev; + ss->attention_done = 0; + + /* If the device has subclass and protocol, then use that. Otherwise, + * take data from the specific interface. + */ + if (subclass) { + ss->subclass = subclass; + ss->protocol = protocol; + } else { + ss->subclass = iface->desc.bInterfaceSubClass; + ss->protocol = iface->desc.bInterfaceProtocol; + } + + /* set the handler pointers based on the protocol */ + debug("Transport: "); + switch (ss->protocol) { + case US_PR_CB: + debug("Control/Bulk\n"); + ss->transport = usb_stor_CB_transport; + ss->transport_reset = usb_stor_CB_reset; + break; + + case US_PR_CBI: + debug("Control/Bulk/Interrupt\n"); + ss->transport = usb_stor_CB_transport; + ss->transport_reset = usb_stor_CB_reset; + break; + case US_PR_BULK: + debug("Bulk/Bulk/Bulk\n"); + ss->transport = usb_stor_BBB_transport; + ss->transport_reset = usb_stor_BBB_reset; + break; + default: + printf("USB Storage Transport unknown / not yet implemented\n"); + return 0; + break; + } + + /* + * We are expecting a minimum of 2 endpoints - in and out (bulk). + * An optional interrupt is OK (necessary for CBI protocol). + * We will ignore any others. + */ + for (i = 0; i < iface->desc.bNumEndpoints; i++) { + ep_desc = &iface->ep_desc[i]; + /* is it an BULK endpoint? */ + if ((ep_desc->bmAttributes & + USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_BULK) { + if (ep_desc->bEndpointAddress & USB_DIR_IN) + ss->ep_in = ep_desc->bEndpointAddress & + USB_ENDPOINT_NUMBER_MASK; + else + ss->ep_out = + ep_desc->bEndpointAddress & + USB_ENDPOINT_NUMBER_MASK; + } + + /* is it an interrupt endpoint? */ + if ((ep_desc->bmAttributes & + USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_INT) { + ss->ep_int = ep_desc->bEndpointAddress & + USB_ENDPOINT_NUMBER_MASK; + ss->irqinterval = ep_desc->bInterval; + } + } + debug("Endpoints In %d Out %d Int %d\n", + ss->ep_in, ss->ep_out, ss->ep_int); + + /* Do some basic sanity checks, and bail if we find a problem */ + if (usb_set_interface(dev, iface->desc.bInterfaceNumber, 0) || + !ss->ep_in || !ss->ep_out || + (ss->protocol == US_PR_CBI && ss->ep_int == 0)) { + debug("Problems with device\n"); + return 0; + } + /* set class specific stuff */ + /* We only handle certain protocols. Currently, these are + * the only ones. + * The SFF8070 accepts the requests used in u-boot + */ + if (ss->subclass != US_SC_UFI && ss->subclass != US_SC_SCSI && + ss->subclass != US_SC_8070) { + printf("Sorry, protocol %d not yet supported.\n", ss->subclass); + return 0; + } + if (ss->ep_int) { + /* we had found an interrupt endpoint, prepare irq pipe + * set up the IRQ pipe and handler + */ + ss->irqinterval = (ss->irqinterval > 0) ? ss->irqinterval : 255; + ss->irqpipe = usb_rcvintpipe(ss->pusb_dev, ss->ep_int); + ss->irqmaxp = usb_maxpacket(dev, ss->irqpipe); + dev->irq_handle = usb_stor_irq; + } + dev->privptr = (void *)ss; + return 1; +} + +int usb_stor_get_info(struct usb_device *dev, struct us_data *ss, + block_dev_desc_t *dev_desc) +{ + unsigned char perq, modi; + ALLOC_CACHE_ALIGN_BUFFER(unsigned long, cap, 2); + ALLOC_CACHE_ALIGN_BUFFER(unsigned char, usb_stor_buf, 36); + unsigned long *capacity, *blksz; + ccb *pccb = &usb_ccb; + + pccb->pdata = usb_stor_buf; + + dev_desc->target = dev->devnum; + pccb->lun = dev_desc->lun; + debug(" address %d\n", dev_desc->target); + + if (usb_inquiry(pccb, ss)) + return -1; + + perq = usb_stor_buf[0]; + modi = usb_stor_buf[1]; + + if ((perq & 0x1f) == 0x1f) { + /* skip unknown devices */ + return 0; + } + if ((modi&0x80) == 0x80) { + /* drive is removable */ + dev_desc->removable = 1; + } + memcpy(&dev_desc->vendor[0], (const void *) &usb_stor_buf[8], 8); + memcpy(&dev_desc->product[0], (const void *) &usb_stor_buf[16], 16); + memcpy(&dev_desc->revision[0], (const void *) &usb_stor_buf[32], 4); + dev_desc->vendor[8] = 0; + dev_desc->product[16] = 0; + dev_desc->revision[4] = 0; +#ifdef CONFIG_USB_BIN_FIXUP + usb_bin_fixup(dev->descriptor, (uchar *)dev_desc->vendor, + (uchar *)dev_desc->product); +#endif /* CONFIG_USB_BIN_FIXUP */ + debug("ISO Vers %X, Response Data %X\n", usb_stor_buf[2], + usb_stor_buf[3]); + if (usb_test_unit_ready(pccb, ss)) { + printf("Device NOT ready\n" + " Request Sense returned %02X %02X %02X\n", + pccb->sense_buf[2], pccb->sense_buf[12], + pccb->sense_buf[13]); + if (dev_desc->removable == 1) { + dev_desc->type = perq; + return 1; + } + return 0; + } + pccb->pdata = (unsigned char *)&cap[0]; + memset(pccb->pdata, 0, 8); + if (usb_read_capacity(pccb, ss) != 0) { + printf("READ_CAP ERROR\n"); + cap[0] = 2880; + cap[1] = 0x200; + } + ss->flags &= ~USB_READY; + debug("Read Capacity returns: 0x%lx, 0x%lx\n", cap[0], cap[1]); +#if 0 + if (cap[0] > (0x200000 * 10)) /* greater than 10 GByte */ + cap[0] >>= 16; +#endif + cap[0] = cpu_to_be32(cap[0]); + cap[1] = cpu_to_be32(cap[1]); + + /* this assumes bigendian! */ + cap[0] += 1; + capacity = &cap[0]; + blksz = &cap[1]; + debug("Capacity = 0x%lx, blocksz = 0x%lx\n", *capacity, *blksz); + dev_desc->lba = *capacity; + dev_desc->blksz = *blksz; + dev_desc->log2blksz = LOG2(dev_desc->blksz); + dev_desc->type = perq; + debug(" address %d\n", dev_desc->target); + debug("partype: %d\n", dev_desc->part_type); + + init_part(dev_desc); + + debug("partype: %d\n", dev_desc->part_type); + return 1; +} diff --git a/qemu/roms/u-boot/common/xyzModem.c b/qemu/roms/u-boot/common/xyzModem.c new file mode 100644 index 000000000..39f7d17a7 --- /dev/null +++ b/qemu/roms/u-boot/common/xyzModem.c @@ -0,0 +1,817 @@ +/* + *========================================================================== + * + * xyzModem.c + * + * RedBoot stream handler for xyzModem protocol + * + *========================================================================== + * SPDX-License-Identifier: eCos-2.0 + *========================================================================== + *#####DESCRIPTIONBEGIN#### + * + * Author(s): gthomas + * Contributors: gthomas, tsmith, Yoshinori Sato + * Date: 2000-07-14 + * Purpose: + * Description: + * + * This code is part of RedBoot (tm). + * + *####DESCRIPTIONEND#### + * + *========================================================================== + */ +#include <common.h> +#include <xyzModem.h> +#include <stdarg.h> +#include <crc.h> + +/* Assumption - run xyzModem protocol over the console port */ + +/* Values magic to the protocol */ +#define SOH 0x01 +#define STX 0x02 +#define EOT 0x04 +#define ACK 0x06 +#define BSP 0x08 +#define NAK 0x15 +#define CAN 0x18 +#define EOF 0x1A /* ^Z for DOS officionados */ + +#define USE_YMODEM_LENGTH + +/* Data & state local to the protocol */ +static struct +{ +#ifdef REDBOOT + hal_virtual_comm_table_t *__chan; +#else + int *__chan; +#endif + unsigned char pkt[1024], *bufp; + unsigned char blk, cblk, crc1, crc2; + unsigned char next_blk; /* Expected block */ + int len, mode, total_retries; + int total_SOH, total_STX, total_CAN; + bool crc_mode, at_eof, tx_ack; +#ifdef USE_YMODEM_LENGTH + unsigned long file_length, read_length; +#endif +} xyz; + +#define xyzModem_CHAR_TIMEOUT 2000 /* 2 seconds */ +#define xyzModem_MAX_RETRIES 20 +#define xyzModem_MAX_RETRIES_WITH_CRC 10 +#define xyzModem_CAN_COUNT 3 /* Wait for 3 CAN before quitting */ + + +#ifndef REDBOOT /*SB */ +typedef int cyg_int32; +static int +CYGACC_COMM_IF_GETC_TIMEOUT (char chan, char *c) +{ +#define DELAY 20 + unsigned long counter = 0; + while (!tstc () && (counter < xyzModem_CHAR_TIMEOUT * 1000 / DELAY)) + { + udelay (DELAY); + counter++; + } + if (tstc ()) + { + *c = getc (); + return 1; + } + return 0; +} + +static void +CYGACC_COMM_IF_PUTC (char x, char y) +{ + putc (y); +} + +/* Validate a hex character */ +__inline__ static bool +_is_hex (char c) +{ + return (((c >= '0') && (c <= '9')) || + ((c >= 'A') && (c <= 'F')) || ((c >= 'a') && (c <= 'f'))); +} + +/* Convert a single hex nibble */ +__inline__ static int +_from_hex (char c) +{ + int ret = 0; + + if ((c >= '0') && (c <= '9')) + { + ret = (c - '0'); + } + else if ((c >= 'a') && (c <= 'f')) + { + ret = (c - 'a' + 0x0a); + } + else if ((c >= 'A') && (c <= 'F')) + { + ret = (c - 'A' + 0x0A); + } + return ret; +} + +/* Convert a character to lower case */ +__inline__ static char +_tolower (char c) +{ + if ((c >= 'A') && (c <= 'Z')) + { + c = (c - 'A') + 'a'; + } + return c; +} + +/* Parse (scan) a number */ +static bool +parse_num (char *s, unsigned long *val, char **es, char *delim) +{ + bool first = true; + int radix = 10; + char c; + unsigned long result = 0; + int digit; + + while (*s == ' ') + s++; + while (*s) + { + if (first && (s[0] == '0') && (_tolower (s[1]) == 'x')) + { + radix = 16; + s += 2; + } + first = false; + c = *s++; + if (_is_hex (c) && ((digit = _from_hex (c)) < radix)) + { + /* Valid digit */ +#ifdef CYGPKG_HAL_MIPS + /* FIXME: tx49 compiler generates 0x2539018 for MUL which */ + /* isn't any good. */ + if (16 == radix) + result = result << 4; + else + result = 10 * result; + result += digit; +#else + result = (result * radix) + digit; +#endif + } + else + { + if (delim != (char *) 0) + { + /* See if this character is one of the delimiters */ + char *dp = delim; + while (*dp && (c != *dp)) + dp++; + if (*dp) + break; /* Found a good delimiter */ + } + return false; /* Malformatted number */ + } + } + *val = result; + if (es != (char **) 0) + { + *es = s; + } + return true; +} + +#endif + +#define USE_SPRINTF +#ifdef DEBUG +#ifndef USE_SPRINTF +/* + * Note: this debug setup only works if the target platform has two serial ports + * available so that the other one (currently only port 1) can be used for debug + * messages. + */ +static int +zm_dprintf (char *fmt, ...) +{ + int cur_console; + va_list args; + + va_start (args, fmt); +#ifdef REDBOOT + cur_console = + CYGACC_CALL_IF_SET_CONSOLE_COMM + (CYGNUM_CALL_IF_SET_COMM_ID_QUERY_CURRENT); + CYGACC_CALL_IF_SET_CONSOLE_COMM (1); +#endif + diag_vprintf (fmt, args); +#ifdef REDBOOT + CYGACC_CALL_IF_SET_CONSOLE_COMM (cur_console); +#endif +} + +static void +zm_flush (void) +{ +} + +#else +/* + * Note: this debug setup works by storing the strings in a fixed buffer + */ +#define FINAL +#ifdef FINAL +static char *zm_out = (char *) 0x00380000; +static char *zm_out_start = (char *) 0x00380000; +#else +static char zm_buf[8192]; +static char *zm_out = zm_buf; +static char *zm_out_start = zm_buf; + +#endif +static int +zm_dprintf (char *fmt, ...) +{ + int len; + va_list args; + + va_start (args, fmt); + len = diag_vsprintf (zm_out, fmt, args); + zm_out += len; + return len; +} + +static void +zm_flush (void) +{ +#ifdef REDBOOT + char *p = zm_out_start; + while (*p) + mon_write_char (*p++); +#endif + zm_out = zm_out_start; +} +#endif + +static void +zm_dump_buf (void *buf, int len) +{ +#ifdef REDBOOT + diag_vdump_buf_with_offset (zm_dprintf, buf, len, 0); +#else + +#endif +} + +static unsigned char zm_buf[2048]; +static unsigned char *zm_bp; + +static void +zm_new (void) +{ + zm_bp = zm_buf; +} + +static void +zm_save (unsigned char c) +{ + *zm_bp++ = c; +} + +static void +zm_dump (int line) +{ + zm_dprintf ("Packet at line: %d\n", line); + zm_dump_buf (zm_buf, zm_bp - zm_buf); +} + +#define ZM_DEBUG(x) x +#else +#define ZM_DEBUG(x) +#endif + +/* Wait for the line to go idle */ +static void +xyzModem_flush (void) +{ + int res; + char c; + while (true) + { + res = CYGACC_COMM_IF_GETC_TIMEOUT (*xyz.__chan, &c); + if (!res) + return; + } +} + +static int +xyzModem_get_hdr (void) +{ + char c; + int res; + bool hdr_found = false; + int i, can_total, hdr_chars; + unsigned short cksum; + + ZM_DEBUG (zm_new ()); + /* Find the start of a header */ + can_total = 0; + hdr_chars = 0; + + if (xyz.tx_ack) + { + CYGACC_COMM_IF_PUTC (*xyz.__chan, ACK); + xyz.tx_ack = false; + } + while (!hdr_found) + { + res = CYGACC_COMM_IF_GETC_TIMEOUT (*xyz.__chan, &c); + ZM_DEBUG (zm_save (c)); + if (res) + { + hdr_chars++; + switch (c) + { + case SOH: + xyz.total_SOH++; + case STX: + if (c == STX) + xyz.total_STX++; + hdr_found = true; + break; + case CAN: + xyz.total_CAN++; + ZM_DEBUG (zm_dump (__LINE__)); + if (++can_total == xyzModem_CAN_COUNT) + { + return xyzModem_cancel; + } + else + { + /* Wait for multiple CAN to avoid early quits */ + break; + } + case EOT: + /* EOT only supported if no noise */ + if (hdr_chars == 1) + { + CYGACC_COMM_IF_PUTC (*xyz.__chan, ACK); + ZM_DEBUG (zm_dprintf ("ACK on EOT #%d\n", __LINE__)); + ZM_DEBUG (zm_dump (__LINE__)); + return xyzModem_eof; + } + default: + /* Ignore, waiting for start of header */ + ; + } + } + else + { + /* Data stream timed out */ + xyzModem_flush (); /* Toss any current input */ + ZM_DEBUG (zm_dump (__LINE__)); + CYGACC_CALL_IF_DELAY_US ((cyg_int32) 250000); + return xyzModem_timeout; + } + } + + /* Header found, now read the data */ + res = CYGACC_COMM_IF_GETC_TIMEOUT (*xyz.__chan, (char *) &xyz.blk); + ZM_DEBUG (zm_save (xyz.blk)); + if (!res) + { + ZM_DEBUG (zm_dump (__LINE__)); + return xyzModem_timeout; + } + res = CYGACC_COMM_IF_GETC_TIMEOUT (*xyz.__chan, (char *) &xyz.cblk); + ZM_DEBUG (zm_save (xyz.cblk)); + if (!res) + { + ZM_DEBUG (zm_dump (__LINE__)); + return xyzModem_timeout; + } + xyz.len = (c == SOH) ? 128 : 1024; + xyz.bufp = xyz.pkt; + for (i = 0; i < xyz.len; i++) + { + res = CYGACC_COMM_IF_GETC_TIMEOUT (*xyz.__chan, &c); + ZM_DEBUG (zm_save (c)); + if (res) + { + xyz.pkt[i] = c; + } + else + { + ZM_DEBUG (zm_dump (__LINE__)); + return xyzModem_timeout; + } + } + res = CYGACC_COMM_IF_GETC_TIMEOUT (*xyz.__chan, (char *) &xyz.crc1); + ZM_DEBUG (zm_save (xyz.crc1)); + if (!res) + { + ZM_DEBUG (zm_dump (__LINE__)); + return xyzModem_timeout; + } + if (xyz.crc_mode) + { + res = CYGACC_COMM_IF_GETC_TIMEOUT (*xyz.__chan, (char *) &xyz.crc2); + ZM_DEBUG (zm_save (xyz.crc2)); + if (!res) + { + ZM_DEBUG (zm_dump (__LINE__)); + return xyzModem_timeout; + } + } + ZM_DEBUG (zm_dump (__LINE__)); + /* Validate the message */ + if ((xyz.blk ^ xyz.cblk) != (unsigned char) 0xFF) + { + ZM_DEBUG (zm_dprintf + ("Framing error - blk: %x/%x/%x\n", xyz.blk, xyz.cblk, + (xyz.blk ^ xyz.cblk))); + ZM_DEBUG (zm_dump_buf (xyz.pkt, xyz.len)); + xyzModem_flush (); + return xyzModem_frame; + } + /* Verify checksum/CRC */ + if (xyz.crc_mode) + { + cksum = cyg_crc16 (xyz.pkt, xyz.len); + if (cksum != ((xyz.crc1 << 8) | xyz.crc2)) + { + ZM_DEBUG (zm_dprintf ("CRC error - recvd: %02x%02x, computed: %x\n", + xyz.crc1, xyz.crc2, cksum & 0xFFFF)); + return xyzModem_cksum; + } + } + else + { + cksum = 0; + for (i = 0; i < xyz.len; i++) + { + cksum += xyz.pkt[i]; + } + if (xyz.crc1 != (cksum & 0xFF)) + { + ZM_DEBUG (zm_dprintf + ("Checksum error - recvd: %x, computed: %x\n", xyz.crc1, + cksum & 0xFF)); + return xyzModem_cksum; + } + } + /* If we get here, the message passes [structural] muster */ + return 0; +} + +int +xyzModem_stream_open (connection_info_t * info, int *err) +{ +#ifdef REDBOOT + int console_chan; +#endif + int stat = 0; + int retries = xyzModem_MAX_RETRIES; + int crc_retries = xyzModem_MAX_RETRIES_WITH_CRC; + +/* ZM_DEBUG(zm_out = zm_out_start); */ +#ifdef xyzModem_zmodem + if (info->mode == xyzModem_zmodem) + { + *err = xyzModem_noZmodem; + return -1; + } +#endif + +#ifdef REDBOOT + /* Set up the I/O channel. Note: this allows for using a different port in the future */ + console_chan = + CYGACC_CALL_IF_SET_CONSOLE_COMM + (CYGNUM_CALL_IF_SET_COMM_ID_QUERY_CURRENT); + if (info->chan >= 0) + { + CYGACC_CALL_IF_SET_CONSOLE_COMM (info->chan); + } + else + { + CYGACC_CALL_IF_SET_CONSOLE_COMM (console_chan); + } + xyz.__chan = CYGACC_CALL_IF_CONSOLE_PROCS (); + + CYGACC_CALL_IF_SET_CONSOLE_COMM (console_chan); + CYGACC_COMM_IF_CONTROL (*xyz.__chan, __COMMCTL_SET_TIMEOUT, + xyzModem_CHAR_TIMEOUT); +#else +/* TODO: CHECK ! */ + int dummy = 0; + xyz.__chan = &dummy; +#endif + xyz.len = 0; + xyz.crc_mode = true; + xyz.at_eof = false; + xyz.tx_ack = false; + xyz.mode = info->mode; + xyz.total_retries = 0; + xyz.total_SOH = 0; + xyz.total_STX = 0; + xyz.total_CAN = 0; +#ifdef USE_YMODEM_LENGTH + xyz.read_length = 0; + xyz.file_length = 0; +#endif + + CYGACC_COMM_IF_PUTC (*xyz.__chan, (xyz.crc_mode ? 'C' : NAK)); + + if (xyz.mode == xyzModem_xmodem) + { + /* X-modem doesn't have an information header - exit here */ + xyz.next_blk = 1; + return 0; + } + + while (retries-- > 0) + { + stat = xyzModem_get_hdr (); + if (stat == 0) + { + /* Y-modem file information header */ + if (xyz.blk == 0) + { +#ifdef USE_YMODEM_LENGTH + /* skip filename */ + while (*xyz.bufp++); + /* get the length */ + parse_num ((char *) xyz.bufp, &xyz.file_length, NULL, " "); +#endif + /* The rest of the file name data block quietly discarded */ + xyz.tx_ack = true; + } + xyz.next_blk = 1; + xyz.len = 0; + return 0; + } + else if (stat == xyzModem_timeout) + { + if (--crc_retries <= 0) + xyz.crc_mode = false; + CYGACC_CALL_IF_DELAY_US (5 * 100000); /* Extra delay for startup */ + CYGACC_COMM_IF_PUTC (*xyz.__chan, (xyz.crc_mode ? 'C' : NAK)); + xyz.total_retries++; + ZM_DEBUG (zm_dprintf ("NAK (%d)\n", __LINE__)); + } + if (stat == xyzModem_cancel) + { + break; + } + } + *err = stat; + ZM_DEBUG (zm_flush ()); + return -1; +} + +int +xyzModem_stream_read (char *buf, int size, int *err) +{ + int stat, total, len; + int retries; + + total = 0; + stat = xyzModem_cancel; + /* Try and get 'size' bytes into the buffer */ + while (!xyz.at_eof && (size > 0)) + { + if (xyz.len == 0) + { + retries = xyzModem_MAX_RETRIES; + while (retries-- > 0) + { + stat = xyzModem_get_hdr (); + if (stat == 0) + { + if (xyz.blk == xyz.next_blk) + { + xyz.tx_ack = true; + ZM_DEBUG (zm_dprintf + ("ACK block %d (%d)\n", xyz.blk, __LINE__)); + xyz.next_blk = (xyz.next_blk + 1) & 0xFF; + +#if defined(xyzModem_zmodem) || defined(USE_YMODEM_LENGTH) + if (xyz.mode == xyzModem_xmodem || xyz.file_length == 0) + { +#else + if (1) + { +#endif + /* Data blocks can be padded with ^Z (EOF) characters */ + /* This code tries to detect and remove them */ + if ((xyz.bufp[xyz.len - 1] == EOF) && + (xyz.bufp[xyz.len - 2] == EOF) && + (xyz.bufp[xyz.len - 3] == EOF)) + { + while (xyz.len + && (xyz.bufp[xyz.len - 1] == EOF)) + { + xyz.len--; + } + } + } + +#ifdef USE_YMODEM_LENGTH + /* + * See if accumulated length exceeds that of the file. + * If so, reduce size (i.e., cut out pad bytes) + * Only do this for Y-modem (and Z-modem should it ever + * be supported since it can fall back to Y-modem mode). + */ + if (xyz.mode != xyzModem_xmodem && 0 != xyz.file_length) + { + xyz.read_length += xyz.len; + if (xyz.read_length > xyz.file_length) + { + xyz.len -= (xyz.read_length - xyz.file_length); + } + } +#endif + break; + } + else if (xyz.blk == ((xyz.next_blk - 1) & 0xFF)) + { + /* Just re-ACK this so sender will get on with it */ + CYGACC_COMM_IF_PUTC (*xyz.__chan, ACK); + continue; /* Need new header */ + } + else + { + stat = xyzModem_sequence; + } + } + if (stat == xyzModem_cancel) + { + break; + } + if (stat == xyzModem_eof) + { + CYGACC_COMM_IF_PUTC (*xyz.__chan, ACK); + ZM_DEBUG (zm_dprintf ("ACK (%d)\n", __LINE__)); + if (xyz.mode == xyzModem_ymodem) + { + CYGACC_COMM_IF_PUTC (*xyz.__chan, + (xyz.crc_mode ? 'C' : NAK)); + xyz.total_retries++; + ZM_DEBUG (zm_dprintf ("Reading Final Header\n")); + stat = xyzModem_get_hdr (); + CYGACC_COMM_IF_PUTC (*xyz.__chan, ACK); + ZM_DEBUG (zm_dprintf ("FINAL ACK (%d)\n", __LINE__)); + } + xyz.at_eof = true; + break; + } + CYGACC_COMM_IF_PUTC (*xyz.__chan, (xyz.crc_mode ? 'C' : NAK)); + xyz.total_retries++; + ZM_DEBUG (zm_dprintf ("NAK (%d)\n", __LINE__)); + } + if (stat < 0) + { + *err = stat; + xyz.len = -1; + return total; + } + } + /* Don't "read" data from the EOF protocol package */ + if (!xyz.at_eof) + { + len = xyz.len; + if (size < len) + len = size; + memcpy (buf, xyz.bufp, len); + size -= len; + buf += len; + total += len; + xyz.len -= len; + xyz.bufp += len; + } + } + return total; +} + +void +xyzModem_stream_close (int *err) +{ + diag_printf + ("xyzModem - %s mode, %d(SOH)/%d(STX)/%d(CAN) packets, %d retries\n", + xyz.crc_mode ? "CRC" : "Cksum", xyz.total_SOH, xyz.total_STX, + xyz.total_CAN, xyz.total_retries); + ZM_DEBUG (zm_flush ()); +} + +/* Need to be able to clean out the input buffer, so have to take the */ +/* getc */ +void +xyzModem_stream_terminate (bool abort, int (*getc) (void)) +{ + int c; + + if (abort) + { + ZM_DEBUG (zm_dprintf ("!!!! TRANSFER ABORT !!!!\n")); + switch (xyz.mode) + { + case xyzModem_xmodem: + case xyzModem_ymodem: + /* The X/YMODEM Spec seems to suggest that multiple CAN followed by an equal */ + /* number of Backspaces is a friendly way to get the other end to abort. */ + CYGACC_COMM_IF_PUTC (*xyz.__chan, CAN); + CYGACC_COMM_IF_PUTC (*xyz.__chan, CAN); + CYGACC_COMM_IF_PUTC (*xyz.__chan, CAN); + CYGACC_COMM_IF_PUTC (*xyz.__chan, CAN); + CYGACC_COMM_IF_PUTC (*xyz.__chan, BSP); + CYGACC_COMM_IF_PUTC (*xyz.__chan, BSP); + CYGACC_COMM_IF_PUTC (*xyz.__chan, BSP); + CYGACC_COMM_IF_PUTC (*xyz.__chan, BSP); + /* Now consume the rest of what's waiting on the line. */ + ZM_DEBUG (zm_dprintf ("Flushing serial line.\n")); + xyzModem_flush (); + xyz.at_eof = true; + break; +#ifdef xyzModem_zmodem + case xyzModem_zmodem: + /* Might support it some day I suppose. */ +#endif + break; + } + } + else + { + ZM_DEBUG (zm_dprintf ("Engaging cleanup mode...\n")); + /* + * Consume any trailing crap left in the inbuffer from + * previous received blocks. Since very few files are an exact multiple + * of the transfer block size, there will almost always be some gunk here. + * If we don't eat it now, RedBoot will think the user typed it. + */ + ZM_DEBUG (zm_dprintf ("Trailing gunk:\n")); + while ((c = (*getc) ()) > -1); + ZM_DEBUG (zm_dprintf ("\n")); + /* + * Make a small delay to give terminal programs like minicom + * time to get control again after their file transfer program + * exits. + */ + CYGACC_CALL_IF_DELAY_US ((cyg_int32) 250000); + } +} + +char * +xyzModem_error (int err) +{ + switch (err) + { + case xyzModem_access: + return "Can't access file"; + break; + case xyzModem_noZmodem: + return "Sorry, zModem not available yet"; + break; + case xyzModem_timeout: + return "Timed out"; + break; + case xyzModem_eof: + return "End of file"; + break; + case xyzModem_cancel: + return "Cancelled"; + break; + case xyzModem_frame: + return "Invalid framing"; + break; + case xyzModem_cksum: + return "CRC/checksum error"; + break; + case xyzModem_sequence: + return "Block sequence error"; + break; + default: + return "Unknown error"; + break; + } +} + +/* + * RedBoot interface + */ +#if 0 /* SB */ +GETC_IO_FUNCS (xyzModem_io, xyzModem_stream_open, xyzModem_stream_close, + xyzModem_stream_terminate, xyzModem_stream_read, + xyzModem_error); +RedBoot_load (xmodem, xyzModem_io, false, false, xyzModem_xmodem); +RedBoot_load (ymodem, xyzModem_io, false, false, xyzModem_ymodem); +#endif |