From 29aa2134931761675bfe4652bd74283ead81c36f Mon Sep 17 00:00:00 2001 From: "joachim.schmidt" <joachim.schmidt@hesge.ch> Date: Wed, 24 Mar 2021 09:46:43 +0100 Subject: [PATCH] Added U-Boot tools. --- tools/u-boot-tools/Makefile | 280 ++ tools/u-boot-tools/aisimage.c | 428 ++ tools/u-boot-tools/aisimage.h | 80 + tools/u-boot-tools/aisimage.o | Bin 0 -> 11232 bytes tools/u-boot-tools/atmel_pmecc_params.c | 50 + tools/u-boot-tools/atmelimage.c | 339 ++ tools/u-boot-tools/atmelimage.o | Bin 0 -> 8736 bytes tools/u-boot-tools/binman/.gitignore | 1 + tools/u-boot-tools/binman/README | 780 ++++ tools/u-boot-tools/binman/README.entries | 698 ++++ tools/u-boot-tools/binman/binman | 1 + tools/u-boot-tools/binman/binman.py | 157 + tools/u-boot-tools/binman/bsection.py | 464 +++ tools/u-boot-tools/binman/cmdline.py | 66 + tools/u-boot-tools/binman/control.py | 195 + tools/u-boot-tools/binman/elf.py | 130 + tools/u-boot-tools/binman/elf_test.py | 140 + tools/u-boot-tools/binman/entry.py | 532 +++ tools/u-boot-tools/binman/entry_test.py | 84 + tools/u-boot-tools/binman/etype/_testing.py | 100 + tools/u-boot-tools/binman/etype/blob.py | 78 + tools/u-boot-tools/binman/etype/blob_dtb.py | 33 + .../binman/etype/blob_named_by_arg.py | 34 + tools/u-boot-tools/binman/etype/cros_ec_rw.py | 22 + tools/u-boot-tools/binman/etype/files.py | 57 + tools/u-boot-tools/binman/etype/fill.py | 32 + tools/u-boot-tools/binman/etype/fmap.py | 64 + tools/u-boot-tools/binman/etype/gbb.py | 96 + tools/u-boot-tools/binman/etype/intel_cmc.py | 23 + .../binman/etype/intel_descriptor.py | 63 + tools/u-boot-tools/binman/etype/intel_fsp.py | 27 + tools/u-boot-tools/binman/etype/intel_me.py | 28 + tools/u-boot-tools/binman/etype/intel_mrc.py | 27 + .../binman/etype/intel_refcode.py | 27 + tools/u-boot-tools/binman/etype/intel_vbt.py | 22 + tools/u-boot-tools/binman/etype/intel_vga.py | 25 + .../etype/powerpc_mpc85xx_bootpg_resetvec.py | 25 + tools/u-boot-tools/binman/etype/section.py | 106 + tools/u-boot-tools/binman/etype/text.py | 60 + tools/u-boot-tools/binman/etype/u_boot.py | 32 + tools/u-boot-tools/binman/etype/u_boot_dtb.py | 28 + .../binman/etype/u_boot_dtb_with_ucode.py | 86 + tools/u-boot-tools/binman/etype/u_boot_elf.py | 38 + tools/u-boot-tools/binman/etype/u_boot_img.py | 27 + .../u-boot-tools/binman/etype/u_boot_nodtb.py | 27 + tools/u-boot-tools/binman/etype/u_boot_spl.py | 43 + .../binman/etype/u_boot_spl_bss_pad.py | 42 + .../binman/etype/u_boot_spl_dtb.py | 25 + .../binman/etype/u_boot_spl_elf.py | 24 + .../binman/etype/u_boot_spl_nodtb.py | 28 + .../binman/etype/u_boot_spl_with_ucode_ptr.py | 29 + tools/u-boot-tools/binman/etype/u_boot_tpl.py | 43 + .../binman/etype/u_boot_tpl_dtb.py | 25 + .../binman/etype/u_boot_tpl_dtb_with_ucode.py | 25 + .../binman/etype/u_boot_tpl_with_ucode_ptr.py | 27 + .../u-boot-tools/binman/etype/u_boot_ucode.py | 99 + .../binman/etype/u_boot_with_ucode_ptr.py | 96 + tools/u-boot-tools/binman/etype/vblock.py | 79 + .../u-boot-tools/binman/etype/x86_start16.py | 30 + .../binman/etype/x86_start16_spl.py | 30 + .../binman/etype/x86_start16_tpl.py | 30 + tools/u-boot-tools/binman/fdt_test.py | 86 + tools/u-boot-tools/binman/fmap_util.py | 113 + tools/u-boot-tools/binman/ftest.py | 1776 +++++++++ tools/u-boot-tools/binman/image.py | 153 + tools/u-boot-tools/binman/image_test.py | 48 + tools/u-boot-tools/binman/state.py | 253 ++ .../u-boot-tools/binman/test/001_invalid.dts | 5 + .../binman/test/002_missing_node.dts | 6 + tools/u-boot-tools/binman/test/003_empty.dts | 9 + .../binman/test/004_invalid_entry.dts | 11 + tools/u-boot-tools/binman/test/005_simple.dts | 11 + .../binman/test/006_dual_image.dts | 22 + .../binman/test/007_bad_align.dts | 12 + tools/u-boot-tools/binman/test/008_pack.dts | 30 + .../binman/test/009_pack_extra.dts | 35 + .../binman/test/010_pack_align_power2.dts | 12 + .../test/011_pack_align_size_power2.dts | 12 + .../binman/test/012_pack_inv_align.dts | 13 + .../binman/test/013_pack_inv_size_align.dts | 13 + .../binman/test/014_pack_overlap.dts | 16 + .../binman/test/015_pack_overflow.dts | 12 + .../binman/test/016_pack_image_overflow.dts | 13 + .../binman/test/017_pack_image_size.dts | 13 + .../binman/test/018_pack_image_align.dts | 13 + .../binman/test/019_pack_inv_image_align.dts | 14 + .../test/020_pack_inv_image_align_power2.dts | 13 + .../binman/test/021_image_pad.dts | 16 + .../binman/test/022_image_name.dts | 21 + tools/u-boot-tools/binman/test/023_blob.dts | 12 + tools/u-boot-tools/binman/test/024_sorted.dts | 17 + .../binman/test/025_pack_zero_size.dts | 15 + .../binman/test/026_pack_u_boot_dtb.dts | 14 + .../binman/test/027_pack_4gb_no_size.dts | 18 + .../binman/test/028_pack_4gb_outside.dts | 19 + .../u-boot-tools/binman/test/029_x86-rom.dts | 19 + .../binman/test/030_x86-rom-me-no-desc.dts | 16 + .../binman/test/031_x86-rom-me.dts | 20 + .../binman/test/032_intel-vga.dts | 14 + .../binman/test/033_x86-start16.dts | 13 + .../binman/test/034_x86_ucode.dts | 29 + .../binman/test/035_x86_single_ucode.dts | 26 + .../binman/test/036_u_boot_img.dts | 11 + .../binman/test/037_x86_no_ucode.dts | 20 + .../test/038_x86_ucode_missing_node.dts | 26 + .../test/039_x86_ucode_missing_node2.dts | 23 + .../test/040_x86_ucode_not_in_image.dts | 28 + .../binman/test/041_unknown_pos_size.dts | 12 + .../binman/test/042_intel-fsp.dts | 14 + .../binman/test/043_intel-cmc.dts | 14 + .../binman/test/044_x86_optional_ucode.dts | 30 + .../binman/test/045_prop_test.dts | 23 + .../binman/test/046_intel-vbt.dts | 14 + .../binman/test/047_spl_bss_pad.dts | 17 + .../binman/test/048_x86-start16-spl.dts | 13 + .../binman/test/049_x86_ucode_spl.dts | 29 + .../binman/test/050_intel_mrc.dts | 13 + .../binman/test/051_u_boot_spl_dtb.dts | 13 + .../binman/test/052_u_boot_spl_nodtb.dts | 11 + .../u-boot-tools/binman/test/053_symbols.dts | 20 + .../binman/test/054_unit_address.dts | 15 + .../u-boot-tools/binman/test/055_sections.dts | 32 + .../binman/test/056_name_prefix.dts | 30 + .../binman/test/057_unknown_contents.dts | 14 + .../test/058_x86_ucode_spl_needs_retry.dts | 36 + .../binman/test/059_change_size.dts | 14 + .../binman/test/060_fdt_update.dts | 31 + .../binman/test/061_fdt_update_bad.dts | 32 + .../binman/test/062_entry_args.dts | 14 + .../binman/test/063_entry_args_missing.dts | 13 + .../binman/test/064_entry_args_required.dts | 14 + .../test/065_entry_args_unknown_datatype.dts | 15 + tools/u-boot-tools/binman/test/066_text.dts | 28 + tools/u-boot-tools/binman/test/067_fmap.dts | 29 + .../binman/test/068_blob_named_by_arg.dts | 12 + tools/u-boot-tools/binman/test/069_fill.dts | 15 + .../binman/test/070_fill_no_size.dts | 14 + tools/u-boot-tools/binman/test/071_gbb.dts | 31 + .../binman/test/072_gbb_too_small.dts | 10 + .../binman/test/073_gbb_no_size.dts | 9 + tools/u-boot-tools/binman/test/074_vblock.dts | 28 + .../binman/test/075_vblock_no_content.dts | 23 + .../binman/test/076_vblock_bad_phandle.dts | 24 + .../binman/test/077_vblock_bad_entry.dts | 27 + .../binman/test/078_u_boot_tpl.dts | 11 + .../u-boot-tools/binman/test/079_uses_pos.dts | 10 + .../binman/test/080_fill_empty.dts | 15 + .../binman/test/081_x86-start16-tpl.dts | 14 + .../binman/test/082_fdt_update_all.dts | 18 + .../u-boot-tools/binman/test/083_compress.dts | 11 + tools/u-boot-tools/binman/test/084_files.dts | 11 + .../binman/test/085_files_compress.dts | 11 + .../binman/test/086_files_none.dts | 12 + .../binman/test/087_files_no_pattern.dts | 11 + .../binman/test/088_expand_size.dts | 43 + .../binman/test/089_expand_size_bad.dts | 14 + tools/u-boot-tools/binman/test/090_hash.dts | 12 + .../binman/test/091_hash_no_algo.dts | 11 + .../binman/test/092_hash_bad_algo.dts | 12 + .../binman/test/093_x86_tpl_ucode.dts | 29 + .../u-boot-tools/binman/test/094_fmap_x86.dts | 20 + .../binman/test/095_fmap_x86_section.dts | 22 + tools/u-boot-tools/binman/test/096_elf.dts | 14 + .../binman/test/097_elf_strip.dts | 15 + .../binman/test/099_hash_section.dts | 18 + .../binman/test/100_intel_refcode.dts | 14 + .../80_4gb_and_skip_at_start_together.dts | 21 + .../81_powerpc_mpc85xx_bootpg_resetvec.dts | 16 + tools/u-boot-tools/binman/test/Makefile | 55 + tools/u-boot-tools/binman/test/bss_data | Bin 0 -> 5020 bytes tools/u-boot-tools/binman/test/bss_data.c | 17 + tools/u-boot-tools/binman/test/bss_data.lds | 15 + tools/u-boot-tools/binman/test/files/1.dat | 1 + tools/u-boot-tools/binman/test/files/2.dat | 1 + .../binman/test/files/ignored_dir.dat/ignore | 0 .../binman/test/files/not-this-one | 1 + .../binman/test/u_boot_binman_syms | Bin 0 -> 4924 bytes .../binman/test/u_boot_binman_syms.c | 13 + .../binman/test/u_boot_binman_syms.lds | 29 + .../binman/test/u_boot_binman_syms_bad | Bin 0 -> 4890 bytes .../binman/test/u_boot_binman_syms_bad.c | 1 + .../binman/test/u_boot_binman_syms_bad.lds | 28 + .../binman/test/u_boot_binman_syms_size | Bin 0 -> 4825 bytes .../binman/test/u_boot_binman_syms_size.c | 11 + .../binman/test/u_boot_no_ucode_ptr | Bin 0 -> 4182 bytes .../binman/test/u_boot_no_ucode_ptr.c | 10 + .../u-boot-tools/binman/test/u_boot_ucode_ptr | Bin 0 -> 4175 bytes .../binman/test/u_boot_ucode_ptr.c | 10 + .../binman/test/u_boot_ucode_ptr.lds | 17 + tools/u-boot-tools/bmp_logo.c | 208 + tools/u-boot-tools/buildman/.gitignore | 1 + tools/u-boot-tools/buildman/README | 1187 ++++++ tools/u-boot-tools/buildman/board.py | 309 ++ tools/u-boot-tools/buildman/bsettings.py | 97 + tools/u-boot-tools/buildman/builder.py | 1582 ++++++++ tools/u-boot-tools/buildman/builderthread.py | 493 +++ tools/u-boot-tools/buildman/buildman | 1 + tools/u-boot-tools/buildman/buildman.py | 66 + tools/u-boot-tools/buildman/cmdline.py | 111 + tools/u-boot-tools/buildman/control.py | 347 ++ tools/u-boot-tools/buildman/func_test.py | 535 +++ tools/u-boot-tools/buildman/kconfiglib.py | 3544 +++++++++++++++++ tools/u-boot-tools/buildman/test.py | 464 +++ tools/u-boot-tools/buildman/toolchain.py | 577 +++ tools/u-boot-tools/common/.bootm.o.cmd | 206 + tools/u-boot-tools/common/.hash.o.cmd | 180 + tools/u-boot-tools/common/.image-fit.o.cmd | 178 + tools/u-boot-tools/common/.image.o.cmd | 189 + tools/u-boot-tools/common/bootm.c | 1 + tools/u-boot-tools/common/bootm.o | Bin 0 -> 4448 bytes tools/u-boot-tools/common/hash.c | 1 + tools/u-boot-tools/common/hash.o | Bin 0 -> 5392 bytes tools/u-boot-tools/common/image-fit.c | 1 + tools/u-boot-tools/common/image-fit.o | Bin 0 -> 31696 bytes tools/u-boot-tools/common/image.c | 1 + tools/u-boot-tools/common/image.o | Bin 0 -> 21168 bytes tools/u-boot-tools/concurrencytest/.gitignore | 1 + tools/u-boot-tools/concurrencytest/README.md | 74 + .../concurrencytest/concurrencytest.py | 144 + tools/u-boot-tools/default_image.c | 184 + tools/u-boot-tools/default_image.o | Bin 0 -> 4192 bytes tools/u-boot-tools/dtoc/.gitignore | 1 + tools/u-boot-tools/dtoc/dtb_platdata.py | 576 +++ tools/u-boot-tools/dtoc/dtoc | 1 + tools/u-boot-tools/dtoc/dtoc.py | 109 + tools/u-boot-tools/dtoc/dtoc_test.dts | 11 + .../u-boot-tools/dtoc/dtoc_test_add_prop.dts | 24 + tools/u-boot-tools/dtoc/dtoc_test_addr32.dts | 26 + .../u-boot-tools/dtoc/dtoc_test_addr32_64.dts | 32 + tools/u-boot-tools/dtoc/dtoc_test_addr64.dts | 32 + .../u-boot-tools/dtoc/dtoc_test_addr64_32.dts | 32 + tools/u-boot-tools/dtoc/dtoc_test_aliases.dts | 17 + tools/u-boot-tools/dtoc/dtoc_test_bad_reg.dts | 17 + .../u-boot-tools/dtoc/dtoc_test_bad_reg2.dts | 17 + tools/u-boot-tools/dtoc/dtoc_test_empty.dts | 11 + tools/u-boot-tools/dtoc/dtoc_test_phandle.dts | 42 + .../dtoc/dtoc_test_phandle_bad.dts | 16 + .../dtoc/dtoc_test_phandle_bad2.dts | 22 + .../dtoc/dtoc_test_phandle_reorder.dts | 23 + .../dtoc/dtoc_test_phandle_single.dts | 23 + tools/u-boot-tools/dtoc/dtoc_test_simple.dts | 62 + tools/u-boot-tools/dtoc/fdt.py | 657 +++ tools/u-boot-tools/dtoc/fdt_util.py | 211 + tools/u-boot-tools/dtoc/test_dtoc.py | 710 ++++ tools/u-boot-tools/dtoc/test_fdt | 1 + tools/u-boot-tools/dtoc/test_fdt.py | 562 +++ tools/u-boot-tools/dumpimage | Bin 0 -> 242488 bytes tools/u-boot-tools/dumpimage.c | 210 + tools/u-boot-tools/dumpimage.h | 32 + tools/u-boot-tools/dumpimage.o | Bin 0 -> 8056 bytes tools/u-boot-tools/easylogo/Makefile | 5 + tools/u-boot-tools/easylogo/easylogo.c | 610 +++ .../u-boot-tools/easylogo/linux_blackfin.tga | Bin 0 -> 158718 bytes tools/u-boot-tools/easylogo/linux_logo.tga | Bin 0 -> 19244 bytes tools/u-boot-tools/easylogo/runme.sh | 4 + tools/u-boot-tools/env/.embedded.o.cmd | 287 ++ tools/u-boot-tools/env/.gitignore | 3 + tools/u-boot-tools/env/Makefile | 37 + tools/u-boot-tools/env/README | 63 + tools/u-boot-tools/env/crc32.c | 1 + tools/u-boot-tools/env/ctype.c | 1 + tools/u-boot-tools/env/embedded.c | 1 + tools/u-boot-tools/env/embedded.o | Bin 0 -> 928 bytes tools/u-boot-tools/env/env_attr.c | 1 + tools/u-boot-tools/env/env_flags.c | 1 + tools/u-boot-tools/env/fw_env.c | 1802 +++++++++ tools/u-boot-tools/env/fw_env.config | 38 + tools/u-boot-tools/env/fw_env.h | 163 + tools/u-boot-tools/env/fw_env_main.c | 283 ++ tools/u-boot-tools/env/fw_env_private.h | 54 + tools/u-boot-tools/env/linux_string.c | 1 + tools/u-boot-tools/envcrc | Bin 0 -> 25480 bytes tools/u-boot-tools/envcrc.c | 130 + tools/u-boot-tools/envcrc.o | Bin 0 -> 1656 bytes tools/u-boot-tools/fdt_host.h | 32 + tools/u-boot-tools/fdtgrep | Bin 0 -> 68744 bytes tools/u-boot-tools/fdtgrep.c | 1236 ++++++ tools/u-boot-tools/fdtgrep.o | Bin 0 -> 28544 bytes tools/u-boot-tools/file2include.c | 103 + tools/u-boot-tools/fit_check_sign.c | 96 + tools/u-boot-tools/fit_common.c | 106 + tools/u-boot-tools/fit_common.h | 32 + tools/u-boot-tools/fit_common.o | Bin 0 -> 3800 bytes tools/u-boot-tools/fit_image.c | 835 ++++ tools/u-boot-tools/fit_image.o | Bin 0 -> 20672 bytes tools/u-boot-tools/fit_info.c | 108 + tools/u-boot-tools/gdb/Makefile | 44 + tools/u-boot-tools/gdb/error.c | 64 + tools/u-boot-tools/gdb/error.h | 13 + tools/u-boot-tools/gdb/gdbcont.c | 70 + tools/u-boot-tools/gdb/gdbsend.c | 120 + tools/u-boot-tools/gdb/remote.c | 915 +++++ tools/u-boot-tools/gdb/remote.h | 11 + tools/u-boot-tools/gdb/serial.c | 132 + tools/u-boot-tools/gdb/serial.h | 17 + tools/u-boot-tools/gen_eth_addr | Bin 0 -> 16800 bytes tools/u-boot-tools/gen_eth_addr.c | 33 + tools/u-boot-tools/gen_ethaddr_crc | Bin 0 -> 17064 bytes tools/u-boot-tools/gen_ethaddr_crc.c | 75 + tools/u-boot-tools/gen_ethaddr_crc.o | Bin 0 -> 3792 bytes tools/u-boot-tools/genboardscfg.py | 447 +++ tools/u-boot-tools/getline.c | 89 + tools/u-boot-tools/getline.h | 1 + tools/u-boot-tools/gpheader.h | 39 + tools/u-boot-tools/gpimage-common.c | 80 + tools/u-boot-tools/gpimage-common.o | Bin 0 -> 2824 bytes tools/u-boot-tools/gpimage.c | 75 + tools/u-boot-tools/gpimage.o | Bin 0 -> 2808 bytes tools/u-boot-tools/ifdtool.c | 1109 ++++++ tools/u-boot-tools/ifdtool.h | 89 + tools/u-boot-tools/image-host.c | 754 ++++ tools/u-boot-tools/image-host.o | Bin 0 -> 4360 bytes tools/u-boot-tools/imagetool.c | 136 + tools/u-boot-tools/imagetool.h | 309 ++ tools/u-boot-tools/imagetool.o | Bin 0 -> 4688 bytes tools/u-boot-tools/img2brec.sh | 388 ++ tools/u-boot-tools/img2srec | Bin 0 -> 17280 bytes tools/u-boot-tools/img2srec.c | 372 ++ tools/u-boot-tools/imx8image.c | 992 +++++ tools/u-boot-tools/imx8image.o | Bin 0 -> 30048 bytes tools/u-boot-tools/imx8m_image.sh | 43 + tools/u-boot-tools/imx8mimage.c | 623 +++ tools/u-boot-tools/imx8mimage.o | Bin 0 -> 21728 bytes tools/u-boot-tools/imx_cntr_image.sh | 29 + tools/u-boot-tools/imximage.c | 996 +++++ tools/u-boot-tools/imximage.o | Bin 0 -> 23240 bytes tools/u-boot-tools/jtagconsole | 39 + tools/u-boot-tools/k3_fit_atf.sh | 99 + tools/u-boot-tools/k3_x509template.txt | 48 + tools/u-boot-tools/kermit/README | 49 + tools/u-boot-tools/kermit/dot.kermrc | 16 + tools/u-boot-tools/kermit/flash_param | 60 + tools/u-boot-tools/kermit/send_cmd | 21 + tools/u-boot-tools/kermit/send_image | 26 + tools/u-boot-tools/kwbimage.c | 1757 ++++++++ tools/u-boot-tools/kwbimage.h | 202 + tools/u-boot-tools/kwbimage.o | Bin 0 -> 19992 bytes tools/u-boot-tools/kwboot.c | 867 ++++ tools/u-boot-tools/lib/.crc16.o.cmd | 105 + tools/u-boot-tools/lib/.crc32.o.cmd | 110 + tools/u-boot-tools/lib/.crc8.o.cmd | 93 + tools/u-boot-tools/lib/.fdtdec.o.cmd | 113 + tools/u-boot-tools/lib/.fdtdec_common.o.cmd | 108 + tools/u-boot-tools/lib/.md5.o.cmd | 96 + tools/u-boot-tools/lib/.rc4.o.cmd | 93 + tools/u-boot-tools/lib/.sha1.o.cmd | 97 + tools/u-boot-tools/lib/.sha256.o.cmd | 97 + tools/u-boot-tools/lib/crc16.c | 1 + tools/u-boot-tools/lib/crc16.o | Bin 0 -> 2208 bytes tools/u-boot-tools/lib/crc32.c | 1 + tools/u-boot-tools/lib/crc32.o | Bin 0 -> 3088 bytes tools/u-boot-tools/lib/crc8.c | 1 + tools/u-boot-tools/lib/crc8.o | Bin 0 -> 1256 bytes tools/u-boot-tools/lib/fdtdec.c | 1 + tools/u-boot-tools/lib/fdtdec.o | Bin 0 -> 920 bytes tools/u-boot-tools/lib/fdtdec_common.c | 1 + tools/u-boot-tools/lib/fdtdec_common.o | Bin 0 -> 1696 bytes .../u-boot-tools/lib/libfdt/.fdt_region.o.cmd | 112 + tools/u-boot-tools/lib/libfdt/.fdt_ro.o.cmd | 115 + tools/u-boot-tools/lib/libfdt/fdt_region.c | 1 + tools/u-boot-tools/lib/libfdt/fdt_region.o | Bin 0 -> 7808 bytes tools/u-boot-tools/lib/libfdt/fdt_ro.c | 1 + tools/u-boot-tools/lib/libfdt/fdt_ro.o | Bin 0 -> 12560 bytes tools/u-boot-tools/lib/md5.c | 1 + tools/u-boot-tools/lib/md5.o | Bin 0 -> 4304 bytes tools/u-boot-tools/lib/rc4.c | 1 + tools/u-boot-tools/lib/rc4.o | Bin 0 -> 1440 bytes tools/u-boot-tools/lib/sha1.c | 1 + tools/u-boot-tools/lib/sha1.o | Bin 0 -> 13024 bytes tools/u-boot-tools/lib/sha256.c | 1 + tools/u-boot-tools/lib/sha256.o | Bin 0 -> 14000 bytes tools/u-boot-tools/libfdt/.fdt.o.cmd | 114 + .../u-boot-tools/libfdt/.fdt_addresses.o.cmd | 114 + .../u-boot-tools/libfdt/.fdt_empty_tree.o.cmd | 114 + tools/u-boot-tools/libfdt/.fdt_overlay.o.cmd | 114 + tools/u-boot-tools/libfdt/.fdt_rw.o.cmd | 114 + tools/u-boot-tools/libfdt/.fdt_strerror.o.cmd | 114 + tools/u-boot-tools/libfdt/.fdt_sw.o.cmd | 114 + tools/u-boot-tools/libfdt/.fdt_wip.o.cmd | 114 + tools/u-boot-tools/libfdt/fdt.c | 2 + tools/u-boot-tools/libfdt/fdt.o | Bin 0 -> 4728 bytes tools/u-boot-tools/libfdt/fdt_addresses.c | 2 + tools/u-boot-tools/libfdt/fdt_addresses.o | Bin 0 -> 1976 bytes tools/u-boot-tools/libfdt/fdt_empty_tree.c | 2 + tools/u-boot-tools/libfdt/fdt_empty_tree.o | Bin 0 -> 2000 bytes tools/u-boot-tools/libfdt/fdt_overlay.c | 2 + tools/u-boot-tools/libfdt/fdt_overlay.o | Bin 0 -> 9128 bytes tools/u-boot-tools/libfdt/fdt_rw.c | 35 + tools/u-boot-tools/libfdt/fdt_rw.o | Bin 0 -> 10144 bytes tools/u-boot-tools/libfdt/fdt_strerror.c | 2 + tools/u-boot-tools/libfdt/fdt_strerror.o | Bin 0 -> 2768 bytes tools/u-boot-tools/libfdt/fdt_sw.c | 2 + tools/u-boot-tools/libfdt/fdt_sw.o | Bin 0 -> 5344 bytes tools/u-boot-tools/libfdt/fdt_wip.c | 2 + tools/u-boot-tools/libfdt/fdt_wip.o | Bin 0 -> 2800 bytes tools/u-boot-tools/logos/atmel.bmp | Bin 0 -> 15478 bytes tools/u-boot-tools/logos/compulab.bmp | Bin 0 -> 31810 bytes tools/u-boot-tools/logos/denx-comp.bmp | Bin 0 -> 4148 bytes tools/u-boot-tools/logos/denx.bmp | Bin 0 -> 15538 bytes tools/u-boot-tools/logos/engicam.bmp | Bin 0 -> 60214 bytes tools/u-boot-tools/logos/esd.bmp | Bin 0 -> 35078 bytes tools/u-boot-tools/logos/freescale.bmp | Bin 0 -> 46738 bytes tools/u-boot-tools/logos/gateworks.bmp | Bin 0 -> 56202 bytes tools/u-boot-tools/logos/intercontrol.bmp | Bin 0 -> 4998 bytes .../logos/linux_logo_ttcontrol.bmp | Bin 0 -> 11830 bytes .../logos/linux_logo_ttcontrol_palfin.bmp | Bin 0 -> 20534 bytes tools/u-boot-tools/logos/microchip.bmp | Bin 0 -> 12726 bytes tools/u-boot-tools/logos/ronetix.bmp | Bin 0 -> 5638 bytes tools/u-boot-tools/logos/siemens.bmp | Bin 0 -> 25766 bytes tools/u-boot-tools/logos/solidrun.bmp | Bin 0 -> 5558 bytes tools/u-boot-tools/logos/syteco.bmp | Bin 0 -> 11414 bytes tools/u-boot-tools/logos/toradex.bmp | Bin 0 -> 24982 bytes tools/u-boot-tools/logos/u-boot_logo.bmp | Bin 0 -> 25738 bytes tools/u-boot-tools/logos/u-boot_logo.svg | 248 ++ tools/u-boot-tools/logos/wandboard.bmp | Bin 0 -> 22390 bytes tools/u-boot-tools/lpc32xximage.c | 177 + tools/u-boot-tools/lpc32xximage.o | Bin 0 -> 3088 bytes tools/u-boot-tools/microcode-tool | 1 + tools/u-boot-tools/microcode-tool.py | 316 ++ tools/u-boot-tools/mingw_support.c | 113 + tools/u-boot-tools/mingw_support.h | 45 + tools/u-boot-tools/mips-relocs.c | 418 ++ tools/u-boot-tools/mkenvimage | Bin 0 -> 18120 bytes tools/u-boot-tools/mkenvimage.c | 314 ++ tools/u-boot-tools/mkenvimage.o | Bin 0 -> 10800 bytes tools/u-boot-tools/mkexynosspl.c | 186 + tools/u-boot-tools/mkimage | Bin 0 -> 242816 bytes tools/u-boot-tools/mkimage.c | 740 ++++ tools/u-boot-tools/mkimage.h | 47 + tools/u-boot-tools/mkimage.o | Bin 0 -> 21984 bytes tools/u-boot-tools/mksunxiboot.c | 177 + tools/u-boot-tools/moveconfig.py | 1885 +++++++++ tools/u-boot-tools/mrvl_uart.sh | 119 + tools/u-boot-tools/mtk_image.c | 749 ++++ tools/u-boot-tools/mtk_image.h | 199 + tools/u-boot-tools/mtk_image.o | Bin 0 -> 14752 bytes tools/u-boot-tools/mxsboot.c | 675 ++++ tools/u-boot-tools/mxsimage.c | 2365 +++++++++++ tools/u-boot-tools/mxsimage.h | 230 ++ tools/u-boot-tools/mxsimage.o | Bin 0 -> 928 bytes tools/u-boot-tools/ncb.c | 38 + tools/u-boot-tools/netconsole | 63 + tools/u-boot-tools/omap/clocks_get_m_n.c | 185 + tools/u-boot-tools/omapimage.c | 181 + tools/u-boot-tools/omapimage.h | 28 + tools/u-boot-tools/omapimage.o | Bin 0 -> 4248 bytes tools/u-boot-tools/os_support.c | 15 + tools/u-boot-tools/os_support.h | 22 + tools/u-boot-tools/os_support.o | Bin 0 -> 928 bytes tools/u-boot-tools/patman/.gitignore | 1 + tools/u-boot-tools/patman/README | 497 +++ tools/u-boot-tools/patman/__init__.py | 3 + tools/u-boot-tools/patman/checkpatch.py | 174 + tools/u-boot-tools/patman/command.py | 127 + tools/u-boot-tools/patman/commit.py | 87 + tools/u-boot-tools/patman/cros_subprocess.py | 397 ++ tools/u-boot-tools/patman/func_test.py | 242 ++ tools/u-boot-tools/patman/get_maintainer.py | 47 + tools/u-boot-tools/patman/gitutil.py | 597 +++ tools/u-boot-tools/patman/patchstream.py | 526 +++ tools/u-boot-tools/patman/patman | 1 + tools/u-boot-tools/patman/patman.py | 183 + tools/u-boot-tools/patman/project.py | 26 + tools/u-boot-tools/patman/series.py | 292 ++ tools/u-boot-tools/patman/settings.py | 356 ++ tools/u-boot-tools/patman/setup.py | 12 + tools/u-boot-tools/patman/terminal.py | 161 + tools/u-boot-tools/patman/test.py | 276 ++ tools/u-boot-tools/patman/test/test01.txt | 56 + tools/u-boot-tools/patman/test_util.py | 85 + tools/u-boot-tools/patman/tools.py | 241 ++ tools/u-boot-tools/patman/tout.py | 171 + tools/u-boot-tools/pbl_crc32.c | 55 + tools/u-boot-tools/pbl_crc32.h | 12 + tools/u-boot-tools/pbl_crc32.o | Bin 0 -> 1616 bytes tools/u-boot-tools/pblimage.c | 329 ++ tools/u-boot-tools/pblimage.h | 19 + tools/u-boot-tools/pblimage.o | Bin 0 -> 8520 bytes tools/u-boot-tools/prelink-riscv.c | 101 + tools/u-boot-tools/prelink-riscv.inc | 111 + tools/u-boot-tools/proftool | Bin 0 -> 22592 bytes tools/u-boot-tools/proftool.c | 599 +++ tools/u-boot-tools/relocate-rela.c | 157 + tools/u-boot-tools/rkcommon.c | 378 ++ tools/u-boot-tools/rkcommon.h | 115 + tools/u-boot-tools/rkcommon.o | Bin 0 -> 7744 bytes tools/u-boot-tools/rkimage.c | 48 + tools/u-boot-tools/rkimage.o | Bin 0 -> 2640 bytes tools/u-boot-tools/rkmux.py | 218 + tools/u-boot-tools/rksd.c | 70 + tools/u-boot-tools/rksd.o | Bin 0 -> 2880 bytes tools/u-boot-tools/rkspi.c | 106 + tools/u-boot-tools/rkspi.o | Bin 0 -> 3200 bytes tools/u-boot-tools/scripts/define2mk.sed | 37 + tools/u-boot-tools/socfpgaimage.c | 402 ++ tools/u-boot-tools/socfpgaimage.o | Bin 0 -> 5848 bytes tools/u-boot-tools/stm32image.c | 147 + tools/u-boot-tools/stm32image.o | Bin 0 -> 3656 bytes tools/u-boot-tools/sunxi-spl-image-builder.c | 484 +++ tools/u-boot-tools/ublimage.c | 259 ++ tools/u-boot-tools/ublimage.h | 83 + tools/u-boot-tools/ublimage.o | Bin 0 -> 8632 bytes tools/u-boot-tools/ubsha1.c | 83 + tools/u-boot-tools/version.h | 1 + tools/u-boot-tools/vybridimage.c | 163 + tools/u-boot-tools/vybridimage.o | Bin 0 -> 3632 bytes tools/u-boot-tools/xway-swap-bytes.c | 38 + tools/u-boot-tools/zynqimage.c | 303 ++ tools/u-boot-tools/zynqimage.o | Bin 0 -> 5992 bytes .../u-boot-tools/zynqmp_pm_cfg_obj_convert.py | 301 ++ .../u-boot-tools/zynqmp_psu_init_minimize.sh | 147 + tools/u-boot-tools/zynqmpbif.c | 1017 +++++ tools/u-boot-tools/zynqmpbif.o | Bin 0 -> 15960 bytes tools/u-boot-tools/zynqmpimage.c | 487 +++ tools/u-boot-tools/zynqmpimage.h | 138 + tools/u-boot-tools/zynqmpimage.o | Bin 0 -> 12824 bytes 516 files changed, 64759 insertions(+) create mode 100644 tools/u-boot-tools/Makefile create mode 100644 tools/u-boot-tools/aisimage.c create mode 100644 tools/u-boot-tools/aisimage.h create mode 100644 tools/u-boot-tools/aisimage.o create mode 100644 tools/u-boot-tools/atmel_pmecc_params.c create mode 100644 tools/u-boot-tools/atmelimage.c create mode 100644 tools/u-boot-tools/atmelimage.o create mode 100644 tools/u-boot-tools/binman/.gitignore create mode 100644 tools/u-boot-tools/binman/README create mode 100644 tools/u-boot-tools/binman/README.entries create mode 120000 tools/u-boot-tools/binman/binman create mode 100755 tools/u-boot-tools/binman/binman.py create mode 100644 tools/u-boot-tools/binman/bsection.py create mode 100644 tools/u-boot-tools/binman/cmdline.py create mode 100644 tools/u-boot-tools/binman/control.py create mode 100644 tools/u-boot-tools/binman/elf.py create mode 100644 tools/u-boot-tools/binman/elf_test.py create mode 100644 tools/u-boot-tools/binman/entry.py create mode 100644 tools/u-boot-tools/binman/entry_test.py create mode 100644 tools/u-boot-tools/binman/etype/_testing.py create mode 100644 tools/u-boot-tools/binman/etype/blob.py create mode 100644 tools/u-boot-tools/binman/etype/blob_dtb.py create mode 100644 tools/u-boot-tools/binman/etype/blob_named_by_arg.py create mode 100644 tools/u-boot-tools/binman/etype/cros_ec_rw.py create mode 100644 tools/u-boot-tools/binman/etype/files.py create mode 100644 tools/u-boot-tools/binman/etype/fill.py create mode 100644 tools/u-boot-tools/binman/etype/fmap.py create mode 100644 tools/u-boot-tools/binman/etype/gbb.py create mode 100644 tools/u-boot-tools/binman/etype/intel_cmc.py create mode 100644 tools/u-boot-tools/binman/etype/intel_descriptor.py create mode 100644 tools/u-boot-tools/binman/etype/intel_fsp.py create mode 100644 tools/u-boot-tools/binman/etype/intel_me.py create mode 100644 tools/u-boot-tools/binman/etype/intel_mrc.py create mode 100644 tools/u-boot-tools/binman/etype/intel_refcode.py create mode 100644 tools/u-boot-tools/binman/etype/intel_vbt.py create mode 100644 tools/u-boot-tools/binman/etype/intel_vga.py create mode 100644 tools/u-boot-tools/binman/etype/powerpc_mpc85xx_bootpg_resetvec.py create mode 100644 tools/u-boot-tools/binman/etype/section.py create mode 100644 tools/u-boot-tools/binman/etype/text.py create mode 100644 tools/u-boot-tools/binman/etype/u_boot.py create mode 100644 tools/u-boot-tools/binman/etype/u_boot_dtb.py create mode 100644 tools/u-boot-tools/binman/etype/u_boot_dtb_with_ucode.py create mode 100644 tools/u-boot-tools/binman/etype/u_boot_elf.py create mode 100644 tools/u-boot-tools/binman/etype/u_boot_img.py create mode 100644 tools/u-boot-tools/binman/etype/u_boot_nodtb.py create mode 100644 tools/u-boot-tools/binman/etype/u_boot_spl.py create mode 100644 tools/u-boot-tools/binman/etype/u_boot_spl_bss_pad.py create mode 100644 tools/u-boot-tools/binman/etype/u_boot_spl_dtb.py create mode 100644 tools/u-boot-tools/binman/etype/u_boot_spl_elf.py create mode 100644 tools/u-boot-tools/binman/etype/u_boot_spl_nodtb.py create mode 100644 tools/u-boot-tools/binman/etype/u_boot_spl_with_ucode_ptr.py create mode 100644 tools/u-boot-tools/binman/etype/u_boot_tpl.py create mode 100644 tools/u-boot-tools/binman/etype/u_boot_tpl_dtb.py create mode 100644 tools/u-boot-tools/binman/etype/u_boot_tpl_dtb_with_ucode.py create mode 100644 tools/u-boot-tools/binman/etype/u_boot_tpl_with_ucode_ptr.py create mode 100644 tools/u-boot-tools/binman/etype/u_boot_ucode.py create mode 100644 tools/u-boot-tools/binman/etype/u_boot_with_ucode_ptr.py create mode 100644 tools/u-boot-tools/binman/etype/vblock.py create mode 100644 tools/u-boot-tools/binman/etype/x86_start16.py create mode 100644 tools/u-boot-tools/binman/etype/x86_start16_spl.py create mode 100644 tools/u-boot-tools/binman/etype/x86_start16_tpl.py create mode 100644 tools/u-boot-tools/binman/fdt_test.py create mode 100644 tools/u-boot-tools/binman/fmap_util.py create mode 100644 tools/u-boot-tools/binman/ftest.py create mode 100644 tools/u-boot-tools/binman/image.py create mode 100644 tools/u-boot-tools/binman/image_test.py create mode 100644 tools/u-boot-tools/binman/state.py create mode 100644 tools/u-boot-tools/binman/test/001_invalid.dts create mode 100644 tools/u-boot-tools/binman/test/002_missing_node.dts create mode 100644 tools/u-boot-tools/binman/test/003_empty.dts create mode 100644 tools/u-boot-tools/binman/test/004_invalid_entry.dts create mode 100644 tools/u-boot-tools/binman/test/005_simple.dts create mode 100644 tools/u-boot-tools/binman/test/006_dual_image.dts create mode 100644 tools/u-boot-tools/binman/test/007_bad_align.dts create mode 100644 tools/u-boot-tools/binman/test/008_pack.dts create mode 100644 tools/u-boot-tools/binman/test/009_pack_extra.dts create mode 100644 tools/u-boot-tools/binman/test/010_pack_align_power2.dts create mode 100644 tools/u-boot-tools/binman/test/011_pack_align_size_power2.dts create mode 100644 tools/u-boot-tools/binman/test/012_pack_inv_align.dts create mode 100644 tools/u-boot-tools/binman/test/013_pack_inv_size_align.dts create mode 100644 tools/u-boot-tools/binman/test/014_pack_overlap.dts create mode 100644 tools/u-boot-tools/binman/test/015_pack_overflow.dts create mode 100644 tools/u-boot-tools/binman/test/016_pack_image_overflow.dts create mode 100644 tools/u-boot-tools/binman/test/017_pack_image_size.dts create mode 100644 tools/u-boot-tools/binman/test/018_pack_image_align.dts create mode 100644 tools/u-boot-tools/binman/test/019_pack_inv_image_align.dts create mode 100644 tools/u-boot-tools/binman/test/020_pack_inv_image_align_power2.dts create mode 100644 tools/u-boot-tools/binman/test/021_image_pad.dts create mode 100644 tools/u-boot-tools/binman/test/022_image_name.dts create mode 100644 tools/u-boot-tools/binman/test/023_blob.dts create mode 100644 tools/u-boot-tools/binman/test/024_sorted.dts create mode 100644 tools/u-boot-tools/binman/test/025_pack_zero_size.dts create mode 100644 tools/u-boot-tools/binman/test/026_pack_u_boot_dtb.dts create mode 100644 tools/u-boot-tools/binman/test/027_pack_4gb_no_size.dts create mode 100644 tools/u-boot-tools/binman/test/028_pack_4gb_outside.dts create mode 100644 tools/u-boot-tools/binman/test/029_x86-rom.dts create mode 100644 tools/u-boot-tools/binman/test/030_x86-rom-me-no-desc.dts create mode 100644 tools/u-boot-tools/binman/test/031_x86-rom-me.dts create mode 100644 tools/u-boot-tools/binman/test/032_intel-vga.dts create mode 100644 tools/u-boot-tools/binman/test/033_x86-start16.dts create mode 100644 tools/u-boot-tools/binman/test/034_x86_ucode.dts create mode 100644 tools/u-boot-tools/binman/test/035_x86_single_ucode.dts create mode 100644 tools/u-boot-tools/binman/test/036_u_boot_img.dts create mode 100644 tools/u-boot-tools/binman/test/037_x86_no_ucode.dts create mode 100644 tools/u-boot-tools/binman/test/038_x86_ucode_missing_node.dts create mode 100644 tools/u-boot-tools/binman/test/039_x86_ucode_missing_node2.dts create mode 100644 tools/u-boot-tools/binman/test/040_x86_ucode_not_in_image.dts create mode 100644 tools/u-boot-tools/binman/test/041_unknown_pos_size.dts create mode 100644 tools/u-boot-tools/binman/test/042_intel-fsp.dts create mode 100644 tools/u-boot-tools/binman/test/043_intel-cmc.dts create mode 100644 tools/u-boot-tools/binman/test/044_x86_optional_ucode.dts create mode 100644 tools/u-boot-tools/binman/test/045_prop_test.dts create mode 100644 tools/u-boot-tools/binman/test/046_intel-vbt.dts create mode 100644 tools/u-boot-tools/binman/test/047_spl_bss_pad.dts create mode 100644 tools/u-boot-tools/binman/test/048_x86-start16-spl.dts create mode 100644 tools/u-boot-tools/binman/test/049_x86_ucode_spl.dts create mode 100644 tools/u-boot-tools/binman/test/050_intel_mrc.dts create mode 100644 tools/u-boot-tools/binman/test/051_u_boot_spl_dtb.dts create mode 100644 tools/u-boot-tools/binman/test/052_u_boot_spl_nodtb.dts create mode 100644 tools/u-boot-tools/binman/test/053_symbols.dts create mode 100644 tools/u-boot-tools/binman/test/054_unit_address.dts create mode 100644 tools/u-boot-tools/binman/test/055_sections.dts create mode 100644 tools/u-boot-tools/binman/test/056_name_prefix.dts create mode 100644 tools/u-boot-tools/binman/test/057_unknown_contents.dts create mode 100644 tools/u-boot-tools/binman/test/058_x86_ucode_spl_needs_retry.dts create mode 100644 tools/u-boot-tools/binman/test/059_change_size.dts create mode 100644 tools/u-boot-tools/binman/test/060_fdt_update.dts create mode 100644 tools/u-boot-tools/binman/test/061_fdt_update_bad.dts create mode 100644 tools/u-boot-tools/binman/test/062_entry_args.dts create mode 100644 tools/u-boot-tools/binman/test/063_entry_args_missing.dts create mode 100644 tools/u-boot-tools/binman/test/064_entry_args_required.dts create mode 100644 tools/u-boot-tools/binman/test/065_entry_args_unknown_datatype.dts create mode 100644 tools/u-boot-tools/binman/test/066_text.dts create mode 100644 tools/u-boot-tools/binman/test/067_fmap.dts create mode 100644 tools/u-boot-tools/binman/test/068_blob_named_by_arg.dts create mode 100644 tools/u-boot-tools/binman/test/069_fill.dts create mode 100644 tools/u-boot-tools/binman/test/070_fill_no_size.dts create mode 100644 tools/u-boot-tools/binman/test/071_gbb.dts create mode 100644 tools/u-boot-tools/binman/test/072_gbb_too_small.dts create mode 100644 tools/u-boot-tools/binman/test/073_gbb_no_size.dts create mode 100644 tools/u-boot-tools/binman/test/074_vblock.dts create mode 100644 tools/u-boot-tools/binman/test/075_vblock_no_content.dts create mode 100644 tools/u-boot-tools/binman/test/076_vblock_bad_phandle.dts create mode 100644 tools/u-boot-tools/binman/test/077_vblock_bad_entry.dts create mode 100644 tools/u-boot-tools/binman/test/078_u_boot_tpl.dts create mode 100644 tools/u-boot-tools/binman/test/079_uses_pos.dts create mode 100644 tools/u-boot-tools/binman/test/080_fill_empty.dts create mode 100644 tools/u-boot-tools/binman/test/081_x86-start16-tpl.dts create mode 100644 tools/u-boot-tools/binman/test/082_fdt_update_all.dts create mode 100644 tools/u-boot-tools/binman/test/083_compress.dts create mode 100644 tools/u-boot-tools/binman/test/084_files.dts create mode 100644 tools/u-boot-tools/binman/test/085_files_compress.dts create mode 100644 tools/u-boot-tools/binman/test/086_files_none.dts create mode 100644 tools/u-boot-tools/binman/test/087_files_no_pattern.dts create mode 100644 tools/u-boot-tools/binman/test/088_expand_size.dts create mode 100644 tools/u-boot-tools/binman/test/089_expand_size_bad.dts create mode 100644 tools/u-boot-tools/binman/test/090_hash.dts create mode 100644 tools/u-boot-tools/binman/test/091_hash_no_algo.dts create mode 100644 tools/u-boot-tools/binman/test/092_hash_bad_algo.dts create mode 100644 tools/u-boot-tools/binman/test/093_x86_tpl_ucode.dts create mode 100644 tools/u-boot-tools/binman/test/094_fmap_x86.dts create mode 100644 tools/u-boot-tools/binman/test/095_fmap_x86_section.dts create mode 100644 tools/u-boot-tools/binman/test/096_elf.dts create mode 100644 tools/u-boot-tools/binman/test/097_elf_strip.dts create mode 100644 tools/u-boot-tools/binman/test/099_hash_section.dts create mode 100644 tools/u-boot-tools/binman/test/100_intel_refcode.dts create mode 100644 tools/u-boot-tools/binman/test/80_4gb_and_skip_at_start_together.dts create mode 100644 tools/u-boot-tools/binman/test/81_powerpc_mpc85xx_bootpg_resetvec.dts create mode 100644 tools/u-boot-tools/binman/test/Makefile create mode 100755 tools/u-boot-tools/binman/test/bss_data create mode 100644 tools/u-boot-tools/binman/test/bss_data.c create mode 100644 tools/u-boot-tools/binman/test/bss_data.lds create mode 100644 tools/u-boot-tools/binman/test/files/1.dat create mode 100644 tools/u-boot-tools/binman/test/files/2.dat create mode 100644 tools/u-boot-tools/binman/test/files/ignored_dir.dat/ignore create mode 100644 tools/u-boot-tools/binman/test/files/not-this-one create mode 100755 tools/u-boot-tools/binman/test/u_boot_binman_syms create mode 100644 tools/u-boot-tools/binman/test/u_boot_binman_syms.c create mode 100644 tools/u-boot-tools/binman/test/u_boot_binman_syms.lds create mode 100755 tools/u-boot-tools/binman/test/u_boot_binman_syms_bad create mode 120000 tools/u-boot-tools/binman/test/u_boot_binman_syms_bad.c create mode 100644 tools/u-boot-tools/binman/test/u_boot_binman_syms_bad.lds create mode 100755 tools/u-boot-tools/binman/test/u_boot_binman_syms_size create mode 100644 tools/u-boot-tools/binman/test/u_boot_binman_syms_size.c create mode 100755 tools/u-boot-tools/binman/test/u_boot_no_ucode_ptr create mode 100644 tools/u-boot-tools/binman/test/u_boot_no_ucode_ptr.c create mode 100755 tools/u-boot-tools/binman/test/u_boot_ucode_ptr create mode 100644 tools/u-boot-tools/binman/test/u_boot_ucode_ptr.c create mode 100644 tools/u-boot-tools/binman/test/u_boot_ucode_ptr.lds create mode 100644 tools/u-boot-tools/bmp_logo.c create mode 100644 tools/u-boot-tools/buildman/.gitignore create mode 100644 tools/u-boot-tools/buildman/README create mode 100644 tools/u-boot-tools/buildman/board.py create mode 100644 tools/u-boot-tools/buildman/bsettings.py create mode 100644 tools/u-boot-tools/buildman/builder.py create mode 100644 tools/u-boot-tools/buildman/builderthread.py create mode 120000 tools/u-boot-tools/buildman/buildman create mode 100755 tools/u-boot-tools/buildman/buildman.py create mode 100644 tools/u-boot-tools/buildman/cmdline.py create mode 100644 tools/u-boot-tools/buildman/control.py create mode 100644 tools/u-boot-tools/buildman/func_test.py create mode 100644 tools/u-boot-tools/buildman/kconfiglib.py create mode 100644 tools/u-boot-tools/buildman/test.py create mode 100644 tools/u-boot-tools/buildman/toolchain.py create mode 100644 tools/u-boot-tools/common/.bootm.o.cmd create mode 100644 tools/u-boot-tools/common/.hash.o.cmd create mode 100644 tools/u-boot-tools/common/.image-fit.o.cmd create mode 100644 tools/u-boot-tools/common/.image.o.cmd create mode 100644 tools/u-boot-tools/common/bootm.c create mode 100644 tools/u-boot-tools/common/bootm.o create mode 100644 tools/u-boot-tools/common/hash.c create mode 100644 tools/u-boot-tools/common/hash.o create mode 100644 tools/u-boot-tools/common/image-fit.c create mode 100644 tools/u-boot-tools/common/image-fit.o create mode 100644 tools/u-boot-tools/common/image.c create mode 100644 tools/u-boot-tools/common/image.o create mode 100644 tools/u-boot-tools/concurrencytest/.gitignore create mode 100644 tools/u-boot-tools/concurrencytest/README.md create mode 100644 tools/u-boot-tools/concurrencytest/concurrencytest.py create mode 100644 tools/u-boot-tools/default_image.c create mode 100644 tools/u-boot-tools/default_image.o create mode 100644 tools/u-boot-tools/dtoc/.gitignore create mode 100644 tools/u-boot-tools/dtoc/dtb_platdata.py create mode 120000 tools/u-boot-tools/dtoc/dtoc create mode 100755 tools/u-boot-tools/dtoc/dtoc.py create mode 100644 tools/u-boot-tools/dtoc/dtoc_test.dts create mode 100644 tools/u-boot-tools/dtoc/dtoc_test_add_prop.dts create mode 100644 tools/u-boot-tools/dtoc/dtoc_test_addr32.dts create mode 100644 tools/u-boot-tools/dtoc/dtoc_test_addr32_64.dts create mode 100644 tools/u-boot-tools/dtoc/dtoc_test_addr64.dts create mode 100644 tools/u-boot-tools/dtoc/dtoc_test_addr64_32.dts create mode 100644 tools/u-boot-tools/dtoc/dtoc_test_aliases.dts create mode 100644 tools/u-boot-tools/dtoc/dtoc_test_bad_reg.dts create mode 100644 tools/u-boot-tools/dtoc/dtoc_test_bad_reg2.dts create mode 100644 tools/u-boot-tools/dtoc/dtoc_test_empty.dts create mode 100644 tools/u-boot-tools/dtoc/dtoc_test_phandle.dts create mode 100644 tools/u-boot-tools/dtoc/dtoc_test_phandle_bad.dts create mode 100644 tools/u-boot-tools/dtoc/dtoc_test_phandle_bad2.dts create mode 100644 tools/u-boot-tools/dtoc/dtoc_test_phandle_reorder.dts create mode 100644 tools/u-boot-tools/dtoc/dtoc_test_phandle_single.dts create mode 100644 tools/u-boot-tools/dtoc/dtoc_test_simple.dts create mode 100644 tools/u-boot-tools/dtoc/fdt.py create mode 100644 tools/u-boot-tools/dtoc/fdt_util.py create mode 100644 tools/u-boot-tools/dtoc/test_dtoc.py create mode 120000 tools/u-boot-tools/dtoc/test_fdt create mode 100755 tools/u-boot-tools/dtoc/test_fdt.py create mode 100755 tools/u-boot-tools/dumpimage create mode 100644 tools/u-boot-tools/dumpimage.c create mode 100644 tools/u-boot-tools/dumpimage.h create mode 100644 tools/u-boot-tools/dumpimage.o create mode 100644 tools/u-boot-tools/easylogo/Makefile create mode 100644 tools/u-boot-tools/easylogo/easylogo.c create mode 100644 tools/u-boot-tools/easylogo/linux_blackfin.tga create mode 100644 tools/u-boot-tools/easylogo/linux_logo.tga create mode 100644 tools/u-boot-tools/easylogo/runme.sh create mode 100644 tools/u-boot-tools/env/.embedded.o.cmd create mode 100644 tools/u-boot-tools/env/.gitignore create mode 100644 tools/u-boot-tools/env/Makefile create mode 100644 tools/u-boot-tools/env/README create mode 100644 tools/u-boot-tools/env/crc32.c create mode 100644 tools/u-boot-tools/env/ctype.c create mode 100644 tools/u-boot-tools/env/embedded.c create mode 100644 tools/u-boot-tools/env/embedded.o create mode 100644 tools/u-boot-tools/env/env_attr.c create mode 100644 tools/u-boot-tools/env/env_flags.c create mode 100644 tools/u-boot-tools/env/fw_env.c create mode 100644 tools/u-boot-tools/env/fw_env.config create mode 100644 tools/u-boot-tools/env/fw_env.h create mode 100644 tools/u-boot-tools/env/fw_env_main.c create mode 100644 tools/u-boot-tools/env/fw_env_private.h create mode 100644 tools/u-boot-tools/env/linux_string.c create mode 100755 tools/u-boot-tools/envcrc create mode 100644 tools/u-boot-tools/envcrc.c create mode 100644 tools/u-boot-tools/envcrc.o create mode 100644 tools/u-boot-tools/fdt_host.h create mode 100755 tools/u-boot-tools/fdtgrep create mode 100644 tools/u-boot-tools/fdtgrep.c create mode 100644 tools/u-boot-tools/fdtgrep.o create mode 100644 tools/u-boot-tools/file2include.c create mode 100644 tools/u-boot-tools/fit_check_sign.c create mode 100644 tools/u-boot-tools/fit_common.c create mode 100644 tools/u-boot-tools/fit_common.h create mode 100644 tools/u-boot-tools/fit_common.o create mode 100644 tools/u-boot-tools/fit_image.c create mode 100644 tools/u-boot-tools/fit_image.o create mode 100644 tools/u-boot-tools/fit_info.c create mode 100644 tools/u-boot-tools/gdb/Makefile create mode 100644 tools/u-boot-tools/gdb/error.c create mode 100644 tools/u-boot-tools/gdb/error.h create mode 100644 tools/u-boot-tools/gdb/gdbcont.c create mode 100644 tools/u-boot-tools/gdb/gdbsend.c create mode 100644 tools/u-boot-tools/gdb/remote.c create mode 100644 tools/u-boot-tools/gdb/remote.h create mode 100644 tools/u-boot-tools/gdb/serial.c create mode 100644 tools/u-boot-tools/gdb/serial.h create mode 100755 tools/u-boot-tools/gen_eth_addr create mode 100644 tools/u-boot-tools/gen_eth_addr.c create mode 100755 tools/u-boot-tools/gen_ethaddr_crc create mode 100644 tools/u-boot-tools/gen_ethaddr_crc.c create mode 100644 tools/u-boot-tools/gen_ethaddr_crc.o create mode 100755 tools/u-boot-tools/genboardscfg.py create mode 100644 tools/u-boot-tools/getline.c create mode 100644 tools/u-boot-tools/getline.h create mode 100644 tools/u-boot-tools/gpheader.h create mode 100644 tools/u-boot-tools/gpimage-common.c create mode 100644 tools/u-boot-tools/gpimage-common.o create mode 100644 tools/u-boot-tools/gpimage.c create mode 100644 tools/u-boot-tools/gpimage.o create mode 100644 tools/u-boot-tools/ifdtool.c create mode 100644 tools/u-boot-tools/ifdtool.h create mode 100644 tools/u-boot-tools/image-host.c create mode 100644 tools/u-boot-tools/image-host.o create mode 100644 tools/u-boot-tools/imagetool.c create mode 100644 tools/u-boot-tools/imagetool.h create mode 100644 tools/u-boot-tools/imagetool.o create mode 100755 tools/u-boot-tools/img2brec.sh create mode 100755 tools/u-boot-tools/img2srec create mode 100644 tools/u-boot-tools/img2srec.c create mode 100644 tools/u-boot-tools/imx8image.c create mode 100644 tools/u-boot-tools/imx8image.o create mode 100755 tools/u-boot-tools/imx8m_image.sh create mode 100644 tools/u-boot-tools/imx8mimage.c create mode 100644 tools/u-boot-tools/imx8mimage.o create mode 100755 tools/u-boot-tools/imx_cntr_image.sh create mode 100644 tools/u-boot-tools/imximage.c create mode 100644 tools/u-boot-tools/imximage.o create mode 100755 tools/u-boot-tools/jtagconsole create mode 100755 tools/u-boot-tools/k3_fit_atf.sh create mode 100644 tools/u-boot-tools/k3_x509template.txt create mode 100644 tools/u-boot-tools/kermit/README create mode 100644 tools/u-boot-tools/kermit/dot.kermrc create mode 100644 tools/u-boot-tools/kermit/flash_param create mode 100644 tools/u-boot-tools/kermit/send_cmd create mode 100644 tools/u-boot-tools/kermit/send_image create mode 100644 tools/u-boot-tools/kwbimage.c create mode 100644 tools/u-boot-tools/kwbimage.h create mode 100644 tools/u-boot-tools/kwbimage.o create mode 100644 tools/u-boot-tools/kwboot.c create mode 100644 tools/u-boot-tools/lib/.crc16.o.cmd create mode 100644 tools/u-boot-tools/lib/.crc32.o.cmd create mode 100644 tools/u-boot-tools/lib/.crc8.o.cmd create mode 100644 tools/u-boot-tools/lib/.fdtdec.o.cmd create mode 100644 tools/u-boot-tools/lib/.fdtdec_common.o.cmd create mode 100644 tools/u-boot-tools/lib/.md5.o.cmd create mode 100644 tools/u-boot-tools/lib/.rc4.o.cmd create mode 100644 tools/u-boot-tools/lib/.sha1.o.cmd create mode 100644 tools/u-boot-tools/lib/.sha256.o.cmd create mode 100644 tools/u-boot-tools/lib/crc16.c create mode 100644 tools/u-boot-tools/lib/crc16.o create mode 100644 tools/u-boot-tools/lib/crc32.c create mode 100644 tools/u-boot-tools/lib/crc32.o create mode 100644 tools/u-boot-tools/lib/crc8.c create mode 100644 tools/u-boot-tools/lib/crc8.o create mode 100644 tools/u-boot-tools/lib/fdtdec.c create mode 100644 tools/u-boot-tools/lib/fdtdec.o create mode 100644 tools/u-boot-tools/lib/fdtdec_common.c create mode 100644 tools/u-boot-tools/lib/fdtdec_common.o create mode 100644 tools/u-boot-tools/lib/libfdt/.fdt_region.o.cmd create mode 100644 tools/u-boot-tools/lib/libfdt/.fdt_ro.o.cmd create mode 100644 tools/u-boot-tools/lib/libfdt/fdt_region.c create mode 100644 tools/u-boot-tools/lib/libfdt/fdt_region.o create mode 100644 tools/u-boot-tools/lib/libfdt/fdt_ro.c create mode 100644 tools/u-boot-tools/lib/libfdt/fdt_ro.o create mode 100644 tools/u-boot-tools/lib/md5.c create mode 100644 tools/u-boot-tools/lib/md5.o create mode 100644 tools/u-boot-tools/lib/rc4.c create mode 100644 tools/u-boot-tools/lib/rc4.o create mode 100644 tools/u-boot-tools/lib/sha1.c create mode 100644 tools/u-boot-tools/lib/sha1.o create mode 100644 tools/u-boot-tools/lib/sha256.c create mode 100644 tools/u-boot-tools/lib/sha256.o create mode 100644 tools/u-boot-tools/libfdt/.fdt.o.cmd create mode 100644 tools/u-boot-tools/libfdt/.fdt_addresses.o.cmd create mode 100644 tools/u-boot-tools/libfdt/.fdt_empty_tree.o.cmd create mode 100644 tools/u-boot-tools/libfdt/.fdt_overlay.o.cmd create mode 100644 tools/u-boot-tools/libfdt/.fdt_rw.o.cmd create mode 100644 tools/u-boot-tools/libfdt/.fdt_strerror.o.cmd create mode 100644 tools/u-boot-tools/libfdt/.fdt_sw.o.cmd create mode 100644 tools/u-boot-tools/libfdt/.fdt_wip.o.cmd create mode 100644 tools/u-boot-tools/libfdt/fdt.c create mode 100644 tools/u-boot-tools/libfdt/fdt.o create mode 100644 tools/u-boot-tools/libfdt/fdt_addresses.c create mode 100644 tools/u-boot-tools/libfdt/fdt_addresses.o create mode 100644 tools/u-boot-tools/libfdt/fdt_empty_tree.c create mode 100644 tools/u-boot-tools/libfdt/fdt_empty_tree.o create mode 100644 tools/u-boot-tools/libfdt/fdt_overlay.c create mode 100644 tools/u-boot-tools/libfdt/fdt_overlay.o create mode 100644 tools/u-boot-tools/libfdt/fdt_rw.c create mode 100644 tools/u-boot-tools/libfdt/fdt_rw.o create mode 100644 tools/u-boot-tools/libfdt/fdt_strerror.c create mode 100644 tools/u-boot-tools/libfdt/fdt_strerror.o create mode 100644 tools/u-boot-tools/libfdt/fdt_sw.c create mode 100644 tools/u-boot-tools/libfdt/fdt_sw.o create mode 100644 tools/u-boot-tools/libfdt/fdt_wip.c create mode 100644 tools/u-boot-tools/libfdt/fdt_wip.o create mode 100644 tools/u-boot-tools/logos/atmel.bmp create mode 100644 tools/u-boot-tools/logos/compulab.bmp create mode 100644 tools/u-boot-tools/logos/denx-comp.bmp create mode 100644 tools/u-boot-tools/logos/denx.bmp create mode 100755 tools/u-boot-tools/logos/engicam.bmp create mode 100644 tools/u-boot-tools/logos/esd.bmp create mode 100644 tools/u-boot-tools/logos/freescale.bmp create mode 100644 tools/u-boot-tools/logos/gateworks.bmp create mode 100644 tools/u-boot-tools/logos/intercontrol.bmp create mode 100644 tools/u-boot-tools/logos/linux_logo_ttcontrol.bmp create mode 100644 tools/u-boot-tools/logos/linux_logo_ttcontrol_palfin.bmp create mode 100644 tools/u-boot-tools/logos/microchip.bmp create mode 100644 tools/u-boot-tools/logos/ronetix.bmp create mode 100644 tools/u-boot-tools/logos/siemens.bmp create mode 100644 tools/u-boot-tools/logos/solidrun.bmp create mode 100644 tools/u-boot-tools/logos/syteco.bmp create mode 100644 tools/u-boot-tools/logos/toradex.bmp create mode 100644 tools/u-boot-tools/logos/u-boot_logo.bmp create mode 100644 tools/u-boot-tools/logos/u-boot_logo.svg create mode 100644 tools/u-boot-tools/logos/wandboard.bmp create mode 100644 tools/u-boot-tools/lpc32xximage.c create mode 100644 tools/u-boot-tools/lpc32xximage.o create mode 120000 tools/u-boot-tools/microcode-tool create mode 100755 tools/u-boot-tools/microcode-tool.py create mode 100644 tools/u-boot-tools/mingw_support.c create mode 100644 tools/u-boot-tools/mingw_support.h create mode 100644 tools/u-boot-tools/mips-relocs.c create mode 100755 tools/u-boot-tools/mkenvimage create mode 100644 tools/u-boot-tools/mkenvimage.c create mode 100644 tools/u-boot-tools/mkenvimage.o create mode 100644 tools/u-boot-tools/mkexynosspl.c create mode 100755 tools/u-boot-tools/mkimage create mode 100644 tools/u-boot-tools/mkimage.c create mode 100644 tools/u-boot-tools/mkimage.h create mode 100644 tools/u-boot-tools/mkimage.o create mode 100644 tools/u-boot-tools/mksunxiboot.c create mode 100755 tools/u-boot-tools/moveconfig.py create mode 100755 tools/u-boot-tools/mrvl_uart.sh create mode 100644 tools/u-boot-tools/mtk_image.c create mode 100644 tools/u-boot-tools/mtk_image.h create mode 100644 tools/u-boot-tools/mtk_image.o create mode 100644 tools/u-boot-tools/mxsboot.c create mode 100644 tools/u-boot-tools/mxsimage.c create mode 100644 tools/u-boot-tools/mxsimage.h create mode 100644 tools/u-boot-tools/mxsimage.o create mode 100644 tools/u-boot-tools/ncb.c create mode 100755 tools/u-boot-tools/netconsole create mode 100644 tools/u-boot-tools/omap/clocks_get_m_n.c create mode 100644 tools/u-boot-tools/omapimage.c create mode 100644 tools/u-boot-tools/omapimage.h create mode 100644 tools/u-boot-tools/omapimage.o create mode 100644 tools/u-boot-tools/os_support.c create mode 100644 tools/u-boot-tools/os_support.h create mode 100644 tools/u-boot-tools/os_support.o create mode 100644 tools/u-boot-tools/patman/.gitignore create mode 100644 tools/u-boot-tools/patman/README create mode 100644 tools/u-boot-tools/patman/__init__.py create mode 100644 tools/u-boot-tools/patman/checkpatch.py create mode 100644 tools/u-boot-tools/patman/command.py create mode 100644 tools/u-boot-tools/patman/commit.py create mode 100644 tools/u-boot-tools/patman/cros_subprocess.py create mode 100644 tools/u-boot-tools/patman/func_test.py create mode 100644 tools/u-boot-tools/patman/get_maintainer.py create mode 100644 tools/u-boot-tools/patman/gitutil.py create mode 100644 tools/u-boot-tools/patman/patchstream.py create mode 120000 tools/u-boot-tools/patman/patman create mode 100755 tools/u-boot-tools/patman/patman.py create mode 100644 tools/u-boot-tools/patman/project.py create mode 100644 tools/u-boot-tools/patman/series.py create mode 100644 tools/u-boot-tools/patman/settings.py create mode 100644 tools/u-boot-tools/patman/setup.py create mode 100644 tools/u-boot-tools/patman/terminal.py create mode 100644 tools/u-boot-tools/patman/test.py create mode 100644 tools/u-boot-tools/patman/test/test01.txt create mode 100644 tools/u-boot-tools/patman/test_util.py create mode 100644 tools/u-boot-tools/patman/tools.py create mode 100644 tools/u-boot-tools/patman/tout.py create mode 100644 tools/u-boot-tools/pbl_crc32.c create mode 100644 tools/u-boot-tools/pbl_crc32.h create mode 100644 tools/u-boot-tools/pbl_crc32.o create mode 100644 tools/u-boot-tools/pblimage.c create mode 100644 tools/u-boot-tools/pblimage.h create mode 100644 tools/u-boot-tools/pblimage.o create mode 100644 tools/u-boot-tools/prelink-riscv.c create mode 100644 tools/u-boot-tools/prelink-riscv.inc create mode 100755 tools/u-boot-tools/proftool create mode 100644 tools/u-boot-tools/proftool.c create mode 100644 tools/u-boot-tools/relocate-rela.c create mode 100644 tools/u-boot-tools/rkcommon.c create mode 100644 tools/u-boot-tools/rkcommon.h create mode 100644 tools/u-boot-tools/rkcommon.o create mode 100644 tools/u-boot-tools/rkimage.c create mode 100644 tools/u-boot-tools/rkimage.o create mode 100755 tools/u-boot-tools/rkmux.py create mode 100644 tools/u-boot-tools/rksd.c create mode 100644 tools/u-boot-tools/rksd.o create mode 100644 tools/u-boot-tools/rkspi.c create mode 100644 tools/u-boot-tools/rkspi.o create mode 100644 tools/u-boot-tools/scripts/define2mk.sed create mode 100644 tools/u-boot-tools/socfpgaimage.c create mode 100644 tools/u-boot-tools/socfpgaimage.o create mode 100644 tools/u-boot-tools/stm32image.c create mode 100644 tools/u-boot-tools/stm32image.o create mode 100644 tools/u-boot-tools/sunxi-spl-image-builder.c create mode 100644 tools/u-boot-tools/ublimage.c create mode 100644 tools/u-boot-tools/ublimage.h create mode 100644 tools/u-boot-tools/ublimage.o create mode 100644 tools/u-boot-tools/ubsha1.c create mode 120000 tools/u-boot-tools/version.h create mode 100644 tools/u-boot-tools/vybridimage.c create mode 100644 tools/u-boot-tools/vybridimage.o create mode 100644 tools/u-boot-tools/xway-swap-bytes.c create mode 100644 tools/u-boot-tools/zynqimage.c create mode 100644 tools/u-boot-tools/zynqimage.o create mode 100755 tools/u-boot-tools/zynqmp_pm_cfg_obj_convert.py create mode 100755 tools/u-boot-tools/zynqmp_psu_init_minimize.sh create mode 100644 tools/u-boot-tools/zynqmpbif.c create mode 100644 tools/u-boot-tools/zynqmpbif.o create mode 100644 tools/u-boot-tools/zynqmpimage.c create mode 100644 tools/u-boot-tools/zynqmpimage.h create mode 100644 tools/u-boot-tools/zynqmpimage.o diff --git a/tools/u-boot-tools/Makefile b/tools/u-boot-tools/Makefile new file mode 100644 index 0000000..081383d --- /dev/null +++ b/tools/u-boot-tools/Makefile @@ -0,0 +1,280 @@ +# SPDX-License-Identifier: GPL-2.0+ +# +# (C) Copyright 2000-2006 +# Wolfgang Denk, DENX Software Engineering, wd@denx.de. + +# Enable all the config-independent tools +ifneq ($(HOST_TOOLS_ALL),) +CONFIG_KIRKWOOD = y +CONFIG_LCD_LOGO = y +CONFIG_CMD_LOADS = y +CONFIG_CMD_NET = y +CONFIG_XWAY_SWAP_BYTES = y +CONFIG_NETCONSOLE = y +CONFIG_SHA1_CHECK_UB_IMG = y +CONFIG_ARCH_SUNXI = y +endif + +subdir-$(HOST_TOOLS_ALL) += easylogo +subdir-$(HOST_TOOLS_ALL) += gdb + +# Merge all the different vars for envcrc into one +ENVCRC-$(CONFIG_ENV_IS_EMBEDDED) = y +ENVCRC-$(CONFIG_ENV_IS_IN_EEPROM) = y +ENVCRC-$(CONFIG_ENV_IS_IN_FLASH) = y +ENVCRC-$(CONFIG_ENV_IS_IN_ONENAND) = y +ENVCRC-$(CONFIG_ENV_IS_IN_NAND) = y +ENVCRC-$(CONFIG_ENV_IS_IN_NVRAM) = y +ENVCRC-$(CONFIG_ENV_IS_IN_SPI_FLASH) = y +CONFIG_BUILD_ENVCRC ?= $(ENVCRC-y) + +hostprogs-$(CONFIG_SPL_GENERATE_ATMEL_PMECC_HEADER) += atmel_pmecc_params + +hostprogs-$(CONFIG_LCD_LOGO) += bmp_logo +hostprogs-$(CONFIG_VIDEO_LOGO) += bmp_logo +HOSTCFLAGS_bmp_logo.o := -pedantic + +hostprogs-$(CONFIG_BUILD_ENVCRC) += envcrc +envcrc-objs := envcrc.o lib/crc32.o env/embedded.o lib/sha1.o + +hostprogs-$(CONFIG_CMD_NET) += gen_eth_addr +HOSTCFLAGS_gen_eth_addr.o := -pedantic + +hostprogs-$(CONFIG_CMD_NET) += gen_ethaddr_crc +gen_ethaddr_crc-objs := gen_ethaddr_crc.o lib/crc8.o +HOSTCFLAGS_gen_ethaddr_crc.o := -pedantic + +hostprogs-$(CONFIG_CMD_LOADS) += img2srec +HOSTCFLAGS_img2srec.o := -pedantic + +hostprogs-$(CONFIG_XWAY_SWAP_BYTES) += xway-swap-bytes +HOSTCFLAGS_xway-swap-bytes.o := -pedantic + +hostprogs-y += mkenvimage +mkenvimage-objs := mkenvimage.o os_support.o lib/crc32.o + +hostprogs-y += dumpimage mkimage +hostprogs-$(CONFIG_FIT_SIGNATURE) += fit_info fit_check_sign + +hostprogs-$(CONFIG_CMD_BOOTEFI_SELFTEST) += file2include + +FIT_SIG_OBJS-$(CONFIG_FIT_SIGNATURE) := common/image-sig.o + +# The following files are synced with upstream DTC. +# Use synced versions from scripts/dtc/libfdt/. +LIBFDT_SRCS_SYNCED := fdt.c fdt_wip.c fdt_sw.c fdt_rw.c \ + fdt_strerror.c fdt_empty_tree.c fdt_addresses.c fdt_overlay.c +# The following files are locally modified for U-Boot (unfotunately). +# Use U-Boot own versions from lib/libfdt/. +LIBFDT_SRCS_UNSYNCED := fdt_ro.c fdt_region.c + +LIBFDT_OBJS := $(addprefix libfdt/, $(patsubst %.c, %.o, $(LIBFDT_SRCS_SYNCED))) \ + $(addprefix lib/libfdt/, $(patsubst %.c, %.o, $(LIBFDT_SRCS_UNSYNCED))) + +RSA_OBJS-$(CONFIG_FIT_SIGNATURE) := $(addprefix lib/rsa/, \ + rsa-sign.o rsa-verify.o rsa-checksum.o \ + rsa-mod-exp.o) + +ROCKCHIP_OBS = lib/rc4.o rkcommon.o rkimage.o rksd.o rkspi.o + +# common objs for dumpimage and mkimage +dumpimage-mkimage-objs := aisimage.o \ + atmelimage.o \ + $(FIT_SIG_OBJS-y) \ + common/bootm.o \ + lib/crc32.o \ + default_image.o \ + lib/fdtdec_common.o \ + lib/fdtdec.o \ + fit_common.o \ + fit_image.o \ + common/image-fit.o \ + image-host.o \ + common/image.o \ + imagetool.o \ + imximage.o \ + imx8image.o \ + imx8mimage.o \ + kwbimage.o \ + lib/md5.o \ + lpc32xximage.o \ + mxsimage.o \ + omapimage.o \ + os_support.o \ + pblimage.o \ + pbl_crc32.o \ + vybridimage.o \ + stm32image.o \ + $(ROCKCHIP_OBS) \ + socfpgaimage.o \ + lib/crc16.o \ + lib/sha1.o \ + lib/sha256.o \ + common/hash.o \ + ublimage.o \ + zynqimage.o \ + zynqmpimage.o \ + zynqmpbif.o \ + $(LIBFDT_OBJS) \ + gpimage.o \ + gpimage-common.o \ + mtk_image.o \ + $(RSA_OBJS-y) + +dumpimage-objs := $(dumpimage-mkimage-objs) dumpimage.o +mkimage-objs := $(dumpimage-mkimage-objs) mkimage.o +fit_info-objs := $(dumpimage-mkimage-objs) fit_info.o +fit_check_sign-objs := $(dumpimage-mkimage-objs) fit_check_sign.o +file2include-objs := file2include.o + +ifneq ($(CONFIG_MX23)$(CONFIG_MX28)$(CONFIG_FIT_SIGNATURE),) +# Add CONFIG_MXS into host CFLAGS, so we can check whether or not register +# the mxsimage support within tools/mxsimage.c . +HOSTCFLAGS_mxsimage.o += -DCONFIG_MXS +endif + +ifdef CONFIG_FIT_SIGNATURE +# This affects include/image.h, but including the board config file +# is tricky, so manually define this options here. +HOST_EXTRACFLAGS += -DCONFIG_FIT_SIGNATURE +HOST_EXTRACFLAGS += -DCONFIG_FIT_SIGNATURE_MAX_SIZE=$(CONFIG_FIT_SIGNATURE_MAX_SIZE) +endif + +ifdef CONFIG_SYS_U_BOOT_OFFS +HOSTCFLAGS_kwbimage.o += -DCONFIG_SYS_U_BOOT_OFFS=$(CONFIG_SYS_U_BOOT_OFFS) +endif + +ifneq ($(CONFIG_ARMADA_38X)$(CONFIG_ARMADA_39X),) +HOSTCFLAGS_kwbimage.o += -DCONFIG_KWB_SECURE +endif + +# MXSImage needs LibSSL +ifneq ($(CONFIG_MX23)$(CONFIG_MX28)$(CONFIG_ARMADA_38X)$(CONFIG_ARMADA_39X)$(CONFIG_FIT_SIGNATURE),) +HOSTLOADLIBES_mkimage += \ + $(shell pkg-config --libs libssl libcrypto 2> /dev/null || echo "-lssl -lcrypto") + +# OS X deprecate openssl in favour of CommonCrypto, supress deprecation +# warnings on those systems +ifeq ($(HOSTOS),darwin) +HOSTCFLAGS_mxsimage.o += -Wno-deprecated-declarations +HOSTCFLAGS_image-sig.o += -Wno-deprecated-declarations +HOSTCFLAGS_rsa-sign.o += -Wno-deprecated-declarations +endif +endif + +HOSTCFLAGS_fit_image.o += -DMKIMAGE_DTC=\"$(CONFIG_MKIMAGE_DTC_PATH)\" + +HOSTLOADLIBES_dumpimage := $(HOSTLOADLIBES_mkimage) +HOSTLOADLIBES_fit_info := $(HOSTLOADLIBES_mkimage) +HOSTLOADLIBES_fit_check_sign := $(HOSTLOADLIBES_mkimage) + +hostprogs-$(CONFIG_EXYNOS5250) += mkexynosspl +hostprogs-$(CONFIG_EXYNOS5420) += mkexynosspl +HOSTCFLAGS_mkexynosspl.o := -pedantic + +ifdtool-objs := $(LIBFDT_OBJS) ifdtool.o +hostprogs-$(CONFIG_X86) += ifdtool + +hostprogs-$(CONFIG_MX23) += mxsboot +hostprogs-$(CONFIG_MX28) += mxsboot +HOSTCFLAGS_mxsboot.o := -pedantic + +hostprogs-$(CONFIG_ARCH_SUNXI) += mksunxiboot +hostprogs-$(CONFIG_ARCH_SUNXI) += sunxi-spl-image-builder +sunxi-spl-image-builder-objs := sunxi-spl-image-builder.o lib/bch.o + +hostprogs-$(CONFIG_NETCONSOLE) += ncb +hostprogs-$(CONFIG_SHA1_CHECK_UB_IMG) += ubsha1 + +ubsha1-objs := os_support.o ubsha1.o lib/sha1.o + +HOSTCFLAGS_ubsha1.o := -pedantic + +hostprogs-$(CONFIG_KIRKWOOD) += kwboot +hostprogs-$(CONFIG_ARCH_MVEBU) += kwboot +hostprogs-y += proftool +hostprogs-$(CONFIG_STATIC_RELA) += relocate-rela +hostprogs-$(CONFIG_RISCV) += prelink-riscv + +hostprogs-y += fdtgrep +fdtgrep-objs += $(LIBFDT_OBJS) fdtgrep.o + +hostprogs-$(CONFIG_MIPS) += mips-relocs + +# We build some files with extra pedantic flags to try to minimize things +# that won't build on some weird host compiler -- though there are lots of +# exceptions for files that aren't complaint. +HOSTCFLAGS_crc32.o := -pedantic +HOSTCFLAGS_crc8.o := -pedantic +HOSTCFLAGS_md5.o := -pedantic +HOSTCFLAGS_sha1.o := -pedantic +HOSTCFLAGS_sha256.o := -pedantic + +quiet_cmd_wrap = WRAP $@ +cmd_wrap = echo "\#include <../$(patsubst $(obj)/%,%,$@)>" >$@ + +$(obj)/lib/%.c $(obj)/common/%.c $(obj)/env/%.c: + $(call cmd,wrap) + +clean-dirs := lib common + +always := $(hostprogs-y) + +# Generated LCD/video logo +LOGO_H = $(objtree)/include/bmp_logo.h +LOGO_DATA_H = $(objtree)/include/bmp_logo_data.h +LOGO-$(CONFIG_LCD_LOGO) += $(LOGO_H) +LOGO-$(CONFIG_LCD_LOGO) += $(LOGO_DATA_H) +LOGO-$(CONFIG_VIDEO_LOGO) += $(LOGO_H) +LOGO-$(CONFIG_VIDEO_LOGO) += $(LOGO_DATA_H) + +# Generic logo +ifeq ($(LOGO_BMP),) +LOGO_BMP= $(srctree)/$(src)/logos/denx.bmp + +# Use board logo and fallback to vendor +ifneq ($(wildcard $(srctree)/$(src)/logos/$(BOARD).bmp),) +LOGO_BMP= $(srctree)/$(src)/logos/$(BOARD).bmp +else +ifneq ($(wildcard $(srctree)/$(src)/logos/$(VENDOR).bmp),) +LOGO_BMP= $(srctree)/$(src)/logos/$(VENDOR).bmp +endif +endif + +endif # !LOGO_BMP + +# +# Use native tools and options +# Define __KERNEL_STRICT_NAMES to prevent typedef overlaps +# Define _GNU_SOURCE to obtain the getline prototype from stdio.h +# +HOST_EXTRACFLAGS += -include $(srctree)/include/compiler.h \ + $(patsubst -I%,-idirafter%, $(filter -I%, $(UBOOTINCLUDE))) \ + -I$(srctree)/scripts/dtc/libfdt \ + -I$(srctree)/tools \ + -DUSE_HOSTCC \ + -D__KERNEL_STRICT_NAMES \ + -D_GNU_SOURCE + +__build: $(LOGO-y) + +$(LOGO_H): $(obj)/bmp_logo $(LOGO_BMP) + $(obj)/bmp_logo --gen-info $(LOGO_BMP) > $@ + +$(LOGO_DATA_H): $(obj)/bmp_logo $(LOGO_BMP) + $(obj)/bmp_logo --gen-data $(LOGO_BMP) > $@ + +# Let clean descend into subdirs +subdir- += env + +ifneq ($(CROSS_BUILD_TOOLS),) +override HOSTCC = $(CC) + +quiet_cmd_crosstools_strip = STRIP $^ + cmd_crosstools_strip = $(STRIP) $^; touch $@ +$(obj)/.strip: $(call objectify,$(filter $(always),$(hostprogs-y))) + $(call cmd,crosstools_strip) + +always += .strip +endif +clean-files += .strip diff --git a/tools/u-boot-tools/aisimage.c b/tools/u-boot-tools/aisimage.c new file mode 100644 index 0000000..4cd76ab --- /dev/null +++ b/tools/u-boot-tools/aisimage.c @@ -0,0 +1,428 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2011 + * Stefano Babic, DENX Software Engineering, sbabic@denx.de. + */ + +#include "imagetool.h" +#include "aisimage.h" +#include <image.h> + +#define IS_FNC_EXEC(c) (cmd_table[c].AIS_cmd == AIS_CMD_FNLOAD) +#define WORD_ALIGN0 4 +#define WORD_ALIGN(len) (((len)+WORD_ALIGN0-1) & ~(WORD_ALIGN0-1)) +#define MAX_CMD_BUFFER 4096 + +static uint32_t ais_img_size; + +/* + * Supported commands for configuration file + */ +static table_entry_t aisimage_cmds[] = { + {CMD_DATA, "DATA", "Reg Write Data"}, + {CMD_FILL, "FILL", "Fill range with pattern"}, + {CMD_CRCON, "CRCON", "CRC Enable"}, + {CMD_CRCOFF, "CRCOFF", "CRC Disable"}, + {CMD_CRCCHECK, "CRCCHECK", "CRC Validate"}, + {CMD_JMPCLOSE, "JMPCLOSE", "Jump & Close"}, + {CMD_JMP, "JMP", "Jump"}, + {CMD_SEQREAD, "SEQREAD", "Sequential read"}, + {CMD_PLL0, "PLL0", "PLL0"}, + {CMD_PLL1, "PLL1", "PLL1"}, + {CMD_CLK, "CLK", "Clock configuration"}, + {CMD_DDR2, "DDR2", "DDR2 Configuration"}, + {CMD_EMIFA, "EMIFA", "EMIFA"}, + {CMD_EMIFA_ASYNC, "EMIFA_ASYNC", "EMIFA Async"}, + {CMD_PLL, "PLL", "PLL & Clock configuration"}, + {CMD_PSC, "PSC", "PSC setup"}, + {CMD_PINMUX, "PINMUX", "Pinmux setup"}, + {CMD_BOOTTABLE, "BOOT_TABLE", "Boot table command"}, + {-1, "", ""}, +}; + +static struct ais_func_exec { + uint32_t index; + uint32_t argcnt; +} ais_func_table[] = { + [CMD_PLL0] = {0, 2}, + [CMD_PLL1] = {1, 2}, + [CMD_CLK] = {2, 1}, + [CMD_DDR2] = {3, 8}, + [CMD_EMIFA] = {4, 5}, + [CMD_EMIFA_ASYNC] = {5, 5}, + [CMD_PLL] = {6, 3}, + [CMD_PSC] = {7, 1}, + [CMD_PINMUX] = {8, 3} +}; + +static struct cmd_table_t { + uint32_t nargs; + uint32_t AIS_cmd; +} cmd_table[] = { + [CMD_FILL] = { 4, AIS_CMD_FILL}, + [CMD_CRCON] = { 0, AIS_CMD_ENCRC}, + [CMD_CRCOFF] = { 0, AIS_CMD_DISCRC}, + [CMD_CRCCHECK] = { 2, AIS_CMD_ENCRC}, + [CMD_JMPCLOSE] = { 1, AIS_CMD_JMPCLOSE}, + [CMD_JMP] = { 1, AIS_CMD_JMP}, + [CMD_SEQREAD] = { 0, AIS_CMD_SEQREAD}, + [CMD_PLL0] = { 2, AIS_CMD_FNLOAD}, + [CMD_PLL1] = { 2, AIS_CMD_FNLOAD}, + [CMD_CLK] = { 1, AIS_CMD_FNLOAD}, + [CMD_DDR2] = { 8, AIS_CMD_FNLOAD}, + [CMD_EMIFA] = { 5, AIS_CMD_FNLOAD}, + [CMD_EMIFA_ASYNC] = { 5, AIS_CMD_FNLOAD}, + [CMD_PLL] = { 3, AIS_CMD_FNLOAD}, + [CMD_PSC] = { 1, AIS_CMD_FNLOAD}, + [CMD_PINMUX] = { 3, AIS_CMD_FNLOAD}, + [CMD_BOOTTABLE] = { 4, AIS_CMD_BOOTTBL}, +}; + +static uint32_t get_cfg_value(char *token, char *name, int linenr) +{ + char *endptr; + uint32_t value; + + errno = 0; + value = strtoul(token, &endptr, 16); + if (errno || (token == endptr)) { + fprintf(stderr, "Error: %s[%d] - Invalid hex data(%s)\n", + name, linenr, token); + exit(EXIT_FAILURE); + } + return value; +} + +static int get_ais_table_id(uint32_t *ptr) +{ + + int i; + int func_no; + + for (i = 0; i < ARRAY_SIZE(cmd_table); i++) { + if (*ptr == cmd_table[i].AIS_cmd) { + if (cmd_table[i].AIS_cmd != AIS_CMD_FNLOAD) + return i; + + func_no = ((struct ais_cmd_func *)ptr)->func_args + & 0xFFFF; + if (func_no == ais_func_table[i].index) + return i; + } + } + + return -1; +} + +static void aisimage_print_header(const void *hdr) +{ + struct ais_header *ais_hdr = (struct ais_header *)hdr; + uint32_t *ptr; + struct ais_cmd_load *ais_load; + int id; + + if (ais_hdr->magic != AIS_MAGIC_WORD) { + fprintf(stderr, "Error: - AIS Magic Number not found\n"); + return; + } + fprintf(stdout, "Image Type: TI Davinci AIS Boot Image\n"); + fprintf(stdout, "AIS magic : %08x\n", ais_hdr->magic); + ptr = (uint32_t *)&ais_hdr->magic; + ptr++; + + while (*ptr != AIS_CMD_JMPCLOSE) { + /* Check if we find the image */ + if (*ptr == AIS_CMD_LOAD) { + ais_load = (struct ais_cmd_load *)ptr; + fprintf(stdout, "Image at : 0x%08x size 0x%08x\n", + ais_load->addr, + ais_load->size); + ptr = ais_load->data + ais_load->size / sizeof(*ptr); + continue; + } + + id = get_ais_table_id(ptr); + if (id < 0) { + fprintf(stderr, "Error: - AIS Image corrupted\n"); + return; + } + fprintf(stdout, "AIS cmd : %s\n", + get_table_entry_name(aisimage_cmds, NULL, id)); + ptr += cmd_table[id].nargs + IS_FNC_EXEC(id) + 1; + if (((void *)ptr - hdr) > ais_img_size) { + fprintf(stderr, + "AIS Image not terminated by JMPCLOSE\n"); + return; + } + } +} + +static uint32_t *ais_insert_cmd_header(uint32_t cmd, uint32_t nargs, + uint32_t *parms, struct image_type_params *tparams, + uint32_t *ptr) +{ + int i; + + *ptr++ = cmd_table[cmd].AIS_cmd; + if (IS_FNC_EXEC(cmd)) + *ptr++ = ((nargs & 0xFFFF) << 16) + ais_func_table[cmd].index; + + /* Copy parameters */ + for (i = 0; i < nargs; i++) + *ptr++ = cpu_to_le32(parms[i]); + + return ptr; + +} + +static uint32_t *ais_alloc_buffer(struct image_tool_params *params) +{ + int dfd; + struct stat sbuf; + char *datafile = params->datafile; + uint32_t *ptr; + + dfd = open(datafile, O_RDONLY|O_BINARY); + if (dfd < 0) { + fprintf(stderr, "%s: Can't open %s: %s\n", + params->cmdname, datafile, strerror(errno)); + exit(EXIT_FAILURE); + } + + if (fstat(dfd, &sbuf) < 0) { + fprintf(stderr, "%s: Can't stat %s: %s\n", + params->cmdname, datafile, strerror(errno)); + exit(EXIT_FAILURE); + } + + /* + * Place for header is allocated. The size is taken from + * the size of the datafile, that the ais_image_generate() + * will copy into the header. Copying the datafile + * is not left to the main program, because after the datafile + * the header must be terminated with the Jump & Close command. + */ + ais_img_size = WORD_ALIGN(sbuf.st_size) + MAX_CMD_BUFFER; + ptr = (uint32_t *)malloc(WORD_ALIGN(sbuf.st_size) + MAX_CMD_BUFFER); + if (!ptr) { + fprintf(stderr, "%s: malloc return failure: %s\n", + params->cmdname, strerror(errno)); + exit(EXIT_FAILURE); + } + + close(dfd); + + return ptr; +} + +static uint32_t *ais_copy_image(struct image_tool_params *params, + uint32_t *aisptr) + +{ + int dfd; + struct stat sbuf; + char *datafile = params->datafile; + void *ptr; + + dfd = open(datafile, O_RDONLY|O_BINARY); + if (dfd < 0) { + fprintf(stderr, "%s: Can't open %s: %s\n", + params->cmdname, datafile, strerror(errno)); + exit(EXIT_FAILURE); + } + + if (fstat(dfd, &sbuf) < 0) { + fprintf(stderr, "%s: Can't stat %s: %s\n", + params->cmdname, datafile, strerror(errno)); + exit(EXIT_FAILURE); + } + + ptr = mmap(0, sbuf.st_size, PROT_READ, MAP_SHARED, dfd, 0); + *aisptr++ = AIS_CMD_LOAD; + *aisptr++ = params->ep; + *aisptr++ = sbuf.st_size; + memcpy((void *)aisptr, ptr, sbuf.st_size); + aisptr += WORD_ALIGN(sbuf.st_size) / sizeof(uint32_t); + + (void) munmap((void *)ptr, sbuf.st_size); + (void) close(dfd); + + return aisptr; + +} + +static int aisimage_generate(struct image_tool_params *params, + struct image_type_params *tparams) +{ + FILE *fd = NULL; + char *line = NULL; + char *token, *saveptr1, *saveptr2; + int lineno = 0; + int fld; + size_t len; + int32_t cmd; + uint32_t nargs, cmd_parms[10]; + uint32_t value, size; + char *name = params->imagename; + uint32_t *aishdr; + + fd = fopen(name, "r"); + if (fd == 0) { + fprintf(stderr, + "Error: %s - Can't open AIS configuration\n", name); + exit(EXIT_FAILURE); + } + + /* + * the size of the header is variable and is computed + * scanning the configuration file. + */ + tparams->header_size = 0; + + /* + * Start allocating a buffer suitable for most command + * The buffer is then reallocated if it is too small + */ + aishdr = ais_alloc_buffer(params); + tparams->hdr = aishdr; + *aishdr++ = AIS_MAGIC_WORD; + + /* Very simple parsing, line starting with # are comments + * and are dropped + */ + while ((getline(&line, &len, fd)) > 0) { + lineno++; + + token = strtok_r(line, "\r\n", &saveptr1); + if (token == NULL) + continue; + + /* Check inside the single line */ + line = token; + fld = CFG_COMMAND; + cmd = CMD_INVALID; + nargs = 0; + while (token != NULL) { + token = strtok_r(line, " \t", &saveptr2); + if (token == NULL) + break; + + /* Drop all text starting with '#' as comments */ + if (token[0] == '#') + break; + + switch (fld) { + case CFG_COMMAND: + cmd = get_table_entry_id(aisimage_cmds, + "aisimage commands", token); + if (cmd < 0) { + fprintf(stderr, + "Error: %s[%d] - Invalid command" + "(%s)\n", name, lineno, token); + + exit(EXIT_FAILURE); + } + break; + case CFG_VALUE: + value = get_cfg_value(token, name, lineno); + cmd_parms[nargs++] = value; + if (nargs > cmd_table[cmd].nargs) { + fprintf(stderr, + "Error: %s[%d] - too much arguments:" + "(%s) for command %s\n", name, + lineno, token, + aisimage_cmds[cmd].sname); + exit(EXIT_FAILURE); + } + break; + } + line = NULL; + fld = CFG_VALUE; + } + if (cmd != CMD_INVALID) { + /* Now insert the command into the header */ + aishdr = ais_insert_cmd_header(cmd, nargs, cmd_parms, + tparams, aishdr); + } + + } + fclose(fd); + + aishdr = ais_copy_image(params, aishdr); + + /* Add Jmp & Close */ + *aishdr++ = AIS_CMD_JMPCLOSE; + *aishdr++ = params->ep; + + size = (aishdr - (uint32_t *)tparams->hdr) * sizeof(uint32_t); + tparams->header_size = size; + + return 0; +} + +static int aisimage_check_image_types(uint8_t type) +{ + if (type == IH_TYPE_AISIMAGE) + return EXIT_SUCCESS; + else + return EXIT_FAILURE; +} + +static int aisimage_verify_header(unsigned char *ptr, int image_size, + struct image_tool_params *params) +{ + struct ais_header *ais_hdr = (struct ais_header *)ptr; + + if (ais_hdr->magic != AIS_MAGIC_WORD) + return -FDT_ERR_BADSTRUCTURE; + + /* Store the total size to remember in print_hdr */ + ais_img_size = image_size; + + return 0; +} + +static void aisimage_set_header(void *ptr, struct stat *sbuf, int ifd, + struct image_tool_params *params) +{ +} + +int aisimage_check_params(struct image_tool_params *params) +{ + if (!params) + return CFG_INVALID; + if (!strlen(params->imagename)) { + fprintf(stderr, "Error: %s - Configuration file not specified, " + "it is needed for aisimage generation\n", + params->cmdname); + return CFG_INVALID; + } + /* + * Check parameters: + * XIP is not allowed and verify that incompatible + * parameters are not sent at the same time + * For example, if list is required a data image must not be provided + */ + return (params->dflag && (params->fflag || params->lflag)) || + (params->fflag && (params->dflag || params->lflag)) || + (params->lflag && (params->dflag || params->fflag)) || + (params->xflag) || !(strlen(params->imagename)); +} + +/* + * aisimage parameters + */ +U_BOOT_IMAGE_TYPE( + aisimage, + "TI Davinci AIS Boot Image support", + 0, + NULL, + aisimage_check_params, + aisimage_verify_header, + aisimage_print_header, + aisimage_set_header, + NULL, + aisimage_check_image_types, + NULL, + aisimage_generate +); diff --git a/tools/u-boot-tools/aisimage.h b/tools/u-boot-tools/aisimage.h new file mode 100644 index 0000000..d8637a0 --- /dev/null +++ b/tools/u-boot-tools/aisimage.h @@ -0,0 +1,80 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * (C) Copyright 2011 + * Stefano Babic, DENX Software Engineering, sbabic@denx.de. + */ + +#ifndef _AISIMAGE_H_ +#define _AISIMAGE_H_ + +/* all values are for little endian systems */ +#define AIS_MAGIC_WORD 0x41504954 +#define AIS_FCN_MAX 8 + +enum { + AIS_CMD_LOAD = 0x58535901, + AIS_CMD_VALCRC = 0x58535902, + AIS_CMD_ENCRC = 0x58535903, + AIS_CMD_DISCRC = 0x58535904, + AIS_CMD_JMP = 0x58535905, + AIS_CMD_JMPCLOSE = 0x58535906, + AIS_CMD_BOOTTBL = 0x58535907, + AIS_CMD_FILL = 0x5853590A, + AIS_CMD_FNLOAD = 0x5853590D, + AIS_CMD_SEQREAD = 0x58535963, +}; + +struct ais_cmd_load { + uint32_t cmd; + uint32_t addr; + uint32_t size; + uint32_t data[1]; +}; + +struct ais_cmd_func { + uint32_t cmd; + uint32_t func_args; + uint32_t parms[AIS_FCN_MAX]; +}; + +struct ais_cmd_jmpclose { + uint32_t cmd; + uint32_t addr; +}; + +#define CMD_DATA_STR "DATA" + +enum ais_file_cmd { + CMD_INVALID, + CMD_FILL, + CMD_CRCON, + CMD_CRCOFF, + CMD_CRCCHECK, + CMD_JMPCLOSE, + CMD_JMP, + CMD_SEQREAD, + CMD_DATA, + CMD_PLL0, + CMD_PLL1, + CMD_CLK, + CMD_DDR2, + CMD_EMIFA, + CMD_EMIFA_ASYNC, + CMD_PLL, + CMD_PSC, + CMD_PINMUX, + CMD_BOOTTABLE +}; + +enum aisimage_fld_types { + CFG_INVALID = -1, + CFG_COMMAND, + CFG_VALUE, +}; + +struct ais_header { + uint32_t magic; + char data[1]; +}; + +#endif /* _AISIMAGE_H_ */ diff --git a/tools/u-boot-tools/aisimage.o b/tools/u-boot-tools/aisimage.o new file mode 100644 index 0000000000000000000000000000000000000000..c739de79f48ef20240fadf03acd268134907b40c GIT binary patch literal 11232 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 Ui%ow8C}T4)Ff4&6gOlj`0lk_Mg#Z8m literal 0 HcmV?d00001 diff --git a/tools/u-boot-tools/atmel_pmecc_params.c b/tools/u-boot-tools/atmel_pmecc_params.c new file mode 100644 index 0000000..a4ae03f --- /dev/null +++ b/tools/u-boot-tools/atmel_pmecc_params.c @@ -0,0 +1,50 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2014 Andreas Bießmann <andreas@biessmann.org> + */ + +/* + * This is a host tool for generating an appropriate string out of board + * configuration. The string is required for correct generation of PMECC + * header which in turn is required for NAND flash booting of Atmel AT91 style + * hardware. + * + * See doc/README.atmel_pmecc for more information. + */ + +#include <config.h> +#include <stdlib.h> + +static int pmecc_get_ecc_bytes(int cap, int sector_size) +{ + int m = 12 + sector_size / 512; + return (m * cap + 7) / 8; +} + +int main(int argc, char *argv[]) +{ + unsigned int use_pmecc = 0; + unsigned int sector_per_page; + unsigned int sector_size = CONFIG_PMECC_SECTOR_SIZE; + unsigned int oob_size = CONFIG_SYS_NAND_OOBSIZE; + unsigned int ecc_bits = CONFIG_PMECC_CAP; + unsigned int ecc_offset; + +#ifdef CONFIG_ATMEL_NAND_HW_PMECC + use_pmecc = 1; +#endif + + sector_per_page = CONFIG_SYS_NAND_PAGE_SIZE / CONFIG_PMECC_SECTOR_SIZE; + ecc_offset = oob_size - + pmecc_get_ecc_bytes(ecc_bits, sector_size) * sector_per_page; + + printf("usePmecc=%d,", use_pmecc); + printf("sectorPerPage=%d,", sector_per_page); + printf("sectorSize=%d,", sector_size); + printf("spareSize=%d,", oob_size); + printf("eccBits=%d,", ecc_bits); + printf("eccOffset=%d", ecc_offset); + printf("\n"); + + exit(EXIT_SUCCESS); +} diff --git a/tools/u-boot-tools/atmelimage.c b/tools/u-boot-tools/atmelimage.c new file mode 100644 index 0000000..7b3b243 --- /dev/null +++ b/tools/u-boot-tools/atmelimage.c @@ -0,0 +1,339 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2014 + * Andreas Bießmann <andreas@biessmann.org> + */ + +#include "imagetool.h" +#include "mkimage.h" + +#include <image.h> + +#define pr_err(fmt, args...) fprintf(stderr, "atmelimage Error: " fmt, ##args) + +static int atmel_check_image_type(uint8_t type) +{ + if (type == IH_TYPE_ATMELIMAGE) + return EXIT_SUCCESS; + else + return EXIT_FAILURE; +} + +static uint32_t nand_pmecc_header[52]; + +/* + * A helper struct for parsing the mkimage -n parameter + * + * Keep in same order as the configs array! + */ +static struct pmecc_config { + int use_pmecc; + int sector_per_page; + int spare_size; + int ecc_bits; + int sector_size; + int ecc_offset; +} pmecc; + +/* + * Strings used for configure the PMECC header via -n mkimage switch + * + * We estimate a coma separated list of key=value pairs. The mkimage -n + * parameter argument should not contain any whitespace. + * + * Keep in same order as struct pmecc_config! + */ +static const char * const configs[] = { + "usePmecc", + "sectorPerPage", + "spareSize", + "eccBits", + "sectorSize", + "eccOffset" +}; + +static int atmel_find_pmecc_parameter_in_token(const char *token) +{ + size_t pos; + char *param; + + debug("token: '%s'\n", token); + + for (pos = 0; pos < ARRAY_SIZE(configs); pos++) { + if (strncmp(token, configs[pos], strlen(configs[pos])) == 0) { + param = strstr(token, "="); + if (!param) + goto err; + + param++; + debug("\t%s parameter: '%s'\n", configs[pos], param); + + switch (pos) { + case 0: + pmecc.use_pmecc = strtol(param, NULL, 10); + return EXIT_SUCCESS; + case 1: + pmecc.sector_per_page = strtol(param, NULL, 10); + return EXIT_SUCCESS; + case 2: + pmecc.spare_size = strtol(param, NULL, 10); + return EXIT_SUCCESS; + case 3: + pmecc.ecc_bits = strtol(param, NULL, 10); + return EXIT_SUCCESS; + case 4: + pmecc.sector_size = strtol(param, NULL, 10); + return EXIT_SUCCESS; + case 5: + pmecc.ecc_offset = strtol(param, NULL, 10); + return EXIT_SUCCESS; + } + } + } + +err: + pr_err("Could not find parameter in token '%s'\n", token); + return EXIT_FAILURE; +} + +static int atmel_parse_pmecc_params(char *txt) +{ + char *token; + + token = strtok(txt, ","); + while (token != NULL) { + if (atmel_find_pmecc_parameter_in_token(token)) + return EXIT_FAILURE; + + token = strtok(NULL, ","); + } + + return EXIT_SUCCESS; +} + +static int atmel_verify_header(unsigned char *ptr, int image_size, + struct image_tool_params *params) +{ + uint32_t *ints = (uint32_t *)ptr; + size_t pos; + size_t size = image_size; + + /* check if we have an PMECC header attached */ + for (pos = 0; pos < ARRAY_SIZE(nand_pmecc_header); pos++) + if (ints[pos] >> 28 != 0xC) + break; + + if (pos == ARRAY_SIZE(nand_pmecc_header)) { + ints += ARRAY_SIZE(nand_pmecc_header); + size -= sizeof(nand_pmecc_header); + } + + /* check the seven interrupt vectors of binary */ + for (pos = 0; pos < 7; pos++) { + debug("atmelimage: interrupt vector #%zu is 0x%08X\n", pos+1, + ints[pos]); + /* + * all vectors except the 6'th one must contain valid + * LDR or B Opcode + */ + if (pos == 5) + /* 6'th vector has image size set, check later */ + continue; + if ((ints[pos] & 0xff000000) == 0xea000000) + /* valid B Opcode */ + continue; + if ((ints[pos] & 0xfffff000) == 0xe59ff000) + /* valid LDR (I=0, P=1, U=1, B=0, W=0, L=1) */ + continue; + /* ouch, one of the checks has missed ... */ + return 1; + } + + return ints[5] != cpu_to_le32(size); +} + +static void atmel_print_pmecc_header(const uint32_t word) +{ + int val; + + printf("\t\tPMECC header\n"); + + printf("\t\t====================\n"); + + val = (word >> 18) & 0x1ff; + printf("\t\teccOffset: %9i\n", val); + + val = (((word >> 16) & 0x3) == 0) ? 512 : 1024; + printf("\t\tsectorSize: %8i\n", val); + + if (((word >> 13) & 0x7) <= 2) + val = (2 << ((word >> 13) & 0x7)); + else + val = (12 << (((word >> 13) & 0x7) - 3)); + printf("\t\teccBitReq: %9i\n", val); + + val = (word >> 4) & 0x1ff; + printf("\t\tspareSize: %9i\n", val); + + val = (1 << ((word >> 1) & 0x3)); + printf("\t\tnbSectorPerPage: %3i\n", val); + + printf("\t\tusePmecc: %10i\n", word & 0x1); + printf("\t\t====================\n"); +} + +static void atmel_print_header(const void *ptr) +{ + uint32_t *ints = (uint32_t *)ptr; + size_t pos; + + /* check if we have an PMECC header attached */ + for (pos = 0; pos < ARRAY_SIZE(nand_pmecc_header); pos++) + if (ints[pos] >> 28 != 0xC) + break; + + if (pos == ARRAY_SIZE(nand_pmecc_header)) { + printf("Image Type:\tATMEL ROM-Boot Image with PMECC Header\n"); + atmel_print_pmecc_header(ints[0]); + pos += 5; + } else { + printf("Image Type:\tATMEL ROM-Boot Image without PMECC Header\n"); + pos = 5; + } + printf("\t\t6'th vector has %u set\n", le32_to_cpu(ints[pos])); +} + +static void atmel_set_header(void *ptr, struct stat *sbuf, int ifd, + struct image_tool_params *params) +{ + /* just save the image size into 6'th interrupt vector */ + uint32_t *ints = (uint32_t *)ptr; + size_t cnt; + size_t pos = 5; + size_t size = sbuf->st_size; + + for (cnt = 0; cnt < ARRAY_SIZE(nand_pmecc_header); cnt++) + if (ints[cnt] >> 28 != 0xC) + break; + + if (cnt == ARRAY_SIZE(nand_pmecc_header)) { + pos += ARRAY_SIZE(nand_pmecc_header); + size -= sizeof(nand_pmecc_header); + } + + ints[pos] = cpu_to_le32(size); +} + +static int atmel_check_params(struct image_tool_params *params) +{ + if (strlen(params->imagename) > 0) + if (atmel_parse_pmecc_params(params->imagename)) + return EXIT_FAILURE; + + return !(!params->eflag && + !params->fflag && + !params->xflag && + ((params->dflag && !params->lflag) || + (params->lflag && !params->dflag))); +} + +static int atmel_vrec_header(struct image_tool_params *params, + struct image_type_params *tparams) +{ + uint32_t tmp; + size_t pos; + + if (strlen(params->imagename) == 0) + return EXIT_SUCCESS; + + tmp = 0xC << 28; + + tmp |= (pmecc.ecc_offset & 0x1ff) << 18; + + switch (pmecc.sector_size) { + case 512: + tmp |= 0 << 16; + break; + case 1024: + tmp |= 1 << 16; + break; + + default: + pr_err("Wrong sectorSize (%i) for PMECC header\n", + pmecc.sector_size); + return EXIT_FAILURE; + } + + switch (pmecc.ecc_bits) { + case 2: + tmp |= 0 << 13; + break; + case 4: + tmp |= 1 << 13; + break; + case 8: + tmp |= 2 << 13; + break; + case 12: + tmp |= 3 << 13; + break; + case 24: + tmp |= 4 << 13; + break; + + default: + pr_err("Wrong eccBits (%i) for PMECC header\n", + pmecc.ecc_bits); + return EXIT_FAILURE; + } + + tmp |= (pmecc.spare_size & 0x1ff) << 4; + + switch (pmecc.sector_per_page) { + case 1: + tmp |= 0 << 1; + break; + case 2: + tmp |= 1 << 1; + break; + case 4: + tmp |= 2 << 1; + break; + case 8: + tmp |= 3 << 1; + break; + + default: + pr_err("Wrong sectorPerPage (%i) for PMECC header\n", + pmecc.sector_per_page); + return EXIT_FAILURE; + } + + if (pmecc.use_pmecc) + tmp |= 1; + + for (pos = 0; pos < ARRAY_SIZE(nand_pmecc_header); pos++) + nand_pmecc_header[pos] = tmp; + + debug("PMECC header filled 52 times with 0x%08X\n", tmp); + + tparams->header_size = sizeof(nand_pmecc_header); + tparams->hdr = nand_pmecc_header; + + return EXIT_SUCCESS; +} + +U_BOOT_IMAGE_TYPE( + atmelimage, + "ATMEL ROM-Boot Image support", + 0, + NULL, + atmel_check_params, + atmel_verify_header, + atmel_print_header, + atmel_set_header, + NULL, + atmel_check_image_type, + NULL, + atmel_vrec_header +); diff --git a/tools/u-boot-tools/atmelimage.o b/tools/u-boot-tools/atmelimage.o new file mode 100644 index 0000000000000000000000000000000000000000..dd1c6953ef37ae3391858a7be0bcbca8c934bc8e GIT binary patch literal 8736 zcmb<-^>JfjWMqH=Mg}_u1P><4z_36D!FB*M9T>zIL>UYZI5hkh;-7lpa2mh73j>2k z@;8spUmm^v7Z?~AUUN<GXg<W^VR@<~zMJXb3z_C0JSA@pFL^ZoVl7eaW|`i3u<;=S z1H=Eup9~BPuS%3u89w~~Z_Dsu{?ih%9Ux~M_GmuD`1*XB9>07G*jA6`Uu>nXy4^)Q zEI;s1J;1;1z+s5Jo!31Y-+?WipN3FA!K1s(0Lej|5cgax5l6UZhX?}$L#GM*VSafB z2B;xBo<PMMLp(Zvc=S#OnH=iT`8W7AAH=@q11uhvhf2)5B@VupY5vVq^46o*7UaYi zAOdV=cL^&)G*r~^K&)fTOOO;aoRAc|bf+>vm4P)Qvt2r$LAizp4!#p=eD<G_fdQ<_ zrSkz)=HLr~<_FBpKNuyy86IeS#_;d||HfksEO2!X55DAWe#qYZlZn5rhJk^h`6YAb zy~_x5p~fKu4!&bSvFPDGP+S-uxcCxkWAj7i%g->hH9unni$N8E+2DYD`RU*P{}6xg zV`5-nxcuy8{r~^}!QtH<?qPVK!HR*uMUs($!K3*NM>j7hn7Tzl<iUqRpcL|lz3~?V z69Yp@K;tiv$4WIiPw+A@Fdl%YYW^j`-~NMvfx)Mj6{M^4x@R)a4j;yY{~vmEgCoVG z*AygScmU!kkas+qk8m8G05*r=!0T-dXA)2?W@&u*L7stu^I-EYCjOR#3=9l){>{Hw z_*>S2g773;^DmB4=Weiltp`dIpq@sg7Knd4IXoaKrS$7@Hx5uLGCbhXStkItz4;eo z$zI$pd>IK2qy~`74>zA=d_BA2OvB5t|Ns9toN;*R2c{KXy8ZwEACDQL|NsAQKFRVj z2uw4*^!oq*KT-y940jB340Q|vr)^N^dvw=(G}tqgx^>sGfU{uh|5BCiQjXSdrLx_= zGOZ^|#kxZox?KfYFY&kJF)}c;9)RRSkdLBc9b+709pfD15A(}|-3<+RaIkxJ9`|TH zP_oRYR~O_ZpI%iE1qr7e9N_4K#L{h#&O<((-+Vg%gY~r@;BVc+z`(E%6qPVRkJbbH zE&CW47(99_7<X|oFfbT?^JxCfT3+j!%reJ^@xlKGp!5Ycpc5QRkbDbG2oRGYE-Gz$ z$qx=`7igA&sDFK#UmlXxp!~yV{LN_m#c2G={PGMQ&4(F1nt!vEi1~Jx+-30T{0?@} zODV904h+zk^8uBa5YI!BC@3_+!H*K84xrGU0xI=h_Wt|-pMimale0KAxg@_RII}9% zN<q~klZ%0qGc`HcDYGOfwa`jI)e<aHT##6l3RMFU$x8|bYYa#&3P?-`=`{wcEG<qA z$W2X7wo*_vGysd(VgU@CoM!4J846`!GZZosixpH$6^c_!K-LHNx;i^6WTYmhq!vM3 z<DZsRoLT~P4a^1x9R|k`UsoT6Ab($7r~Ld91<%~X^i+l7(t?8gq7nv($Dr<Gfct;} z;=5v~JjhcZA&3<W5F<h=3sS8(v6^0<S(2duaiIsqg`h-8oWA_h5_GMJCAq0NnP7Lj z78T_eSt*1U<>#d<KpYQpoPvgGrlvw#ei2kFBwRoV7GyCl{ZLQh)Qn3X#9nBi;ndB* zfT`a(zceRBAuqo~AuTg6MWG<EC^0v+B(+E(Gf$x;KRY!~L0z?29p-M3+ea~$5O8;P zwo=e=NlnU3%u}$?GuAWEHPeJJK&nA)4G_&(6~w?;A;2ij!_F~*k%2*gfq?<m#Daw} zNX!7Fn1O)-()xj@bAgIM+F=X~8Vn2!5m2!hP}SzdC(y&>%_q^u?82we%u>L`C*j2> z;Kaw_&8>oDRw+nrFrNTZHWwd<6E{;07axZscQzk~BcDJD0|UbvsF`~~>YVrl`k9>g zB>I@0_!N3socJ_)SRMHc+Spw9ESlL}_#A4v_$(ax3>^71ocI)+_#~XbHhOR~+c0wR zS-4@yxN<{{$4Y^$WMTLSD&jEYnX$G|85o!um@(9X1eifh1`q=Y^D!_mFd>-(3v*DF z1QG{@JxDn$+%2K%7k~sA7#Lt-58^U2fa@0!69qFfu%Yll951Lj4?r9S1_p3^F)%R1 zLd6|G?IQ*T25{UkFfimZFfcG8xnBaR4@5CDu!ATRTnbgM02KwbI6%yHsQCw=K?IId z1_p*%VD;b@7f2ZcEWLp^%nY0$3I#6$t4D}~s%?;hJz#M}l)};@NRF8SAr9ihFf#+f zUYHn&W(Ma55DOJEGYFyzFfcG&0*kOQSb%8+aRba`WvBqt2;wf7i5V}Cz~Zb7lfX2B zcn)T=G9-d&1n~yUWMx<grV+$PFcUL9e1nP$gJ}fu8_Z;70Hr%H6GnhC00S$7IgAgb zS)t<oP(F;}0*kXT6u<<av;b5b-0FaefhaMk_y!OM3Cn=RSs6g_i6qRR1Qp+mEXcsX zpaB+VgVLyy%nbaf0t^fcdeE{CUBU&2cpwgOPaNU_IK(4xh{xj)Psbq+YVpF#24p{g z@+&rRq>5HA8N!KA&PYwpjt4gy;!7$EQlY|SsYRJ-mGO}F0aUCwwFFh5peQpBO{lCW zH5sO;0MvA2$V<#ii3c-b;^68YYB9KGFJ?&2&r8cpFJ?fvBfg-dC?2d5EQTzDB&z4* zY-qrs=i_V!BFsUA1&A;O5tbkVENcj28G#665Mcr$48ekiU;#t0oFQ13Aw<@UA>Q4` z-^tM@-rvnF*fk_R#L>yeHJ+iMw4|7!xFjXDsE8pA8~|wy#U({0`Pm>UFFCgWL?>qy zfvA%F91xY0ng@;^K5+U3mGuk^ptAGNe+UqUinA~<FlaL{Fu=rBpyHrnSQ1Ix8cAFV zN!$S{j&6=OR2*cEG?IEy`UE))U41x`dQdw6<{nu6fUZ6bNxdwR`DIXXka`^?@dl_k z$USmM;`5;5=<2~eesG<^z#xyL{wNOh=b_>t^FehF%>DPF;^^jof{KIGD<YZy2P%%P zo(-fB>K-K|^#V|FboJs;aS(+Zo-!Z-1_p4AiX88uP;n3iG7FZMqd@{tdqLtnpvYrj zU}!`Wht-1<k;Fl@8!Q|aBZ-50zcBGtNaD&!{@Mr?2id8IBz_Jm4x&JEAPjQS6_5au zJ3(R~%m}UTLDC?#Fnf8>#6e*MQtyf+j_gh!Bymt15@t>?R2*bBvO7DW;vjpG&6$8C z4yyBD=1hl*qno1)5`g+k4JjNnpyD7(A4%LCBmh+p8bN}YZ-*ofikAeCXBik60+7Vj zk<3Yeii0Rnn+Rr3DM$dCehiV+FGLbILK0sN6$ga_hzUzSn?W3?JCXgh3n~txko|QL zBmlJ++5GEJahUnAbodY|4x*6NKL-gw?FBJG7!(vAp$rg(to}Pl07*SaEeJD0n>Zk8 zWcBP&ahQ8x`A85dj;>xDDh^W*3uj}fI7q!QQg}K;#X;(k)0HohIH*4aGbaQp4l)Nh zeS+E{Ab)_wk;_St7zl&R2jMoPbcrn91r>*dH%KiA!}1|4o<U+DJPE2E79Jom5blGD z!}^6FF%X^w6$jN>$o9^Iio^0NNG%92f{KI6US##tpyHrBi!8nhDh>)OWbt)SaZs2b zi*JI8gYp8h_)n1K3=9k=AVFw40SWDbs)x19L1G}h4=Qehrv4CA9M-P_sRiL<P;pqF z7bFJ4r=a2<Xy%-Qiu<66UxJDUpow3Diie<y--3$6`Y|9oLHHh2JO)kuBd9p6Tmh*C zVVHYB{b`UEkQfLHK+|OgNDwL9zJL-W0|P@2n)nN-IIMjSG6RI~K*dYY)Ng=_!}>oU zwID14wHK-!+yjQCJ(w7X235nLv<(_z1@U3&5F`#V2iA{-r8k&3EPcb$6-*q~--V?g zm^gZW8YT|16NF*$1rvw$k3n)E3=#*q6V@*Qv601ZgE$P3aT5>|rXD;M!vJX$B8z_p zaTpjFko^TR6VgwEjJ1H2z|3I-F_6@Q!UlvvYCsr0U%<i#J>9~>0X<#8!mR^j7*aZc zg#)br2C@@`Ve!JCS6rD}l9<Gx2WlyU=?oaFC^aWhuOzjigh4MQu_Tc}FR8egL9Zwu z!~rQc)H6ioS}^E=^+1$BIN&Y<r27Jq1nVwJ&C$!rPfpB%C<2=glGZEAhlwZW=jNv7 zl`!b#<(H)Dx%-9c7MCO@XG1llX2hp~1|Ff_rI-T6BPfc{d;G9+rWP$fz~TpHF{teh z%1SWxF!2Xag$7WALH&A=J)pD#QUl8`pt>C-4lN!)QeeCn&0eq|cnk%`g3zFH7-S~8 z{sU0`A0W!$B*?#T4ro#YY9^?TMiz(Zhl~#(WI%2O@nAH_-yk-+{tHllCxIkE8lV{D zRuB^=3@TSaY*5++iNml7R3T_S5+(+sL17J|VHh;d0TKhb111Kd1;jxl1H%ib!JzO5 zjfO#u1^4wpDnR2lAp6n7Zv#{zlmaOQV{|?!KVq~02dHz1RDOW$2aSrs^uyx65E}mI z_8)*Mgw-!#%aO_v7#~y*V6$HU)Cpx^U|0cF2(lkE$_6zS?*BF%_A7uK%fNs#-Ul)Y zJ^eI7+nng(zXNLj1dt{M1_qG*pmsI7{WGEV!^(ZAa)tn?{h+=pObkSW;tH9Dj5mTr zP;o9)KdLBbtQ1vbJJfz?GJ#7LK=q@if0+AW3OC{KzXzybU|?WC_CIKJ5~d#({~-UM z+YK_i0XpFP02H|l3=E(;Sc`#y0bM`HndoT;q(1>Vv{nPM03-m#Xk74!DK`B#pza6F LYeThyDRlh+OgFm% literal 0 HcmV?d00001 diff --git a/tools/u-boot-tools/binman/.gitignore b/tools/u-boot-tools/binman/.gitignore new file mode 100644 index 0000000..0d20b64 --- /dev/null +++ b/tools/u-boot-tools/binman/.gitignore @@ -0,0 +1 @@ +*.pyc diff --git a/tools/u-boot-tools/binman/README b/tools/u-boot-tools/binman/README new file mode 100644 index 0000000..04ed2b7 --- /dev/null +++ b/tools/u-boot-tools/binman/README @@ -0,0 +1,780 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (c) 2016 Google, Inc + +Introduction +------------ + +Firmware often consists of several components which must be packaged together. +For example, we may have SPL, U-Boot, a device tree and an environment area +grouped together and placed in MMC flash. When the system starts, it must be +able to find these pieces. + +So far U-Boot has not provided a way to handle creating such images in a +general way. Each SoC does what it needs to build an image, often packing or +concatenating images in the U-Boot build system. + +Binman aims to provide a mechanism for building images, from simple +SPL + U-Boot combinations, to more complex arrangements with many parts. + + +What it does +------------ + +Binman reads your board's device tree and finds a node which describes the +required image layout. It uses this to work out what to place where. The +output file normally contains the device tree, so it is in principle possible +to read an image and extract its constituent parts. + + +Features +-------- + +So far binman is pretty simple. It supports binary blobs, such as 'u-boot', +'spl' and 'fdt'. It supports empty entries (such as setting to 0xff). It can +place entries at a fixed location in the image, or fit them together with +suitable padding and alignment. It provides a way to process binaries before +they are included, by adding a Python plug-in. The device tree is available +to U-Boot at run-time so that the images can be interpreted. + +Binman does not yet update the device tree with the final location of +everything when it is done. A simple C structure could be generated for +constrained environments like SPL (using dtoc) but this is also not +implemented. + +Binman can also support incorporating filesystems in the image if required. +For example x86 platforms may use CBFS in some cases. + +Binman is intended for use with U-Boot but is designed to be general enough +to be useful in other image-packaging situations. + + +Motivation +---------- + +Packaging of firmware is quite a different task from building the various +parts. In many cases the various binaries which go into the image come from +separate build systems. For example, ARM Trusted Firmware is used on ARMv8 +devices but is not built in the U-Boot tree. If a Linux kernel is included +in the firmware image, it is built elsewhere. + +It is of course possible to add more and more build rules to the U-Boot +build system to cover these cases. It can shell out to other Makefiles and +build scripts. But it seems better to create a clear divide between building +software and packaging it. + +At present this is handled by manual instructions, different for each board, +on how to create images that will boot. By turning these instructions into a +standard format, we can support making valid images for any board without +manual effort, lots of READMEs, etc. + +Benefits: +- Each binary can have its own build system and tool chain without creating +any dependencies between them +- Avoids the need for a single-shot build: individual parts can be updated +and brought in as needed +- Provides for a standard image description available in the build and at +run-time +- SoC-specific image-signing tools can be accomodated +- Avoids cluttering the U-Boot build system with image-building code +- The image description is automatically available at run-time in U-Boot, +SPL. It can be made available to other software also +- The image description is easily readable (it's a text file in device-tree +format) and permits flexible packing of binaries + + +Terminology +----------- + +Binman uses the following terms: + +- image - an output file containing a firmware image +- binary - an input binary that goes into the image + + +Relationship to FIT +------------------- + +FIT is U-Boot's official image format. It supports multiple binaries with +load / execution addresses, compression. It also supports verification +through hashing and RSA signatures. + +FIT was originally designed to support booting a Linux kernel (with an +optional ramdisk) and device tree chosen from various options in the FIT. +Now that U-Boot supports configuration via device tree, it is possible to +load U-Boot from a FIT, with the device tree chosen by SPL. + +Binman considers FIT to be one of the binaries it can place in the image. + +Where possible it is best to put as much as possible in the FIT, with binman +used to deal with cases not covered by FIT. Examples include initial +execution (since FIT itself does not have an executable header) and dealing +with device boundaries, such as the read-only/read-write separation in SPI +flash. + +For U-Boot, binman should not be used to create ad-hoc images in place of +FIT. + + +Relationship to mkimage +----------------------- + +The mkimage tool provides a means to create a FIT. Traditionally it has +needed an image description file: a device tree, like binman, but in a +different format. More recently it has started to support a '-f auto' mode +which can generate that automatically. + +More relevant to binman, mkimage also permits creation of many SoC-specific +image types. These can be listed by running 'mkimage -T list'. Examples +include 'rksd', the Rockchip SD/MMC boot format. The mkimage tool is often +called from the U-Boot build system for this reason. + +Binman considers the output files created by mkimage to be binary blobs +which it can place in an image. Binman does not replace the mkimage tool or +this purpose. It would be possible in some situations to create a new entry +type for the images in mkimage, but this would not add functionality. It +seems better to use the mkimage tool to generate binaries and avoid blurring +the boundaries between building input files (mkimage) and packaging then +into a final image (binman). + + +Example use of binman in U-Boot +------------------------------- + +Binman aims to replace some of the ad-hoc image creation in the U-Boot +build system. + +Consider sunxi. It has the following steps: + +1. It uses a custom mksunxiboot tool to build an SPL image called +sunxi-spl.bin. This should probably move into mkimage. + +2. It uses mkimage to package U-Boot into a legacy image file (so that it can +hold the load and execution address) called u-boot.img. + +3. It builds a final output image called u-boot-sunxi-with-spl.bin which +consists of sunxi-spl.bin, some padding and u-boot.img. + +Binman is intended to replace the last step. The U-Boot build system builds +u-boot.bin and sunxi-spl.bin. Binman can then take over creation of +sunxi-spl.bin (by calling mksunxiboot, or hopefully one day mkimage). In any +case, it would then create the image from the component parts. + +This simplifies the U-Boot Makefile somewhat, since various pieces of logic +can be replaced by a call to binman. + + +Example use of binman for x86 +----------------------------- + +In most cases x86 images have a lot of binary blobs, 'black-box' code +provided by Intel which must be run for the platform to work. Typically +these blobs are not relocatable and must be placed at fixed areas in the +firmware image. + +Currently this is handled by ifdtool, which places microcode, FSP, MRC, VGA +BIOS, reference code and Intel ME binaries into a u-boot.rom file. + +Binman is intended to replace all of this, with ifdtool left to handle only +the configuration of the Intel-format descriptor. + + +Running binman +-------------- + +Type: + + binman -b <board_name> + +to build an image for a board. The board name is the same name used when +configuring U-Boot (e.g. for sandbox_defconfig the board name is 'sandbox'). +Binman assumes that the input files for the build are in ../b/<board_name>. + +Or you can specify this explicitly: + + binman -I <build_path> + +where <build_path> is the build directory containing the output of the U-Boot +build. + +(Future work will make this more configurable) + +In either case, binman picks up the device tree file (u-boot.dtb) and looks +for its instructions in the 'binman' node. + +Binman has a few other options which you can see by running 'binman -h'. + + +Enabling binman for a board +--------------------------- + +At present binman is invoked from a rule in the main Makefile. Typically you +will have a rule like: + +ifneq ($(CONFIG_ARCH_<something>),) +u-boot-<your_suffix>.bin: <input_file_1> <input_file_2> checkbinman FORCE + $(call if_changed,binman) +endif + +This assumes that u-boot-<your_suffix>.bin is a target, and is the final file +that you need to produce. You can make it a target by adding it to ALL-y +either in the main Makefile or in a config.mk file in your arch subdirectory. + +Once binman is executed it will pick up its instructions from a device-tree +file, typically <soc>-u-boot.dtsi, where <soc> is your CONFIG_SYS_SOC value. +You can use other, more specific CONFIG options - see 'Automatic .dtsi +inclusion' below. + + +Image description format +------------------------ + +The binman node is called 'binman'. An example image description is shown +below: + + binman { + filename = "u-boot-sunxi-with-spl.bin"; + pad-byte = <0xff>; + blob { + filename = "spl/sunxi-spl.bin"; + }; + u-boot { + offset = <CONFIG_SPL_PAD_TO>; + }; + }; + + +This requests binman to create an image file called u-boot-sunxi-with-spl.bin +consisting of a specially formatted SPL (spl/sunxi-spl.bin, built by the +normal U-Boot Makefile), some 0xff padding, and a U-Boot legacy image. The +padding comes from the fact that the second binary is placed at +CONFIG_SPL_PAD_TO. If that line were omitted then the U-Boot binary would +immediately follow the SPL binary. + +The binman node describes an image. The sub-nodes describe entries in the +image. Each entry represents a region within the overall image. The name of +the entry (blob, u-boot) tells binman what to put there. For 'blob' we must +provide a filename. For 'u-boot', binman knows that this means 'u-boot.bin'. + +Entries are normally placed into the image sequentially, one after the other. +The image size is the total size of all entries. As you can see, you can +specify the start offset of an entry using the 'offset' property. + +Note that due to a device tree requirement, all entries must have a unique +name. If you want to put the same binary in the image multiple times, you can +use any unique name, with the 'type' property providing the type. + +The attributes supported for entries are described below. + +offset: + This sets the offset of an entry within the image or section containing + it. The first byte of the image is normally at offset 0. If 'offset' is + not provided, binman sets it to the end of the previous region, or the + start of the image's entry area (normally 0) if there is no previous + region. + +align: + This sets the alignment of the entry. The entry offset is adjusted + so that the entry starts on an aligned boundary within the image. For + example 'align = <16>' means that the entry will start on a 16-byte + boundary. Alignment shold be a power of 2. If 'align' is not + provided, no alignment is performed. + +size: + This sets the size of the entry. The contents will be padded out to + this size. If this is not provided, it will be set to the size of the + contents. + +pad-before: + Padding before the contents of the entry. Normally this is 0, meaning + that the contents start at the beginning of the entry. This can be + offset the entry contents a little. Defaults to 0. + +pad-after: + Padding after the contents of the entry. Normally this is 0, meaning + that the entry ends at the last byte of content (unless adjusted by + other properties). This allows room to be created in the image for + this entry to expand later. Defaults to 0. + +align-size: + This sets the alignment of the entry size. For example, to ensure + that the size of an entry is a multiple of 64 bytes, set this to 64. + If 'align-size' is not provided, no alignment is performed. + +align-end: + This sets the alignment of the end of an entry. Some entries require + that they end on an alignment boundary, regardless of where they + start. This does not move the start of the entry, so the contents of + the entry will still start at the beginning. But there may be padding + at the end. If 'align-end' is not provided, no alignment is performed. + +filename: + For 'blob' types this provides the filename containing the binary to + put into the entry. If binman knows about the entry type (like + u-boot-bin), then there is no need to specify this. + +type: + Sets the type of an entry. This defaults to the entry name, but it is + possible to use any name, and then add (for example) 'type = "u-boot"' + to specify the type. + +offset-unset: + Indicates that the offset of this entry should not be set by placing + it immediately after the entry before. Instead, is set by another + entry which knows where this entry should go. When this boolean + property is present, binman will give an error if another entry does + not set the offset (with the GetOffsets() method). + +image-pos: + This cannot be set on entry (or at least it is ignored if it is), but + with the -u option, binman will set it to the absolute image position + for each entry. This makes it easy to find out exactly where the entry + ended up in the image, regardless of parent sections, etc. + +expand-size: + Expand the size of this entry to fit available space. This space is only + limited by the size of the image/section and the position of the next + entry. + +The attributes supported for images and sections are described below. Several +are similar to those for entries. + +size: + Sets the image size in bytes, for example 'size = <0x100000>' for a + 1MB image. + +align-size: + This sets the alignment of the image size. For example, to ensure + that the image ends on a 512-byte boundary, use 'align-size = <512>'. + If 'align-size' is not provided, no alignment is performed. + +pad-before: + This sets the padding before the image entries. The first entry will + be positioned after the padding. This defaults to 0. + +pad-after: + This sets the padding after the image entries. The padding will be + placed after the last entry. This defaults to 0. + +pad-byte: + This specifies the pad byte to use when padding in the image. It + defaults to 0. To use 0xff, you would add 'pad-byte = <0xff>'. + +filename: + This specifies the image filename. It defaults to 'image.bin'. + +sort-by-offset: + This causes binman to reorder the entries as needed to make sure they + are in increasing positional order. This can be used when your entry + order may not match the positional order. A common situation is where + the 'offset' properties are set by CONFIG options, so their ordering is + not known a priori. + + This is a boolean property so needs no value. To enable it, add a + line 'sort-by-offset;' to your description. + +multiple-images: + Normally only a single image is generated. To create more than one + image, put this property in the binman node. For example, this will + create image1.bin containing u-boot.bin, and image2.bin containing + both spl/u-boot-spl.bin and u-boot.bin: + + binman { + multiple-images; + image1 { + u-boot { + }; + }; + + image2 { + spl { + }; + u-boot { + }; + }; + }; + +end-at-4gb: + For x86 machines the ROM offsets start just before 4GB and extend + up so that the image finished at the 4GB boundary. This boolean + option can be enabled to support this. The image size must be + provided so that binman knows when the image should start. For an + 8MB ROM, the offset of the first entry would be 0xfff80000 with + this option, instead of 0 without this option. + +skip-at-start: + This property specifies the entry offset of the first entry. + + For PowerPC mpc85xx based CPU, CONFIG_SYS_TEXT_BASE is the entry + offset of the first entry. It can be 0xeff40000 or 0xfff40000 for + nor flash boot, 0x201000 for sd boot etc. + + 'end-at-4gb' property is not applicable where CONFIG_SYS_TEXT_BASE + + Image size != 4gb. + +Examples of the above options can be found in the tests. See the +tools/binman/test directory. + +It is possible to have the same binary appear multiple times in the image, +either by using a unit number suffix (u-boot@0, u-boot@1) or by using a +different name for each and specifying the type with the 'type' attribute. + + +Sections and hierachical images +------------------------------- + +Sometimes it is convenient to split an image into several pieces, each of which +contains its own set of binaries. An example is a flash device where part of +the image is read-only and part is read-write. We can set up sections for each +of these, and place binaries in them independently. The image is still produced +as a single output file. + +This feature provides a way of creating hierarchical images. For example here +is an example image with two copies of U-Boot. One is read-only (ro), intended +to be written only in the factory. Another is read-write (rw), so that it can be +upgraded in the field. The sizes are fixed so that the ro/rw boundary is known +and can be programmed: + + binman { + section@0 { + read-only; + name-prefix = "ro-"; + size = <0x100000>; + u-boot { + }; + }; + section@1 { + name-prefix = "rw-"; + size = <0x100000>; + u-boot { + }; + }; + }; + +This image could be placed into a SPI flash chip, with the protection boundary +set at 1MB. + +A few special properties are provided for sections: + +read-only: + Indicates that this section is read-only. This has no impact on binman's + operation, but his property can be read at run time. + +name-prefix: + This string is prepended to all the names of the binaries in the + section. In the example above, the 'u-boot' binaries which actually be + renamed to 'ro-u-boot' and 'rw-u-boot'. This can be useful to + distinguish binaries with otherwise identical names. + + +Entry Documentation +------------------- + +For details on the various entry types supported by binman and how to use them, +see README.entries. This is generated from the source code using: + + binman -E >tools/binman/README.entries + + +Hashing Entries +--------------- + +It is possible to ask binman to hash the contents of an entry and write that +value back to the device-tree node. For example: + + binman { + u-boot { + hash { + algo = "sha256"; + }; + }; + }; + +Here, a new 'value' property will be written to the 'hash' node containing +the hash of the 'u-boot' entry. Only SHA256 is supported at present. Whole +sections can be hased if desired, by adding the 'hash' node to the section. + +The has value can be chcked at runtime by hashing the data actually read and +comparing this has to the value in the device tree. + + +Order of image creation +----------------------- + +Image creation proceeds in the following order, for each entry in the image. + +1. AddMissingProperties() - binman can add calculated values to the device +tree as part of its processing, for example the offset and size of each +entry. This method adds any properties associated with this, expanding the +device tree as needed. These properties can have placeholder values which are +set later by SetCalculatedProperties(). By that stage the size of sections +cannot be changed (since it would cause the images to need to be repacked), +but the correct values can be inserted. + +2. ProcessFdt() - process the device tree information as required by the +particular entry. This may involve adding or deleting properties. If the +processing is complete, this method should return True. If the processing +cannot complete because it needs the ProcessFdt() method of another entry to +run first, this method should return False, in which case it will be called +again later. + +3. GetEntryContents() - the contents of each entry are obtained, normally by +reading from a file. This calls the Entry.ObtainContents() to read the +contents. The default version of Entry.ObtainContents() calls +Entry.GetDefaultFilename() and then reads that file. So a common mechanism +to select a file to read is to override that function in the subclass. The +functions must return True when they have read the contents. Binman will +retry calling the functions a few times if False is returned, allowing +dependencies between the contents of different entries. + +4. GetEntryOffsets() - calls Entry.GetOffsets() for each entry. This can +return a dict containing entries that need updating. The key should be the +entry name and the value is a tuple (offset, size). This allows an entry to +provide the offset and size for other entries. The default implementation +of GetEntryOffsets() returns {}. + +5. PackEntries() - calls Entry.Pack() which figures out the offset and +size of an entry. The 'current' image offset is passed in, and the function +returns the offset immediately after the entry being packed. The default +implementation of Pack() is usually sufficient. + +6. CheckSize() - checks that the contents of all the entries fits within +the image size. If the image does not have a defined size, the size is set +large enough to hold all the entries. + +7. CheckEntries() - checks that the entries do not overlap, nor extend +outside the image. + +8. SetCalculatedProperties() - update any calculated properties in the device +tree. This sets the correct 'offset' and 'size' vaues, for example. + +9. ProcessEntryContents() - this calls Entry.ProcessContents() on each entry. +The default implementatoin does nothing. This can be overriden to adjust the +contents of an entry in some way. For example, it would be possible to create +an entry containing a hash of the contents of some other entries. At this +stage the offset and size of entries should not be adjusted. + +10. WriteSymbols() - write the value of symbols into the U-Boot SPL binary. +See 'Access to binman entry offsets at run time' below for a description of +what happens in this stage. + +11. BuildImage() - builds the image and writes it to a file. This is the final +step. + + +Automatic .dtsi inclusion +------------------------- + +It is sometimes inconvenient to add a 'binman' node to the .dts file for each +board. This can be done by using #include to bring in a common file. Another +approach supported by the U-Boot build system is to automatically include +a common header. You can then put the binman node (and anything else that is +specific to U-Boot, such as u-boot,dm-pre-reloc properies) in that header +file. + +Binman will search for the following files in arch/<arch>/dts: + + <dts>-u-boot.dtsi where <dts> is the base name of the .dts file + <CONFIG_SYS_SOC>-u-boot.dtsi + <CONFIG_SYS_CPU>-u-boot.dtsi + <CONFIG_SYS_VENDOR>-u-boot.dtsi + u-boot.dtsi + +U-Boot will only use the first one that it finds. If you need to include a +more general file you can do that from the more specific file using #include. +If you are having trouble figuring out what is going on, you can uncomment +the 'warning' line in scripts/Makefile.lib to see what it has found: + + # Uncomment for debugging + # This shows all the files that were considered and the one that we chose. + # u_boot_dtsi_options_debug = $(u_boot_dtsi_options_raw) + + +Access to binman entry offsets at run time (symbols) +---------------------------------------------------- + +Binman assembles images and determines where each entry is placed in the image. +This information may be useful to U-Boot at run time. For example, in SPL it +is useful to be able to find the location of U-Boot so that it can be executed +when SPL is finished. + +Binman allows you to declare symbols in the SPL image which are filled in +with their correct values during the build. For example: + + binman_sym_declare(ulong, u_boot_any, offset); + +declares a ulong value which will be assigned to the offset of any U-Boot +image (u-boot.bin, u-boot.img, u-boot-nodtb.bin) that is present in the image. +You can access this value with something like: + + ulong u_boot_offset = binman_sym(ulong, u_boot_any, offset); + +Thus u_boot_offset will be set to the offset of U-Boot in memory, assuming that +the whole image has been loaded, or is available in flash. You can then jump to +that address to start U-Boot. + +At present this feature is only supported in SPL. In principle it is possible +to fill in such symbols in U-Boot proper, as well. + + +Access to binman entry offsets at run time (fdt) +------------------------------------------------ + +Binman can update the U-Boot FDT to include the final position and size of +each entry in the images it processes. The option to enable this is -u and it +causes binman to make sure that the 'offset', 'image-pos' and 'size' properties +are set correctly for every entry. Since it is not necessary to specify these in +the image definition, binman calculates the final values and writes these to +the device tree. These can be used by U-Boot at run-time to find the location +of each entry. + + +Compression +----------- + +Binman support compression for 'blob' entries (those of type 'blob' and +derivatives). To enable this for an entry, add a 'compression' property: + + blob { + filename = "datafile"; + compression = "lz4"; + }; + +The entry will then contain the compressed data, using the 'lz4' compression +algorithm. Currently this is the only one that is supported. + + + +Map files +--------- + +The -m option causes binman to output a .map file for each image that it +generates. This shows the offset and size of each entry. For example: + + Offset Size Name + 00000000 00000028 main-section + 00000000 00000010 section@0 + 00000000 00000004 u-boot + 00000010 00000010 section@1 + 00000000 00000004 u-boot + +This shows a hierarchical image with two sections, each with a single entry. The +offsets of the sections are absolute hex byte offsets within the image. The +offsets of the entries are relative to their respective sections. The size of +each entry is also shown, in bytes (hex). The indentation shows the entries +nested inside their sections. + + +Passing command-line arguments to entries +----------------------------------------- + +Sometimes it is useful to pass binman the value of an entry property from the +command line. For example some entries need access to files and it is not +always convenient to put these filenames in the image definition (device tree). + +The-a option supports this: + + -a<prop>=<value> + +where + + <prop> is the property to set + <value> is the value to set it to + +Not all properties can be provided this way. Only some entries support it, +typically for filenames. + + +Code coverage +------------- + +Binman is a critical tool and is designed to be very testable. Entry +implementations target 100% test coverage. Run 'binman -T' to check this. + +To enable Python test coverage on Debian-type distributions (e.g. Ubuntu): + + $ sudo apt-get install python-coverage python-pytest + + +Advanced Features / Technical docs +---------------------------------- + +The behaviour of entries is defined by the Entry class. All other entries are +a subclass of this. An important subclass is Entry_blob which takes binary +data from a file and places it in the entry. In fact most entry types are +subclasses of Entry_blob. + +Each entry type is a separate file in the tools/binman/etype directory. Each +file contains a class called Entry_<type> where <type> is the entry type. +New entry types can be supported by adding new files in that directory. +These will automatically be detected by binman when needed. + +Entry properties are documented in entry.py. The entry subclasses are free +to change the values of properties to support special behaviour. For example, +when Entry_blob loads a file, it sets content_size to the size of the file. +Entry classes can adjust other entries. For example, an entry that knows +where other entries should be positioned can set up those entries' offsets +so they don't need to be set in the binman decription. It can also adjust +entry contents. + +Most of the time such essoteric behaviour is not needed, but it can be +essential for complex images. + +If you need to specify a particular device-tree compiler to use, you can define +the DTC environment variable. This can be useful when the system dtc is too +old. + +To enable a full backtrace and other debugging features in binman, pass +BINMAN_DEBUG=1 to your build: + + make sandbox_defconfig + make BINMAN_DEBUG=1 + + +History / Credits +----------------- + +Binman takes a lot of inspiration from a Chrome OS tool called +'cros_bundle_firmware', which I wrote some years ago. That tool was based on +a reasonably simple and sound design but has expanded greatly over the +years. In particular its handling of x86 images is convoluted. + +Quite a few lessons have been learned which are hopefully applied here. + + +Design notes +------------ + +On the face of it, a tool to create firmware images should be fairly simple: +just find all the input binaries and place them at the right place in the +image. The difficulty comes from the wide variety of input types (simple +flat binaries containing code, packaged data with various headers), packing +requirments (alignment, spacing, device boundaries) and other required +features such as hierarchical images. + +The design challenge is to make it easy to create simple images, while +allowing the more complex cases to be supported. For example, for most +images we don't much care exactly where each binary ends up, so we should +not have to specify that unnecessarily. + +New entry types should aim to provide simple usage where possible. If new +core features are needed, they can be added in the Entry base class. + + +To do +----- + +Some ideas: +- Use of-platdata to make the information available to code that is unable + to use device tree (such as a very small SPL image) +- Allow easy building of images by specifying just the board name +- Produce a full Python binding for libfdt (for upstream). This is nearing + completion but some work remains +- Add an option to decode an image into the constituent binaries +- Support building an image for a board (-b) more completely, with a + configurable build directory +- Consider making binman work with buildman, although if it is used in the + Makefile, this will be automatic + +-- +Simon Glass <sjg@chromium.org> +7/7/2016 diff --git a/tools/u-boot-tools/binman/README.entries b/tools/u-boot-tools/binman/README.entries new file mode 100644 index 0000000..9fc2f83 --- /dev/null +++ b/tools/u-boot-tools/binman/README.entries @@ -0,0 +1,698 @@ +Binman Entry Documentation +=========================== + +This file describes the entry types supported by binman. These entry types can +be placed in an image one by one to build up a final firmware image. It is +fairly easy to create new entry types. Just add a new file to the 'etype' +directory. You can use the existing entries as examples. + +Note that some entries are subclasses of others, using and extending their +features to produce new behaviours. + + + +Entry: blob: Entry containing an arbitrary binary blob +------------------------------------------------------ + +Note: This should not be used by itself. It is normally used as a parent +class by other entry types. + +Properties / Entry arguments: + - filename: Filename of file to read into entry + - compress: Compression algorithm to use: + none: No compression + lz4: Use lz4 compression (via 'lz4' command-line utility) + +This entry reads data from a file and places it in the entry. The +default filename is often specified specified by the subclass. See for +example the 'u_boot' entry which provides the filename 'u-boot.bin'. + +If compression is enabled, an extra 'uncomp-size' property is written to +the node (if enabled with -u) which provides the uncompressed size of the +data. + + + +Entry: blob-dtb: A blob that holds a device tree +------------------------------------------------ + +This is a blob containing a device tree. The contents of the blob are +obtained from the list of available device-tree files, managed by the +'state' module. + + + +Entry: blob-named-by-arg: A blob entry which gets its filename property from its subclass +----------------------------------------------------------------------------------------- + +Properties / Entry arguments: + - <xxx>-path: Filename containing the contents of this entry (optional, + defaults to 0) + +where <xxx> is the blob_fname argument to the constructor. + +This entry cannot be used directly. Instead, it is used as a parent class +for another entry, which defined blob_fname. This parameter is used to +set the entry-arg or property containing the filename. The entry-arg or +property is in turn used to set the actual filename. + +See cros_ec_rw for an example of this. + + + +Entry: cros-ec-rw: A blob entry which contains a Chromium OS read-write EC image +-------------------------------------------------------------------------------- + +Properties / Entry arguments: + - cros-ec-rw-path: Filename containing the EC image + +This entry holds a Chromium OS EC (embedded controller) image, for use in +updating the EC on startup via software sync. + + + +Entry: files: Entry containing a set of files +--------------------------------------------- + +Properties / Entry arguments: + - pattern: Filename pattern to match the files to include + - compress: Compression algorithm to use: + none: No compression + lz4: Use lz4 compression (via 'lz4' command-line utility) + +This entry reads a number of files and places each in a separate sub-entry +within this entry. To access these you need to enable device-tree updates +at run-time so you can obtain the file positions. + + + +Entry: fill: An entry which is filled to a particular byte value +---------------------------------------------------------------- + +Properties / Entry arguments: + - fill-byte: Byte to use to fill the entry + +Note that the size property must be set since otherwise this entry does not +know how large it should be. + +You can often achieve the same effect using the pad-byte property of the +overall image, in that the space between entries will then be padded with +that byte. But this entry is sometimes useful for explicitly setting the +byte value of a region. + + + +Entry: fmap: An entry which contains an Fmap section +---------------------------------------------------- + +Properties / Entry arguments: + None + +FMAP is a simple format used by flashrom, an open-source utility for +reading and writing the SPI flash, typically on x86 CPUs. The format +provides flashrom with a list of areas, so it knows what it in the flash. +It can then read or write just a single area, instead of the whole flash. + +The format is defined by the flashrom project, in the file lib/fmap.h - +see www.flashrom.org/Flashrom for more information. + +When used, this entry will be populated with an FMAP which reflects the +entries in the current image. Note that any hierarchy is squashed, since +FMAP does not support this. + + + +Entry: gbb: An entry which contains a Chromium OS Google Binary Block +--------------------------------------------------------------------- + +Properties / Entry arguments: + - hardware-id: Hardware ID to use for this build (a string) + - keydir: Directory containing the public keys to use + - bmpblk: Filename containing images used by recovery + +Chromium OS uses a GBB to store various pieces of information, in particular +the root and recovery keys that are used to verify the boot process. Some +more details are here: + + https://www.chromium.org/chromium-os/firmware-porting-guide/2-concepts + +but note that the page dates from 2013 so is quite out of date. See +README.chromium for how to obtain the required keys and tools. + + + +Entry: intel-cmc: Entry containing an Intel Chipset Micro Code (CMC) file +------------------------------------------------------------------------- + +Properties / Entry arguments: + - filename: Filename of file to read into entry + +This file contains microcode for some devices in a special format. An +example filename is 'Microcode/C0_22211.BIN'. + +See README.x86 for information about x86 binary blobs. + + + +Entry: intel-descriptor: Intel flash descriptor block (4KB) +----------------------------------------------------------- + +Properties / Entry arguments: + filename: Filename of file containing the descriptor. This is typically + a 4KB binary file, sometimes called 'descriptor.bin' + +This entry is placed at the start of flash and provides information about +the SPI flash regions. In particular it provides the base address and +size of the ME (Management Engine) region, allowing us to place the ME +binary in the right place. + +With this entry in your image, the position of the 'intel-me' entry will be +fixed in the image, which avoids you needed to specify an offset for that +region. This is useful, because it is not possible to change the position +of the ME region without updating the descriptor. + +See README.x86 for information about x86 binary blobs. + + + +Entry: intel-fsp: Entry containing an Intel Firmware Support Package (FSP) file +------------------------------------------------------------------------------- + +Properties / Entry arguments: + - filename: Filename of file to read into entry + +This file contains binary blobs which are used on some devices to make the +platform work. U-Boot executes this code since it is not possible to set up +the hardware using U-Boot open-source code. Documentation is typically not +available in sufficient detail to allow this. + +An example filename is 'FSP/QUEENSBAY_FSP_GOLD_001_20-DECEMBER-2013.fd' + +See README.x86 for information about x86 binary blobs. + + + +Entry: intel-me: Entry containing an Intel Management Engine (ME) file +---------------------------------------------------------------------- + +Properties / Entry arguments: + - filename: Filename of file to read into entry + +This file contains code used by the SoC that is required to make it work. +The Management Engine is like a background task that runs things that are +not clearly documented, but may include keyboard, deplay and network +access. For platform that use ME it is not possible to disable it. U-Boot +does not directly execute code in the ME binary. + +A typical filename is 'me.bin'. + +See README.x86 for information about x86 binary blobs. + + + +Entry: intel-mrc: Entry containing an Intel Memory Reference Code (MRC) file +---------------------------------------------------------------------------- + +Properties / Entry arguments: + - filename: Filename of file to read into entry + +This file contains code for setting up the SDRAM on some Intel systems. This +is executed by U-Boot when needed early during startup. A typical filename +is 'mrc.bin'. + +See README.x86 for information about x86 binary blobs. + + + +Entry: intel-vbt: Entry containing an Intel Video BIOS Table (VBT) file +----------------------------------------------------------------------- + +Properties / Entry arguments: + - filename: Filename of file to read into entry + +This file contains code that sets up the integrated graphics subsystem on +some Intel SoCs. U-Boot executes this when the display is started up. + +See README.x86 for information about Intel binary blobs. + + + +Entry: intel-vga: Entry containing an Intel Video Graphics Adaptor (VGA) file +----------------------------------------------------------------------------- + +Properties / Entry arguments: + - filename: Filename of file to read into entry + +This file contains code that sets up the integrated graphics subsystem on +some Intel SoCs. U-Boot executes this when the display is started up. + +This is similar to the VBT file but in a different format. + +See README.x86 for information about Intel binary blobs. + + + +Entry: powerpc-mpc85xx-bootpg-resetvec: PowerPC mpc85xx bootpg + resetvec code for U-Boot +----------------------------------------------------------------------------------------- + +Properties / Entry arguments: + - filename: Filename of u-boot-br.bin (default 'u-boot-br.bin') + +This enrty is valid for PowerPC mpc85xx cpus. This entry holds +'bootpg + resetvec' code for PowerPC mpc85xx CPUs which needs to be +placed at offset 'RESET_VECTOR_ADDRESS - 0xffc'. + + + +Entry: section: Entry that contains other entries +------------------------------------------------- + +Properties / Entry arguments: (see binman README for more information) + - size: Size of section in bytes + - align-size: Align size to a particular power of two + - pad-before: Add padding before the entry + - pad-after: Add padding after the entry + - pad-byte: Pad byte to use when padding + - sort-by-offset: Reorder the entries by offset + - end-at-4gb: Used to build an x86 ROM which ends at 4GB (2^32) + - name-prefix: Adds a prefix to the name of every entry in the section + when writing out the map + +A section is an entry which can contain other entries, thus allowing +hierarchical images to be created. See 'Sections and hierarchical images' +in the binman README for more information. + + + +Entry: text: An entry which contains text +----------------------------------------- + +The text can be provided either in the node itself or by a command-line +argument. There is a level of indirection to allow multiple text strings +and sharing of text. + +Properties / Entry arguments: + text-label: The value of this string indicates the property / entry-arg + that contains the string to place in the entry + <xxx> (actual name is the value of text-label): contains the string to + place in the entry. + +Example node: + + text { + size = <50>; + text-label = "message"; + }; + +You can then use: + + binman -amessage="this is my message" + +and binman will insert that string into the entry. + +It is also possible to put the string directly in the node: + + text { + size = <8>; + text-label = "message"; + message = "a message directly in the node" + }; + +The text is not itself nul-terminated. This can be achieved, if required, +by setting the size of the entry to something larger than the text. + + + +Entry: u-boot: U-Boot flat binary +--------------------------------- + +Properties / Entry arguments: + - filename: Filename of u-boot.bin (default 'u-boot.bin') + +This is the U-Boot binary, containing relocation information to allow it +to relocate itself at runtime. The binary typically includes a device tree +blob at the end of it. Use u_boot_nodtb if you want to package the device +tree separately. + +U-Boot can access binman symbols at runtime. See: + + 'Access to binman entry offsets at run time (fdt)' + +in the binman README for more information. + + + +Entry: u-boot-dtb: U-Boot device tree +------------------------------------- + +Properties / Entry arguments: + - filename: Filename of u-boot.dtb (default 'u-boot.dtb') + +This is the U-Boot device tree, containing configuration information for +U-Boot. U-Boot needs this to know what devices are present and which drivers +to activate. + +Note: This is mostly an internal entry type, used by others. This allows +binman to know which entries contain a device tree. + + + +Entry: u-boot-dtb-with-ucode: A U-Boot device tree file, with the microcode removed +----------------------------------------------------------------------------------- + +Properties / Entry arguments: + - filename: Filename of u-boot.dtb (default 'u-boot.dtb') + +See Entry_u_boot_ucode for full details of the three entries involved in +this process. This entry provides the U-Boot device-tree file, which +contains the microcode. If the microcode is not being collated into one +place then the offset and size of the microcode is recorded by this entry, +for use by u_boot_with_ucode_ptr. If it is being collated, then this +entry deletes the microcode from the device tree (to save space) and makes +it available to u_boot_ucode. + + + +Entry: u-boot-elf: U-Boot ELF image +----------------------------------- + +Properties / Entry arguments: + - filename: Filename of u-boot (default 'u-boot') + +This is the U-Boot ELF image. It does not include a device tree but can be +relocated to any address for execution. + + + +Entry: u-boot-img: U-Boot legacy image +-------------------------------------- + +Properties / Entry arguments: + - filename: Filename of u-boot.img (default 'u-boot.img') + +This is the U-Boot binary as a packaged image, in legacy format. It has a +header which allows it to be loaded at the correct address for execution. + +You should use FIT (Flat Image Tree) instead of the legacy image for new +applications. + + + +Entry: u-boot-nodtb: U-Boot flat binary without device tree appended +-------------------------------------------------------------------- + +Properties / Entry arguments: + - filename: Filename of u-boot.bin (default 'u-boot-nodtb.bin') + +This is the U-Boot binary, containing relocation information to allow it +to relocate itself at runtime. It does not include a device tree blob at +the end of it so normally cannot work without it. You can add a u_boot_dtb +entry after this one, or use a u_boot entry instead (which contains both +U-Boot and the device tree). + + + +Entry: u-boot-spl: U-Boot SPL binary +------------------------------------ + +Properties / Entry arguments: + - filename: Filename of u-boot-spl.bin (default 'spl/u-boot-spl.bin') + +This is the U-Boot SPL (Secondary Program Loader) binary. This is a small +binary which loads before U-Boot proper, typically into on-chip SRAM. It is +responsible for locating, loading and jumping to U-Boot. Note that SPL is +not relocatable so must be loaded to the correct address in SRAM, or written +to run from the correct address if direct flash execution is possible (e.g. +on x86 devices). + +SPL can access binman symbols at runtime. See: + + 'Access to binman entry offsets at run time (symbols)' + +in the binman README for more information. + +The ELF file 'spl/u-boot-spl' must also be available for this to work, since +binman uses that to look up symbols to write into the SPL binary. + + + +Entry: u-boot-spl-bss-pad: U-Boot SPL binary padded with a BSS region +--------------------------------------------------------------------- + +Properties / Entry arguments: + None + +This is similar to u_boot_spl except that padding is added after the SPL +binary to cover the BSS (Block Started by Symbol) region. This region holds +the various used by SPL. It is set to 0 by SPL when it starts up. If you +want to append data to the SPL image (such as a device tree file), you must +pad out the BSS region to avoid the data overlapping with U-Boot variables. +This entry is useful in that case. It automatically pads out the entry size +to cover both the code, data and BSS. + +The ELF file 'spl/u-boot-spl' must also be available for this to work, since +binman uses that to look up the BSS address. + + + +Entry: u-boot-spl-dtb: U-Boot SPL device tree +--------------------------------------------- + +Properties / Entry arguments: + - filename: Filename of u-boot.dtb (default 'spl/u-boot-spl.dtb') + +This is the SPL device tree, containing configuration information for +SPL. SPL needs this to know what devices are present and which drivers +to activate. + + + +Entry: u-boot-spl-elf: U-Boot SPL ELF image +------------------------------------------- + +Properties / Entry arguments: + - filename: Filename of SPL u-boot (default 'spl/u-boot') + +This is the U-Boot SPL ELF image. It does not include a device tree but can +be relocated to any address for execution. + + + +Entry: u-boot-spl-nodtb: SPL binary without device tree appended +---------------------------------------------------------------- + +Properties / Entry arguments: + - filename: Filename of spl/u-boot-spl-nodtb.bin (default + 'spl/u-boot-spl-nodtb.bin') + +This is the U-Boot SPL binary, It does not include a device tree blob at +the end of it so may not be able to work without it, assuming SPL needs +a device tree to operation on your platform. You can add a u_boot_spl_dtb +entry after this one, or use a u_boot_spl entry instead (which contains +both SPL and the device tree). + + + +Entry: u-boot-spl-with-ucode-ptr: U-Boot SPL with embedded microcode pointer +---------------------------------------------------------------------------- + +This is used when SPL must set up the microcode for U-Boot. + +See Entry_u_boot_ucode for full details of the entries involved in this +process. + + + +Entry: u-boot-tpl: U-Boot TPL binary +------------------------------------ + +Properties / Entry arguments: + - filename: Filename of u-boot-tpl.bin (default 'tpl/u-boot-tpl.bin') + +This is the U-Boot TPL (Tertiary Program Loader) binary. This is a small +binary which loads before SPL, typically into on-chip SRAM. It is +responsible for locating, loading and jumping to SPL, the next-stage +loader. Note that SPL is not relocatable so must be loaded to the correct +address in SRAM, or written to run from the correct address if direct +flash execution is possible (e.g. on x86 devices). + +SPL can access binman symbols at runtime. See: + + 'Access to binman entry offsets at run time (symbols)' + +in the binman README for more information. + +The ELF file 'tpl/u-boot-tpl' must also be available for this to work, since +binman uses that to look up symbols to write into the TPL binary. + + + +Entry: u-boot-tpl-dtb: U-Boot TPL device tree +--------------------------------------------- + +Properties / Entry arguments: + - filename: Filename of u-boot.dtb (default 'tpl/u-boot-tpl.dtb') + +This is the TPL device tree, containing configuration information for +TPL. TPL needs this to know what devices are present and which drivers +to activate. + + + +Entry: u-boot-tpl-dtb-with-ucode: U-Boot TPL with embedded microcode pointer +---------------------------------------------------------------------------- + +This is used when TPL must set up the microcode for U-Boot. + +See Entry_u_boot_ucode for full details of the entries involved in this +process. + + + +Entry: u-boot-tpl-with-ucode-ptr: U-Boot TPL with embedded microcode pointer +---------------------------------------------------------------------------- + +See Entry_u_boot_ucode for full details of the entries involved in this +process. + + + +Entry: u-boot-ucode: U-Boot microcode block +------------------------------------------- + +Properties / Entry arguments: + None + +The contents of this entry are filled in automatically by other entries +which must also be in the image. + +U-Boot on x86 needs a single block of microcode. This is collected from +the various microcode update nodes in the device tree. It is also unable +to read the microcode from the device tree on platforms that use FSP +(Firmware Support Package) binaries, because the API requires that the +microcode is supplied before there is any SRAM available to use (i.e. +the FSP sets up the SRAM / cache-as-RAM but does so in the call that +requires the microcode!). To keep things simple, all x86 platforms handle +microcode the same way in U-Boot (even non-FSP platforms). This is that +a table is placed at _dt_ucode_base_size containing the base address and +size of the microcode. This is either passed to the FSP (for FSP +platforms), or used to set up the microcode (for non-FSP platforms). +This all happens in the build system since it is the only way to get +the microcode into a single blob and accessible without SRAM. + +There are two cases to handle. If there is only one microcode blob in +the device tree, then the ucode pointer it set to point to that. This +entry (u-boot-ucode) is empty. If there is more than one update, then +this entry holds the concatenation of all updates, and the device tree +entry (u-boot-dtb-with-ucode) is updated to remove the microcode. This +last step ensures that that the microcode appears in one contiguous +block in the image and is not unnecessarily duplicated in the device +tree. It is referred to as 'collation' here. + +Entry types that have a part to play in handling microcode: + + Entry_u_boot_with_ucode_ptr: + Contains u-boot-nodtb.bin (i.e. U-Boot without the device tree). + It updates it with the address and size of the microcode so that + U-Boot can find it early on start-up. + Entry_u_boot_dtb_with_ucode: + Contains u-boot.dtb. It stores the microcode in a + 'self.ucode_data' property, which is then read by this class to + obtain the microcode if needed. If collation is performed, it + removes the microcode from the device tree. + Entry_u_boot_ucode: + This class. If collation is enabled it reads the microcode from + the Entry_u_boot_dtb_with_ucode entry, and uses it as the + contents of this entry. + + + +Entry: u-boot-with-ucode-ptr: U-Boot with embedded microcode pointer +-------------------------------------------------------------------- + +Properties / Entry arguments: + - filename: Filename of u-boot-nodtb.dtb (default 'u-boot-nodtb.dtb') + - optional-ucode: boolean property to make microcode optional. If the + u-boot.bin image does not include microcode, no error will + be generated. + +See Entry_u_boot_ucode for full details of the three entries involved in +this process. This entry updates U-Boot with the offset and size of the +microcode, to allow early x86 boot code to find it without doing anything +complicated. Otherwise it is the same as the u_boot entry. + + + +Entry: vblock: An entry which contains a Chromium OS verified boot block +------------------------------------------------------------------------ + +Properties / Entry arguments: + - keydir: Directory containing the public keys to use + - keyblock: Name of the key file to use (inside keydir) + - signprivate: Name of provide key file to use (inside keydir) + - version: Version number of the vblock (typically 1) + - kernelkey: Name of the kernel key to use (inside keydir) + - preamble-flags: Value of the vboot preamble flags (typically 0) + +Output files: + - input.<unique_name> - input file passed to futility + - vblock.<unique_name> - output file generated by futility (which is + used as the entry contents) + +Chromium OS signs the read-write firmware and kernel, writing the signature +in this block. This allows U-Boot to verify that the next firmware stage +and kernel are genuine. + + + +Entry: x86-start16: x86 16-bit start-up code for U-Boot +------------------------------------------------------- + +Properties / Entry arguments: + - filename: Filename of u-boot-x86-16bit.bin (default + 'u-boot-x86-16bit.bin') + +x86 CPUs start up in 16-bit mode, even if they are 32-bit CPUs. This code +must be placed at a particular address. This entry holds that code. It is +typically placed at offset CONFIG_SYS_X86_START16. The code is responsible +for changing to 32-bit mode and jumping to U-Boot's entry point, which +requires 32-bit mode (for 32-bit U-Boot). + +For 64-bit U-Boot, the 'x86_start16_spl' entry type is used instead. + + + +Entry: x86-start16-spl: x86 16-bit start-up code for SPL +-------------------------------------------------------- + +Properties / Entry arguments: + - filename: Filename of spl/u-boot-x86-16bit-spl.bin (default + 'spl/u-boot-x86-16bit-spl.bin') + +x86 CPUs start up in 16-bit mode, even if they are 64-bit CPUs. This code +must be placed at a particular address. This entry holds that code. It is +typically placed at offset CONFIG_SYS_X86_START16. The code is responsible +for changing to 32-bit mode and starting SPL, which in turn changes to +64-bit mode and jumps to U-Boot (for 64-bit U-Boot). + +For 32-bit U-Boot, the 'x86_start16' entry type is used instead. + + + +Entry: x86-start16-tpl: x86 16-bit start-up code for TPL +-------------------------------------------------------- + +Properties / Entry arguments: + - filename: Filename of tpl/u-boot-x86-16bit-tpl.bin (default + 'tpl/u-boot-x86-16bit-tpl.bin') + +x86 CPUs start up in 16-bit mode, even if they are 64-bit CPUs. This code +must be placed at a particular address. This entry holds that code. It is +typically placed at offset CONFIG_SYS_X86_START16. The code is responsible +for changing to 32-bit mode and starting TPL, which in turn jumps to SPL. + +If TPL is not being used, the 'x86_start16_spl or 'x86_start16' entry types +may be used instead. + + + diff --git a/tools/u-boot-tools/binman/binman b/tools/u-boot-tools/binman/binman new file mode 120000 index 0000000..979b7e4 --- /dev/null +++ b/tools/u-boot-tools/binman/binman @@ -0,0 +1 @@ +binman.py \ No newline at end of file diff --git a/tools/u-boot-tools/binman/binman.py b/tools/u-boot-tools/binman/binman.py new file mode 100755 index 0000000..439908e --- /dev/null +++ b/tools/u-boot-tools/binman/binman.py @@ -0,0 +1,157 @@ +#!/usr/bin/env python2 +# SPDX-License-Identifier: GPL-2.0+ + +# Copyright (c) 2016 Google, Inc +# Written by Simon Glass <sjg@chromium.org> +# +# Creates binary images from input files controlled by a description +# + +"""See README for more information""" + +import glob +import multiprocessing +import os +import sys +import traceback +import unittest + +# Bring in the patman and dtoc libraries +our_path = os.path.dirname(os.path.realpath(__file__)) +for dirname in ['../patman', '../dtoc', '..', '../concurrencytest']: + sys.path.insert(0, os.path.join(our_path, dirname)) + +# Bring in the libfdt module +sys.path.insert(0, 'scripts/dtc/pylibfdt') +sys.path.insert(0, os.path.join(our_path, + '../../build-sandbox_spl/scripts/dtc/pylibfdt')) + +import cmdline +import command +use_concurrent = True +try: + from concurrencytest import ConcurrentTestSuite, fork_for_tests +except: + use_concurrent = False +import control +import test_util + +def RunTests(debug, processes, args): + """Run the functional tests and any embedded doctests + + Args: + debug: True to enable debugging, which shows a full stack trace on error + args: List of positional args provided to binman. This can hold a test + name to execute (as in 'binman -t testSections', for example) + processes: Number of processes to use to run tests (None=same as #CPUs) + """ + import elf_test + import entry_test + import fdt_test + import ftest + import image_test + import test + import doctest + + result = unittest.TestResult() + for module in []: + suite = doctest.DocTestSuite(module) + suite.run(result) + + sys.argv = [sys.argv[0]] + if debug: + sys.argv.append('-D') + if debug: + sys.argv.append('-D') + + # Run the entry tests first ,since these need to be the first to import the + # 'entry' module. + test_name = args and args[0] or None + suite = unittest.TestSuite() + loader = unittest.TestLoader() + for module in (entry_test.TestEntry, ftest.TestFunctional, fdt_test.TestFdt, + elf_test.TestElf, image_test.TestImage): + if test_name: + try: + suite.addTests(loader.loadTestsFromName(test_name, module)) + except AttributeError: + continue + else: + suite.addTests(loader.loadTestsFromTestCase(module)) + if use_concurrent and processes != 1: + concurrent_suite = ConcurrentTestSuite(suite, + fork_for_tests(processes or multiprocessing.cpu_count())) + concurrent_suite.run(result) + else: + suite.run(result) + + print result + for test, err in result.errors: + print test.id(), err + for test, err in result.failures: + print err, result.failures + if result.errors or result.failures: + print 'binman tests FAILED' + return 1 + return 0 + +def GetEntryModules(include_testing=True): + """Get a set of entry class implementations + + Returns: + Set of paths to entry class filenames + """ + glob_list = glob.glob(os.path.join(our_path, 'etype/*.py')) + return set([os.path.splitext(os.path.basename(item))[0] + for item in glob_list + if include_testing or '_testing' not in item]) + +def RunTestCoverage(): + """Run the tests and check that we get 100% coverage""" + glob_list = GetEntryModules(False) + all_set = set([os.path.splitext(os.path.basename(item))[0] + for item in glob_list if '_testing' not in item]) + test_util.RunTestCoverage('tools/binman/binman.py', None, + ['*test*', '*binman.py', 'tools/patman/*', 'tools/dtoc/*'], + options.build_dir, all_set) + +def RunBinman(options, args): + """Main entry point to binman once arguments are parsed + + Args: + options: Command-line options + args: Non-option arguments + """ + ret_code = 0 + + # For testing: This enables full exception traces. + #options.debug = True + + if not options.debug: + sys.tracebacklimit = 0 + + if options.test: + ret_code = RunTests(options.debug, options.processes, args[1:]) + + elif options.test_coverage: + RunTestCoverage() + + elif options.entry_docs: + control.WriteEntryDocs(GetEntryModules()) + + else: + try: + ret_code = control.Binman(options, args) + except Exception as e: + print 'binman: %s' % e + if options.debug: + print + traceback.print_exc() + ret_code = 1 + return ret_code + + +if __name__ == "__main__": + (options, args) = cmdline.ParseArgs(sys.argv) + ret_code = RunBinman(options, args) + sys.exit(ret_code) diff --git a/tools/u-boot-tools/binman/bsection.py b/tools/u-boot-tools/binman/bsection.py new file mode 100644 index 0000000..ccf2920 --- /dev/null +++ b/tools/u-boot-tools/binman/bsection.py @@ -0,0 +1,464 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (c) 2018 Google, Inc +# Written by Simon Glass <sjg@chromium.org> +# +# Base class for sections (collections of entries) +# + +from __future__ import print_function + +from collections import OrderedDict +from sets import Set +import sys + +import fdt_util +import re +import state +import tools + +class Section(object): + """A section which contains multiple entries + + A section represents a collection of entries. There must be one or more + sections in an image. Sections are used to group entries together. + + Attributes: + _node: Node object that contains the section definition in device tree + _parent_section: Parent Section object which created this Section + _size: Section size in bytes, or None if not known yet + _align_size: Section size alignment, or None + _pad_before: Number of bytes before the first entry starts. This + effectively changes the place where entry offset 0 starts + _pad_after: Number of bytes after the last entry ends. The last + entry will finish on or before this boundary + _pad_byte: Byte to use to pad the section where there is no entry + _sort: True if entries should be sorted by offset, False if they + must be in-order in the device tree description + _skip_at_start: Number of bytes before the first entry starts. These + effectively adjust the starting offset of entries. For example, + if _pad_before is 16, then the first entry would start at 16. + An entry with offset = 20 would in fact be written at offset 4 + in the image file. + _end_4gb: Indicates that the section ends at the 4GB boundary. This is + used for x86 images, which want to use offsets such that a memory + address (like 0xff800000) is the first entry offset. This causes + _skip_at_start to be set to the starting memory address. + _name_prefix: Prefix to add to the name of all entries within this + section + _entries: OrderedDict() of entries + """ + def __init__(self, name, parent_section, node, image, test=False): + global entry + global Entry + import entry + from entry import Entry + + self._parent_section = parent_section + self._name = name + self._node = node + self._image = image + self._offset = 0 + self._size = None + self._align_size = None + self._pad_before = 0 + self._pad_after = 0 + self._pad_byte = 0 + self._sort = False + self._skip_at_start = None + self._end_4gb = False + self._name_prefix = '' + self._entries = OrderedDict() + self._image_pos = None + if not test: + self._ReadNode() + self._ReadEntries() + + def _ReadNode(self): + """Read properties from the section node""" + self._size = fdt_util.GetInt(self._node, 'size') + self._align_size = fdt_util.GetInt(self._node, 'align-size') + if tools.NotPowerOfTwo(self._align_size): + self._Raise("Alignment size %s must be a power of two" % + self._align_size) + self._pad_before = fdt_util.GetInt(self._node, 'pad-before', 0) + self._pad_after = fdt_util.GetInt(self._node, 'pad-after', 0) + self._pad_byte = fdt_util.GetInt(self._node, 'pad-byte', 0) + self._sort = fdt_util.GetBool(self._node, 'sort-by-offset') + self._end_4gb = fdt_util.GetBool(self._node, 'end-at-4gb') + self._skip_at_start = fdt_util.GetInt(self._node, 'skip-at-start') + if self._end_4gb: + if not self._size: + self._Raise("Section size must be provided when using end-at-4gb") + if self._skip_at_start is not None: + self._Raise("Provide either 'end-at-4gb' or 'skip-at-start'") + else: + self._skip_at_start = 0x100000000 - self._size + else: + if self._skip_at_start is None: + self._skip_at_start = 0 + self._name_prefix = fdt_util.GetString(self._node, 'name-prefix') + + def _ReadEntries(self): + for node in self._node.subnodes: + if node.name == 'hash': + continue + entry = Entry.Create(self, node) + entry.SetPrefix(self._name_prefix) + self._entries[node.name] = entry + + def GetFdtSet(self): + """Get the set of device tree files used by this image""" + fdt_set = Set() + for entry in self._entries.values(): + fdt_set.update(entry.GetFdtSet()) + return fdt_set + + def SetOffset(self, offset): + self._offset = offset + + def ExpandEntries(self): + for entry in self._entries.values(): + entry.ExpandEntries() + + def AddMissingProperties(self): + """Add new properties to the device tree as needed for this entry""" + for prop in ['offset', 'size', 'image-pos']: + if not prop in self._node.props: + state.AddZeroProp(self._node, prop) + state.CheckAddHashProp(self._node) + for entry in self._entries.values(): + entry.AddMissingProperties() + + def SetCalculatedProperties(self): + state.SetInt(self._node, 'offset', self._offset) + state.SetInt(self._node, 'size', self._size) + image_pos = self._image_pos + if self._parent_section: + image_pos -= self._parent_section.GetRootSkipAtStart() + state.SetInt(self._node, 'image-pos', image_pos) + for entry in self._entries.values(): + entry.SetCalculatedProperties() + + def ProcessFdt(self, fdt): + todo = self._entries.values() + for passnum in range(3): + next_todo = [] + for entry in todo: + if not entry.ProcessFdt(fdt): + next_todo.append(entry) + todo = next_todo + if not todo: + break + if todo: + self._Raise('Internal error: Could not complete processing of Fdt: ' + 'remaining %s' % todo) + return True + + def CheckSize(self): + """Check that the section contents does not exceed its size, etc.""" + contents_size = 0 + for entry in self._entries.values(): + contents_size = max(contents_size, entry.offset + entry.size) + + contents_size -= self._skip_at_start + + size = self._size + if not size: + size = self._pad_before + contents_size + self._pad_after + size = tools.Align(size, self._align_size) + + if self._size and contents_size > self._size: + self._Raise("contents size %#x (%d) exceeds section size %#x (%d)" % + (contents_size, contents_size, self._size, self._size)) + if not self._size: + self._size = size + if self._size != tools.Align(self._size, self._align_size): + self._Raise("Size %#x (%d) does not match align-size %#x (%d)" % + (self._size, self._size, self._align_size, self._align_size)) + return size + + def _Raise(self, msg): + """Raises an error for this section + + Args: + msg: Error message to use in the raise string + Raises: + ValueError() + """ + raise ValueError("Section '%s': %s" % (self._node.path, msg)) + + def GetPath(self): + """Get the path of an image (in the FDT) + + Returns: + Full path of the node for this image + """ + return self._node.path + + def FindEntryType(self, etype): + """Find an entry type in the section + + Args: + etype: Entry type to find + Returns: + entry matching that type, or None if not found + """ + for entry in self._entries.values(): + if entry.etype == etype: + return entry + return None + + def GetEntryContents(self): + """Call ObtainContents() for each entry + + This calls each entry's ObtainContents() a few times until they all + return True. We stop calling an entry's function once it returns + True. This allows the contents of one entry to depend on another. + + After 3 rounds we give up since it's likely an error. + """ + todo = self._entries.values() + for passnum in range(3): + next_todo = [] + for entry in todo: + if not entry.ObtainContents(): + next_todo.append(entry) + todo = next_todo + if not todo: + break + if todo: + self._Raise('Internal error: Could not complete processing of ' + 'contents: remaining %s' % todo) + return True + + def _SetEntryOffsetSize(self, name, offset, size): + """Set the offset and size of an entry + + Args: + name: Entry name to update + offset: New offset + size: New size + """ + entry = self._entries.get(name) + if not entry: + self._Raise("Unable to set offset/size for unknown entry '%s'" % + name) + entry.SetOffsetSize(self._skip_at_start + offset, size) + + def GetEntryOffsets(self): + """Handle entries that want to set the offset/size of other entries + + This calls each entry's GetOffsets() method. If it returns a list + of entries to update, it updates them. + """ + for entry in self._entries.values(): + offset_dict = entry.GetOffsets() + for name, info in offset_dict.iteritems(): + self._SetEntryOffsetSize(name, *info) + + def PackEntries(self): + """Pack all entries into the section""" + offset = self._skip_at_start + for entry in self._entries.values(): + offset = entry.Pack(offset) + self._size = self.CheckSize() + + def _SortEntries(self): + """Sort entries by offset""" + entries = sorted(self._entries.values(), key=lambda entry: entry.offset) + self._entries.clear() + for entry in entries: + self._entries[entry._node.name] = entry + + def _ExpandEntries(self): + """Expand any entries that are permitted to""" + exp_entry = None + for entry in self._entries.values(): + if exp_entry: + exp_entry.ExpandToLimit(entry.offset) + exp_entry = None + if entry.expand_size: + exp_entry = entry + if exp_entry: + exp_entry.ExpandToLimit(self._size) + + def CheckEntries(self): + """Check that entries do not overlap or extend outside the section + + This also sorts entries, if needed and expands + """ + if self._sort: + self._SortEntries() + self._ExpandEntries() + offset = 0 + prev_name = 'None' + for entry in self._entries.values(): + entry.CheckOffset() + if (entry.offset < self._skip_at_start or + entry.offset + entry.size > self._skip_at_start + self._size): + entry.Raise("Offset %#x (%d) is outside the section starting " + "at %#x (%d)" % + (entry.offset, entry.offset, self._skip_at_start, + self._skip_at_start)) + if entry.offset < offset: + entry.Raise("Offset %#x (%d) overlaps with previous entry '%s' " + "ending at %#x (%d)" % + (entry.offset, entry.offset, prev_name, offset, offset)) + offset = entry.offset + entry.size + prev_name = entry.GetPath() + + def SetImagePos(self, image_pos): + self._image_pos = image_pos + for entry in self._entries.values(): + entry.SetImagePos(image_pos) + + def ProcessEntryContents(self): + """Call the ProcessContents() method for each entry + + This is intended to adjust the contents as needed by the entry type. + """ + for entry in self._entries.values(): + entry.ProcessContents() + + def WriteSymbols(self): + """Write symbol values into binary files for access at run time""" + for entry in self._entries.values(): + entry.WriteSymbols(self) + + def BuildSection(self, fd, base_offset): + """Write the section to a file""" + fd.seek(base_offset) + fd.write(self.GetData()) + + def GetData(self): + """Get the contents of the section""" + section_data = chr(self._pad_byte) * self._size + + for entry in self._entries.values(): + data = entry.GetData() + base = self._pad_before + entry.offset - self._skip_at_start + section_data = (section_data[:base] + data + + section_data[base + len(data):]) + return section_data + + def LookupSymbol(self, sym_name, optional, msg): + """Look up a symbol in an ELF file + + Looks up a symbol in an ELF file. Only entry types which come from an + ELF image can be used by this function. + + At present the only entry property supported is offset. + + Args: + sym_name: Symbol name in the ELF file to look up in the format + _binman_<entry>_prop_<property> where <entry> is the name of + the entry and <property> is the property to find (e.g. + _binman_u_boot_prop_offset). As a special case, you can append + _any to <entry> to have it search for any matching entry. E.g. + _binman_u_boot_any_prop_offset will match entries called u-boot, + u-boot-img and u-boot-nodtb) + optional: True if the symbol is optional. If False this function + will raise if the symbol is not found + msg: Message to display if an error occurs + + Returns: + Value that should be assigned to that symbol, or None if it was + optional and not found + + Raises: + ValueError if the symbol is invalid or not found, or references a + property which is not supported + """ + m = re.match(r'^_binman_(\w+)_prop_(\w+)$', sym_name) + if not m: + raise ValueError("%s: Symbol '%s' has invalid format" % + (msg, sym_name)) + entry_name, prop_name = m.groups() + entry_name = entry_name.replace('_', '-') + entry = self._entries.get(entry_name) + if not entry: + if entry_name.endswith('-any'): + root = entry_name[:-4] + for name in self._entries: + if name.startswith(root): + rest = name[len(root):] + if rest in ['', '-img', '-nodtb']: + entry = self._entries[name] + if not entry: + err = ("%s: Entry '%s' not found in list (%s)" % + (msg, entry_name, ','.join(self._entries.keys()))) + if optional: + print('Warning: %s' % err, file=sys.stderr) + return None + raise ValueError(err) + if prop_name == 'offset': + return entry.offset + elif prop_name == 'image_pos': + return entry.image_pos + else: + raise ValueError("%s: No such property '%s'" % (msg, prop_name)) + + def GetEntries(self): + """Get the number of entries in a section + + Returns: + Number of entries in a section + """ + return self._entries + + def GetSize(self): + """Get the size of a section in bytes + + This is only meaningful if the section has a pre-defined size, or the + entries within it have been packed, so that the size has been + calculated. + + Returns: + Entry size in bytes + """ + return self._size + + def WriteMap(self, fd, indent): + """Write a map of the section to a .map file + + Args: + fd: File to write the map to + """ + Entry.WriteMapLine(fd, indent, self._name, self._offset, self._size, + self._image_pos) + for entry in self._entries.values(): + entry.WriteMap(fd, indent + 1) + + def GetContentsByPhandle(self, phandle, source_entry): + """Get the data contents of an entry specified by a phandle + + This uses a phandle to look up a node and and find the entry + associated with it. Then it returnst he contents of that entry. + + Args: + phandle: Phandle to look up (integer) + source_entry: Entry containing that phandle (used for error + reporting) + + Returns: + data from associated entry (as a string), or None if not found + """ + node = self._node.GetFdt().LookupPhandle(phandle) + if not node: + source_entry.Raise("Cannot find node for phandle %d" % phandle) + for entry in self._entries.values(): + if entry._node == node: + return entry.GetData() + source_entry.Raise("Cannot find entry for node '%s'" % node.name) + + def ExpandSize(self, size): + if size != self._size: + self._size = size + + def GetRootSkipAtStart(self): + if self._parent_section: + return self._parent_section.GetRootSkipAtStart() + return self._skip_at_start + + def GetImageSize(self): + return self._image._size diff --git a/tools/u-boot-tools/binman/cmdline.py b/tools/u-boot-tools/binman/cmdline.py new file mode 100644 index 0000000..3886d52 --- /dev/null +++ b/tools/u-boot-tools/binman/cmdline.py @@ -0,0 +1,66 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (c) 2016 Google, Inc +# Written by Simon Glass <sjg@chromium.org> +# +# Command-line parser for binman +# + +from optparse import OptionParser + +def ParseArgs(argv): + """Parse the binman command-line arguments + + Args: + argv: List of string arguments + Returns: + Tuple (options, args) with the command-line options and arugments. + options provides access to the options (e.g. option.debug) + args is a list of string arguments + """ + parser = OptionParser() + parser.add_option('-a', '--entry-arg', type='string', action='append', + help='Set argument value arg=value') + parser.add_option('-b', '--board', type='string', + help='Board name to build') + parser.add_option('-B', '--build-dir', type='string', default='b', + help='Directory containing the build output') + parser.add_option('-d', '--dt', type='string', + help='Configuration file (.dtb) to use') + parser.add_option('-D', '--debug', action='store_true', + help='Enabling debugging (provides a full traceback on error)') + parser.add_option('-E', '--entry-docs', action='store_true', + help='Write out entry documentation (see README.entries)') + parser.add_option('--fake-dtb', action='store_true', + help='Use fake device tree contents (for testing only)') + parser.add_option('-i', '--image', type='string', action='append', + help='Image filename to build (if not specified, build all)') + parser.add_option('-I', '--indir', action='append', + help='Add a path to a directory to use for input files') + parser.add_option('-H', '--full-help', action='store_true', + default=False, help='Display the README file') + parser.add_option('-m', '--map', action='store_true', + default=False, help='Output a map file for each image') + parser.add_option('-O', '--outdir', type='string', + action='store', help='Path to directory to use for intermediate and ' + 'output files') + parser.add_option('-p', '--preserve', action='store_true',\ + help='Preserve temporary output directory even if option -O is not ' + 'given') + parser.add_option('-P', '--processes', type=int, + help='set number of processes to use for running tests') + parser.add_option('-t', '--test', action='store_true', + default=False, help='run tests') + parser.add_option('-T', '--test-coverage', action='store_true', + default=False, help='run tests and check for 100% coverage') + parser.add_option('-u', '--update-fdt', action='store_true', + default=False, help='Update the binman node with offset/size info') + parser.add_option('-v', '--verbosity', default=1, + type='int', help='Control verbosity: 0=silent, 1=progress, 3=full, ' + '4=debug') + + parser.usage += """ + +Create images for a board from a set of binaries. It is controlled by a +description in the board device tree.""" + + return parser.parse_args(argv) diff --git a/tools/u-boot-tools/binman/control.py b/tools/u-boot-tools/binman/control.py new file mode 100644 index 0000000..3446e2e --- /dev/null +++ b/tools/u-boot-tools/binman/control.py @@ -0,0 +1,195 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (c) 2016 Google, Inc +# Written by Simon Glass <sjg@chromium.org> +# +# Creates binary images from input files controlled by a description +# + +from collections import OrderedDict +import os +import sys +import tools + +import command +import elf +from image import Image +import state +import tout + +# List of images we plan to create +# Make this global so that it can be referenced from tests +images = OrderedDict() + +def _ReadImageDesc(binman_node): + """Read the image descriptions from the /binman node + + This normally produces a single Image object called 'image'. But if + multiple images are present, they will all be returned. + + Args: + binman_node: Node object of the /binman node + Returns: + OrderedDict of Image objects, each of which describes an image + """ + images = OrderedDict() + if 'multiple-images' in binman_node.props: + for node in binman_node.subnodes: + images[node.name] = Image(node.name, node) + else: + images['image'] = Image('image', binman_node) + return images + +def _FindBinmanNode(dtb): + """Find the 'binman' node in the device tree + + Args: + dtb: Fdt object to scan + Returns: + Node object of /binman node, or None if not found + """ + for node in dtb.GetRoot().subnodes: + if node.name == 'binman': + return node + return None + +def WriteEntryDocs(modules, test_missing=None): + """Write out documentation for all entries + + Args: + modules: List of Module objects to get docs for + test_missing: Used for testing only, to force an entry's documeentation + to show as missing even if it is present. Should be set to None in + normal use. + """ + from entry import Entry + Entry.WriteDocs(modules, test_missing) + +def Binman(options, args): + """The main control code for binman + + This assumes that help and test options have already been dealt with. It + deals with the core task of building images. + + Args: + options: Command line options object + args: Command line arguments (list of strings) + """ + global images + + if options.full_help: + pager = os.getenv('PAGER') + if not pager: + pager = 'more' + fname = os.path.join(os.path.dirname(os.path.realpath(sys.argv[0])), + 'README') + command.Run(pager, fname) + return 0 + + # Try to figure out which device tree contains our image description + if options.dt: + dtb_fname = options.dt + else: + board = options.board + if not board: + raise ValueError('Must provide a board to process (use -b <board>)') + board_pathname = os.path.join(options.build_dir, board) + dtb_fname = os.path.join(board_pathname, 'u-boot.dtb') + if not options.indir: + options.indir = ['.'] + options.indir.append(board_pathname) + + try: + # Import these here in case libfdt.py is not available, in which case + # the above help option still works. + import fdt + import fdt_util + + tout.Init(options.verbosity) + elf.debug = options.debug + state.use_fake_dtb = options.fake_dtb + try: + tools.SetInputDirs(options.indir) + tools.PrepareOutputDir(options.outdir, options.preserve) + state.SetEntryArgs(options.entry_arg) + + # Get the device tree ready by compiling it and copying the compiled + # output into a file in our output directly. Then scan it for use + # in binman. + dtb_fname = fdt_util.EnsureCompiled(dtb_fname) + fname = tools.GetOutputFilename('u-boot.dtb.out') + tools.WriteFile(fname, tools.ReadFile(dtb_fname)) + dtb = fdt.FdtScan(fname) + + node = _FindBinmanNode(dtb) + if not node: + raise ValueError("Device tree '%s' does not have a 'binman' " + "node" % dtb_fname) + + images = _ReadImageDesc(node) + + if options.image: + skip = [] + for name, image in images.iteritems(): + if name not in options.image: + del images[name] + skip.append(name) + if skip: + print 'Skipping images: %s\n' % ', '.join(skip) + + state.Prepare(images, dtb) + + # Prepare the device tree by making sure that any missing + # properties are added (e.g. 'pos' and 'size'). The values of these + # may not be correct yet, but we add placeholders so that the + # size of the device tree is correct. Later, in + # SetCalculatedProperties() we will insert the correct values + # without changing the device-tree size, thus ensuring that our + # entry offsets remain the same. + for image in images.values(): + image.ExpandEntries() + if options.update_fdt: + image.AddMissingProperties() + image.ProcessFdt(dtb) + + for dtb_item in state.GetFdts(): + dtb_item.Sync(auto_resize=True) + dtb_item.Pack() + dtb_item.Flush() + + for image in images.values(): + # Perform all steps for this image, including checking and + # writing it. This means that errors found with a later + # image will be reported after earlier images are already + # completed and written, but that does not seem important. + image.GetEntryContents() + image.GetEntryOffsets() + try: + image.PackEntries() + image.CheckSize() + image.CheckEntries() + except Exception as e: + if options.map: + fname = image.WriteMap() + print "Wrote map file '%s' to show errors" % fname + raise + image.SetImagePos() + if options.update_fdt: + image.SetCalculatedProperties() + for dtb_item in state.GetFdts(): + dtb_item.Sync() + image.ProcessEntryContents() + image.WriteSymbols() + image.BuildImage() + if options.map: + image.WriteMap() + + # Write the updated FDTs to our output files + for dtb_item in state.GetFdts(): + tools.WriteFile(dtb_item._fname, dtb_item.GetContents()) + + finally: + tools.FinaliseOutputDir() + finally: + tout.Uninit() + + return 0 diff --git a/tools/u-boot-tools/binman/elf.py b/tools/u-boot-tools/binman/elf.py new file mode 100644 index 0000000..97df8e3 --- /dev/null +++ b/tools/u-boot-tools/binman/elf.py @@ -0,0 +1,130 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (c) 2016 Google, Inc +# Written by Simon Glass <sjg@chromium.org> +# +# Handle various things related to ELF images +# + +from collections import namedtuple, OrderedDict +import command +import os +import re +import struct + +import tools + +# This is enabled from control.py +debug = False + +Symbol = namedtuple('Symbol', ['section', 'address', 'size', 'weak']) + + +def GetSymbols(fname, patterns): + """Get the symbols from an ELF file + + Args: + fname: Filename of the ELF file to read + patterns: List of regex patterns to search for, each a string + + Returns: + None, if the file does not exist, or Dict: + key: Name of symbol + value: Hex value of symbol + """ + stdout = command.Output('objdump', '-t', fname, raise_on_error=False) + lines = stdout.splitlines() + if patterns: + re_syms = re.compile('|'.join(patterns)) + else: + re_syms = None + syms = {} + syms_started = False + for line in lines: + if not line or not syms_started: + if 'SYMBOL TABLE' in line: + syms_started = True + line = None # Otherwise code coverage complains about 'continue' + continue + if re_syms and not re_syms.search(line): + continue + + space_pos = line.find(' ') + value, rest = line[:space_pos], line[space_pos + 1:] + flags = rest[:7] + parts = rest[7:].split() + section, size = parts[:2] + if len(parts) > 2: + name = parts[2] + syms[name] = Symbol(section, int(value, 16), int(size,16), + flags[1] == 'w') + + # Sort dict by address + return OrderedDict(sorted(syms.iteritems(), key=lambda x: x[1].address)) + +def GetSymbolAddress(fname, sym_name): + """Get a value of a symbol from an ELF file + + Args: + fname: Filename of the ELF file to read + patterns: List of regex patterns to search for, each a string + + Returns: + Symbol value (as an integer) or None if not found + """ + syms = GetSymbols(fname, [sym_name]) + sym = syms.get(sym_name) + if not sym: + return None + return sym.address + +def LookupAndWriteSymbols(elf_fname, entry, section): + """Replace all symbols in an entry with their correct values + + The entry contents is updated so that values for referenced symbols will be + visible at run time. This is done by finding out the symbols offsets in the + entry (using the ELF file) and replacing them with values from binman's data + structures. + + Args: + elf_fname: Filename of ELF image containing the symbol information for + entry + entry: Entry to process + section: Section which can be used to lookup symbol values + """ + fname = tools.GetInputFilename(elf_fname) + syms = GetSymbols(fname, ['image', 'binman']) + if not syms: + return + base = syms.get('__image_copy_start') + if not base: + return + for name, sym in syms.iteritems(): + if name.startswith('_binman'): + msg = ("Section '%s': Symbol '%s'\n in entry '%s'" % + (section.GetPath(), name, entry.GetPath())) + offset = sym.address - base.address + if offset < 0 or offset + sym.size > entry.contents_size: + raise ValueError('%s has offset %x (size %x) but the contents ' + 'size is %x' % (entry.GetPath(), offset, + sym.size, entry.contents_size)) + if sym.size == 4: + pack_string = '<I' + elif sym.size == 8: + pack_string = '<Q' + else: + raise ValueError('%s has size %d: only 4 and 8 are supported' % + (msg, sym.size)) + + # Look up the symbol in our entry tables. + value = section.LookupSymbol(name, sym.weak, msg) + if value is not None: + value += base.address + else: + value = -1 + pack_string = pack_string.lower() + value_bytes = struct.pack(pack_string, value) + if debug: + print('%s:\n insert %s, offset %x, value %x, length %d' % + (msg, name, offset, value, len(value_bytes))) + entry.data = (entry.data[:offset] + value_bytes + + entry.data[offset + sym.size:]) diff --git a/tools/u-boot-tools/binman/elf_test.py b/tools/u-boot-tools/binman/elf_test.py new file mode 100644 index 0000000..b68530c --- /dev/null +++ b/tools/u-boot-tools/binman/elf_test.py @@ -0,0 +1,140 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (c) 2017 Google, Inc +# Written by Simon Glass <sjg@chromium.org> +# +# Test for the elf module + +import os +import sys +import unittest + +import elf +import test_util +import tools + +binman_dir = os.path.dirname(os.path.realpath(sys.argv[0])) + + +class FakeEntry: + """A fake Entry object, usedfor testing + + This supports an entry with a given size. + """ + def __init__(self, contents_size): + self.contents_size = contents_size + self.data = 'a' * contents_size + + def GetPath(self): + return 'entry_path' + + +class FakeSection: + """A fake Section object, used for testing + + This has the minimum feature set needed to support testing elf functions. + A LookupSymbol() function is provided which returns a fake value for amu + symbol requested. + """ + def __init__(self, sym_value=1): + self.sym_value = sym_value + + def GetPath(self): + return 'section_path' + + def LookupSymbol(self, name, weak, msg): + """Fake implementation which returns the same value for all symbols""" + return self.sym_value + + +class TestElf(unittest.TestCase): + @classmethod + def setUpClass(self): + tools.SetInputDirs(['.']) + + def testAllSymbols(self): + """Test that we can obtain a symbol from the ELF file""" + fname = os.path.join(binman_dir, 'test', 'u_boot_ucode_ptr') + syms = elf.GetSymbols(fname, []) + self.assertIn('.ucode', syms) + + def testRegexSymbols(self): + """Test that we can obtain from the ELF file by regular expression""" + fname = os.path.join(binman_dir, 'test', 'u_boot_ucode_ptr') + syms = elf.GetSymbols(fname, ['ucode']) + self.assertIn('.ucode', syms) + syms = elf.GetSymbols(fname, ['missing']) + self.assertNotIn('.ucode', syms) + syms = elf.GetSymbols(fname, ['missing', 'ucode']) + self.assertIn('.ucode', syms) + + def testMissingFile(self): + """Test that a missing file is detected""" + entry = FakeEntry(10) + section = FakeSection() + with self.assertRaises(ValueError) as e: + syms = elf.LookupAndWriteSymbols('missing-file', entry, section) + self.assertIn("Filename 'missing-file' not found in input path", + str(e.exception)) + + def testOutsideFile(self): + """Test a symbol which extends outside the entry area is detected""" + entry = FakeEntry(10) + section = FakeSection() + elf_fname = os.path.join(binman_dir, 'test', 'u_boot_binman_syms') + with self.assertRaises(ValueError) as e: + syms = elf.LookupAndWriteSymbols(elf_fname, entry, section) + self.assertIn('entry_path has offset 4 (size 8) but the contents size ' + 'is a', str(e.exception)) + + def testMissingImageStart(self): + """Test that we detect a missing __image_copy_start symbol + + This is needed to mark the start of the image. Without it we cannot + locate the offset of a binman symbol within the image. + """ + entry = FakeEntry(10) + section = FakeSection() + elf_fname = os.path.join(binman_dir, 'test', 'u_boot_binman_syms_bad') + self.assertEqual(elf.LookupAndWriteSymbols(elf_fname, entry, section), + None) + + def testBadSymbolSize(self): + """Test that an attempt to use an 8-bit symbol are detected + + Only 32 and 64 bits are supported, since we need to store an offset + into the image. + """ + entry = FakeEntry(10) + section = FakeSection() + elf_fname = os.path.join(binman_dir, 'test', 'u_boot_binman_syms_size') + with self.assertRaises(ValueError) as e: + syms = elf.LookupAndWriteSymbols(elf_fname, entry, section) + self.assertIn('has size 1: only 4 and 8 are supported', + str(e.exception)) + + def testNoValue(self): + """Test the case where we have no value for the symbol + + This should produce -1 values for all thress symbols, taking up the + first 16 bytes of the image. + """ + entry = FakeEntry(20) + section = FakeSection(sym_value=None) + elf_fname = os.path.join(binman_dir, 'test', 'u_boot_binman_syms') + syms = elf.LookupAndWriteSymbols(elf_fname, entry, section) + self.assertEqual(chr(255) * 16 + 'a' * 4, entry.data) + + def testDebug(self): + """Check that enabling debug in the elf module produced debug output""" + elf.debug = True + entry = FakeEntry(20) + section = FakeSection() + elf_fname = os.path.join(binman_dir, 'test', 'u_boot_binman_syms') + with test_util.capture_sys_output() as (stdout, stderr): + syms = elf.LookupAndWriteSymbols(elf_fname, entry, section) + elf.debug = False + self.assertTrue(len(stdout.getvalue()) > 0) + + +if __name__ == '__main__': + unittest.main() diff --git a/tools/u-boot-tools/binman/entry.py b/tools/u-boot-tools/binman/entry.py new file mode 100644 index 0000000..648cfd2 --- /dev/null +++ b/tools/u-boot-tools/binman/entry.py @@ -0,0 +1,532 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (c) 2016 Google, Inc +# +# Base class for all entries +# + +from __future__ import print_function + +from collections import namedtuple + +# importlib was introduced in Python 2.7 but there was a report of it not +# working in 2.7.12, so we work around this: +# http://lists.denx.de/pipermail/u-boot/2016-October/269729.html +try: + import importlib + have_importlib = True +except: + have_importlib = False + +import os +from sets import Set +import sys + +import fdt_util +import state +import tools + +modules = {} + +our_path = os.path.dirname(os.path.realpath(__file__)) + + +# An argument which can be passed to entries on the command line, in lieu of +# device-tree properties. +EntryArg = namedtuple('EntryArg', ['name', 'datatype']) + + +class Entry(object): + """An Entry in the section + + An entry corresponds to a single node in the device-tree description + of the section. Each entry ends up being a part of the final section. + Entries can be placed either right next to each other, or with padding + between them. The type of the entry determines the data that is in it. + + This class is not used by itself. All entry objects are subclasses of + Entry. + + Attributes: + section: Section object containing this entry + node: The node that created this entry + offset: Offset of entry within the section, None if not known yet (in + which case it will be calculated by Pack()) + size: Entry size in bytes, None if not known + contents_size: Size of contents in bytes, 0 by default + align: Entry start offset alignment, or None + align_size: Entry size alignment, or None + align_end: Entry end offset alignment, or None + pad_before: Number of pad bytes before the contents, 0 if none + pad_after: Number of pad bytes after the contents, 0 if none + data: Contents of entry (string of bytes) + """ + def __init__(self, section, etype, node, read_node=True, name_prefix=''): + self.section = section + self.etype = etype + self._node = node + self.name = node and (name_prefix + node.name) or 'none' + self.offset = None + self.size = None + self.data = None + self.contents_size = 0 + self.align = None + self.align_size = None + self.align_end = None + self.pad_before = 0 + self.pad_after = 0 + self.offset_unset = False + self.image_pos = None + self._expand_size = False + if read_node: + self.ReadNode() + + @staticmethod + def Lookup(section, node_path, etype): + """Look up the entry class for a node. + + Args: + section: Section object containing this node + node_node: Path name of Node object containing information about + the entry to create (used for errors) + etype: Entry type to use + + Returns: + The entry class object if found, else None + """ + # Convert something like 'u-boot@0' to 'u_boot' since we are only + # interested in the type. + module_name = etype.replace('-', '_') + if '@' in module_name: + module_name = module_name.split('@')[0] + module = modules.get(module_name) + + # Also allow entry-type modules to be brought in from the etype directory. + + # Import the module if we have not already done so. + if not module: + old_path = sys.path + sys.path.insert(0, os.path.join(our_path, 'etype')) + try: + if have_importlib: + module = importlib.import_module(module_name) + else: + module = __import__(module_name) + except ImportError as e: + raise ValueError("Unknown entry type '%s' in node '%s' (expected etype/%s.py, error '%s'" % + (etype, node_path, module_name, e)) + finally: + sys.path = old_path + modules[module_name] = module + + # Look up the expected class name + return getattr(module, 'Entry_%s' % module_name) + + @staticmethod + def Create(section, node, etype=None): + """Create a new entry for a node. + + Args: + section: Section object containing this node + node: Node object containing information about the entry to + create + etype: Entry type to use, or None to work it out (used for tests) + + Returns: + A new Entry object of the correct type (a subclass of Entry) + """ + if not etype: + etype = fdt_util.GetString(node, 'type', node.name) + obj = Entry.Lookup(section, node.path, etype) + + # Call its constructor to get the object we want. + return obj(section, etype, node) + + def ReadNode(self): + """Read entry information from the node + + This reads all the fields we recognise from the node, ready for use. + """ + if 'pos' in self._node.props: + self.Raise("Please use 'offset' instead of 'pos'") + self.offset = fdt_util.GetInt(self._node, 'offset') + self.size = fdt_util.GetInt(self._node, 'size') + self.align = fdt_util.GetInt(self._node, 'align') + if tools.NotPowerOfTwo(self.align): + raise ValueError("Node '%s': Alignment %s must be a power of two" % + (self._node.path, self.align)) + self.pad_before = fdt_util.GetInt(self._node, 'pad-before', 0) + self.pad_after = fdt_util.GetInt(self._node, 'pad-after', 0) + self.align_size = fdt_util.GetInt(self._node, 'align-size') + if tools.NotPowerOfTwo(self.align_size): + raise ValueError("Node '%s': Alignment size %s must be a power " + "of two" % (self._node.path, self.align_size)) + self.align_end = fdt_util.GetInt(self._node, 'align-end') + self.offset_unset = fdt_util.GetBool(self._node, 'offset-unset') + self.expand_size = fdt_util.GetBool(self._node, 'expand-size') + + def GetDefaultFilename(self): + return None + + def GetFdtSet(self): + """Get the set of device trees used by this entry + + Returns: + Set containing the filename from this entry, if it is a .dtb, else + an empty set + """ + fname = self.GetDefaultFilename() + # It would be better to use isinstance(self, Entry_blob_dtb) here but + # we cannot access Entry_blob_dtb + if fname and fname.endswith('.dtb'): + return Set([fname]) + return Set() + + def ExpandEntries(self): + pass + + def AddMissingProperties(self): + """Add new properties to the device tree as needed for this entry""" + for prop in ['offset', 'size', 'image-pos']: + if not prop in self._node.props: + state.AddZeroProp(self._node, prop) + err = state.CheckAddHashProp(self._node) + if err: + self.Raise(err) + + def SetCalculatedProperties(self): + """Set the value of device-tree properties calculated by binman""" + state.SetInt(self._node, 'offset', self.offset) + state.SetInt(self._node, 'size', self.size) + state.SetInt(self._node, 'image-pos', + self.image_pos - self.section.GetRootSkipAtStart()) + state.CheckSetHashValue(self._node, self.GetData) + + def ProcessFdt(self, fdt): + """Allow entries to adjust the device tree + + Some entries need to adjust the device tree for their purposes. This + may involve adding or deleting properties. + + Returns: + True if processing is complete + False if processing could not be completed due to a dependency. + This will cause the entry to be retried after others have been + called + """ + return True + + def SetPrefix(self, prefix): + """Set the name prefix for a node + + Args: + prefix: Prefix to set, or '' to not use a prefix + """ + if prefix: + self.name = prefix + self.name + + def SetContents(self, data): + """Set the contents of an entry + + This sets both the data and content_size properties + + Args: + data: Data to set to the contents (string) + """ + self.data = data + self.contents_size = len(self.data) + + def ProcessContentsUpdate(self, data): + """Update the contens of an entry, after the size is fixed + + This checks that the new data is the same size as the old. + + Args: + data: Data to set to the contents (string) + + Raises: + ValueError if the new data size is not the same as the old + """ + if len(data) != self.contents_size: + self.Raise('Cannot update entry size from %d to %d' % + (len(data), self.contents_size)) + self.SetContents(data) + + def ObtainContents(self): + """Figure out the contents of an entry. + + Returns: + True if the contents were found, False if another call is needed + after the other entries are processed. + """ + # No contents by default: subclasses can implement this + return True + + def Pack(self, offset): + """Figure out how to pack the entry into the section + + Most of the time the entries are not fully specified. There may be + an alignment but no size. In that case we take the size from the + contents of the entry. + + If an entry has no hard-coded offset, it will be placed at @offset. + + Once this function is complete, both the offset and size of the + entry will be know. + + Args: + Current section offset pointer + + Returns: + New section offset pointer (after this entry) + """ + if self.offset is None: + if self.offset_unset: + self.Raise('No offset set with offset-unset: should another ' + 'entry provide this correct offset?') + self.offset = tools.Align(offset, self.align) + needed = self.pad_before + self.contents_size + self.pad_after + needed = tools.Align(needed, self.align_size) + size = self.size + if not size: + size = needed + new_offset = self.offset + size + aligned_offset = tools.Align(new_offset, self.align_end) + if aligned_offset != new_offset: + size = aligned_offset - self.offset + new_offset = aligned_offset + + if not self.size: + self.size = size + + if self.size < needed: + self.Raise("Entry contents size is %#x (%d) but entry size is " + "%#x (%d)" % (needed, needed, self.size, self.size)) + # Check that the alignment is correct. It could be wrong if the + # and offset or size values were provided (i.e. not calculated), but + # conflict with the provided alignment values + if self.size != tools.Align(self.size, self.align_size): + self.Raise("Size %#x (%d) does not match align-size %#x (%d)" % + (self.size, self.size, self.align_size, self.align_size)) + if self.offset != tools.Align(self.offset, self.align): + self.Raise("Offset %#x (%d) does not match align %#x (%d)" % + (self.offset, self.offset, self.align, self.align)) + + return new_offset + + def Raise(self, msg): + """Convenience function to raise an error referencing a node""" + raise ValueError("Node '%s': %s" % (self._node.path, msg)) + + def GetEntryArgsOrProps(self, props, required=False): + """Return the values of a set of properties + + Args: + props: List of EntryArg objects + + Raises: + ValueError if a property is not found + """ + values = [] + missing = [] + for prop in props: + python_prop = prop.name.replace('-', '_') + if hasattr(self, python_prop): + value = getattr(self, python_prop) + else: + value = None + if value is None: + value = self.GetArg(prop.name, prop.datatype) + if value is None and required: + missing.append(prop.name) + values.append(value) + if missing: + self.Raise('Missing required properties/entry args: %s' % + (', '.join(missing))) + return values + + def GetPath(self): + """Get the path of a node + + Returns: + Full path of the node for this entry + """ + return self._node.path + + def GetData(self): + return self.data + + def GetOffsets(self): + return {} + + def SetOffsetSize(self, pos, size): + self.offset = pos + self.size = size + + def SetImagePos(self, image_pos): + """Set the position in the image + + Args: + image_pos: Position of this entry in the image + """ + self.image_pos = image_pos + self.offset + + def ProcessContents(self): + pass + + def WriteSymbols(self, section): + """Write symbol values into binary files for access at run time + + Args: + section: Section containing the entry + """ + pass + + def CheckOffset(self): + """Check that the entry offsets are correct + + This is used for entries which have extra offset requirements (other + than having to be fully inside their section). Sub-classes can implement + this function and raise if there is a problem. + """ + pass + + @staticmethod + def GetStr(value): + if value is None: + return '<none> ' + return '%08x' % value + + @staticmethod + def WriteMapLine(fd, indent, name, offset, size, image_pos): + print('%s %s%s %s %s' % (Entry.GetStr(image_pos), ' ' * indent, + Entry.GetStr(offset), Entry.GetStr(size), + name), file=fd) + + def WriteMap(self, fd, indent): + """Write a map of the entry to a .map file + + Args: + fd: File to write the map to + indent: Curent indent level of map (0=none, 1=one level, etc.) + """ + self.WriteMapLine(fd, indent, self.name, self.offset, self.size, + self.image_pos) + + def GetEntries(self): + """Return a list of entries contained by this entry + + Returns: + List of entries, or None if none. A normal entry has no entries + within it so will return None + """ + return None + + def GetArg(self, name, datatype=str): + """Get the value of an entry argument or device-tree-node property + + Some node properties can be provided as arguments to binman. First check + the entry arguments, and fall back to the device tree if not found + + Args: + name: Argument name + datatype: Data type (str or int) + + Returns: + Value of argument as a string or int, or None if no value + + Raises: + ValueError if the argument cannot be converted to in + """ + value = state.GetEntryArg(name) + if value is not None: + if datatype == int: + try: + value = int(value) + except ValueError: + self.Raise("Cannot convert entry arg '%s' (value '%s') to integer" % + (name, value)) + elif datatype == str: + pass + else: + raise ValueError("GetArg() internal error: Unknown data type '%s'" % + datatype) + else: + value = fdt_util.GetDatatype(self._node, name, datatype) + return value + + @staticmethod + def WriteDocs(modules, test_missing=None): + """Write out documentation about the various entry types to stdout + + Args: + modules: List of modules to include + test_missing: Used for testing. This is a module to report + as missing + """ + print('''Binman Entry Documentation +=========================== + +This file describes the entry types supported by binman. These entry types can +be placed in an image one by one to build up a final firmware image. It is +fairly easy to create new entry types. Just add a new file to the 'etype' +directory. You can use the existing entries as examples. + +Note that some entries are subclasses of others, using and extending their +features to produce new behaviours. + + +''') + modules = sorted(modules) + + # Don't show the test entry + if '_testing' in modules: + modules.remove('_testing') + missing = [] + for name in modules: + module = Entry.Lookup(name, name, name) + docs = getattr(module, '__doc__') + if test_missing == name: + docs = None + if docs: + lines = docs.splitlines() + first_line = lines[0] + rest = [line[4:] for line in lines[1:]] + hdr = 'Entry: %s: %s' % (name.replace('_', '-'), first_line) + print(hdr) + print('-' * len(hdr)) + print('\n'.join(rest)) + print() + print() + else: + missing.append(name) + + if missing: + raise ValueError('Documentation is missing for modules: %s' % + ', '.join(missing)) + + def GetUniqueName(self): + """Get a unique name for a node + + Returns: + String containing a unique name for a node, consisting of the name + of all ancestors (starting from within the 'binman' node) separated + by a dot ('.'). This can be useful for generating unique filesnames + in the output directory. + """ + name = self.name + node = self._node + while node.parent: + node = node.parent + if node.name == 'binman': + break + name = '%s.%s' % (node.name, name) + return name + + def ExpandToLimit(self, limit): + """Expand an entry so that it ends at the given offset limit""" + if self.offset + self.size < limit: + self.size = limit - self.offset + # Request the contents again, since changing the size requires that + # the data grows. This should not fail, but check it to be sure. + if not self.ObtainContents(): + self.Raise('Cannot obtain contents when expanding entry') diff --git a/tools/u-boot-tools/binman/entry_test.py b/tools/u-boot-tools/binman/entry_test.py new file mode 100644 index 0000000..1f7ff5b --- /dev/null +++ b/tools/u-boot-tools/binman/entry_test.py @@ -0,0 +1,84 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (c) 2016 Google, Inc +# Written by Simon Glass <sjg@chromium.org> +# +# Test for the Entry class + +import collections +import os +import sys +import unittest + +import fdt +import fdt_util +import tools + +entry = None + +class TestEntry(unittest.TestCase): + def setUp(self): + tools.PrepareOutputDir(None) + + def tearDown(self): + tools.FinaliseOutputDir() + + def GetNode(self): + binman_dir = os.path.dirname(os.path.realpath(sys.argv[0])) + fname = fdt_util.EnsureCompiled( + os.path.join(binman_dir,('test/005_simple.dts'))) + dtb = fdt.FdtScan(fname) + return dtb.GetNode('/binman/u-boot') + + def test1EntryNoImportLib(self): + """Test that we can import Entry subclassess successfully""" + + sys.modules['importlib'] = None + global entry + import entry + entry.Entry.Create(None, self.GetNode(), 'u-boot') + + def test2EntryImportLib(self): + del sys.modules['importlib'] + global entry + if entry: + reload(entry) + else: + import entry + entry.Entry.Create(None, self.GetNode(), 'u-boot-spl') + del entry + + def testEntryContents(self): + """Test the Entry bass class""" + import entry + base_entry = entry.Entry(None, None, None, read_node=False) + self.assertEqual(True, base_entry.ObtainContents()) + + def testUnknownEntry(self): + """Test that unknown entry types are detected""" + import entry + Node = collections.namedtuple('Node', ['name', 'path']) + node = Node('invalid-name', 'invalid-path') + with self.assertRaises(ValueError) as e: + entry.Entry.Create(None, node, node.name) + self.assertIn("Unknown entry type 'invalid-name' in node " + "'invalid-path'", str(e.exception)) + + def testUniqueName(self): + """Test Entry.GetUniqueName""" + import entry + Node = collections.namedtuple('Node', ['name', 'parent']) + base_node = Node('root', None) + base_entry = entry.Entry(None, None, base_node, read_node=False) + self.assertEqual('root', base_entry.GetUniqueName()) + sub_node = Node('subnode', base_node) + sub_entry = entry.Entry(None, None, sub_node, read_node=False) + self.assertEqual('root.subnode', sub_entry.GetUniqueName()) + + def testGetDefaultFilename(self): + """Trivial test for this base class function""" + import entry + base_entry = entry.Entry(None, None, None, read_node=False) + self.assertIsNone(base_entry.GetDefaultFilename()) + +if __name__ == "__main__": + unittest.main() diff --git a/tools/u-boot-tools/binman/etype/_testing.py b/tools/u-boot-tools/binman/etype/_testing.py new file mode 100644 index 0000000..3e345bd --- /dev/null +++ b/tools/u-boot-tools/binman/etype/_testing.py @@ -0,0 +1,100 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (c) 2016 Google, Inc +# Written by Simon Glass <sjg@chromium.org> +# +# Entry-type module for testing purposes. Not used in real images. +# + +from collections import OrderedDict + +from entry import Entry, EntryArg +import fdt_util +import tools + + +class Entry__testing(Entry): + """A fake entry used for testing + + This entry should not be used in normal images. It is a special entry with + strange features used for testing. + + Properties / Entry arguments + test-str-fdt: Test string, normally in the node + test-int-fdt: Test integer, normally in the node + test-str-arg: Test string, normally in the entry arguments + test-int-arg: Test integer, normally in the entry arguments + + The entry has a single 'a' byte as its contents. Operation is controlled by + a number of properties in the node, as follows: + + Properties: + return-invalid-entry: Return an invalid entry from GetOffsets() + return-unknown-contents: Refuse to provide any contents (to cause a + failure) + bad-update-contents: Implement ProcessContents() incorrectly so as to + cause a failure + never-complete-process-fdt: Refund to process the FDT (to cause a + failure) + require-args: Require that all used args are present (generating an + error if not) + force-bad-datatype: Force a call to GetEntryArgsOrProps() with a bad + data type (generating an error) + """ + def __init__(self, section, etype, node): + Entry.__init__(self, section, etype, node) + self.return_invalid_entry = fdt_util.GetBool(self._node, + 'return-invalid-entry') + self.return_unknown_contents = fdt_util.GetBool(self._node, + 'return-unknown-contents') + self.bad_update_contents = fdt_util.GetBool(self._node, + 'bad-update-contents') + self.return_contents_once = fdt_util.GetBool(self._node, + 'return-contents-once') + + # Set to True when the entry is ready to process the FDT. + self.process_fdt_ready = False + self.never_complete_process_fdt = fdt_util.GetBool(self._node, + 'never-complete-process-fdt') + self.require_args = fdt_util.GetBool(self._node, 'require-args') + + # This should be picked up by GetEntryArgsOrProps() + self.test_existing_prop = 'existing' + self.force_bad_datatype = fdt_util.GetBool(self._node, + 'force-bad-datatype') + (self.test_str_fdt, self.test_str_arg, self.test_int_fdt, + self.test_int_arg, existing) = self.GetEntryArgsOrProps([ + EntryArg('test-str-fdt', str), + EntryArg('test-str-arg', str), + EntryArg('test-int-fdt', int), + EntryArg('test-int-arg', int), + EntryArg('test-existing-prop', str)], self.require_args) + if self.force_bad_datatype: + self.GetEntryArgsOrProps([EntryArg('test-bad-datatype-arg', bool)]) + self.return_contents = True + + def ObtainContents(self): + if self.return_unknown_contents or not self.return_contents: + return False + self.data = 'a' + self.contents_size = len(self.data) + if self.return_contents_once: + self.return_contents = False + return True + + def GetOffsets(self): + if self.return_invalid_entry : + return {'invalid-entry': [1, 2]} + return {} + + def ProcessContents(self): + if self.bad_update_contents: + # Request to update the conents with something larger, to cause a + # failure. + self.ProcessContentsUpdate('aa') + + def ProcessFdt(self, fdt): + """Force reprocessing the first time""" + ready = self.process_fdt_ready + if not self.never_complete_process_fdt: + self.process_fdt_ready = True + return ready diff --git a/tools/u-boot-tools/binman/etype/blob.py b/tools/u-boot-tools/binman/etype/blob.py new file mode 100644 index 0000000..ae80bbe --- /dev/null +++ b/tools/u-boot-tools/binman/etype/blob.py @@ -0,0 +1,78 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (c) 2016 Google, Inc +# Written by Simon Glass <sjg@chromium.org> +# +# Entry-type module for blobs, which are binary objects read from files +# + +from entry import Entry +import fdt_util +import state +import tools + +class Entry_blob(Entry): + """Entry containing an arbitrary binary blob + + Note: This should not be used by itself. It is normally used as a parent + class by other entry types. + + Properties / Entry arguments: + - filename: Filename of file to read into entry + - compress: Compression algorithm to use: + none: No compression + lz4: Use lz4 compression (via 'lz4' command-line utility) + + This entry reads data from a file and places it in the entry. The + default filename is often specified specified by the subclass. See for + example the 'u_boot' entry which provides the filename 'u-boot.bin'. + + If compression is enabled, an extra 'uncomp-size' property is written to + the node (if enabled with -u) which provides the uncompressed size of the + data. + """ + def __init__(self, section, etype, node): + Entry.__init__(self, section, etype, node) + self._filename = fdt_util.GetString(self._node, 'filename', self.etype) + self._compress = fdt_util.GetString(self._node, 'compress', 'none') + self._uncompressed_size = None + + def ObtainContents(self): + self._filename = self.GetDefaultFilename() + self._pathname = tools.GetInputFilename(self._filename) + self.ReadBlobContents() + return True + + def ReadBlobContents(self): + # We assume the data is small enough to fit into memory. If this + # is used for large filesystem image that might not be true. + # In that case, Image.BuildImage() could be adjusted to use a + # new Entry method which can read in chunks. Then we could copy + # the data in chunks and avoid reading it all at once. For now + # this seems like an unnecessary complication. + data = tools.ReadFile(self._pathname) + if self._compress == 'lz4': + self._uncompressed_size = len(data) + ''' + import lz4 # Import this only if needed (python-lz4 dependency) + + try: + data = lz4.frame.compress(data) + except AttributeError: + data = lz4.compress(data) + ''' + data = tools.Run('lz4', '-c', self._pathname) + self.SetContents(data) + return True + + def GetDefaultFilename(self): + return self._filename + + def AddMissingProperties(self): + Entry.AddMissingProperties(self) + if self._compress != 'none': + state.AddZeroProp(self._node, 'uncomp-size') + + def SetCalculatedProperties(self): + Entry.SetCalculatedProperties(self) + if self._uncompressed_size is not None: + state.SetInt(self._node, 'uncomp-size', self._uncompressed_size) diff --git a/tools/u-boot-tools/binman/etype/blob_dtb.py b/tools/u-boot-tools/binman/etype/blob_dtb.py new file mode 100644 index 0000000..cc5b4a3 --- /dev/null +++ b/tools/u-boot-tools/binman/etype/blob_dtb.py @@ -0,0 +1,33 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (c) 2018 Google, Inc +# Written by Simon Glass <sjg@chromium.org> +# +# Entry-type module for U-Boot device tree files +# + +import state + +from entry import Entry +from blob import Entry_blob + +class Entry_blob_dtb(Entry_blob): + """A blob that holds a device tree + + This is a blob containing a device tree. The contents of the blob are + obtained from the list of available device-tree files, managed by the + 'state' module. + """ + def __init__(self, section, etype, node): + Entry_blob.__init__(self, section, etype, node) + + def ObtainContents(self): + """Get the device-tree from the list held by the 'state' module""" + self._filename = self.GetDefaultFilename() + self._pathname, data = state.GetFdtContents(self._filename) + self.SetContents(data) + return True + + def ProcessContents(self): + """Re-read the DTB contents so that we get any calculated properties""" + _, data = state.GetFdtContents(self._filename) + self.SetContents(data) diff --git a/tools/u-boot-tools/binman/etype/blob_named_by_arg.py b/tools/u-boot-tools/binman/etype/blob_named_by_arg.py new file mode 100644 index 0000000..344112b --- /dev/null +++ b/tools/u-boot-tools/binman/etype/blob_named_by_arg.py @@ -0,0 +1,34 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (c) 2018 Google, Inc +# Written by Simon Glass <sjg@chromium.org> +# +# Entry-type module for a blob where the filename comes from a property in the +# node or an entry argument. The property is called '<blob_fname>-path' where +# <blob_fname> is provided by the subclass using this entry type. + +from collections import OrderedDict + +from blob import Entry_blob +from entry import EntryArg + + +class Entry_blob_named_by_arg(Entry_blob): + """A blob entry which gets its filename property from its subclass + + Properties / Entry arguments: + - <xxx>-path: Filename containing the contents of this entry (optional, + defaults to 0) + + where <xxx> is the blob_fname argument to the constructor. + + This entry cannot be used directly. Instead, it is used as a parent class + for another entry, which defined blob_fname. This parameter is used to + set the entry-arg or property containing the filename. The entry-arg or + property is in turn used to set the actual filename. + + See cros_ec_rw for an example of this. + """ + def __init__(self, section, etype, node, blob_fname): + Entry_blob.__init__(self, section, etype, node) + self._filename, = self.GetEntryArgsOrProps( + [EntryArg('%s-path' % blob_fname, str)]) diff --git a/tools/u-boot-tools/binman/etype/cros_ec_rw.py b/tools/u-boot-tools/binman/etype/cros_ec_rw.py new file mode 100644 index 0000000..261f865 --- /dev/null +++ b/tools/u-boot-tools/binman/etype/cros_ec_rw.py @@ -0,0 +1,22 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (c) 2018 Google, Inc +# Written by Simon Glass <sjg@chromium.org> +# +# Entry-type module for a Chromium OS EC image (read-write section) +# + +from blob_named_by_arg import Entry_blob_named_by_arg + + +class Entry_cros_ec_rw(Entry_blob_named_by_arg): + """A blob entry which contains a Chromium OS read-write EC image + + Properties / Entry arguments: + - cros-ec-rw-path: Filename containing the EC image + + This entry holds a Chromium OS EC (embedded controller) image, for use in + updating the EC on startup via software sync. + """ + def __init__(self, section, etype, node): + Entry_blob_named_by_arg.__init__(self, section, etype, node, + 'cros-ec-rw') diff --git a/tools/u-boot-tools/binman/etype/files.py b/tools/u-boot-tools/binman/etype/files.py new file mode 100644 index 0000000..99f2f2f --- /dev/null +++ b/tools/u-boot-tools/binman/etype/files.py @@ -0,0 +1,57 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (c) 2018 Google, Inc +# Written by Simon Glass <sjg@chromium.org> +# +# Entry-type module for a set of files which are placed in individual +# sub-entries +# + +import glob +import os + +from section import Entry_section +import fdt_util +import state +import tools + +import bsection + +class Entry_files(Entry_section): + """Entry containing a set of files + + Properties / Entry arguments: + - pattern: Filename pattern to match the files to include + - compress: Compression algorithm to use: + none: No compression + lz4: Use lz4 compression (via 'lz4' command-line utility) + + This entry reads a number of files and places each in a separate sub-entry + within this entry. To access these you need to enable device-tree updates + at run-time so you can obtain the file positions. + """ + def __init__(self, section, etype, node): + Entry_section.__init__(self, section, etype, node) + self._pattern = fdt_util.GetString(self._node, 'pattern') + if not self._pattern: + self.Raise("Missing 'pattern' property") + self._compress = fdt_util.GetString(self._node, 'compress', 'none') + self._require_matches = fdt_util.GetBool(self._node, + 'require-matches') + + def ExpandEntries(self): + files = tools.GetInputFilenameGlob(self._pattern) + if self._require_matches and not files: + self.Raise("Pattern '%s' matched no files" % self._pattern) + for fname in files: + if not os.path.isfile(fname): + continue + name = os.path.basename(fname) + subnode = self._node.FindNode(name) + if not subnode: + subnode = state.AddSubnode(self._node, name) + state.AddString(subnode, 'type', 'blob') + state.AddString(subnode, 'filename', fname) + state.AddString(subnode, 'compress', self._compress) + + # Read entries again, now that we have some + self._section._ReadEntries() diff --git a/tools/u-boot-tools/binman/etype/fill.py b/tools/u-boot-tools/binman/etype/fill.py new file mode 100644 index 0000000..dcfe978 --- /dev/null +++ b/tools/u-boot-tools/binman/etype/fill.py @@ -0,0 +1,32 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (c) 2018 Google, Inc +# Written by Simon Glass <sjg@chromium.org> +# + +from entry import Entry +import fdt_util + + +class Entry_fill(Entry): + """An entry which is filled to a particular byte value + + Properties / Entry arguments: + - fill-byte: Byte to use to fill the entry + + Note that the size property must be set since otherwise this entry does not + know how large it should be. + + You can often achieve the same effect using the pad-byte property of the + overall image, in that the space between entries will then be padded with + that byte. But this entry is sometimes useful for explicitly setting the + byte value of a region. + """ + def __init__(self, section, etype, node): + Entry.__init__(self, section, etype, node) + if self.size is None: + self.Raise("'fill' entry must have a size property") + self.fill_value = fdt_util.GetByte(self._node, 'fill-byte', 0) + + def ObtainContents(self): + self.SetContents(chr(self.fill_value) * self.size) + return True diff --git a/tools/u-boot-tools/binman/etype/fmap.py b/tools/u-boot-tools/binman/etype/fmap.py new file mode 100644 index 0000000..bf35a5b --- /dev/null +++ b/tools/u-boot-tools/binman/etype/fmap.py @@ -0,0 +1,64 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (c) 2018 Google, Inc +# Written by Simon Glass <sjg@chromium.org> +# +# Entry-type module for a Flash map, as used by the flashrom SPI flash tool +# + +from entry import Entry +import fmap_util + + +class Entry_fmap(Entry): + """An entry which contains an Fmap section + + Properties / Entry arguments: + None + + FMAP is a simple format used by flashrom, an open-source utility for + reading and writing the SPI flash, typically on x86 CPUs. The format + provides flashrom with a list of areas, so it knows what it in the flash. + It can then read or write just a single area, instead of the whole flash. + + The format is defined by the flashrom project, in the file lib/fmap.h - + see www.flashrom.org/Flashrom for more information. + + When used, this entry will be populated with an FMAP which reflects the + entries in the current image. Note that any hierarchy is squashed, since + FMAP does not support this. + """ + def __init__(self, section, etype, node): + Entry.__init__(self, section, etype, node) + + def _GetFmap(self): + """Build an FMAP from the entries in the current image + + Returns: + FMAP binary data + """ + def _AddEntries(areas, entry): + entries = entry.GetEntries() + if entries: + for subentry in entries.values(): + _AddEntries(areas, subentry) + else: + pos = entry.image_pos + if pos is not None: + pos -= entry.section.GetRootSkipAtStart() + areas.append(fmap_util.FmapArea(pos or 0, entry.size or 0, + entry.name, 0)) + + entries = self.section._image.GetEntries() + areas = [] + for entry in entries.values(): + _AddEntries(areas, entry) + return fmap_util.EncodeFmap(self.section.GetImageSize() or 0, self.name, + areas) + + def ObtainContents(self): + """Obtain a placeholder for the fmap contents""" + self.SetContents(self._GetFmap()) + return True + + def ProcessContents(self): + self.SetContents(self._GetFmap()) diff --git a/tools/u-boot-tools/binman/etype/gbb.py b/tools/u-boot-tools/binman/etype/gbb.py new file mode 100644 index 0000000..8fe10f4 --- /dev/null +++ b/tools/u-boot-tools/binman/etype/gbb.py @@ -0,0 +1,96 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (c) 2018 Google, Inc +# Written by Simon Glass <sjg@chromium.org> +# + +# Support for a Chromium OS Google Binary Block, used to record read-only +# information mostly used by firmware. + +from collections import OrderedDict + +import command +from entry import Entry, EntryArg + +import fdt_util +import tools + +# Build GBB flags. +# (src/platform/vboot_reference/firmware/include/gbb_header.h) +gbb_flag_properties = { + 'dev-screen-short-delay': 0x1, + 'load-option-roms': 0x2, + 'enable-alternate-os': 0x4, + 'force-dev-switch-on': 0x8, + 'force-dev-boot-usb': 0x10, + 'disable-fw-rollback-check': 0x20, + 'enter-triggers-tonorm': 0x40, + 'force-dev-boot-legacy': 0x80, + 'faft-key-override': 0x100, + 'disable-ec-software-sync': 0x200, + 'default-dev-boot-legacy': 0x400, + 'disable-pd-software-sync': 0x800, + 'disable-lid-shutdown': 0x1000, + 'force-dev-boot-fastboot-full-cap': 0x2000, + 'enable-serial': 0x4000, + 'disable-dwmp': 0x8000, +} + + +class Entry_gbb(Entry): + """An entry which contains a Chromium OS Google Binary Block + + Properties / Entry arguments: + - hardware-id: Hardware ID to use for this build (a string) + - keydir: Directory containing the public keys to use + - bmpblk: Filename containing images used by recovery + + Chromium OS uses a GBB to store various pieces of information, in particular + the root and recovery keys that are used to verify the boot process. Some + more details are here: + + https://www.chromium.org/chromium-os/firmware-porting-guide/2-concepts + + but note that the page dates from 2013 so is quite out of date. See + README.chromium for how to obtain the required keys and tools. + """ + def __init__(self, section, etype, node): + Entry.__init__(self, section, etype, node) + self.hardware_id, self.keydir, self.bmpblk = self.GetEntryArgsOrProps( + [EntryArg('hardware-id', str), + EntryArg('keydir', str), + EntryArg('bmpblk', str)]) + + # Read in the GBB flags from the config + self.gbb_flags = 0 + flags_node = node.FindNode('flags') + if flags_node: + for flag, value in gbb_flag_properties.iteritems(): + if fdt_util.GetBool(flags_node, flag): + self.gbb_flags |= value + + def ObtainContents(self): + gbb = 'gbb.bin' + fname = tools.GetOutputFilename(gbb) + if not self.size: + self.Raise('GBB must have a fixed size') + gbb_size = self.size + bmpfv_size = gbb_size - 0x2180 + if bmpfv_size < 0: + self.Raise('GBB is too small (minimum 0x2180 bytes)') + sizes = [0x100, 0x1000, bmpfv_size, 0x1000] + sizes = ['%#x' % size for size in sizes] + keydir = tools.GetInputFilename(self.keydir) + gbb_set_command = [ + 'gbb_utility', '-s', + '--hwid=%s' % self.hardware_id, + '--rootkey=%s/root_key.vbpubk' % keydir, + '--recoverykey=%s/recovery_key.vbpubk' % keydir, + '--flags=%d' % self.gbb_flags, + '--bmpfv=%s' % tools.GetInputFilename(self.bmpblk), + fname] + + tools.Run('futility', 'gbb_utility', '-c', ','.join(sizes), fname) + tools.Run('futility', *gbb_set_command) + + self.SetContents(tools.ReadFile(fname)) + return True diff --git a/tools/u-boot-tools/binman/etype/intel_cmc.py b/tools/u-boot-tools/binman/etype/intel_cmc.py new file mode 100644 index 0000000..fa6f779 --- /dev/null +++ b/tools/u-boot-tools/binman/etype/intel_cmc.py @@ -0,0 +1,23 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (c) 2016 Google, Inc +# Written by Simon Glass <sjg@chromium.org> +# +# Entry-type module for Intel Chip Microcode binary blob +# + +from entry import Entry +from blob import Entry_blob + +class Entry_intel_cmc(Entry_blob): + """Entry containing an Intel Chipset Micro Code (CMC) file + + Properties / Entry arguments: + - filename: Filename of file to read into entry + + This file contains microcode for some devices in a special format. An + example filename is 'Microcode/C0_22211.BIN'. + + See README.x86 for information about x86 binary blobs. + """ + def __init__(self, section, etype, node): + Entry_blob.__init__(self, section, etype, node) diff --git a/tools/u-boot-tools/binman/etype/intel_descriptor.py b/tools/u-boot-tools/binman/etype/intel_descriptor.py new file mode 100644 index 0000000..6acbbd8 --- /dev/null +++ b/tools/u-boot-tools/binman/etype/intel_descriptor.py @@ -0,0 +1,63 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (c) 2016 Google, Inc +# Written by Simon Glass <sjg@chromium.org> +# +# Entry-type module for 'u-boot' +# + +import struct + +from entry import Entry +from blob import Entry_blob + +FD_SIGNATURE = struct.pack('<L', 0x0ff0a55a) +MAX_REGIONS = 5 + +# Region numbers supported by the Intel firmware format +(REGION_DESCRIPTOR, REGION_BIOS, REGION_ME, REGION_GBE, + REGION_PDATA) = range(5) + +class Region: + def __init__(self, data, frba, region_num): + pos = frba + region_num * 4 + val = struct.unpack('<L', data[pos:pos + 4])[0] + self.base = (val & 0xfff) << 12 + self.limit = ((val & 0x0fff0000) >> 4) | 0xfff + self.size = self.limit - self.base + 1 + +class Entry_intel_descriptor(Entry_blob): + """Intel flash descriptor block (4KB) + + Properties / Entry arguments: + filename: Filename of file containing the descriptor. This is typically + a 4KB binary file, sometimes called 'descriptor.bin' + + This entry is placed at the start of flash and provides information about + the SPI flash regions. In particular it provides the base address and + size of the ME (Management Engine) region, allowing us to place the ME + binary in the right place. + + With this entry in your image, the position of the 'intel-me' entry will be + fixed in the image, which avoids you needed to specify an offset for that + region. This is useful, because it is not possible to change the position + of the ME region without updating the descriptor. + + See README.x86 for information about x86 binary blobs. + """ + def __init__(self, section, etype, node): + Entry_blob.__init__(self, section, etype, node) + self._regions = [] + + def GetOffsets(self): + offset = self.data.find(FD_SIGNATURE) + if offset == -1: + self.Raise('Cannot find FD signature') + flvalsig, flmap0, flmap1, flmap2 = struct.unpack('<LLLL', + self.data[offset:offset + 16]) + frba = ((flmap0 >> 16) & 0xff) << 4 + for i in range(MAX_REGIONS): + self._regions.append(Region(self.data, frba, i)) + + # Set the offset for ME only, for now, since the others are not used + return {'intel-me': [self._regions[REGION_ME].base, + self._regions[REGION_ME].size]} diff --git a/tools/u-boot-tools/binman/etype/intel_fsp.py b/tools/u-boot-tools/binman/etype/intel_fsp.py new file mode 100644 index 0000000..00a78e7 --- /dev/null +++ b/tools/u-boot-tools/binman/etype/intel_fsp.py @@ -0,0 +1,27 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (c) 2016 Google, Inc +# Written by Simon Glass <sjg@chromium.org> +# +# Entry-type module for Intel Firmware Support Package binary blob +# + +from entry import Entry +from blob import Entry_blob + +class Entry_intel_fsp(Entry_blob): + """Entry containing an Intel Firmware Support Package (FSP) file + + Properties / Entry arguments: + - filename: Filename of file to read into entry + + This file contains binary blobs which are used on some devices to make the + platform work. U-Boot executes this code since it is not possible to set up + the hardware using U-Boot open-source code. Documentation is typically not + available in sufficient detail to allow this. + + An example filename is 'FSP/QUEENSBAY_FSP_GOLD_001_20-DECEMBER-2013.fd' + + See README.x86 for information about x86 binary blobs. + """ + def __init__(self, section, etype, node): + Entry_blob.__init__(self, section, etype, node) diff --git a/tools/u-boot-tools/binman/etype/intel_me.py b/tools/u-boot-tools/binman/etype/intel_me.py new file mode 100644 index 0000000..247c5b3 --- /dev/null +++ b/tools/u-boot-tools/binman/etype/intel_me.py @@ -0,0 +1,28 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (c) 2016 Google, Inc +# Written by Simon Glass <sjg@chromium.org> +# +# Entry-type module for Intel Management Engine binary blob +# + +from entry import Entry +from blob import Entry_blob + +class Entry_intel_me(Entry_blob): + """Entry containing an Intel Management Engine (ME) file + + Properties / Entry arguments: + - filename: Filename of file to read into entry + + This file contains code used by the SoC that is required to make it work. + The Management Engine is like a background task that runs things that are + not clearly documented, but may include keyboard, deplay and network + access. For platform that use ME it is not possible to disable it. U-Boot + does not directly execute code in the ME binary. + + A typical filename is 'me.bin'. + + See README.x86 for information about x86 binary blobs. + """ + def __init__(self, section, etype, node): + Entry_blob.__init__(self, section, etype, node) diff --git a/tools/u-boot-tools/binman/etype/intel_mrc.py b/tools/u-boot-tools/binman/etype/intel_mrc.py new file mode 100644 index 0000000..4dbc99a --- /dev/null +++ b/tools/u-boot-tools/binman/etype/intel_mrc.py @@ -0,0 +1,27 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (c) 2016 Google, Inc +# Written by Simon Glass <sjg@chromium.org> +# +# Entry-type module for Intel Memory Reference Code binary blob +# + +from entry import Entry +from blob import Entry_blob + +class Entry_intel_mrc(Entry_blob): + """Entry containing an Intel Memory Reference Code (MRC) file + + Properties / Entry arguments: + - filename: Filename of file to read into entry + + This file contains code for setting up the SDRAM on some Intel systems. This + is executed by U-Boot when needed early during startup. A typical filename + is 'mrc.bin'. + + See README.x86 for information about x86 binary blobs. + """ + def __init__(self, section, etype, node): + Entry_blob.__init__(self, section, etype, node) + + def GetDefaultFilename(self): + return 'mrc.bin' diff --git a/tools/u-boot-tools/binman/etype/intel_refcode.py b/tools/u-boot-tools/binman/etype/intel_refcode.py new file mode 100644 index 0000000..045db58 --- /dev/null +++ b/tools/u-boot-tools/binman/etype/intel_refcode.py @@ -0,0 +1,27 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (c) 2016 Google, Inc +# Written by Simon Glass <sjg@chromium.org> +# +# Entry-type module for Intel Memory Reference Code binary blob +# + +from entry import Entry +from blob import Entry_blob + +class Entry_intel_refcode(Entry_blob): + """Entry containing an Intel Reference Code file + + Properties / Entry arguments: + - filename: Filename of file to read into entry + + This file contains code for setting up the platform on some Intel systems. + This is executed by U-Boot when needed early during startup. A typical + filename is 'refcode.bin'. + + See README.x86 for information about x86 binary blobs. + """ + def __init__(self, section, etype, node): + Entry_blob.__init__(self, section, etype, node) + + def GetDefaultFilename(self): + return 'refcode.bin' diff --git a/tools/u-boot-tools/binman/etype/intel_vbt.py b/tools/u-boot-tools/binman/etype/intel_vbt.py new file mode 100644 index 0000000..d93dd19 --- /dev/null +++ b/tools/u-boot-tools/binman/etype/intel_vbt.py @@ -0,0 +1,22 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (C) 2017, Bin Meng <bmeng.cn@gmail.com> +# +# Entry-type module for Intel Video BIOS Table binary blob +# + +from entry import Entry +from blob import Entry_blob + +class Entry_intel_vbt(Entry_blob): + """Entry containing an Intel Video BIOS Table (VBT) file + + Properties / Entry arguments: + - filename: Filename of file to read into entry + + This file contains code that sets up the integrated graphics subsystem on + some Intel SoCs. U-Boot executes this when the display is started up. + + See README.x86 for information about Intel binary blobs. + """ + def __init__(self, section, etype, node): + Entry_blob.__init__(self, section, etype, node) diff --git a/tools/u-boot-tools/binman/etype/intel_vga.py b/tools/u-boot-tools/binman/etype/intel_vga.py new file mode 100644 index 0000000..40982c8 --- /dev/null +++ b/tools/u-boot-tools/binman/etype/intel_vga.py @@ -0,0 +1,25 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (c) 2016 Google, Inc +# Written by Simon Glass <sjg@chromium.org> +# +# Entry-type module for x86 VGA ROM binary blob +# + +from entry import Entry +from blob import Entry_blob + +class Entry_intel_vga(Entry_blob): + """Entry containing an Intel Video Graphics Adaptor (VGA) file + + Properties / Entry arguments: + - filename: Filename of file to read into entry + + This file contains code that sets up the integrated graphics subsystem on + some Intel SoCs. U-Boot executes this when the display is started up. + + This is similar to the VBT file but in a different format. + + See README.x86 for information about Intel binary blobs. + """ + def __init__(self, section, etype, node): + Entry_blob.__init__(self, section, etype, node) diff --git a/tools/u-boot-tools/binman/etype/powerpc_mpc85xx_bootpg_resetvec.py b/tools/u-boot-tools/binman/etype/powerpc_mpc85xx_bootpg_resetvec.py new file mode 100644 index 0000000..59fedd2 --- /dev/null +++ b/tools/u-boot-tools/binman/etype/powerpc_mpc85xx_bootpg_resetvec.py @@ -0,0 +1,25 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright 2018 NXP +# +# Entry-type module for the PowerPC mpc85xx bootpg and resetvec code for U-Boot +# + +from entry import Entry +from blob import Entry_blob + +class Entry_powerpc_mpc85xx_bootpg_resetvec(Entry_blob): + """PowerPC mpc85xx bootpg + resetvec code for U-Boot + + Properties / Entry arguments: + - filename: Filename of u-boot-br.bin (default 'u-boot-br.bin') + + This enrty is valid for PowerPC mpc85xx cpus. This entry holds + 'bootpg + resetvec' code for PowerPC mpc85xx CPUs which needs to be + placed at offset 'RESET_VECTOR_ADDRESS - 0xffc'. + """ + + def __init__(self, section, etype, node): + Entry_blob.__init__(self, section, etype, node) + + def GetDefaultFilename(self): + return 'u-boot-br.bin' diff --git a/tools/u-boot-tools/binman/etype/section.py b/tools/u-boot-tools/binman/etype/section.py new file mode 100644 index 0000000..7f1b413 --- /dev/null +++ b/tools/u-boot-tools/binman/etype/section.py @@ -0,0 +1,106 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (c) 2018 Google, Inc +# Written by Simon Glass <sjg@chromium.org> +# +# Entry-type module for sections, which are entries which can contain other +# entries. +# + +from entry import Entry +import fdt_util +import tools + +import bsection + +class Entry_section(Entry): + """Entry that contains other entries + + Properties / Entry arguments: (see binman README for more information) + - size: Size of section in bytes + - align-size: Align size to a particular power of two + - pad-before: Add padding before the entry + - pad-after: Add padding after the entry + - pad-byte: Pad byte to use when padding + - sort-by-offset: Reorder the entries by offset + - end-at-4gb: Used to build an x86 ROM which ends at 4GB (2^32) + - name-prefix: Adds a prefix to the name of every entry in the section + when writing out the map + + A section is an entry which can contain other entries, thus allowing + hierarchical images to be created. See 'Sections and hierarchical images' + in the binman README for more information. + """ + def __init__(self, section, etype, node): + Entry.__init__(self, section, etype, node) + self._section = bsection.Section(node.name, section, node, + section._image) + + def GetFdtSet(self): + return self._section.GetFdtSet() + + def ProcessFdt(self, fdt): + return self._section.ProcessFdt(fdt) + + def ExpandEntries(self): + Entry.ExpandEntries(self) + self._section.ExpandEntries() + + def AddMissingProperties(self): + Entry.AddMissingProperties(self) + self._section.AddMissingProperties() + + def ObtainContents(self): + return self._section.GetEntryContents() + + def GetData(self): + return self._section.GetData() + + def GetOffsets(self): + """Handle entries that want to set the offset/size of other entries + + This calls each entry's GetOffsets() method. If it returns a list + of entries to update, it updates them. + """ + self._section.GetEntryOffsets() + return {} + + def Pack(self, offset): + """Pack all entries into the section""" + self._section.PackEntries() + self._section.SetOffset(offset) + self.size = self._section.GetSize() + return super(Entry_section, self).Pack(offset) + + def SetImagePos(self, image_pos): + Entry.SetImagePos(self, image_pos) + self._section.SetImagePos(image_pos + self.offset) + + def WriteSymbols(self, section): + """Write symbol values into binary files for access at run time""" + self._section.WriteSymbols() + + def SetCalculatedProperties(self): + Entry.SetCalculatedProperties(self) + self._section.SetCalculatedProperties() + + def ProcessContents(self): + self._section.ProcessEntryContents() + super(Entry_section, self).ProcessContents() + + def CheckOffset(self): + self._section.CheckEntries() + + def WriteMap(self, fd, indent): + """Write a map of the section to a .map file + + Args: + fd: File to write the map to + """ + self._section.WriteMap(fd, indent) + + def GetEntries(self): + return self._section.GetEntries() + + def ExpandToLimit(self, limit): + super(Entry_section, self).ExpandToLimit(limit) + self._section.ExpandSize(self.size) diff --git a/tools/u-boot-tools/binman/etype/text.py b/tools/u-boot-tools/binman/etype/text.py new file mode 100644 index 0000000..6e99819 --- /dev/null +++ b/tools/u-boot-tools/binman/etype/text.py @@ -0,0 +1,60 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (c) 2018 Google, Inc +# Written by Simon Glass <sjg@chromium.org> +# + +from collections import OrderedDict + +from entry import Entry, EntryArg +import fdt_util + + +class Entry_text(Entry): + """An entry which contains text + + The text can be provided either in the node itself or by a command-line + argument. There is a level of indirection to allow multiple text strings + and sharing of text. + + Properties / Entry arguments: + text-label: The value of this string indicates the property / entry-arg + that contains the string to place in the entry + <xxx> (actual name is the value of text-label): contains the string to + place in the entry. + + Example node: + + text { + size = <50>; + text-label = "message"; + }; + + You can then use: + + binman -amessage="this is my message" + + and binman will insert that string into the entry. + + It is also possible to put the string directly in the node: + + text { + size = <8>; + text-label = "message"; + message = "a message directly in the node" + }; + + The text is not itself nul-terminated. This can be achieved, if required, + by setting the size of the entry to something larger than the text. + """ + def __init__(self, section, etype, node): + Entry.__init__(self, section, etype, node) + self.text_label, = self.GetEntryArgsOrProps( + [EntryArg('text-label', str)]) + self.value, = self.GetEntryArgsOrProps([EntryArg(self.text_label, str)]) + if not self.value: + self.Raise("No value provided for text label '%s'" % + self.text_label) + + def ObtainContents(self): + self.SetContents(self.value) + return True diff --git a/tools/u-boot-tools/binman/etype/u_boot.py b/tools/u-boot-tools/binman/etype/u_boot.py new file mode 100644 index 0000000..23dd12c --- /dev/null +++ b/tools/u-boot-tools/binman/etype/u_boot.py @@ -0,0 +1,32 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (c) 2016 Google, Inc +# Written by Simon Glass <sjg@chromium.org> +# +# Entry-type module for U-Boot binary +# + +from entry import Entry +from blob import Entry_blob + +class Entry_u_boot(Entry_blob): + """U-Boot flat binary + + Properties / Entry arguments: + - filename: Filename of u-boot.bin (default 'u-boot.bin') + + This is the U-Boot binary, containing relocation information to allow it + to relocate itself at runtime. The binary typically includes a device tree + blob at the end of it. Use u_boot_nodtb if you want to package the device + tree separately. + + U-Boot can access binman symbols at runtime. See: + + 'Access to binman entry offsets at run time (fdt)' + + in the binman README for more information. + """ + def __init__(self, section, etype, node): + Entry_blob.__init__(self, section, etype, node) + + def GetDefaultFilename(self): + return 'u-boot.bin' diff --git a/tools/u-boot-tools/binman/etype/u_boot_dtb.py b/tools/u-boot-tools/binman/etype/u_boot_dtb.py new file mode 100644 index 0000000..6263c4e --- /dev/null +++ b/tools/u-boot-tools/binman/etype/u_boot_dtb.py @@ -0,0 +1,28 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (c) 2016 Google, Inc +# Written by Simon Glass <sjg@chromium.org> +# +# Entry-type module for U-Boot device tree +# + +from entry import Entry +from blob_dtb import Entry_blob_dtb + +class Entry_u_boot_dtb(Entry_blob_dtb): + """U-Boot device tree + + Properties / Entry arguments: + - filename: Filename of u-boot.dtb (default 'u-boot.dtb') + + This is the U-Boot device tree, containing configuration information for + U-Boot. U-Boot needs this to know what devices are present and which drivers + to activate. + + Note: This is mostly an internal entry type, used by others. This allows + binman to know which entries contain a device tree. + """ + def __init__(self, section, etype, node): + Entry_blob_dtb.__init__(self, section, etype, node) + + def GetDefaultFilename(self): + return 'u-boot.dtb' diff --git a/tools/u-boot-tools/binman/etype/u_boot_dtb_with_ucode.py b/tools/u-boot-tools/binman/etype/u_boot_dtb_with_ucode.py new file mode 100644 index 0000000..444c51b --- /dev/null +++ b/tools/u-boot-tools/binman/etype/u_boot_dtb_with_ucode.py @@ -0,0 +1,86 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (c) 2016 Google, Inc +# Written by Simon Glass <sjg@chromium.org> +# +# Entry-type module for U-Boot device tree with the microcode removed +# + +from entry import Entry +from blob_dtb import Entry_blob_dtb +import state +import tools + +class Entry_u_boot_dtb_with_ucode(Entry_blob_dtb): + """A U-Boot device tree file, with the microcode removed + + Properties / Entry arguments: + - filename: Filename of u-boot.dtb (default 'u-boot.dtb') + + See Entry_u_boot_ucode for full details of the three entries involved in + this process. This entry provides the U-Boot device-tree file, which + contains the microcode. If the microcode is not being collated into one + place then the offset and size of the microcode is recorded by this entry, + for use by u_boot_with_ucode_ptr. If it is being collated, then this + entry deletes the microcode from the device tree (to save space) and makes + it available to u_boot_ucode. + """ + def __init__(self, section, etype, node): + Entry_blob_dtb.__init__(self, section, etype, node) + self.ucode_data = '' + self.collate = False + self.ucode_offset = None + self.ucode_size = None + self.ucode = None + self.ready = False + + def GetDefaultFilename(self): + return 'u-boot.dtb' + + def ProcessFdt(self, fdt): + # So the module can be loaded without it + import fdt + + # If the section does not need microcode, there is nothing to do + ucode_dest_entry = self.section.FindEntryType( + 'u-boot-spl-with-ucode-ptr') + if not ucode_dest_entry or not ucode_dest_entry.target_offset: + ucode_dest_entry = self.section.FindEntryType( + 'u-boot-tpl-with-ucode-ptr') + if not ucode_dest_entry or not ucode_dest_entry.target_offset: + ucode_dest_entry = self.section.FindEntryType( + 'u-boot-with-ucode-ptr') + if not ucode_dest_entry or not ucode_dest_entry.target_offset: + return True + + # Remove the microcode + fname = self.GetDefaultFilename() + fdt = state.GetFdt(fname) + self.ucode = fdt.GetNode('/microcode') + if not self.ucode: + raise self.Raise("No /microcode node found in '%s'" % fname) + + # There's no need to collate it (move all microcode into one place) + # if we only have one chunk of microcode. + self.collate = len(self.ucode.subnodes) > 1 + for node in self.ucode.subnodes: + data_prop = node.props.get('data') + if data_prop: + self.ucode_data += ''.join(data_prop.bytes) + if self.collate: + node.DeleteProp('data') + return True + + def ObtainContents(self): + # Call the base class just in case it does something important. + Entry_blob_dtb.ObtainContents(self) + if self.ucode and not self.collate: + for node in self.ucode.subnodes: + data_prop = node.props.get('data') + if data_prop: + # Find the offset in the device tree of the ucode data + self.ucode_offset = data_prop.GetOffset() + 12 + self.ucode_size = len(data_prop.bytes) + self.ready = True + else: + self.ready = True + return self.ready diff --git a/tools/u-boot-tools/binman/etype/u_boot_elf.py b/tools/u-boot-tools/binman/etype/u_boot_elf.py new file mode 100644 index 0000000..f83860d --- /dev/null +++ b/tools/u-boot-tools/binman/etype/u_boot_elf.py @@ -0,0 +1,38 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (c) 2018 Google, Inc +# Written by Simon Glass <sjg@chromium.org> +# +# Entry-type module for U-Boot ELF image +# + +from entry import Entry +from blob import Entry_blob + +import fdt_util +import tools + +class Entry_u_boot_elf(Entry_blob): + """U-Boot ELF image + + Properties / Entry arguments: + - filename: Filename of u-boot (default 'u-boot') + + This is the U-Boot ELF image. It does not include a device tree but can be + relocated to any address for execution. + """ + def __init__(self, section, etype, node): + Entry_blob.__init__(self, section, etype, node) + self._strip = fdt_util.GetBool(self._node, 'strip') + + def ReadBlobContents(self): + if self._strip: + uniq = self.GetUniqueName() + out_fname = tools.GetOutputFilename('%s.stripped' % uniq) + tools.WriteFile(out_fname, tools.ReadFile(self._pathname)) + tools.Run('strip', out_fname) + self._pathname = out_fname + Entry_blob.ReadBlobContents(self) + return True + + def GetDefaultFilename(self): + return 'u-boot' diff --git a/tools/u-boot-tools/binman/etype/u_boot_img.py b/tools/u-boot-tools/binman/etype/u_boot_img.py new file mode 100644 index 0000000..1ec0757 --- /dev/null +++ b/tools/u-boot-tools/binman/etype/u_boot_img.py @@ -0,0 +1,27 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (c) 2016 Google, Inc +# Written by Simon Glass <sjg@chromium.org> +# +# Entry-type module for U-Boot binary +# + +from entry import Entry +from blob import Entry_blob + +class Entry_u_boot_img(Entry_blob): + """U-Boot legacy image + + Properties / Entry arguments: + - filename: Filename of u-boot.img (default 'u-boot.img') + + This is the U-Boot binary as a packaged image, in legacy format. It has a + header which allows it to be loaded at the correct address for execution. + + You should use FIT (Flat Image Tree) instead of the legacy image for new + applications. + """ + def __init__(self, section, etype, node): + Entry_blob.__init__(self, section, etype, node) + + def GetDefaultFilename(self): + return 'u-boot.img' diff --git a/tools/u-boot-tools/binman/etype/u_boot_nodtb.py b/tools/u-boot-tools/binman/etype/u_boot_nodtb.py new file mode 100644 index 0000000..a4b95a4 --- /dev/null +++ b/tools/u-boot-tools/binman/etype/u_boot_nodtb.py @@ -0,0 +1,27 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (c) 2016 Google, Inc +# Written by Simon Glass <sjg@chromium.org> +# +# Entry-type module for 'u-boot-nodtb.bin' +# + +from entry import Entry +from blob import Entry_blob + +class Entry_u_boot_nodtb(Entry_blob): + """U-Boot flat binary without device tree appended + + Properties / Entry arguments: + - filename: Filename of u-boot.bin (default 'u-boot-nodtb.bin') + + This is the U-Boot binary, containing relocation information to allow it + to relocate itself at runtime. It does not include a device tree blob at + the end of it so normally cannot work without it. You can add a u_boot_dtb + entry after this one, or use a u_boot entry instead (which contains both + U-Boot and the device tree). + """ + def __init__(self, section, etype, node): + Entry_blob.__init__(self, section, etype, node) + + def GetDefaultFilename(self): + return 'u-boot-nodtb.bin' diff --git a/tools/u-boot-tools/binman/etype/u_boot_spl.py b/tools/u-boot-tools/binman/etype/u_boot_spl.py new file mode 100644 index 0000000..ab78714 --- /dev/null +++ b/tools/u-boot-tools/binman/etype/u_boot_spl.py @@ -0,0 +1,43 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (c) 2016 Google, Inc +# Written by Simon Glass <sjg@chromium.org> +# +# Entry-type module for spl/u-boot-spl.bin +# + +import elf + +from entry import Entry +from blob import Entry_blob + +class Entry_u_boot_spl(Entry_blob): + """U-Boot SPL binary + + Properties / Entry arguments: + - filename: Filename of u-boot-spl.bin (default 'spl/u-boot-spl.bin') + + This is the U-Boot SPL (Secondary Program Loader) binary. This is a small + binary which loads before U-Boot proper, typically into on-chip SRAM. It is + responsible for locating, loading and jumping to U-Boot. Note that SPL is + not relocatable so must be loaded to the correct address in SRAM, or written + to run from the correct address if direct flash execution is possible (e.g. + on x86 devices). + + SPL can access binman symbols at runtime. See: + + 'Access to binman entry offsets at run time (symbols)' + + in the binman README for more information. + + The ELF file 'spl/u-boot-spl' must also be available for this to work, since + binman uses that to look up symbols to write into the SPL binary. + """ + def __init__(self, section, etype, node): + Entry_blob.__init__(self, section, etype, node) + self.elf_fname = 'spl/u-boot-spl' + + def GetDefaultFilename(self): + return 'spl/u-boot-spl.bin' + + def WriteSymbols(self, section): + elf.LookupAndWriteSymbols(self.elf_fname, self, section) diff --git a/tools/u-boot-tools/binman/etype/u_boot_spl_bss_pad.py b/tools/u-boot-tools/binman/etype/u_boot_spl_bss_pad.py new file mode 100644 index 0000000..00b7ac5 --- /dev/null +++ b/tools/u-boot-tools/binman/etype/u_boot_spl_bss_pad.py @@ -0,0 +1,42 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (c) 2016 Google, Inc +# Written by Simon Glass <sjg@chromium.org> +# +# Entry-type module for BSS padding for spl/u-boot-spl.bin. This padding +# can be added after the SPL binary to ensure that anything concatenated +# to it will appear to SPL to be at the end of BSS rather than the start. +# + +import command +import elf +from entry import Entry +from blob import Entry_blob +import tools + +class Entry_u_boot_spl_bss_pad(Entry_blob): + """U-Boot SPL binary padded with a BSS region + + Properties / Entry arguments: + None + + This is similar to u_boot_spl except that padding is added after the SPL + binary to cover the BSS (Block Started by Symbol) region. This region holds + the various used by SPL. It is set to 0 by SPL when it starts up. If you + want to append data to the SPL image (such as a device tree file), you must + pad out the BSS region to avoid the data overlapping with U-Boot variables. + This entry is useful in that case. It automatically pads out the entry size + to cover both the code, data and BSS. + + The ELF file 'spl/u-boot-spl' must also be available for this to work, since + binman uses that to look up the BSS address. + """ + def __init__(self, section, etype, node): + Entry_blob.__init__(self, section, etype, node) + + def ObtainContents(self): + fname = tools.GetInputFilename('spl/u-boot-spl') + bss_size = elf.GetSymbolAddress(fname, '__bss_size') + if not bss_size: + self.Raise('Expected __bss_size symbol in spl/u-boot-spl') + self.SetContents(chr(0) * bss_size) + return True diff --git a/tools/u-boot-tools/binman/etype/u_boot_spl_dtb.py b/tools/u-boot-tools/binman/etype/u_boot_spl_dtb.py new file mode 100644 index 0000000..e735464 --- /dev/null +++ b/tools/u-boot-tools/binman/etype/u_boot_spl_dtb.py @@ -0,0 +1,25 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (c) 2016 Google, Inc +# Written by Simon Glass <sjg@chromium.org> +# +# Entry-type module for U-Boot device tree in SPL (Secondary Program Loader) +# + +from entry import Entry +from blob_dtb import Entry_blob_dtb + +class Entry_u_boot_spl_dtb(Entry_blob_dtb): + """U-Boot SPL device tree + + Properties / Entry arguments: + - filename: Filename of u-boot.dtb (default 'spl/u-boot-spl.dtb') + + This is the SPL device tree, containing configuration information for + SPL. SPL needs this to know what devices are present and which drivers + to activate. + """ + def __init__(self, section, etype, node): + Entry_blob_dtb.__init__(self, section, etype, node) + + def GetDefaultFilename(self): + return 'spl/u-boot-spl.dtb' diff --git a/tools/u-boot-tools/binman/etype/u_boot_spl_elf.py b/tools/u-boot-tools/binman/etype/u_boot_spl_elf.py new file mode 100644 index 0000000..da328ae --- /dev/null +++ b/tools/u-boot-tools/binman/etype/u_boot_spl_elf.py @@ -0,0 +1,24 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (c) 2018 Google, Inc +# Written by Simon Glass <sjg@chromium.org> +# +# Entry-type module for U-Boot SPL ELF image +# + +from entry import Entry +from blob import Entry_blob + +class Entry_u_boot_spl_elf(Entry_blob): + """U-Boot SPL ELF image + + Properties / Entry arguments: + - filename: Filename of SPL u-boot (default 'spl/u-boot') + + This is the U-Boot SPL ELF image. It does not include a device tree but can + be relocated to any address for execution. + """ + def __init__(self, section, etype, node): + Entry_blob.__init__(self, section, etype, node) + + def GetDefaultFilename(self): + return 'spl/u-boot-spl' diff --git a/tools/u-boot-tools/binman/etype/u_boot_spl_nodtb.py b/tools/u-boot-tools/binman/etype/u_boot_spl_nodtb.py new file mode 100644 index 0000000..41c1736 --- /dev/null +++ b/tools/u-boot-tools/binman/etype/u_boot_spl_nodtb.py @@ -0,0 +1,28 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (c) 2016 Google, Inc +# Written by Simon Glass <sjg@chromium.org> +# +# Entry-type module for 'u-boot-nodtb.bin' +# + +from entry import Entry +from blob import Entry_blob + +class Entry_u_boot_spl_nodtb(Entry_blob): + """SPL binary without device tree appended + + Properties / Entry arguments: + - filename: Filename of spl/u-boot-spl-nodtb.bin (default + 'spl/u-boot-spl-nodtb.bin') + + This is the U-Boot SPL binary, It does not include a device tree blob at + the end of it so may not be able to work without it, assuming SPL needs + a device tree to operation on your platform. You can add a u_boot_spl_dtb + entry after this one, or use a u_boot_spl entry instead (which contains + both SPL and the device tree). + """ + def __init__(self, section, etype, node): + Entry_blob.__init__(self, section, etype, node) + + def GetDefaultFilename(self): + return 'spl/u-boot-spl-nodtb.bin' diff --git a/tools/u-boot-tools/binman/etype/u_boot_spl_with_ucode_ptr.py b/tools/u-boot-tools/binman/etype/u_boot_spl_with_ucode_ptr.py new file mode 100644 index 0000000..b650cf0 --- /dev/null +++ b/tools/u-boot-tools/binman/etype/u_boot_spl_with_ucode_ptr.py @@ -0,0 +1,29 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (c) 2016 Google, Inc +# Written by Simon Glass <sjg@chromium.org> +# +# Entry-type module for an SPL binary with an embedded microcode pointer +# + +import struct + +import command +from entry import Entry +from blob import Entry_blob +from u_boot_with_ucode_ptr import Entry_u_boot_with_ucode_ptr +import tools + +class Entry_u_boot_spl_with_ucode_ptr(Entry_u_boot_with_ucode_ptr): + """U-Boot SPL with embedded microcode pointer + + This is used when SPL must set up the microcode for U-Boot. + + See Entry_u_boot_ucode for full details of the entries involved in this + process. + """ + def __init__(self, section, etype, node): + Entry_u_boot_with_ucode_ptr.__init__(self, section, etype, node) + self.elf_fname = 'spl/u-boot-spl' + + def GetDefaultFilename(self): + return 'spl/u-boot-spl-nodtb.bin' diff --git a/tools/u-boot-tools/binman/etype/u_boot_tpl.py b/tools/u-boot-tools/binman/etype/u_boot_tpl.py new file mode 100644 index 0000000..4d4bb92 --- /dev/null +++ b/tools/u-boot-tools/binman/etype/u_boot_tpl.py @@ -0,0 +1,43 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (c) 2016 Google, Inc +# Written by Simon Glass <sjg@chromium.org> +# +# Entry-type module for tpl/u-boot-tpl.bin +# + +import elf + +from entry import Entry +from blob import Entry_blob + +class Entry_u_boot_tpl(Entry_blob): + """U-Boot TPL binary + + Properties / Entry arguments: + - filename: Filename of u-boot-tpl.bin (default 'tpl/u-boot-tpl.bin') + + This is the U-Boot TPL (Tertiary Program Loader) binary. This is a small + binary which loads before SPL, typically into on-chip SRAM. It is + responsible for locating, loading and jumping to SPL, the next-stage + loader. Note that SPL is not relocatable so must be loaded to the correct + address in SRAM, or written to run from the correct address if direct + flash execution is possible (e.g. on x86 devices). + + SPL can access binman symbols at runtime. See: + + 'Access to binman entry offsets at run time (symbols)' + + in the binman README for more information. + + The ELF file 'tpl/u-boot-tpl' must also be available for this to work, since + binman uses that to look up symbols to write into the TPL binary. + """ + def __init__(self, section, etype, node): + Entry_blob.__init__(self, section, etype, node) + self.elf_fname = 'tpl/u-boot-tpl' + + def GetDefaultFilename(self): + return 'tpl/u-boot-tpl.bin' + + def WriteSymbols(self, section): + elf.LookupAndWriteSymbols(self.elf_fname, self, section) diff --git a/tools/u-boot-tools/binman/etype/u_boot_tpl_dtb.py b/tools/u-boot-tools/binman/etype/u_boot_tpl_dtb.py new file mode 100644 index 0000000..bdeb0f7 --- /dev/null +++ b/tools/u-boot-tools/binman/etype/u_boot_tpl_dtb.py @@ -0,0 +1,25 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (c) 2018 Google, Inc +# Written by Simon Glass <sjg@chromium.org> +# +# Entry-type module for U-Boot device tree in TPL (Tertiary Program Loader) +# + +from entry import Entry +from blob_dtb import Entry_blob_dtb + +class Entry_u_boot_tpl_dtb(Entry_blob_dtb): + """U-Boot TPL device tree + + Properties / Entry arguments: + - filename: Filename of u-boot.dtb (default 'tpl/u-boot-tpl.dtb') + + This is the TPL device tree, containing configuration information for + TPL. TPL needs this to know what devices are present and which drivers + to activate. + """ + def __init__(self, section, etype, node): + Entry_blob_dtb.__init__(self, section, etype, node) + + def GetDefaultFilename(self): + return 'tpl/u-boot-tpl.dtb' diff --git a/tools/u-boot-tools/binman/etype/u_boot_tpl_dtb_with_ucode.py b/tools/u-boot-tools/binman/etype/u_boot_tpl_dtb_with_ucode.py new file mode 100644 index 0000000..71e04fc --- /dev/null +++ b/tools/u-boot-tools/binman/etype/u_boot_tpl_dtb_with_ucode.py @@ -0,0 +1,25 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (c) 2016 Google, Inc +# Written by Simon Glass <sjg@chromium.org> +# +# Entry-type module for U-Boot device tree with the microcode removed +# + +import control +from entry import Entry +from u_boot_dtb_with_ucode import Entry_u_boot_dtb_with_ucode +import tools + +class Entry_u_boot_tpl_dtb_with_ucode(Entry_u_boot_dtb_with_ucode): + """U-Boot TPL with embedded microcode pointer + + This is used when TPL must set up the microcode for U-Boot. + + See Entry_u_boot_ucode for full details of the entries involved in this + process. + """ + def __init__(self, section, etype, node): + Entry_u_boot_dtb_with_ucode.__init__(self, section, etype, node) + + def GetDefaultFilename(self): + return 'tpl/u-boot-tpl.dtb' diff --git a/tools/u-boot-tools/binman/etype/u_boot_tpl_with_ucode_ptr.py b/tools/u-boot-tools/binman/etype/u_boot_tpl_with_ucode_ptr.py new file mode 100644 index 0000000..8d94dde --- /dev/null +++ b/tools/u-boot-tools/binman/etype/u_boot_tpl_with_ucode_ptr.py @@ -0,0 +1,27 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (c) 2016 Google, Inc +# Written by Simon Glass <sjg@chromium.org> +# +# Entry-type module for an TPL binary with an embedded microcode pointer +# + +import struct + +import command +from entry import Entry +from blob import Entry_blob +from u_boot_with_ucode_ptr import Entry_u_boot_with_ucode_ptr +import tools + +class Entry_u_boot_tpl_with_ucode_ptr(Entry_u_boot_with_ucode_ptr): + """U-Boot TPL with embedded microcode pointer + + See Entry_u_boot_ucode for full details of the entries involved in this + process. + """ + def __init__(self, section, etype, node): + Entry_u_boot_with_ucode_ptr.__init__(self, section, etype, node) + self.elf_fname = 'tpl/u-boot-tpl' + + def GetDefaultFilename(self): + return 'tpl/u-boot-tpl-nodtb.bin' diff --git a/tools/u-boot-tools/binman/etype/u_boot_ucode.py b/tools/u-boot-tools/binman/etype/u_boot_ucode.py new file mode 100644 index 0000000..a00e530 --- /dev/null +++ b/tools/u-boot-tools/binman/etype/u_boot_ucode.py @@ -0,0 +1,99 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (c) 2016 Google, Inc +# Written by Simon Glass <sjg@chromium.org> +# +# Entry-type module for a U-Boot binary with an embedded microcode pointer +# + +from entry import Entry +from blob import Entry_blob +import tools + +class Entry_u_boot_ucode(Entry_blob): + """U-Boot microcode block + + Properties / Entry arguments: + None + + The contents of this entry are filled in automatically by other entries + which must also be in the image. + + U-Boot on x86 needs a single block of microcode. This is collected from + the various microcode update nodes in the device tree. It is also unable + to read the microcode from the device tree on platforms that use FSP + (Firmware Support Package) binaries, because the API requires that the + microcode is supplied before there is any SRAM available to use (i.e. + the FSP sets up the SRAM / cache-as-RAM but does so in the call that + requires the microcode!). To keep things simple, all x86 platforms handle + microcode the same way in U-Boot (even non-FSP platforms). This is that + a table is placed at _dt_ucode_base_size containing the base address and + size of the microcode. This is either passed to the FSP (for FSP + platforms), or used to set up the microcode (for non-FSP platforms). + This all happens in the build system since it is the only way to get + the microcode into a single blob and accessible without SRAM. + + There are two cases to handle. If there is only one microcode blob in + the device tree, then the ucode pointer it set to point to that. This + entry (u-boot-ucode) is empty. If there is more than one update, then + this entry holds the concatenation of all updates, and the device tree + entry (u-boot-dtb-with-ucode) is updated to remove the microcode. This + last step ensures that that the microcode appears in one contiguous + block in the image and is not unnecessarily duplicated in the device + tree. It is referred to as 'collation' here. + + Entry types that have a part to play in handling microcode: + + Entry_u_boot_with_ucode_ptr: + Contains u-boot-nodtb.bin (i.e. U-Boot without the device tree). + It updates it with the address and size of the microcode so that + U-Boot can find it early on start-up. + Entry_u_boot_dtb_with_ucode: + Contains u-boot.dtb. It stores the microcode in a + 'self.ucode_data' property, which is then read by this class to + obtain the microcode if needed. If collation is performed, it + removes the microcode from the device tree. + Entry_u_boot_ucode: + This class. If collation is enabled it reads the microcode from + the Entry_u_boot_dtb_with_ucode entry, and uses it as the + contents of this entry. + """ + def __init__(self, section, etype, node): + Entry_blob.__init__(self, section, etype, node) + + def ObtainContents(self): + # If the section does not need microcode, there is nothing to do + found = False + for suffix in ['', '-spl', '-tpl']: + name = 'u-boot%s-with-ucode-ptr' % suffix + entry = self.section.FindEntryType(name) + if entry and entry.target_offset: + found = True + if not found: + self.data = '' + return True + # Get the microcode from the device tree entry. If it is not available + # yet, return False so we will be called later. If the section simply + # doesn't exist, then we may as well return True, since we are going to + # get an error anyway. + for suffix in ['', '-spl', '-tpl']: + name = 'u-boot%s-dtb-with-ucode' % suffix + fdt_entry = self.section.FindEntryType(name) + if fdt_entry: + break + if not fdt_entry: + return True + if not fdt_entry.ready: + return False + + if not fdt_entry.collate: + # This binary can be empty + self.data = '' + return True + + # Write it out to a file + self._pathname = tools.GetOutputFilename('u-boot-ucode.bin') + tools.WriteFile(self._pathname, fdt_entry.ucode_data) + + self.ReadBlobContents() + + return True diff --git a/tools/u-boot-tools/binman/etype/u_boot_with_ucode_ptr.py b/tools/u-boot-tools/binman/etype/u_boot_with_ucode_ptr.py new file mode 100644 index 0000000..da0e124 --- /dev/null +++ b/tools/u-boot-tools/binman/etype/u_boot_with_ucode_ptr.py @@ -0,0 +1,96 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (c) 2016 Google, Inc +# Written by Simon Glass <sjg@chromium.org> +# +# Entry-type module for a U-Boot binary with an embedded microcode pointer +# + +import struct + +import command +import elf +from entry import Entry +from blob import Entry_blob +import fdt_util +import tools + +class Entry_u_boot_with_ucode_ptr(Entry_blob): + """U-Boot with embedded microcode pointer + + Properties / Entry arguments: + - filename: Filename of u-boot-nodtb.dtb (default 'u-boot-nodtb.dtb') + - optional-ucode: boolean property to make microcode optional. If the + u-boot.bin image does not include microcode, no error will + be generated. + + See Entry_u_boot_ucode for full details of the three entries involved in + this process. This entry updates U-Boot with the offset and size of the + microcode, to allow early x86 boot code to find it without doing anything + complicated. Otherwise it is the same as the u_boot entry. + """ + def __init__(self, section, etype, node): + Entry_blob.__init__(self, section, etype, node) + self.elf_fname = 'u-boot' + self.target_offset = None + + def GetDefaultFilename(self): + return 'u-boot-nodtb.bin' + + def ProcessFdt(self, fdt): + # Figure out where to put the microcode pointer + fname = tools.GetInputFilename(self.elf_fname) + sym = elf.GetSymbolAddress(fname, '_dt_ucode_base_size') + if sym: + self.target_offset = sym + elif not fdt_util.GetBool(self._node, 'optional-ucode'): + self.Raise('Cannot locate _dt_ucode_base_size symbol in u-boot') + return True + + def ProcessContents(self): + # If the image does not need microcode, there is nothing to do + if not self.target_offset: + return + + # Get the offset of the microcode + ucode_entry = self.section.FindEntryType('u-boot-ucode') + if not ucode_entry: + self.Raise('Cannot find microcode region u-boot-ucode') + + # Check the target pos is in the section. If it is not, then U-Boot is + # being linked incorrectly, or is being placed at the wrong offset + # in the section. + # + # The section must be set up so that U-Boot is placed at the + # flash address to which it is linked. For example, if + # CONFIG_SYS_TEXT_BASE is 0xfff00000, and the ROM is 8MB, then + # the U-Boot region must start at offset 7MB in the section. In this + # case the ROM starts at 0xff800000, so the offset of the first + # entry in the section corresponds to that. + if (self.target_offset < self.image_pos or + self.target_offset >= self.image_pos + self.size): + self.Raise('Microcode pointer _dt_ucode_base_size at %08x is outside the section ranging from %08x to %08x' % + (self.target_offset, self.image_pos, + self.image_pos + self.size)) + + # Get the microcode, either from u-boot-ucode or u-boot-dtb-with-ucode. + # If we have left the microcode in the device tree, then it will be + # in the latter. If we extracted the microcode from the device tree + # and collated it in one place, it will be in the former. + if ucode_entry.size: + offset, size = ucode_entry.offset, ucode_entry.size + else: + dtb_entry = self.section.FindEntryType('u-boot-dtb-with-ucode') + if not dtb_entry: + dtb_entry = self.section.FindEntryType( + 'u-boot-tpl-dtb-with-ucode') + if not dtb_entry: + self.Raise('Cannot find microcode region u-boot-dtb-with-ucode') + offset = dtb_entry.offset + dtb_entry.ucode_offset + size = dtb_entry.ucode_size + + # Write the microcode offset and size into the entry + offset_and_size = struct.pack('<2L', offset, size) + self.target_offset -= self.image_pos + self.ProcessContentsUpdate(self.data[:self.target_offset] + + offset_and_size + + self.data[self.target_offset + 8:]) diff --git a/tools/u-boot-tools/binman/etype/vblock.py b/tools/u-boot-tools/binman/etype/vblock.py new file mode 100644 index 0000000..c4d970e --- /dev/null +++ b/tools/u-boot-tools/binman/etype/vblock.py @@ -0,0 +1,79 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (c) 2018 Google, Inc +# Written by Simon Glass <sjg@chromium.org> +# + +# Support for a Chromium OS verified boot block, used to sign a read-write +# section of the image. + +from collections import OrderedDict +import os + +from entry import Entry, EntryArg + +import fdt_util +import tools + +class Entry_vblock(Entry): + """An entry which contains a Chromium OS verified boot block + + Properties / Entry arguments: + - keydir: Directory containing the public keys to use + - keyblock: Name of the key file to use (inside keydir) + - signprivate: Name of provide key file to use (inside keydir) + - version: Version number of the vblock (typically 1) + - kernelkey: Name of the kernel key to use (inside keydir) + - preamble-flags: Value of the vboot preamble flags (typically 0) + + Output files: + - input.<unique_name> - input file passed to futility + - vblock.<unique_name> - output file generated by futility (which is + used as the entry contents) + + Chromium OS signs the read-write firmware and kernel, writing the signature + in this block. This allows U-Boot to verify that the next firmware stage + and kernel are genuine. + """ + def __init__(self, section, etype, node): + Entry.__init__(self, section, etype, node) + self.content = fdt_util.GetPhandleList(self._node, 'content') + if not self.content: + self.Raise("Vblock must have a 'content' property") + (self.keydir, self.keyblock, self.signprivate, self.version, + self.kernelkey, self.preamble_flags) = self.GetEntryArgsOrProps([ + EntryArg('keydir', str), + EntryArg('keyblock', str), + EntryArg('signprivate', str), + EntryArg('version', int), + EntryArg('kernelkey', str), + EntryArg('preamble-flags', int)]) + + def ObtainContents(self): + # Join up the data files to be signed + input_data = '' + for entry_phandle in self.content: + data = self.section.GetContentsByPhandle(entry_phandle, self) + if data is None: + # Data not available yet + return False + input_data += data + + uniq = self.GetUniqueName() + output_fname = tools.GetOutputFilename('vblock.%s' % uniq) + input_fname = tools.GetOutputFilename('input.%s' % uniq) + tools.WriteFile(input_fname, input_data) + prefix = self.keydir + '/' + args = [ + 'vbutil_firmware', + '--vblock', output_fname, + '--keyblock', prefix + self.keyblock, + '--signprivate', prefix + self.signprivate, + '--version', '%d' % self.version, + '--fv', input_fname, + '--kernelkey', prefix + self.kernelkey, + '--flags', '%d' % self.preamble_flags, + ] + #out.Notice("Sign '%s' into %s" % (', '.join(self.value), self.label)) + stdout = tools.Run('futility', *args) + self.SetContents(tools.ReadFile(output_fname)) + return True diff --git a/tools/u-boot-tools/binman/etype/x86_start16.py b/tools/u-boot-tools/binman/etype/x86_start16.py new file mode 100644 index 0000000..7d32ecd --- /dev/null +++ b/tools/u-boot-tools/binman/etype/x86_start16.py @@ -0,0 +1,30 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (c) 2016 Google, Inc +# Written by Simon Glass <sjg@chromium.org> +# +# Entry-type module for the 16-bit x86 start-up code for U-Boot +# + +from entry import Entry +from blob import Entry_blob + +class Entry_x86_start16(Entry_blob): + """x86 16-bit start-up code for U-Boot + + Properties / Entry arguments: + - filename: Filename of u-boot-x86-16bit.bin (default + 'u-boot-x86-16bit.bin') + + x86 CPUs start up in 16-bit mode, even if they are 32-bit CPUs. This code + must be placed at a particular address. This entry holds that code. It is + typically placed at offset CONFIG_SYS_X86_START16. The code is responsible + for changing to 32-bit mode and jumping to U-Boot's entry point, which + requires 32-bit mode (for 32-bit U-Boot). + + For 64-bit U-Boot, the 'x86_start16_spl' entry type is used instead. + """ + def __init__(self, section, etype, node): + Entry_blob.__init__(self, section, etype, node) + + def GetDefaultFilename(self): + return 'u-boot-x86-16bit.bin' diff --git a/tools/u-boot-tools/binman/etype/x86_start16_spl.py b/tools/u-boot-tools/binman/etype/x86_start16_spl.py new file mode 100644 index 0000000..d85909e --- /dev/null +++ b/tools/u-boot-tools/binman/etype/x86_start16_spl.py @@ -0,0 +1,30 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (c) 2016 Google, Inc +# Written by Simon Glass <sjg@chromium.org> +# +# Entry-type module for the 16-bit x86 start-up code for U-Boot SPL +# + +from entry import Entry +from blob import Entry_blob + +class Entry_x86_start16_spl(Entry_blob): + """x86 16-bit start-up code for SPL + + Properties / Entry arguments: + - filename: Filename of spl/u-boot-x86-16bit-spl.bin (default + 'spl/u-boot-x86-16bit-spl.bin') + + x86 CPUs start up in 16-bit mode, even if they are 64-bit CPUs. This code + must be placed at a particular address. This entry holds that code. It is + typically placed at offset CONFIG_SYS_X86_START16. The code is responsible + for changing to 32-bit mode and starting SPL, which in turn changes to + 64-bit mode and jumps to U-Boot (for 64-bit U-Boot). + + For 32-bit U-Boot, the 'x86_start16' entry type is used instead. + """ + def __init__(self, section, etype, node): + Entry_blob.__init__(self, section, etype, node) + + def GetDefaultFilename(self): + return 'spl/u-boot-x86-16bit-spl.bin' diff --git a/tools/u-boot-tools/binman/etype/x86_start16_tpl.py b/tools/u-boot-tools/binman/etype/x86_start16_tpl.py new file mode 100644 index 0000000..46ce169 --- /dev/null +++ b/tools/u-boot-tools/binman/etype/x86_start16_tpl.py @@ -0,0 +1,30 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (c) 2018 Google, Inc +# Written by Simon Glass <sjg@chromium.org> +# +# Entry-type module for the 16-bit x86 start-up code for U-Boot TPL +# + +from entry import Entry +from blob import Entry_blob + +class Entry_x86_start16_tpl(Entry_blob): + """x86 16-bit start-up code for TPL + + Properties / Entry arguments: + - filename: Filename of tpl/u-boot-x86-16bit-tpl.bin (default + 'tpl/u-boot-x86-16bit-tpl.bin') + + x86 CPUs start up in 16-bit mode, even if they are 64-bit CPUs. This code + must be placed at a particular address. This entry holds that code. It is + typically placed at offset CONFIG_SYS_X86_START16. The code is responsible + for changing to 32-bit mode and starting TPL, which in turn jumps to SPL. + + If TPL is not being used, the 'x86_start16_spl or 'x86_start16' entry types + may be used instead. + """ + def __init__(self, section, etype, node): + Entry_blob.__init__(self, section, etype, node) + + def GetDefaultFilename(self): + return 'tpl/u-boot-x86-16bit-tpl.bin' diff --git a/tools/u-boot-tools/binman/fdt_test.py b/tools/u-boot-tools/binman/fdt_test.py new file mode 100644 index 0000000..ac6f910 --- /dev/null +++ b/tools/u-boot-tools/binman/fdt_test.py @@ -0,0 +1,86 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (c) 2016 Google, Inc +# Written by Simon Glass <sjg@chromium.org> +# +# Test for the fdt modules + +import os +import sys +import tempfile +import unittest + +import fdt +from fdt import FdtScan +import fdt_util +import tools + +class TestFdt(unittest.TestCase): + @classmethod + def setUpClass(self): + self._binman_dir = os.path.dirname(os.path.realpath(sys.argv[0])) + self._indir = tempfile.mkdtemp(prefix='binmant.') + tools.PrepareOutputDir(self._indir, True) + + @classmethod + def tearDownClass(self): + tools._FinaliseForTest() + + def TestFile(self, fname): + return os.path.join(self._binman_dir, 'test', fname) + + def GetCompiled(self, fname): + return fdt_util.EnsureCompiled(self.TestFile(fname)) + + def _DeleteProp(self, dt): + node = dt.GetNode('/microcode/update@0') + node.DeleteProp('data') + + def testFdtNormal(self): + fname = self.GetCompiled('034_x86_ucode.dts') + dt = FdtScan(fname) + self._DeleteProp(dt) + + def testFdtNormalProp(self): + fname = self.GetCompiled('045_prop_test.dts') + dt = FdtScan(fname) + node = dt.GetNode('/binman/intel-me') + self.assertEquals('intel-me', node.name) + val = fdt_util.GetString(node, 'filename') + self.assertEquals(str, type(val)) + self.assertEquals('me.bin', val) + + prop = node.props['intval'] + self.assertEquals(fdt.TYPE_INT, prop.type) + self.assertEquals(3, fdt_util.GetInt(node, 'intval')) + + prop = node.props['intarray'] + self.assertEquals(fdt.TYPE_INT, prop.type) + self.assertEquals(list, type(prop.value)) + self.assertEquals(2, len(prop.value)) + self.assertEquals([5, 6], + [fdt_util.fdt32_to_cpu(val) for val in prop.value]) + + prop = node.props['byteval'] + self.assertEquals(fdt.TYPE_BYTE, prop.type) + self.assertEquals(chr(8), prop.value) + + prop = node.props['bytearray'] + self.assertEquals(fdt.TYPE_BYTE, prop.type) + self.assertEquals(list, type(prop.value)) + self.assertEquals(str, type(prop.value[0])) + self.assertEquals(3, len(prop.value)) + self.assertEquals([chr(1), '#', '4'], prop.value) + + prop = node.props['longbytearray'] + self.assertEquals(fdt.TYPE_INT, prop.type) + self.assertEquals(0x090a0b0c, fdt_util.GetInt(node, 'longbytearray')) + + prop = node.props['stringval'] + self.assertEquals(fdt.TYPE_STRING, prop.type) + self.assertEquals('message2', fdt_util.GetString(node, 'stringval')) + + prop = node.props['stringarray'] + self.assertEquals(fdt.TYPE_STRING, prop.type) + self.assertEquals(list, type(prop.value)) + self.assertEquals(3, len(prop.value)) + self.assertEquals(['another', 'multi-word', 'message'], prop.value) diff --git a/tools/u-boot-tools/binman/fmap_util.py b/tools/u-boot-tools/binman/fmap_util.py new file mode 100644 index 0000000..be3cbee --- /dev/null +++ b/tools/u-boot-tools/binman/fmap_util.py @@ -0,0 +1,113 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (c) 2018 Google, Inc +# Written by Simon Glass <sjg@chromium.org> +# +# Support for flashrom's FMAP format. This supports a header followed by a +# number of 'areas', describing regions of a firmware storage device, +# generally SPI flash. + +import collections +import struct + +# constants imported from lib/fmap.h +FMAP_SIGNATURE = '__FMAP__' +FMAP_VER_MAJOR = 1 +FMAP_VER_MINOR = 0 +FMAP_STRLEN = 32 + +FMAP_AREA_STATIC = 1 << 0 +FMAP_AREA_COMPRESSED = 1 << 1 +FMAP_AREA_RO = 1 << 2 + +FMAP_HEADER_LEN = 56 +FMAP_AREA_LEN = 42 + +FMAP_HEADER_FORMAT = '<8sBBQI%dsH'% (FMAP_STRLEN) +FMAP_AREA_FORMAT = '<II%dsH' % (FMAP_STRLEN) + +FMAP_HEADER_NAMES = ( + 'signature', + 'ver_major', + 'ver_minor', + 'base', + 'image_size', + 'name', + 'nareas', +) + +FMAP_AREA_NAMES = ( + 'offset', + 'size', + 'name', + 'flags', +) + +# These are the two data structures supported by flashrom, a header (which +# appears once at the start) and an area (which is repeated until the end of +# the list of areas) +FmapHeader = collections.namedtuple('FmapHeader', FMAP_HEADER_NAMES) +FmapArea = collections.namedtuple('FmapArea', FMAP_AREA_NAMES) + + +def NameToFmap(name): + return name.replace('\0', '').replace('-', '_').upper() + +def ConvertName(field_names, fields): + """Convert a name to something flashrom likes + + Flashrom requires upper case, underscores instead of hyphens. We remove any + null characters as well. This updates the 'name' value in fields. + + Args: + field_names: List of field names for this struct + fields: Dict: + key: Field name + value: value of that field (string for the ones we support) + """ + name_index = field_names.index('name') + fields[name_index] = NameToFmap(fields[name_index]) + +def DecodeFmap(data): + """Decode a flashmap into a header and list of areas + + Args: + data: Data block containing the FMAP + + Returns: + Tuple: + header: FmapHeader object + List of FmapArea objects + """ + fields = list(struct.unpack(FMAP_HEADER_FORMAT, data[:FMAP_HEADER_LEN])) + ConvertName(FMAP_HEADER_NAMES, fields) + header = FmapHeader(*fields) + areas = [] + data = data[FMAP_HEADER_LEN:] + for area in range(header.nareas): + fields = list(struct.unpack(FMAP_AREA_FORMAT, data[:FMAP_AREA_LEN])) + ConvertName(FMAP_AREA_NAMES, fields) + areas.append(FmapArea(*fields)) + data = data[FMAP_AREA_LEN:] + return header, areas + +def EncodeFmap(image_size, name, areas): + """Create a new FMAP from a list of areas + + Args: + image_size: Size of image, to put in the header + name: Name of image, to put in the header + areas: List of FmapArea objects + + Returns: + String containing the FMAP created + """ + def _FormatBlob(fmt, names, obj): + params = [getattr(obj, name) for name in names] + ConvertName(names, params) + return struct.pack(fmt, *params) + + values = FmapHeader(FMAP_SIGNATURE, 1, 0, 0, image_size, name, len(areas)) + blob = _FormatBlob(FMAP_HEADER_FORMAT, FMAP_HEADER_NAMES, values) + for area in areas: + blob += _FormatBlob(FMAP_AREA_FORMAT, FMAP_AREA_NAMES, area) + return blob diff --git a/tools/u-boot-tools/binman/ftest.py b/tools/u-boot-tools/binman/ftest.py new file mode 100644 index 0000000..e77fce5 --- /dev/null +++ b/tools/u-boot-tools/binman/ftest.py @@ -0,0 +1,1776 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (c) 2016 Google, Inc +# Written by Simon Glass <sjg@chromium.org> +# +# To run a single test, change to this directory, and: +# +# python -m unittest func_test.TestFunctional.testHelp + +import hashlib +from optparse import OptionParser +import os +import shutil +import struct +import sys +import tempfile +import unittest + +import binman +import cmdline +import command +import control +import elf +import fdt +import fdt_util +import fmap_util +import test_util +import state +import tools +import tout + +# Contents of test files, corresponding to different entry types +U_BOOT_DATA = '1234' +U_BOOT_IMG_DATA = 'img' +U_BOOT_SPL_DATA = '56780123456789abcde' +U_BOOT_TPL_DATA = 'tpl' +BLOB_DATA = '89' +ME_DATA = '0abcd' +VGA_DATA = 'vga' +U_BOOT_DTB_DATA = 'udtb' +U_BOOT_SPL_DTB_DATA = 'spldtb' +U_BOOT_TPL_DTB_DATA = 'tpldtb' +X86_START16_DATA = 'start16' +X86_START16_SPL_DATA = 'start16spl' +X86_START16_TPL_DATA = 'start16tpl' +PPC_MPC85XX_BR_DATA = 'ppcmpc85xxbr' +U_BOOT_NODTB_DATA = 'nodtb with microcode pointer somewhere in here' +U_BOOT_SPL_NODTB_DATA = 'splnodtb with microcode pointer somewhere in here' +U_BOOT_TPL_NODTB_DATA = 'tplnodtb with microcode pointer somewhere in here' +FSP_DATA = 'fsp' +CMC_DATA = 'cmc' +VBT_DATA = 'vbt' +MRC_DATA = 'mrc' +TEXT_DATA = 'text' +TEXT_DATA2 = 'text2' +TEXT_DATA3 = 'text3' +CROS_EC_RW_DATA = 'ecrw' +GBB_DATA = 'gbbd' +BMPBLK_DATA = 'bmp' +VBLOCK_DATA = 'vblk' +FILES_DATA = ("sorry I'm late\nOh, don't bother apologising, I'm " + + "sorry you're alive\n") +COMPRESS_DATA = 'data to compress' +REFCODE_DATA = 'refcode' + + +class TestFunctional(unittest.TestCase): + """Functional tests for binman + + Most of these use a sample .dts file to build an image and then check + that it looks correct. The sample files are in the test/ subdirectory + and are numbered. + + For each entry type a very small test file is created using fixed + string contents. This makes it easy to test that things look right, and + debug problems. + + In some cases a 'real' file must be used - these are also supplied in + the test/ diurectory. + """ + @classmethod + def setUpClass(self): + global entry + import entry + + # Handle the case where argv[0] is 'python' + self._binman_dir = os.path.dirname(os.path.realpath(sys.argv[0])) + self._binman_pathname = os.path.join(self._binman_dir, 'binman') + + # Create a temporary directory for input files + self._indir = tempfile.mkdtemp(prefix='binmant.') + + # Create some test files + TestFunctional._MakeInputFile('u-boot.bin', U_BOOT_DATA) + TestFunctional._MakeInputFile('u-boot.img', U_BOOT_IMG_DATA) + TestFunctional._MakeInputFile('spl/u-boot-spl.bin', U_BOOT_SPL_DATA) + TestFunctional._MakeInputFile('tpl/u-boot-tpl.bin', U_BOOT_TPL_DATA) + TestFunctional._MakeInputFile('blobfile', BLOB_DATA) + TestFunctional._MakeInputFile('me.bin', ME_DATA) + TestFunctional._MakeInputFile('vga.bin', VGA_DATA) + self._ResetDtbs() + TestFunctional._MakeInputFile('u-boot-x86-16bit.bin', X86_START16_DATA) + TestFunctional._MakeInputFile('u-boot-br.bin', PPC_MPC85XX_BR_DATA) + TestFunctional._MakeInputFile('spl/u-boot-x86-16bit-spl.bin', + X86_START16_SPL_DATA) + TestFunctional._MakeInputFile('tpl/u-boot-x86-16bit-tpl.bin', + X86_START16_TPL_DATA) + TestFunctional._MakeInputFile('u-boot-nodtb.bin', U_BOOT_NODTB_DATA) + TestFunctional._MakeInputFile('spl/u-boot-spl-nodtb.bin', + U_BOOT_SPL_NODTB_DATA) + TestFunctional._MakeInputFile('tpl/u-boot-tpl-nodtb.bin', + U_BOOT_TPL_NODTB_DATA) + TestFunctional._MakeInputFile('fsp.bin', FSP_DATA) + TestFunctional._MakeInputFile('cmc.bin', CMC_DATA) + TestFunctional._MakeInputFile('vbt.bin', VBT_DATA) + TestFunctional._MakeInputFile('mrc.bin', MRC_DATA) + TestFunctional._MakeInputFile('ecrw.bin', CROS_EC_RW_DATA) + TestFunctional._MakeInputDir('devkeys') + TestFunctional._MakeInputFile('bmpblk.bin', BMPBLK_DATA) + TestFunctional._MakeInputFile('refcode.bin', REFCODE_DATA) + + # ELF file with a '_dt_ucode_base_size' symbol + with open(self.TestFile('u_boot_ucode_ptr')) as fd: + TestFunctional._MakeInputFile('u-boot', fd.read()) + + # Intel flash descriptor file + with open(self.TestFile('descriptor.bin')) as fd: + TestFunctional._MakeInputFile('descriptor.bin', fd.read()) + + shutil.copytree(self.TestFile('files'), + os.path.join(self._indir, 'files')) + + TestFunctional._MakeInputFile('compress', COMPRESS_DATA) + + @classmethod + def tearDownClass(self): + """Remove the temporary input directory and its contents""" + if self._indir: + shutil.rmtree(self._indir) + self._indir = None + + def setUp(self): + # Enable this to turn on debugging output + # tout.Init(tout.DEBUG) + command.test_result = None + + def tearDown(self): + """Remove the temporary output directory""" + tools._FinaliseForTest() + + @classmethod + def _ResetDtbs(self): + TestFunctional._MakeInputFile('u-boot.dtb', U_BOOT_DTB_DATA) + TestFunctional._MakeInputFile('spl/u-boot-spl.dtb', U_BOOT_SPL_DTB_DATA) + TestFunctional._MakeInputFile('tpl/u-boot-tpl.dtb', U_BOOT_TPL_DTB_DATA) + + def _RunBinman(self, *args, **kwargs): + """Run binman using the command line + + Args: + Arguments to pass, as a list of strings + kwargs: Arguments to pass to Command.RunPipe() + """ + result = command.RunPipe([[self._binman_pathname] + list(args)], + capture=True, capture_stderr=True, raise_on_error=False) + if result.return_code and kwargs.get('raise_on_error', True): + raise Exception("Error running '%s': %s" % (' '.join(args), + result.stdout + result.stderr)) + return result + + def _DoBinman(self, *args): + """Run binman using directly (in the same process) + + Args: + Arguments to pass, as a list of strings + Returns: + Return value (0 for success) + """ + args = list(args) + if '-D' in sys.argv: + args = args + ['-D'] + (options, args) = cmdline.ParseArgs(args) + options.pager = 'binman-invalid-pager' + options.build_dir = self._indir + + # For testing, you can force an increase in verbosity here + # options.verbosity = tout.DEBUG + return control.Binman(options, args) + + def _DoTestFile(self, fname, debug=False, map=False, update_dtb=False, + entry_args=None, images=None, use_real_dtb=False): + """Run binman with a given test file + + Args: + fname: Device-tree source filename to use (e.g. 005_simple.dts) + debug: True to enable debugging output + map: True to output map files for the images + update_dtb: Update the offset and size of each entry in the device + tree before packing it into the image + entry_args: Dict of entry args to supply to binman + key: arg name + value: value of that arg + images: List of image names to build + """ + args = ['-p', '-I', self._indir, '-d', self.TestFile(fname)] + if debug: + args.append('-D') + if map: + args.append('-m') + if update_dtb: + args.append('-up') + if not use_real_dtb: + args.append('--fake-dtb') + if entry_args: + for arg, value in entry_args.iteritems(): + args.append('-a%s=%s' % (arg, value)) + if images: + for image in images: + args += ['-i', image] + return self._DoBinman(*args) + + def _SetupDtb(self, fname, outfile='u-boot.dtb'): + """Set up a new test device-tree file + + The given file is compiled and set up as the device tree to be used + for ths test. + + Args: + fname: Filename of .dts file to read + outfile: Output filename for compiled device-tree binary + + Returns: + Contents of device-tree binary + """ + tools.PrepareOutputDir(None) + dtb = fdt_util.EnsureCompiled(self.TestFile(fname)) + with open(dtb) as fd: + data = fd.read() + TestFunctional._MakeInputFile(outfile, data) + tools.FinaliseOutputDir() + return data + + def _GetDtbContentsForSplTpl(self, dtb_data, name): + """Create a version of the main DTB for SPL or SPL + + For testing we don't actually have different versions of the DTB. With + U-Boot we normally run fdtgrep to remove unwanted nodes, but for tests + we don't normally have any unwanted nodes. + + We still want the DTBs for SPL and TPL to be different though, since + otherwise it is confusing to know which one we are looking at. So add + an 'spl' or 'tpl' property to the top-level node. + """ + dtb = fdt.Fdt.FromData(dtb_data) + dtb.Scan() + dtb.GetNode('/binman').AddZeroProp(name) + dtb.Sync(auto_resize=True) + dtb.Pack() + return dtb.GetContents() + + def _DoReadFileDtb(self, fname, use_real_dtb=False, map=False, + update_dtb=False, entry_args=None, reset_dtbs=True): + """Run binman and return the resulting image + + This runs binman with a given test file and then reads the resulting + output file. It is a shortcut function since most tests need to do + these steps. + + Raises an assertion failure if binman returns a non-zero exit code. + + Args: + fname: Device-tree source filename to use (e.g. 005_simple.dts) + use_real_dtb: True to use the test file as the contents of + the u-boot-dtb entry. Normally this is not needed and the + test contents (the U_BOOT_DTB_DATA string) can be used. + But in some test we need the real contents. + map: True to output map files for the images + update_dtb: Update the offset and size of each entry in the device + tree before packing it into the image + + Returns: + Tuple: + Resulting image contents + Device tree contents + Map data showing contents of image (or None if none) + Output device tree binary filename ('u-boot.dtb' path) + """ + dtb_data = None + # Use the compiled test file as the u-boot-dtb input + if use_real_dtb: + dtb_data = self._SetupDtb(fname) + infile = os.path.join(self._indir, 'u-boot.dtb') + + # For testing purposes, make a copy of the DT for SPL and TPL. Add + # a node indicating which it is, so aid verification. + for name in ['spl', 'tpl']: + dtb_fname = '%s/u-boot-%s.dtb' % (name, name) + outfile = os.path.join(self._indir, dtb_fname) + TestFunctional._MakeInputFile(dtb_fname, + self._GetDtbContentsForSplTpl(dtb_data, name)) + + try: + retcode = self._DoTestFile(fname, map=map, update_dtb=update_dtb, + entry_args=entry_args, use_real_dtb=use_real_dtb) + self.assertEqual(0, retcode) + out_dtb_fname = tools.GetOutputFilename('u-boot.dtb.out') + + # Find the (only) image, read it and return its contents + image = control.images['image'] + image_fname = tools.GetOutputFilename('image.bin') + self.assertTrue(os.path.exists(image_fname)) + if map: + map_fname = tools.GetOutputFilename('image.map') + with open(map_fname) as fd: + map_data = fd.read() + else: + map_data = None + with open(image_fname) as fd: + return fd.read(), dtb_data, map_data, out_dtb_fname + finally: + # Put the test file back + if reset_dtbs and use_real_dtb: + self._ResetDtbs() + + def _DoReadFile(self, fname, use_real_dtb=False): + """Helper function which discards the device-tree binary + + Args: + fname: Device-tree source filename to use (e.g. 005_simple.dts) + use_real_dtb: True to use the test file as the contents of + the u-boot-dtb entry. Normally this is not needed and the + test contents (the U_BOOT_DTB_DATA string) can be used. + But in some test we need the real contents. + + Returns: + Resulting image contents + """ + return self._DoReadFileDtb(fname, use_real_dtb)[0] + + @classmethod + def _MakeInputFile(self, fname, contents): + """Create a new test input file, creating directories as needed + + Args: + fname: Filename to create + contents: File contents to write in to the file + Returns: + Full pathname of file created + """ + pathname = os.path.join(self._indir, fname) + dirname = os.path.dirname(pathname) + if dirname and not os.path.exists(dirname): + os.makedirs(dirname) + with open(pathname, 'wb') as fd: + fd.write(contents) + return pathname + + @classmethod + def _MakeInputDir(self, dirname): + """Create a new test input directory, creating directories as needed + + Args: + dirname: Directory name to create + + Returns: + Full pathname of directory created + """ + pathname = os.path.join(self._indir, dirname) + if not os.path.exists(pathname): + os.makedirs(pathname) + return pathname + + @classmethod + def _SetupSplElf(self, src_fname='bss_data'): + """Set up an ELF file with a '_dt_ucode_base_size' symbol + + Args: + Filename of ELF file to use as SPL + """ + with open(self.TestFile(src_fname)) as fd: + TestFunctional._MakeInputFile('spl/u-boot-spl', fd.read()) + + @classmethod + def TestFile(self, fname): + return os.path.join(self._binman_dir, 'test', fname) + + def AssertInList(self, grep_list, target): + """Assert that at least one of a list of things is in a target + + Args: + grep_list: List of strings to check + target: Target string + """ + for grep in grep_list: + if grep in target: + return + self.fail("Error: '%' not found in '%s'" % (grep_list, target)) + + def CheckNoGaps(self, entries): + """Check that all entries fit together without gaps + + Args: + entries: List of entries to check + """ + offset = 0 + for entry in entries.values(): + self.assertEqual(offset, entry.offset) + offset += entry.size + + def GetFdtLen(self, dtb): + """Get the totalsize field from a device-tree binary + + Args: + dtb: Device-tree binary contents + + Returns: + Total size of device-tree binary, from the header + """ + return struct.unpack('>L', dtb[4:8])[0] + + def _GetPropTree(self, dtb, prop_names): + def AddNode(node, path): + if node.name != '/': + path += '/' + node.name + for subnode in node.subnodes: + for prop in subnode.props.values(): + if prop.name in prop_names: + prop_path = path + '/' + subnode.name + ':' + prop.name + tree[prop_path[len('/binman/'):]] = fdt_util.fdt32_to_cpu( + prop.value) + AddNode(subnode, path) + + tree = {} + AddNode(dtb.GetRoot(), '') + return tree + + def testRun(self): + """Test a basic run with valid args""" + result = self._RunBinman('-h') + + def testFullHelp(self): + """Test that the full help is displayed with -H""" + result = self._RunBinman('-H') + help_file = os.path.join(self._binman_dir, 'README') + # Remove possible extraneous strings + extra = '::::::::::::::\n' + help_file + '\n::::::::::::::\n' + gothelp = result.stdout.replace(extra, '') + self.assertEqual(len(gothelp), os.path.getsize(help_file)) + self.assertEqual(0, len(result.stderr)) + self.assertEqual(0, result.return_code) + + def testFullHelpInternal(self): + """Test that the full help is displayed with -H""" + try: + command.test_result = command.CommandResult() + result = self._DoBinman('-H') + help_file = os.path.join(self._binman_dir, 'README') + finally: + command.test_result = None + + def testHelp(self): + """Test that the basic help is displayed with -h""" + result = self._RunBinman('-h') + self.assertTrue(len(result.stdout) > 200) + self.assertEqual(0, len(result.stderr)) + self.assertEqual(0, result.return_code) + + def testBoard(self): + """Test that we can run it with a specific board""" + self._SetupDtb('005_simple.dts', 'sandbox/u-boot.dtb') + TestFunctional._MakeInputFile('sandbox/u-boot.bin', U_BOOT_DATA) + result = self._DoBinman('-b', 'sandbox') + self.assertEqual(0, result) + + def testNeedBoard(self): + """Test that we get an error when no board ius supplied""" + with self.assertRaises(ValueError) as e: + result = self._DoBinman() + self.assertIn("Must provide a board to process (use -b <board>)", + str(e.exception)) + + def testMissingDt(self): + """Test that an invalid device-tree file generates an error""" + with self.assertRaises(Exception) as e: + self._RunBinman('-d', 'missing_file') + # We get one error from libfdt, and a different one from fdtget. + self.AssertInList(["Couldn't open blob from 'missing_file'", + 'No such file or directory'], str(e.exception)) + + def testBrokenDt(self): + """Test that an invalid device-tree source file generates an error + + Since this is a source file it should be compiled and the error + will come from the device-tree compiler (dtc). + """ + with self.assertRaises(Exception) as e: + self._RunBinman('-d', self.TestFile('001_invalid.dts')) + self.assertIn("FATAL ERROR: Unable to parse input tree", + str(e.exception)) + + def testMissingNode(self): + """Test that a device tree without a 'binman' node generates an error""" + with self.assertRaises(Exception) as e: + self._DoBinman('-d', self.TestFile('002_missing_node.dts')) + self.assertIn("does not have a 'binman' node", str(e.exception)) + + def testEmpty(self): + """Test that an empty binman node works OK (i.e. does nothing)""" + result = self._RunBinman('-d', self.TestFile('003_empty.dts')) + self.assertEqual(0, len(result.stderr)) + self.assertEqual(0, result.return_code) + + def testInvalidEntry(self): + """Test that an invalid entry is flagged""" + with self.assertRaises(Exception) as e: + result = self._RunBinman('-d', + self.TestFile('004_invalid_entry.dts')) + self.assertIn("Unknown entry type 'not-a-valid-type' in node " + "'/binman/not-a-valid-type'", str(e.exception)) + + def testSimple(self): + """Test a simple binman with a single file""" + data = self._DoReadFile('005_simple.dts') + self.assertEqual(U_BOOT_DATA, data) + + def testSimpleDebug(self): + """Test a simple binman run with debugging enabled""" + data = self._DoTestFile('005_simple.dts', debug=True) + + def testDual(self): + """Test that we can handle creating two images + + This also tests image padding. + """ + retcode = self._DoTestFile('006_dual_image.dts') + self.assertEqual(0, retcode) + + image = control.images['image1'] + self.assertEqual(len(U_BOOT_DATA), image._size) + fname = tools.GetOutputFilename('image1.bin') + self.assertTrue(os.path.exists(fname)) + with open(fname) as fd: + data = fd.read() + self.assertEqual(U_BOOT_DATA, data) + + image = control.images['image2'] + self.assertEqual(3 + len(U_BOOT_DATA) + 5, image._size) + fname = tools.GetOutputFilename('image2.bin') + self.assertTrue(os.path.exists(fname)) + with open(fname) as fd: + data = fd.read() + self.assertEqual(U_BOOT_DATA, data[3:7]) + self.assertEqual(chr(0) * 3, data[:3]) + self.assertEqual(chr(0) * 5, data[7:]) + + def testBadAlign(self): + """Test that an invalid alignment value is detected""" + with self.assertRaises(ValueError) as e: + self._DoTestFile('007_bad_align.dts') + self.assertIn("Node '/binman/u-boot': Alignment 23 must be a power " + "of two", str(e.exception)) + + def testPackSimple(self): + """Test that packing works as expected""" + retcode = self._DoTestFile('008_pack.dts') + self.assertEqual(0, retcode) + self.assertIn('image', control.images) + image = control.images['image'] + entries = image.GetEntries() + self.assertEqual(5, len(entries)) + + # First u-boot + self.assertIn('u-boot', entries) + entry = entries['u-boot'] + self.assertEqual(0, entry.offset) + self.assertEqual(len(U_BOOT_DATA), entry.size) + + # Second u-boot, aligned to 16-byte boundary + self.assertIn('u-boot-align', entries) + entry = entries['u-boot-align'] + self.assertEqual(16, entry.offset) + self.assertEqual(len(U_BOOT_DATA), entry.size) + + # Third u-boot, size 23 bytes + self.assertIn('u-boot-size', entries) + entry = entries['u-boot-size'] + self.assertEqual(20, entry.offset) + self.assertEqual(len(U_BOOT_DATA), entry.contents_size) + self.assertEqual(23, entry.size) + + # Fourth u-boot, placed immediate after the above + self.assertIn('u-boot-next', entries) + entry = entries['u-boot-next'] + self.assertEqual(43, entry.offset) + self.assertEqual(len(U_BOOT_DATA), entry.size) + + # Fifth u-boot, placed at a fixed offset + self.assertIn('u-boot-fixed', entries) + entry = entries['u-boot-fixed'] + self.assertEqual(61, entry.offset) + self.assertEqual(len(U_BOOT_DATA), entry.size) + + self.assertEqual(65, image._size) + + def testPackExtra(self): + """Test that extra packing feature works as expected""" + retcode = self._DoTestFile('009_pack_extra.dts') + + self.assertEqual(0, retcode) + self.assertIn('image', control.images) + image = control.images['image'] + entries = image.GetEntries() + self.assertEqual(5, len(entries)) + + # First u-boot with padding before and after + self.assertIn('u-boot', entries) + entry = entries['u-boot'] + self.assertEqual(0, entry.offset) + self.assertEqual(3, entry.pad_before) + self.assertEqual(3 + 5 + len(U_BOOT_DATA), entry.size) + + # Second u-boot has an aligned size, but it has no effect + self.assertIn('u-boot-align-size-nop', entries) + entry = entries['u-boot-align-size-nop'] + self.assertEqual(12, entry.offset) + self.assertEqual(4, entry.size) + + # Third u-boot has an aligned size too + self.assertIn('u-boot-align-size', entries) + entry = entries['u-boot-align-size'] + self.assertEqual(16, entry.offset) + self.assertEqual(32, entry.size) + + # Fourth u-boot has an aligned end + self.assertIn('u-boot-align-end', entries) + entry = entries['u-boot-align-end'] + self.assertEqual(48, entry.offset) + self.assertEqual(16, entry.size) + + # Fifth u-boot immediately afterwards + self.assertIn('u-boot-align-both', entries) + entry = entries['u-boot-align-both'] + self.assertEqual(64, entry.offset) + self.assertEqual(64, entry.size) + + self.CheckNoGaps(entries) + self.assertEqual(128, image._size) + + def testPackAlignPowerOf2(self): + """Test that invalid entry alignment is detected""" + with self.assertRaises(ValueError) as e: + self._DoTestFile('010_pack_align_power2.dts') + self.assertIn("Node '/binman/u-boot': Alignment 5 must be a power " + "of two", str(e.exception)) + + def testPackAlignSizePowerOf2(self): + """Test that invalid entry size alignment is detected""" + with self.assertRaises(ValueError) as e: + self._DoTestFile('011_pack_align_size_power2.dts') + self.assertIn("Node '/binman/u-boot': Alignment size 55 must be a " + "power of two", str(e.exception)) + + def testPackInvalidAlign(self): + """Test detection of an offset that does not match its alignment""" + with self.assertRaises(ValueError) as e: + self._DoTestFile('012_pack_inv_align.dts') + self.assertIn("Node '/binman/u-boot': Offset 0x5 (5) does not match " + "align 0x4 (4)", str(e.exception)) + + def testPackInvalidSizeAlign(self): + """Test that invalid entry size alignment is detected""" + with self.assertRaises(ValueError) as e: + self._DoTestFile('013_pack_inv_size_align.dts') + self.assertIn("Node '/binman/u-boot': Size 0x5 (5) does not match " + "align-size 0x4 (4)", str(e.exception)) + + def testPackOverlap(self): + """Test that overlapping regions are detected""" + with self.assertRaises(ValueError) as e: + self._DoTestFile('014_pack_overlap.dts') + self.assertIn("Node '/binman/u-boot-align': Offset 0x3 (3) overlaps " + "with previous entry '/binman/u-boot' ending at 0x4 (4)", + str(e.exception)) + + def testPackEntryOverflow(self): + """Test that entries that overflow their size are detected""" + with self.assertRaises(ValueError) as e: + self._DoTestFile('015_pack_overflow.dts') + self.assertIn("Node '/binman/u-boot': Entry contents size is 0x4 (4) " + "but entry size is 0x3 (3)", str(e.exception)) + + def testPackImageOverflow(self): + """Test that entries which overflow the image size are detected""" + with self.assertRaises(ValueError) as e: + self._DoTestFile('016_pack_image_overflow.dts') + self.assertIn("Section '/binman': contents size 0x4 (4) exceeds section " + "size 0x3 (3)", str(e.exception)) + + def testPackImageSize(self): + """Test that the image size can be set""" + retcode = self._DoTestFile('017_pack_image_size.dts') + self.assertEqual(0, retcode) + self.assertIn('image', control.images) + image = control.images['image'] + self.assertEqual(7, image._size) + + def testPackImageSizeAlign(self): + """Test that image size alignemnt works as expected""" + retcode = self._DoTestFile('018_pack_image_align.dts') + self.assertEqual(0, retcode) + self.assertIn('image', control.images) + image = control.images['image'] + self.assertEqual(16, image._size) + + def testPackInvalidImageAlign(self): + """Test that invalid image alignment is detected""" + with self.assertRaises(ValueError) as e: + self._DoTestFile('019_pack_inv_image_align.dts') + self.assertIn("Section '/binman': Size 0x7 (7) does not match " + "align-size 0x8 (8)", str(e.exception)) + + def testPackAlignPowerOf2(self): + """Test that invalid image alignment is detected""" + with self.assertRaises(ValueError) as e: + self._DoTestFile('020_pack_inv_image_align_power2.dts') + self.assertIn("Section '/binman': Alignment size 131 must be a power of " + "two", str(e.exception)) + + def testImagePadByte(self): + """Test that the image pad byte can be specified""" + self._SetupSplElf() + data = self._DoReadFile('021_image_pad.dts') + self.assertEqual(U_BOOT_SPL_DATA + (chr(0xff) * 1) + U_BOOT_DATA, data) + + def testImageName(self): + """Test that image files can be named""" + retcode = self._DoTestFile('022_image_name.dts') + self.assertEqual(0, retcode) + image = control.images['image1'] + fname = tools.GetOutputFilename('test-name') + self.assertTrue(os.path.exists(fname)) + + image = control.images['image2'] + fname = tools.GetOutputFilename('test-name.xx') + self.assertTrue(os.path.exists(fname)) + + def testBlobFilename(self): + """Test that generic blobs can be provided by filename""" + data = self._DoReadFile('023_blob.dts') + self.assertEqual(BLOB_DATA, data) + + def testPackSorted(self): + """Test that entries can be sorted""" + self._SetupSplElf() + data = self._DoReadFile('024_sorted.dts') + self.assertEqual(chr(0) * 1 + U_BOOT_SPL_DATA + chr(0) * 2 + + U_BOOT_DATA, data) + + def testPackZeroOffset(self): + """Test that an entry at offset 0 is not given a new offset""" + with self.assertRaises(ValueError) as e: + self._DoTestFile('025_pack_zero_size.dts') + self.assertIn("Node '/binman/u-boot-spl': Offset 0x0 (0) overlaps " + "with previous entry '/binman/u-boot' ending at 0x4 (4)", + str(e.exception)) + + def testPackUbootDtb(self): + """Test that a device tree can be added to U-Boot""" + data = self._DoReadFile('026_pack_u_boot_dtb.dts') + self.assertEqual(U_BOOT_NODTB_DATA + U_BOOT_DTB_DATA, data) + + def testPackX86RomNoSize(self): + """Test that the end-at-4gb property requires a size property""" + with self.assertRaises(ValueError) as e: + self._DoTestFile('027_pack_4gb_no_size.dts') + self.assertIn("Section '/binman': Section size must be provided when " + "using end-at-4gb", str(e.exception)) + + def test4gbAndSkipAtStartTogether(self): + """Test that the end-at-4gb and skip-at-size property can't be used + together""" + with self.assertRaises(ValueError) as e: + self._DoTestFile('80_4gb_and_skip_at_start_together.dts') + self.assertIn("Section '/binman': Provide either 'end-at-4gb' or " + "'skip-at-start'", str(e.exception)) + + def testPackX86RomOutside(self): + """Test that the end-at-4gb property checks for offset boundaries""" + with self.assertRaises(ValueError) as e: + self._DoTestFile('028_pack_4gb_outside.dts') + self.assertIn("Node '/binman/u-boot': Offset 0x0 (0) is outside " + "the section starting at 0xffffffe0 (4294967264)", + str(e.exception)) + + def testPackX86Rom(self): + """Test that a basic x86 ROM can be created""" + self._SetupSplElf() + data = self._DoReadFile('029_x86-rom.dts') + self.assertEqual(U_BOOT_DATA + chr(0) * 7 + U_BOOT_SPL_DATA + + chr(0) * 2, data) + + def testPackX86RomMeNoDesc(self): + """Test that an invalid Intel descriptor entry is detected""" + TestFunctional._MakeInputFile('descriptor.bin', '') + with self.assertRaises(ValueError) as e: + self._DoTestFile('031_x86-rom-me.dts') + self.assertIn("Node '/binman/intel-descriptor': Cannot find FD " + "signature", str(e.exception)) + + def testPackX86RomBadDesc(self): + """Test that the Intel requires a descriptor entry""" + with self.assertRaises(ValueError) as e: + self._DoTestFile('030_x86-rom-me-no-desc.dts') + self.assertIn("Node '/binman/intel-me': No offset set with " + "offset-unset: should another entry provide this correct " + "offset?", str(e.exception)) + + def testPackX86RomMe(self): + """Test that an x86 ROM with an ME region can be created""" + data = self._DoReadFile('031_x86-rom-me.dts') + self.assertEqual(ME_DATA, data[0x1000:0x1000 + len(ME_DATA)]) + + def testPackVga(self): + """Test that an image with a VGA binary can be created""" + data = self._DoReadFile('032_intel-vga.dts') + self.assertEqual(VGA_DATA, data[:len(VGA_DATA)]) + + def testPackStart16(self): + """Test that an image with an x86 start16 region can be created""" + data = self._DoReadFile('033_x86-start16.dts') + self.assertEqual(X86_START16_DATA, data[:len(X86_START16_DATA)]) + + def testPackPowerpcMpc85xxBootpgResetvec(self): + """Test that an image with powerpc-mpc85xx-bootpg-resetvec can be + created""" + data = self._DoReadFile('81_powerpc_mpc85xx_bootpg_resetvec.dts') + self.assertEqual(PPC_MPC85XX_BR_DATA, data[:len(PPC_MPC85XX_BR_DATA)]) + + def _RunMicrocodeTest(self, dts_fname, nodtb_data, ucode_second=False): + """Handle running a test for insertion of microcode + + Args: + dts_fname: Name of test .dts file + nodtb_data: Data that we expect in the first section + ucode_second: True if the microsecond entry is second instead of + third + + Returns: + Tuple: + Contents of first region (U-Boot or SPL) + Offset and size components of microcode pointer, as inserted + in the above (two 4-byte words) + """ + data = self._DoReadFile(dts_fname, True) + + # Now check the device tree has no microcode + if ucode_second: + ucode_content = data[len(nodtb_data):] + ucode_pos = len(nodtb_data) + dtb_with_ucode = ucode_content[16:] + fdt_len = self.GetFdtLen(dtb_with_ucode) + else: + dtb_with_ucode = data[len(nodtb_data):] + fdt_len = self.GetFdtLen(dtb_with_ucode) + ucode_content = dtb_with_ucode[fdt_len:] + ucode_pos = len(nodtb_data) + fdt_len + fname = tools.GetOutputFilename('test.dtb') + with open(fname, 'wb') as fd: + fd.write(dtb_with_ucode) + dtb = fdt.FdtScan(fname) + ucode = dtb.GetNode('/microcode') + self.assertTrue(ucode) + for node in ucode.subnodes: + self.assertFalse(node.props.get('data')) + + # Check that the microcode appears immediately after the Fdt + # This matches the concatenation of the data properties in + # the /microcode/update@xxx nodes in 34_x86_ucode.dts. + ucode_data = struct.pack('>4L', 0x12345678, 0x12345679, 0xabcd0000, + 0x78235609) + self.assertEqual(ucode_data, ucode_content[:len(ucode_data)]) + + # Check that the microcode pointer was inserted. It should match the + # expected offset and size + pos_and_size = struct.pack('<2L', 0xfffffe00 + ucode_pos, + len(ucode_data)) + u_boot = data[:len(nodtb_data)] + return u_boot, pos_and_size + + def testPackUbootMicrocode(self): + """Test that x86 microcode can be handled correctly + + We expect to see the following in the image, in order: + u-boot-nodtb.bin with a microcode pointer inserted at the correct + place + u-boot.dtb with the microcode removed + the microcode + """ + first, pos_and_size = self._RunMicrocodeTest('034_x86_ucode.dts', + U_BOOT_NODTB_DATA) + self.assertEqual('nodtb with microcode' + pos_and_size + + ' somewhere in here', first) + + def _RunPackUbootSingleMicrocode(self): + """Test that x86 microcode can be handled correctly + + We expect to see the following in the image, in order: + u-boot-nodtb.bin with a microcode pointer inserted at the correct + place + u-boot.dtb with the microcode + an empty microcode region + """ + # We need the libfdt library to run this test since only that allows + # finding the offset of a property. This is required by + # Entry_u_boot_dtb_with_ucode.ObtainContents(). + data = self._DoReadFile('035_x86_single_ucode.dts', True) + + second = data[len(U_BOOT_NODTB_DATA):] + + fdt_len = self.GetFdtLen(second) + third = second[fdt_len:] + second = second[:fdt_len] + + ucode_data = struct.pack('>2L', 0x12345678, 0x12345679) + self.assertIn(ucode_data, second) + ucode_pos = second.find(ucode_data) + len(U_BOOT_NODTB_DATA) + + # Check that the microcode pointer was inserted. It should match the + # expected offset and size + pos_and_size = struct.pack('<2L', 0xfffffe00 + ucode_pos, + len(ucode_data)) + first = data[:len(U_BOOT_NODTB_DATA)] + self.assertEqual('nodtb with microcode' + pos_and_size + + ' somewhere in here', first) + + def testPackUbootSingleMicrocode(self): + """Test that x86 microcode can be handled correctly with fdt_normal. + """ + self._RunPackUbootSingleMicrocode() + + def testUBootImg(self): + """Test that u-boot.img can be put in a file""" + data = self._DoReadFile('036_u_boot_img.dts') + self.assertEqual(U_BOOT_IMG_DATA, data) + + def testNoMicrocode(self): + """Test that a missing microcode region is detected""" + with self.assertRaises(ValueError) as e: + self._DoReadFile('037_x86_no_ucode.dts', True) + self.assertIn("Node '/binman/u-boot-dtb-with-ucode': No /microcode " + "node found in ", str(e.exception)) + + def testMicrocodeWithoutNode(self): + """Test that a missing u-boot-dtb-with-ucode node is detected""" + with self.assertRaises(ValueError) as e: + self._DoReadFile('038_x86_ucode_missing_node.dts', True) + self.assertIn("Node '/binman/u-boot-with-ucode-ptr': Cannot find " + "microcode region u-boot-dtb-with-ucode", str(e.exception)) + + def testMicrocodeWithoutNode2(self): + """Test that a missing u-boot-ucode node is detected""" + with self.assertRaises(ValueError) as e: + self._DoReadFile('039_x86_ucode_missing_node2.dts', True) + self.assertIn("Node '/binman/u-boot-with-ucode-ptr': Cannot find " + "microcode region u-boot-ucode", str(e.exception)) + + def testMicrocodeWithoutPtrInElf(self): + """Test that a U-Boot binary without the microcode symbol is detected""" + # ELF file without a '_dt_ucode_base_size' symbol + try: + with open(self.TestFile('u_boot_no_ucode_ptr')) as fd: + TestFunctional._MakeInputFile('u-boot', fd.read()) + + with self.assertRaises(ValueError) as e: + self._RunPackUbootSingleMicrocode() + self.assertIn("Node '/binman/u-boot-with-ucode-ptr': Cannot locate " + "_dt_ucode_base_size symbol in u-boot", str(e.exception)) + + finally: + # Put the original file back + with open(self.TestFile('u_boot_ucode_ptr')) as fd: + TestFunctional._MakeInputFile('u-boot', fd.read()) + + def testMicrocodeNotInImage(self): + """Test that microcode must be placed within the image""" + with self.assertRaises(ValueError) as e: + self._DoReadFile('040_x86_ucode_not_in_image.dts', True) + self.assertIn("Node '/binman/u-boot-with-ucode-ptr': Microcode " + "pointer _dt_ucode_base_size at fffffe14 is outside the " + "section ranging from 00000000 to 0000002e", str(e.exception)) + + def testWithoutMicrocode(self): + """Test that we can cope with an image without microcode (e.g. qemu)""" + with open(self.TestFile('u_boot_no_ucode_ptr')) as fd: + TestFunctional._MakeInputFile('u-boot', fd.read()) + data, dtb, _, _ = self._DoReadFileDtb('044_x86_optional_ucode.dts', True) + + # Now check the device tree has no microcode + self.assertEqual(U_BOOT_NODTB_DATA, data[:len(U_BOOT_NODTB_DATA)]) + second = data[len(U_BOOT_NODTB_DATA):] + + fdt_len = self.GetFdtLen(second) + self.assertEqual(dtb, second[:fdt_len]) + + used_len = len(U_BOOT_NODTB_DATA) + fdt_len + third = data[used_len:] + self.assertEqual(chr(0) * (0x200 - used_len), third) + + def testUnknownPosSize(self): + """Test that microcode must be placed within the image""" + with self.assertRaises(ValueError) as e: + self._DoReadFile('041_unknown_pos_size.dts', True) + self.assertIn("Section '/binman': Unable to set offset/size for unknown " + "entry 'invalid-entry'", str(e.exception)) + + def testPackFsp(self): + """Test that an image with a FSP binary can be created""" + data = self._DoReadFile('042_intel-fsp.dts') + self.assertEqual(FSP_DATA, data[:len(FSP_DATA)]) + + def testPackCmc(self): + """Test that an image with a CMC binary can be created""" + data = self._DoReadFile('043_intel-cmc.dts') + self.assertEqual(CMC_DATA, data[:len(CMC_DATA)]) + + def testPackVbt(self): + """Test that an image with a VBT binary can be created""" + data = self._DoReadFile('046_intel-vbt.dts') + self.assertEqual(VBT_DATA, data[:len(VBT_DATA)]) + + def testSplBssPad(self): + """Test that we can pad SPL's BSS with zeros""" + # ELF file with a '__bss_size' symbol + self._SetupSplElf() + data = self._DoReadFile('047_spl_bss_pad.dts') + self.assertEqual(U_BOOT_SPL_DATA + (chr(0) * 10) + U_BOOT_DATA, data) + + def testSplBssPadMissing(self): + """Test that a missing symbol is detected""" + self._SetupSplElf('u_boot_ucode_ptr') + with self.assertRaises(ValueError) as e: + self._DoReadFile('047_spl_bss_pad.dts') + self.assertIn('Expected __bss_size symbol in spl/u-boot-spl', + str(e.exception)) + + def testPackStart16Spl(self): + """Test that an image with an x86 start16 SPL region can be created""" + data = self._DoReadFile('048_x86-start16-spl.dts') + self.assertEqual(X86_START16_SPL_DATA, data[:len(X86_START16_SPL_DATA)]) + + def _PackUbootSplMicrocode(self, dts, ucode_second=False): + """Helper function for microcode tests + + We expect to see the following in the image, in order: + u-boot-spl-nodtb.bin with a microcode pointer inserted at the + correct place + u-boot.dtb with the microcode removed + the microcode + + Args: + dts: Device tree file to use for test + ucode_second: True if the microsecond entry is second instead of + third + """ + self._SetupSplElf('u_boot_ucode_ptr') + first, pos_and_size = self._RunMicrocodeTest(dts, U_BOOT_SPL_NODTB_DATA, + ucode_second=ucode_second) + self.assertEqual('splnodtb with microc' + pos_and_size + + 'ter somewhere in here', first) + + def testPackUbootSplMicrocode(self): + """Test that x86 microcode can be handled correctly in SPL""" + self._PackUbootSplMicrocode('049_x86_ucode_spl.dts') + + def testPackUbootSplMicrocodeReorder(self): + """Test that order doesn't matter for microcode entries + + This is the same as testPackUbootSplMicrocode but when we process the + u-boot-ucode entry we have not yet seen the u-boot-dtb-with-ucode + entry, so we reply on binman to try later. + """ + self._PackUbootSplMicrocode('058_x86_ucode_spl_needs_retry.dts', + ucode_second=True) + + def testPackMrc(self): + """Test that an image with an MRC binary can be created""" + data = self._DoReadFile('050_intel_mrc.dts') + self.assertEqual(MRC_DATA, data[:len(MRC_DATA)]) + + def testSplDtb(self): + """Test that an image with spl/u-boot-spl.dtb can be created""" + data = self._DoReadFile('051_u_boot_spl_dtb.dts') + self.assertEqual(U_BOOT_SPL_DTB_DATA, data[:len(U_BOOT_SPL_DTB_DATA)]) + + def testSplNoDtb(self): + """Test that an image with spl/u-boot-spl-nodtb.bin can be created""" + data = self._DoReadFile('052_u_boot_spl_nodtb.dts') + self.assertEqual(U_BOOT_SPL_NODTB_DATA, data[:len(U_BOOT_SPL_NODTB_DATA)]) + + def testSymbols(self): + """Test binman can assign symbols embedded in U-Boot""" + elf_fname = self.TestFile('u_boot_binman_syms') + syms = elf.GetSymbols(elf_fname, ['binman', 'image']) + addr = elf.GetSymbolAddress(elf_fname, '__image_copy_start') + self.assertEqual(syms['_binman_u_boot_spl_prop_offset'].address, addr) + + self._SetupSplElf('u_boot_binman_syms') + data = self._DoReadFile('053_symbols.dts') + sym_values = struct.pack('<LQL', 0x24 + 0, 0x24 + 24, 0x24 + 20) + expected = (sym_values + U_BOOT_SPL_DATA[16:] + chr(0xff) + + U_BOOT_DATA + + sym_values + U_BOOT_SPL_DATA[16:]) + self.assertEqual(expected, data) + + def testPackUnitAddress(self): + """Test that we support multiple binaries with the same name""" + data = self._DoReadFile('054_unit_address.dts') + self.assertEqual(U_BOOT_DATA + U_BOOT_DATA, data) + + def testSections(self): + """Basic test of sections""" + data = self._DoReadFile('055_sections.dts') + expected = (U_BOOT_DATA + '!' * 12 + U_BOOT_DATA + 'a' * 12 + + U_BOOT_DATA + '&' * 4) + self.assertEqual(expected, data) + + def testMap(self): + """Tests outputting a map of the images""" + _, _, map_data, _ = self._DoReadFileDtb('055_sections.dts', map=True) + self.assertEqual('''ImagePos Offset Size Name +00000000 00000000 00000028 main-section +00000000 00000000 00000010 section@0 +00000000 00000000 00000004 u-boot +00000010 00000010 00000010 section@1 +00000010 00000000 00000004 u-boot +00000020 00000020 00000004 section@2 +00000020 00000000 00000004 u-boot +''', map_data) + + def testNamePrefix(self): + """Tests that name prefixes are used""" + _, _, map_data, _ = self._DoReadFileDtb('056_name_prefix.dts', map=True) + self.assertEqual('''ImagePos Offset Size Name +00000000 00000000 00000028 main-section +00000000 00000000 00000010 section@0 +00000000 00000000 00000004 ro-u-boot +00000010 00000010 00000010 section@1 +00000010 00000000 00000004 rw-u-boot +''', map_data) + + def testUnknownContents(self): + """Test that obtaining the contents works as expected""" + with self.assertRaises(ValueError) as e: + self._DoReadFile('057_unknown_contents.dts', True) + self.assertIn("Section '/binman': Internal error: Could not complete " + "processing of contents: remaining [<_testing.Entry__testing ", + str(e.exception)) + + def testBadChangeSize(self): + """Test that trying to change the size of an entry fails""" + with self.assertRaises(ValueError) as e: + self._DoReadFile('059_change_size.dts', True) + self.assertIn("Node '/binman/_testing': Cannot update entry size from " + '2 to 1', str(e.exception)) + + def testUpdateFdt(self): + """Test that we can update the device tree with offset/size info""" + _, _, _, out_dtb_fname = self._DoReadFileDtb('060_fdt_update.dts', + update_dtb=True) + dtb = fdt.Fdt(out_dtb_fname) + dtb.Scan() + props = self._GetPropTree(dtb, ['offset', 'size', 'image-pos']) + self.assertEqual({ + 'image-pos': 0, + 'offset': 0, + '_testing:offset': 32, + '_testing:size': 1, + '_testing:image-pos': 32, + 'section@0/u-boot:offset': 0, + 'section@0/u-boot:size': len(U_BOOT_DATA), + 'section@0/u-boot:image-pos': 0, + 'section@0:offset': 0, + 'section@0:size': 16, + 'section@0:image-pos': 0, + + 'section@1/u-boot:offset': 0, + 'section@1/u-boot:size': len(U_BOOT_DATA), + 'section@1/u-boot:image-pos': 16, + 'section@1:offset': 16, + 'section@1:size': 16, + 'section@1:image-pos': 16, + 'size': 40 + }, props) + + def testUpdateFdtBad(self): + """Test that we detect when ProcessFdt never completes""" + with self.assertRaises(ValueError) as e: + self._DoReadFileDtb('061_fdt_update_bad.dts', update_dtb=True) + self.assertIn('Could not complete processing of Fdt: remaining ' + '[<_testing.Entry__testing', str(e.exception)) + + def testEntryArgs(self): + """Test passing arguments to entries from the command line""" + entry_args = { + 'test-str-arg': 'test1', + 'test-int-arg': '456', + } + self._DoReadFileDtb('062_entry_args.dts', entry_args=entry_args) + self.assertIn('image', control.images) + entry = control.images['image'].GetEntries()['_testing'] + self.assertEqual('test0', entry.test_str_fdt) + self.assertEqual('test1', entry.test_str_arg) + self.assertEqual(123, entry.test_int_fdt) + self.assertEqual(456, entry.test_int_arg) + + def testEntryArgsMissing(self): + """Test missing arguments and properties""" + entry_args = { + 'test-int-arg': '456', + } + self._DoReadFileDtb('063_entry_args_missing.dts', entry_args=entry_args) + entry = control.images['image'].GetEntries()['_testing'] + self.assertEqual('test0', entry.test_str_fdt) + self.assertEqual(None, entry.test_str_arg) + self.assertEqual(None, entry.test_int_fdt) + self.assertEqual(456, entry.test_int_arg) + + def testEntryArgsRequired(self): + """Test missing arguments and properties""" + entry_args = { + 'test-int-arg': '456', + } + with self.assertRaises(ValueError) as e: + self._DoReadFileDtb('064_entry_args_required.dts') + self.assertIn("Node '/binman/_testing': Missing required " + 'properties/entry args: test-str-arg, test-int-fdt, test-int-arg', + str(e.exception)) + + def testEntryArgsInvalidFormat(self): + """Test that an invalid entry-argument format is detected""" + args = ['-d', self.TestFile('064_entry_args_required.dts'), '-ano-value'] + with self.assertRaises(ValueError) as e: + self._DoBinman(*args) + self.assertIn("Invalid entry arguemnt 'no-value'", str(e.exception)) + + def testEntryArgsInvalidInteger(self): + """Test that an invalid entry-argument integer is detected""" + entry_args = { + 'test-int-arg': 'abc', + } + with self.assertRaises(ValueError) as e: + self._DoReadFileDtb('062_entry_args.dts', entry_args=entry_args) + self.assertIn("Node '/binman/_testing': Cannot convert entry arg " + "'test-int-arg' (value 'abc') to integer", + str(e.exception)) + + def testEntryArgsInvalidDatatype(self): + """Test that an invalid entry-argument datatype is detected + + This test could be written in entry_test.py except that it needs + access to control.entry_args, which seems more than that module should + be able to see. + """ + entry_args = { + 'test-bad-datatype-arg': '12', + } + with self.assertRaises(ValueError) as e: + self._DoReadFileDtb('065_entry_args_unknown_datatype.dts', + entry_args=entry_args) + self.assertIn('GetArg() internal error: Unknown data type ', + str(e.exception)) + + def testText(self): + """Test for a text entry type""" + entry_args = { + 'test-id': TEXT_DATA, + 'test-id2': TEXT_DATA2, + 'test-id3': TEXT_DATA3, + } + data, _, _, _ = self._DoReadFileDtb('066_text.dts', + entry_args=entry_args) + expected = (TEXT_DATA + chr(0) * (8 - len(TEXT_DATA)) + TEXT_DATA2 + + TEXT_DATA3 + 'some text') + self.assertEqual(expected, data) + + def testEntryDocs(self): + """Test for creation of entry documentation""" + with test_util.capture_sys_output() as (stdout, stderr): + control.WriteEntryDocs(binman.GetEntryModules()) + self.assertTrue(len(stdout.getvalue()) > 0) + + def testEntryDocsMissing(self): + """Test handling of missing entry documentation""" + with self.assertRaises(ValueError) as e: + with test_util.capture_sys_output() as (stdout, stderr): + control.WriteEntryDocs(binman.GetEntryModules(), 'u_boot') + self.assertIn('Documentation is missing for modules: u_boot', + str(e.exception)) + + def testFmap(self): + """Basic test of generation of a flashrom fmap""" + data = self._DoReadFile('067_fmap.dts') + fhdr, fentries = fmap_util.DecodeFmap(data[32:]) + expected = U_BOOT_DATA + '!' * 12 + U_BOOT_DATA + 'a' * 12 + self.assertEqual(expected, data[:32]) + self.assertEqual('__FMAP__', fhdr.signature) + self.assertEqual(1, fhdr.ver_major) + self.assertEqual(0, fhdr.ver_minor) + self.assertEqual(0, fhdr.base) + self.assertEqual(16 + 16 + + fmap_util.FMAP_HEADER_LEN + + fmap_util.FMAP_AREA_LEN * 3, fhdr.image_size) + self.assertEqual('FMAP', fhdr.name) + self.assertEqual(3, fhdr.nareas) + for fentry in fentries: + self.assertEqual(0, fentry.flags) + + self.assertEqual(0, fentries[0].offset) + self.assertEqual(4, fentries[0].size) + self.assertEqual('RO_U_BOOT', fentries[0].name) + + self.assertEqual(16, fentries[1].offset) + self.assertEqual(4, fentries[1].size) + self.assertEqual('RW_U_BOOT', fentries[1].name) + + self.assertEqual(32, fentries[2].offset) + self.assertEqual(fmap_util.FMAP_HEADER_LEN + + fmap_util.FMAP_AREA_LEN * 3, fentries[2].size) + self.assertEqual('FMAP', fentries[2].name) + + def testBlobNamedByArg(self): + """Test we can add a blob with the filename coming from an entry arg""" + entry_args = { + 'cros-ec-rw-path': 'ecrw.bin', + } + data, _, _, _ = self._DoReadFileDtb('068_blob_named_by_arg.dts', + entry_args=entry_args) + + def testFill(self): + """Test for an fill entry type""" + data = self._DoReadFile('069_fill.dts') + expected = 8 * chr(0xff) + 8 * chr(0) + self.assertEqual(expected, data) + + def testFillNoSize(self): + """Test for an fill entry type with no size""" + with self.assertRaises(ValueError) as e: + self._DoReadFile('070_fill_no_size.dts') + self.assertIn("'fill' entry must have a size property", + str(e.exception)) + + def _HandleGbbCommand(self, pipe_list): + """Fake calls to the futility utility""" + if pipe_list[0][0] == 'futility': + fname = pipe_list[0][-1] + # Append our GBB data to the file, which will happen every time the + # futility command is called. + with open(fname, 'a') as fd: + fd.write(GBB_DATA) + return command.CommandResult() + + def testGbb(self): + """Test for the Chromium OS Google Binary Block""" + command.test_result = self._HandleGbbCommand + entry_args = { + 'keydir': 'devkeys', + 'bmpblk': 'bmpblk.bin', + } + data, _, _, _ = self._DoReadFileDtb('071_gbb.dts', entry_args=entry_args) + + # Since futility + expected = GBB_DATA + GBB_DATA + 8 * chr(0) + (0x2180 - 16) * chr(0) + self.assertEqual(expected, data) + + def testGbbTooSmall(self): + """Test for the Chromium OS Google Binary Block being large enough""" + with self.assertRaises(ValueError) as e: + self._DoReadFileDtb('072_gbb_too_small.dts') + self.assertIn("Node '/binman/gbb': GBB is too small", + str(e.exception)) + + def testGbbNoSize(self): + """Test for the Chromium OS Google Binary Block having a size""" + with self.assertRaises(ValueError) as e: + self._DoReadFileDtb('073_gbb_no_size.dts') + self.assertIn("Node '/binman/gbb': GBB must have a fixed size", + str(e.exception)) + + def _HandleVblockCommand(self, pipe_list): + """Fake calls to the futility utility""" + if pipe_list[0][0] == 'futility': + fname = pipe_list[0][3] + with open(fname, 'wb') as fd: + fd.write(VBLOCK_DATA) + return command.CommandResult() + + def testVblock(self): + """Test for the Chromium OS Verified Boot Block""" + command.test_result = self._HandleVblockCommand + entry_args = { + 'keydir': 'devkeys', + } + data, _, _, _ = self._DoReadFileDtb('074_vblock.dts', + entry_args=entry_args) + expected = U_BOOT_DATA + VBLOCK_DATA + U_BOOT_DTB_DATA + self.assertEqual(expected, data) + + def testVblockNoContent(self): + """Test we detect a vblock which has no content to sign""" + with self.assertRaises(ValueError) as e: + self._DoReadFile('075_vblock_no_content.dts') + self.assertIn("Node '/binman/vblock': Vblock must have a 'content' " + 'property', str(e.exception)) + + def testVblockBadPhandle(self): + """Test that we detect a vblock with an invalid phandle in contents""" + with self.assertRaises(ValueError) as e: + self._DoReadFile('076_vblock_bad_phandle.dts') + self.assertIn("Node '/binman/vblock': Cannot find node for phandle " + '1000', str(e.exception)) + + def testVblockBadEntry(self): + """Test that we detect an entry that points to a non-entry""" + with self.assertRaises(ValueError) as e: + self._DoReadFile('077_vblock_bad_entry.dts') + self.assertIn("Node '/binman/vblock': Cannot find entry for node " + "'other'", str(e.exception)) + + def testTpl(self): + """Test that an image with TPL and ots device tree can be created""" + # ELF file with a '__bss_size' symbol + with open(self.TestFile('bss_data')) as fd: + TestFunctional._MakeInputFile('tpl/u-boot-tpl', fd.read()) + data = self._DoReadFile('078_u_boot_tpl.dts') + self.assertEqual(U_BOOT_TPL_DATA + U_BOOT_TPL_DTB_DATA, data) + + def testUsesPos(self): + """Test that the 'pos' property cannot be used anymore""" + with self.assertRaises(ValueError) as e: + data = self._DoReadFile('079_uses_pos.dts') + self.assertIn("Node '/binman/u-boot': Please use 'offset' instead of " + "'pos'", str(e.exception)) + + def testFillZero(self): + """Test for an fill entry type with a size of 0""" + data = self._DoReadFile('080_fill_empty.dts') + self.assertEqual(chr(0) * 16, data) + + def testTextMissing(self): + """Test for a text entry type where there is no text""" + with self.assertRaises(ValueError) as e: + self._DoReadFileDtb('066_text.dts',) + self.assertIn("Node '/binman/text': No value provided for text label " + "'test-id'", str(e.exception)) + + def testPackStart16Tpl(self): + """Test that an image with an x86 start16 TPL region can be created""" + data = self._DoReadFile('081_x86-start16-tpl.dts') + self.assertEqual(X86_START16_TPL_DATA, data[:len(X86_START16_TPL_DATA)]) + + def testSelectImage(self): + """Test that we can select which images to build""" + with test_util.capture_sys_output() as (stdout, stderr): + retcode = self._DoTestFile('006_dual_image.dts', images=['image2']) + self.assertEqual(0, retcode) + self.assertIn('Skipping images: image1', stdout.getvalue()) + + self.assertFalse(os.path.exists(tools.GetOutputFilename('image1.bin'))) + self.assertTrue(os.path.exists(tools.GetOutputFilename('image2.bin'))) + + def testUpdateFdtAll(self): + """Test that all device trees are updated with offset/size info""" + data, _, _, _ = self._DoReadFileDtb('082_fdt_update_all.dts', + use_real_dtb=True, update_dtb=True) + + base_expected = { + 'section:image-pos': 0, + 'u-boot-tpl-dtb:size': 513, + 'u-boot-spl-dtb:size': 513, + 'u-boot-spl-dtb:offset': 493, + 'image-pos': 0, + 'section/u-boot-dtb:image-pos': 0, + 'u-boot-spl-dtb:image-pos': 493, + 'section/u-boot-dtb:size': 493, + 'u-boot-tpl-dtb:image-pos': 1006, + 'section/u-boot-dtb:offset': 0, + 'section:size': 493, + 'offset': 0, + 'section:offset': 0, + 'u-boot-tpl-dtb:offset': 1006, + 'size': 1519 + } + + # We expect three device-tree files in the output, one after the other. + # Read them in sequence. We look for an 'spl' property in the SPL tree, + # and 'tpl' in the TPL tree, to make sure they are distinct from the + # main U-Boot tree. All three should have the same postions and offset. + start = 0 + for item in ['', 'spl', 'tpl']: + dtb = fdt.Fdt.FromData(data[start:]) + dtb.Scan() + props = self._GetPropTree(dtb, ['offset', 'size', 'image-pos', + 'spl', 'tpl']) + expected = dict(base_expected) + if item: + expected[item] = 0 + self.assertEqual(expected, props) + start += dtb._fdt_obj.totalsize() + + def testUpdateFdtOutput(self): + """Test that output DTB files are updated""" + try: + data, dtb_data, _, _ = self._DoReadFileDtb('082_fdt_update_all.dts', + use_real_dtb=True, update_dtb=True, reset_dtbs=False) + + # Unfortunately, compiling a source file always results in a file + # called source.dtb (see fdt_util.EnsureCompiled()). The test + # source file (e.g. test/075_fdt_update_all.dts) thus does not enter + # binman as a file called u-boot.dtb. To fix this, copy the file + # over to the expected place. + #tools.WriteFile(os.path.join(self._indir, 'u-boot.dtb'), + #tools.ReadFile(tools.GetOutputFilename('source.dtb'))) + start = 0 + for fname in ['u-boot.dtb.out', 'spl/u-boot-spl.dtb.out', + 'tpl/u-boot-tpl.dtb.out']: + dtb = fdt.Fdt.FromData(data[start:]) + size = dtb._fdt_obj.totalsize() + pathname = tools.GetOutputFilename(os.path.split(fname)[1]) + outdata = tools.ReadFile(pathname) + name = os.path.split(fname)[0] + + if name: + orig_indata = self._GetDtbContentsForSplTpl(dtb_data, name) + else: + orig_indata = dtb_data + self.assertNotEqual(outdata, orig_indata, + "Expected output file '%s' be updated" % pathname) + self.assertEqual(outdata, data[start:start + size], + "Expected output file '%s' to match output image" % + pathname) + start += size + finally: + self._ResetDtbs() + + def _decompress(self, data): + out = os.path.join(self._indir, 'lz4.tmp') + with open(out, 'wb') as fd: + fd.write(data) + return tools.Run('lz4', '-dc', out) + ''' + try: + orig = lz4.frame.decompress(data) + except AttributeError: + orig = lz4.decompress(data) + ''' + + def testCompress(self): + """Test compression of blobs""" + data, _, _, out_dtb_fname = self._DoReadFileDtb('083_compress.dts', + use_real_dtb=True, update_dtb=True) + dtb = fdt.Fdt(out_dtb_fname) + dtb.Scan() + props = self._GetPropTree(dtb, ['size', 'uncomp-size']) + orig = self._decompress(data) + self.assertEquals(COMPRESS_DATA, orig) + expected = { + 'blob:uncomp-size': len(COMPRESS_DATA), + 'blob:size': len(data), + 'size': len(data), + } + self.assertEqual(expected, props) + + def testFiles(self): + """Test bringing in multiple files""" + data = self._DoReadFile('084_files.dts') + self.assertEqual(FILES_DATA, data) + + def testFilesCompress(self): + """Test bringing in multiple files and compressing them""" + data = self._DoReadFile('085_files_compress.dts') + + image = control.images['image'] + entries = image.GetEntries() + files = entries['files'] + entries = files._section._entries + + orig = '' + for i in range(1, 3): + key = '%d.dat' % i + start = entries[key].image_pos + len = entries[key].size + chunk = data[start:start + len] + orig += self._decompress(chunk) + + self.assertEqual(FILES_DATA, orig) + + def testFilesMissing(self): + """Test missing files""" + with self.assertRaises(ValueError) as e: + data = self._DoReadFile('086_files_none.dts') + self.assertIn("Node '/binman/files': Pattern \'files/*.none\' matched " + 'no files', str(e.exception)) + + def testFilesNoPattern(self): + """Test missing files""" + with self.assertRaises(ValueError) as e: + data = self._DoReadFile('087_files_no_pattern.dts') + self.assertIn("Node '/binman/files': Missing 'pattern' property", + str(e.exception)) + + def testExpandSize(self): + """Test an expanding entry""" + data, _, map_data, _ = self._DoReadFileDtb('088_expand_size.dts', + map=True) + expect = ('a' * 8 + U_BOOT_DATA + + MRC_DATA + 'b' * 1 + U_BOOT_DATA + + 'c' * 8 + U_BOOT_DATA + + 'd' * 8) + self.assertEqual(expect, data) + self.assertEqual('''ImagePos Offset Size Name +00000000 00000000 00000028 main-section +00000000 00000000 00000008 fill +00000008 00000008 00000004 u-boot +0000000c 0000000c 00000004 section +0000000c 00000000 00000003 intel-mrc +00000010 00000010 00000004 u-boot2 +00000014 00000014 0000000c section2 +00000014 00000000 00000008 fill +0000001c 00000008 00000004 u-boot +00000020 00000020 00000008 fill2 +''', map_data) + + def testExpandSizeBad(self): + """Test an expanding entry which fails to provide contents""" + with test_util.capture_sys_output() as (stdout, stderr): + with self.assertRaises(ValueError) as e: + self._DoReadFileDtb('089_expand_size_bad.dts', map=True) + self.assertIn("Node '/binman/_testing': Cannot obtain contents when " + 'expanding entry', str(e.exception)) + + def testHash(self): + """Test hashing of the contents of an entry""" + _, _, _, out_dtb_fname = self._DoReadFileDtb('090_hash.dts', + use_real_dtb=True, update_dtb=True) + dtb = fdt.Fdt(out_dtb_fname) + dtb.Scan() + hash_node = dtb.GetNode('/binman/u-boot/hash').props['value'] + m = hashlib.sha256() + m.update(U_BOOT_DATA) + self.assertEqual(m.digest(), ''.join(hash_node.value)) + + def testHashNoAlgo(self): + with self.assertRaises(ValueError) as e: + self._DoReadFileDtb('091_hash_no_algo.dts', update_dtb=True) + self.assertIn("Node \'/binman/u-boot\': Missing \'algo\' property for " + 'hash node', str(e.exception)) + + def testHashBadAlgo(self): + with self.assertRaises(ValueError) as e: + self._DoReadFileDtb('092_hash_bad_algo.dts', update_dtb=True) + self.assertIn("Node '/binman/u-boot': Unknown hash algorithm", + str(e.exception)) + + def testHashSection(self): + """Test hashing of the contents of an entry""" + _, _, _, out_dtb_fname = self._DoReadFileDtb('099_hash_section.dts', + use_real_dtb=True, update_dtb=True) + dtb = fdt.Fdt(out_dtb_fname) + dtb.Scan() + hash_node = dtb.GetNode('/binman/section/hash').props['value'] + m = hashlib.sha256() + m.update(U_BOOT_DATA) + m.update(16 * 'a') + self.assertEqual(m.digest(), ''.join(hash_node.value)) + + def testPackUBootTplMicrocode(self): + """Test that x86 microcode can be handled correctly in TPL + + We expect to see the following in the image, in order: + u-boot-tpl-nodtb.bin with a microcode pointer inserted at the correct + place + u-boot-tpl.dtb with the microcode removed + the microcode + """ + with open(self.TestFile('u_boot_ucode_ptr')) as fd: + TestFunctional._MakeInputFile('tpl/u-boot-tpl', fd.read()) + first, pos_and_size = self._RunMicrocodeTest('093_x86_tpl_ucode.dts', + U_BOOT_TPL_NODTB_DATA) + self.assertEqual('tplnodtb with microc' + pos_and_size + + 'ter somewhere in here', first) + + def testFmapX86(self): + """Basic test of generation of a flashrom fmap""" + data = self._DoReadFile('094_fmap_x86.dts') + fhdr, fentries = fmap_util.DecodeFmap(data[32:]) + expected = U_BOOT_DATA + MRC_DATA + 'a' * (32 - 7) + self.assertEqual(expected, data[:32]) + fhdr, fentries = fmap_util.DecodeFmap(data[32:]) + + self.assertEqual(0x100, fhdr.image_size) + + self.assertEqual(0, fentries[0].offset) + self.assertEqual(4, fentries[0].size) + self.assertEqual('U_BOOT', fentries[0].name) + + self.assertEqual(4, fentries[1].offset) + self.assertEqual(3, fentries[1].size) + self.assertEqual('INTEL_MRC', fentries[1].name) + + self.assertEqual(32, fentries[2].offset) + self.assertEqual(fmap_util.FMAP_HEADER_LEN + + fmap_util.FMAP_AREA_LEN * 3, fentries[2].size) + self.assertEqual('FMAP', fentries[2].name) + + def testFmapX86Section(self): + """Basic test of generation of a flashrom fmap""" + data = self._DoReadFile('095_fmap_x86_section.dts') + expected = U_BOOT_DATA + MRC_DATA + 'b' * (32 - 7) + self.assertEqual(expected, data[:32]) + fhdr, fentries = fmap_util.DecodeFmap(data[36:]) + + self.assertEqual(0x100, fhdr.image_size) + + self.assertEqual(0, fentries[0].offset) + self.assertEqual(4, fentries[0].size) + self.assertEqual('U_BOOT', fentries[0].name) + + self.assertEqual(4, fentries[1].offset) + self.assertEqual(3, fentries[1].size) + self.assertEqual('INTEL_MRC', fentries[1].name) + + self.assertEqual(36, fentries[2].offset) + self.assertEqual(fmap_util.FMAP_HEADER_LEN + + fmap_util.FMAP_AREA_LEN * 3, fentries[2].size) + self.assertEqual('FMAP', fentries[2].name) + + def testElf(self): + """Basic test of ELF entries""" + self._SetupSplElf() + with open(self.TestFile('bss_data')) as fd: + TestFunctional._MakeInputFile('-boot', fd.read()) + data = self._DoReadFile('096_elf.dts') + + def testElfStripg(self): + """Basic test of ELF entries""" + self._SetupSplElf() + with open(self.TestFile('bss_data')) as fd: + TestFunctional._MakeInputFile('-boot', fd.read()) + data = self._DoReadFile('097_elf_strip.dts') + + def testPackOverlapMap(self): + """Test that overlapping regions are detected""" + with test_util.capture_sys_output() as (stdout, stderr): + with self.assertRaises(ValueError) as e: + self._DoTestFile('014_pack_overlap.dts', map=True) + map_fname = tools.GetOutputFilename('image.map') + self.assertEqual("Wrote map file '%s' to show errors\n" % map_fname, + stdout.getvalue()) + + # We should not get an inmage, but there should be a map file + self.assertFalse(os.path.exists(tools.GetOutputFilename('image.bin'))) + self.assertTrue(os.path.exists(map_fname)) + map_data = tools.ReadFile(map_fname) + self.assertEqual('''ImagePos Offset Size Name +<none> 00000000 00000007 main-section +<none> 00000000 00000004 u-boot +<none> 00000003 00000004 u-boot-align +''', map_data) + + def testPacRefCode(self): + """Test that an image with an Intel Reference code binary works""" + data = self._DoReadFile('100_intel_refcode.dts') + self.assertEqual(REFCODE_DATA, data[:len(REFCODE_DATA)]) + + +if __name__ == "__main__": + unittest.main() diff --git a/tools/u-boot-tools/binman/image.py b/tools/u-boot-tools/binman/image.py new file mode 100644 index 0000000..f237ae3 --- /dev/null +++ b/tools/u-boot-tools/binman/image.py @@ -0,0 +1,153 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (c) 2016 Google, Inc +# Written by Simon Glass <sjg@chromium.org> +# +# Class for an image, the output of binman +# + +from __future__ import print_function + +from collections import OrderedDict +from operator import attrgetter +import re +import sys + +import fdt_util +import bsection +import tools + +class Image: + """A Image, representing an output from binman + + An image is comprised of a collection of entries each containing binary + data. The image size must be large enough to hold all of this data. + + This class implements the various operations needed for images. + + Atrtributes: + _node: Node object that contains the image definition in device tree + _name: Image name + _size: Image size in bytes, or None if not known yet + _filename: Output filename for image + _sections: Sections present in this image (may be one or more) + + Args: + test: True if this is being called from a test of Images. This this case + there is no device tree defining the structure of the section, so + we create a section manually. + """ + def __init__(self, name, node, test=False): + self._node = node + self._name = name + self._size = None + self._filename = '%s.bin' % self._name + if test: + self._section = bsection.Section('main-section', None, self._node, + self, True) + else: + self._ReadNode() + + def _ReadNode(self): + """Read properties from the image node""" + self._size = fdt_util.GetInt(self._node, 'size') + filename = fdt_util.GetString(self._node, 'filename') + if filename: + self._filename = filename + self._section = bsection.Section('main-section', None, self._node, self) + + def GetFdtSet(self): + """Get the set of device tree files used by this image""" + return self._section.GetFdtSet() + + def ExpandEntries(self): + """Expand out any entries which have calculated sub-entries + + Some entries are expanded out at runtime, e.g. 'files', which produces + a section containing a list of files. Process these entries so that + this information is added to the device tree. + """ + self._section.ExpandEntries() + + def AddMissingProperties(self): + """Add properties that are not present in the device tree + + When binman has completed packing the entries the offset and size of + each entry are known. But before this the device tree may not specify + these. Add any missing properties, with a dummy value, so that the + size of the entry is correct. That way we can insert the correct values + later. + """ + self._section.AddMissingProperties() + + def ProcessFdt(self, fdt): + """Allow entries to adjust the device tree + + Some entries need to adjust the device tree for their purposes. This + may involve adding or deleting properties. + """ + return self._section.ProcessFdt(fdt) + + def GetEntryContents(self): + """Call ObtainContents() for the section + """ + self._section.GetEntryContents() + + def GetEntryOffsets(self): + """Handle entries that want to set the offset/size of other entries + + This calls each entry's GetOffsets() method. If it returns a list + of entries to update, it updates them. + """ + self._section.GetEntryOffsets() + + def PackEntries(self): + """Pack all entries into the image""" + self._section.PackEntries() + + def CheckSize(self): + """Check that the image contents does not exceed its size, etc.""" + self._size = self._section.CheckSize() + + def CheckEntries(self): + """Check that entries do not overlap or extend outside the image""" + self._section.CheckEntries() + + def SetCalculatedProperties(self): + self._section.SetCalculatedProperties() + + def SetImagePos(self): + self._section.SetImagePos(0) + + def ProcessEntryContents(self): + """Call the ProcessContents() method for each entry + + This is intended to adjust the contents as needed by the entry type. + """ + self._section.ProcessEntryContents() + + def WriteSymbols(self): + """Write symbol values into binary files for access at run time""" + self._section.WriteSymbols() + + def BuildImage(self): + """Write the image to a file""" + fname = tools.GetOutputFilename(self._filename) + with open(fname, 'wb') as fd: + self._section.BuildSection(fd, 0) + + def GetEntries(self): + return self._section.GetEntries() + + def WriteMap(self): + """Write a map of the image to a .map file + + Returns: + Filename of map file written + """ + filename = '%s.map' % self._name + fname = tools.GetOutputFilename(filename) + with open(fname, 'w') as fd: + print('%8s %8s %8s %s' % ('ImagePos', 'Offset', 'Size', 'Name'), + file=fd) + self._section.WriteMap(fd, 0) + return fname diff --git a/tools/u-boot-tools/binman/image_test.py b/tools/u-boot-tools/binman/image_test.py new file mode 100644 index 0000000..3775e1a --- /dev/null +++ b/tools/u-boot-tools/binman/image_test.py @@ -0,0 +1,48 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (c) 2017 Google, Inc +# Written by Simon Glass <sjg@chromium.org> +# +# Test for the image module + +import unittest + +from image import Image +from test_util import capture_sys_output + +class TestImage(unittest.TestCase): + def testInvalidFormat(self): + image = Image('name', 'node', test=True) + section = image._section + with self.assertRaises(ValueError) as e: + section.LookupSymbol('_binman_something_prop_', False, 'msg') + self.assertIn( + "msg: Symbol '_binman_something_prop_' has invalid format", + str(e.exception)) + + def testMissingSymbol(self): + image = Image('name', 'node', test=True) + section = image._section + section._entries = {} + with self.assertRaises(ValueError) as e: + section.LookupSymbol('_binman_type_prop_pname', False, 'msg') + self.assertIn("msg: Entry 'type' not found in list ()", + str(e.exception)) + + def testMissingSymbolOptional(self): + image = Image('name', 'node', test=True) + section = image._section + section._entries = {} + with capture_sys_output() as (stdout, stderr): + val = section.LookupSymbol('_binman_type_prop_pname', True, 'msg') + self.assertEqual(val, None) + self.assertEqual("Warning: msg: Entry 'type' not found in list ()\n", + stderr.getvalue()) + self.assertEqual('', stdout.getvalue()) + + def testBadProperty(self): + image = Image('name', 'node', test=True) + section = image._section + section._entries = {'u-boot': 1} + with self.assertRaises(ValueError) as e: + section.LookupSymbol('_binman_u_boot_prop_bad', False, 'msg') + self.assertIn("msg: No such property 'bad", str(e.exception)) diff --git a/tools/u-boot-tools/binman/state.py b/tools/u-boot-tools/binman/state.py new file mode 100644 index 0000000..d945e4b --- /dev/null +++ b/tools/u-boot-tools/binman/state.py @@ -0,0 +1,253 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright 2018 Google, Inc +# Written by Simon Glass <sjg@chromium.org> +# +# Holds and modifies the state information held by binman +# + +import hashlib +import re +from sets import Set + +import os +import tools + +# Records the device-tree files known to binman, keyed by filename (e.g. +# 'u-boot-spl.dtb') +fdt_files = {} + +# Arguments passed to binman to provide arguments to entries +entry_args = {} + +# True to use fake device-tree files for testing (see U_BOOT_DTB_DATA in +# ftest.py) +use_fake_dtb = False + +# Set of all device tree files references by images +fdt_set = Set() + +# Same as above, but excluding the main one +fdt_subset = Set() + +# The DTB which contains the full image information +main_dtb = None + +def GetFdt(fname): + """Get the Fdt object for a particular device-tree filename + + Binman keeps track of at least one device-tree file called u-boot.dtb but + can also have others (e.g. for SPL). This function looks up the given + filename and returns the associated Fdt object. + + Args: + fname: Filename to look up (e.g. 'u-boot.dtb'). + + Returns: + Fdt object associated with the filename + """ + return fdt_files[fname] + +def GetFdtPath(fname): + """Get the full pathname of a particular Fdt object + + Similar to GetFdt() but returns the pathname associated with the Fdt. + + Args: + fname: Filename to look up (e.g. 'u-boot.dtb'). + + Returns: + Full path name to the associated Fdt + """ + return fdt_files[fname]._fname + +def GetFdtContents(fname): + """Looks up the FDT pathname and contents + + This is used to obtain the Fdt pathname and contents when needed by an + entry. It supports a 'fake' dtb, allowing tests to substitute test data for + the real dtb. + + Args: + fname: Filename to look up (e.g. 'u-boot.dtb'). + + Returns: + tuple: + pathname to Fdt + Fdt data (as bytes) + """ + if fname in fdt_files and not use_fake_dtb: + pathname = GetFdtPath(fname) + data = GetFdt(fname).GetContents() + else: + pathname = tools.GetInputFilename(fname) + data = tools.ReadFile(pathname) + return pathname, data + +def SetEntryArgs(args): + """Set the value of the entry args + + This sets up the entry_args dict which is used to supply entry arguments to + entries. + + Args: + args: List of entry arguments, each in the format "name=value" + """ + global entry_args + + entry_args = {} + if args: + for arg in args: + m = re.match('([^=]*)=(.*)', arg) + if not m: + raise ValueError("Invalid entry arguemnt '%s'" % arg) + entry_args[m.group(1)] = m.group(2) + +def GetEntryArg(name): + """Get the value of an entry argument + + Args: + name: Name of argument to retrieve + + Returns: + String value of argument + """ + return entry_args.get(name) + +def Prepare(images, dtb): + """Get device tree files ready for use + + This sets up a set of device tree files that can be retrieved by GetFdts(). + At present there is only one, that for U-Boot proper. + + Args: + images: List of images being used + dtb: Main dtb + """ + global fdt_set, fdt_subset, fdt_files, main_dtb + # Import these here in case libfdt.py is not available, in which case + # the above help option still works. + import fdt + import fdt_util + + # If we are updating the DTBs we need to put these updated versions + # where Entry_blob_dtb can find them. We can ignore 'u-boot.dtb' + # since it is assumed to be the one passed in with options.dt, and + # was handled just above. + main_dtb = dtb + fdt_files.clear() + fdt_files['u-boot.dtb'] = dtb + fdt_subset = Set() + if not use_fake_dtb: + for image in images.values(): + fdt_subset.update(image.GetFdtSet()) + fdt_subset.discard('u-boot.dtb') + for other_fname in fdt_subset: + infile = tools.GetInputFilename(other_fname) + other_fname_dtb = fdt_util.EnsureCompiled(infile) + out_fname = tools.GetOutputFilename('%s.out' % + os.path.split(other_fname)[1]) + tools.WriteFile(out_fname, tools.ReadFile(other_fname_dtb)) + other_dtb = fdt.FdtScan(out_fname) + fdt_files[other_fname] = other_dtb + +def GetFdts(): + """Yield all device tree files being used by binman + + Yields: + Device trees being used (U-Boot proper, SPL, TPL) + """ + yield main_dtb + for other_fname in fdt_subset: + yield fdt_files[other_fname] + +def GetUpdateNodes(node): + """Yield all the nodes that need to be updated in all device trees + + The property referenced by this node is added to any device trees which + have the given node. Due to removable of unwanted notes, SPL and TPL may + not have this node. + + Args: + node: Node object in the main device tree to look up + + Yields: + Node objects in each device tree that is in use (U-Boot proper, which + is node, SPL and TPL) + """ + yield node + for dtb in fdt_files.values(): + if dtb != node.GetFdt(): + other_node = dtb.GetNode(node.path) + if other_node: + yield other_node + +def AddZeroProp(node, prop): + """Add a new property to affected device trees with an integer value of 0. + + Args: + prop_name: Name of property + """ + for n in GetUpdateNodes(node): + n.AddZeroProp(prop) + +def AddSubnode(node, name): + """Add a new subnode to a node in affected device trees + + Args: + node: Node to add to + name: name of node to add + + Returns: + New subnode that was created in main tree + """ + first = None + for n in GetUpdateNodes(node): + subnode = n.AddSubnode(name) + if not first: + first = subnode + return first + +def AddString(node, prop, value): + """Add a new string property to affected device trees + + Args: + prop_name: Name of property + value: String value (which will be \0-terminated in the DT) + """ + for n in GetUpdateNodes(node): + n.AddString(prop, value) + +def SetInt(node, prop, value): + """Update an integer property in affected device trees with an integer value + + This is not allowed to change the size of the FDT. + + Args: + prop_name: Name of property + """ + for n in GetUpdateNodes(node): + n.SetInt(prop, value) + +def CheckAddHashProp(node): + hash_node = node.FindNode('hash') + if hash_node: + algo = hash_node.props.get('algo') + if not algo: + return "Missing 'algo' property for hash node" + if algo.value == 'sha256': + size = 32 + else: + return "Unknown hash algorithm '%s'" % algo + for n in GetUpdateNodes(hash_node): + n.AddEmptyProp('value', size) + +def CheckSetHashValue(node, get_data_func): + hash_node = node.FindNode('hash') + if hash_node: + algo = hash_node.props.get('algo').value + if algo == 'sha256': + m = hashlib.sha256() + m.update(get_data_func()) + data = m.digest() + for n in GetUpdateNodes(hash_node): + n.SetData('value', data) diff --git a/tools/u-boot-tools/binman/test/001_invalid.dts b/tools/u-boot-tools/binman/test/001_invalid.dts new file mode 100644 index 0000000..7d00455 --- /dev/null +++ b/tools/u-boot-tools/binman/test/001_invalid.dts @@ -0,0 +1,5 @@ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; diff --git a/tools/u-boot-tools/binman/test/002_missing_node.dts b/tools/u-boot-tools/binman/test/002_missing_node.dts new file mode 100644 index 0000000..3a51ec2 --- /dev/null +++ b/tools/u-boot-tools/binman/test/002_missing_node.dts @@ -0,0 +1,6 @@ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; +}; diff --git a/tools/u-boot-tools/binman/test/003_empty.dts b/tools/u-boot-tools/binman/test/003_empty.dts new file mode 100644 index 0000000..493c9a0 --- /dev/null +++ b/tools/u-boot-tools/binman/test/003_empty.dts @@ -0,0 +1,9 @@ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + }; +}; diff --git a/tools/u-boot-tools/binman/test/004_invalid_entry.dts b/tools/u-boot-tools/binman/test/004_invalid_entry.dts new file mode 100644 index 0000000..b043455 --- /dev/null +++ b/tools/u-boot-tools/binman/test/004_invalid_entry.dts @@ -0,0 +1,11 @@ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + not-a-valid-type { + }; + }; +}; diff --git a/tools/u-boot-tools/binman/test/005_simple.dts b/tools/u-boot-tools/binman/test/005_simple.dts new file mode 100644 index 0000000..3771aa2 --- /dev/null +++ b/tools/u-boot-tools/binman/test/005_simple.dts @@ -0,0 +1,11 @@ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + u-boot { + }; + }; +}; diff --git a/tools/u-boot-tools/binman/test/006_dual_image.dts b/tools/u-boot-tools/binman/test/006_dual_image.dts new file mode 100644 index 0000000..78be16f --- /dev/null +++ b/tools/u-boot-tools/binman/test/006_dual_image.dts @@ -0,0 +1,22 @@ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + multiple-images; + image1 { + u-boot { + }; + }; + + image2 { + pad-before = <3>; + pad-after = <5>; + + u-boot { + }; + }; + }; +}; diff --git a/tools/u-boot-tools/binman/test/007_bad_align.dts b/tools/u-boot-tools/binman/test/007_bad_align.dts new file mode 100644 index 0000000..123bb13 --- /dev/null +++ b/tools/u-boot-tools/binman/test/007_bad_align.dts @@ -0,0 +1,12 @@ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + u-boot { + align = <23>; + }; + }; +}; diff --git a/tools/u-boot-tools/binman/test/008_pack.dts b/tools/u-boot-tools/binman/test/008_pack.dts new file mode 100644 index 0000000..a88785d --- /dev/null +++ b/tools/u-boot-tools/binman/test/008_pack.dts @@ -0,0 +1,30 @@ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + u-boot { + }; + + u-boot-align { + type = "u-boot"; + align = <16>; + }; + + u-boot-size { + type = "u-boot"; + size = <23>; + }; + + u-boot-next { + type = "u-boot"; + }; + + u-boot-fixed { + type = "u-boot"; + offset = <61>; + }; + }; +}; diff --git a/tools/u-boot-tools/binman/test/009_pack_extra.dts b/tools/u-boot-tools/binman/test/009_pack_extra.dts new file mode 100644 index 0000000..0765707 --- /dev/null +++ b/tools/u-boot-tools/binman/test/009_pack_extra.dts @@ -0,0 +1,35 @@ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + u-boot { + pad-before = <3>; + pad-after = <5>; + }; + + u-boot-align-size-nop { + type = "u-boot"; + align-size = <4>; + }; + + u-boot-align-size { + type = "u-boot"; + align = <16>; + align-size = <32>; + }; + + u-boot-align-end { + type = "u-boot"; + align-end = <64>; + }; + + u-boot-align-both { + type = "u-boot"; + align= <64>; + align-end = <128>; + }; + }; +}; diff --git a/tools/u-boot-tools/binman/test/010_pack_align_power2.dts b/tools/u-boot-tools/binman/test/010_pack_align_power2.dts new file mode 100644 index 0000000..8f6253a --- /dev/null +++ b/tools/u-boot-tools/binman/test/010_pack_align_power2.dts @@ -0,0 +1,12 @@ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + u-boot { + align = <5>; + }; + }; +}; diff --git a/tools/u-boot-tools/binman/test/011_pack_align_size_power2.dts b/tools/u-boot-tools/binman/test/011_pack_align_size_power2.dts new file mode 100644 index 0000000..04f7672 --- /dev/null +++ b/tools/u-boot-tools/binman/test/011_pack_align_size_power2.dts @@ -0,0 +1,12 @@ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + u-boot { + align-size = <55>; + }; + }; +}; diff --git a/tools/u-boot-tools/binman/test/012_pack_inv_align.dts b/tools/u-boot-tools/binman/test/012_pack_inv_align.dts new file mode 100644 index 0000000..d8dd600 --- /dev/null +++ b/tools/u-boot-tools/binman/test/012_pack_inv_align.dts @@ -0,0 +1,13 @@ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + u-boot { + offset = <5>; + align = <4>; + }; + }; +}; diff --git a/tools/u-boot-tools/binman/test/013_pack_inv_size_align.dts b/tools/u-boot-tools/binman/test/013_pack_inv_size_align.dts new file mode 100644 index 0000000..dfafa13 --- /dev/null +++ b/tools/u-boot-tools/binman/test/013_pack_inv_size_align.dts @@ -0,0 +1,13 @@ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + u-boot { + size = <5>; + align-size = <4>; + }; + }; +}; diff --git a/tools/u-boot-tools/binman/test/014_pack_overlap.dts b/tools/u-boot-tools/binman/test/014_pack_overlap.dts new file mode 100644 index 0000000..3895cba --- /dev/null +++ b/tools/u-boot-tools/binman/test/014_pack_overlap.dts @@ -0,0 +1,16 @@ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + u-boot { + }; + + u-boot-align { + type = "u-boot"; + offset = <3>; + }; + }; +}; diff --git a/tools/u-boot-tools/binman/test/015_pack_overflow.dts b/tools/u-boot-tools/binman/test/015_pack_overflow.dts new file mode 100644 index 0000000..6f65433 --- /dev/null +++ b/tools/u-boot-tools/binman/test/015_pack_overflow.dts @@ -0,0 +1,12 @@ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + u-boot { + size = <3>; + }; + }; +}; diff --git a/tools/u-boot-tools/binman/test/016_pack_image_overflow.dts b/tools/u-boot-tools/binman/test/016_pack_image_overflow.dts new file mode 100644 index 0000000..6ae66f3 --- /dev/null +++ b/tools/u-boot-tools/binman/test/016_pack_image_overflow.dts @@ -0,0 +1,13 @@ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + size = <3>; + + u-boot { + }; + }; +}; diff --git a/tools/u-boot-tools/binman/test/017_pack_image_size.dts b/tools/u-boot-tools/binman/test/017_pack_image_size.dts new file mode 100644 index 0000000..2360eb5 --- /dev/null +++ b/tools/u-boot-tools/binman/test/017_pack_image_size.dts @@ -0,0 +1,13 @@ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + size = <7>; + + u-boot { + }; + }; +}; diff --git a/tools/u-boot-tools/binman/test/018_pack_image_align.dts b/tools/u-boot-tools/binman/test/018_pack_image_align.dts new file mode 100644 index 0000000..16cd2a4 --- /dev/null +++ b/tools/u-boot-tools/binman/test/018_pack_image_align.dts @@ -0,0 +1,13 @@ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + align-size = <16>; + + u-boot { + }; + }; +}; diff --git a/tools/u-boot-tools/binman/test/019_pack_inv_image_align.dts b/tools/u-boot-tools/binman/test/019_pack_inv_image_align.dts new file mode 100644 index 0000000..e5ee87b --- /dev/null +++ b/tools/u-boot-tools/binman/test/019_pack_inv_image_align.dts @@ -0,0 +1,14 @@ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + size = <7>; + align-size = <8>; + + u-boot { + }; + }; +}; diff --git a/tools/u-boot-tools/binman/test/020_pack_inv_image_align_power2.dts b/tools/u-boot-tools/binman/test/020_pack_inv_image_align_power2.dts new file mode 100644 index 0000000..a428c4b --- /dev/null +++ b/tools/u-boot-tools/binman/test/020_pack_inv_image_align_power2.dts @@ -0,0 +1,13 @@ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + align-size = <131>; + + u-boot { + }; + }; +}; diff --git a/tools/u-boot-tools/binman/test/021_image_pad.dts b/tools/u-boot-tools/binman/test/021_image_pad.dts new file mode 100644 index 0000000..c651668 --- /dev/null +++ b/tools/u-boot-tools/binman/test/021_image_pad.dts @@ -0,0 +1,16 @@ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + pad-byte = <0xff>; + u-boot-spl { + }; + + u-boot { + offset = <20>; + }; + }; +}; diff --git a/tools/u-boot-tools/binman/test/022_image_name.dts b/tools/u-boot-tools/binman/test/022_image_name.dts new file mode 100644 index 0000000..94fc069 --- /dev/null +++ b/tools/u-boot-tools/binman/test/022_image_name.dts @@ -0,0 +1,21 @@ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + multiple-images; + image1 { + filename = "test-name"; + u-boot { + }; + }; + + image2 { + filename = "test-name.xx"; + u-boot { + }; + }; + }; +}; diff --git a/tools/u-boot-tools/binman/test/023_blob.dts b/tools/u-boot-tools/binman/test/023_blob.dts new file mode 100644 index 0000000..7dcff69 --- /dev/null +++ b/tools/u-boot-tools/binman/test/023_blob.dts @@ -0,0 +1,12 @@ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + blob { + filename = "blobfile"; + }; + }; +}; diff --git a/tools/u-boot-tools/binman/test/024_sorted.dts b/tools/u-boot-tools/binman/test/024_sorted.dts new file mode 100644 index 0000000..d35d39f --- /dev/null +++ b/tools/u-boot-tools/binman/test/024_sorted.dts @@ -0,0 +1,17 @@ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + sort-by-offset; + u-boot { + offset = <22>; + }; + + u-boot-spl { + offset = <1>; + }; + }; +}; diff --git a/tools/u-boot-tools/binman/test/025_pack_zero_size.dts b/tools/u-boot-tools/binman/test/025_pack_zero_size.dts new file mode 100644 index 0000000..e863c44 --- /dev/null +++ b/tools/u-boot-tools/binman/test/025_pack_zero_size.dts @@ -0,0 +1,15 @@ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + u-boot { + }; + + u-boot-spl { + offset = <0>; + }; + }; +}; diff --git a/tools/u-boot-tools/binman/test/026_pack_u_boot_dtb.dts b/tools/u-boot-tools/binman/test/026_pack_u_boot_dtb.dts new file mode 100644 index 0000000..2707a73 --- /dev/null +++ b/tools/u-boot-tools/binman/test/026_pack_u_boot_dtb.dts @@ -0,0 +1,14 @@ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + u-boot-nodtb { + }; + + u-boot-dtb { + }; + }; +}; diff --git a/tools/u-boot-tools/binman/test/027_pack_4gb_no_size.dts b/tools/u-boot-tools/binman/test/027_pack_4gb_no_size.dts new file mode 100644 index 0000000..371cca1 --- /dev/null +++ b/tools/u-boot-tools/binman/test/027_pack_4gb_no_size.dts @@ -0,0 +1,18 @@ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + sort-by-offset; + end-at-4gb; + u-boot { + offset = <0xfffffff0>; + }; + + u-boot-spl { + offset = <0xfffffff7>; + }; + }; +}; diff --git a/tools/u-boot-tools/binman/test/028_pack_4gb_outside.dts b/tools/u-boot-tools/binman/test/028_pack_4gb_outside.dts new file mode 100644 index 0000000..2216abf --- /dev/null +++ b/tools/u-boot-tools/binman/test/028_pack_4gb_outside.dts @@ -0,0 +1,19 @@ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + sort-by-offset; + end-at-4gb; + size = <32>; + u-boot { + offset = <0>; + }; + + u-boot-spl { + offset = <0xffffffeb>; + }; + }; +}; diff --git a/tools/u-boot-tools/binman/test/029_x86-rom.dts b/tools/u-boot-tools/binman/test/029_x86-rom.dts new file mode 100644 index 0000000..d5c69f9 --- /dev/null +++ b/tools/u-boot-tools/binman/test/029_x86-rom.dts @@ -0,0 +1,19 @@ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + sort-by-offset; + end-at-4gb; + size = <32>; + u-boot { + offset = <0xffffffe0>; + }; + + u-boot-spl { + offset = <0xffffffeb>; + }; + }; +}; diff --git a/tools/u-boot-tools/binman/test/030_x86-rom-me-no-desc.dts b/tools/u-boot-tools/binman/test/030_x86-rom-me-no-desc.dts new file mode 100644 index 0000000..796cb87 --- /dev/null +++ b/tools/u-boot-tools/binman/test/030_x86-rom-me-no-desc.dts @@ -0,0 +1,16 @@ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + sort-by-offset; + end-at-4gb; + size = <16>; + intel-me { + filename = "me.bin"; + offset-unset; + }; + }; +}; diff --git a/tools/u-boot-tools/binman/test/031_x86-rom-me.dts b/tools/u-boot-tools/binman/test/031_x86-rom-me.dts new file mode 100644 index 0000000..b8b0a5a --- /dev/null +++ b/tools/u-boot-tools/binman/test/031_x86-rom-me.dts @@ -0,0 +1,20 @@ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + sort-by-offset; + end-at-4gb; + size = <0x800000>; + intel-descriptor { + filename = "descriptor.bin"; + }; + + intel-me { + filename = "me.bin"; + offset-unset; + }; + }; +}; diff --git a/tools/u-boot-tools/binman/test/032_intel-vga.dts b/tools/u-boot-tools/binman/test/032_intel-vga.dts new file mode 100644 index 0000000..9c532d0 --- /dev/null +++ b/tools/u-boot-tools/binman/test/032_intel-vga.dts @@ -0,0 +1,14 @@ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + size = <16>; + + intel-vga { + filename = "vga.bin"; + }; + }; +}; diff --git a/tools/u-boot-tools/binman/test/033_x86-start16.dts b/tools/u-boot-tools/binman/test/033_x86-start16.dts new file mode 100644 index 0000000..2e279de --- /dev/null +++ b/tools/u-boot-tools/binman/test/033_x86-start16.dts @@ -0,0 +1,13 @@ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + size = <16>; + + x86-start16 { + }; + }; +}; diff --git a/tools/u-boot-tools/binman/test/034_x86_ucode.dts b/tools/u-boot-tools/binman/test/034_x86_ucode.dts new file mode 100644 index 0000000..4072573 --- /dev/null +++ b/tools/u-boot-tools/binman/test/034_x86_ucode.dts @@ -0,0 +1,29 @@ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + sort-by-offset; + end-at-4gb; + size = <0x200>; + u-boot-with-ucode-ptr { + }; + + u-boot-dtb-with-ucode { + }; + + u-boot-ucode { + }; + }; + + microcode { + update@0 { + data = <0x12345678 0x12345679>; + }; + update@1 { + data = <0xabcd0000 0x78235609>; + }; + }; +}; diff --git a/tools/u-boot-tools/binman/test/035_x86_single_ucode.dts b/tools/u-boot-tools/binman/test/035_x86_single_ucode.dts new file mode 100644 index 0000000..2b1f086 --- /dev/null +++ b/tools/u-boot-tools/binman/test/035_x86_single_ucode.dts @@ -0,0 +1,26 @@ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + sort-by-offset; + end-at-4gb; + size = <0x200>; + u-boot-with-ucode-ptr { + }; + + u-boot-dtb-with-ucode { + }; + + u-boot-ucode { + }; + }; + + microcode { + update@0 { + data = <0x12345678 0x12345679>; + }; + }; +}; diff --git a/tools/u-boot-tools/binman/test/036_u_boot_img.dts b/tools/u-boot-tools/binman/test/036_u_boot_img.dts new file mode 100644 index 0000000..aa5a3fe --- /dev/null +++ b/tools/u-boot-tools/binman/test/036_u_boot_img.dts @@ -0,0 +1,11 @@ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + u-boot-img { + }; + }; +}; diff --git a/tools/u-boot-tools/binman/test/037_x86_no_ucode.dts b/tools/u-boot-tools/binman/test/037_x86_no_ucode.dts new file mode 100644 index 0000000..6da49c3 --- /dev/null +++ b/tools/u-boot-tools/binman/test/037_x86_no_ucode.dts @@ -0,0 +1,20 @@ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + sort-by-offset; + end-at-4gb; + size = <0x200>; + u-boot-with-ucode-ptr { + }; + + u-boot-dtb-with-ucode { + }; + + u-boot-ucode { + }; + }; +}; diff --git a/tools/u-boot-tools/binman/test/038_x86_ucode_missing_node.dts b/tools/u-boot-tools/binman/test/038_x86_ucode_missing_node.dts new file mode 100644 index 0000000..720677c --- /dev/null +++ b/tools/u-boot-tools/binman/test/038_x86_ucode_missing_node.dts @@ -0,0 +1,26 @@ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + sort-by-offset; + end-at-4gb; + size = <0x200>; + u-boot-with-ucode-ptr { + }; + + u-boot-ucode { + }; + }; + + microcode { + update@0 { + data = <0x12345678 0x12345679>; + }; + update@1 { + data = <0xabcd0000 0x78235609>; + }; + }; +}; diff --git a/tools/u-boot-tools/binman/test/039_x86_ucode_missing_node2.dts b/tools/u-boot-tools/binman/test/039_x86_ucode_missing_node2.dts new file mode 100644 index 0000000..10ac086 --- /dev/null +++ b/tools/u-boot-tools/binman/test/039_x86_ucode_missing_node2.dts @@ -0,0 +1,23 @@ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + sort-by-offset; + end-at-4gb; + size = <0x200>; + u-boot-with-ucode-ptr { + }; + }; + + microcode { + update@0 { + data = <0x12345678 0x12345679>; + }; + update@1 { + data = <0xabcd0000 0x78235609>; + }; + }; +}; diff --git a/tools/u-boot-tools/binman/test/040_x86_ucode_not_in_image.dts b/tools/u-boot-tools/binman/test/040_x86_ucode_not_in_image.dts new file mode 100644 index 0000000..6097258 --- /dev/null +++ b/tools/u-boot-tools/binman/test/040_x86_ucode_not_in_image.dts @@ -0,0 +1,28 @@ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + sort-by-offset; + size = <0x200>; + u-boot-with-ucode-ptr { + }; + + u-boot-dtb-with-ucode { + }; + + u-boot-ucode { + }; + }; + + microcode { + update@0 { + data = <0x12345678 0x12345679>; + }; + update@1 { + data = <0xabcd0000 0x78235609>; + }; + }; +}; diff --git a/tools/u-boot-tools/binman/test/041_unknown_pos_size.dts b/tools/u-boot-tools/binman/test/041_unknown_pos_size.dts new file mode 100644 index 0000000..94fe821 --- /dev/null +++ b/tools/u-boot-tools/binman/test/041_unknown_pos_size.dts @@ -0,0 +1,12 @@ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + _testing { + return-invalid-entry; + }; + }; +}; diff --git a/tools/u-boot-tools/binman/test/042_intel-fsp.dts b/tools/u-boot-tools/binman/test/042_intel-fsp.dts new file mode 100644 index 0000000..8a7c889 --- /dev/null +++ b/tools/u-boot-tools/binman/test/042_intel-fsp.dts @@ -0,0 +1,14 @@ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + size = <16>; + + intel-fsp { + filename = "fsp.bin"; + }; + }; +}; diff --git a/tools/u-boot-tools/binman/test/043_intel-cmc.dts b/tools/u-boot-tools/binman/test/043_intel-cmc.dts new file mode 100644 index 0000000..5a56c7d --- /dev/null +++ b/tools/u-boot-tools/binman/test/043_intel-cmc.dts @@ -0,0 +1,14 @@ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + size = <16>; + + intel-cmc { + filename = "cmc.bin"; + }; + }; +}; diff --git a/tools/u-boot-tools/binman/test/044_x86_optional_ucode.dts b/tools/u-boot-tools/binman/test/044_x86_optional_ucode.dts new file mode 100644 index 0000000..24a7040 --- /dev/null +++ b/tools/u-boot-tools/binman/test/044_x86_optional_ucode.dts @@ -0,0 +1,30 @@ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + sort-by-offset; + end-at-4gb; + size = <0x200>; + u-boot-with-ucode-ptr { + optional-ucode; + }; + + u-boot-dtb-with-ucode { + }; + + u-boot-ucode { + }; + }; + + microcode { + update@0 { + data = <0x12345678 0x12345679>; + }; + update@1 { + data = <0xabcd0000 0x78235609>; + }; + }; +}; diff --git a/tools/u-boot-tools/binman/test/045_prop_test.dts b/tools/u-boot-tools/binman/test/045_prop_test.dts new file mode 100644 index 0000000..064de2b --- /dev/null +++ b/tools/u-boot-tools/binman/test/045_prop_test.dts @@ -0,0 +1,23 @@ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + sort-by-offset; + end-at-4gb; + size = <16>; + intel-me { + filename = "me.bin"; + offset-unset; + intval = <3>; + intarray = <5 6>; + byteval = [08]; + bytearray = [01 23 34]; + longbytearray = [09 0a 0b 0c]; + stringval = "message2"; + stringarray = "another", "multi-word", "message"; + }; + }; +}; diff --git a/tools/u-boot-tools/binman/test/046_intel-vbt.dts b/tools/u-boot-tools/binman/test/046_intel-vbt.dts new file mode 100644 index 0000000..733f575 --- /dev/null +++ b/tools/u-boot-tools/binman/test/046_intel-vbt.dts @@ -0,0 +1,14 @@ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + size = <16>; + + intel-vbt { + filename = "vbt.bin"; + }; + }; +}; diff --git a/tools/u-boot-tools/binman/test/047_spl_bss_pad.dts b/tools/u-boot-tools/binman/test/047_spl_bss_pad.dts new file mode 100644 index 0000000..6bd88b8 --- /dev/null +++ b/tools/u-boot-tools/binman/test/047_spl_bss_pad.dts @@ -0,0 +1,17 @@ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + u-boot-spl { + }; + + u-boot-spl-bss-pad { + }; + + u-boot { + }; + }; +}; diff --git a/tools/u-boot-tools/binman/test/048_x86-start16-spl.dts b/tools/u-boot-tools/binman/test/048_x86-start16-spl.dts new file mode 100644 index 0000000..e2009f1 --- /dev/null +++ b/tools/u-boot-tools/binman/test/048_x86-start16-spl.dts @@ -0,0 +1,13 @@ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + size = <16>; + + x86-start16-spl { + }; + }; +}; diff --git a/tools/u-boot-tools/binman/test/049_x86_ucode_spl.dts b/tools/u-boot-tools/binman/test/049_x86_ucode_spl.dts new file mode 100644 index 0000000..350d2c4 --- /dev/null +++ b/tools/u-boot-tools/binman/test/049_x86_ucode_spl.dts @@ -0,0 +1,29 @@ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + sort-by-offset; + end-at-4gb; + size = <0x200>; + u-boot-spl-with-ucode-ptr { + }; + + u-boot-dtb-with-ucode { + }; + + u-boot-ucode { + }; + }; + + microcode { + update@0 { + data = <0x12345678 0x12345679>; + }; + update@1 { + data = <0xabcd0000 0x78235609>; + }; + }; +}; diff --git a/tools/u-boot-tools/binman/test/050_intel_mrc.dts b/tools/u-boot-tools/binman/test/050_intel_mrc.dts new file mode 100644 index 0000000..54cd52a --- /dev/null +++ b/tools/u-boot-tools/binman/test/050_intel_mrc.dts @@ -0,0 +1,13 @@ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + size = <16>; + + intel-mrc { + }; + }; +}; diff --git a/tools/u-boot-tools/binman/test/051_u_boot_spl_dtb.dts b/tools/u-boot-tools/binman/test/051_u_boot_spl_dtb.dts new file mode 100644 index 0000000..3912f86 --- /dev/null +++ b/tools/u-boot-tools/binman/test/051_u_boot_spl_dtb.dts @@ -0,0 +1,13 @@ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + size = <16>; + + u-boot-spl-dtb { + }; + }; +}; diff --git a/tools/u-boot-tools/binman/test/052_u_boot_spl_nodtb.dts b/tools/u-boot-tools/binman/test/052_u_boot_spl_nodtb.dts new file mode 100644 index 0000000..7f4e277 --- /dev/null +++ b/tools/u-boot-tools/binman/test/052_u_boot_spl_nodtb.dts @@ -0,0 +1,11 @@ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + u-boot-spl-nodtb { + }; + }; +}; diff --git a/tools/u-boot-tools/binman/test/053_symbols.dts b/tools/u-boot-tools/binman/test/053_symbols.dts new file mode 100644 index 0000000..9f13567 --- /dev/null +++ b/tools/u-boot-tools/binman/test/053_symbols.dts @@ -0,0 +1,20 @@ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + pad-byte = <0xff>; + u-boot-spl { + }; + + u-boot { + offset = <20>; + }; + + u-boot-spl2 { + type = "u-boot-spl"; + }; + }; +}; diff --git a/tools/u-boot-tools/binman/test/054_unit_address.dts b/tools/u-boot-tools/binman/test/054_unit_address.dts new file mode 100644 index 0000000..3216dbb --- /dev/null +++ b/tools/u-boot-tools/binman/test/054_unit_address.dts @@ -0,0 +1,15 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + u-boot@0 { + }; + u-boot@1 { + }; + }; +}; diff --git a/tools/u-boot-tools/binman/test/055_sections.dts b/tools/u-boot-tools/binman/test/055_sections.dts new file mode 100644 index 0000000..6b306ae --- /dev/null +++ b/tools/u-boot-tools/binman/test/055_sections.dts @@ -0,0 +1,32 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + pad-byte = <0x26>; + size = <0x28>; + section@0 { + read-only; + size = <0x10>; + pad-byte = <0x21>; + + u-boot { + }; + }; + section@1 { + size = <0x10>; + pad-byte = <0x61>; + + u-boot { + }; + }; + section@2 { + u-boot { + }; + }; + }; +}; diff --git a/tools/u-boot-tools/binman/test/056_name_prefix.dts b/tools/u-boot-tools/binman/test/056_name_prefix.dts new file mode 100644 index 0000000..f38c80e --- /dev/null +++ b/tools/u-boot-tools/binman/test/056_name_prefix.dts @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + pad-byte = <0x26>; + size = <0x28>; + section@0 { + read-only; + name-prefix = "ro-"; + size = <0x10>; + pad-byte = <0x21>; + + u-boot { + }; + }; + section@1 { + name-prefix = "rw-"; + size = <0x10>; + pad-byte = <0x61>; + + u-boot { + }; + }; + }; +}; diff --git a/tools/u-boot-tools/binman/test/057_unknown_contents.dts b/tools/u-boot-tools/binman/test/057_unknown_contents.dts new file mode 100644 index 0000000..6ea98d7 --- /dev/null +++ b/tools/u-boot-tools/binman/test/057_unknown_contents.dts @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + _testing { + return-unknown-contents; + }; + }; +}; diff --git a/tools/u-boot-tools/binman/test/058_x86_ucode_spl_needs_retry.dts b/tools/u-boot-tools/binman/test/058_x86_ucode_spl_needs_retry.dts new file mode 100644 index 0000000..a04adaa --- /dev/null +++ b/tools/u-boot-tools/binman/test/058_x86_ucode_spl_needs_retry.dts @@ -0,0 +1,36 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + sort-by-offset; + end-at-4gb; + size = <0x200>; + u-boot-spl-with-ucode-ptr { + }; + + /* + * Microcode goes before the DTB which contains it, so binman + * will need to obtain the contents of the next section before + * obtaining the contents of this one. + */ + u-boot-ucode { + }; + + u-boot-dtb-with-ucode { + }; + }; + + microcode { + update@0 { + data = <0x12345678 0x12345679>; + }; + update@1 { + data = <0xabcd0000 0x78235609>; + }; + }; +}; diff --git a/tools/u-boot-tools/binman/test/059_change_size.dts b/tools/u-boot-tools/binman/test/059_change_size.dts new file mode 100644 index 0000000..1a69026 --- /dev/null +++ b/tools/u-boot-tools/binman/test/059_change_size.dts @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + _testing { + bad-update-contents; + }; + }; +}; diff --git a/tools/u-boot-tools/binman/test/060_fdt_update.dts b/tools/u-boot-tools/binman/test/060_fdt_update.dts new file mode 100644 index 0000000..f53c8a5 --- /dev/null +++ b/tools/u-boot-tools/binman/test/060_fdt_update.dts @@ -0,0 +1,31 @@ +// SPDX-License-Identifier: GPL-2.0+ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + pad-byte = <0x26>; + size = <0x28>; + section@0 { + read-only; + name-prefix = "ro-"; + size = <0x10>; + pad-byte = <0x21>; + + u-boot { + }; + }; + section@1 { + name-prefix = "rw-"; + size = <0x10>; + pad-byte = <0x61>; + + u-boot { + }; + }; + _testing { + }; + }; +}; diff --git a/tools/u-boot-tools/binman/test/061_fdt_update_bad.dts b/tools/u-boot-tools/binman/test/061_fdt_update_bad.dts new file mode 100644 index 0000000..e5abf31 --- /dev/null +++ b/tools/u-boot-tools/binman/test/061_fdt_update_bad.dts @@ -0,0 +1,32 @@ +// SPDX-License-Identifier: GPL-2.0+ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + pad-byte = <0x26>; + size = <0x28>; + section@0 { + read-only; + name-prefix = "ro-"; + size = <0x10>; + pad-byte = <0x21>; + + u-boot { + }; + }; + section@1 { + name-prefix = "rw-"; + size = <0x10>; + pad-byte = <0x61>; + + u-boot { + }; + }; + _testing { + never-complete-process-fdt; + }; + }; +}; diff --git a/tools/u-boot-tools/binman/test/062_entry_args.dts b/tools/u-boot-tools/binman/test/062_entry_args.dts new file mode 100644 index 0000000..4d4f102 --- /dev/null +++ b/tools/u-boot-tools/binman/test/062_entry_args.dts @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: GPL-2.0+ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + _testing { + test-str-fdt = "test0"; + test-int-fdt = <123>; + }; + }; +}; diff --git a/tools/u-boot-tools/binman/test/063_entry_args_missing.dts b/tools/u-boot-tools/binman/test/063_entry_args_missing.dts new file mode 100644 index 0000000..1644e2f --- /dev/null +++ b/tools/u-boot-tools/binman/test/063_entry_args_missing.dts @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: GPL-2.0+ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + _testing { + test-str-fdt = "test0"; + }; + }; +}; diff --git a/tools/u-boot-tools/binman/test/064_entry_args_required.dts b/tools/u-boot-tools/binman/test/064_entry_args_required.dts new file mode 100644 index 0000000..705be10 --- /dev/null +++ b/tools/u-boot-tools/binman/test/064_entry_args_required.dts @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: GPL-2.0+ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + _testing { + require-args; + test-str-fdt = "test0"; + }; + }; +}; diff --git a/tools/u-boot-tools/binman/test/065_entry_args_unknown_datatype.dts b/tools/u-boot-tools/binman/test/065_entry_args_unknown_datatype.dts new file mode 100644 index 0000000..3e4838f --- /dev/null +++ b/tools/u-boot-tools/binman/test/065_entry_args_unknown_datatype.dts @@ -0,0 +1,15 @@ +// SPDX-License-Identifier: GPL-2.0+ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + _testing { + test-str-fdt = "test0"; + test-int-fdt = <123>; + force-bad-datatype; + }; + }; +}; diff --git a/tools/u-boot-tools/binman/test/066_text.dts b/tools/u-boot-tools/binman/test/066_text.dts new file mode 100644 index 0000000..59b1fed --- /dev/null +++ b/tools/u-boot-tools/binman/test/066_text.dts @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: GPL-2.0+ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + text { + size = <8>; + text-label = "test-id"; + }; + text2 { + type = "text"; + text-label = "test-id2"; + }; + text3 { + type = "text"; + text-label = "test-id3"; + }; + /* This one does not use command-line args */ + text4 { + type = "text"; + text-label = "test-id4"; + test-id4 = "some text"; + }; + }; +}; diff --git a/tools/u-boot-tools/binman/test/067_fmap.dts b/tools/u-boot-tools/binman/test/067_fmap.dts new file mode 100644 index 0000000..9c0e293 --- /dev/null +++ b/tools/u-boot-tools/binman/test/067_fmap.dts @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: GPL-2.0+ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + section@0 { + read-only; + name-prefix = "ro-"; + size = <0x10>; + pad-byte = <0x21>; + + u-boot { + }; + }; + section@1 { + name-prefix = "rw-"; + size = <0x10>; + pad-byte = <0x61>; + + u-boot { + }; + }; + fmap { + }; + }; +}; diff --git a/tools/u-boot-tools/binman/test/068_blob_named_by_arg.dts b/tools/u-boot-tools/binman/test/068_blob_named_by_arg.dts new file mode 100644 index 0000000..e129f84 --- /dev/null +++ b/tools/u-boot-tools/binman/test/068_blob_named_by_arg.dts @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: GPL-2.0+ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + cros-ec-rw { + }; + }; +}; diff --git a/tools/u-boot-tools/binman/test/069_fill.dts b/tools/u-boot-tools/binman/test/069_fill.dts new file mode 100644 index 0000000..e372ea3 --- /dev/null +++ b/tools/u-boot-tools/binman/test/069_fill.dts @@ -0,0 +1,15 @@ +// SPDX-License-Identifier: GPL-2.0+ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + size = <16>; + fill { + size = <8>; + fill-byte = [ff]; + }; + }; +}; diff --git a/tools/u-boot-tools/binman/test/070_fill_no_size.dts b/tools/u-boot-tools/binman/test/070_fill_no_size.dts new file mode 100644 index 0000000..7b1fcf1 --- /dev/null +++ b/tools/u-boot-tools/binman/test/070_fill_no_size.dts @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: GPL-2.0+ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + size = <16>; + fill { + fill-byte = [ff]; + }; + }; +}; diff --git a/tools/u-boot-tools/binman/test/071_gbb.dts b/tools/u-boot-tools/binman/test/071_gbb.dts new file mode 100644 index 0000000..5517563 --- /dev/null +++ b/tools/u-boot-tools/binman/test/071_gbb.dts @@ -0,0 +1,31 @@ +// SPDX-License-Identifier: GPL-2.0+ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + gbb { + size = <0x2180>; + flags { + dev-screen-short-delay; + load-option-roms; + enable-alternate-os; + force-dev-switch-on; + force-dev-boot-usb; + disable-fw-rollback-check; + enter-triggers-tonorm; + force-dev-boot-legacy; + faft-key-override; + disable-ec-software-sync; + default-dev-boot-legacy; + disable-pd-software-sync; + disable-lid-shutdown; + force-dev-boot-fastboot-full-cap; + enable-serial; + disable-dwmp; + }; + }; + }; +}; diff --git a/tools/u-boot-tools/binman/test/072_gbb_too_small.dts b/tools/u-boot-tools/binman/test/072_gbb_too_small.dts new file mode 100644 index 0000000..c088f36 --- /dev/null +++ b/tools/u-boot-tools/binman/test/072_gbb_too_small.dts @@ -0,0 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0+ +/dts-v1/; + +/ { + binman { + gbb { + size = <0x200>; + }; + }; +}; diff --git a/tools/u-boot-tools/binman/test/073_gbb_no_size.dts b/tools/u-boot-tools/binman/test/073_gbb_no_size.dts new file mode 100644 index 0000000..83be403 --- /dev/null +++ b/tools/u-boot-tools/binman/test/073_gbb_no_size.dts @@ -0,0 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0+ +/dts-v1/; + +/ { + binman { + gbb { + }; + }; +}; diff --git a/tools/u-boot-tools/binman/test/074_vblock.dts b/tools/u-boot-tools/binman/test/074_vblock.dts new file mode 100644 index 0000000..f0c21bf --- /dev/null +++ b/tools/u-boot-tools/binman/test/074_vblock.dts @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: GPL-2.0+ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + u_boot: u-boot { + }; + + vblock { + content = <&u_boot &dtb>; + keyblock = "firmware.keyblock"; + signprivate = "firmware_data_key.vbprivk"; + version = <1>; + kernelkey = "kernel_subkey.vbpubk"; + preamble-flags = <1>; + }; + + /* + * Put this after the vblock so that its contents are not + * available when the vblock first tries to obtain its contents + */ + dtb: u-boot-dtb { + }; + }; +}; diff --git a/tools/u-boot-tools/binman/test/075_vblock_no_content.dts b/tools/u-boot-tools/binman/test/075_vblock_no_content.dts new file mode 100644 index 0000000..676d947 --- /dev/null +++ b/tools/u-boot-tools/binman/test/075_vblock_no_content.dts @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: GPL-2.0+ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + u_boot: u-boot { + }; + + vblock { + keyblock = "firmware.keyblock"; + signprivate = "firmware_data_key.vbprivk"; + version = <1>; + kernelkey = "kernel_subkey.vbpubk"; + preamble-flags = <1>; + }; + + dtb: u-boot-dtb { + }; + }; +}; diff --git a/tools/u-boot-tools/binman/test/076_vblock_bad_phandle.dts b/tools/u-boot-tools/binman/test/076_vblock_bad_phandle.dts new file mode 100644 index 0000000..ffbd0c3 --- /dev/null +++ b/tools/u-boot-tools/binman/test/076_vblock_bad_phandle.dts @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: GPL-2.0+ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + u_boot: u-boot { + }; + + vblock { + content = <1000>; + keyblock = "firmware.keyblock"; + signprivate = "firmware_data_key.vbprivk"; + version = <1>; + kernelkey = "kernel_subkey.vbpubk"; + preamble-flags = <1>; + }; + + dtb: u-boot-dtb { + }; + }; +}; diff --git a/tools/u-boot-tools/binman/test/077_vblock_bad_entry.dts b/tools/u-boot-tools/binman/test/077_vblock_bad_entry.dts new file mode 100644 index 0000000..764c42a --- /dev/null +++ b/tools/u-boot-tools/binman/test/077_vblock_bad_entry.dts @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: GPL-2.0+ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + u_boot: u-boot { + }; + + vblock { + content = <&u_boot &other>; + keyblock = "firmware.keyblock"; + signprivate = "firmware_data_key.vbprivk"; + version = <1>; + kernelkey = "kernel_subkey.vbpubk"; + preamble-flags = <1>; + }; + + dtb: u-boot-dtb { + }; + }; + + other: other { + }; +}; diff --git a/tools/u-boot-tools/binman/test/078_u_boot_tpl.dts b/tools/u-boot-tools/binman/test/078_u_boot_tpl.dts new file mode 100644 index 0000000..6c60b4c --- /dev/null +++ b/tools/u-boot-tools/binman/test/078_u_boot_tpl.dts @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: GPL-2.0+ +/dts-v1/; + +/ { + binman { + u-boot-tpl { + }; + u-boot-tpl-dtb { + }; + }; +}; diff --git a/tools/u-boot-tools/binman/test/079_uses_pos.dts b/tools/u-boot-tools/binman/test/079_uses_pos.dts new file mode 100644 index 0000000..7638b9b --- /dev/null +++ b/tools/u-boot-tools/binman/test/079_uses_pos.dts @@ -0,0 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0+ +/dts-v1/; + +/ { + binman { + u-boot { + pos = <10>; + }; + }; +}; diff --git a/tools/u-boot-tools/binman/test/080_fill_empty.dts b/tools/u-boot-tools/binman/test/080_fill_empty.dts new file mode 100644 index 0000000..2b78d3a --- /dev/null +++ b/tools/u-boot-tools/binman/test/080_fill_empty.dts @@ -0,0 +1,15 @@ +// SPDX-License-Identifier: GPL-2.0+ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + size = <16>; + fill { + size = <0>; + fill-byte = [ff]; + }; + }; +}; diff --git a/tools/u-boot-tools/binman/test/081_x86-start16-tpl.dts b/tools/u-boot-tools/binman/test/081_x86-start16-tpl.dts new file mode 100644 index 0000000..68e6bbd --- /dev/null +++ b/tools/u-boot-tools/binman/test/081_x86-start16-tpl.dts @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: GPL-2.0+ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + size = <16>; + + x86-start16-tpl { + }; + }; +}; diff --git a/tools/u-boot-tools/binman/test/082_fdt_update_all.dts b/tools/u-boot-tools/binman/test/082_fdt_update_all.dts new file mode 100644 index 0000000..284975c --- /dev/null +++ b/tools/u-boot-tools/binman/test/082_fdt_update_all.dts @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: GPL-2.0+ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + section { + u-boot-dtb { + }; + }; + u-boot-spl-dtb { + }; + u-boot-tpl-dtb { + }; + }; +}; diff --git a/tools/u-boot-tools/binman/test/083_compress.dts b/tools/u-boot-tools/binman/test/083_compress.dts new file mode 100644 index 0000000..07813bd --- /dev/null +++ b/tools/u-boot-tools/binman/test/083_compress.dts @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: GPL-2.0+ +/dts-v1/; + +/ { + binman { + blob { + filename = "compress"; + compress = "lz4"; + }; + }; +}; diff --git a/tools/u-boot-tools/binman/test/084_files.dts b/tools/u-boot-tools/binman/test/084_files.dts new file mode 100644 index 0000000..83ddb78 --- /dev/null +++ b/tools/u-boot-tools/binman/test/084_files.dts @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: GPL-2.0+ +/dts-v1/; + +/ { + binman { + files { + pattern = "files/*.dat"; + compress = "none"; + }; + }; +}; diff --git a/tools/u-boot-tools/binman/test/085_files_compress.dts b/tools/u-boot-tools/binman/test/085_files_compress.dts new file mode 100644 index 0000000..847b398 --- /dev/null +++ b/tools/u-boot-tools/binman/test/085_files_compress.dts @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: GPL-2.0+ +/dts-v1/; + +/ { + binman { + files { + pattern = "files/*.dat"; + compress = "lz4"; + }; + }; +}; diff --git a/tools/u-boot-tools/binman/test/086_files_none.dts b/tools/u-boot-tools/binman/test/086_files_none.dts new file mode 100644 index 0000000..34bd92f --- /dev/null +++ b/tools/u-boot-tools/binman/test/086_files_none.dts @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: GPL-2.0+ +/dts-v1/; + +/ { + binman { + files { + pattern = "files/*.none"; + compress = "none"; + require-matches; + }; + }; +}; diff --git a/tools/u-boot-tools/binman/test/087_files_no_pattern.dts b/tools/u-boot-tools/binman/test/087_files_no_pattern.dts new file mode 100644 index 0000000..0cb5b46 --- /dev/null +++ b/tools/u-boot-tools/binman/test/087_files_no_pattern.dts @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: GPL-2.0+ +/dts-v1/; + +/ { + binman { + files { + compress = "none"; + require-matches; + }; + }; +}; diff --git a/tools/u-boot-tools/binman/test/088_expand_size.dts b/tools/u-boot-tools/binman/test/088_expand_size.dts new file mode 100644 index 0000000..c8a0130 --- /dev/null +++ b/tools/u-boot-tools/binman/test/088_expand_size.dts @@ -0,0 +1,43 @@ +// SPDX-License-Identifier: GPL-2.0+ +/dts-v1/; + +/ { + binman { + size = <40>; + fill { + expand-size; + fill-byte = [61]; + size = <0>; + }; + u-boot { + offset = <8>; + }; + section { + expand-size; + pad-byte = <0x62>; + intel-mrc { + }; + }; + u-boot2 { + type = "u-boot"; + offset = <16>; + }; + section2 { + type = "section"; + fill { + expand-size; + fill-byte = [63]; + size = <0>; + }; + u-boot { + offset = <8>; + }; + }; + fill2 { + type = "fill"; + expand-size; + fill-byte = [64]; + size = <0>; + }; + }; +}; diff --git a/tools/u-boot-tools/binman/test/089_expand_size_bad.dts b/tools/u-boot-tools/binman/test/089_expand_size_bad.dts new file mode 100644 index 0000000..edc0e5c --- /dev/null +++ b/tools/u-boot-tools/binman/test/089_expand_size_bad.dts @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: GPL-2.0+ +/dts-v1/; + +/ { + binman { + _testing { + expand-size; + return-contents-once; + }; + u-boot { + offset = <8>; + }; + }; +}; diff --git a/tools/u-boot-tools/binman/test/090_hash.dts b/tools/u-boot-tools/binman/test/090_hash.dts new file mode 100644 index 0000000..2003045 --- /dev/null +++ b/tools/u-boot-tools/binman/test/090_hash.dts @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: GPL-2.0+ +/dts-v1/; + +/ { + binman { + u-boot { + hash { + algo = "sha256"; + }; + }; + }; +}; diff --git a/tools/u-boot-tools/binman/test/091_hash_no_algo.dts b/tools/u-boot-tools/binman/test/091_hash_no_algo.dts new file mode 100644 index 0000000..b64df20 --- /dev/null +++ b/tools/u-boot-tools/binman/test/091_hash_no_algo.dts @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: GPL-2.0+ +/dts-v1/; + +/ { + binman { + u-boot { + hash { + }; + }; + }; +}; diff --git a/tools/u-boot-tools/binman/test/092_hash_bad_algo.dts b/tools/u-boot-tools/binman/test/092_hash_bad_algo.dts new file mode 100644 index 0000000..d240200 --- /dev/null +++ b/tools/u-boot-tools/binman/test/092_hash_bad_algo.dts @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: GPL-2.0+ +/dts-v1/; + +/ { + binman { + u-boot { + hash { + algo = "invalid"; + }; + }; + }; +}; diff --git a/tools/u-boot-tools/binman/test/093_x86_tpl_ucode.dts b/tools/u-boot-tools/binman/test/093_x86_tpl_ucode.dts new file mode 100644 index 0000000..d7ed9fc --- /dev/null +++ b/tools/u-boot-tools/binman/test/093_x86_tpl_ucode.dts @@ -0,0 +1,29 @@ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + sort-by-offset; + end-at-4gb; + size = <0x200>; + u-boot-tpl-with-ucode-ptr { + }; + + u-boot-tpl-dtb-with-ucode { + }; + + u-boot-ucode { + }; + }; + + microcode { + update@0 { + data = <0x12345678 0x12345679>; + }; + update@1 { + data = <0xabcd0000 0x78235609>; + }; + }; +}; diff --git a/tools/u-boot-tools/binman/test/094_fmap_x86.dts b/tools/u-boot-tools/binman/test/094_fmap_x86.dts new file mode 100644 index 0000000..613c5da --- /dev/null +++ b/tools/u-boot-tools/binman/test/094_fmap_x86.dts @@ -0,0 +1,20 @@ +// SPDX-License-Identifier: GPL-2.0+ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + end-at-4gb; + size = <0x100>; + pad-byte = <0x61>; + u-boot { + }; + intel-mrc { + }; + fmap { + offset = <0xffffff20>; + }; + }; +}; diff --git a/tools/u-boot-tools/binman/test/095_fmap_x86_section.dts b/tools/u-boot-tools/binman/test/095_fmap_x86_section.dts new file mode 100644 index 0000000..4cfce45 --- /dev/null +++ b/tools/u-boot-tools/binman/test/095_fmap_x86_section.dts @@ -0,0 +1,22 @@ +// SPDX-License-Identifier: GPL-2.0+ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + end-at-4gb; + size = <0x100>; + u-boot { + }; + section { + pad-byte = <0x62>; + intel-mrc { + }; + fmap { + offset = <0x20>; + }; + }; + }; +}; diff --git a/tools/u-boot-tools/binman/test/096_elf.dts b/tools/u-boot-tools/binman/test/096_elf.dts new file mode 100644 index 0000000..df3440c --- /dev/null +++ b/tools/u-boot-tools/binman/test/096_elf.dts @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: GPL-2.0+ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + u-boot-elf { + }; + u-boot-spl-elf { + }; + }; +}; diff --git a/tools/u-boot-tools/binman/test/097_elf_strip.dts b/tools/u-boot-tools/binman/test/097_elf_strip.dts new file mode 100644 index 0000000..6f3c66f --- /dev/null +++ b/tools/u-boot-tools/binman/test/097_elf_strip.dts @@ -0,0 +1,15 @@ +// SPDX-License-Identifier: GPL-2.0+ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + u-boot-elf { + strip; + }; + u-boot-spl-elf { + }; + }; +}; diff --git a/tools/u-boot-tools/binman/test/099_hash_section.dts b/tools/u-boot-tools/binman/test/099_hash_section.dts new file mode 100644 index 0000000..dcd8683 --- /dev/null +++ b/tools/u-boot-tools/binman/test/099_hash_section.dts @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: GPL-2.0+ +/dts-v1/; + +/ { + binman { + section { + u-boot { + }; + fill { + size = <0x10>; + fill-byte = [61]; + }; + hash { + algo = "sha256"; + }; + }; + }; +}; diff --git a/tools/u-boot-tools/binman/test/100_intel_refcode.dts b/tools/u-boot-tools/binman/test/100_intel_refcode.dts new file mode 100644 index 0000000..0a1a027 --- /dev/null +++ b/tools/u-boot-tools/binman/test/100_intel_refcode.dts @@ -0,0 +1,14 @@ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + size = <16>; + + intel-refcode { + filename = "refcode.bin"; + }; + }; +}; diff --git a/tools/u-boot-tools/binman/test/80_4gb_and_skip_at_start_together.dts b/tools/u-boot-tools/binman/test/80_4gb_and_skip_at_start_together.dts new file mode 100644 index 0000000..90c467d --- /dev/null +++ b/tools/u-boot-tools/binman/test/80_4gb_and_skip_at_start_together.dts @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2018 NXP + */ + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + size = <32>; + sort-by-offset; + end-at-4gb; + skip-at-start = <0xffffffe0>; + u-boot { + offset = <0xffffffe0>; + }; + }; +}; diff --git a/tools/u-boot-tools/binman/test/81_powerpc_mpc85xx_bootpg_resetvec.dts b/tools/u-boot-tools/binman/test/81_powerpc_mpc85xx_bootpg_resetvec.dts new file mode 100644 index 0000000..8f4b16c --- /dev/null +++ b/tools/u-boot-tools/binman/test/81_powerpc_mpc85xx_bootpg_resetvec.dts @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2018 NXP + */ + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + powerpc-mpc85xx-bootpg-resetvec { + }; + }; +}; diff --git a/tools/u-boot-tools/binman/test/Makefile b/tools/u-boot-tools/binman/test/Makefile new file mode 100644 index 0000000..e58fc80 --- /dev/null +++ b/tools/u-boot-tools/binman/test/Makefile @@ -0,0 +1,55 @@ +# +# Builds test programs +# +# Copyright (C) 2017 Google, Inc +# Written by Simon Glass <sjg@chromium.org> +# +# SPDX-License-Identifier: GPL-2.0+ +# + +CFLAGS := -march=i386 -m32 -nostdlib -I ../../../include + +LDS_UCODE := -T u_boot_ucode_ptr.lds +LDS_BINMAN := -T u_boot_binman_syms.lds +LDS_BINMAN_BAD := -T u_boot_binman_syms_bad.lds + +TARGETS = u_boot_ucode_ptr u_boot_no_ucode_ptr bss_data \ + u_boot_binman_syms u_boot_binman_syms.bin u_boot_binman_syms_bad \ + u_boot_binman_syms_size + +all: $(TARGETS) + +u_boot_no_ucode_ptr: CFLAGS += $(LDS_UCODE) +u_boot_no_ucode_ptr: u_boot_no_ucode_ptr.c + +u_boot_ucode_ptr: CFLAGS += $(LDS_UCODE) +u_boot_ucode_ptr: u_boot_ucode_ptr.c + +bss_data: CFLAGS += bss_data.lds +bss_data: bss_data.c + +u_boot_binman_syms.bin: u_boot_binman_syms + objcopy -O binary $< -R .note.gnu.build-id $@ + +u_boot_binman_syms: CFLAGS += $(LDS_BINMAN) +u_boot_binman_syms: u_boot_binman_syms.c + +u_boot_binman_syms_bad: CFLAGS += $(LDS_BINMAN_BAD) +u_boot_binman_syms_bad: u_boot_binman_syms_bad.c + +u_boot_binman_syms_size: CFLAGS += $(LDS_BINMAN) +u_boot_binman_syms_size: u_boot_binman_syms_size.c + +clean: + rm -f $(TARGETS) + +help: + @echo "Makefile for binman test programs" + @echo + @echo "Intended for use on x86 hosts" + @echo + @echo "Targets:" + @echo + @echo -e "\thelp - Print help (this is it!)" + @echo -e "\tall - Builds test programs (default targget)" + @echo -e "\tclean - Delete output files" diff --git a/tools/u-boot-tools/binman/test/bss_data b/tools/u-boot-tools/binman/test/bss_data new file mode 100755 index 0000000000000000000000000000000000000000..afa28282aa157f6717c1ba244ecbfc6e1b081734 GIT binary patch literal 5020 zcmb<-^>JflWMqH=CI)5(5bwj^|Nl)G7#Jo9Kx9l96c|_;G#I!UIKc8?(FPU{h6Wan z2u2155M~7l3NS$ALHZW3a4;-j;b384VDMmIU|@sFgZNh%7#OZJuyBB|3IhWJ2!q5y zIN)hX3J)U#15YCh2MCKWFff2HNDhPppO&P6j6%mC^Fh{v)PU48GcYi?`-L(b{VBZ6 z@X7tKIjZXzmwu`+pAab?6x#XpIIG4277iu`28JCVlVT4uN;6x7^n?8W=KufyI#7E- zvW!(h42(4#jM6+TEFFvt3^EK5yyO4>{~)^<9Qim}nVfi81CH{rfE)nQG>S(<U^E0q zLtr!nMnhmU1V%$(Gz3ONU^E0qLtr!nMnhnbgn$O9kL>JhrJxa-RGL>(s$imLp=Y9N zR0`%9))|`U8JOr9YeK{s81#xOb4wDF81#xuiXe0bjFp#PlB$=USE`p(nwgWLo0-C( zSCU#$!l0L$5ua9+n421(ky3=<G3ccvmLxLhB^4Jl=q2ap=BDPA5FaSg3=9nH3=9mQ z?mK7<0W_Wf8bg4Y0PBM5F+j#6Kw|?x8dx}fAob}%YC(N{5DgmZ0I@;lfyNv_;}76| zJxG#)fdOWo6VyDAI)z3S4h2RA1`DVf5Cv1`162nag8+?zFbE*bKvoyTz`(%4z`y`f zrvMr&5nx~d#R*6a2rGaH1_p+3sCfoZK8pYYgEo{6qCjp3VL_-_ATbcu7hqsW05PB# zqz1$U(I7v7*dXj7#K0iH$iTqG0Kp)CfW|^N(aehwW?*muX@FvoIZ!4B1&SAtAY?>| zfdLf$5I)3aP&h;QAm1}EfWi~P2ieZR01h{>{os)$22l7w<Uz3sVnHx713P5Q3BqMy zVfgqT$^=s&J`a+=1Q|a57iVAqn+FPOK?XJkP&k9eyg(WR7#JDUL2(WmzhYou5MW?q zFlK<bACxA97?{9u&H|TbWv~a0al!d)3=B!d#qpp7u9wUZ9}nUbXI7=cMH%9YOA?Dp XU{YXqZenI0LwqVo6GMDzUJ3&M0XCPn literal 0 HcmV?d00001 diff --git a/tools/u-boot-tools/binman/test/bss_data.c b/tools/u-boot-tools/binman/test/bss_data.c new file mode 100644 index 0000000..79537c3 --- /dev/null +++ b/tools/u-boot-tools/binman/test/bss_data.c @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2016 Google, Inc + * + * Simple program to create a _dt_ucode_base_size symbol which can be read + * by binutils. This is used by binman tests. + */ + +int bss_data[10]; +int __bss_size = sizeof(bss_data); + +int main() +{ + bss_data[2] = 2; + + return 0; +} diff --git a/tools/u-boot-tools/binman/test/bss_data.lds b/tools/u-boot-tools/binman/test/bss_data.lds new file mode 100644 index 0000000..306dab5 --- /dev/null +++ b/tools/u-boot-tools/binman/test/bss_data.lds @@ -0,0 +1,15 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (c) 2016 Google, Inc + */ + +OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386") +OUTPUT_ARCH(i386) +ENTRY(_start) + +SECTIONS +{ + . = 0xfffffdf0; + _start = .; + __bss_size = 10; +} diff --git a/tools/u-boot-tools/binman/test/files/1.dat b/tools/u-boot-tools/binman/test/files/1.dat new file mode 100644 index 0000000..a952470 --- /dev/null +++ b/tools/u-boot-tools/binman/test/files/1.dat @@ -0,0 +1 @@ +sorry I'm late diff --git a/tools/u-boot-tools/binman/test/files/2.dat b/tools/u-boot-tools/binman/test/files/2.dat new file mode 100644 index 0000000..687ea52 --- /dev/null +++ b/tools/u-boot-tools/binman/test/files/2.dat @@ -0,0 +1 @@ +Oh, don't bother apologising, I'm sorry you're alive diff --git a/tools/u-boot-tools/binman/test/files/ignored_dir.dat/ignore b/tools/u-boot-tools/binman/test/files/ignored_dir.dat/ignore new file mode 100644 index 0000000..e69de29 diff --git a/tools/u-boot-tools/binman/test/files/not-this-one b/tools/u-boot-tools/binman/test/files/not-this-one new file mode 100644 index 0000000..e71c225 --- /dev/null +++ b/tools/u-boot-tools/binman/test/files/not-this-one @@ -0,0 +1 @@ +this does not have a .dat extenion diff --git a/tools/u-boot-tools/binman/test/u_boot_binman_syms b/tools/u-boot-tools/binman/test/u_boot_binman_syms new file mode 100755 index 0000000000000000000000000000000000000000..126a1a623092cbe25f7a24118d26d4a0e8d0e0fc GIT binary patch literal 4924 zcmb<-^>JflWMqH=CI)5(7|(=(fk8zGB4fg!z`)F)!NAVI0+APhh=WLwIuK?9aRe9` zSfDhB0$~-9ISdeSkX+!?k`$0QItJ-SG8SEWls_5*qaiRF0;3@?8UmvsFd71*Aut*O zqaiRF0;3@?8UmvsKxPPl`r{%D3=GT+3=Hmmp$sbTzVps4?w-TE<Z!Vv`;t|DPHXkh zjdpi-wo=dtO)AYRDOE7lGto29H7f=44C@Td^bAb&3=K3P5(-8JhGqt42A1e%pz~p) z4xq6DkQ_4u6NrLiW~6ZmkRS`g$Nyps3=A3!3=A9$3=Dz{Oblj7;|w7F6p%tF7GPjx z2xVYkFo2FjfaJ>=7#Kj~79b@83``6iQ2j9eY!p5t14C(iQht6(d{Sm!Zem`1ab<3? zUNQrW3zaM`$T5m9D9SI0&reG$PA$PAk54)=uM#4inVXoN8efoK%n%Q=p|~<PJ~b}| zRiL;ev8aS0J|3bzIlrJX9wNk`S6rD}l9<GxS6osAp)+8ty!?_>z4W|Ny`<92oD|*6 z6b3znr{YTzlX6lS^pf*)b5rw5K%qe#mS$jJU}s=J#GC*F11vs4CV<i!hz2Dn&=?aa z&4J`q1Q-~Q$9-Vx92gkDV@;qq1L-pnU|=u>F`yV^1_%p6(=Sw*;g%o+gC~pwH3P&2 psYOmzIRXp}2N)O_K;urV&~yQE2Pc|&I)V%g>p%uTF~}Sc69BM%hC~1W literal 0 HcmV?d00001 diff --git a/tools/u-boot-tools/binman/test/u_boot_binman_syms.c b/tools/u-boot-tools/binman/test/u_boot_binman_syms.c new file mode 100644 index 0000000..4898f98 --- /dev/null +++ b/tools/u-boot-tools/binman/test/u_boot_binman_syms.c @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2017 Google, Inc + * + * Simple program to create some binman symbols. This is used by binman tests. + */ + +#define CONFIG_BINMAN +#include <binman_sym.h> + +binman_sym_declare(unsigned long, u_boot_spl, offset); +binman_sym_declare(unsigned long long, u_boot_spl2, offset); +binman_sym_declare(unsigned long, u_boot_any, image_pos); diff --git a/tools/u-boot-tools/binman/test/u_boot_binman_syms.lds b/tools/u-boot-tools/binman/test/u_boot_binman_syms.lds new file mode 100644 index 0000000..29cf9d0 --- /dev/null +++ b/tools/u-boot-tools/binman/test/u_boot_binman_syms.lds @@ -0,0 +1,29 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (c) 2016 Google, Inc + */ + +OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386") +OUTPUT_ARCH(i386) +ENTRY(_start) + +SECTIONS +{ + . = 0x00000000; + _start = .; + + . = ALIGN(4); + .text : + { + __image_copy_start = .; + *(.text*) + } + + . = ALIGN(4); + .binman_sym_table : { + __binman_sym_start = .; + KEEP(*(SORT(.binman_sym*))); + __binman_sym_end = .; + } + +} diff --git a/tools/u-boot-tools/binman/test/u_boot_binman_syms_bad b/tools/u-boot-tools/binman/test/u_boot_binman_syms_bad new file mode 100755 index 0000000000000000000000000000000000000000..8da3d9d48f388a9be53e92590984411691f6721f GIT binary patch literal 4890 zcmb<-^>JflWMqH=CI)5(7|(=(fnkLJM8<?cfq|JpgMpoa1tJd>2g!oefiN40Bf!AG z0;NF|2&*tKFn};f9E1a(mZX5h(J@FrlCkL0qx{hj7!85Z5Eu=C(GVC7fzc2c4S~@R z7!85Z5Eu=C(GVC70Ww1X)E^gNU|?WoU|?|f3uTyc_pW=V$LG&;9{R>~cD;{`Y4YEU zZnV3zvz3BIXi{ljNvVQ~o`s%?u2CtNXIN)wqGw>DXRHYkXJF7PuFNe-Ok&V0E-8Z0 z88B8}eo3lcdS0nsQfX#Rif(2KgI-c*UT$Jud~s!Nd`V(bPAY?5a(-@ZYF-Jt6L9jS z85kJY85kItU@8P4V-GMc$iE=A9s>h-90TNU(Ab2E00RT^*aJ+R15}*>ND~7CgNXnG zgEo{6qCjIMAS?(CLy#B<#|tnpc!C&E3{nGPf@rwg7#J812r@7nU|?VXjeUSH$X<{; zIMK{&5Mp4M1u_7NLFPc2I4Rgj4#<BXMa+m$0dX0ak;ZyJ0xS$4|AS~42Jxkk#%=@| zm>A4pk_@2mVq~yoU|`T-U|`^2U|;~rM=>xkfWivo4-mf$6lPF80t`$H?NIYTLLfdv zX?#+Cen~tc=8NN#5>xb&8DK(C1;qt9@dZWs1@Q&>#SHPqC5c5PSd<tcseu{^)t{JG Vi6jRz9F)>h^HLb%kp&=z0{}s#fJ^`Y literal 0 HcmV?d00001 diff --git a/tools/u-boot-tools/binman/test/u_boot_binman_syms_bad.c b/tools/u-boot-tools/binman/test/u_boot_binman_syms_bad.c new file mode 120000 index 0000000..939b2e9 --- /dev/null +++ b/tools/u-boot-tools/binman/test/u_boot_binman_syms_bad.c @@ -0,0 +1 @@ +u_boot_binman_syms.c \ No newline at end of file diff --git a/tools/u-boot-tools/binman/test/u_boot_binman_syms_bad.lds b/tools/u-boot-tools/binman/test/u_boot_binman_syms_bad.lds new file mode 100644 index 0000000..849d158 --- /dev/null +++ b/tools/u-boot-tools/binman/test/u_boot_binman_syms_bad.lds @@ -0,0 +1,28 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (c) 2016 Google, Inc + */ + +OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386") +OUTPUT_ARCH(i386) +ENTRY(_start) + +SECTIONS +{ + . = 0x00000000; + _start = .; + + . = ALIGN(4); + .text : + { + *(.text*) + } + + . = ALIGN(4); + .binman_sym_table : { + __binman_sym_start = .; + KEEP(*(SORT(.binman_sym*))); + __binman_sym_end = .; + } + +} diff --git a/tools/u-boot-tools/binman/test/u_boot_binman_syms_size b/tools/u-boot-tools/binman/test/u_boot_binman_syms_size new file mode 100755 index 0000000000000000000000000000000000000000..d691e897c0f1842cb82efbc67f57d9f62853b99c GIT binary patch literal 4825 zcmb<-^>JflWMqH=CI)5(7|(=(fnkmSM8<?cfq|JpgMpoa1tJd>2g#~3Fff2H8;B#o zz`z2fK@<q9FfcHHFi0GP1D}?pfW*-;NI#OX=+dM7(GVC7fzc2c4S~@R7!85Z5Eu=C z(GVC7fzc2c4S~@R7!3h3Ljcqt7hzyvU}j)oaQ6#kIH6*xciy`1VE>-i*G!7~HVVgl zSjph->};i=5t>w*S5m5AqGzFJqH9zN<{8!*n&=sr=oxE5#2Fa$iYs$V5|bG8ic5+h zbOwx-mtT^qm!4OumsFaWlcJlM!l0LwnU|ZG7hhbN8()%`l#|M!mz<xQo0?Zbig%<L z7#P?Y7#Nsfngk%@4KOaqk07=l0|R*M0_0cFc!Y`ogb$JfVVF7xs5%3XAZTnufPq09 z$_7!O@evRfgoYbP41@y&7#KW33@8Sv0Wm=|+-(dD3|j;l7&b64Fo4E8Kp12%XxxJn z&Ab#L28L3Q0Z<Gw2g<}r!A5UD{sSpuMuZB8%fO5@z5^0qVfgqTM8hzM4+>9^evo~F z3``8>Fi8dm1_1^}20KugK*a<Ym>B$_<2xWB5I+fp&&a?~8lRM(UlNaq?Be+1%&Jtq zWCoZRR7G(?PJBU8enEUeelbIQaY<rP2}3+g4JeVM=B1ztz|>^sCZ?yxC+8PbLbU+^ D(8+G& literal 0 HcmV?d00001 diff --git a/tools/u-boot-tools/binman/test/u_boot_binman_syms_size.c b/tools/u-boot-tools/binman/test/u_boot_binman_syms_size.c new file mode 100644 index 0000000..7224bc1 --- /dev/null +++ b/tools/u-boot-tools/binman/test/u_boot_binman_syms_size.c @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2017 Google, Inc + * + * Simple program to create some binman symbols. This is used by binman tests. + */ + +#define CONFIG_BINMAN +#include <binman_sym.h> + +binman_sym_declare(char, u_boot_spl, pos); diff --git a/tools/u-boot-tools/binman/test/u_boot_no_ucode_ptr b/tools/u-boot-tools/binman/test/u_boot_no_ucode_ptr new file mode 100755 index 0000000000000000000000000000000000000000..f72462f0be41a934d468481bf627d6c1ec9a8e1c GIT binary patch literal 4182 zcmb<-^>JflWMqH=CI)5(5bwj^|Nl)G7#KSEATlNl3JlB)8Vu|VEMR%C=!gIR89w~~ zFT=;c0K#k_K>-E^76t}}54;QvATvN%g@J(qghApU9Qd>(1*8-mgY*la3y$(fLtr!n zMnhmU1V%$(Gz3ONU^E0qLtr!nMnhmU1jq^jQ2$GWfq{XUfq}u@FO=a$<jDue4Sa7u zF#G@D(J~8xPgPsfLH$1_ki4_Am4ZfSQfXdEse*}~g`SD7Q7M>bSZ8RWXJDddtO*fk zV9+bB%q>YwV$drtDT2@$FjiiENvd9YUa4MEX=YA}Ze|LDUTJcEN-Be1a(-@ZYF-H` z9+75XU|?rpU;w!f)VBonGeLbzm>D4df!KNs4B&n$$e$wr{{I)@V_@I_NkB17ohekE z0aP5+kJW~<K@=z~Kv)nOW*{*TcIRVYumdrm7^DWo1koV3f!H9tfS-Y(fq{Vm)VBq> z8)Pr2kIRW>o`L`agBeHz6obrxGBGHSzd?eKo;m{q$bS$%vdy44hN%F_g8Jnj|BEv~ z)Pur;5gdvTIS|Rh@bSMssPF#w|9=o)02G3y@k#mlCGmOr@!&{{FDNO}OJ>N+FNu#& ZDS?V5B^IZ~7iU(bGQ<~`Bo>u0000x(cwztm literal 0 HcmV?d00001 diff --git a/tools/u-boot-tools/binman/test/u_boot_no_ucode_ptr.c b/tools/u-boot-tools/binman/test/u_boot_no_ucode_ptr.c new file mode 100644 index 0000000..24cdb90 --- /dev/null +++ b/tools/u-boot-tools/binman/test/u_boot_no_ucode_ptr.c @@ -0,0 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2016 Google, Inc + * + * Simple program to create a bad _dt_ucode_base_size symbol to create an + * error when it is used. This is used by binman tests. + */ + +static unsigned long not__dt_ucode_base_size[2] + __attribute__((section(".ucode"))) = {1, 2}; diff --git a/tools/u-boot-tools/binman/test/u_boot_ucode_ptr b/tools/u-boot-tools/binman/test/u_boot_ucode_ptr new file mode 100755 index 0000000000000000000000000000000000000000..dbfb184cecfbcf55cf43ed4f4ac0ee90a7364d93 GIT binary patch literal 4175 zcmb<-^>JflWMqH=CI)5(5bwj^|Nl)G7#KSEATlNl3JlB)8Vu|VEMR%C=!gIR89w~~ zFT=;c0K#k_K>-E^76t}}54;QvATvN%g@J(qghApU9Qd>(1*8-mgY*la3y$(fLtr!n zMnhmU1V%$(Gz3ONU^E0qLtr!nMnhmU1jq^jQ2$GWfq{XUfq}u@FO=bvP@>tlh&G|_ zhu^;NXWjj2P$oSI)c<1w$vZn+DQJWymFAU{Dwyb5=$Ysmm4bPOb%rK-1}1vOnh<dY z2EF3S+>*p32EF2vA_$!UW98+Sr0S*TmFgvxX6B^mW~MOcl_uw>q%!Cw=jZ08=9Q4* z5orbn26hGp29WzeeM?Y36V$hanE~=2h^@!K0Pd%P{3-J9|9=ra1_ln01Qf&6nL^bW zK*d4*SZyd9M1jHrgax5t1`-2dcRmINI}ihkL25uu5Dju0hz-IE_!$@)7#J8peOr*b zLH2_BxSVL_DF`qy=z}ysF~}S!6N3Wz8zcznsWUKu{0HG9+YE|hm<o_As9*l^KPc`& z>Oo<_2o6Pv9EfCL`1oG~)OY{;|38Q?01Cm<_@w;&l6Y{8#TS$m=_ND7r@;70iN&e$ Q#hF#94DrP!iA5z00Do<BhyVZp literal 0 HcmV?d00001 diff --git a/tools/u-boot-tools/binman/test/u_boot_ucode_ptr.c b/tools/u-boot-tools/binman/test/u_boot_ucode_ptr.c new file mode 100644 index 0000000..243c8e9 --- /dev/null +++ b/tools/u-boot-tools/binman/test/u_boot_ucode_ptr.c @@ -0,0 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2016 Google, Inc + * + * Simple program to create a _dt_ucode_base_size symbol which can be read + * by binutils. This is used by binman tests. + */ + +static unsigned long _dt_ucode_base_size[2] + __attribute__((section(".ucode"))) = {1, 2}; diff --git a/tools/u-boot-tools/binman/test/u_boot_ucode_ptr.lds b/tools/u-boot-tools/binman/test/u_boot_ucode_ptr.lds new file mode 100644 index 0000000..0cf9b76 --- /dev/null +++ b/tools/u-boot-tools/binman/test/u_boot_ucode_ptr.lds @@ -0,0 +1,17 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (c) 2016 Google, Inc + */ + +OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386") +OUTPUT_ARCH(i386) +ENTRY(_start) + +SECTIONS +{ + . = 0xfffffdf0; + _start = .; + .ucode : { + *(.ucode) + } +} diff --git a/tools/u-boot-tools/bmp_logo.c b/tools/u-boot-tools/bmp_logo.c new file mode 100644 index 0000000..55f833f --- /dev/null +++ b/tools/u-boot-tools/bmp_logo.c @@ -0,0 +1,208 @@ +#include "compiler.h" + +enum { + MODE_GEN_INFO, + MODE_GEN_DATA +}; + +typedef struct bitmap_s { /* bitmap description */ + uint16_t width; + uint16_t height; + uint8_t palette[256*3]; + uint8_t *data; +} bitmap_t; + +#define DEFAULT_CMAP_SIZE 16 /* size of default color map */ + +void usage(const char *prog) +{ + fprintf(stderr, "Usage: %s [--gen-info|--gen-data] file\n", prog); +} + +/* + * Neutralize little endians. + */ +uint16_t le_short(uint16_t x) +{ + uint16_t val; + uint8_t *p = (uint8_t *)(&x); + + val = (*p++ & 0xff) << 0; + val |= (*p & 0xff) << 8; + + return val; +} + +void skip_bytes (FILE *fp, int n) +{ + while (n-- > 0) + fgetc (fp); +} + +__attribute__ ((__noreturn__)) +int error (char * msg, FILE *fp) +{ + fprintf (stderr, "ERROR: %s\n", msg); + + fclose (fp); + + exit (EXIT_FAILURE); +} + +void gen_info(bitmap_t *b, uint16_t n_colors) +{ + printf("/*\n" + " * Automatically generated by \"tools/bmp_logo\"\n" + " *\n" + " * DO NOT EDIT\n" + " *\n" + " */\n\n\n" + "#ifndef __BMP_LOGO_H__\n" + "#define __BMP_LOGO_H__\n\n" + "#define BMP_LOGO_WIDTH\t\t%d\n" + "#define BMP_LOGO_HEIGHT\t\t%d\n" + "#define BMP_LOGO_COLORS\t\t%d\n" + "#define BMP_LOGO_OFFSET\t\t%d\n\n" + "extern unsigned short bmp_logo_palette[];\n" + "extern unsigned char bmp_logo_bitmap[];\n\n" + "#endif /* __BMP_LOGO_H__ */\n", + b->width, b->height, n_colors, + DEFAULT_CMAP_SIZE); +} + +int main (int argc, char *argv[]) +{ + int mode, i, x; + FILE *fp; + bitmap_t bmp; + bitmap_t *b = &bmp; + uint16_t data_offset, n_colors, hdr_size; + + if (argc < 3) { + usage(argv[0]); + exit (EXIT_FAILURE); + } + + if (!strcmp(argv[1], "--gen-info")) + mode = MODE_GEN_INFO; + else if (!strcmp(argv[1], "--gen-data")) + mode = MODE_GEN_DATA; + else { + usage(argv[0]); + exit(EXIT_FAILURE); + } + + fp = fopen(argv[2], "rb"); + if (!fp) { + perror(argv[2]); + exit (EXIT_FAILURE); + } + + if (fgetc (fp) != 'B' || fgetc (fp) != 'M') + error ("Input file is not a bitmap", fp); + + /* + * read width and height of the image, and the number of colors used; + * ignore the rest + */ + skip_bytes (fp, 8); + if (fread (&data_offset, sizeof (uint16_t), 1, fp) != 1) + error ("Couldn't read bitmap data offset", fp); + skip_bytes(fp, 2); + if (fread(&hdr_size, sizeof(uint16_t), 1, fp) != 1) + error("Couldn't read bitmap header size", fp); + if (hdr_size < 40) + error("Invalid bitmap header", fp); + skip_bytes(fp, 2); + if (fread (&b->width, sizeof (uint16_t), 1, fp) != 1) + error ("Couldn't read bitmap width", fp); + skip_bytes (fp, 2); + if (fread (&b->height, sizeof (uint16_t), 1, fp) != 1) + error ("Couldn't read bitmap height", fp); + skip_bytes (fp, 22); + if (fread (&n_colors, sizeof (uint16_t), 1, fp) != 1) + error ("Couldn't read bitmap colors", fp); + skip_bytes(fp, hdr_size - 34); + + /* + * Repair endianess. + */ + data_offset = le_short(data_offset); + b->width = le_short(b->width); + b->height = le_short(b->height); + n_colors = le_short(n_colors); + + /* assume we are working with an 8-bit file */ + if ((n_colors == 0) || (n_colors > 256 - DEFAULT_CMAP_SIZE)) { + /* reserve DEFAULT_CMAP_SIZE color map entries for default map */ + n_colors = 256 - DEFAULT_CMAP_SIZE; + } + + if (mode == MODE_GEN_INFO) { + gen_info(b, n_colors); + goto out; + } + + printf("/*\n" + " * Automatically generated by \"tools/bmp_logo\"\n" + " *\n" + " * DO NOT EDIT\n" + " *\n" + " */\n\n\n" + "#ifndef __BMP_LOGO_DATA_H__\n" + "#define __BMP_LOGO_DATA_H__\n\n"); + + /* allocate memory */ + if ((b->data = (uint8_t *)malloc(b->width * b->height)) == NULL) + error ("Error allocating memory for file", fp); + + /* read and print the palette information */ + printf("unsigned short bmp_logo_palette[] = {\n"); + + for (i=0; i<n_colors; ++i) { + b->palette[(int)(i*3+2)] = fgetc(fp); + b->palette[(int)(i*3+1)] = fgetc(fp); + b->palette[(int)(i*3+0)] = fgetc(fp); + x=fgetc(fp); + + printf ("%s0x0%X%X%X,%s", + ((i%8) == 0) ? "\t" : " ", + (b->palette[(int)(i*3+0)] >> 4) & 0x0F, + (b->palette[(int)(i*3+1)] >> 4) & 0x0F, + (b->palette[(int)(i*3+2)] >> 4) & 0x0F, + ((i%8) == 7) ? "\n" : "" + ); + } + + /* seek to offset indicated by file header */ + fseek(fp, (long)data_offset, SEEK_SET); + + /* read the bitmap; leave room for default color map */ + printf ("\n"); + printf ("};\n"); + printf ("\n"); + printf("unsigned char bmp_logo_bitmap[] = {\n"); + for (i=(b->height-1)*b->width; i>=0; i-=b->width) { + for (x = 0; x < b->width; x++) { + b->data[i + x] = (uint8_t) fgetc(fp) + + DEFAULT_CMAP_SIZE; + } + } + + for (i=0; i<(b->height*b->width); ++i) { + if ((i%8) == 0) + putchar ('\t'); + printf ("0x%02X,%c", + b->data[i], + ((i%8) == 7) ? '\n' : ' ' + ); + } + printf ("\n" + "};\n\n" + "#endif /* __BMP_LOGO_DATA_H__ */\n" + ); + +out: + fclose(fp); + return 0; +} diff --git a/tools/u-boot-tools/buildman/.gitignore b/tools/u-boot-tools/buildman/.gitignore new file mode 100644 index 0000000..0d20b64 --- /dev/null +++ b/tools/u-boot-tools/buildman/.gitignore @@ -0,0 +1 @@ +*.pyc diff --git a/tools/u-boot-tools/buildman/README b/tools/u-boot-tools/buildman/README new file mode 100644 index 0000000..5a709c6 --- /dev/null +++ b/tools/u-boot-tools/buildman/README @@ -0,0 +1,1187 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (c) 2013 The Chromium OS Authors. + +(Please read 'How to change from MAKEALL' if you are used to that tool) + +Quick-start +=========== + +If you just want to quickly set up buildman so you can build something (for +example Raspberry Pi 2): + + cd /path/to/u-boot + PATH=$PATH:`pwd`/tools/buildman + buildman --fetch-arch arm + buildman -k rpi_2 + ls ../current/rpi_2 + # u-boot.bin is the output image + + +What is this? +============= + +This tool handles building U-Boot to check that you have not broken it +with your patch series. It can build each individual commit and report +which boards fail on which commits, and which errors come up. It aims +to make full use of multi-processor machines. + +A key feature of buildman is its output summary, which allows warnings, +errors or image size increases in a particular commit or board to be +quickly identified and the offending commit pinpointed. This can be a big +help for anyone working with >10 patches at a time. + + +Caveats +======= + +Buildman can be stopped and restarted, in which case it will continue +where it left off. This should happen cleanly and without side-effects. +If not, it is a bug, for which a patch would be welcome. + +Buildman gets so tied up in its work that it can ignore the outside world. +You may need to press Ctrl-C several times to quit it. Also it will print +out various exceptions when stopped. You may have to kill it since the +Ctrl-C handling is somewhat broken. + + +Theory of Operation +=================== + +(please read this section in full twice or you will be perpetually confused) + +Buildman is a builder. It is not make, although it runs make. It does not +produce any useful output on the terminal while building, except for +progress information (except with -v, see below). All the output (errors, +warnings and binaries if you ask for them) is stored in output +directories, which you can look at while the build is progressing, or when +it is finished. + +Buildman is designed to build entire git branches, i.e. muliple commits. It +can be run repeatedly on the same branch. In this case it will automatically +rebuild commits which have changed (and remove its old results for that +commit). It is possible to build a branch for one board, then later build it +for another board. If you want buildman to re-build a commit it has already +built (e.g. because of a toolchain update), use the -f flag. + +Buildman produces a concise summary of which boards succeeded and failed. +It shows which commit introduced which board failure using a simple +red/green colour coding. Full error information can be requested, in which +case it is de-duped and displayed against the commit that introduced the +error. An example workflow is below. + +Buildman stores image size information and can report changes in image size +from commit to commit. An example of this is below. + +Buildman starts multiple threads, and each thread builds for one board at +a time. A thread starts at the first commit, configures the source for your +board and builds it. Then it checks out the next commit and does an +incremental build. Eventually the thread reaches the last commit and stops. +If errors or warnings are found along the way, the thread will reconfigure +after every commit, and your build will be very slow. This is because a +file that produces just a warning would not normally be rebuilt in an +incremental build. + +Buildman works in an entirely separate place from your U-Boot repository. +It creates a separate working directory for each thread, and puts the +output files in the working directory, organised by commit name and board +name, in a two-level hierarchy. + +Buildman is invoked in your U-Boot directory, the one with the .git +directory. It clones this repository into a copy for each thread, and the +threads do not affect the state of your git repository. Any checkouts done +by the thread affect only the working directory for that thread. + +Buildman automatically selects the correct tool chain for each board. You +must supply suitable tool chains, but buildman takes care of selecting the +right one. + +Buildman generally builds a branch (with the -b flag), and in this case +builds the upstream commit as well, for comparison. It cannot build +individual commits at present, unless (maybe) you point it at an empty +branch. Put all your commits in a branch, set the branch's upstream to a +valid value, and all will be well. Otherwise buildman will perform random +actions. Use -n to check what the random actions might be. + +If you just want to build the current source tree, leave off the -b flag +and add -e. This will display results and errors as they happen. You can +still look at them later using -se. Note that buildman will assume that the +source has changed, and will build all specified boards in this case. + +Buildman is optimised for building many commits at once, for many boards. +On multi-core machines, Buildman is fast because it uses most of the +available CPU power. When it gets to the end, or if you are building just +a few commits or boards, it will be pretty slow. As a tip, if you don't +plan to use your machine for anything else, you can use -T to increase the +number of threads beyond the default. + + +Selecting which boards to build +=============================== + +Buildman lets you build all boards, or a subset. Specify the subset by passing +command-line arguments that list the desired board name, architecture name, +SOC name, or anything else in the boards.cfg file. Multiple arguments are +allowed. Each argument will be interpreted as a regular expression, so +behaviour is a superset of exact or substring matching. Examples are: + +* 'tegra20' All boards with a Tegra20 SoC +* 'tegra' All boards with any Tegra Soc (Tegra20, Tegra30, Tegra114...) +* '^tegra[23]0$' All boards with either Tegra20 or Tegra30 SoC +* 'powerpc' All PowerPC boards + +While the default is to OR the terms together, you can also make use of +the '&' operator to limit the selection: + +* 'freescale & arm sandbox' All Freescale boards with ARM architecture, + plus sandbox + +You can also use -x to specifically exclude some boards. For example: + + buildmand arm -x nvidia,freescale,.*ball$ + +means to build all arm boards except nvidia, freescale and anything ending +with 'ball'. + +For building specific boards you can use the --boards option, which takes a +comma-separated list of board target names and be used multiple times on +the command line: + + buidman --boards sandbox,snow --boards + +It is convenient to use the -n option to see what will be built based on +the subset given. Use -v as well to get an actual list of boards. + +Buildman does not store intermediate object files. It optionally copies +the binary output into a directory when a build is successful (-k). Size +information is always recorded. It needs a fair bit of disk space to work, +typically 250MB per thread. + + +Setting up +========== + +1. Get the U-Boot source. You probably already have it, but if not these +steps should get you started with a repo and some commits for testing. + +$ cd /path/to/u-boot +$ git clone git://git.denx.de/u-boot.git . +$ git checkout -b my-branch origin/master +$ # Add some commits to the branch, reading for testing + +2. Create ~/.buildman to tell buildman where to find tool chains (see 'The +.buildman file' later for details). As an example: + +# Buildman settings file + +[toolchain] +root: / +rest: /toolchains/* +eldk: /opt/eldk-4.2 +arm: /opt/linaro/gcc-linaro-arm-linux-gnueabihf-4.8-2013.08_linux +aarch64: /opt/linaro/gcc-linaro-aarch64-none-elf-4.8-2013.10_linux + +[toolchain-alias] +x86: i386 +blackfin: bfin +nds32: nds32le +openrisc: or1k + + +This selects the available toolchain paths. Add the base directory for +each of your toolchains here. Buildman will search inside these directories +and also in any '/usr' and '/usr/bin' subdirectories. + +Make sure the tags (here root: rest: and eldk:) are unique. + +The toolchain-alias section indicates that the i386 toolchain should be used +to build x86 commits. + +Note that you can also specific exactly toolchain prefixes if you like: + +[toolchain-prefix] +arm: /opt/arm-eabi-4.6/bin/arm-eabi- + +or even: + +[toolchain-prefix] +arm: /opt/arm-eabi-4.6/bin/arm-eabi-gcc + +This tells buildman that you want to use this exact toolchain for the arm +architecture. This will override any toolchains found by searching using the +[toolchain] settings. + +Since the toolchain prefix is an explicit request, buildman will report an +error if a toolchain is not found with that prefix. The current PATH will be +searched, so it is possible to use: + +[toolchain-prefix] +arm: arm-none-eabi- + +and buildman will find arm-none-eabi-gcc in /usr/bin if you have it installed. + +[toolchain-wrapper] +wrapper: ccache + +This tells buildman to use a compiler wrapper in front of CROSS_COMPILE. In +this example, ccache. It doesn't affect the toolchain scan. The wrapper is +added when CROSS_COMPILE environtal variable is set. The name in this +section is ignored. If more than one line is provided, only the last one +is taken. + +3. Make sure you have the require Python pre-requisites + +Buildman uses multiprocessing, Queue, shutil, StringIO, ConfigParser and +urllib2. These should normally be available, but if you get an error like +this then you will need to obtain those modules: + + ImportError: No module named multiprocessing + + +4. Check the available toolchains + +Run this check to make sure that you have a toolchain for every architecture. + +$ ./tools/buildman/buildman --list-tool-chains +Scanning for tool chains + - scanning prefix '/opt/gcc-4.6.3-nolibc/x86_64-linux/bin/x86_64-linux-' +Tool chain test: OK, arch='x86', priority 1 + - scanning prefix '/opt/arm-eabi-4.6/bin/arm-eabi-' +Tool chain test: OK, arch='arm', priority 1 + - scanning path '/toolchains/gcc-4.9.0-nolibc/i386-linux' + - looking in '/toolchains/gcc-4.9.0-nolibc/i386-linux/.' + - looking in '/toolchains/gcc-4.9.0-nolibc/i386-linux/bin' + - found '/toolchains/gcc-4.9.0-nolibc/i386-linux/bin/i386-linux-gcc' + - looking in '/toolchains/gcc-4.9.0-nolibc/i386-linux/usr/bin' +Tool chain test: OK, arch='i386', priority 4 + - scanning path '/toolchains/gcc-4.9.0-nolibc/aarch64-linux' + - looking in '/toolchains/gcc-4.9.0-nolibc/aarch64-linux/.' + - looking in '/toolchains/gcc-4.9.0-nolibc/aarch64-linux/bin' + - found '/toolchains/gcc-4.9.0-nolibc/aarch64-linux/bin/aarch64-linux-gcc' + - looking in '/toolchains/gcc-4.9.0-nolibc/aarch64-linux/usr/bin' +Tool chain test: OK, arch='aarch64', priority 4 + - scanning path '/toolchains/gcc-4.9.0-nolibc/microblaze-linux' + - looking in '/toolchains/gcc-4.9.0-nolibc/microblaze-linux/.' + - looking in '/toolchains/gcc-4.9.0-nolibc/microblaze-linux/bin' + - found '/toolchains/gcc-4.9.0-nolibc/microblaze-linux/bin/microblaze-linux-gcc' + - looking in '/toolchains/gcc-4.9.0-nolibc/microblaze-linux/usr/bin' +Tool chain test: OK, arch='microblaze', priority 4 + - scanning path '/toolchains/gcc-4.9.0-nolibc/mips64-linux' + - looking in '/toolchains/gcc-4.9.0-nolibc/mips64-linux/.' + - looking in '/toolchains/gcc-4.9.0-nolibc/mips64-linux/bin' + - found '/toolchains/gcc-4.9.0-nolibc/mips64-linux/bin/mips64-linux-gcc' + - looking in '/toolchains/gcc-4.9.0-nolibc/mips64-linux/usr/bin' +Tool chain test: OK, arch='mips64', priority 4 + - scanning path '/toolchains/gcc-4.9.0-nolibc/sparc64-linux' + - looking in '/toolchains/gcc-4.9.0-nolibc/sparc64-linux/.' + - looking in '/toolchains/gcc-4.9.0-nolibc/sparc64-linux/bin' + - found '/toolchains/gcc-4.9.0-nolibc/sparc64-linux/bin/sparc64-linux-gcc' + - looking in '/toolchains/gcc-4.9.0-nolibc/sparc64-linux/usr/bin' +Tool chain test: OK, arch='sparc64', priority 4 + - scanning path '/toolchains/gcc-4.9.0-nolibc/arm-unknown-linux-gnueabi' + - looking in '/toolchains/gcc-4.9.0-nolibc/arm-unknown-linux-gnueabi/.' + - looking in '/toolchains/gcc-4.9.0-nolibc/arm-unknown-linux-gnueabi/bin' + - found '/toolchains/gcc-4.9.0-nolibc/arm-unknown-linux-gnueabi/bin/arm-unknown-linux-gnueabi-gcc' + - looking in '/toolchains/gcc-4.9.0-nolibc/arm-unknown-linux-gnueabi/usr/bin' +Tool chain test: OK, arch='arm', priority 3 +Toolchain '/toolchains/gcc-4.9.0-nolibc/arm-unknown-linux-gnueabi/bin/arm-unknown-linux-gnueabi-gcc' at priority 3 will be ignored because another toolchain for arch 'arm' has priority 1 + - scanning path '/toolchains/gcc-4.9.0-nolibc/sparc-linux' + - looking in '/toolchains/gcc-4.9.0-nolibc/sparc-linux/.' + - looking in '/toolchains/gcc-4.9.0-nolibc/sparc-linux/bin' + - found '/toolchains/gcc-4.9.0-nolibc/sparc-linux/bin/sparc-linux-gcc' + - looking in '/toolchains/gcc-4.9.0-nolibc/sparc-linux/usr/bin' +Tool chain test: OK, arch='sparc', priority 4 + - scanning path '/toolchains/gcc-4.9.0-nolibc/mips-linux' + - looking in '/toolchains/gcc-4.9.0-nolibc/mips-linux/.' + - looking in '/toolchains/gcc-4.9.0-nolibc/mips-linux/bin' + - found '/toolchains/gcc-4.9.0-nolibc/mips-linux/bin/mips-linux-gcc' + - looking in '/toolchains/gcc-4.9.0-nolibc/mips-linux/usr/bin' +Tool chain test: OK, arch='mips', priority 4 + - scanning path '/toolchains/gcc-4.9.0-nolibc/x86_64-linux' + - looking in '/toolchains/gcc-4.9.0-nolibc/x86_64-linux/.' + - looking in '/toolchains/gcc-4.9.0-nolibc/x86_64-linux/bin' + - found '/toolchains/gcc-4.9.0-nolibc/x86_64-linux/bin/x86_64-linux-gcc' + - found '/toolchains/gcc-4.9.0-nolibc/x86_64-linux/bin/x86_64-linux-x86_64-linux-gcc' + - looking in '/toolchains/gcc-4.9.0-nolibc/x86_64-linux/usr/bin' +Tool chain test: OK, arch='x86_64', priority 4 +Tool chain test: OK, arch='x86_64', priority 4 +Toolchain '/toolchains/gcc-4.9.0-nolibc/x86_64-linux/bin/x86_64-linux-x86_64-linux-gcc' at priority 4 will be ignored because another toolchain for arch 'x86_64' has priority 4 + - scanning path '/toolchains/gcc-4.9.0-nolibc/m68k-linux' + - looking in '/toolchains/gcc-4.9.0-nolibc/m68k-linux/.' + - looking in '/toolchains/gcc-4.9.0-nolibc/m68k-linux/bin' + - found '/toolchains/gcc-4.9.0-nolibc/m68k-linux/bin/m68k-linux-gcc' + - looking in '/toolchains/gcc-4.9.0-nolibc/m68k-linux/usr/bin' +Tool chain test: OK, arch='m68k', priority 4 + - scanning path '/toolchains/gcc-4.9.0-nolibc/powerpc-linux' + - looking in '/toolchains/gcc-4.9.0-nolibc/powerpc-linux/.' + - looking in '/toolchains/gcc-4.9.0-nolibc/powerpc-linux/bin' + - found '/toolchains/gcc-4.9.0-nolibc/powerpc-linux/bin/powerpc-linux-gcc' + - looking in '/toolchains/gcc-4.9.0-nolibc/powerpc-linux/usr/bin' +Tool chain test: OK, arch='powerpc', priority 4 + - scanning path '/toolchains/gcc-4.6.3-nolibc/bfin-uclinux' + - looking in '/toolchains/gcc-4.6.3-nolibc/bfin-uclinux/.' + - looking in '/toolchains/gcc-4.6.3-nolibc/bfin-uclinux/bin' + - found '/toolchains/gcc-4.6.3-nolibc/bfin-uclinux/bin/bfin-uclinux-gcc' + - looking in '/toolchains/gcc-4.6.3-nolibc/bfin-uclinux/usr/bin' +Tool chain test: OK, arch='bfin', priority 6 + - scanning path '/toolchains/gcc-4.6.3-nolibc/sparc-linux' + - looking in '/toolchains/gcc-4.6.3-nolibc/sparc-linux/.' + - looking in '/toolchains/gcc-4.6.3-nolibc/sparc-linux/bin' + - found '/toolchains/gcc-4.6.3-nolibc/sparc-linux/bin/sparc-linux-gcc' + - looking in '/toolchains/gcc-4.6.3-nolibc/sparc-linux/usr/bin' +Tool chain test: OK, arch='sparc', priority 4 +Toolchain '/toolchains/gcc-4.6.3-nolibc/sparc-linux/bin/sparc-linux-gcc' at priority 4 will be ignored because another toolchain for arch 'sparc' has priority 4 + - scanning path '/toolchains/gcc-4.6.3-nolibc/mips-linux' + - looking in '/toolchains/gcc-4.6.3-nolibc/mips-linux/.' + - looking in '/toolchains/gcc-4.6.3-nolibc/mips-linux/bin' + - found '/toolchains/gcc-4.6.3-nolibc/mips-linux/bin/mips-linux-gcc' + - looking in '/toolchains/gcc-4.6.3-nolibc/mips-linux/usr/bin' +Tool chain test: OK, arch='mips', priority 4 +Toolchain '/toolchains/gcc-4.6.3-nolibc/mips-linux/bin/mips-linux-gcc' at priority 4 will be ignored because another toolchain for arch 'mips' has priority 4 + - scanning path '/toolchains/gcc-4.6.3-nolibc/m68k-linux' + - looking in '/toolchains/gcc-4.6.3-nolibc/m68k-linux/.' + - looking in '/toolchains/gcc-4.6.3-nolibc/m68k-linux/bin' + - found '/toolchains/gcc-4.6.3-nolibc/m68k-linux/bin/m68k-linux-gcc' + - looking in '/toolchains/gcc-4.6.3-nolibc/m68k-linux/usr/bin' +Tool chain test: OK, arch='m68k', priority 4 +Toolchain '/toolchains/gcc-4.6.3-nolibc/m68k-linux/bin/m68k-linux-gcc' at priority 4 will be ignored because another toolchain for arch 'm68k' has priority 4 + - scanning path '/toolchains/gcc-4.6.3-nolibc/powerpc-linux' + - looking in '/toolchains/gcc-4.6.3-nolibc/powerpc-linux/.' + - looking in '/toolchains/gcc-4.6.3-nolibc/powerpc-linux/bin' + - found '/toolchains/gcc-4.6.3-nolibc/powerpc-linux/bin/powerpc-linux-gcc' + - looking in '/toolchains/gcc-4.6.3-nolibc/powerpc-linux/usr/bin' +Tool chain test: OK, arch='powerpc', priority 4 +Tool chain test: OK, arch='or32', priority 4 + - scanning path '/' + - looking in '/.' + - looking in '/bin' + - looking in '/usr/bin' + - found '/usr/bin/i586-mingw32msvc-gcc' + - found '/usr/bin/c89-gcc' + - found '/usr/bin/x86_64-linux-gnu-gcc' + - found '/usr/bin/gcc' + - found '/usr/bin/c99-gcc' + - found '/usr/bin/arm-linux-gnueabi-gcc' + - found '/usr/bin/aarch64-linux-gnu-gcc' + - found '/usr/bin/winegcc' + - found '/usr/bin/arm-linux-gnueabihf-gcc' +Tool chain test: OK, arch='i586', priority 11 +Tool chain test: OK, arch='c89', priority 11 +Tool chain test: OK, arch='x86_64', priority 4 +Toolchain '/usr/bin/x86_64-linux-gnu-gcc' at priority 4 will be ignored because another toolchain for arch 'x86_64' has priority 4 +Tool chain test: OK, arch='sandbox', priority 11 +Tool chain test: OK, arch='c99', priority 11 +Tool chain test: OK, arch='arm', priority 4 +Toolchain '/usr/bin/arm-linux-gnueabi-gcc' at priority 4 will be ignored because another toolchain for arch 'arm' has priority 1 +Tool chain test: OK, arch='aarch64', priority 4 +Toolchain '/usr/bin/aarch64-linux-gnu-gcc' at priority 4 will be ignored because another toolchain for arch 'aarch64' has priority 4 +Tool chain test: OK, arch='sandbox', priority 11 +Toolchain '/usr/bin/winegcc' at priority 11 will be ignored because another toolchain for arch 'sandbox' has priority 11 +Tool chain test: OK, arch='arm', priority 4 +Toolchain '/usr/bin/arm-linux-gnueabihf-gcc' at priority 4 will be ignored because another toolchain for arch 'arm' has priority 1 +List of available toolchains (34): +aarch64 : /toolchains/gcc-4.9.0-nolibc/aarch64-linux/bin/aarch64-linux-gcc +alpha : /toolchains/gcc-4.9.0-nolibc/alpha-linux/bin/alpha-linux-gcc +am33_2.0 : /toolchains/gcc-4.9.0-nolibc/am33_2.0-linux/bin/am33_2.0-linux-gcc +arm : /opt/arm-eabi-4.6/bin/arm-eabi-gcc +bfin : /toolchains/gcc-4.6.3-nolibc/bfin-uclinux/bin/bfin-uclinux-gcc +c89 : /usr/bin/c89-gcc +c99 : /usr/bin/c99-gcc +frv : /toolchains/gcc-4.9.0-nolibc/frv-linux/bin/frv-linux-gcc +h8300 : /toolchains/gcc-4.9.0-nolibc/h8300-elf/bin/h8300-elf-gcc +hppa : /toolchains/gcc-4.9.0-nolibc/hppa-linux/bin/hppa-linux-gcc +hppa64 : /toolchains/gcc-4.9.0-nolibc/hppa64-linux/bin/hppa64-linux-gcc +i386 : /toolchains/gcc-4.9.0-nolibc/i386-linux/bin/i386-linux-gcc +i586 : /usr/bin/i586-mingw32msvc-gcc +ia64 : /toolchains/gcc-4.9.0-nolibc/ia64-linux/bin/ia64-linux-gcc +m32r : /toolchains/gcc-4.9.0-nolibc/m32r-linux/bin/m32r-linux-gcc +m68k : /toolchains/gcc-4.9.0-nolibc/m68k-linux/bin/m68k-linux-gcc +microblaze: /toolchains/gcc-4.9.0-nolibc/microblaze-linux/bin/microblaze-linux-gcc +mips : /toolchains/gcc-4.9.0-nolibc/mips-linux/bin/mips-linux-gcc +mips64 : /toolchains/gcc-4.9.0-nolibc/mips64-linux/bin/mips64-linux-gcc +or32 : /toolchains/gcc-4.5.1-nolibc/or32-linux/bin/or32-linux-gcc +powerpc : /toolchains/gcc-4.9.0-nolibc/powerpc-linux/bin/powerpc-linux-gcc +powerpc64 : /toolchains/gcc-4.9.0-nolibc/powerpc64-linux/bin/powerpc64-linux-gcc +ppc64le : /toolchains/gcc-4.9.0-nolibc/ppc64le-linux/bin/ppc64le-linux-gcc +s390x : /toolchains/gcc-4.9.0-nolibc/s390x-linux/bin/s390x-linux-gcc +sandbox : /usr/bin/gcc +sh4 : /toolchains/gcc-4.6.3-nolibc/sh4-linux/bin/sh4-linux-gcc +sparc : /toolchains/gcc-4.9.0-nolibc/sparc-linux/bin/sparc-linux-gcc +sparc64 : /toolchains/gcc-4.9.0-nolibc/sparc64-linux/bin/sparc64-linux-gcc +tilegx : /toolchains/gcc-4.6.2-nolibc/tilegx-linux/bin/tilegx-linux-gcc +x86 : /opt/gcc-4.6.3-nolibc/x86_64-linux/bin/x86_64-linux-gcc +x86_64 : /toolchains/gcc-4.9.0-nolibc/x86_64-linux/bin/x86_64-linux-gcc + + +You can see that everything is covered, even some strange ones that won't +be used (c88 and c99). This is a feature. + + +5. Install new toolchains if needed + +You can download toolchains and update the [toolchain] section of the +settings file to find them. + +To make this easier, buildman can automatically download and install +toolchains from kernel.org. First list the available architectures: + +$ ./tools/buildman/buildman --fetch-arch list +Checking: https://www.kernel.org/pub/tools/crosstool/files/bin/x86_64/4.6.3/ +Checking: https://www.kernel.org/pub/tools/crosstool/files/bin/x86_64/4.6.2/ +Checking: https://www.kernel.org/pub/tools/crosstool/files/bin/x86_64/4.5.1/ +Checking: https://www.kernel.org/pub/tools/crosstool/files/bin/x86_64/4.2.4/ +Available architectures: alpha am33_2.0 arm bfin cris crisv32 frv h8300 +hppa hppa64 i386 ia64 m32r m68k mips mips64 or32 powerpc powerpc64 s390x sh4 +sparc sparc64 tilegx x86_64 xtensa + +Then pick one and download it: + +$ ./tools/buildman/buildman --fetch-arch or32 +Checking: https://www.kernel.org/pub/tools/crosstool/files/bin/x86_64/4.6.3/ +Checking: https://www.kernel.org/pub/tools/crosstool/files/bin/x86_64/4.6.2/ +Checking: https://www.kernel.org/pub/tools/crosstool/files/bin/x86_64/4.5.1/ +Downloading: https://www.kernel.org/pub/tools/crosstool/files/bin/x86_64/4.5.1//x86_64-gcc-4.5.1-nolibc_or32-linux.tar.xz +Unpacking to: /home/sjg/.buildman-toolchains +Testing + - looking in '/home/sjg/.buildman-toolchains/gcc-4.5.1-nolibc/or32-linux/.' + - looking in '/home/sjg/.buildman-toolchains/gcc-4.5.1-nolibc/or32-linux/bin' + - found '/home/sjg/.buildman-toolchains/gcc-4.5.1-nolibc/or32-linux/bin/or32-linux-gcc' +Tool chain test: OK + +Or download them all from kernel.org and move them to /toolchains directory, + +$ ./tools/buildman/buildman --fetch-arch all +$ sudo mkdir -p /toolchains +$ sudo mv ~/.buildman-toolchains/*/* /toolchains/ + +For those not available from kernel.org, download from the following links. + +arc: https://github.com/foss-for-synopsys-dwc-arc-processors/toolchain/releases/ + download/arc-2016.09-release/arc_gnu_2016.09_prebuilt_uclibc_le_archs_linux_install.tar.gz +blackfin: http://sourceforge.net/projects/adi-toolchain/files/ + blackfin-toolchain-elf-gcc-4.5-2014R1_45-RC2.x86_64.tar.bz2 +nds32: http://osdk.andestech.com/packages/ + nds32le-linux-glibc-v1.tgz +nios2: http://sourcery.mentor.com/public/gnu_toolchain/nios2-linux-gnu/ + sourceryg++-2015.11-27-nios2-linux-gnu-i686-pc-linux-gnu.tar.bz2 +sh: http://sourcery.mentor.com/public/gnu_toolchain/sh-linux-gnu/ + renesas-4.4-200-sh-linux-gnu-i686-pc-linux-gnu.tar.bz2 + +Note openrisc kernel.org toolchain is out of date. Download the latest one from +http://opencores.org/or1k/OpenRISC_GNU_tool_chain#Prebuilt_versions - eg: +ftp://ocuser:ocuser@openrisc.opencores.org/toolchain/gcc-or1k-elf-4.8.1-x86.tar.bz2. + +Buildman should now be set up to use your new toolchain. + +At the time of writing, U-Boot has these architectures: + + arc, arm, blackfin, m68k, microblaze, mips, nds32, nios2, openrisc + powerpc, sandbox, sh, sparc, x86 + +Of these, only arc and nds32 are not available at kernel.org.. + + +How to run it +============= + +First do a dry run using the -n flag: (replace <branch> with a real, local +branch with a valid upstream) + +$ ./tools/buildman/buildman -b <branch> -n + +If it can't detect the upstream branch, try checking out the branch, and +doing something like 'git branch --set-upstream-to upstream/master' +or something similar. Buildman will try to guess a suitable upstream branch +if it can't find one (you will see a message like" Guessing upstream as ...). + +As an example: + +Dry run, so not doing much. But I would do this: + +Building 18 commits for 1059 boards (4 threads, 1 job per thread) +Build directory: ../lcd9b + 5bb3505 Merge branch 'master' of git://git.denx.de/u-boot-arm + c18f1b4 tegra: Use const for pinmux_config_pingroup/table() + 2f043ae tegra: Add display support to funcmux + e349900 tegra: fdt: Add pwm binding and node + 424a5f0 tegra: fdt: Add LCD definitions for Tegra + 0636ccf tegra: Add support for PWM + a994fe7 tegra: Add SOC support for display/lcd + fcd7350 tegra: Add LCD driver + 4d46e9d tegra: Add LCD support to Nvidia boards + 991bd48 arm: Add control over cachability of memory regions + 54e8019 lcd: Add CONFIG_LCD_ALIGNMENT to select frame buffer alignment + d92aff7 lcd: Add support for flushing LCD fb from dcache after update + dbd0677 tegra: Align LCD frame buffer to section boundary + 0cff9b8 tegra: Support control of cache settings for LCD + 9c56900 tegra: fdt: Add LCD definitions for Seaboard + 5cc29db lcd: Add CONFIG_CONSOLE_SCROLL_LINES option to speed console + cac5a23 tegra: Enable display/lcd support on Seaboard + 49ff541 wip + +Total boards to build for each commit: 1059 + +This shows that it will build all 1059 boards, using 4 threads (because +we have a 4-core CPU). Each thread will run with -j1, meaning that each +make job will use a single CPU. The list of commits to be built helps you +confirm that things look about right. Notice that buildman has chosen a +'base' directory for you, immediately above your source tree. + +Buildman works entirely inside the base directory, here ../lcd9b, +creating a working directory for each thread, and creating output +directories for each commit and board. + + +Suggested Workflow +================== + +To run the build for real, take off the -n: + +$ ./tools/buildman/buildman -b <branch> + +Buildman will set up some working directories, and get started. After a +minute or so it will settle down to a steady pace, with a display like this: + +Building 18 commits for 1059 boards (4 threads, 1 job per thread) + 528 36 124 /19062 1:13:30 : SIMPC8313_SP + +This means that it is building 19062 board/commit combinations. So far it +has managed to successfully build 528. Another 36 have built with warnings, +and 124 more didn't build at all. Buildman expects to complete the process +in around an hour and a quarter. Use this time to buy a faster computer. + + +To find out how the build went, ask for a summary with -s. You can do this +either before the build completes (presumably in another terminal) or +afterwards. Let's work through an example of how this is used: + +$ ./tools/buildman/buildman -b lcd9b -s +... +01: Merge branch 'master' of git://git.denx.de/u-boot-arm + powerpc: + galaxy5200_LOWBOOT +02: tegra: Use const for pinmux_config_pingroup/table() +03: tegra: Add display support to funcmux +04: tegra: fdt: Add pwm binding and node +05: tegra: fdt: Add LCD definitions for Tegra +06: tegra: Add support for PWM +07: tegra: Add SOC support for display/lcd +08: tegra: Add LCD driver +09: tegra: Add LCD support to Nvidia boards +10: arm: Add control over cachability of memory regions +11: lcd: Add CONFIG_LCD_ALIGNMENT to select frame buffer alignment +12: lcd: Add support for flushing LCD fb from dcache after update + arm: + lubbock +13: tegra: Align LCD frame buffer to section boundary +14: tegra: Support control of cache settings for LCD +15: tegra: fdt: Add LCD definitions for Seaboard +16: lcd: Add CONFIG_CONSOLE_SCROLL_LINES option to speed console +17: tegra: Enable display/lcd support on Seaboard +18: wip + +This shows which commits have succeeded and which have failed. In this case +the build is still in progress so many boards are not built yet (use -u to +see which ones). But still we can see a few failures. The galaxy5200_LOWBOOT +never builds correctly. This could be a problem with our toolchain, or it +could be a bug in the upstream. The good news is that we probably don't need +to blame our commits. The bad news is that our commits are not tested on that +board. + +Commit 12 broke lubbock. That's what the '+ lubbock' means. The failure +is never fixed by a later commit, or you would see lubbock again, in green, +without the +. + +To see the actual error: + +$ ./tools/buildman/buildman -b <branch> -se lubbock +... +12: lcd: Add support for flushing LCD fb from dcache after update + arm: + lubbock ++common/libcommon.o: In function `lcd_sync': ++/u-boot/lcd9b/.bm-work/00/common/lcd.c:120: undefined reference to `flush_dcache_range' ++arm-none-linux-gnueabi-ld: BFD (Sourcery G++ Lite 2010q1-202) 2.19.51.20090709 assertion fail /scratch/julian/2010q1-release-linux-lite/obj/binutils-src-2010q1-202-arm-none-linux-gnueabi-i686-pc-linux-gnu/bfd/elf32-arm.c:12572 ++make: *** [/u-boot/lcd9b/.bm-work/00/build/u-boot] Error 139 +13: tegra: Align LCD frame buffer to section boundary +14: tegra: Support control of cache settings for LCD +15: tegra: fdt: Add LCD definitions for Seaboard +16: lcd: Add CONFIG_CONSOLE_SCROLL_LINES option to speed console +-/u-boot/lcd9b/.bm-work/00/common/lcd.c:120: undefined reference to `flush_dcache_range' ++/u-boot/lcd9b/.bm-work/00/common/lcd.c:125: undefined reference to `flush_dcache_range' +17: tegra: Enable display/lcd support on Seaboard +18: wip + +So the problem is in lcd.c, due to missing cache operations. This information +should be enough to work out what that commit is doing to break these +boards. (In this case pxa did not have cache operations defined). + +If you see error lines marked with '-', that means that the errors were fixed +by that commit. Sometimes commits can be in the wrong order, so that a +breakage is introduced for a few commits and fixed by later commits. This +shows up clearly with buildman. You can then reorder the commits and try +again. + +At commit 16, the error moves: you can see that the old error at line 120 +is fixed, but there is a new one at line 126. This is probably only because +we added some code and moved the broken line further down the file. + +If many boards have the same error, then -e will display the error only +once. This makes the output as concise as possible. To see which boards have +each error, use -l. So it is safe to omit the board name - you will not get +lots of repeated output for every board. + +Buildman tries to distinguish warnings from errors, and shows warning lines +separately with a 'w' prefix. + +The full build output in this case is available in: + +../lcd9b/12_of_18_gd92aff7_lcd--Add-support-for/lubbock/ + + done: Indicates the build was done, and holds the return code from make. + This is 0 for a good build, typically 2 for a failure. + + err: Output from stderr, if any. Errors and warnings appear here. + + log: Output from stdout. Normally there isn't any since buildman runs + in silent mode. Use -V to force a verbose build (this passes V=1 + to 'make') + + toolchain: Shows information about the toolchain used for the build. + + sizes: Shows image size information. + +It is possible to get the build binary output there also. Use the -k option +for this. In that case you will also see some output files, like: + + System.map toolchain u-boot u-boot.bin u-boot.map autoconf.mk + (also SPL versions u-boot-spl and u-boot-spl.bin if available) + + +Checking Image Sizes +==================== + +A key requirement for U-Boot is that you keep code/data size to a minimum. +Where a new feature increases this noticeably it should normally be put +behind a CONFIG flag so that boards can leave it disabled and keep the image +size more or less the same with each new release. + +To check the impact of your commits on image size, use -S. For example: + +$ ./tools/buildman/buildman -b us-x86 -sS +Summary of 10 commits for 1066 boards (4 threads, 1 job per thread) +01: MAKEALL: add support for per architecture toolchains +02: x86: Add function to get top of usable ram + x86: (for 1/3 boards) text -272.0 rodata +41.0 +03: x86: Add basic cache operations +04: x86: Permit bootstage and timer data to be used prior to relocation + x86: (for 1/3 boards) data +16.0 +05: x86: Add an __end symbol to signal the end of the U-Boot binary + x86: (for 1/3 boards) text +76.0 +06: x86: Rearrange the output input to remove BSS + x86: (for 1/3 boards) bss -2140.0 +07: x86: Support relocation of FDT on start-up + x86: + coreboot-x86 +08: x86: Add error checking to x86 relocation code +09: x86: Adjust link device tree include file +10: x86: Enable CONFIG_OF_CONTROL on coreboot + + +You can see that image size only changed on x86, which is good because this +series is not supposed to change any other board. From commit 7 onwards the +build fails so we don't get code size numbers. The numbers are fractional +because they are an average of all boards for that architecture. The +intention is to allow you to quickly find image size problems introduced by +your commits. + +Note that the 'text' region and 'rodata' are split out. You should add the +two together to get the total read-only size (reported as the first column +in the output from binutil's 'size' utility). + +A useful option is --step which lets you skip some commits. For example +--step 2 will show the image sizes for only every 2nd commit (so it will +compare the image sizes of the 1st, 3rd, 5th... commits). You can also use +--step 0 which will compare only the first and last commits. This is useful +for an overview of how your entire series affects code size. It will build +only the upstream commit and your final branch commit. + +You can also use -d to see a detailed size breakdown for each board. This +list is sorted in order from largest growth to largest reduction. + +It is even possible to go a little further with the -B option (--bloat). This +shows where U-Boot has bloated, breaking the size change down to the function +level. Example output is below: + +$ ./tools/buildman/buildman -b us-mem4 -sSdB +... +19: Roll crc32 into hash infrastructure + arm: (for 10/10 boards) all -143.4 bss +1.2 data -4.8 rodata -48.2 text -91.6 + paz00 : all +23 bss -4 rodata -29 text +56 + u-boot: add: 1/0, grow: 3/-2 bytes: 168/-104 (64) + function old new delta + hash_command 80 160 +80 + crc32_wd_buf - 56 +56 + ext4fs_read_file 540 568 +28 + insert_var_value_sub 688 692 +4 + run_list_real 1996 1992 -4 + do_mem_crc 168 68 -100 + trimslice : all -9 bss +16 rodata -29 text +4 + u-boot: add: 1/0, grow: 1/-3 bytes: 136/-124 (12) + function old new delta + hash_command 80 160 +80 + crc32_wd_buf - 56 +56 + ext4fs_iterate_dir 672 668 -4 + ext4fs_read_file 568 548 -20 + do_mem_crc 168 68 -100 + whistler : all -9 bss +16 rodata -29 text +4 + u-boot: add: 1/0, grow: 1/-3 bytes: 136/-124 (12) + function old new delta + hash_command 80 160 +80 + crc32_wd_buf - 56 +56 + ext4fs_iterate_dir 672 668 -4 + ext4fs_read_file 568 548 -20 + do_mem_crc 168 68 -100 + seaboard : all -9 bss -28 rodata -29 text +48 + u-boot: add: 1/0, grow: 3/-2 bytes: 160/-104 (56) + function old new delta + hash_command 80 160 +80 + crc32_wd_buf - 56 +56 + ext4fs_read_file 548 568 +20 + run_list_real 1996 2000 +4 + do_nandboot 760 756 -4 + do_mem_crc 168 68 -100 + colibri_t20 : all -9 rodata -29 text +20 + u-boot: add: 1/0, grow: 2/-3 bytes: 140/-112 (28) + function old new delta + hash_command 80 160 +80 + crc32_wd_buf - 56 +56 + read_abs_bbt 204 208 +4 + do_nandboot 760 756 -4 + ext4fs_read_file 576 568 -8 + do_mem_crc 168 68 -100 + ventana : all -37 bss -12 rodata -29 text +4 + u-boot: add: 1/0, grow: 1/-3 bytes: 136/-124 (12) + function old new delta + hash_command 80 160 +80 + crc32_wd_buf - 56 +56 + ext4fs_iterate_dir 672 668 -4 + ext4fs_read_file 568 548 -20 + do_mem_crc 168 68 -100 + harmony : all -37 bss -16 rodata -29 text +8 + u-boot: add: 1/0, grow: 2/-3 bytes: 140/-124 (16) + function old new delta + hash_command 80 160 +80 + crc32_wd_buf - 56 +56 + nand_write_oob_syndrome 428 432 +4 + ext4fs_iterate_dir 672 668 -4 + ext4fs_read_file 568 548 -20 + do_mem_crc 168 68 -100 + medcom-wide : all -417 bss +28 data -16 rodata -93 text -336 + u-boot: add: 1/-1, grow: 1/-2 bytes: 88/-376 (-288) + function old new delta + crc32_wd_buf - 56 +56 + do_fat_read_at 2872 2904 +32 + hash_algo 16 - -16 + do_mem_crc 168 68 -100 + hash_command 420 160 -260 + tec : all -449 bss -4 data -16 rodata -93 text -336 + u-boot: add: 1/-1, grow: 1/-2 bytes: 88/-376 (-288) + function old new delta + crc32_wd_buf - 56 +56 + do_fat_read_at 2872 2904 +32 + hash_algo 16 - -16 + do_mem_crc 168 68 -100 + hash_command 420 160 -260 + plutux : all -481 bss +16 data -16 rodata -93 text -388 + u-boot: add: 1/-1, grow: 1/-3 bytes: 68/-408 (-340) + function old new delta + crc32_wd_buf - 56 +56 + do_load_serial_bin 1688 1700 +12 + hash_algo 16 - -16 + do_fat_read_at 2904 2872 -32 + do_mem_crc 168 68 -100 + hash_command 420 160 -260 + powerpc: (for 5/5 boards) all +37.4 data -3.2 rodata -41.8 text +82.4 + MPC8610HPCD : all +55 rodata -29 text +84 + u-boot: add: 1/0, grow: 0/-1 bytes: 176/-96 (80) + function old new delta + hash_command - 176 +176 + do_mem_crc 184 88 -96 + MPC8641HPCN : all +55 rodata -29 text +84 + u-boot: add: 1/0, grow: 0/-1 bytes: 176/-96 (80) + function old new delta + hash_command - 176 +176 + do_mem_crc 184 88 -96 + MPC8641HPCN_36BIT: all +55 rodata -29 text +84 + u-boot: add: 1/0, grow: 0/-1 bytes: 176/-96 (80) + function old new delta + hash_command - 176 +176 + do_mem_crc 184 88 -96 + sbc8641d : all +55 rodata -29 text +84 + u-boot: add: 1/0, grow: 0/-1 bytes: 176/-96 (80) + function old new delta + hash_command - 176 +176 + do_mem_crc 184 88 -96 + xpedite517x : all -33 data -16 rodata -93 text +76 + u-boot: add: 1/-1, grow: 0/-1 bytes: 176/-112 (64) + function old new delta + hash_command - 176 +176 + hash_algo 16 - -16 + do_mem_crc 184 88 -96 +... + + +This shows that commit 19 has reduced codesize for arm slightly and increased +it for powerpc. This increase was offset in by reductions in rodata and +data/bss. + +Shown below the summary lines are the sizes for each board. Below each board +are the sizes for each function. This information starts with: + + add - number of functions added / removed + grow - number of functions which grew / shrunk + bytes - number of bytes of code added to / removed from all functions, + plus the total byte change in brackets + +The change seems to be that hash_command() has increased by more than the +do_mem_crc() function has decreased. The function sizes typically add up to +roughly the text area size, but note that every read-only section except +rodata is included in 'text', so the function total does not exactly +correspond. + +It is common when refactoring code for the rodata to decrease as the text size +increases, and vice versa. + + +The .buildman file +================== + +The .buildman file provides information about the available toolchains and +also allows build flags to be passed to 'make'. It consists of several +sections, with the section name in square brackets. Within each section are +a set of (tag, value) pairs. + +'[toolchain]' section + + This lists the available toolchains. The tag here doesn't matter, but + make sure it is unique. The value is the path to the toolchain. Buildman + will look in that path for a file ending in 'gcc'. It will then execute + it to check that it is a C compiler, passing only the --version flag to + it. If the return code is 0, buildman assumes that it is a valid C + compiler. It uses the first part of the name as the architecture and + strips off the last part when setting the CROSS_COMPILE environment + variable (parts are delimited with a hyphen). + + For example powerpc-linux-gcc will be noted as a toolchain for 'powerpc' + and CROSS_COMPILE will be set to powerpc-linux- when using it. + +'[toolchain-alias]' section + + This converts toolchain architecture names to U-Boot names. For example, + if an x86 toolchains is called i386-linux-gcc it will not normally be + used for architecture 'x86'. Adding 'x86: i386 x86_64' to this section + will tell buildman that the i386 and x86_64 toolchains can be used for + the x86 architecture. + +'[make-flags]' section + + U-Boot's build system supports a few flags (such as BUILD_TAG) which + affect the build product. These flags can be specified in the buildman + settings file. They can also be useful when building U-Boot against other + open source software. + + [make-flags] + at91-boards=ENABLE_AT91_TEST=1 + snapper9260=${at91-boards} BUILD_TAG=442 + snapper9g45=${at91-boards} BUILD_TAG=443 + + This will use 'make ENABLE_AT91_TEST=1 BUILD_TAG=442' for snapper9260 + and 'make ENABLE_AT91_TEST=1 BUILD_TAG=443' for snapper9g45. A special + variable ${target} is available to access the target name (snapper9260 + and snapper9g20 in this case). Variables are resolved recursively. Note + that variables can only contain the characters A-Z, a-z, 0-9, hyphen (-) + and underscore (_). + + It is expected that any variables added are dealt with in U-Boot's + config.mk file and documented in the README. + + Note that you can pass ad-hoc options to the build using environment + variables, for example: + + SOME_OPTION=1234 ./tools/buildman/buildman my_board + + +Quick Sanity Check +================== + +If you have made changes and want to do a quick sanity check of the +currently checked-out source, run buildman without the -b flag. This will +build the selected boards and display build status as it runs (i.e. -v is +enabled automatically). Use -e to see errors/warnings as well. + + +Building Ranges +=============== + +You can build a range of commits by specifying a range instead of a branch +when using the -b flag. For example: + + upstream/master..us-buildman + +will build commits in us-buildman that are not in upstream/master. + + +Building Faster +=============== + +By default, buildman executes 'make mrproper' prior to building the first +commit for each board. This causes everything to be built from scratch. If you +trust the build system's incremental build capabilities, you can pass the -I +flag to skip the 'make mproper' invocation, which will reduce the amount of +work 'make' does, and hence speed up the build. This flag will speed up any +buildman invocation, since it reduces the amount of work done on any build. + +One possible application of buildman is as part of a continual edit, build, +edit, build, ... cycle; repeatedly applying buildman to the same change or +series of changes while making small incremental modifications to the source +each time. This provides quick feedback regarding the correctness of recent +modifications. In this scenario, buildman's default choice of build directory +causes more build work to be performed than strictly necessary. + +By default, each buildman thread uses a single directory for all builds. When a +thread builds multiple boards, the configuration built in this directory will +cycle through various different configurations, one per board built by the +thread. Variations in the configuration will force a rebuild of affected source +files when a thread switches between boards. Ideally, such buildman-induced +rebuilds would not happen, thus allowing the build to operate as efficiently as +the build system and source changes allow. buildman's -P flag may be used to +enable this; -P causes each board to be built in a separate (board-specific) +directory, thus avoiding any buildman-induced configuration changes in any +build directory. + +U-Boot's build system embeds information such as a build timestamp into the +final binary. This information varies each time U-Boot is built. This causes +various files to be rebuilt even if no source changes are made, which in turn +requires that the final U-Boot binary be re-linked. This unnecessary work can +be avoided by turning off the timestamp feature. This can be achieved by +setting the SOURCE_DATE_EPOCH environment variable to 0. + +Combining all of these options together yields the command-line shown below. +This will provide the quickest possible feedback regarding the current content +of the source tree, thus allowing rapid tested evolution of the code. + + SOURCE_DATE_EPOCH=0 ./tools/buildman/buildman -I -P tegra + + +Checking configuration +====================== + +A common requirement when converting CONFIG options to Kconfig is to check +that the effective configuration has not changed due to the conversion. +Buildman supports this with the -K option, used after a build. This shows +differences in effective configuration between one commit and the next. + +For example: + + $ buildman -b kc4 -sK + ... + 43: Convert CONFIG_SPL_USBETH_SUPPORT to Kconfig + arm: + + u-boot.cfg: CONFIG_SPL_ENV_SUPPORT=1 CONFIG_SPL_NET_SUPPORT=1 + + u-boot-spl.cfg: CONFIG_SPL_MMC_SUPPORT=1 CONFIG_SPL_NAND_SUPPORT=1 + + all: CONFIG_SPL_ENV_SUPPORT=1 CONFIG_SPL_MMC_SUPPORT=1 CONFIG_SPL_NAND_SUPPORT=1 CONFIG_SPL_NET_SUPPORT=1 + am335x_evm_usbspl : + + u-boot.cfg: CONFIG_SPL_ENV_SUPPORT=1 CONFIG_SPL_NET_SUPPORT=1 + + u-boot-spl.cfg: CONFIG_SPL_MMC_SUPPORT=1 CONFIG_SPL_NAND_SUPPORT=1 + + all: CONFIG_SPL_ENV_SUPPORT=1 CONFIG_SPL_MMC_SUPPORT=1 CONFIG_SPL_NAND_SUPPORT=1 CONFIG_SPL_NET_SUPPORT=1 + 44: Convert CONFIG_SPL_USB_HOST_SUPPORT to Kconfig + ... + +This shows that commit 44 enabled three new options for the board +am335x_evm_usbspl which were not enabled in commit 43. There is also a +summary for 'arm' showing all the changes detected for that architecture. +In this case there is only one board with changes, so 'arm' output is the +same as 'am335x_evm_usbspl'/ + +The -K option uses the u-boot.cfg, spl/u-boot-spl.cfg and tpl/u-boot-tpl.cfg +files which are produced by a build. If all you want is to check the +configuration you can in fact avoid doing a full build, using -D. This tells +buildman to configuration U-Boot and create the .cfg files, but not actually +build the source. This is 5-10 times faster than doing a full build. + +By default buildman considers the follow two configuration methods +equivalent: + + #define CONFIG_SOME_OPTION + + CONFIG_SOME_OPTION=y + +The former would appear in a header filer and the latter in a defconfig +file. The achieve this, buildman considers 'y' to be '1' in configuration +variables. This avoids lots of useless output when converting a CONFIG +option to Kconfig. To disable this behaviour, use --squash-config-y. + + +Checking the environment +======================== + +When converting CONFIG options which manipulate the default environment, +a common requirement is to check that the default environment has not +changed due to the conversion. Buildman supports this with the -U option, +used after a build. This shows differences in the default environment +between one commit and the next. + +For example: + +$ buildman -b squash brppt1 -sU +boards.cfg is up to date. Nothing to do. +Summary of 2 commits for 3 boards (3 threads, 3 jobs per thread) +01: Migrate bootlimit to Kconfig +02: Squashed commit of the following: + c brppt1_mmc: altbootcmd=mmc dev 1; run mmcboot0; -> mmc dev 1; run mmcboot0 + c brppt1_spi: altbootcmd=mmc dev 1; run mmcboot0; -> mmc dev 1; run mmcboot0 + + brppt1_nand: altbootcmd=run usbscript + - brppt1_nand: altbootcmd=run usbscript +(no errors to report) + +This shows that commit 2 modified the value of 'altbootcmd' for 'brppt1_mmc' +and 'brppt1_spi', removing a trailing semicolon. 'brppt1_nand' gained an a +value for 'altbootcmd', but lost one for ' altbootcmd'. + +The -U option uses the u-boot.env files which are produced by a build. + +Other options +============= + +Buildman has various other command line options. Try --help to see them. + +When doing builds, Buildman's return code will reflect the overall result: + + 0 (success) No errors or warnings found + 128 Errors found + 129 Warnings found + + +How to change from MAKEALL +========================== + +Buildman includes most of the features of MAKEALL and is generally faster +and easier to use. In particular it builds entire branches: if a particular +commit introduces an error in a particular board, buildman can easily show +you this, even if a later commit fixes that error. + +The reasons to deprecate MAKEALL are: +- We don't want to maintain two build systems +- Buildman is typically faster +- Buildman has a lot more features + +But still, many people will be sad to lose MAKEALL. If you are used to +MAKEALL, here are a few pointers. + +First you need to set up your tool chains - see the 'Setting up' section +for details. Once you have your required toolchain(s) detected then you are +ready to go. + +To build the current source tree, run buildman without a -b flag: + + ./tools/buildman/buildman <list of things to build> + +This will build the current source tree for the given boards and display +the results and errors. + +However buildman usually works on entire branches, and for that you must +specify a board flag: + + ./tools/buildman/buildman -b <branch_name> <list of things to build> + +followed by (afterwards, or perhaps concurrently in another terminal): + + ./tools/buildman/buildman -b <branch_name> -s <list of things to build> + +to see the results of the build. Rather than showing you all the output, +buildman just shows a summary, with red indicating that a commit introduced +an error and green indicating that a commit fixed an error. Use the -e +flag to see the full errors and -l to see which boards caused which errors. + +If you really want to see build results as they happen, use -v when doing a +build (and -e to see the errors/warnings too). + +You don't need to stick around on that branch while buildman is running. It +checks out its own copy of the source code, so you can change branches, +add commits, etc. without affecting the build in progress. + +The <list of things to build> can include board names, architectures or the +like. There are no flags to disambiguate since ambiguities are rare. Using +the examples from MAKEALL: + +Examples: + - build all Power Architecture boards: + MAKEALL -a powerpc + MAKEALL --arch powerpc + MAKEALL powerpc + ** buildman -b <branch> powerpc + - build all PowerPC boards manufactured by vendor "esd": + MAKEALL -a powerpc -v esd + ** buildman -b <branch> esd + - build all PowerPC boards manufactured either by "keymile" or "siemens": + MAKEALL -a powerpc -v keymile -v siemens + ** buildman -b <branch> keymile siemens + - build all Freescale boards with MPC83xx CPUs, plus all 4xx boards: + MAKEALL -c mpc83xx -v freescale 4xx + ** buildman -b <branch> mpc83xx freescale 4xx + +Buildman automatically tries to use all the CPUs in your machine. If you +are building a lot of boards it will use one thread for every CPU core +it detects in your machine. This is like MAKEALL's BUILD_NBUILDS option. +You can use the -T flag to change the number of threads. If you are only +building a few boards, buildman will automatically run make with the -j +flag to increase the number of concurrent make tasks. It isn't normally +that helpful to fiddle with this option, but if you use the BUILD_NCPUS +option in MAKEALL then -j is the equivalent in buildman. + +Buildman puts its output in ../<branch_name> by default but you can change +this with the -o option. Buildman normally does out-of-tree builds: use -i +to disable that if you really want to. But be careful that once you have +used -i you pollute buildman's copies of the source tree, and you will need +to remove the build directory (normally ../<branch_name>) to run buildman +in normal mode (without -i). + +Buildman doesn't keep the output result normally, but use the -k option to +do this. + +Please read 'Theory of Operation' a few times as it will make a lot of +things clearer. + +Some options you might like are: + + -B shows which functions are growing/shrinking in which commit - great + for finding code bloat. + -S shows image sizes for each commit (just an overall summary) + -u shows boards that you haven't built yet + --step 0 will build just the upstream commit and the last commit of your + branch. This is often a quick sanity check that your branch doesn't + break anything. But note this does not check bisectability! + + +TODO +==== + +This has mostly be written in my spare time as a response to my difficulties +in testing large series of patches. Apart from tidying up there is quite a +bit of scope for improvement. Things like better error diffs and easier +access to log files. Also it would be nice if buildman could 'hunt' for +problems, perhaps by building a few boards for each arch, or checking +commits for changed files and building only boards which use those files. + +A specific problem to fix is that Ctrl-C does not exit buildman cleanly when +multiple builder threads are active. + +Credits +======= + +Thanks to Grant Grundler <grundler@chromium.org> for his ideas for improving +the build speed by building all commits for a board instead of the other +way around. + + +Simon Glass +sjg@chromium.org +Halloween 2012 +Updated 12-12-12 +Updated 23-02-13 diff --git a/tools/u-boot-tools/buildman/board.py b/tools/u-boot-tools/buildman/board.py new file mode 100644 index 0000000..2a1d021 --- /dev/null +++ b/tools/u-boot-tools/buildman/board.py @@ -0,0 +1,309 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (c) 2012 The Chromium OS Authors. + +import re + +class Expr: + """A single regular expression for matching boards to build""" + + def __init__(self, expr): + """Set up a new Expr object. + + Args: + expr: String cotaining regular expression to store + """ + self._expr = expr + self._re = re.compile(expr) + + def Matches(self, props): + """Check if any of the properties match the regular expression. + + Args: + props: List of properties to check + Returns: + True if any of the properties match the regular expression + """ + for prop in props: + if self._re.match(prop): + return True + return False + + def __str__(self): + return self._expr + +class Term: + """A list of expressions each of which must match with properties. + + This provides a list of 'AND' expressions, meaning that each must + match the board properties for that board to be built. + """ + def __init__(self): + self._expr_list = [] + self._board_count = 0 + + def AddExpr(self, expr): + """Add an Expr object to the list to check. + + Args: + expr: New Expr object to add to the list of those that must + match for a board to be built. + """ + self._expr_list.append(Expr(expr)) + + def __str__(self): + """Return some sort of useful string describing the term""" + return '&'.join([str(expr) for expr in self._expr_list]) + + def Matches(self, props): + """Check if any of the properties match this term + + Each of the expressions in the term is checked. All must match. + + Args: + props: List of properties to check + Returns: + True if all of the expressions in the Term match, else False + """ + for expr in self._expr_list: + if not expr.Matches(props): + return False + return True + +class Board: + """A particular board that we can build""" + def __init__(self, status, arch, cpu, soc, vendor, board_name, target, options): + """Create a new board type. + + Args: + status: define whether the board is 'Active' or 'Orphaned' + arch: Architecture name (e.g. arm) + cpu: Cpu name (e.g. arm1136) + soc: Name of SOC, or '' if none (e.g. mx31) + vendor: Name of vendor (e.g. armltd) + board_name: Name of board (e.g. integrator) + target: Target name (use make <target>_defconfig to configure) + options: board-specific options (e.g. integratorcp:CM1136) + """ + self.target = target + self.arch = arch + self.cpu = cpu + self.board_name = board_name + self.vendor = vendor + self.soc = soc + self.options = options + self.props = [self.target, self.arch, self.cpu, self.board_name, + self.vendor, self.soc, self.options] + self.build_it = False + + +class Boards: + """Manage a list of boards.""" + def __init__(self): + # Use a simple list here, sinc OrderedDict requires Python 2.7 + self._boards = [] + + def AddBoard(self, board): + """Add a new board to the list. + + The board's target member must not already exist in the board list. + + Args: + board: board to add + """ + self._boards.append(board) + + def ReadBoards(self, fname): + """Read a list of boards from a board file. + + Create a board object for each and add it to our _boards list. + + Args: + fname: Filename of boards.cfg file + """ + with open(fname, 'r') as fd: + for line in fd: + if line[0] == '#': + continue + fields = line.split() + if not fields: + continue + for upto in range(len(fields)): + if fields[upto] == '-': + fields[upto] = '' + while len(fields) < 8: + fields.append('') + if len(fields) > 8: + fields = fields[:8] + + board = Board(*fields) + self.AddBoard(board) + + + def GetList(self): + """Return a list of available boards. + + Returns: + List of Board objects + """ + return self._boards + + def GetDict(self): + """Build a dictionary containing all the boards. + + Returns: + Dictionary: + key is board.target + value is board + """ + board_dict = {} + for board in self._boards: + board_dict[board.target] = board + return board_dict + + def GetSelectedDict(self): + """Return a dictionary containing the selected boards + + Returns: + List of Board objects that are marked selected + """ + board_dict = {} + for board in self._boards: + if board.build_it: + board_dict[board.target] = board + return board_dict + + def GetSelected(self): + """Return a list of selected boards + + Returns: + List of Board objects that are marked selected + """ + return [board for board in self._boards if board.build_it] + + def GetSelectedNames(self): + """Return a list of selected boards + + Returns: + List of board names that are marked selected + """ + return [board.target for board in self._boards if board.build_it] + + def _BuildTerms(self, args): + """Convert command line arguments to a list of terms. + + This deals with parsing of the arguments. It handles the '&' + operator, which joins several expressions into a single Term. + + For example: + ['arm & freescale sandbox', 'tegra'] + + will produce 3 Terms containing expressions as follows: + arm, freescale + sandbox + tegra + + The first Term has two expressions, both of which must match for + a board to be selected. + + Args: + args: List of command line arguments + Returns: + A list of Term objects + """ + syms = [] + for arg in args: + for word in arg.split(): + sym_build = [] + for term in word.split('&'): + if term: + sym_build.append(term) + sym_build.append('&') + syms += sym_build[:-1] + terms = [] + term = None + oper = None + for sym in syms: + if sym == '&': + oper = sym + elif oper: + term.AddExpr(sym) + oper = None + else: + if term: + terms.append(term) + term = Term() + term.AddExpr(sym) + if term: + terms.append(term) + return terms + + def SelectBoards(self, args, exclude=[], boards=None): + """Mark boards selected based on args + + Normally either boards (an explicit list of boards) or args (a list of + terms to match against) is used. It is possible to specify both, in + which case they are additive. + + If boards and args are both empty, all boards are selected. + + Args: + args: List of strings specifying boards to include, either named, + or by their target, architecture, cpu, vendor or soc. If + empty, all boards are selected. + exclude: List of boards to exclude, regardless of 'args' + boards: List of boards to build + + Returns: + Tuple + Dictionary which holds the list of boards which were selected + due to each argument, arranged by argument. + List of errors found + """ + result = {} + warnings = [] + terms = self._BuildTerms(args) + + result['all'] = [] + for term in terms: + result[str(term)] = [] + + exclude_list = [] + for expr in exclude: + exclude_list.append(Expr(expr)) + + found = [] + for board in self._boards: + matching_term = None + build_it = False + if terms: + match = False + for term in terms: + if term.Matches(board.props): + matching_term = str(term) + build_it = True + break + elif boards: + if board.target in boards: + build_it = True + found.append(board.target) + else: + build_it = True + + # Check that it is not specifically excluded + for expr in exclude_list: + if expr.Matches(board.props): + build_it = False + break + + if build_it: + board.build_it = True + if matching_term: + result[matching_term].append(board.target) + result['all'].append(board.target) + + if boards: + remaining = set(boards) - set(found) + if remaining: + warnings.append('Boards not found: %s\n' % ', '.join(remaining)) + + return result, warnings diff --git a/tools/u-boot-tools/buildman/bsettings.py b/tools/u-boot-tools/buildman/bsettings.py new file mode 100644 index 0000000..03d7439 --- /dev/null +++ b/tools/u-boot-tools/buildman/bsettings.py @@ -0,0 +1,97 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (c) 2012 The Chromium OS Authors. + +import ConfigParser +import os +import StringIO + + +def Setup(fname=''): + """Set up the buildman settings module by reading config files + + Args: + config_fname: Config filename to read ('' for default) + """ + global settings + global config_fname + + settings = ConfigParser.SafeConfigParser() + if fname is not None: + config_fname = fname + if config_fname == '': + config_fname = '%s/.buildman' % os.getenv('HOME') + if not os.path.exists(config_fname): + print 'No config file found ~/.buildman\nCreating one...\n' + CreateBuildmanConfigFile(config_fname) + print 'To install tool chains, please use the --fetch-arch option' + if config_fname: + settings.read(config_fname) + +def AddFile(data): + settings.readfp(StringIO.StringIO(data)) + +def GetItems(section): + """Get the items from a section of the config. + + Args: + section: name of section to retrieve + + Returns: + List of (name, value) tuples for the section + """ + try: + return settings.items(section) + except ConfigParser.NoSectionError as e: + return [] + except: + raise + +def SetItem(section, tag, value): + """Set an item and write it back to the settings file""" + global settings + global config_fname + + settings.set(section, tag, value) + if config_fname is not None: + with open(config_fname, 'w') as fd: + settings.write(fd) + +def CreateBuildmanConfigFile(config_fname): + """Creates a new config file with no tool chain information. + + Args: + config_fname: Config filename to create + + Returns: + None + """ + try: + f = open(config_fname, 'w') + except IOError: + print "Couldn't create buildman config file '%s'\n" % config_fname + raise + + print >>f, '''[toolchain] +# name = path +# e.g. x86 = /opt/gcc-4.6.3-nolibc/x86_64-linux + +[toolchain-prefix] +# name = path to prefix +# e.g. x86 = /opt/gcc-4.6.3-nolibc/x86_64-linux/bin/x86_64-linux- + +[toolchain-alias] +# arch = alias +# Indicates which toolchain should be used to build for that arch +x86 = i386 +blackfin = bfin +nds32 = nds32le +openrisc = or1k + +[make-flags] +# Special flags to pass to 'make' for certain boards, e.g. to pass a test +# flag and build tag to snapper boards: +# snapper-boards=ENABLE_AT91_TEST=1 +# snapper9260=${snapper-boards} BUILD_TAG=442 +# snapper9g45=${snapper-boards} BUILD_TAG=443 +''' + f.close(); diff --git a/tools/u-boot-tools/buildman/builder.py b/tools/u-boot-tools/buildman/builder.py new file mode 100644 index 0000000..6a6c83b --- /dev/null +++ b/tools/u-boot-tools/buildman/builder.py @@ -0,0 +1,1582 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (c) 2013 The Chromium OS Authors. +# +# Bloat-o-meter code used here Copyright 2004 Matt Mackall <mpm@selenic.com> +# + +import collections +from datetime import datetime, timedelta +import glob +import os +import re +import Queue +import shutil +import signal +import string +import sys +import threading +import time + +import builderthread +import command +import gitutil +import terminal +from terminal import Print +import toolchain + + +""" +Theory of Operation + +Please see README for user documentation, and you should be familiar with +that before trying to make sense of this. + +Buildman works by keeping the machine as busy as possible, building different +commits for different boards on multiple CPUs at once. + +The source repo (self.git_dir) contains all the commits to be built. Each +thread works on a single board at a time. It checks out the first commit, +configures it for that board, then builds it. Then it checks out the next +commit and builds it (typically without re-configuring). When it runs out +of commits, it gets another job from the builder and starts again with that +board. + +Clearly the builder threads could work either way - they could check out a +commit and then built it for all boards. Using separate directories for each +commit/board pair they could leave their build product around afterwards +also. + +The intent behind building a single board for multiple commits, is to make +use of incremental builds. Since each commit is built incrementally from +the previous one, builds are faster. Reconfiguring for a different board +removes all intermediate object files. + +Many threads can be working at once, but each has its own working directory. +When a thread finishes a build, it puts the output files into a result +directory. + +The base directory used by buildman is normally '../<branch>', i.e. +a directory higher than the source repository and named after the branch +being built. + +Within the base directory, we have one subdirectory for each commit. Within +that is one subdirectory for each board. Within that is the build output for +that commit/board combination. + +Buildman also create working directories for each thread, in a .bm-work/ +subdirectory in the base dir. + +As an example, say we are building branch 'us-net' for boards 'sandbox' and +'seaboard', and say that us-net has two commits. We will have directories +like this: + +us-net/ base directory + 01_of_02_g4ed4ebc_net--Add-tftp-speed-/ + sandbox/ + u-boot.bin + seaboard/ + u-boot.bin + 02_of_02_g4ed4ebc_net--Check-tftp-comp/ + sandbox/ + u-boot.bin + seaboard/ + u-boot.bin + .bm-work/ + 00/ working directory for thread 0 (contains source checkout) + build/ build output + 01/ working directory for thread 1 + build/ build output + ... +u-boot/ source directory + .git/ repository +""" + +# Possible build outcomes +OUTCOME_OK, OUTCOME_WARNING, OUTCOME_ERROR, OUTCOME_UNKNOWN = range(4) + +# Translate a commit subject into a valid filename (and handle unicode) +trans_valid_chars = string.maketrans('/: ', '---') +trans_valid_chars = trans_valid_chars.decode('latin-1') + +BASE_CONFIG_FILENAMES = [ + 'u-boot.cfg', 'u-boot-spl.cfg', 'u-boot-tpl.cfg' +] + +EXTRA_CONFIG_FILENAMES = [ + '.config', '.config-spl', '.config-tpl', + 'autoconf.mk', 'autoconf-spl.mk', 'autoconf-tpl.mk', + 'autoconf.h', 'autoconf-spl.h','autoconf-tpl.h', +] + +class Config: + """Holds information about configuration settings for a board.""" + def __init__(self, config_filename, target): + self.target = target + self.config = {} + for fname in config_filename: + self.config[fname] = {} + + def Add(self, fname, key, value): + self.config[fname][key] = value + + def __hash__(self): + val = 0 + for fname in self.config: + for key, value in self.config[fname].iteritems(): + print key, value + val = val ^ hash(key) & hash(value) + return val + +class Environment: + """Holds information about environment variables for a board.""" + def __init__(self, target): + self.target = target + self.environment = {} + + def Add(self, key, value): + self.environment[key] = value + +class Builder: + """Class for building U-Boot for a particular commit. + + Public members: (many should ->private) + already_done: Number of builds already completed + base_dir: Base directory to use for builder + checkout: True to check out source, False to skip that step. + This is used for testing. + col: terminal.Color() object + count: Number of commits to build + do_make: Method to call to invoke Make + fail: Number of builds that failed due to error + force_build: Force building even if a build already exists + force_config_on_failure: If a commit fails for a board, disable + incremental building for the next commit we build for that + board, so that we will see all warnings/errors again. + force_build_failures: If a previously-built build (i.e. built on + a previous run of buildman) is marked as failed, rebuild it. + git_dir: Git directory containing source repository + last_line_len: Length of the last line we printed (used for erasing + it with new progress information) + num_jobs: Number of jobs to run at once (passed to make as -j) + num_threads: Number of builder threads to run + out_queue: Queue of results to process + re_make_err: Compiled regular expression for ignore_lines + queue: Queue of jobs to run + threads: List of active threads + toolchains: Toolchains object to use for building + upto: Current commit number we are building (0.count-1) + warned: Number of builds that produced at least one warning + force_reconfig: Reconfigure U-Boot on each comiit. This disables + incremental building, where buildman reconfigures on the first + commit for a baord, and then just does an incremental build for + the following commits. In fact buildman will reconfigure and + retry for any failing commits, so generally the only effect of + this option is to slow things down. + in_tree: Build U-Boot in-tree instead of specifying an output + directory separate from the source code. This option is really + only useful for testing in-tree builds. + + Private members: + _base_board_dict: Last-summarised Dict of boards + _base_err_lines: Last-summarised list of errors + _base_warn_lines: Last-summarised list of warnings + _build_period_us: Time taken for a single build (float object). + _complete_delay: Expected delay until completion (timedelta) + _next_delay_update: Next time we plan to display a progress update + (datatime) + _show_unknown: Show unknown boards (those not built) in summary + _timestamps: List of timestamps for the completion of the last + last _timestamp_count builds. Each is a datetime object. + _timestamp_count: Number of timestamps to keep in our list. + _working_dir: Base working directory containing all threads + """ + class Outcome: + """Records a build outcome for a single make invocation + + Public Members: + rc: Outcome value (OUTCOME_...) + err_lines: List of error lines or [] if none + sizes: Dictionary of image size information, keyed by filename + - Each value is itself a dictionary containing + values for 'text', 'data' and 'bss', being the integer + size in bytes of each section. + func_sizes: Dictionary keyed by filename - e.g. 'u-boot'. Each + value is itself a dictionary: + key: function name + value: Size of function in bytes + config: Dictionary keyed by filename - e.g. '.config'. Each + value is itself a dictionary: + key: config name + value: config value + environment: Dictionary keyed by environment variable, Each + value is the value of environment variable. + """ + def __init__(self, rc, err_lines, sizes, func_sizes, config, + environment): + self.rc = rc + self.err_lines = err_lines + self.sizes = sizes + self.func_sizes = func_sizes + self.config = config + self.environment = environment + + def __init__(self, toolchains, base_dir, git_dir, num_threads, num_jobs, + gnu_make='make', checkout=True, show_unknown=True, step=1, + no_subdirs=False, full_path=False, verbose_build=False, + incremental=False, per_board_out_dir=False, + config_only=False, squash_config_y=False, + warnings_as_errors=False): + """Create a new Builder object + + Args: + toolchains: Toolchains object to use for building + base_dir: Base directory to use for builder + git_dir: Git directory containing source repository + num_threads: Number of builder threads to run + num_jobs: Number of jobs to run at once (passed to make as -j) + gnu_make: the command name of GNU Make. + checkout: True to check out source, False to skip that step. + This is used for testing. + show_unknown: Show unknown boards (those not built) in summary + step: 1 to process every commit, n to process every nth commit + no_subdirs: Don't create subdirectories when building current + source for a single board + full_path: Return the full path in CROSS_COMPILE and don't set + PATH + verbose_build: Run build with V=1 and don't use 'make -s' + incremental: Always perform incremental builds; don't run make + mrproper when configuring + per_board_out_dir: Build in a separate persistent directory per + board rather than a thread-specific directory + config_only: Only configure each build, don't build it + squash_config_y: Convert CONFIG options with the value 'y' to '1' + warnings_as_errors: Treat all compiler warnings as errors + """ + self.toolchains = toolchains + self.base_dir = base_dir + self._working_dir = os.path.join(base_dir, '.bm-work') + self.threads = [] + self.do_make = self.Make + self.gnu_make = gnu_make + self.checkout = checkout + self.num_threads = num_threads + self.num_jobs = num_jobs + self.already_done = 0 + self.force_build = False + self.git_dir = git_dir + self._show_unknown = show_unknown + self._timestamp_count = 10 + self._build_period_us = None + self._complete_delay = None + self._next_delay_update = datetime.now() + self.force_config_on_failure = True + self.force_build_failures = False + self.force_reconfig = False + self._step = step + self.in_tree = False + self._error_lines = 0 + self.no_subdirs = no_subdirs + self.full_path = full_path + self.verbose_build = verbose_build + self.config_only = config_only + self.squash_config_y = squash_config_y + self.config_filenames = BASE_CONFIG_FILENAMES + if not self.squash_config_y: + self.config_filenames += EXTRA_CONFIG_FILENAMES + + self.warnings_as_errors = warnings_as_errors + self.col = terminal.Color() + + self._re_function = re.compile('(.*): In function.*') + self._re_files = re.compile('In file included from.*') + self._re_warning = re.compile('(.*):(\d*):(\d*): warning: .*') + self._re_dtb_warning = re.compile('(.*): Warning .*') + self._re_note = re.compile('(.*):(\d*):(\d*): note: this is the location of the previous.*') + + self.queue = Queue.Queue() + self.out_queue = Queue.Queue() + for i in range(self.num_threads): + t = builderthread.BuilderThread(self, i, incremental, + per_board_out_dir) + t.setDaemon(True) + t.start() + self.threads.append(t) + + self.last_line_len = 0 + t = builderthread.ResultThread(self) + t.setDaemon(True) + t.start() + self.threads.append(t) + + ignore_lines = ['(make.*Waiting for unfinished)', '(Segmentation fault)'] + self.re_make_err = re.compile('|'.join(ignore_lines)) + + # Handle existing graceful with SIGINT / Ctrl-C + signal.signal(signal.SIGINT, self.signal_handler) + + def __del__(self): + """Get rid of all threads created by the builder""" + for t in self.threads: + del t + + def signal_handler(self, signal, frame): + sys.exit(1) + + def SetDisplayOptions(self, show_errors=False, show_sizes=False, + show_detail=False, show_bloat=False, + list_error_boards=False, show_config=False, + show_environment=False): + """Setup display options for the builder. + + show_errors: True to show summarised error/warning info + show_sizes: Show size deltas + show_detail: Show detail for each board + show_bloat: Show detail for each function + list_error_boards: Show the boards which caused each error/warning + show_config: Show config deltas + show_environment: Show environment deltas + """ + self._show_errors = show_errors + self._show_sizes = show_sizes + self._show_detail = show_detail + self._show_bloat = show_bloat + self._list_error_boards = list_error_boards + self._show_config = show_config + self._show_environment = show_environment + + def _AddTimestamp(self): + """Add a new timestamp to the list and record the build period. + + The build period is the length of time taken to perform a single + build (one board, one commit). + """ + now = datetime.now() + self._timestamps.append(now) + count = len(self._timestamps) + delta = self._timestamps[-1] - self._timestamps[0] + seconds = delta.total_seconds() + + # If we have enough data, estimate build period (time taken for a + # single build) and therefore completion time. + if count > 1 and self._next_delay_update < now: + self._next_delay_update = now + timedelta(seconds=2) + if seconds > 0: + self._build_period = float(seconds) / count + todo = self.count - self.upto + self._complete_delay = timedelta(microseconds= + self._build_period * todo * 1000000) + # Round it + self._complete_delay -= timedelta( + microseconds=self._complete_delay.microseconds) + + if seconds > 60: + self._timestamps.popleft() + count -= 1 + + def ClearLine(self, length): + """Clear any characters on the current line + + Make way for a new line of length 'length', by outputting enough + spaces to clear out the old line. Then remember the new length for + next time. + + Args: + length: Length of new line, in characters + """ + if length < self.last_line_len: + Print(' ' * (self.last_line_len - length), newline=False) + Print('\r', newline=False) + self.last_line_len = length + sys.stdout.flush() + + def SelectCommit(self, commit, checkout=True): + """Checkout the selected commit for this build + """ + self.commit = commit + if checkout and self.checkout: + gitutil.Checkout(commit.hash) + + def Make(self, commit, brd, stage, cwd, *args, **kwargs): + """Run make + + Args: + commit: Commit object that is being built + brd: Board object that is being built + stage: Stage that we are at (mrproper, config, build) + cwd: Directory where make should be run + args: Arguments to pass to make + kwargs: Arguments to pass to command.RunPipe() + """ + cmd = [self.gnu_make] + list(args) + result = command.RunPipe([cmd], capture=True, capture_stderr=True, + cwd=cwd, raise_on_error=False, infile='/dev/null', **kwargs) + if self.verbose_build: + result.stdout = '%s\n' % (' '.join(cmd)) + result.stdout + result.combined = '%s\n' % (' '.join(cmd)) + result.combined + return result + + def ProcessResult(self, result): + """Process the result of a build, showing progress information + + Args: + result: A CommandResult object, which indicates the result for + a single build + """ + col = terminal.Color() + if result: + target = result.brd.target + + self.upto += 1 + if result.return_code != 0: + self.fail += 1 + elif result.stderr: + self.warned += 1 + if result.already_done: + self.already_done += 1 + if self._verbose: + Print('\r', newline=False) + self.ClearLine(0) + boards_selected = {target : result.brd} + self.ResetResultSummary(boards_selected) + self.ProduceResultSummary(result.commit_upto, self.commits, + boards_selected) + else: + target = '(starting)' + + # Display separate counts for ok, warned and fail + ok = self.upto - self.warned - self.fail + line = '\r' + self.col.Color(self.col.GREEN, '%5d' % ok) + line += self.col.Color(self.col.YELLOW, '%5d' % self.warned) + line += self.col.Color(self.col.RED, '%5d' % self.fail) + + name = ' /%-5d ' % self.count + + # Add our current completion time estimate + self._AddTimestamp() + if self._complete_delay: + name += '%s : ' % self._complete_delay + # When building all boards for a commit, we can print a commit + # progress message. + if result and result.commit_upto is None: + name += 'commit %2d/%-3d' % (self.commit_upto + 1, + self.commit_count) + + name += target + Print(line + name, newline=False) + length = 16 + len(name) + self.ClearLine(length) + + def _GetOutputDir(self, commit_upto): + """Get the name of the output directory for a commit number + + The output directory is typically .../<branch>/<commit>. + + Args: + commit_upto: Commit number to use (0..self.count-1) + """ + commit_dir = None + if self.commits: + commit = self.commits[commit_upto] + subject = commit.subject.translate(trans_valid_chars) + commit_dir = ('%02d_of_%02d_g%s_%s' % (commit_upto + 1, + self.commit_count, commit.hash, subject[:20])) + elif not self.no_subdirs: + commit_dir = 'current' + if not commit_dir: + return self.base_dir + return os.path.join(self.base_dir, commit_dir) + + def GetBuildDir(self, commit_upto, target): + """Get the name of the build directory for a commit number + + The build directory is typically .../<branch>/<commit>/<target>. + + Args: + commit_upto: Commit number to use (0..self.count-1) + target: Target name + """ + output_dir = self._GetOutputDir(commit_upto) + return os.path.join(output_dir, target) + + def GetDoneFile(self, commit_upto, target): + """Get the name of the done file for a commit number + + Args: + commit_upto: Commit number to use (0..self.count-1) + target: Target name + """ + return os.path.join(self.GetBuildDir(commit_upto, target), 'done') + + def GetSizesFile(self, commit_upto, target): + """Get the name of the sizes file for a commit number + + Args: + commit_upto: Commit number to use (0..self.count-1) + target: Target name + """ + return os.path.join(self.GetBuildDir(commit_upto, target), 'sizes') + + def GetFuncSizesFile(self, commit_upto, target, elf_fname): + """Get the name of the funcsizes file for a commit number and ELF file + + Args: + commit_upto: Commit number to use (0..self.count-1) + target: Target name + elf_fname: Filename of elf image + """ + return os.path.join(self.GetBuildDir(commit_upto, target), + '%s.sizes' % elf_fname.replace('/', '-')) + + def GetObjdumpFile(self, commit_upto, target, elf_fname): + """Get the name of the objdump file for a commit number and ELF file + + Args: + commit_upto: Commit number to use (0..self.count-1) + target: Target name + elf_fname: Filename of elf image + """ + return os.path.join(self.GetBuildDir(commit_upto, target), + '%s.objdump' % elf_fname.replace('/', '-')) + + def GetErrFile(self, commit_upto, target): + """Get the name of the err file for a commit number + + Args: + commit_upto: Commit number to use (0..self.count-1) + target: Target name + """ + output_dir = self.GetBuildDir(commit_upto, target) + return os.path.join(output_dir, 'err') + + def FilterErrors(self, lines): + """Filter out errors in which we have no interest + + We should probably use map(). + + Args: + lines: List of error lines, each a string + Returns: + New list with only interesting lines included + """ + out_lines = [] + for line in lines: + if not self.re_make_err.search(line): + out_lines.append(line) + return out_lines + + def ReadFuncSizes(self, fname, fd): + """Read function sizes from the output of 'nm' + + Args: + fd: File containing data to read + fname: Filename we are reading from (just for errors) + + Returns: + Dictionary containing size of each function in bytes, indexed by + function name. + """ + sym = {} + for line in fd.readlines(): + try: + size, type, name = line[:-1].split() + except: + Print("Invalid line in file '%s': '%s'" % (fname, line[:-1])) + continue + if type in 'tTdDbB': + # function names begin with '.' on 64-bit powerpc + if '.' in name[1:]: + name = 'static.' + name.split('.')[0] + sym[name] = sym.get(name, 0) + int(size, 16) + return sym + + def _ProcessConfig(self, fname): + """Read in a .config, autoconf.mk or autoconf.h file + + This function handles all config file types. It ignores comments and + any #defines which don't start with CONFIG_. + + Args: + fname: Filename to read + + Returns: + Dictionary: + key: Config name (e.g. CONFIG_DM) + value: Config value (e.g. 1) + """ + config = {} + if os.path.exists(fname): + with open(fname) as fd: + for line in fd: + line = line.strip() + if line.startswith('#define'): + values = line[8:].split(' ', 1) + if len(values) > 1: + key, value = values + else: + key = values[0] + value = '1' if self.squash_config_y else '' + if not key.startswith('CONFIG_'): + continue + elif not line or line[0] in ['#', '*', '/']: + continue + else: + key, value = line.split('=', 1) + if self.squash_config_y and value == 'y': + value = '1' + config[key] = value + return config + + def _ProcessEnvironment(self, fname): + """Read in a uboot.env file + + This function reads in environment variables from a file. + + Args: + fname: Filename to read + + Returns: + Dictionary: + key: environment variable (e.g. bootlimit) + value: value of environment variable (e.g. 1) + """ + environment = {} + if os.path.exists(fname): + with open(fname) as fd: + for line in fd.read().split('\0'): + try: + key, value = line.split('=', 1) + environment[key] = value + except ValueError: + # ignore lines we can't parse + pass + return environment + + def GetBuildOutcome(self, commit_upto, target, read_func_sizes, + read_config, read_environment): + """Work out the outcome of a build. + + Args: + commit_upto: Commit number to check (0..n-1) + target: Target board to check + read_func_sizes: True to read function size information + read_config: True to read .config and autoconf.h files + read_environment: True to read uboot.env files + + Returns: + Outcome object + """ + done_file = self.GetDoneFile(commit_upto, target) + sizes_file = self.GetSizesFile(commit_upto, target) + sizes = {} + func_sizes = {} + config = {} + environment = {} + if os.path.exists(done_file): + with open(done_file, 'r') as fd: + return_code = int(fd.readline()) + err_lines = [] + err_file = self.GetErrFile(commit_upto, target) + if os.path.exists(err_file): + with open(err_file, 'r') as fd: + err_lines = self.FilterErrors(fd.readlines()) + + # Decide whether the build was ok, failed or created warnings + if return_code: + rc = OUTCOME_ERROR + elif len(err_lines): + rc = OUTCOME_WARNING + else: + rc = OUTCOME_OK + + # Convert size information to our simple format + if os.path.exists(sizes_file): + with open(sizes_file, 'r') as fd: + for line in fd.readlines(): + values = line.split() + rodata = 0 + if len(values) > 6: + rodata = int(values[6], 16) + size_dict = { + 'all' : int(values[0]) + int(values[1]) + + int(values[2]), + 'text' : int(values[0]) - rodata, + 'data' : int(values[1]), + 'bss' : int(values[2]), + 'rodata' : rodata, + } + sizes[values[5]] = size_dict + + if read_func_sizes: + pattern = self.GetFuncSizesFile(commit_upto, target, '*') + for fname in glob.glob(pattern): + with open(fname, 'r') as fd: + dict_name = os.path.basename(fname).replace('.sizes', + '') + func_sizes[dict_name] = self.ReadFuncSizes(fname, fd) + + if read_config: + output_dir = self.GetBuildDir(commit_upto, target) + for name in self.config_filenames: + fname = os.path.join(output_dir, name) + config[name] = self._ProcessConfig(fname) + + if read_environment: + output_dir = self.GetBuildDir(commit_upto, target) + fname = os.path.join(output_dir, 'uboot.env') + environment = self._ProcessEnvironment(fname) + + return Builder.Outcome(rc, err_lines, sizes, func_sizes, config, + environment) + + return Builder.Outcome(OUTCOME_UNKNOWN, [], {}, {}, {}, {}) + + def GetResultSummary(self, boards_selected, commit_upto, read_func_sizes, + read_config, read_environment): + """Calculate a summary of the results of building a commit. + + Args: + board_selected: Dict containing boards to summarise + commit_upto: Commit number to summarize (0..self.count-1) + read_func_sizes: True to read function size information + read_config: True to read .config and autoconf.h files + read_environment: True to read uboot.env files + + Returns: + Tuple: + Dict containing boards which passed building this commit. + keyed by board.target + List containing a summary of error lines + Dict keyed by error line, containing a list of the Board + objects with that error + List containing a summary of warning lines + Dict keyed by error line, containing a list of the Board + objects with that warning + Dictionary keyed by board.target. Each value is a dictionary: + key: filename - e.g. '.config' + value is itself a dictionary: + key: config name + value: config value + Dictionary keyed by board.target. Each value is a dictionary: + key: environment variable + value: value of environment variable + """ + def AddLine(lines_summary, lines_boards, line, board): + line = line.rstrip() + if line in lines_boards: + lines_boards[line].append(board) + else: + lines_boards[line] = [board] + lines_summary.append(line) + + board_dict = {} + err_lines_summary = [] + err_lines_boards = {} + warn_lines_summary = [] + warn_lines_boards = {} + config = {} + environment = {} + + for board in boards_selected.itervalues(): + outcome = self.GetBuildOutcome(commit_upto, board.target, + read_func_sizes, read_config, + read_environment) + board_dict[board.target] = outcome + last_func = None + last_was_warning = False + for line in outcome.err_lines: + if line: + if (self._re_function.match(line) or + self._re_files.match(line)): + last_func = line + else: + is_warning = (self._re_warning.match(line) or + self._re_dtb_warning.match(line)) + is_note = self._re_note.match(line) + if is_warning or (last_was_warning and is_note): + if last_func: + AddLine(warn_lines_summary, warn_lines_boards, + last_func, board) + AddLine(warn_lines_summary, warn_lines_boards, + line, board) + else: + if last_func: + AddLine(err_lines_summary, err_lines_boards, + last_func, board) + AddLine(err_lines_summary, err_lines_boards, + line, board) + last_was_warning = is_warning + last_func = None + tconfig = Config(self.config_filenames, board.target) + for fname in self.config_filenames: + if outcome.config: + for key, value in outcome.config[fname].iteritems(): + tconfig.Add(fname, key, value) + config[board.target] = tconfig + + tenvironment = Environment(board.target) + if outcome.environment: + for key, value in outcome.environment.iteritems(): + tenvironment.Add(key, value) + environment[board.target] = tenvironment + + return (board_dict, err_lines_summary, err_lines_boards, + warn_lines_summary, warn_lines_boards, config, environment) + + def AddOutcome(self, board_dict, arch_list, changes, char, color): + """Add an output to our list of outcomes for each architecture + + This simple function adds failing boards (changes) to the + relevant architecture string, so we can print the results out + sorted by architecture. + + Args: + board_dict: Dict containing all boards + arch_list: Dict keyed by arch name. Value is a string containing + a list of board names which failed for that arch. + changes: List of boards to add to arch_list + color: terminal.Colour object + """ + done_arch = {} + for target in changes: + if target in board_dict: + arch = board_dict[target].arch + else: + arch = 'unknown' + str = self.col.Color(color, ' ' + target) + if not arch in done_arch: + str = ' %s %s' % (self.col.Color(color, char), str) + done_arch[arch] = True + if not arch in arch_list: + arch_list[arch] = str + else: + arch_list[arch] += str + + + def ColourNum(self, num): + color = self.col.RED if num > 0 else self.col.GREEN + if num == 0: + return '0' + return self.col.Color(color, str(num)) + + def ResetResultSummary(self, board_selected): + """Reset the results summary ready for use. + + Set up the base board list to be all those selected, and set the + error lines to empty. + + Following this, calls to PrintResultSummary() will use this + information to work out what has changed. + + Args: + board_selected: Dict containing boards to summarise, keyed by + board.target + """ + self._base_board_dict = {} + for board in board_selected: + self._base_board_dict[board] = Builder.Outcome(0, [], [], {}, {}, + {}) + self._base_err_lines = [] + self._base_warn_lines = [] + self._base_err_line_boards = {} + self._base_warn_line_boards = {} + self._base_config = None + self._base_environment = None + + def PrintFuncSizeDetail(self, fname, old, new): + grow, shrink, add, remove, up, down = 0, 0, 0, 0, 0, 0 + delta, common = [], {} + + for a in old: + if a in new: + common[a] = 1 + + for name in old: + if name not in common: + remove += 1 + down += old[name] + delta.append([-old[name], name]) + + for name in new: + if name not in common: + add += 1 + up += new[name] + delta.append([new[name], name]) + + for name in common: + diff = new.get(name, 0) - old.get(name, 0) + if diff > 0: + grow, up = grow + 1, up + diff + elif diff < 0: + shrink, down = shrink + 1, down - diff + delta.append([diff, name]) + + delta.sort() + delta.reverse() + + args = [add, -remove, grow, -shrink, up, -down, up - down] + if max(args) == 0 and min(args) == 0: + return + args = [self.ColourNum(x) for x in args] + indent = ' ' * 15 + Print('%s%s: add: %s/%s, grow: %s/%s bytes: %s/%s (%s)' % + tuple([indent, self.col.Color(self.col.YELLOW, fname)] + args)) + Print('%s %-38s %7s %7s %+7s' % (indent, 'function', 'old', 'new', + 'delta')) + for diff, name in delta: + if diff: + color = self.col.RED if diff > 0 else self.col.GREEN + msg = '%s %-38s %7s %7s %+7d' % (indent, name, + old.get(name, '-'), new.get(name,'-'), diff) + Print(msg, colour=color) + + + def PrintSizeDetail(self, target_list, show_bloat): + """Show details size information for each board + + Args: + target_list: List of targets, each a dict containing: + 'target': Target name + 'total_diff': Total difference in bytes across all areas + <part_name>: Difference for that part + show_bloat: Show detail for each function + """ + targets_by_diff = sorted(target_list, reverse=True, + key=lambda x: x['_total_diff']) + for result in targets_by_diff: + printed_target = False + for name in sorted(result): + diff = result[name] + if name.startswith('_'): + continue + if diff != 0: + color = self.col.RED if diff > 0 else self.col.GREEN + msg = ' %s %+d' % (name, diff) + if not printed_target: + Print('%10s %-15s:' % ('', result['_target']), + newline=False) + printed_target = True + Print(msg, colour=color, newline=False) + if printed_target: + Print() + if show_bloat: + target = result['_target'] + outcome = result['_outcome'] + base_outcome = self._base_board_dict[target] + for fname in outcome.func_sizes: + self.PrintFuncSizeDetail(fname, + base_outcome.func_sizes[fname], + outcome.func_sizes[fname]) + + + def PrintSizeSummary(self, board_selected, board_dict, show_detail, + show_bloat): + """Print a summary of image sizes broken down by section. + + The summary takes the form of one line per architecture. The + line contains deltas for each of the sections (+ means the section + got bigger, - means smaller). The nunmbers are the average number + of bytes that a board in this section increased by. + + For example: + powerpc: (622 boards) text -0.0 + arm: (285 boards) text -0.0 + nds32: (3 boards) text -8.0 + + Args: + board_selected: Dict containing boards to summarise, keyed by + board.target + board_dict: Dict containing boards for which we built this + commit, keyed by board.target. The value is an Outcome object. + show_detail: Show detail for each board + show_bloat: Show detail for each function + """ + arch_list = {} + arch_count = {} + + # Calculate changes in size for different image parts + # The previous sizes are in Board.sizes, for each board + for target in board_dict: + if target not in board_selected: + continue + base_sizes = self._base_board_dict[target].sizes + outcome = board_dict[target] + sizes = outcome.sizes + + # Loop through the list of images, creating a dict of size + # changes for each image/part. We end up with something like + # {'target' : 'snapper9g45, 'data' : 5, 'u-boot-spl:text' : -4} + # which means that U-Boot data increased by 5 bytes and SPL + # text decreased by 4. + err = {'_target' : target} + for image in sizes: + if image in base_sizes: + base_image = base_sizes[image] + # Loop through the text, data, bss parts + for part in sorted(sizes[image]): + diff = sizes[image][part] - base_image[part] + col = None + if diff: + if image == 'u-boot': + name = part + else: + name = image + ':' + part + err[name] = diff + arch = board_selected[target].arch + if not arch in arch_count: + arch_count[arch] = 1 + else: + arch_count[arch] += 1 + if not sizes: + pass # Only add to our list when we have some stats + elif not arch in arch_list: + arch_list[arch] = [err] + else: + arch_list[arch].append(err) + + # We now have a list of image size changes sorted by arch + # Print out a summary of these + for arch, target_list in arch_list.iteritems(): + # Get total difference for each type + totals = {} + for result in target_list: + total = 0 + for name, diff in result.iteritems(): + if name.startswith('_'): + continue + total += diff + if name in totals: + totals[name] += diff + else: + totals[name] = diff + result['_total_diff'] = total + result['_outcome'] = board_dict[result['_target']] + + count = len(target_list) + printed_arch = False + for name in sorted(totals): + diff = totals[name] + if diff: + # Display the average difference in this name for this + # architecture + avg_diff = float(diff) / count + color = self.col.RED if avg_diff > 0 else self.col.GREEN + msg = ' %s %+1.1f' % (name, avg_diff) + if not printed_arch: + Print('%10s: (for %d/%d boards)' % (arch, count, + arch_count[arch]), newline=False) + printed_arch = True + Print(msg, colour=color, newline=False) + + if printed_arch: + Print() + if show_detail: + self.PrintSizeDetail(target_list, show_bloat) + + + def PrintResultSummary(self, board_selected, board_dict, err_lines, + err_line_boards, warn_lines, warn_line_boards, + config, environment, show_sizes, show_detail, + show_bloat, show_config, show_environment): + """Compare results with the base results and display delta. + + Only boards mentioned in board_selected will be considered. This + function is intended to be called repeatedly with the results of + each commit. It therefore shows a 'diff' between what it saw in + the last call and what it sees now. + + Args: + board_selected: Dict containing boards to summarise, keyed by + board.target + board_dict: Dict containing boards for which we built this + commit, keyed by board.target. The value is an Outcome object. + err_lines: A list of errors for this commit, or [] if there is + none, or we don't want to print errors + err_line_boards: Dict keyed by error line, containing a list of + the Board objects with that error + warn_lines: A list of warnings for this commit, or [] if there is + none, or we don't want to print errors + warn_line_boards: Dict keyed by warning line, containing a list of + the Board objects with that warning + config: Dictionary keyed by filename - e.g. '.config'. Each + value is itself a dictionary: + key: config name + value: config value + environment: Dictionary keyed by environment variable, Each + value is the value of environment variable. + show_sizes: Show image size deltas + show_detail: Show detail for each board + show_bloat: Show detail for each function + show_config: Show config changes + show_environment: Show environment changes + """ + def _BoardList(line, line_boards): + """Helper function to get a line of boards containing a line + + Args: + line: Error line to search for + Return: + String containing a list of boards with that error line, or + '' if the user has not requested such a list + """ + if self._list_error_boards: + names = [] + for board in line_boards[line]: + if not board.target in names: + names.append(board.target) + names_str = '(%s) ' % ','.join(names) + else: + names_str = '' + return names_str + + def _CalcErrorDelta(base_lines, base_line_boards, lines, line_boards, + char): + better_lines = [] + worse_lines = [] + for line in lines: + if line not in base_lines: + worse_lines.append(char + '+' + + _BoardList(line, line_boards) + line) + for line in base_lines: + if line not in lines: + better_lines.append(char + '-' + + _BoardList(line, base_line_boards) + line) + return better_lines, worse_lines + + def _CalcConfig(delta, name, config): + """Calculate configuration changes + + Args: + delta: Type of the delta, e.g. '+' + name: name of the file which changed (e.g. .config) + config: configuration change dictionary + key: config name + value: config value + Returns: + String containing the configuration changes which can be + printed + """ + out = '' + for key in sorted(config.keys()): + out += '%s=%s ' % (key, config[key]) + return '%s %s: %s' % (delta, name, out) + + def _AddConfig(lines, name, config_plus, config_minus, config_change): + """Add changes in configuration to a list + + Args: + lines: list to add to + name: config file name + config_plus: configurations added, dictionary + key: config name + value: config value + config_minus: configurations removed, dictionary + key: config name + value: config value + config_change: configurations changed, dictionary + key: config name + value: config value + """ + if config_plus: + lines.append(_CalcConfig('+', name, config_plus)) + if config_minus: + lines.append(_CalcConfig('-', name, config_minus)) + if config_change: + lines.append(_CalcConfig('c', name, config_change)) + + def _OutputConfigInfo(lines): + for line in lines: + if not line: + continue + if line[0] == '+': + col = self.col.GREEN + elif line[0] == '-': + col = self.col.RED + elif line[0] == 'c': + col = self.col.YELLOW + Print(' ' + line, newline=True, colour=col) + + + ok_boards = [] # List of boards fixed since last commit + warn_boards = [] # List of boards with warnings since last commit + err_boards = [] # List of new broken boards since last commit + new_boards = [] # List of boards that didn't exist last time + unknown_boards = [] # List of boards that were not built + + for target in board_dict: + if target not in board_selected: + continue + + # If the board was built last time, add its outcome to a list + if target in self._base_board_dict: + base_outcome = self._base_board_dict[target].rc + outcome = board_dict[target] + if outcome.rc == OUTCOME_UNKNOWN: + unknown_boards.append(target) + elif outcome.rc < base_outcome: + if outcome.rc == OUTCOME_WARNING: + warn_boards.append(target) + else: + ok_boards.append(target) + elif outcome.rc > base_outcome: + if outcome.rc == OUTCOME_WARNING: + warn_boards.append(target) + else: + err_boards.append(target) + else: + new_boards.append(target) + + # Get a list of errors that have appeared, and disappeared + better_err, worse_err = _CalcErrorDelta(self._base_err_lines, + self._base_err_line_boards, err_lines, err_line_boards, '') + better_warn, worse_warn = _CalcErrorDelta(self._base_warn_lines, + self._base_warn_line_boards, warn_lines, warn_line_boards, 'w') + + # Display results by arch + if any((ok_boards, warn_boards, err_boards, unknown_boards, new_boards, + worse_err, better_err, worse_warn, better_warn)): + arch_list = {} + self.AddOutcome(board_selected, arch_list, ok_boards, '', + self.col.GREEN) + self.AddOutcome(board_selected, arch_list, warn_boards, 'w+', + self.col.YELLOW) + self.AddOutcome(board_selected, arch_list, err_boards, '+', + self.col.RED) + self.AddOutcome(board_selected, arch_list, new_boards, '*', self.col.BLUE) + if self._show_unknown: + self.AddOutcome(board_selected, arch_list, unknown_boards, '?', + self.col.MAGENTA) + for arch, target_list in arch_list.iteritems(): + Print('%10s: %s' % (arch, target_list)) + self._error_lines += 1 + if better_err: + Print('\n'.join(better_err), colour=self.col.GREEN) + self._error_lines += 1 + if worse_err: + Print('\n'.join(worse_err), colour=self.col.RED) + self._error_lines += 1 + if better_warn: + Print('\n'.join(better_warn), colour=self.col.CYAN) + self._error_lines += 1 + if worse_warn: + Print('\n'.join(worse_warn), colour=self.col.MAGENTA) + self._error_lines += 1 + + if show_sizes: + self.PrintSizeSummary(board_selected, board_dict, show_detail, + show_bloat) + + if show_environment and self._base_environment: + lines = [] + + for target in board_dict: + if target not in board_selected: + continue + + tbase = self._base_environment[target] + tenvironment = environment[target] + environment_plus = {} + environment_minus = {} + environment_change = {} + base = tbase.environment + for key, value in tenvironment.environment.iteritems(): + if key not in base: + environment_plus[key] = value + for key, value in base.iteritems(): + if key not in tenvironment.environment: + environment_minus[key] = value + for key, value in base.iteritems(): + new_value = tenvironment.environment.get(key) + if new_value and value != new_value: + desc = '%s -> %s' % (value, new_value) + environment_change[key] = desc + + _AddConfig(lines, target, environment_plus, environment_minus, + environment_change) + + _OutputConfigInfo(lines) + + if show_config and self._base_config: + summary = {} + arch_config_plus = {} + arch_config_minus = {} + arch_config_change = {} + arch_list = [] + + for target in board_dict: + if target not in board_selected: + continue + arch = board_selected[target].arch + if arch not in arch_list: + arch_list.append(arch) + + for arch in arch_list: + arch_config_plus[arch] = {} + arch_config_minus[arch] = {} + arch_config_change[arch] = {} + for name in self.config_filenames: + arch_config_plus[arch][name] = {} + arch_config_minus[arch][name] = {} + arch_config_change[arch][name] = {} + + for target in board_dict: + if target not in board_selected: + continue + + arch = board_selected[target].arch + + all_config_plus = {} + all_config_minus = {} + all_config_change = {} + tbase = self._base_config[target] + tconfig = config[target] + lines = [] + for name in self.config_filenames: + if not tconfig.config[name]: + continue + config_plus = {} + config_minus = {} + config_change = {} + base = tbase.config[name] + for key, value in tconfig.config[name].iteritems(): + if key not in base: + config_plus[key] = value + all_config_plus[key] = value + for key, value in base.iteritems(): + if key not in tconfig.config[name]: + config_minus[key] = value + all_config_minus[key] = value + for key, value in base.iteritems(): + new_value = tconfig.config.get(key) + if new_value and value != new_value: + desc = '%s -> %s' % (value, new_value) + config_change[key] = desc + all_config_change[key] = desc + + arch_config_plus[arch][name].update(config_plus) + arch_config_minus[arch][name].update(config_minus) + arch_config_change[arch][name].update(config_change) + + _AddConfig(lines, name, config_plus, config_minus, + config_change) + _AddConfig(lines, 'all', all_config_plus, all_config_minus, + all_config_change) + summary[target] = '\n'.join(lines) + + lines_by_target = {} + for target, lines in summary.iteritems(): + if lines in lines_by_target: + lines_by_target[lines].append(target) + else: + lines_by_target[lines] = [target] + + for arch in arch_list: + lines = [] + all_plus = {} + all_minus = {} + all_change = {} + for name in self.config_filenames: + all_plus.update(arch_config_plus[arch][name]) + all_minus.update(arch_config_minus[arch][name]) + all_change.update(arch_config_change[arch][name]) + _AddConfig(lines, name, arch_config_plus[arch][name], + arch_config_minus[arch][name], + arch_config_change[arch][name]) + _AddConfig(lines, 'all', all_plus, all_minus, all_change) + #arch_summary[target] = '\n'.join(lines) + if lines: + Print('%s:' % arch) + _OutputConfigInfo(lines) + + for lines, targets in lines_by_target.iteritems(): + if not lines: + continue + Print('%s :' % ' '.join(sorted(targets))) + _OutputConfigInfo(lines.split('\n')) + + + # Save our updated information for the next call to this function + self._base_board_dict = board_dict + self._base_err_lines = err_lines + self._base_warn_lines = warn_lines + self._base_err_line_boards = err_line_boards + self._base_warn_line_boards = warn_line_boards + self._base_config = config + self._base_environment = environment + + # Get a list of boards that did not get built, if needed + not_built = [] + for board in board_selected: + if not board in board_dict: + not_built.append(board) + if not_built: + Print("Boards not built (%d): %s" % (len(not_built), + ', '.join(not_built))) + + def ProduceResultSummary(self, commit_upto, commits, board_selected): + (board_dict, err_lines, err_line_boards, warn_lines, + warn_line_boards, config, environment) = self.GetResultSummary( + board_selected, commit_upto, + read_func_sizes=self._show_bloat, + read_config=self._show_config, + read_environment=self._show_environment) + if commits: + msg = '%02d: %s' % (commit_upto + 1, + commits[commit_upto].subject) + Print(msg, colour=self.col.BLUE) + self.PrintResultSummary(board_selected, board_dict, + err_lines if self._show_errors else [], err_line_boards, + warn_lines if self._show_errors else [], warn_line_boards, + config, environment, self._show_sizes, self._show_detail, + self._show_bloat, self._show_config, self._show_environment) + + def ShowSummary(self, commits, board_selected): + """Show a build summary for U-Boot for a given board list. + + Reset the result summary, then repeatedly call GetResultSummary on + each commit's results, then display the differences we see. + + Args: + commit: Commit objects to summarise + board_selected: Dict containing boards to summarise + """ + self.commit_count = len(commits) if commits else 1 + self.commits = commits + self.ResetResultSummary(board_selected) + self._error_lines = 0 + + for commit_upto in range(0, self.commit_count, self._step): + self.ProduceResultSummary(commit_upto, commits, board_selected) + if not self._error_lines: + Print('(no errors to report)', colour=self.col.GREEN) + + + def SetupBuild(self, board_selected, commits): + """Set up ready to start a build. + + Args: + board_selected: Selected boards to build + commits: Selected commits to build + """ + # First work out how many commits we will build + count = (self.commit_count + self._step - 1) / self._step + self.count = len(board_selected) * count + self.upto = self.warned = self.fail = 0 + self._timestamps = collections.deque() + + def GetThreadDir(self, thread_num): + """Get the directory path to the working dir for a thread. + + Args: + thread_num: Number of thread to check. + """ + return os.path.join(self._working_dir, '%02d' % thread_num) + + def _PrepareThread(self, thread_num, setup_git): + """Prepare the working directory for a thread. + + This clones or fetches the repo into the thread's work directory. + + Args: + thread_num: Thread number (0, 1, ...) + setup_git: True to set up a git repo clone + """ + thread_dir = self.GetThreadDir(thread_num) + builderthread.Mkdir(thread_dir) + git_dir = os.path.join(thread_dir, '.git') + + # Clone the repo if it doesn't already exist + # TODO(sjg@chromium): Perhaps some git hackery to symlink instead, so + # we have a private index but uses the origin repo's contents? + if setup_git and self.git_dir: + src_dir = os.path.abspath(self.git_dir) + if os.path.exists(git_dir): + gitutil.Fetch(git_dir, thread_dir) + else: + Print('\rCloning repo for thread %d' % thread_num, + newline=False) + gitutil.Clone(src_dir, thread_dir) + Print('\r%s\r' % (' ' * 30), newline=False) + + def _PrepareWorkingSpace(self, max_threads, setup_git): + """Prepare the working directory for use. + + Set up the git repo for each thread. + + Args: + max_threads: Maximum number of threads we expect to need. + setup_git: True to set up a git repo clone + """ + builderthread.Mkdir(self._working_dir) + for thread in range(max_threads): + self._PrepareThread(thread, setup_git) + + def _PrepareOutputSpace(self): + """Get the output directories ready to receive files. + + We delete any output directories which look like ones we need to + create. Having left over directories is confusing when the user wants + to check the output manually. + """ + if not self.commits: + return + dir_list = [] + for commit_upto in range(self.commit_count): + dir_list.append(self._GetOutputDir(commit_upto)) + + to_remove = [] + for dirname in glob.glob(os.path.join(self.base_dir, '*')): + if dirname not in dir_list: + to_remove.append(dirname) + if to_remove: + Print('Removing %d old build directories' % len(to_remove), + newline=False) + for dirname in to_remove: + shutil.rmtree(dirname) + + def BuildBoards(self, commits, board_selected, keep_outputs, verbose): + """Build all commits for a list of boards + + Args: + commits: List of commits to be build, each a Commit object + boards_selected: Dict of selected boards, key is target name, + value is Board object + keep_outputs: True to save build output files + verbose: Display build results as they are completed + Returns: + Tuple containing: + - number of boards that failed to build + - number of boards that issued warnings + """ + self.commit_count = len(commits) if commits else 1 + self.commits = commits + self._verbose = verbose + + self.ResetResultSummary(board_selected) + builderthread.Mkdir(self.base_dir, parents = True) + self._PrepareWorkingSpace(min(self.num_threads, len(board_selected)), + commits is not None) + self._PrepareOutputSpace() + Print('\rStarting build...', newline=False) + self.SetupBuild(board_selected, commits) + self.ProcessResult(None) + + # Create jobs to build all commits for each board + for brd in board_selected.itervalues(): + job = builderthread.BuilderJob() + job.board = brd + job.commits = commits + job.keep_outputs = keep_outputs + job.step = self._step + self.queue.put(job) + + term = threading.Thread(target=self.queue.join) + term.setDaemon(True) + term.start() + while term.isAlive(): + term.join(100) + + # Wait until we have processed all output + self.out_queue.join() + Print() + self.ClearLine(0) + return (self.fail, self.warned) diff --git a/tools/u-boot-tools/buildman/builderthread.py b/tools/u-boot-tools/buildman/builderthread.py new file mode 100644 index 0000000..c84ba6a --- /dev/null +++ b/tools/u-boot-tools/buildman/builderthread.py @@ -0,0 +1,493 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (c) 2014 Google, Inc +# + +import errno +import glob +import os +import shutil +import sys +import threading + +import command +import gitutil + +RETURN_CODE_RETRY = -1 + +def Mkdir(dirname, parents = False): + """Make a directory if it doesn't already exist. + + Args: + dirname: Directory to create + """ + try: + if parents: + os.makedirs(dirname) + else: + os.mkdir(dirname) + except OSError as err: + if err.errno == errno.EEXIST: + if os.path.realpath('.') == os.path.realpath(dirname): + print "Cannot create the current working directory '%s'!" % dirname + sys.exit(1) + pass + else: + raise + +class BuilderJob: + """Holds information about a job to be performed by a thread + + Members: + board: Board object to build + commits: List of commit options to build. + """ + def __init__(self): + self.board = None + self.commits = [] + + +class ResultThread(threading.Thread): + """This thread processes results from builder threads. + + It simply passes the results on to the builder. There is only one + result thread, and this helps to serialise the build output. + """ + def __init__(self, builder): + """Set up a new result thread + + Args: + builder: Builder which will be sent each result + """ + threading.Thread.__init__(self) + self.builder = builder + + def run(self): + """Called to start up the result thread. + + We collect the next result job and pass it on to the build. + """ + while True: + result = self.builder.out_queue.get() + self.builder.ProcessResult(result) + self.builder.out_queue.task_done() + + +class BuilderThread(threading.Thread): + """This thread builds U-Boot for a particular board. + + An input queue provides each new job. We run 'make' to build U-Boot + and then pass the results on to the output queue. + + Members: + builder: The builder which contains information we might need + thread_num: Our thread number (0-n-1), used to decide on a + temporary directory + """ + def __init__(self, builder, thread_num, incremental, per_board_out_dir): + """Set up a new builder thread""" + threading.Thread.__init__(self) + self.builder = builder + self.thread_num = thread_num + self.incremental = incremental + self.per_board_out_dir = per_board_out_dir + + def Make(self, commit, brd, stage, cwd, *args, **kwargs): + """Run 'make' on a particular commit and board. + + The source code will already be checked out, so the 'commit' + argument is only for information. + + Args: + commit: Commit object that is being built + brd: Board object that is being built + stage: Stage of the build. Valid stages are: + mrproper - can be called to clean source + config - called to configure for a board + build - the main make invocation - it does the build + args: A list of arguments to pass to 'make' + kwargs: A list of keyword arguments to pass to command.RunPipe() + + Returns: + CommandResult object + """ + return self.builder.do_make(commit, brd, stage, cwd, *args, + **kwargs) + + def RunCommit(self, commit_upto, brd, work_dir, do_config, config_only, + force_build, force_build_failures): + """Build a particular commit. + + If the build is already done, and we are not forcing a build, we skip + the build and just return the previously-saved results. + + Args: + commit_upto: Commit number to build (0...n-1) + brd: Board object to build + work_dir: Directory to which the source will be checked out + do_config: True to run a make <board>_defconfig on the source + config_only: Only configure the source, do not build it + force_build: Force a build even if one was previously done + force_build_failures: Force a bulid if the previous result showed + failure + + Returns: + tuple containing: + - CommandResult object containing the results of the build + - boolean indicating whether 'make config' is still needed + """ + # Create a default result - it will be overwritte by the call to + # self.Make() below, in the event that we do a build. + result = command.CommandResult() + result.return_code = 0 + if self.builder.in_tree: + out_dir = work_dir + else: + if self.per_board_out_dir: + out_rel_dir = os.path.join('..', brd.target) + else: + out_rel_dir = 'build' + out_dir = os.path.join(work_dir, out_rel_dir) + + # Check if the job was already completed last time + done_file = self.builder.GetDoneFile(commit_upto, brd.target) + result.already_done = os.path.exists(done_file) + will_build = (force_build or force_build_failures or + not result.already_done) + if result.already_done: + # Get the return code from that build and use it + with open(done_file, 'r') as fd: + result.return_code = int(fd.readline()) + + # Check the signal that the build needs to be retried + if result.return_code == RETURN_CODE_RETRY: + will_build = True + elif will_build: + err_file = self.builder.GetErrFile(commit_upto, brd.target) + if os.path.exists(err_file) and os.stat(err_file).st_size: + result.stderr = 'bad' + elif not force_build: + # The build passed, so no need to build it again + will_build = False + + if will_build: + # We are going to have to build it. First, get a toolchain + if not self.toolchain: + try: + self.toolchain = self.builder.toolchains.Select(brd.arch) + except ValueError as err: + result.return_code = 10 + result.stdout = '' + result.stderr = str(err) + # TODO(sjg@chromium.org): This gets swallowed, but needs + # to be reported. + + if self.toolchain: + # Checkout the right commit + if self.builder.commits: + commit = self.builder.commits[commit_upto] + if self.builder.checkout: + git_dir = os.path.join(work_dir, '.git') + gitutil.Checkout(commit.hash, git_dir, work_dir, + force=True) + else: + commit = 'current' + + # Set up the environment and command line + env = self.toolchain.MakeEnvironment(self.builder.full_path) + Mkdir(out_dir) + args = [] + cwd = work_dir + src_dir = os.path.realpath(work_dir) + if not self.builder.in_tree: + if commit_upto is None: + # In this case we are building in the original source + # directory (i.e. the current directory where buildman + # is invoked. The output directory is set to this + # thread's selected work directory. + # + # Symlinks can confuse U-Boot's Makefile since + # we may use '..' in our path, so remove them. + out_dir = os.path.realpath(out_dir) + args.append('O=%s' % out_dir) + cwd = None + src_dir = os.getcwd() + else: + args.append('O=%s' % out_rel_dir) + if self.builder.verbose_build: + args.append('V=1') + else: + args.append('-s') + if self.builder.num_jobs is not None: + args.extend(['-j', str(self.builder.num_jobs)]) + if self.builder.warnings_as_errors: + args.append('KCFLAGS=-Werror') + config_args = ['%s_defconfig' % brd.target] + config_out = '' + args.extend(self.builder.toolchains.GetMakeArguments(brd)) + + # If we need to reconfigure, do that now + if do_config: + config_out = '' + if not self.incremental: + result = self.Make(commit, brd, 'mrproper', cwd, + 'mrproper', *args, env=env) + config_out += result.combined + result = self.Make(commit, brd, 'config', cwd, + *(args + config_args), env=env) + config_out += result.combined + do_config = False # No need to configure next time + if result.return_code == 0: + if config_only: + args.append('cfg') + result = self.Make(commit, brd, 'build', cwd, *args, + env=env) + result.stderr = result.stderr.replace(src_dir + '/', '') + if self.builder.verbose_build: + result.stdout = config_out + result.stdout + else: + result.return_code = 1 + result.stderr = 'No tool chain for %s\n' % brd.arch + result.already_done = False + + result.toolchain = self.toolchain + result.brd = brd + result.commit_upto = commit_upto + result.out_dir = out_dir + return result, do_config + + def _WriteResult(self, result, keep_outputs): + """Write a built result to the output directory. + + Args: + result: CommandResult object containing result to write + keep_outputs: True to store the output binaries, False + to delete them + """ + # Fatal error + if result.return_code < 0: + return + + # If we think this might have been aborted with Ctrl-C, record the + # failure but not that we are 'done' with this board. A retry may fix + # it. + maybe_aborted = result.stderr and 'No child processes' in result.stderr + + if result.already_done: + return + + # Write the output and stderr + output_dir = self.builder._GetOutputDir(result.commit_upto) + Mkdir(output_dir) + build_dir = self.builder.GetBuildDir(result.commit_upto, + result.brd.target) + Mkdir(build_dir) + + outfile = os.path.join(build_dir, 'log') + with open(outfile, 'w') as fd: + if result.stdout: + # We don't want unicode characters in log files + fd.write(result.stdout.decode('UTF-8').encode('ASCII', 'replace')) + + errfile = self.builder.GetErrFile(result.commit_upto, + result.brd.target) + if result.stderr: + with open(errfile, 'w') as fd: + # We don't want unicode characters in log files + fd.write(result.stderr.decode('UTF-8').encode('ASCII', 'replace')) + elif os.path.exists(errfile): + os.remove(errfile) + + if result.toolchain: + # Write the build result and toolchain information. + done_file = self.builder.GetDoneFile(result.commit_upto, + result.brd.target) + with open(done_file, 'w') as fd: + if maybe_aborted: + # Special code to indicate we need to retry + fd.write('%s' % RETURN_CODE_RETRY) + else: + fd.write('%s' % result.return_code) + with open(os.path.join(build_dir, 'toolchain'), 'w') as fd: + print >>fd, 'gcc', result.toolchain.gcc + print >>fd, 'path', result.toolchain.path + print >>fd, 'cross', result.toolchain.cross + print >>fd, 'arch', result.toolchain.arch + fd.write('%s' % result.return_code) + + # Write out the image and function size information and an objdump + env = result.toolchain.MakeEnvironment(self.builder.full_path) + lines = [] + for fname in ['u-boot', 'spl/u-boot-spl']: + cmd = ['%snm' % self.toolchain.cross, '--size-sort', fname] + nm_result = command.RunPipe([cmd], capture=True, + capture_stderr=True, cwd=result.out_dir, + raise_on_error=False, env=env) + if nm_result.stdout: + nm = self.builder.GetFuncSizesFile(result.commit_upto, + result.brd.target, fname) + with open(nm, 'w') as fd: + print >>fd, nm_result.stdout, + + cmd = ['%sobjdump' % self.toolchain.cross, '-h', fname] + dump_result = command.RunPipe([cmd], capture=True, + capture_stderr=True, cwd=result.out_dir, + raise_on_error=False, env=env) + rodata_size = '' + if dump_result.stdout: + objdump = self.builder.GetObjdumpFile(result.commit_upto, + result.brd.target, fname) + with open(objdump, 'w') as fd: + print >>fd, dump_result.stdout, + for line in dump_result.stdout.splitlines(): + fields = line.split() + if len(fields) > 5 and fields[1] == '.rodata': + rodata_size = fields[2] + + cmd = ['%ssize' % self.toolchain.cross, fname] + size_result = command.RunPipe([cmd], capture=True, + capture_stderr=True, cwd=result.out_dir, + raise_on_error=False, env=env) + if size_result.stdout: + lines.append(size_result.stdout.splitlines()[1] + ' ' + + rodata_size) + + # Extract the environment from U-Boot and dump it out + cmd = ['%sobjcopy' % self.toolchain.cross, '-O', 'binary', + '-j', '.rodata.default_environment', + 'env/built-in.o', 'uboot.env'] + command.RunPipe([cmd], capture=True, + capture_stderr=True, cwd=result.out_dir, + raise_on_error=False, env=env) + ubootenv = os.path.join(result.out_dir, 'uboot.env') + self.CopyFiles(result.out_dir, build_dir, '', ['uboot.env']) + + # Write out the image sizes file. This is similar to the output + # of binutil's 'size' utility, but it omits the header line and + # adds an additional hex value at the end of each line for the + # rodata size + if len(lines): + sizes = self.builder.GetSizesFile(result.commit_upto, + result.brd.target) + with open(sizes, 'w') as fd: + print >>fd, '\n'.join(lines) + + # Write out the configuration files, with a special case for SPL + for dirname in ['', 'spl', 'tpl']: + self.CopyFiles(result.out_dir, build_dir, dirname, ['u-boot.cfg', + 'spl/u-boot-spl.cfg', 'tpl/u-boot-tpl.cfg', '.config', + 'include/autoconf.mk', 'include/generated/autoconf.h']) + + # Now write the actual build output + if keep_outputs: + self.CopyFiles(result.out_dir, build_dir, '', ['u-boot*', '*.bin', + '*.map', '*.img', 'MLO', 'SPL', 'include/autoconf.mk', + 'spl/u-boot-spl*']) + + def CopyFiles(self, out_dir, build_dir, dirname, patterns): + """Copy files from the build directory to the output. + + Args: + out_dir: Path to output directory containing the files + build_dir: Place to copy the files + dirname: Source directory, '' for normal U-Boot, 'spl' for SPL + patterns: A list of filenames (strings) to copy, each relative + to the build directory + """ + for pattern in patterns: + file_list = glob.glob(os.path.join(out_dir, dirname, pattern)) + for fname in file_list: + target = os.path.basename(fname) + if dirname: + base, ext = os.path.splitext(target) + if ext: + target = '%s-%s%s' % (base, dirname, ext) + shutil.copy(fname, os.path.join(build_dir, target)) + + def RunJob(self, job): + """Run a single job + + A job consists of a building a list of commits for a particular board. + + Args: + job: Job to build + """ + brd = job.board + work_dir = self.builder.GetThreadDir(self.thread_num) + self.toolchain = None + if job.commits: + # Run 'make board_defconfig' on the first commit + do_config = True + commit_upto = 0 + force_build = False + for commit_upto in range(0, len(job.commits), job.step): + result, request_config = self.RunCommit(commit_upto, brd, + work_dir, do_config, self.builder.config_only, + force_build or self.builder.force_build, + self.builder.force_build_failures) + failed = result.return_code or result.stderr + did_config = do_config + if failed and not do_config: + # If our incremental build failed, try building again + # with a reconfig. + if self.builder.force_config_on_failure: + result, request_config = self.RunCommit(commit_upto, + brd, work_dir, True, False, True, False) + did_config = True + if not self.builder.force_reconfig: + do_config = request_config + + # If we built that commit, then config is done. But if we got + # an warning, reconfig next time to force it to build the same + # files that created warnings this time. Otherwise an + # incremental build may not build the same file, and we will + # think that the warning has gone away. + # We could avoid this by using -Werror everywhere... + # For errors, the problem doesn't happen, since presumably + # the build stopped and didn't generate output, so will retry + # that file next time. So we could detect warnings and deal + # with them specially here. For now, we just reconfigure if + # anything goes work. + # Of course this is substantially slower if there are build + # errors/warnings (e.g. 2-3x slower even if only 10% of builds + # have problems). + if (failed and not result.already_done and not did_config and + self.builder.force_config_on_failure): + # If this build failed, try the next one with a + # reconfigure. + # Sometimes if the board_config.h file changes it can mess + # with dependencies, and we get: + # make: *** No rule to make target `include/autoconf.mk', + # needed by `depend'. + do_config = True + force_build = True + else: + force_build = False + if self.builder.force_config_on_failure: + if failed: + do_config = True + result.commit_upto = commit_upto + if result.return_code < 0: + raise ValueError('Interrupt') + + # We have the build results, so output the result + self._WriteResult(result, job.keep_outputs) + self.builder.out_queue.put(result) + else: + # Just build the currently checked-out build + result, request_config = self.RunCommit(None, brd, work_dir, True, + self.builder.config_only, True, + self.builder.force_build_failures) + result.commit_upto = 0 + self._WriteResult(result, job.keep_outputs) + self.builder.out_queue.put(result) + + def run(self): + """Our thread's run function + + This thread picks a job from the queue, runs it, and then goes to the + next job. + """ + while True: + job = self.builder.queue.get() + self.RunJob(job) + self.builder.queue.task_done() diff --git a/tools/u-boot-tools/buildman/buildman b/tools/u-boot-tools/buildman/buildman new file mode 120000 index 0000000..e4fba2d --- /dev/null +++ b/tools/u-boot-tools/buildman/buildman @@ -0,0 +1 @@ +buildman.py \ No newline at end of file diff --git a/tools/u-boot-tools/buildman/buildman.py b/tools/u-boot-tools/buildman/buildman.py new file mode 100755 index 0000000..f17aa15 --- /dev/null +++ b/tools/u-boot-tools/buildman/buildman.py @@ -0,0 +1,66 @@ +#!/usr/bin/env python2 +# SPDX-License-Identifier: GPL-2.0+ +# +# Copyright (c) 2012 The Chromium OS Authors. +# + +"""See README for more information""" + +import multiprocessing +import os +import re +import sys +import unittest + +# Bring in the patman libraries +our_path = os.path.dirname(os.path.realpath(__file__)) +sys.path.insert(1, os.path.join(our_path, '../patman')) + +# Our modules +import board +import bsettings +import builder +import checkpatch +import cmdline +import control +import doctest +import gitutil +import patchstream +import terminal +import toolchain + +def RunTests(skip_net_tests): + import func_test + import test + import doctest + + result = unittest.TestResult() + for module in ['toolchain', 'gitutil']: + suite = doctest.DocTestSuite(module) + suite.run(result) + + sys.argv = [sys.argv[0]] + if skip_net_tests: + test.use_network = False + for module in (test.TestBuild, func_test.TestFunctional): + suite = unittest.TestLoader().loadTestsFromTestCase(module) + suite.run(result) + + print result + for test, err in result.errors: + print err + for test, err in result.failures: + print err + + +options, args = cmdline.ParseArgs() + +# Run our meagre tests +if options.test: + RunTests(options.skip_net_tests) + +# Build selected commits for selected boards +else: + bsettings.Setup(options.config_file) + ret_code = control.DoBuildman(options, args) + sys.exit(ret_code) diff --git a/tools/u-boot-tools/buildman/cmdline.py b/tools/u-boot-tools/buildman/cmdline.py new file mode 100644 index 0000000..93d09ca --- /dev/null +++ b/tools/u-boot-tools/buildman/cmdline.py @@ -0,0 +1,111 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (c) 2014 Google, Inc +# + +from optparse import OptionParser + +def ParseArgs(): + """Parse command line arguments from sys.argv[] + + Returns: + tuple containing: + options: command line options + args: command lin arguments + """ + parser = OptionParser() + parser.add_option('-b', '--branch', type='string', + help='Branch name to build, or range of commits to build') + parser.add_option('-B', '--bloat', dest='show_bloat', + action='store_true', default=False, + help='Show changes in function code size for each board') + parser.add_option('--boards', type='string', action='append', + help='List of board names to build separated by comma') + parser.add_option('-c', '--count', dest='count', type='int', + default=-1, help='Run build on the top n commits') + parser.add_option('-C', '--force-reconfig', dest='force_reconfig', + action='store_true', default=False, + help='Reconfigure for every commit (disable incremental build)') + parser.add_option('-d', '--detail', dest='show_detail', + action='store_true', default=False, + help='Show detailed information for each board in summary') + parser.add_option('-D', '--config-only', action='store_true', default=False, + help="Don't build, just configure each commit") + parser.add_option('-e', '--show_errors', action='store_true', + default=False, help='Show errors and warnings') + parser.add_option('-E', '--warnings-as-errors', action='store_true', + default=False, help='Treat all compiler warnings as errors') + parser.add_option('-f', '--force-build', dest='force_build', + action='store_true', default=False, + help='Force build of boards even if already built') + parser.add_option('-F', '--force-build-failures', dest='force_build_failures', + action='store_true', default=False, + help='Force build of previously-failed build') + parser.add_option('--fetch-arch', type='string', + help="Fetch a toolchain for architecture FETCH_ARCH ('list' to list)." + ' You can also fetch several toolchains separate by comma, or' + " 'all' to download all") + parser.add_option('-g', '--git', type='string', + help='Git repo containing branch to build', default='.') + parser.add_option('-G', '--config-file', type='string', + help='Path to buildman config file', default='') + parser.add_option('-H', '--full-help', action='store_true', dest='full_help', + default=False, help='Display the README file') + parser.add_option('-i', '--in-tree', dest='in_tree', + action='store_true', default=False, + help='Build in the source tree instead of a separate directory') + parser.add_option('-I', '--incremental', action='store_true', + default=False, help='Do not run make mrproper (when reconfiguring)') + parser.add_option('-j', '--jobs', dest='jobs', type='int', + default=None, help='Number of jobs to run at once (passed to make)') + parser.add_option('-k', '--keep-outputs', action='store_true', + default=False, help='Keep all build output files (e.g. binaries)') + parser.add_option('-K', '--show-config', action='store_true', + default=False, help='Show configuration changes in summary (both board config files and Kconfig)') + parser.add_option('--preserve-config-y', action='store_true', + default=False, help="Don't convert y to 1 in configs") + parser.add_option('-l', '--list-error-boards', action='store_true', + default=False, help='Show a list of boards next to each error/warning') + parser.add_option('--list-tool-chains', action='store_true', default=False, + help='List available tool chains (use -v to see probing detail)') + parser.add_option('-n', '--dry-run', action='store_true', dest='dry_run', + default=False, help="Do a dry run (describe actions, but do nothing)") + parser.add_option('-N', '--no-subdirs', action='store_true', dest='no_subdirs', + default=False, help="Don't create subdirectories when building current source for a single board") + parser.add_option('-o', '--output-dir', type='string', + dest='output_dir', default='..', + help='Directory where all builds happen and buildman has its workspace (default is ../)') + parser.add_option('-Q', '--quick', action='store_true', + default=False, help='Do a rough build, with limited warning resolution') + parser.add_option('-p', '--full-path', action='store_true', + default=False, help="Use full toolchain path in CROSS_COMPILE") + parser.add_option('-P', '--per-board-out-dir', action='store_true', + default=False, help="Use an O= (output) directory per board rather than per thread") + parser.add_option('-s', '--summary', action='store_true', + default=False, help='Show a build summary') + parser.add_option('-S', '--show-sizes', action='store_true', + default=False, help='Show image size variation in summary') + parser.add_option('--skip-net-tests', action='store_true', default=False, + help='Skip tests which need the network') + parser.add_option('--step', type='int', + default=1, help='Only build every n commits (0=just first and last)') + parser.add_option('-t', '--test', action='store_true', dest='test', + default=False, help='run tests') + parser.add_option('-T', '--threads', type='int', + default=None, help='Number of builder threads to use') + parser.add_option('-u', '--show_unknown', action='store_true', + default=False, help='Show boards with unknown build result') + parser.add_option('-U', '--show-environment', action='store_true', + default=False, help='Show environment changes in summary') + parser.add_option('-v', '--verbose', action='store_true', + default=False, help='Show build results while the build progresses') + parser.add_option('-V', '--verbose-build', action='store_true', + default=False, help='Run make with V=1, logging all output') + parser.add_option('-x', '--exclude', dest='exclude', + type='string', action='append', + help='Specify a list of boards to exclude, separated by comma') + + parser.usage += """ [list of target/arch/cpu/board/vendor/soc to build] + + Build U-Boot for all commits in a branch. Use -n to do a dry run""" + + return parser.parse_args() diff --git a/tools/u-boot-tools/buildman/control.py b/tools/u-boot-tools/buildman/control.py new file mode 100644 index 0000000..c900211 --- /dev/null +++ b/tools/u-boot-tools/buildman/control.py @@ -0,0 +1,347 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (c) 2013 The Chromium OS Authors. +# + +import multiprocessing +import os +import shutil +import sys + +import board +import bsettings +from builder import Builder +import gitutil +import patchstream +import terminal +from terminal import Print +import toolchain +import command +import subprocess + +def GetPlural(count): + """Returns a plural 's' if count is not 1""" + return 's' if count != 1 else '' + +def GetActionSummary(is_summary, commits, selected, options): + """Return a string summarising the intended action. + + Returns: + Summary string. + """ + if commits: + count = len(commits) + count = (count + options.step - 1) / options.step + commit_str = '%d commit%s' % (count, GetPlural(count)) + else: + commit_str = 'current source' + str = '%s %s for %d boards' % ( + 'Summary of' if is_summary else 'Building', commit_str, + len(selected)) + str += ' (%d thread%s, %d job%s per thread)' % (options.threads, + GetPlural(options.threads), options.jobs, GetPlural(options.jobs)) + return str + +def ShowActions(series, why_selected, boards_selected, builder, options, + board_warnings): + """Display a list of actions that we would take, if not a dry run. + + Args: + series: Series object + why_selected: Dictionary where each key is a buildman argument + provided by the user, and the value is the list of boards + brought in by that argument. For example, 'arm' might bring + in 400 boards, so in this case the key would be 'arm' and + the value would be a list of board names. + boards_selected: Dict of selected boards, key is target name, + value is Board object + builder: The builder that will be used to build the commits + options: Command line options object + board_warnings: List of warnings obtained from board selected + """ + col = terminal.Color() + print 'Dry run, so not doing much. But I would do this:' + print + if series: + commits = series.commits + else: + commits = None + print GetActionSummary(False, commits, boards_selected, + options) + print 'Build directory: %s' % builder.base_dir + if commits: + for upto in range(0, len(series.commits), options.step): + commit = series.commits[upto] + print ' ', col.Color(col.YELLOW, commit.hash[:8], bright=False), + print commit.subject + print + for arg in why_selected: + if arg != 'all': + print arg, ': %d boards' % len(why_selected[arg]) + if options.verbose: + print ' %s' % ' '.join(why_selected[arg]) + print ('Total boards to build for each commit: %d\n' % + len(why_selected['all'])) + if board_warnings: + for warning in board_warnings: + print col.Color(col.YELLOW, warning) + +def CheckOutputDir(output_dir): + """Make sure that the output directory is not within the current directory + + If we try to use an output directory which is within the current directory + (which is assumed to hold the U-Boot source) we may end up deleting the + U-Boot source code. Detect this and print an error in this case. + + Args: + output_dir: Output directory path to check + """ + path = os.path.realpath(output_dir) + cwd_path = os.path.realpath('.') + while True: + if os.path.realpath(path) == cwd_path: + Print("Cannot use output directory '%s' since it is within the current directtory '%s'" % + (path, cwd_path)) + sys.exit(1) + parent = os.path.dirname(path) + if parent == path: + break + path = parent + +def DoBuildman(options, args, toolchains=None, make_func=None, boards=None, + clean_dir=False): + """The main control code for buildman + + Args: + options: Command line options object + args: Command line arguments (list of strings) + toolchains: Toolchains to use - this should be a Toolchains() + object. If None, then it will be created and scanned + make_func: Make function to use for the builder. This is called + to execute 'make'. If this is None, the normal function + will be used, which calls the 'make' tool with suitable + arguments. This setting is useful for tests. + board: Boards() object to use, containing a list of available + boards. If this is None it will be created and scanned. + """ + global builder + + if options.full_help: + pager = os.getenv('PAGER') + if not pager: + pager = 'more' + fname = os.path.join(os.path.dirname(os.path.realpath(sys.argv[0])), + 'README') + command.Run(pager, fname) + return 0 + + gitutil.Setup() + col = terminal.Color() + + options.git_dir = os.path.join(options.git, '.git') + + no_toolchains = toolchains is None + if no_toolchains: + toolchains = toolchain.Toolchains() + + if options.fetch_arch: + if options.fetch_arch == 'list': + sorted_list = toolchains.ListArchs() + print col.Color(col.BLUE, 'Available architectures: %s\n' % + ' '.join(sorted_list)) + return 0 + else: + fetch_arch = options.fetch_arch + if fetch_arch == 'all': + fetch_arch = ','.join(toolchains.ListArchs()) + print col.Color(col.CYAN, '\nDownloading toolchains: %s' % + fetch_arch) + for arch in fetch_arch.split(','): + print + ret = toolchains.FetchAndInstall(arch) + if ret: + return ret + return 0 + + if no_toolchains: + toolchains.GetSettings() + toolchains.Scan(options.list_tool_chains and options.verbose) + if options.list_tool_chains: + toolchains.List() + print + return 0 + + # Work out how many commits to build. We want to build everything on the + # branch. We also build the upstream commit as a control so we can see + # problems introduced by the first commit on the branch. + count = options.count + has_range = options.branch and '..' in options.branch + if count == -1: + if not options.branch: + count = 1 + else: + if has_range: + count, msg = gitutil.CountCommitsInRange(options.git_dir, + options.branch) + else: + count, msg = gitutil.CountCommitsInBranch(options.git_dir, + options.branch) + if count is None: + sys.exit(col.Color(col.RED, msg)) + elif count == 0: + sys.exit(col.Color(col.RED, "Range '%s' has no commits" % + options.branch)) + if msg: + print col.Color(col.YELLOW, msg) + count += 1 # Build upstream commit also + + if not count: + str = ("No commits found to process in branch '%s': " + "set branch's upstream or use -c flag" % options.branch) + sys.exit(col.Color(col.RED, str)) + + # Work out what subset of the boards we are building + if not boards: + board_file = os.path.join(options.git, 'boards.cfg') + status = subprocess.call([os.path.join(options.git, + 'tools/genboardscfg.py')]) + if status != 0: + sys.exit("Failed to generate boards.cfg") + + boards = board.Boards() + boards.ReadBoards(os.path.join(options.git, 'boards.cfg')) + + exclude = [] + if options.exclude: + for arg in options.exclude: + exclude += arg.split(',') + + + if options.boards: + requested_boards = [] + for b in options.boards: + requested_boards += b.split(',') + else: + requested_boards = None + why_selected, board_warnings = boards.SelectBoards(args, exclude, + requested_boards) + selected = boards.GetSelected() + if not len(selected): + sys.exit(col.Color(col.RED, 'No matching boards found')) + + # Read the metadata from the commits. First look at the upstream commit, + # then the ones in the branch. We would like to do something like + # upstream/master~..branch but that isn't possible if upstream/master is + # a merge commit (it will list all the commits that form part of the + # merge) + # Conflicting tags are not a problem for buildman, since it does not use + # them. For example, Series-version is not useful for buildman. On the + # other hand conflicting tags will cause an error. So allow later tags + # to overwrite earlier ones by setting allow_overwrite=True + if options.branch: + if count == -1: + if has_range: + range_expr = options.branch + else: + range_expr = gitutil.GetRangeInBranch(options.git_dir, + options.branch) + upstream_commit = gitutil.GetUpstream(options.git_dir, + options.branch) + series = patchstream.GetMetaDataForList(upstream_commit, + options.git_dir, 1, series=None, allow_overwrite=True) + + series = patchstream.GetMetaDataForList(range_expr, + options.git_dir, None, series, allow_overwrite=True) + else: + # Honour the count + series = patchstream.GetMetaDataForList(options.branch, + options.git_dir, count, series=None, allow_overwrite=True) + else: + series = None + if not options.dry_run: + options.verbose = True + if not options.summary: + options.show_errors = True + + # By default we have one thread per CPU. But if there are not enough jobs + # we can have fewer threads and use a high '-j' value for make. + if not options.threads: + options.threads = min(multiprocessing.cpu_count(), len(selected)) + if not options.jobs: + options.jobs = max(1, (multiprocessing.cpu_count() + + len(selected) - 1) / len(selected)) + + if not options.step: + options.step = len(series.commits) - 1 + + gnu_make = command.Output(os.path.join(options.git, + 'scripts/show-gnu-make'), raise_on_error=False).rstrip() + if not gnu_make: + sys.exit('GNU Make not found') + + # Create a new builder with the selected options. + output_dir = options.output_dir + if options.branch: + dirname = options.branch.replace('/', '_') + # As a special case allow the board directory to be placed in the + # output directory itself rather than any subdirectory. + if not options.no_subdirs: + output_dir = os.path.join(options.output_dir, dirname) + if clean_dir and os.path.exists(output_dir): + shutil.rmtree(output_dir) + CheckOutputDir(output_dir) + builder = Builder(toolchains, output_dir, options.git_dir, + options.threads, options.jobs, gnu_make=gnu_make, checkout=True, + show_unknown=options.show_unknown, step=options.step, + no_subdirs=options.no_subdirs, full_path=options.full_path, + verbose_build=options.verbose_build, + incremental=options.incremental, + per_board_out_dir=options.per_board_out_dir, + config_only=options.config_only, + squash_config_y=not options.preserve_config_y, + warnings_as_errors=options.warnings_as_errors) + builder.force_config_on_failure = not options.quick + if make_func: + builder.do_make = make_func + + # For a dry run, just show our actions as a sanity check + if options.dry_run: + ShowActions(series, why_selected, selected, builder, options, + board_warnings) + else: + builder.force_build = options.force_build + builder.force_build_failures = options.force_build_failures + builder.force_reconfig = options.force_reconfig + builder.in_tree = options.in_tree + + # Work out which boards to build + board_selected = boards.GetSelectedDict() + + if series: + commits = series.commits + # Number the commits for test purposes + for commit in range(len(commits)): + commits[commit].sequence = commit + else: + commits = None + + Print(GetActionSummary(options.summary, commits, board_selected, + options)) + + # We can't show function sizes without board details at present + if options.show_bloat: + options.show_detail = True + builder.SetDisplayOptions(options.show_errors, options.show_sizes, + options.show_detail, options.show_bloat, + options.list_error_boards, + options.show_config, + options.show_environment) + if options.summary: + builder.ShowSummary(commits, board_selected) + else: + fail, warned = builder.BuildBoards(commits, board_selected, + options.keep_outputs, options.verbose) + if fail: + return 128 + elif warned: + return 129 + return 0 diff --git a/tools/u-boot-tools/buildman/func_test.py b/tools/u-boot-tools/buildman/func_test.py new file mode 100644 index 0000000..119d02c --- /dev/null +++ b/tools/u-boot-tools/buildman/func_test.py @@ -0,0 +1,535 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (c) 2014 Google, Inc +# + +import os +import shutil +import sys +import tempfile +import unittest + +import board +import bsettings +import cmdline +import command +import control +import gitutil +import terminal +import toolchain + +settings_data = ''' +# Buildman settings file + +[toolchain] + +[toolchain-alias] + +[make-flags] +src=/home/sjg/c/src +chroot=/home/sjg/c/chroot +vboot=VBOOT_DEBUG=1 MAKEFLAGS_VBOOT=DEBUG=1 CFLAGS_EXTRA_VBOOT=-DUNROLL_LOOPS VBOOT_SOURCE=${src}/platform/vboot_reference +chromeos_coreboot=VBOOT=${chroot}/build/link/usr ${vboot} +chromeos_daisy=VBOOT=${chroot}/build/daisy/usr ${vboot} +chromeos_peach=VBOOT=${chroot}/build/peach_pit/usr ${vboot} +''' + +boards = [ + ['Active', 'arm', 'armv7', '', 'Tester', 'ARM Board 1', 'board0', ''], + ['Active', 'arm', 'armv7', '', 'Tester', 'ARM Board 2', 'board1', ''], + ['Active', 'powerpc', 'powerpc', '', 'Tester', 'PowerPC board 1', 'board2', ''], + ['Active', 'sandbox', 'sandbox', '', 'Tester', 'Sandbox board', 'board4', ''], +] + +commit_shortlog = """4aca821 patman: Avoid changing the order of tags +39403bb patman: Use --no-pager' to stop git from forking a pager +db6e6f2 patman: Remove the -a option +f2ccf03 patman: Correct unit tests to run correctly +1d097f9 patman: Fix indentation in terminal.py +d073747 patman: Support the 'reverse' option for 'git log +""" + +commit_log = ["""commit 7f6b8315d18f683c5181d0c3694818c1b2a20dcd +Author: Masahiro Yamada <yamada.m@jp.panasonic.com> +Date: Fri Aug 22 19:12:41 2014 +0900 + + buildman: refactor help message + + "buildman [options]" is displayed by default. + + Append the rest of help messages to parser.usage + instead of replacing it. + + Besides, "-b <branch>" is not mandatory since commit fea5858e. + Drop it from the usage. + + Signed-off-by: Masahiro Yamada <yamada.m@jp.panasonic.com> +""", +"""commit d0737479be6baf4db5e2cdbee123e96bc5ed0ba8 +Author: Simon Glass <sjg@chromium.org> +Date: Thu Aug 14 16:48:25 2014 -0600 + + patman: Support the 'reverse' option for 'git log' + + This option is currently not supported, but needs to be, for buildman to + operate as expected. + + Series-changes: 7 + - Add new patch to fix the 'reverse' bug + + Series-version: 8 + + Change-Id: I79078f792e8b390b8a1272a8023537821d45feda + Reported-by: York Sun <yorksun@freescale.com> + Signed-off-by: Simon Glass <sjg@chromium.org> + +""", +"""commit 1d097f9ab487c5019152fd47bda126839f3bf9fc +Author: Simon Glass <sjg@chromium.org> +Date: Sat Aug 9 11:44:32 2014 -0600 + + patman: Fix indentation in terminal.py + + This code came from a different project with 2-character indentation. Fix + it for U-Boot. + + Series-changes: 6 + - Add new patch to fix indentation in teminal.py + + Change-Id: I5a74d2ebbb3cc12a665f5c725064009ac96e8a34 + Signed-off-by: Simon Glass <sjg@chromium.org> + +""", +"""commit f2ccf03869d1e152c836515a3ceb83cdfe04a105 +Author: Simon Glass <sjg@chromium.org> +Date: Sat Aug 9 11:08:24 2014 -0600 + + patman: Correct unit tests to run correctly + + It seems that doctest behaves differently now, and some of the unit tests + do not run. Adjust the tests to work correctly. + + ./tools/patman/patman --test + <unittest.result.TestResult run=10 errors=0 failures=0> + + Series-changes: 6 + - Add new patch to fix patman unit tests + + Change-Id: I3d2ca588f4933e1f9d6b1665a00e4ae58269ff3b + +""", +"""commit db6e6f2f9331c5a37647d6668768d4a40b8b0d1c +Author: Simon Glass <sjg@chromium.org> +Date: Sat Aug 9 12:06:02 2014 -0600 + + patman: Remove the -a option + + It seems that this is no longer needed, since checkpatch.pl will catch + whitespace problems in patches. Also the option is not widely used, so + it seems safe to just remove it. + + Series-changes: 6 + - Add new patch to remove patman's -a option + + Suggested-by: Masahiro Yamada <yamada.m@jp.panasonic.com> + Change-Id: I5821a1c75154e532c46513486ca40b808de7e2cc + +""", +"""commit 39403bb4f838153028a6f21ca30bf100f3791133 +Author: Simon Glass <sjg@chromium.org> +Date: Thu Aug 14 21:50:52 2014 -0600 + + patman: Use --no-pager' to stop git from forking a pager + +""", +"""commit 4aca821e27e97925c039e69fd37375b09c6f129c +Author: Simon Glass <sjg@chromium.org> +Date: Fri Aug 22 15:57:39 2014 -0600 + + patman: Avoid changing the order of tags + + patman collects tags that it sees in the commit and places them nicely + sorted at the end of the patch. However, this is not really necessary and + in fact is apparently not desirable. + + Series-changes: 9 + - Add new patch to avoid changing the order of tags + + Series-version: 9 + + Suggested-by: Masahiro Yamada <yamada.m@jp.panasonic.com> + Change-Id: Ib1518588c1a189ad5c3198aae76f8654aed8d0db +"""] + +TEST_BRANCH = '__testbranch' + +class TestFunctional(unittest.TestCase): + """Functional test for buildman. + + This aims to test from just below the invocation of buildman (parsing + of arguments) to 'make' and 'git' invocation. It is not a true + emd-to-end test, as it mocks git, make and the tool chain. But this + makes it easier to detect when the builder is doing the wrong thing, + since in many cases this test code will fail. For example, only a + very limited subset of 'git' arguments is supported - anything + unexpected will fail. + """ + def setUp(self): + self._base_dir = tempfile.mkdtemp() + self._git_dir = os.path.join(self._base_dir, 'src') + self._buildman_pathname = sys.argv[0] + self._buildman_dir = os.path.dirname(os.path.realpath(sys.argv[0])) + command.test_result = self._HandleCommand + self.setupToolchains() + self._toolchains.Add('arm-gcc', test=False) + self._toolchains.Add('powerpc-gcc', test=False) + bsettings.Setup(None) + bsettings.AddFile(settings_data) + self._boards = board.Boards() + for brd in boards: + self._boards.AddBoard(board.Board(*brd)) + + # Directories where the source been cloned + self._clone_dirs = [] + self._commits = len(commit_shortlog.splitlines()) + 1 + self._total_builds = self._commits * len(boards) + + # Number of calls to make + self._make_calls = 0 + + # Map of [board, commit] to error messages + self._error = {} + + self._test_branch = TEST_BRANCH + + # Avoid sending any output and clear all terminal output + terminal.SetPrintTestMode() + terminal.GetPrintTestLines() + + def tearDown(self): + shutil.rmtree(self._base_dir) + + def setupToolchains(self): + self._toolchains = toolchain.Toolchains() + self._toolchains.Add('gcc', test=False) + + def _RunBuildman(self, *args): + return command.RunPipe([[self._buildman_pathname] + list(args)], + capture=True, capture_stderr=True) + + def _RunControl(self, *args, **kwargs): + sys.argv = [sys.argv[0]] + list(args) + options, args = cmdline.ParseArgs() + result = control.DoBuildman(options, args, toolchains=self._toolchains, + make_func=self._HandleMake, boards=self._boards, + clean_dir=kwargs.get('clean_dir', True)) + self._builder = control.builder + return result + + def testFullHelp(self): + command.test_result = None + result = self._RunBuildman('-H') + help_file = os.path.join(self._buildman_dir, 'README') + # Remove possible extraneous strings + extra = '::::::::::::::\n' + help_file + '\n::::::::::::::\n' + gothelp = result.stdout.replace(extra, '') + self.assertEqual(len(gothelp), os.path.getsize(help_file)) + self.assertEqual(0, len(result.stderr)) + self.assertEqual(0, result.return_code) + + def testHelp(self): + command.test_result = None + result = self._RunBuildman('-h') + help_file = os.path.join(self._buildman_dir, 'README') + self.assertTrue(len(result.stdout) > 1000) + self.assertEqual(0, len(result.stderr)) + self.assertEqual(0, result.return_code) + + def testGitSetup(self): + """Test gitutils.Setup(), from outside the module itself""" + command.test_result = command.CommandResult(return_code=1) + gitutil.Setup() + self.assertEqual(gitutil.use_no_decorate, False) + + command.test_result = command.CommandResult(return_code=0) + gitutil.Setup() + self.assertEqual(gitutil.use_no_decorate, True) + + def _HandleCommandGitLog(self, args): + if args[-1] == '--': + args = args[:-1] + if '-n0' in args: + return command.CommandResult(return_code=0) + elif args[-1] == 'upstream/master..%s' % self._test_branch: + return command.CommandResult(return_code=0, stdout=commit_shortlog) + elif args[:3] == ['--no-color', '--no-decorate', '--reverse']: + if args[-1] == self._test_branch: + count = int(args[3][2:]) + return command.CommandResult(return_code=0, + stdout=''.join(commit_log[:count])) + + # Not handled, so abort + print 'git log', args + sys.exit(1) + + def _HandleCommandGitConfig(self, args): + config = args[0] + if config == 'sendemail.aliasesfile': + return command.CommandResult(return_code=0) + elif config.startswith('branch.badbranch'): + return command.CommandResult(return_code=1) + elif config == 'branch.%s.remote' % self._test_branch: + return command.CommandResult(return_code=0, stdout='upstream\n') + elif config == 'branch.%s.merge' % self._test_branch: + return command.CommandResult(return_code=0, + stdout='refs/heads/master\n') + + # Not handled, so abort + print 'git config', args + sys.exit(1) + + def _HandleCommandGit(self, in_args): + """Handle execution of a git command + + This uses a hacked-up parser. + + Args: + in_args: Arguments after 'git' from the command line + """ + git_args = [] # Top-level arguments to git itself + sub_cmd = None # Git sub-command selected + args = [] # Arguments to the git sub-command + for arg in in_args: + if sub_cmd: + args.append(arg) + elif arg[0] == '-': + git_args.append(arg) + else: + if git_args and git_args[-1] in ['--git-dir', '--work-tree']: + git_args.append(arg) + else: + sub_cmd = arg + if sub_cmd == 'config': + return self._HandleCommandGitConfig(args) + elif sub_cmd == 'log': + return self._HandleCommandGitLog(args) + elif sub_cmd == 'clone': + return command.CommandResult(return_code=0) + elif sub_cmd == 'checkout': + return command.CommandResult(return_code=0) + + # Not handled, so abort + print 'git', git_args, sub_cmd, args + sys.exit(1) + + def _HandleCommandNm(self, args): + return command.CommandResult(return_code=0) + + def _HandleCommandObjdump(self, args): + return command.CommandResult(return_code=0) + + def _HandleCommandObjcopy(self, args): + return command.CommandResult(return_code=0) + + def _HandleCommandSize(self, args): + return command.CommandResult(return_code=0) + + def _HandleCommand(self, **kwargs): + """Handle a command execution. + + The command is in kwargs['pipe-list'], as a list of pipes, each a + list of commands. The command should be emulated as required for + testing purposes. + + Returns: + A CommandResult object + """ + pipe_list = kwargs['pipe_list'] + wc = False + if len(pipe_list) != 1: + if pipe_list[1] == ['wc', '-l']: + wc = True + else: + print 'invalid pipe', kwargs + sys.exit(1) + cmd = pipe_list[0][0] + args = pipe_list[0][1:] + result = None + if cmd == 'git': + result = self._HandleCommandGit(args) + elif cmd == './scripts/show-gnu-make': + return command.CommandResult(return_code=0, stdout='make') + elif cmd.endswith('nm'): + return self._HandleCommandNm(args) + elif cmd.endswith('objdump'): + return self._HandleCommandObjdump(args) + elif cmd.endswith('objcopy'): + return self._HandleCommandObjcopy(args) + elif cmd.endswith( 'size'): + return self._HandleCommandSize(args) + + if not result: + # Not handled, so abort + print 'unknown command', kwargs + sys.exit(1) + + if wc: + result.stdout = len(result.stdout.splitlines()) + return result + + def _HandleMake(self, commit, brd, stage, cwd, *args, **kwargs): + """Handle execution of 'make' + + Args: + commit: Commit object that is being built + brd: Board object that is being built + stage: Stage that we are at (mrproper, config, build) + cwd: Directory where make should be run + args: Arguments to pass to make + kwargs: Arguments to pass to command.RunPipe() + """ + self._make_calls += 1 + if stage == 'mrproper': + return command.CommandResult(return_code=0) + elif stage == 'config': + return command.CommandResult(return_code=0, + combined='Test configuration complete') + elif stage == 'build': + stderr = '' + if type(commit) is not str: + stderr = self._error.get((brd.target, commit.sequence)) + if stderr: + return command.CommandResult(return_code=1, stderr=stderr) + return command.CommandResult(return_code=0) + + # Not handled, so abort + print 'make', stage + sys.exit(1) + + # Example function to print output lines + def print_lines(self, lines): + print len(lines) + for line in lines: + print line + #self.print_lines(terminal.GetPrintTestLines()) + + def testNoBoards(self): + """Test that buildman aborts when there are no boards""" + self._boards = board.Boards() + with self.assertRaises(SystemExit): + self._RunControl() + + def testCurrentSource(self): + """Very simple test to invoke buildman on the current source""" + self.setupToolchains(); + self._RunControl() + lines = terminal.GetPrintTestLines() + self.assertIn('Building current source for %d boards' % len(boards), + lines[0].text) + + def testBadBranch(self): + """Test that we can detect an invalid branch""" + with self.assertRaises(ValueError): + self._RunControl('-b', 'badbranch') + + def testBadToolchain(self): + """Test that missing toolchains are detected""" + self.setupToolchains(); + ret_code = self._RunControl('-b', TEST_BRANCH) + lines = terminal.GetPrintTestLines() + + # Buildman always builds the upstream commit as well + self.assertIn('Building %d commits for %d boards' % + (self._commits, len(boards)), lines[0].text) + self.assertEqual(self._builder.count, self._total_builds) + + # Only sandbox should succeed, the others don't have toolchains + self.assertEqual(self._builder.fail, + self._total_builds - self._commits) + self.assertEqual(ret_code, 128) + + for commit in range(self._commits): + for board in self._boards.GetList(): + if board.arch != 'sandbox': + errfile = self._builder.GetErrFile(commit, board.target) + fd = open(errfile) + self.assertEqual(fd.readlines(), + ['No tool chain for %s\n' % board.arch]) + fd.close() + + def testBranch(self): + """Test building a branch with all toolchains present""" + self._RunControl('-b', TEST_BRANCH) + self.assertEqual(self._builder.count, self._total_builds) + self.assertEqual(self._builder.fail, 0) + + def testCount(self): + """Test building a specific number of commitst""" + self._RunControl('-b', TEST_BRANCH, '-c2') + self.assertEqual(self._builder.count, 2 * len(boards)) + self.assertEqual(self._builder.fail, 0) + # Each board has a mrproper, config, and then one make per commit + self.assertEqual(self._make_calls, len(boards) * (2 + 2)) + + def testIncremental(self): + """Test building a branch twice - the second time should do nothing""" + self._RunControl('-b', TEST_BRANCH) + + # Each board has a mrproper, config, and then one make per commit + self.assertEqual(self._make_calls, len(boards) * (self._commits + 2)) + self._make_calls = 0 + self._RunControl('-b', TEST_BRANCH, clean_dir=False) + self.assertEqual(self._make_calls, 0) + self.assertEqual(self._builder.count, self._total_builds) + self.assertEqual(self._builder.fail, 0) + + def testForceBuild(self): + """The -f flag should force a rebuild""" + self._RunControl('-b', TEST_BRANCH) + self._make_calls = 0 + self._RunControl('-b', TEST_BRANCH, '-f', clean_dir=False) + # Each board has a mrproper, config, and then one make per commit + self.assertEqual(self._make_calls, len(boards) * (self._commits + 2)) + + def testForceReconfigure(self): + """The -f flag should force a rebuild""" + self._RunControl('-b', TEST_BRANCH, '-C') + # Each commit has a mrproper, config and make + self.assertEqual(self._make_calls, len(boards) * self._commits * 3) + + def testErrors(self): + """Test handling of build errors""" + self._error['board2', 1] = 'fred\n' + self._RunControl('-b', TEST_BRANCH) + self.assertEqual(self._builder.count, self._total_builds) + self.assertEqual(self._builder.fail, 1) + + # Remove the error. This should have no effect since the commit will + # not be rebuilt + del self._error['board2', 1] + self._make_calls = 0 + self._RunControl('-b', TEST_BRANCH, clean_dir=False) + self.assertEqual(self._builder.count, self._total_builds) + self.assertEqual(self._make_calls, 0) + self.assertEqual(self._builder.fail, 1) + + # Now use the -F flag to force rebuild of the bad commit + self._RunControl('-b', TEST_BRANCH, '-F', clean_dir=False) + self.assertEqual(self._builder.count, self._total_builds) + self.assertEqual(self._builder.fail, 0) + self.assertEqual(self._make_calls, 3) + + def testBranchWithSlash(self): + """Test building a branch with a '/' in the name""" + self._test_branch = '/__dev/__testbranch' + self._RunControl('-b', self._test_branch, clean_dir=False) + self.assertEqual(self._builder.count, self._total_builds) + self.assertEqual(self._builder.fail, 0) + + def testBadOutputDir(self): + """Test building with an output dir the same as out current dir""" + self._test_branch = '/__dev/__testbranch' + with self.assertRaises(SystemExit): + self._RunControl('-b', self._test_branch, '-o', os.getcwd()) + with self.assertRaises(SystemExit): + self._RunControl('-b', self._test_branch, '-o', + os.path.join(os.getcwd(), 'test')) diff --git a/tools/u-boot-tools/buildman/kconfiglib.py b/tools/u-boot-tools/buildman/kconfiglib.py new file mode 100644 index 0000000..d68af05 --- /dev/null +++ b/tools/u-boot-tools/buildman/kconfiglib.py @@ -0,0 +1,3544 @@ +# SPDX-License-Identifier: ISC +# +# Author: Ulf Magnusson +# https://github.com/ulfalizer/Kconfiglib + +# This is Kconfiglib, a Python library for scripting, debugging, and extracting +# information from Kconfig-based configuration systems. To view the +# documentation, run +# +# $ pydoc kconfiglib +# +# or, if you prefer HTML, +# +# $ pydoc -w kconfiglib +# +# The examples/ subdirectory contains examples, to be run with e.g. +# +# $ make scriptconfig SCRIPT=Kconfiglib/examples/print_tree.py +# +# Look in testsuite.py for the test suite. + +""" +Kconfiglib is a Python library for scripting and extracting information from +Kconfig-based configuration systems. Features include the following: + + - Symbol values and properties can be looked up and values assigned + programmatically. + - .config files can be read and written. + - Expressions can be evaluated in the context of a Kconfig configuration. + - Relations between symbols can be quickly determined, such as finding all + symbols that reference a particular symbol. + - Highly compatible with the scripts/kconfig/*conf utilities. The test suite + automatically compares outputs between Kconfiglib and the C implementation + for a large number of cases. + +For the Linux kernel, scripts are run using + + $ make scriptconfig [ARCH=<arch>] SCRIPT=<path to script> [SCRIPT_ARG=<arg>] + +Using the 'scriptconfig' target ensures that required environment variables +(SRCARCH, ARCH, srctree, KERNELVERSION, etc.) are set up correctly. + +Scripts receive the name of the Kconfig file to load in sys.argv[1]. As of +Linux 4.1.0-rc5, this is always "Kconfig" from the kernel top-level directory. +If an argument is provided with SCRIPT_ARG, it appears as sys.argv[2]. + +To get an interactive Python prompt with Kconfiglib preloaded and a Config +object 'c' created, run + + $ make iscriptconfig [ARCH=<arch>] + +Kconfiglib supports both Python 2 and Python 3. For (i)scriptconfig, the Python +interpreter to use can be passed in PYTHONCMD, which defaults to 'python'. PyPy +works well too, and might give a nice speedup for long-running jobs. + +The examples/ directory contains short example scripts, which can be run with +e.g. + + $ make scriptconfig SCRIPT=Kconfiglib/examples/print_tree.py + +or + + $ make scriptconfig SCRIPT=Kconfiglib/examples/help_grep.py SCRIPT_ARG=kernel + +testsuite.py contains the test suite. See the top of the script for how to run +it. + +Credits: Written by Ulf "Ulfalizer" Magnusson + +Send bug reports, suggestions and other feedback to ulfalizer a.t Google's +email service. Don't wrestle with internal APIs. Tell me what you need and I +might add it in a safe way as a client API instead.""" + +import os +import platform +import re +import sys + +# File layout: +# +# Public classes +# Public functions +# Internal classes +# Internal functions +# Internal global constants + +# Line length: 79 columns + +# +# Public classes +# + +class Config(object): + + """Represents a Kconfig configuration, e.g. for i386 or ARM. This is the + set of symbols and other items appearing in the configuration together with + their values. Creating any number of Config objects -- including for + different architectures -- is safe; Kconfiglib has no global state.""" + + # + # Public interface + # + + def __init__(self, filename="Kconfig", base_dir=None, print_warnings=True, + print_undef_assign=False): + """Creates a new Config object, representing a Kconfig configuration. + Raises Kconfig_Syntax_Error on syntax errors. + + filename (default: "Kconfig"): The base Kconfig file of the + configuration. For the Linux kernel, you'll probably want "Kconfig" + from the top-level directory, as environment variables will make + sure the right Kconfig is included from there + (arch/<architecture>/Kconfig). If you are using Kconfiglib via 'make + scriptconfig', the filename of the base base Kconfig file will be in + sys.argv[1]. + + base_dir (default: None): The base directory relative to which 'source' + statements within Kconfig files will work. For the Linux kernel this + should be the top-level directory of the kernel tree. $-references + to existing environment variables will be expanded. + + If None (the default), the environment variable 'srctree' will be + used if set, and the current directory otherwise. 'srctree' is set + by the Linux makefiles to the top-level kernel directory. A default + of "." would not work with an alternative build directory. + + print_warnings (default: True): Set to True if warnings related to this + configuration should be printed to stderr. This can be changed later + with Config.set_print_warnings(). It is provided as a constructor + argument since warnings might be generated during parsing. + + print_undef_assign (default: False): Set to True if informational + messages related to assignments to undefined symbols should be + printed to stderr for this configuration. Can be changed later with + Config.set_print_undef_assign().""" + + # The set of all symbols, indexed by name (a string) + self.syms = {} + # Python 2/3 compatibility hack. This is the only one needed. + self.syms_iter = self.syms.values if sys.version_info[0] >= 3 else \ + self.syms.itervalues + + # The set of all defined symbols in the configuration in the order they + # appear in the Kconfig files. This excludes the special symbols n, m, + # and y as well as symbols that are referenced but never defined. + self.kconfig_syms = [] + + # The set of all named choices (yes, choices can have names), indexed + # by name (a string) + self.named_choices = {} + + # Lists containing all choices, menus and comments in the configuration + self.choices = [] + self.menus = [] + self.comments = [] + + def register_special_symbol(type_, name, val): + sym = Symbol() + sym.is_special_ = True + sym.is_defined_ = True + sym.config = self + sym.name = name + sym.type = type_ + sym.cached_val = val + self.syms[name] = sym + return sym + + # The special symbols n, m and y, used as shorthand for "n", "m" and + # "y" + self.n = register_special_symbol(TRISTATE, "n", "n") + self.m = register_special_symbol(TRISTATE, "m", "m") + self.y = register_special_symbol(TRISTATE, "y", "y") + # DEFCONFIG_LIST uses this + register_special_symbol(STRING, "UNAME_RELEASE", platform.uname()[2]) + + # The symbol with "option defconfig_list" set, containing a list of + # default .config files + self.defconfig_sym = None + + # See Symbol.get_(src)arch() + self.arch = os.environ.get("ARCH") + self.srcarch = os.environ.get("SRCARCH") + + # If you set CONFIG_ in the environment, Kconfig will prefix all symbols + # with its value when saving the configuration, instead of using the default, "CONFIG_". + self.config_prefix = os.environ.get("CONFIG_") + if self.config_prefix is None: + self.config_prefix = "CONFIG_" + + # See Config.__init__(). We need this for get_defconfig_filename(). + self.srctree = os.environ.get("srctree") + if self.srctree is None: + self.srctree = "." + + self.filename = filename + self.base_dir = self.srctree if base_dir is None else \ + os.path.expandvars(base_dir) + + # The 'mainmenu' text + self.mainmenu_text = None + + # The filename of the most recently loaded .config file + self.config_filename = None + # The textual header of the most recently loaded .config, uncommented + self.config_header = None + + self.print_warnings = print_warnings + self.print_undef_assign = print_undef_assign + self._warnings = [] + + # For parsing routines that stop when finding a line belonging to a + # different construct, these holds that line and the tokenized version + # of that line. The purpose is to avoid having to re-tokenize the line, + # which is inefficient and causes problems when recording references to + # symbols. + self.end_line = None + self.end_line_tokens = None + + # See the comment in _parse_expr(). + self._cur_item = None + self._line = None + self._filename = None + self._linenr = None + self._transform_m = None + + # Parse the Kconfig files + self.top_block = [] + self._parse_file(filename, None, None, None, self.top_block) + + # Build Symbol.dep for all symbols + self._build_dep() + + def get_arch(self): + """Returns the value the environment variable ARCH had at the time the + Config instance was created, or None if ARCH was not set. For the + kernel, this corresponds to the architecture being built for, with + values such as "i386" or "mips".""" + return self.arch + + def get_srcarch(self): + """Returns the value the environment variable SRCARCH had at the time + the Config instance was created, or None if SRCARCH was not set. For + the kernel, this corresponds to the particular arch/ subdirectory + containing architecture-specific code.""" + return self.srcarch + + def get_srctree(self): + """Returns the value the environment variable srctree had at the time + the Config instance was created, or None if srctree was not defined. + This variable points to the source directory and is used when building + in a separate directory.""" + return self.srctree + + def get_base_dir(self): + """Returns the base directory relative to which 'source' statements + will work, passed as an argument to Config.__init__().""" + return self.base_dir + + def get_kconfig_filename(self): + """Returns the name of the (base) kconfig file this configuration was + loaded from.""" + return self.filename + + def get_config_filename(self): + """Returns the filename of the most recently loaded configuration file, + or None if no configuration has been loaded.""" + return self.config_filename + + def get_config_header(self): + """Returns the (uncommented) textual header of the .config file most + recently loaded with load_config(). Returns None if no .config file has + been loaded or if the most recently loaded .config file has no header. + The header consists of all lines up to but not including the first line + that either + + 1. Does not start with "#" + 2. Has the form "# CONFIG_FOO is not set." + """ + return self.config_header + + def get_mainmenu_text(self): + """Returns the text of the 'mainmenu' statement (with $-references to + symbols replaced by symbol values), or None if the configuration has no + 'mainmenu' statement.""" + return None if self.mainmenu_text is None else \ + self._expand_sym_refs(self.mainmenu_text) + + def get_defconfig_filename(self): + """Returns the name of the defconfig file, which is the first existing + file in the list given in a symbol having 'option defconfig_list' set. + $-references to symbols will be expanded ("$FOO bar" -> "foo bar" if + FOO has the value "foo"). Returns None in case of no defconfig file. + Setting 'option defconfig_list' on multiple symbols currently results + in undefined behavior. + + If the environment variable 'srctree' was set when the Config was + created, get_defconfig_filename() will first look relative to that + directory before looking in the current directory; see + Config.__init__(). + + WARNING: A wart here is that scripts/kconfig/Makefile sometimes uses + the --defconfig=<defconfig> option when calling the C implementation of + e.g. 'make defconfig'. This option overrides the 'option + defconfig_list' symbol, meaning the result from + get_defconfig_filename() might not match what 'make defconfig' would + use. That probably ought to be worked around somehow, so that this + function always gives the "expected" result.""" + if self.defconfig_sym is None: + return None + for filename, cond_expr in self.defconfig_sym.def_exprs: + if self._eval_expr(cond_expr) == "y": + filename = self._expand_sym_refs(filename) + # We first look in $srctree. os.path.join() won't work here as + # an absolute path in filename would override $srctree. + srctree_filename = os.path.normpath(self.srctree + "/" + + filename) + if os.path.exists(srctree_filename): + return srctree_filename + if os.path.exists(filename): + return filename + return None + + def get_symbol(self, name): + """Returns the symbol with name 'name', or None if no such symbol + appears in the configuration. An alternative shorthand is conf[name], + where conf is a Config instance, though that will instead raise + KeyError if the symbol does not exist.""" + return self.syms.get(name) + + def __getitem__(self, name): + """Returns the symbol with name 'name'. Raises KeyError if the symbol + does not appear in the configuration.""" + return self.syms[name] + + def get_symbols(self, all_symbols=True): + """Returns a list of symbols from the configuration. An alternative for + iterating over all defined symbols (in the order of definition) is + + for sym in config: + ... + + which relies on Config implementing __iter__() and is equivalent to + + for sym in config.get_symbols(False): + ... + + all_symbols (default: True): If True, all symbols -- including special + and undefined symbols -- will be included in the result, in an + undefined order. If False, only symbols actually defined and not + merely referred to in the configuration will be included in the + result, and will appear in the order that they are defined within + the Kconfig configuration files.""" + return list(self.syms.values()) if all_symbols else self.kconfig_syms + + def __iter__(self): + """Convenience function for iterating over the set of all defined + symbols in the configuration, used like + + for sym in conf: + ... + + The iteration happens in the order of definition within the Kconfig + configuration files. Symbols only referred to but not defined will not + be included, nor will the special symbols n, m, and y. If you want to + include such symbols as well, see config.get_symbols().""" + return iter(self.kconfig_syms) + + def get_choices(self): + """Returns a list containing all choice statements in the + configuration, in the order they appear in the Kconfig files.""" + return self.choices + + def get_menus(self): + """Returns a list containing all menus in the configuration, in the + order they appear in the Kconfig files.""" + return self.menus + + def get_comments(self): + """Returns a list containing all comments in the configuration, in the + order they appear in the Kconfig files.""" + return self.comments + + def get_top_level_items(self): + """Returns a list containing the items (symbols, menus, choices, and + comments) at the top level of the configuration -- that is, all items + that do not appear within a menu or choice. The items appear in the + same order as within the configuration.""" + return self.top_block + + def load_config(self, filename, replace=True): + """Loads symbol values from a file in the familiar .config format. + Equivalent to calling Symbol.set_user_value() to set each of the + values. + + "# CONFIG_FOO is not set" within a .config file is treated specially + and sets the user value of FOO to 'n'. The C implementation works the + same way. + + filename: The .config file to load. $-references to existing + environment variables will be expanded. For scripts to work even when + an alternative build directory is used with the Linux kernel, you + need to refer to the top-level kernel directory with "$srctree". + + replace (default: True): True if the configuration should replace the + old configuration; False if it should add to it. + + Returns a list or warnings (hopefully empty) + """ + + self._warnings = [] + # Regular expressions for parsing .config files + _set_re_match = re.compile(r"{}(\w+)=(.*)".format(self.config_prefix)).match + _unset_re_match = re.compile(r"# {}(\w+) is not set".format(self.config_prefix)).match + + # Put this first so that a missing file doesn't screw up our state + filename = os.path.expandvars(filename) + line_feeder = _FileFeed(filename) + + self.config_filename = filename + + # + # Read header + # + + def is_header_line(line): + return line is not None and line.startswith("#") and \ + not _unset_re_match(line) + + self.config_header = None + + line = line_feeder.peek_next() + if is_header_line(line): + self.config_header = "" + while is_header_line(line_feeder.peek_next()): + self.config_header += line_feeder.get_next()[1:] + # Remove trailing newline + if self.config_header.endswith("\n"): + self.config_header = self.config_header[:-1] + + # + # Read assignments. Hotspot for some workloads. + # + + def warn_override(filename, linenr, name, old_user_val, new_user_val): + self._warn('overriding the value of {0}. ' + 'Old value: "{1}", new value: "{2}".' + .format(name, old_user_val, new_user_val), + filename, linenr) + + # Invalidate everything to keep things simple. It might be possible to + # improve performance for the case where multiple configurations are + # loaded by only invalidating a symbol (and its dependent symbols) if + # the new user value differs from the old. One complication would be + # that symbols not mentioned in the .config must lose their user value + # when replace = True, which is the usual case. + if replace: + self.unset_user_values() + else: + self._invalidate_all() + + while 1: + line = line_feeder.get_next() + if line is None: + return self._warnings + + line = line.rstrip() + + set_match = _set_re_match(line) + if set_match: + name, val = set_match.groups() + + if val.startswith('"'): + if len(val) < 2 or val[-1] != '"': + _parse_error(line, "malformed string literal", + line_feeder.filename, line_feeder.linenr) + # Strip quotes and remove escapings. The unescaping + # procedure should be safe since " can only appear as \" + # inside the string. + val = val[1:-1].replace('\\"', '"').replace("\\\\", "\\") + + if name in self.syms: + sym = self.syms[name] + if sym.user_val is not None: + warn_override(line_feeder.filename, line_feeder.linenr, + name, sym.user_val, val) + + if sym.is_choice_sym: + user_mode = sym.parent.user_mode + if user_mode is not None and user_mode != val: + self._warn("assignment to {0} changes mode of " + 'containing choice from "{1}" to "{2}".' + .format(name, val, user_mode), + line_feeder.filename, + line_feeder.linenr) + + sym._set_user_value_no_invalidate(val, True) + else: + if self.print_undef_assign: + _stderr_msg('note: attempt to assign the value "{0}" ' + "to the undefined symbol {1}." + .format(val, name), + line_feeder.filename, line_feeder.linenr) + else: + unset_match = _unset_re_match(line) + if unset_match: + name = unset_match.group(1) + if name in self.syms: + sym = self.syms[name] + if sym.user_val is not None: + warn_override(line_feeder.filename, + line_feeder.linenr, + name, sym.user_val, "n") + + sym._set_user_value_no_invalidate("n", True) + + def write_config(self, filename, header=None): + """Writes out symbol values in the familiar .config format. + + Kconfiglib makes sure the format matches what the C implementation + would generate, down to whitespace. This eases testing. + + filename: The filename under which to save the configuration. + + header (default: None): A textual header that will appear at the + beginning of the file, with each line commented out automatically. + None means no header.""" + + for sym in self.syms_iter(): + sym.already_written = False + + with open(filename, "w") as f: + # Write header + if header is not None: + f.write(_comment(header) + "\n") + + # Build and write configuration + conf_strings = [] + _make_block_conf(self.top_block, conf_strings.append) + f.write("\n".join(conf_strings) + "\n") + + def eval(self, s): + """Returns the value of the expression 's' -- where 's' is represented + as a string -- in the context of the configuration. Raises + Kconfig_Syntax_Error if syntax errors are detected in 's'. + + For example, if FOO and BAR are tristate symbols at least one of which + has the value "y", then config.eval("y && (FOO || BAR)") => "y" + + This function always yields a tristate value. To get the value of + non-bool, non-tristate symbols, use Symbol.get_value(). + + The result of this function is consistent with how evaluation works for + conditional expressions in the configuration as well as in the C + implementation. "m" and m are rewritten as '"m" && MODULES' and 'm && + MODULES', respectively, and a result of "m" will get promoted to "y" if + we're running without modules. + + Syntax checking is somewhat lax, partly to be compatible with lax + parsing in the C implementation.""" + return self._eval_expr(self._parse_expr(self._tokenize(s, True), # Feed + None, # Current symbol/choice + s)) # line + + def unset_user_values(self): + """Resets the values of all symbols, as if Config.load_config() or + Symbol.set_user_value() had never been called.""" + for sym in self.syms_iter(): + sym._unset_user_value_no_recursive_invalidate() + + def set_print_warnings(self, print_warnings): + """Determines whether warnings related to this configuration (for + things like attempting to assign illegal values to symbols with + Symbol.set_user_value()) should be printed to stderr. + + print_warnings: True if warnings should be printed.""" + self.print_warnings = print_warnings + + def set_print_undef_assign(self, print_undef_assign): + """Determines whether informational messages related to assignments to + undefined symbols should be printed to stderr for this configuration. + + print_undef_assign: If True, such messages will be printed.""" + self.print_undef_assign = print_undef_assign + + def __str__(self): + """Returns a string containing various information about the Config.""" + return _lines("Configuration", + "File : " + + self.filename, + "Base directory : " + + self.base_dir, + "Value of $ARCH at creation time : " + + ("(not set)" if self.arch is None else self.arch), + "Value of $SRCARCH at creation time : " + + ("(not set)" if self.srcarch is None else + self.srcarch), + "Source tree (derived from $srctree;", + "defaults to '.' if $srctree isn't set) : " + + self.srctree, + "Most recently loaded .config : " + + ("(no .config loaded)" + if self.config_filename is None else + self.config_filename), + "Print warnings : " + + BOOL_STR[self.print_warnings], + "Print assignments to undefined symbols : " + + BOOL_STR[self.print_undef_assign]) + + # + # Private methods + # + + # + # Kconfig parsing + # + + def _parse_file(self, filename, parent, deps, visible_if_deps, block): + """Parses the Kconfig file 'filename'. Appends the Items in the file + (and any file it sources) to the list passed in the 'block' parameter. + See _parse_block() for the meaning of the parameters.""" + self._parse_block(_FileFeed(filename), None, parent, deps, + visible_if_deps, block) + + def _parse_block(self, line_feeder, end_marker, parent, deps, + visible_if_deps, block): + """Parses a block, which is the contents of either a file or an if, + menu, or choice statement. Appends the Items to the list passed in the + 'block' parameter. + + line_feeder: A _FileFeed instance feeding lines from a file. The + Kconfig language is line-based in practice. + + end_marker: The token that ends the block, e.g. T_ENDIF ("endif") for + ifs. None for files. + + parent: The enclosing menu or choice, or None if we're at the top + level. + + deps: Dependencies from enclosing menus, choices and ifs. + + visible_if_deps (default: None): 'visible if' dependencies from + enclosing menus. + + block: The list to add items to.""" + + while 1: + # Do we already have a tokenized line that we determined wasn't + # part of whatever we were parsing earlier? See comment in + # Config.__init__(). + if self.end_line is not None: + line = self.end_line + tokens = self.end_line_tokens + tokens.unget_all() + + self.end_line = None + self.end_line_tokens = None + else: + line = line_feeder.get_next() + if line is None: + if end_marker is not None: + raise Kconfig_Syntax_Error("Unexpected end of file {0}" + .format(line_feeder.filename)) + return + + tokens = self._tokenize(line, False, line_feeder.filename, + line_feeder.linenr) + + t0 = tokens.get_next() + if t0 is None: + continue + + # Cases are ordered roughly by frequency, which speeds things up a + # bit + + if t0 == T_CONFIG or t0 == T_MENUCONFIG: + # The tokenizer will automatically allocate a new Symbol object + # for any new names it encounters, so we don't need to worry + # about that here. + sym = tokens.get_next() + + # Symbols defined in multiple places get the parent of their + # first definition. However, for symbols whose parents are + # choice statements, the choice statement takes precedence. + if not sym.is_defined_ or isinstance(parent, Choice): + sym.parent = parent + sym.is_defined_ = True + + self._parse_properties(line_feeder, sym, deps, visible_if_deps) + + self.kconfig_syms.append(sym) + block.append(sym) + + elif t0 == T_SOURCE: + kconfig_file = tokens.get_next() + exp_kconfig_file = self._expand_sym_refs(kconfig_file) + f = os.path.join(self.base_dir, exp_kconfig_file) + if not os.path.exists(f): + raise IOError('{0}:{1}: sourced file "{2}" (expands to ' + '"{3}") not found. Perhaps base_dir ' + '(argument to Config.__init__(), currently ' + '"{4}") is set to the wrong value.' + .format(line_feeder.filename, + line_feeder.linenr, + kconfig_file, exp_kconfig_file, + self.base_dir)) + # Add items to the same block + self._parse_file(f, parent, deps, visible_if_deps, block) + + elif t0 == end_marker: + # We have reached the end of the block + return + + elif t0 == T_IF: + # If statements are treated as syntactic sugar for adding + # dependencies to enclosed items and do not have an explicit + # object representation. + + dep_expr = self._parse_expr(tokens, None, line, + line_feeder.filename, + line_feeder.linenr) + # Add items to the same block + self._parse_block(line_feeder, T_ENDIF, parent, + _make_and(dep_expr, deps), + visible_if_deps, block) + + elif t0 == T_COMMENT: + comment = Comment() + comment.config = self + comment.parent = parent + comment.filename = line_feeder.filename + comment.linenr = line_feeder.linenr + comment.text = tokens.get_next() + + self._parse_properties(line_feeder, comment, deps, + visible_if_deps) + + self.comments.append(comment) + block.append(comment) + + elif t0 == T_MENU: + menu = Menu() + menu.config = self + menu.parent = parent + menu.filename = line_feeder.filename + menu.linenr = line_feeder.linenr + menu.title = tokens.get_next() + + self._parse_properties(line_feeder, menu, deps, + visible_if_deps) + + # This needs to go before _parse_block() so that we get the + # proper menu ordering in the case of nested functions + self.menus.append(menu) + # Parse contents and put Items in menu.block + self._parse_block(line_feeder, T_ENDMENU, menu, menu.dep_expr, + _make_and(visible_if_deps, + menu.visible_if_expr), + menu.block) + + block.append(menu) + + elif t0 == T_CHOICE: + name = tokens.get_next() + if name is None: + choice = Choice() + self.choices.append(choice) + else: + # Named choice + choice = self.named_choices.get(name) + if choice is None: + choice = Choice() + choice.name = name + self.named_choices[name] = choice + self.choices.append(choice) + + choice.config = self + choice.parent = parent + + choice.def_locations.append((line_feeder.filename, + line_feeder.linenr)) + + self._parse_properties(line_feeder, choice, deps, + visible_if_deps) + + # Parse contents and put Items in choice.block + self._parse_block(line_feeder, T_ENDCHOICE, choice, deps, + visible_if_deps, choice.block) + + choice._determine_actual_symbols() + + # If no type is specified for the choice, its type is that of + # the first choice item with a specified type + if choice.type == UNKNOWN: + for item in choice.actual_symbols: + if item.type != UNKNOWN: + choice.type = item.type + break + + # Each choice item of UNKNOWN type gets the type of the choice + for item in choice.actual_symbols: + if item.type == UNKNOWN: + item.type = choice.type + + block.append(choice) + + elif t0 == T_MAINMENU: + text = tokens.get_next() + if self.mainmenu_text is not None: + self._warn("overriding 'mainmenu' text. " + 'Old value: "{0}", new value: "{1}".' + .format(self.mainmenu_text, text), + line_feeder.filename, line_feeder.linenr) + self.mainmenu_text = text + + else: + _parse_error(line, "unrecognized construct", + line_feeder.filename, line_feeder.linenr) + + def _parse_properties(self, line_feeder, stmt, deps, visible_if_deps): + """Parsing of properties for symbols, menus, choices, and comments. + Takes care of propagating dependencies from enclosing menus and ifs.""" + + def parse_val_and_cond(tokens, line, filename, linenr): + """Parses '<expr1> if <expr2>' constructs, where the 'if' part is + optional. Returns a tuple containing the parsed expressions, with + None as the second element if the 'if' part is missing.""" + return (self._parse_expr(tokens, stmt, line, filename, linenr, + False), + self._parse_expr(tokens, stmt, line, filename, linenr) + if tokens.check(T_IF) else None) + + # In case the symbol is defined in multiple locations, we need to + # remember what prompts, defaults, selects, and implies are new for + # this definition, as "depends on" should only apply to the local + # definition. + new_prompt = None + new_def_exprs = [] + new_selects = [] + new_implies = [] + + # Dependencies from 'depends on' statements + depends_on_expr = None + + while 1: + line = line_feeder.get_next() + if line is None: + break + + filename = line_feeder.filename + linenr = line_feeder.linenr + + tokens = self._tokenize(line, False, filename, linenr) + + t0 = tokens.get_next() + if t0 is None: + continue + + # Cases are ordered roughly by frequency, which speeds things up a + # bit + + if t0 == T_DEPENDS: + if not tokens.check(T_ON): + _parse_error(line, 'expected "on" after "depends"', + filename, linenr) + + parsed_deps = self._parse_expr(tokens, stmt, line, filename, + linenr) + + if isinstance(stmt, (Menu, Comment)): + stmt.orig_deps = _make_and(stmt.orig_deps, parsed_deps) + else: + depends_on_expr = _make_and(depends_on_expr, parsed_deps) + + elif t0 == T_HELP: + # Find first non-blank (not all-space) line and get its + # indentation + line = line_feeder.next_nonblank() + if line is None: + stmt.help = "" + break + indent = _indentation(line) + if indent == 0: + # If the first non-empty lines has zero indent, there is no + # help text + stmt.help = "" + line_feeder.unget() + break + + # The help text goes on till the first non-empty line with less + # indent + help_lines = [_deindent(line, indent)] + while 1: + line = line_feeder.get_next() + if line is None or \ + (not line.isspace() and _indentation(line) < indent): + stmt.help = "".join(help_lines) + break + help_lines.append(_deindent(line, indent)) + + if line is None: + break + + line_feeder.unget() + + elif t0 == T_SELECT: + target = tokens.get_next() + + stmt.referenced_syms.add(target) + stmt.selected_syms.add(target) + + new_selects.append( + (target, + self._parse_expr(tokens, stmt, line, filename, linenr) + if tokens.check(T_IF) else None)) + + elif t0 == T_IMPLY: + target = tokens.get_next() + + stmt.referenced_syms.add(target) + stmt.implied_syms.add(target) + + new_implies.append( + (target, + self._parse_expr(tokens, stmt, line, filename, linenr) + if tokens.check(T_IF) else None)) + + elif t0 in (T_BOOL, T_TRISTATE, T_INT, T_HEX, T_STRING): + stmt.type = TOKEN_TO_TYPE[t0] + if tokens.peek_next() is not None: + new_prompt = parse_val_and_cond(tokens, line, filename, + linenr) + + elif t0 == T_DEFAULT: + new_def_exprs.append(parse_val_and_cond(tokens, line, filename, + linenr)) + + elif t0 == T_DEF_BOOL: + stmt.type = BOOL + if tokens.peek_next() is not None: + new_def_exprs.append(parse_val_and_cond(tokens, line, + filename, linenr)) + + elif t0 == T_PROMPT: + # 'prompt' properties override each other within a single + # definition of a symbol, but additional prompts can be added + # by defining the symbol multiple times; hence 'new_prompt' + # instead of 'prompt'. + new_prompt = parse_val_and_cond(tokens, line, filename, linenr) + + elif t0 == T_RANGE: + low = tokens.get_next() + high = tokens.get_next() + stmt.referenced_syms.add(low) + stmt.referenced_syms.add(high) + + stmt.ranges.append( + (low, high, + self._parse_expr(tokens, stmt, line, filename, linenr) + if tokens.check(T_IF) else None)) + + elif t0 == T_DEF_TRISTATE: + stmt.type = TRISTATE + if tokens.peek_next() is not None: + new_def_exprs.append(parse_val_and_cond(tokens, line, + filename, linenr)) + + elif t0 == T_OPTION: + if tokens.check(T_ENV) and tokens.check(T_EQUAL): + env_var = tokens.get_next() + + stmt.is_special_ = True + stmt.is_from_env = True + + if env_var not in os.environ: + self._warn("The symbol {0} references the " + "non-existent environment variable {1} and " + "will get the empty string as its value. " + "If you're using Kconfiglib via " + "'make (i)scriptconfig', it should have " + "set up the environment correctly for you. " + "If you still got this message, that " + "might be an error, and you should email " + "ulfalizer a.t Google's email service.""" + .format(stmt.name, env_var), + filename, linenr) + + stmt.cached_val = "" + else: + stmt.cached_val = os.environ[env_var] + + elif tokens.check(T_DEFCONFIG_LIST): + self.defconfig_sym = stmt + + elif tokens.check(T_MODULES): + # To reduce warning spam, only warn if 'option modules' is + # set on some symbol that isn't MODULES, which should be + # safe. I haven't run into any projects that make use + # modules besides the kernel yet, and there it's likely to + # keep being called "MODULES". + if stmt.name != "MODULES": + self._warn("the 'modules' option is not supported. " + "Let me know if this is a problem for you; " + "it shouldn't be that hard to implement. " + "(Note that modules are still supported -- " + "Kconfiglib just assumes the symbol name " + "MODULES, like older versions of the C " + "implementation did when 'option modules' " + "wasn't used.)", + filename, linenr) + + elif tokens.check(T_ALLNOCONFIG_Y): + if not isinstance(stmt, Symbol): + _parse_error(line, + "the 'allnoconfig_y' option is only " + "valid for symbols", + filename, linenr) + stmt.allnoconfig_y = True + + else: + _parse_error(line, "unrecognized option", filename, linenr) + + elif t0 == T_VISIBLE: + if not tokens.check(T_IF): + _parse_error(line, 'expected "if" after "visible"', + filename, linenr) + if not isinstance(stmt, Menu): + _parse_error(line, + "'visible if' is only valid for menus", + filename, linenr) + + parsed_deps = self._parse_expr(tokens, stmt, line, filename, + linenr) + stmt.visible_if_expr = _make_and(stmt.visible_if_expr, + parsed_deps) + + elif t0 == T_OPTIONAL: + if not isinstance(stmt, Choice): + _parse_error(line, + '"optional" is only valid for choices', + filename, + linenr) + stmt.optional = True + + else: + # See comment in Config.__init__() + self.end_line = line + self.end_line_tokens = tokens + break + + # Done parsing properties. Now propagate 'depends on' and enclosing + # menu/if dependencies to expressions. + + # The set of symbols referenced directly by the statement plus all + # symbols referenced by enclosing menus and ifs + stmt.all_referenced_syms = stmt.referenced_syms | _get_expr_syms(deps) + + # Save original dependencies from enclosing menus and ifs + stmt.deps_from_containing = deps + + if isinstance(stmt, (Menu, Comment)): + stmt.dep_expr = _make_and(stmt.orig_deps, deps) + else: + # Symbol or Choice + + # See comment for 'menu_dep' + stmt.menu_dep = _make_and(deps, depends_on_expr) + + # Propagate dependencies to prompts + + if new_prompt is not None: + prompt, cond_expr = new_prompt + # Propagate 'visible if' dependencies from menus and local + # 'depends on' dependencies + cond_expr = _make_and(_make_and(cond_expr, visible_if_deps), + depends_on_expr) + # Save original + stmt.orig_prompts.append((prompt, cond_expr)) + # Finalize with dependencies from enclosing menus and ifs + stmt.prompts.append((prompt, _make_and(cond_expr, deps))) + + # Propagate dependencies to defaults + + # Propagate 'depends on' dependencies + new_def_exprs = [(val_expr, _make_and(cond_expr, depends_on_expr)) + for val_expr, cond_expr in new_def_exprs] + # Save original + stmt.orig_def_exprs.extend(new_def_exprs) + # Finalize with dependencies from enclosing menus and ifs + stmt.def_exprs.extend([(val_expr, _make_and(cond_expr, deps)) + for val_expr, cond_expr in new_def_exprs]) + + # Propagate dependencies to selects and implies + + # Only symbols can select and imply + if isinstance(stmt, Symbol): + # Propagate 'depends on' dependencies + new_selects = [(target, _make_and(cond_expr, depends_on_expr)) + for target, cond_expr in new_selects] + new_implies = [(target, _make_and(cond_expr, depends_on_expr)) + for target, cond_expr in new_implies] + # Save original + stmt.orig_selects.extend(new_selects) + stmt.orig_implies.extend(new_implies) + # Finalize with dependencies from enclosing menus and ifs + for target, cond in new_selects: + target.rev_dep = \ + _make_or(target.rev_dep, + _make_and(stmt, _make_and(cond, deps))) + for target, cond in new_implies: + target.weak_rev_dep = \ + _make_or(target.weak_rev_dep, + _make_and(stmt, _make_and(cond, deps))) + + def _parse_expr(self, feed, cur_item, line, filename=None, linenr=None, + transform_m=True): + """Parses an expression from the tokens in 'feed' using a simple + top-down approach. The result has the form + '(<operator>, [<parsed operands>])', where <operator> is e.g. + kconfiglib.AND. If there is only one operand (i.e., no && or ||), then + the operand is returned directly. This also goes for subexpressions. + + feed: _Feed instance containing the tokens for the expression. + + cur_item: The item (Symbol, Choice, Menu, or Comment) currently being + parsed, or None if we're not parsing an item. Used for recording + references to symbols. + + line: The line containing the expression being parsed. + + filename (default: None): The file containing the expression. + + linenr (default: None): The line number containing the expression. + + transform_m (default: False): Determines if 'm' should be rewritten to + 'm && MODULES' -- see parse_val_and_cond(). + + Expression grammar, in decreasing order of precedence: + + <expr> -> <symbol> + <symbol> '=' <symbol> + <symbol> '!=' <symbol> + '(' <expr> ')' + '!' <expr> + <expr> '&&' <expr> + <expr> '||' <expr>""" + + # Use instance variables to avoid having to pass these as arguments + # through the top-down parser in _parse_expr_rec(), which is tedious + # and obfuscates the code. A profiler run shows no noticeable + # performance difference. + self._cur_item = cur_item + self._transform_m = transform_m + self._line = line + self._filename = filename + self._linenr = linenr + + return self._parse_expr_rec(feed) + + def _parse_expr_rec(self, feed): + or_term = self._parse_or_term(feed) + if not feed.check(T_OR): + # Common case -- no need for an OR node since it's just a single + # operand + return or_term + or_terms = [or_term, self._parse_or_term(feed)] + while feed.check(T_OR): + or_terms.append(self._parse_or_term(feed)) + return (OR, or_terms) + + def _parse_or_term(self, feed): + and_term = self._parse_factor(feed) + if not feed.check(T_AND): + # Common case -- no need for an AND node since it's just a single + # operand + return and_term + and_terms = [and_term, self._parse_factor(feed)] + while feed.check(T_AND): + and_terms.append(self._parse_factor(feed)) + return (AND, and_terms) + + def _parse_factor(self, feed): + token = feed.get_next() + + if isinstance(token, (Symbol, str)): + if self._cur_item is not None and isinstance(token, Symbol): + self._cur_item.referenced_syms.add(token) + + next_token = feed.peek_next() + # For conditional expressions ('depends on <expr>', + # '... if <expr>', # etc.), "m" and m are rewritten to + # "m" && MODULES. + if next_token != T_EQUAL and next_token != T_UNEQUAL: + if self._transform_m and (token is self.m or token == "m"): + return (AND, ["m", self._sym_lookup("MODULES")]) + return token + + relation = EQUAL if (feed.get_next() == T_EQUAL) else UNEQUAL + token_2 = feed.get_next() + if self._cur_item is not None and isinstance(token_2, Symbol): + self._cur_item.referenced_syms.add(token_2) + return (relation, token, token_2) + + if token == T_NOT: + return (NOT, self._parse_factor(feed)) + + if token == T_OPEN_PAREN: + expr_parse = self._parse_expr_rec(feed) + if not feed.check(T_CLOSE_PAREN): + _parse_error(self._line, "missing end parenthesis", + self._filename, self._linenr) + return expr_parse + + _parse_error(self._line, "malformed expression", self._filename, + self._linenr) + + def _tokenize(self, s, for_eval, filename=None, linenr=None): + """Returns a _Feed instance containing tokens derived from the string + 's'. Registers any new symbols encountered (via _sym_lookup()). + + (I experimented with a pure regular expression implementation, but it + came out slower, less readable, and wouldn't have been as flexible.) + + for_eval: True when parsing an expression for a call to Config.eval(), + in which case we should not treat the first token specially nor + register new symbols.""" + + s = s.strip() + if s == "" or s[0] == "#": + return _Feed([]) + + if for_eval: + previous = None # The previous token seen + tokens = [] + i = 0 # The current index in the string being tokenized + + else: + # The initial word on a line is parsed specially. Let + # command_chars = [A-Za-z0-9_]. Then + # - leading non-command_chars characters are ignored, and + # - the first token consists the following one or more + # command_chars characters. + # This is why things like "----help--" are accepted. + initial_token_match = _initial_token_re_match(s) + if initial_token_match is None: + return _Feed([]) + keyword = _get_keyword(initial_token_match.group(1)) + if keyword == T_HELP: + # Avoid junk after "help", e.g. "---", being registered as a + # symbol + return _Feed([T_HELP]) + if keyword is None: + # We expect a keyword as the first token + _tokenization_error(s, filename, linenr) + + previous = keyword + tokens = [keyword] + # The current index in the string being tokenized + i = initial_token_match.end() + + # _tokenize() is a hotspot during parsing, and this speeds things up a + # bit + strlen = len(s) + append = tokens.append + + # Main tokenization loop. (Handles tokens past the first one.) + while i < strlen: + # Test for an identifier/keyword preceded by whitespace first; this + # is the most common case. + id_keyword_match = _id_keyword_re_match(s, i) + if id_keyword_match: + # We have an identifier or keyword. The above also stripped any + # whitespace for us. + name = id_keyword_match.group(1) + # Jump past it + i = id_keyword_match.end() + + keyword = _get_keyword(name) + if keyword is not None: + # It's a keyword + append(keyword) + elif previous in STRING_LEX: + # What would ordinarily be considered an identifier is + # treated as a string after certain tokens + append(name) + else: + # It's a symbol name. _sym_lookup() will take care of + # allocating a new Symbol instance if it's the first time + # we see it. + sym = self._sym_lookup(name, for_eval) + + if previous == T_CONFIG or previous == T_MENUCONFIG: + # If the previous token is T_(MENU)CONFIG + # ("(menu)config"), we're tokenizing the first line of + # a symbol definition, and should remember this as a + # location where the symbol is defined + sym.def_locations.append((filename, linenr)) + else: + # Otherwise, it's a reference to the symbol + sym.ref_locations.append((filename, linenr)) + + append(sym) + + else: + # Not an identifier/keyword + + while i < strlen and s[i].isspace(): + i += 1 + if i == strlen: + break + c = s[i] + i += 1 + + # String literal (constant symbol) + if c == '"' or c == "'": + if "\\" in s: + # Slow path: This could probably be sped up, but it's a + # very unusual case anyway. + quote = c + val = "" + while 1: + if i >= len(s): + _tokenization_error(s, filename, linenr) + c = s[i] + if c == quote: + break + if c == "\\": + if i + 1 >= len(s): + _tokenization_error(s, filename, linenr) + val += s[i + 1] + i += 2 + else: + val += c + i += 1 + i += 1 + append(val) + else: + # Fast path: If the string contains no backslashes + # (almost always) we can simply look for the matching + # quote. + end = s.find(c, i) + if end == -1: + _tokenization_error(s, filename, linenr) + append(s[i:end]) + i = end + 1 + + elif c == "&": + # Invalid characters are ignored + if i >= len(s) or s[i] != "&": continue + append(T_AND) + i += 1 + + elif c == "|": + # Invalid characters are ignored + if i >= len(s) or s[i] != "|": continue + append(T_OR) + i += 1 + + elif c == "!": + if i < len(s) and s[i] == "=": + append(T_UNEQUAL) + i += 1 + else: + append(T_NOT) + + elif c == "=": append(T_EQUAL) + elif c == "(": append(T_OPEN_PAREN) + elif c == ")": append(T_CLOSE_PAREN) + elif c == "#": break # Comment + + else: continue # Invalid characters are ignored + + previous = tokens[-1] + + return _Feed(tokens) + + def _sym_lookup(self, name, for_eval=False): + """Fetches the symbol 'name' from the symbol table, creating and + registering it if it does not exist. If 'for_eval' is True, the symbol + won't be added to the symbol table if it does not exist -- this is for + Config.eval().""" + if name in self.syms: + return self.syms[name] + + new_sym = Symbol() + new_sym.config = self + new_sym.name = name + if for_eval: + self._warn("no symbol {0} in configuration".format(name)) + else: + self.syms[name] = new_sym + return new_sym + + # + # Expression evaluation + # + + def _eval_expr(self, expr): + """Evaluates an expression to "n", "m", or "y".""" + + # Handles e.g. an "x if y" condition where the "if y" part is missing. + if expr is None: + return "y" + + res = self._eval_expr_rec(expr) + if res == "m": + # Promote "m" to "y" if we're running without modules. + # + # Internally, "m" is often rewritten to "m" && MODULES by both the + # C implementation and Kconfiglib, which takes care of cases where + # "m" should be demoted to "n" instead. + modules_sym = self.syms.get("MODULES") + if modules_sym is None or modules_sym.get_value() != "y": + return "y" + return res + + def _eval_expr_rec(self, expr): + if isinstance(expr, Symbol): + # Non-bool/tristate symbols are always "n" in a tristate sense, + # regardless of their value + if expr.type != BOOL and expr.type != TRISTATE: + return "n" + return expr.get_value() + + if isinstance(expr, str): + return expr if (expr == "y" or expr == "m") else "n" + + # Ordered by frequency + + if expr[0] == AND: + res = "y" + for subexpr in expr[1]: + ev = self._eval_expr_rec(subexpr) + # Return immediately upon discovering an "n" term + if ev == "n": + return "n" + if ev == "m": + res = "m" + # 'res' is either "m" or "y" here; we already handled the + # short-circuiting "n" case in the loop. + return res + + if expr[0] == NOT: + ev = self._eval_expr_rec(expr[1]) + if ev == "y": + return "n" + return "y" if (ev == "n") else "m" + + if expr[0] == OR: + res = "n" + for subexpr in expr[1]: + ev = self._eval_expr_rec(subexpr) + # Return immediately upon discovering a "y" term + if ev == "y": + return "y" + if ev == "m": + res = "m" + # 'res' is either "n" or "m" here; we already handled the + # short-circuiting "y" case in the loop. + return res + + if expr[0] == EQUAL: + return "y" if (_str_val(expr[1]) == _str_val(expr[2])) else "n" + + if expr[0] == UNEQUAL: + return "y" if (_str_val(expr[1]) != _str_val(expr[2])) else "n" + + _internal_error("Internal error while evaluating expression: " + "unknown operation {0}.".format(expr[0])) + + def _eval_min(self, e1, e2): + """Returns the minimum value of the two expressions. Equates None with + 'y'.""" + e1_eval = self._eval_expr(e1) + e2_eval = self._eval_expr(e2) + return e1_eval if tri_less(e1_eval, e2_eval) else e2_eval + + def _eval_max(self, e1, e2): + """Returns the maximum value of the two expressions. Equates None with + 'y'.""" + e1_eval = self._eval_expr(e1) + e2_eval = self._eval_expr(e2) + return e1_eval if tri_greater(e1_eval, e2_eval) else e2_eval + + # + # Dependency tracking (for caching and invalidation) + # + + def _build_dep(self): + """Populates the Symbol.dep sets, linking the symbol to the symbols + that immediately depend on it in the sense that changing the value of + the symbol might affect the values of those other symbols. This is used + for caching/invalidation purposes. The calculated sets might be larger + than necessary as we don't do any complicated analysis of the + expressions.""" + + # Adds 'sym' as a directly dependent symbol to all symbols that appear + # in the expression 'e' + def add_expr_deps(e, sym): + for s in _get_expr_syms(e): + s.dep.add(sym) + + # The directly dependent symbols of a symbol are: + # - Any symbols whose prompts, default values, rev_dep (select + # condition), weak_rev_dep (imply condition) or ranges depend on the + # symbol + # - Any symbols that belong to the same choice statement as the symbol + # (these won't be included in 'dep' as that makes the dependency + # graph unwieldy, but Symbol._get_dependent() will include them) + # - Any symbols in a choice statement that depends on the symbol + for sym in self.syms_iter(): + for _, e in sym.prompts: + add_expr_deps(e, sym) + + for v, e in sym.def_exprs: + add_expr_deps(v, sym) + add_expr_deps(e, sym) + + add_expr_deps(sym.rev_dep, sym) + add_expr_deps(sym.weak_rev_dep, sym) + + for l, u, e in sym.ranges: + add_expr_deps(l, sym) + add_expr_deps(u, sym) + add_expr_deps(e, sym) + + if sym.is_choice_sym: + choice = sym.parent + for _, e in choice.prompts: + add_expr_deps(e, sym) + for _, e in choice.def_exprs: + add_expr_deps(e, sym) + + def _eq_to_sym(self, eq): + """_expr_depends_on() helper. For (in)equalities of the form sym = y/m + or sym != n, returns sym. For other (in)equalities, returns None.""" + relation, left, right = eq + + def transform_y_m_n(item): + if item is self.y: return "y" + if item is self.m: return "m" + if item is self.n: return "n" + return item + + left = transform_y_m_n(left) + right = transform_y_m_n(right) + + # Make sure the symbol (if any) appears to the left + if not isinstance(left, Symbol): + left, right = right, left + if not isinstance(left, Symbol): + return None + if (relation == EQUAL and (right == "y" or right == "m")) or \ + (relation == UNEQUAL and right == "n"): + return left + return None + + def _expr_depends_on(self, expr, sym): + """Reimplementation of expr_depends_symbol() from mconf.c. Used to + determine if a submenu should be implicitly created, which influences + what items inside choice statements are considered choice items.""" + if expr is None: + return False + + def rec(expr): + if isinstance(expr, str): + return False + if isinstance(expr, Symbol): + return expr is sym + + if expr[0] in (EQUAL, UNEQUAL): + return self._eq_to_sym(expr) is sym + if expr[0] == AND: + for and_expr in expr[1]: + if rec(and_expr): + return True + return False + + return rec(expr) + + def _invalidate_all(self): + for sym in self.syms_iter(): + sym._invalidate() + + # + # Printing and misc. + # + + def _expand_sym_refs(self, s): + """Expands $-references to symbols in 's' to symbol values, or to the + empty string for undefined symbols.""" + + while 1: + sym_ref_match = _sym_ref_re_search(s) + if sym_ref_match is None: + return s + + sym_name = sym_ref_match.group(0)[1:] + sym = self.syms.get(sym_name) + expansion = "" if sym is None else sym.get_value() + + s = s[:sym_ref_match.start()] + \ + expansion + \ + s[sym_ref_match.end():] + + def _expr_val_str(self, expr, no_value_str="(none)", + get_val_instead_of_eval=False): + """Printing helper. Returns a string with 'expr' and its value. + + no_value_str: String to return when 'expr' is missing (None). + + get_val_instead_of_eval: Assume 'expr' is a symbol or string (constant + symbol) and get its value directly instead of evaluating it to a + tristate value.""" + + if expr is None: + return no_value_str + + if get_val_instead_of_eval: + if isinstance(expr, str): + return _expr_to_str(expr) + val = expr.get_value() + else: + val = self._eval_expr(expr) + + return "{0} (value: {1})".format(_expr_to_str(expr), _expr_to_str(val)) + + def _get_sym_or_choice_str(self, sc): + """Symbols and choices have many properties in common, so we factor out + common __str__() stuff here. "sc" is short for "symbol or choice".""" + + # As we deal a lot with string representations here, use some + # convenient shorthand: + s = _expr_to_str + + # + # Common symbol/choice properties + # + + user_val_str = "(no user value)" if sc.user_val is None else \ + s(sc.user_val) + + # Build prompts string + if not sc.prompts: + prompts_str = " (no prompts)" + else: + prompts_str_rows = [] + for prompt, cond_expr in sc.orig_prompts: + prompts_str_rows.append( + ' "{0}"'.format(prompt) if cond_expr is None else + ' "{0}" if {1}'.format(prompt, + self._expr_val_str(cond_expr))) + prompts_str = "\n".join(prompts_str_rows) + + # Build locations string + locations_str = "(no locations)" if not sc.def_locations else \ + " ".join(["{0}:{1}".format(filename, linenr) for + filename, linenr in sc.def_locations]) + + # Build additional-dependencies-from-menus-and-ifs string + additional_deps_str = " " + \ + self._expr_val_str(sc.deps_from_containing, + "(no additional dependencies)") + + # + # Symbol-specific stuff + # + + if isinstance(sc, Symbol): + # Build ranges string + if isinstance(sc, Symbol): + if not sc.ranges: + ranges_str = " (no ranges)" + else: + ranges_str_rows = [] + for l, u, cond_expr in sc.ranges: + ranges_str_rows.append( + " [{0}, {1}]".format(s(l), s(u)) + if cond_expr is None else + " [{0}, {1}] if {2}" + .format(s(l), s(u), self._expr_val_str(cond_expr))) + ranges_str = "\n".join(ranges_str_rows) + + # Build default values string + if not sc.def_exprs: + defaults_str = " (no default values)" + else: + defaults_str_rows = [] + for val_expr, cond_expr in sc.orig_def_exprs: + row_str = " " + self._expr_val_str(val_expr, "(none)", + sc.type == STRING) + defaults_str_rows.append(row_str) + defaults_str_rows.append(" Condition: " + + self._expr_val_str(cond_expr)) + defaults_str = "\n".join(defaults_str_rows) + + # Build selects string + if not sc.orig_selects: + selects_str = " (no selects)" + else: + selects_str_rows = [] + for target, cond_expr in sc.orig_selects: + selects_str_rows.append( + " {0}".format(target.name) if cond_expr is None else + " {0} if {1}".format(target.name, + self._expr_val_str(cond_expr))) + selects_str = "\n".join(selects_str_rows) + + # Build implies string + if not sc.orig_implies: + implies_str = " (no implies)" + else: + implies_str_rows = [] + for target, cond_expr in sc.orig_implies: + implies_str_rows.append( + " {0}".format(target.name) if cond_expr is None else + " {0} if {1}".format(target.name, + self._expr_val_str(cond_expr))) + implies_str = "\n".join(implies_str_rows) + + res = _lines("Symbol " + + ("(no name)" if sc.name is None else sc.name), + "Type : " + TYPENAME[sc.type], + "Value : " + s(sc.get_value()), + "User value : " + user_val_str, + "Visibility : " + s(_get_visibility(sc)), + "Is choice item : " + BOOL_STR[sc.is_choice_sym], + "Is defined : " + BOOL_STR[sc.is_defined_], + "Is from env. : " + BOOL_STR[sc.is_from_env], + "Is special : " + BOOL_STR[sc.is_special_] + "\n") + if sc.ranges: + res += _lines("Ranges:", ranges_str + "\n") + res += _lines("Prompts:", + prompts_str, + "Default values:", + defaults_str, + "Selects:", + selects_str, + "Implies:", + implies_str, + "Reverse (select-related) dependencies:", + " (no reverse dependencies)" + if sc.rev_dep == "n" + else " " + self._expr_val_str(sc.rev_dep), + "Weak reverse (imply-related) dependencies:", + " (no weak reverse dependencies)" + if sc.weak_rev_dep == "n" + else " " + self._expr_val_str(sc.weak_rev_dep), + "Additional dependencies from enclosing menus " + "and ifs:", + additional_deps_str, + "Locations: " + locations_str) + + return res + + # + # Choice-specific stuff + # + + # Build selected symbol string + sel = sc.get_selection() + sel_str = "(no selection)" if sel is None else sel.name + + # Build default values string + if not sc.def_exprs: + defaults_str = " (no default values)" + else: + defaults_str_rows = [] + for sym, cond_expr in sc.orig_def_exprs: + defaults_str_rows.append( + " {0}".format(sym.name) if cond_expr is None else + " {0} if {1}".format(sym.name, + self._expr_val_str(cond_expr))) + defaults_str = "\n".join(defaults_str_rows) + + # Build contained symbols string + names = [sym.name for sym in sc.actual_symbols] + syms_string = " ".join(names) if names else "(empty)" + + return _lines("Choice", + "Name (for named choices): " + + ("(no name)" if sc.name is None else sc.name), + "Type : " + TYPENAME[sc.type], + "Selected symbol : " + sel_str, + "User value : " + user_val_str, + "Mode : " + s(sc.get_mode()), + "Visibility : " + s(_get_visibility(sc)), + "Optional : " + BOOL_STR[sc.optional], + "Prompts:", + prompts_str, + "Defaults:", + defaults_str, + "Choice symbols:", + " " + syms_string, + "Additional dependencies from enclosing menus and " + "ifs:", + additional_deps_str, + "Locations: " + locations_str) + + def _warn(self, msg, filename=None, linenr=None): + """For printing warnings to stderr.""" + msg = _build_msg("warning: " + msg, filename, linenr) + if self.print_warnings: + sys.stderr.write(msg + "\n") + self._warnings.append(msg) + +class Item(object): + + """Base class for symbols and other Kconfig constructs. Subclasses are + Symbol, Choice, Menu, and Comment.""" + + def is_symbol(self): + """Returns True if the item is a symbol. Short for + isinstance(item, kconfiglib.Symbol).""" + return isinstance(self, Symbol) + + def is_choice(self): + """Returns True if the item is a choice. Short for + isinstance(item, kconfiglib.Choice).""" + return isinstance(self, Choice) + + def is_menu(self): + """Returns True if the item is a menu. Short for + isinstance(item, kconfiglib.Menu).""" + return isinstance(self, Menu) + + def is_comment(self): + """Returns True if the item is a comment. Short for + isinstance(item, kconfiglib.Comment).""" + return isinstance(self, Comment) + +class Symbol(Item): + + """Represents a configuration symbol - e.g. FOO for + + config FOO + ...""" + + # + # Public interface + # + + def get_config(self): + """Returns the Config instance this symbol is from.""" + return self.config + + def get_name(self): + """Returns the name of the symbol.""" + return self.name + + def get_type(self): + """Returns the type of the symbol: one of UNKNOWN, BOOL, TRISTATE, + STRING, HEX, or INT. These are defined at the top level of the module, + so you'd do something like + + if sym.get_type() == kconfiglib.STRING: + ...""" + return self.type + + def get_prompts(self): + """Returns a list of prompts defined for the symbol, in the order they + appear in the configuration files. Returns the empty list for symbols + with no prompt. + + This list will have a single entry for the vast majority of symbols + having prompts, but having multiple prompts for a single symbol is + possible through having multiple 'config' entries for it.""" + return [prompt for prompt, _ in self.orig_prompts] + + def get_help(self): + """Returns the help text of the symbol, or None if the symbol has no + help text.""" + return self.help + + def get_parent(self): + """Returns the menu or choice statement that contains the symbol, or + None if the symbol is at the top level. Note that if statements are + treated as syntactic and do not have an explicit class + representation.""" + return self.parent + + def get_def_locations(self): + """Returns a list of (filename, linenr) tuples, where filename (string) + and linenr (int) represent a location where the symbol is defined. For + the vast majority of symbols this list will only contain one element. + For the following Kconfig, FOO would get two entries: the lines marked + with *. + + config FOO * + bool "foo prompt 1" + + config FOO * + bool "foo prompt 2" + """ + return self.def_locations + + def get_ref_locations(self): + """Returns a list of (filename, linenr) tuples, where filename (string) + and linenr (int) represent a location where the symbol is referenced in + the configuration. For example, the lines marked by * would be included + for FOO below: + + config A + bool + default BAR || FOO * + + config B + tristate + depends on FOO * + default m if FOO * + + if FOO * + config A + bool "A" + endif + + config FOO (definition not included) + bool + """ + return self.ref_locations + + def get_value(self): + """Calculate and return the value of the symbol. See also + Symbol.set_user_value().""" + + if self.cached_val is not None: + return self.cached_val + + # As a quirk of Kconfig, undefined symbols get their name as their + # value. This is why things like "FOO = bar" work for seeing if FOO has + # the value "bar". + if self.type == UNKNOWN: + self.cached_val = self.name + return self.name + + new_val = DEFAULT_VALUE[self.type] + vis = _get_visibility(self) + + # This is easiest to calculate together with the value + self.write_to_conf = False + + if self.type == BOOL or self.type == TRISTATE: + # The visibility and mode (modules-only or single-selection) of + # choice items will be taken into account in _get_visibility() + if self.is_choice_sym: + if vis != "n": + choice = self.parent + mode = choice.get_mode() + + self.write_to_conf = (mode != "n") + + if mode == "y": + new_val = "y" if choice.get_selection() is self \ + else "n" + elif mode == "m": + if self.user_val == "m" or self.user_val == "y": + new_val = "m" + + else: + # If the symbol is visible and has a user value, use that. + # Otherwise, look at defaults and weak reverse dependencies + # (implies). + use_defaults_and_weak_rev_deps = True + + if vis != "n": + self.write_to_conf = True + if self.user_val is not None: + new_val = self.config._eval_min(self.user_val, vis) + use_defaults_and_weak_rev_deps = False + + if use_defaults_and_weak_rev_deps: + for val_expr, cond_expr in self.def_exprs: + cond_eval = self.config._eval_expr(cond_expr) + if cond_eval != "n": + self.write_to_conf = True + new_val = self.config._eval_min(val_expr, + cond_eval) + break + + weak_rev_dep_val = \ + self.config._eval_expr(self.weak_rev_dep) + if weak_rev_dep_val != "n": + self.write_to_conf = True + new_val = self.config._eval_max(new_val, + weak_rev_dep_val) + + # Reverse (select-related) dependencies take precedence + rev_dep_val = self.config._eval_expr(self.rev_dep) + if rev_dep_val != "n": + self.write_to_conf = True + new_val = self.config._eval_max(new_val, rev_dep_val) + + # We need to promote "m" to "y" in two circumstances: + # 1) If our type is boolean + # 2) If our weak_rev_dep (from IMPLY) is "y" + if new_val == "m" and \ + (self.type == BOOL or + self.config._eval_expr(self.weak_rev_dep) == "y"): + new_val = "y" + + elif self.type == INT or self.type == HEX: + has_active_range = False + low = None + high = None + use_defaults = True + + base = 16 if self.type == HEX else 10 + + for l, h, cond_expr in self.ranges: + if self.config._eval_expr(cond_expr) != "n": + has_active_range = True + + low_str = _str_val(l) + high_str = _str_val(h) + low = int(low_str, base) if \ + _is_base_n(low_str, base) else 0 + high = int(high_str, base) if \ + _is_base_n(high_str, base) else 0 + + break + + if vis != "n": + self.write_to_conf = True + + if self.user_val is not None and \ + _is_base_n(self.user_val, base) and \ + (not has_active_range or + low <= int(self.user_val, base) <= high): + + # If the user value is OK, it is stored in exactly the same + # form as specified in the assignment (with or without + # "0x", etc). + + use_defaults = False + new_val = self.user_val + + if use_defaults: + for val_expr, cond_expr in self.def_exprs: + if self.config._eval_expr(cond_expr) != "n": + self.write_to_conf = True + + # If the default value is OK, it is stored in exactly + # the same form as specified. Otherwise, it is clamped + # to the range, and the output has "0x" as appropriate + # for the type. + + new_val = _str_val(val_expr) + + if _is_base_n(new_val, base): + new_val_num = int(new_val, base) + if has_active_range: + clamped_val = None + + if new_val_num < low: + clamped_val = low + elif new_val_num > high: + clamped_val = high + + if clamped_val is not None: + new_val = (hex(clamped_val) if \ + self.type == HEX else str(clamped_val)) + + break + else: # For the for loop + # If no user value or default kicks in but the hex/int has + # an active range, then the low end of the range is used, + # provided it's > 0, with "0x" prepended as appropriate. + if has_active_range and low > 0: + new_val = (hex(low) if self.type == HEX else str(low)) + + elif self.type == STRING: + use_defaults = True + + if vis != "n": + self.write_to_conf = True + if self.user_val is not None: + new_val = self.user_val + use_defaults = False + + if use_defaults: + for val_expr, cond_expr in self.def_exprs: + if self.config._eval_expr(cond_expr) != "n": + self.write_to_conf = True + new_val = _str_val(val_expr) + break + + self.cached_val = new_val + return new_val + + def get_user_value(self): + """Returns the value assigned to the symbol in a .config or via + Symbol.set_user_value() (provided the value was valid for the type of + the symbol). Returns None in case of no user value.""" + return self.user_val + + def get_upper_bound(self): + """For string/hex/int symbols and for bool and tristate symbols that + cannot be modified (see is_modifiable()), returns None. + + Otherwise, returns the highest value the symbol can be set to with + Symbol.set_user_value() (that will not be truncated): one of "m" or + "y", arranged from lowest to highest. This corresponds to the highest + value the symbol could be given in e.g. the 'make menuconfig' + interface. + + See also the tri_less*() and tri_greater*() functions, which could come + in handy.""" + if self.type != BOOL and self.type != TRISTATE: + return None + rev_dep = self.config._eval_expr(self.rev_dep) + # A bool selected to "m" gets promoted to "y", pinning it + if rev_dep == "m" and self.type == BOOL: + return None + vis = _get_visibility(self) + if TRI_TO_INT[vis] > TRI_TO_INT[rev_dep]: + return vis + return None + + def get_lower_bound(self): + """For string/hex/int symbols and for bool and tristate symbols that + cannot be modified (see is_modifiable()), returns None. + + Otherwise, returns the lowest value the symbol can be set to with + Symbol.set_user_value() (that will not be truncated): one of "n" or + "m", arranged from lowest to highest. This corresponds to the lowest + value the symbol could be given in e.g. the 'make menuconfig' + interface. + + See also the tri_less*() and tri_greater*() functions, which could come + in handy.""" + if self.type != BOOL and self.type != TRISTATE: + return None + rev_dep = self.config._eval_expr(self.rev_dep) + # A bool selected to "m" gets promoted to "y", pinning it + if rev_dep == "m" and self.type == BOOL: + return None + if TRI_TO_INT[_get_visibility(self)] > TRI_TO_INT[rev_dep]: + return rev_dep + return None + + def get_assignable_values(self): + """For string/hex/int symbols and for bool and tristate symbols that + cannot be modified (see is_modifiable()), returns the empty list. + + Otherwise, returns a list containing the user values that can be + assigned to the symbol (that won't be truncated). Usage example: + + if "m" in sym.get_assignable_values(): + sym.set_user_value("m") + + This is basically a more convenient interface to + get_lower/upper_bound() when wanting to test if a particular tristate + value can be assigned.""" + if self.type != BOOL and self.type != TRISTATE: + return [] + rev_dep = self.config._eval_expr(self.rev_dep) + # A bool selected to "m" gets promoted to "y", pinning it + if rev_dep == "m" and self.type == BOOL: + return [] + res = ["n", "m", "y"][TRI_TO_INT[rev_dep] : + TRI_TO_INT[_get_visibility(self)] + 1] + return res if len(res) > 1 else [] + + def get_visibility(self): + """Returns the visibility of the symbol: one of "n", "m" or "y". For + bool and tristate symbols, this is an upper bound on the value users + can set for the symbol. For other types of symbols, a visibility of "n" + means the user value will be ignored. A visibility of "n" corresponds + to not being visible in the 'make *config' interfaces. + + Example (assuming we're running with modules enabled -- i.e., MODULES + set to 'y'): + + # Assume this has been assigned 'n' + config N_SYM + tristate "N_SYM" + + # Assume this has been assigned 'm' + config M_SYM + tristate "M_SYM" + + # Has visibility 'n' + config A + tristate "A" + depends on N_SYM + + # Has visibility 'm' + config B + tristate "B" + depends on M_SYM + + # Has visibility 'y' + config C + tristate "C" + + # Has no prompt, and hence visibility 'n' + config D + tristate + + Having visibility be tri-valued ensures that e.g. a symbol cannot be + set to "y" by the user if it depends on a symbol with value "m", which + wouldn't be safe. + + You should probably look at get_lower/upper_bound(), + get_assignable_values() and is_modifiable() before using this.""" + return _get_visibility(self) + + def get_referenced_symbols(self, refs_from_enclosing=False): + """Returns the set() of all symbols referenced by this symbol. For + example, the symbol defined by + + config FOO + bool + prompt "foo" if A && B + default C if D + depends on E + select F if G + + references the symbols A through G. + + refs_from_enclosing (default: False): If True, the symbols referenced + by enclosing menus and ifs will be included in the result.""" + return self.all_referenced_syms if refs_from_enclosing else \ + self.referenced_syms + + def get_selected_symbols(self): + """Returns the set() of all symbols X for which this symbol has a + 'select X' or 'select X if Y' (regardless of whether Y is satisfied or + not). This is a subset of the symbols returned by + get_referenced_symbols().""" + return self.selected_syms + + def get_implied_symbols(self): + """Returns the set() of all symbols X for which this symbol has an + 'imply X' or 'imply X if Y' (regardless of whether Y is satisfied or + not). This is a subset of the symbols returned by + get_referenced_symbols().""" + return self.implied_syms + + def set_user_value(self, v): + """Sets the user value of the symbol. + + Equal in effect to assigning the value to the symbol within a .config + file. Use get_lower/upper_bound() or get_assignable_values() to find + the range of currently assignable values for bool and tristate symbols; + setting values outside this range will cause the user value to differ + from the result of Symbol.get_value() (be truncated). Values that are + invalid for the type (such as a_bool.set_user_value("foo")) are + ignored, and a warning is emitted if an attempt is made to assign such + a value. + + For any type of symbol, is_modifiable() can be used to check if a user + value will currently have any effect on the symbol, as determined by + its visibility and range of assignable values. Any value that is valid + for the type (bool, tristate, etc.) will end up being reflected in + get_user_value() though, and might have an effect later if conditions + change. To get rid of the user value, use unset_user_value(). + + Any symbols dependent on the symbol are (recursively) invalidated, so + things will just work with regards to dependencies. + + v: The user value to give to the symbol.""" + self._set_user_value_no_invalidate(v, False) + + # There might be something more efficient you could do here, but play + # it safe. + if self.name == "MODULES": + self.config._invalidate_all() + return + + self._invalidate() + self._invalidate_dependent() + + def unset_user_value(self): + """Resets the user value of the symbol, as if the symbol had never + gotten a user value via Config.load_config() or + Symbol.set_user_value().""" + self._unset_user_value_no_recursive_invalidate() + self._invalidate_dependent() + + def is_modifiable(self): + """Returns True if the value of the symbol could be modified by calling + Symbol.set_user_value(). + + For bools and tristates, this corresponds to the symbol being visible + in the 'make menuconfig' interface and not already being pinned to a + specific value (e.g. because it is selected by another symbol). + + For strings and numbers, this corresponds to just being visible. (See + Symbol.get_visibility().)""" + if self.is_special_: + return False + if self.type == BOOL or self.type == TRISTATE: + rev_dep = self.config._eval_expr(self.rev_dep) + # A bool selected to "m" gets promoted to "y", pinning it + if rev_dep == "m" and self.type == BOOL: + return False + return TRI_TO_INT[_get_visibility(self)] > TRI_TO_INT[rev_dep] + return _get_visibility(self) != "n" + + def is_defined(self): + """Returns False if the symbol is referred to in the Kconfig but never + actually defined.""" + return self.is_defined_ + + def is_special(self): + """Returns True if the symbol is one of the special symbols n, m, y, or + UNAME_RELEASE, or gets its value from the environment.""" + return self.is_special_ + + def is_from_environment(self): + """Returns True if the symbol gets its value from the environment.""" + return self.is_from_env + + def has_ranges(self): + """Returns True if the symbol is of type INT or HEX and has ranges that + limit what values it can take on.""" + return bool(self.ranges) + + def is_choice_symbol(self): + """Returns True if the symbol is in a choice statement and is an actual + choice symbol (see Choice.get_symbols()).""" + return self.is_choice_sym + + def is_choice_selection(self): + """Returns True if the symbol is contained in a choice statement and is + the selected item. Equivalent to + + sym.is_choice_symbol() and sym.get_parent().get_selection() is sym""" + return self.is_choice_sym and self.parent.get_selection() is self + + def is_allnoconfig_y(self): + """Returns True if the symbol has the 'allnoconfig_y' option set.""" + return self.allnoconfig_y + + def __str__(self): + """Returns a string containing various information about the symbol.""" + return self.config._get_sym_or_choice_str(self) + + # + # Private methods + # + + def __init__(self): + """Symbol constructor -- not intended to be called directly by + Kconfiglib clients.""" + + self.name = None + self.type = UNKNOWN + self.prompts = [] + self.def_exprs = [] # 'default' properties + self.ranges = [] # 'range' properties (for int and hex) + self.help = None # Help text + self.rev_dep = "n" # Reverse (select-related) dependencies + self.weak_rev_dep = "n" # Weak reverse (imply-related) dependencies + self.config = None + self.parent = None + + self.user_val = None # Value set by user + + # The prompt, default value, select, and imply conditions without any + # dependencies from menus and ifs propagated to them + self.orig_prompts = [] + self.orig_def_exprs = [] + self.orig_selects = [] + self.orig_implies = [] + + # Dependencies inherited from containing menus and ifs + self.deps_from_containing = None + # The set of symbols referenced by this symbol (see + # get_referenced_symbols()) + self.referenced_syms = set() + # The set of symbols selected by this symbol (see + # get_selected_symbols()) + self.selected_syms = set() + # The set of symbols implied by this symbol (see get_implied_symbols()) + self.implied_syms = set() + # Like 'referenced_syms', but includes symbols from + # dependencies inherited from enclosing menus and ifs + self.all_referenced_syms = set() + + # This records only dependencies from enclosing ifs and menus together + # with local 'depends on' dependencies. Needed when determining actual + # choice items (hrrrr...). See Choice._determine_actual_symbols(). + self.menu_dep = None + + # See Symbol.get_ref/def_locations(). + self.def_locations = [] + self.ref_locations = [] + + # Populated in Config._build_dep() after parsing. Links the symbol to + # the symbols that immediately depend on it (in a caching/invalidation + # sense). The total set of dependent symbols for the symbol (the + # transitive closure) is calculated on an as-needed basis in + # _get_dependent(). + self.dep = set() + + # Cached values + + # Caches the calculated value + self.cached_val = None + # Caches the visibility, which acts as an upper bound on the value + self.cached_visibility = None + # Caches the total list of dependent symbols. Calculated in + # _get_dependent(). + self.cached_deps = None + + # Flags + + # Does the symbol have an entry in the Kconfig file? The trailing + # underscore avoids a collision with is_defined(). + self.is_defined_ = False + # Should the symbol get an entry in .config? + self.write_to_conf = False + # Set to true when _make_conf() is called on a symbol, so that symbols + # defined in multiple locations only get one .config entry. We need to + # reset it prior to writing out a new .config. + self.already_written = False + # This is set to True for "actual" choice symbols; see + # Choice._determine_actual_symbols(). + self.is_choice_sym = False + # Does the symbol get its value in some special way, e.g. from the + # environment or by being one of the special symbols n, m, and y? If + # so, the value is stored in self.cached_val, which is never + # invalidated. The trailing underscore avoids a collision with + # is_special(). + self.is_special_ = False + # Does the symbol get its value from the environment? + self.is_from_env = False + # Does the symbol have the 'allnoconfig_y' option set? + self.allnoconfig_y = False + + def _invalidate(self): + if self.is_special_: + return + + if self.is_choice_sym: + self.parent._invalidate() + + self.cached_val = None + self.cached_visibility = None + + def _invalidate_dependent(self): + for sym in self._get_dependent(): + sym._invalidate() + + def _set_user_value_no_invalidate(self, v, suppress_load_warnings): + """Like set_user_value(), but does not invalidate any symbols. + + suppress_load_warnings: some warnings are annoying when loading a + .config that can be helpful when manually invoking set_user_value(). + This flag is set to True to suppress such warnings. + + Perhaps this could be made optional for load_config() instead.""" + + if self.is_special_: + if self.is_from_env: + self.config._warn('attempt to assign the value "{0}" to the ' + 'symbol {1}, which gets its value from the ' + 'environment. Assignment ignored.' + .format(v, self.name)) + else: + self.config._warn('attempt to assign the value "{0}" to the ' + 'special symbol {1}. Assignment ignored.' + .format(v, self.name)) + return + + if not self.is_defined_: + filename, linenr = self.ref_locations[0] + if self.config.print_undef_assign: + _stderr_msg('note: attempt to assign the value "{0}" to {1}, ' + "which is referenced at {2}:{3} but never " + "defined. Assignment ignored." + .format(v, self.name, filename, linenr)) + return + + # Check if the value is valid for our type + if not ((self.type == BOOL and (v == "y" or v == "n") ) or + (self.type == TRISTATE and (v == "y" or v == "m" or + v == "n") ) or + (self.type == STRING ) or + (self.type == INT and _is_base_n(v, 10) ) or + (self.type == HEX and _is_base_n(v, 16) )): + self.config._warn('the value "{0}" is invalid for {1}, which has ' + "type {2}. Assignment ignored." + .format(v, self.name, TYPENAME[self.type])) + return + + if not self.prompts and not suppress_load_warnings: + self.config._warn('assigning "{0}" to the symbol {1} which ' + 'lacks prompts and thus has visibility "n". ' + 'The assignment will have no effect.' + .format(v, self.name)) + + self.user_val = v + + if self.is_choice_sym and (self.type == BOOL or self.type == TRISTATE): + choice = self.parent + if v == "y": + choice.user_val = self + choice.user_mode = "y" + elif v == "m": + choice.user_val = None + choice.user_mode = "m" + + def _unset_user_value_no_recursive_invalidate(self): + self._invalidate() + self.user_val = None + + if self.is_choice_sym: + self.parent._unset_user_value() + + def _make_conf(self, append_fn): + if self.already_written: + return + + self.already_written = True + + # Note: write_to_conf is determined in get_value() + val = self.get_value() + if not self.write_to_conf: + return + + if self.type == BOOL or self.type == TRISTATE: + append_fn("{0}{1}={2}".format(self.config.config_prefix, self.name, val) + if val == "y" or val == "m" else + "# {0}{1} is not set".format(self.config.config_prefix, self.name)) + + elif self.type == INT or self.type == HEX: + append_fn("{0}{1}={2}".format(self.config.config_prefix, self.name, val)) + + elif self.type == STRING: + # Escape \ and " + append_fn('{0}{1}="{2}"' + .format(self.config.config_prefix, self.name, + val.replace("\\", "\\\\").replace('"', '\\"'))) + + else: + _internal_error("Internal error while creating .config: unknown " + 'type "{0}".'.format(self.type)) + + def _get_dependent(self): + """Returns the set of symbols that should be invalidated if the value + of the symbol changes, because they might be affected by the change. + Note that this is an internal API -- it's probably of limited + usefulness to clients.""" + if self.cached_deps is not None: + return self.cached_deps + + res = set(self.dep) + for s in self.dep: + res |= s._get_dependent() + + if self.is_choice_sym: + # Choice symbols also depend (recursively) on their siblings. The + # siblings are not included in 'dep' to avoid dependency loops. + for sibling in self.parent.actual_symbols: + if sibling is not self: + res.add(sibling) + res |= sibling.dep + for s in sibling.dep: + res |= s._get_dependent() + + self.cached_deps = res + return res + + def _has_auto_menu_dep_on(self, on): + """See Choice._determine_actual_symbols().""" + if not isinstance(self.parent, Choice): + _internal_error("Attempt to determine auto menu dependency for " + "symbol ouside of choice.") + + if not self.prompts: + # If we have no prompt, use the menu dependencies instead (what was + # specified with 'depends on') + return self.menu_dep is not None and \ + self.config._expr_depends_on(self.menu_dep, on) + + for _, cond_expr in self.prompts: + if self.config._expr_depends_on(cond_expr, on): + return True + + return False + +class Menu(Item): + + """Represents a menu statement.""" + + # + # Public interface + # + + def get_config(self): + """Return the Config instance this menu is from.""" + return self.config + + def get_title(self): + """Returns the title text of the menu.""" + return self.title + + def get_parent(self): + """Returns the menu or choice statement that contains the menu, or + None if the menu is at the top level. Note that if statements are + treated as syntactic sugar and do not have an explicit class + representation.""" + return self.parent + + def get_location(self): + """Returns the location of the menu as a (filename, linenr) tuple, + where filename is a string and linenr an int.""" + return (self.filename, self.linenr) + + def get_items(self, recursive=False): + """Returns a list containing the items (symbols, menus, choice + statements and comments) in in the menu, in the same order that the + items appear within the menu. + + recursive (default: False): True if items contained in items within the + menu should be included recursively (preorder).""" + + if not recursive: + return self.block + + res = [] + for item in self.block: + res.append(item) + if isinstance(item, Menu): + res.extend(item.get_items(True)) + elif isinstance(item, Choice): + res.extend(item.get_items()) + return res + + def get_symbols(self, recursive=False): + """Returns a list containing the symbols in the menu, in the same order + that they appear within the menu. + + recursive (default: False): True if symbols contained in items within + the menu should be included recursively.""" + + return [item for item in self.get_items(recursive) if + isinstance(item, Symbol)] + + def get_visibility(self): + """Returns the visibility of the menu. This also affects the visibility + of subitems. See also Symbol.get_visibility().""" + return self.config._eval_expr(self.dep_expr) + + def get_visible_if_visibility(self): + """Returns the visibility the menu gets from its 'visible if' + condition. "y" if the menu has no 'visible if' condition.""" + return self.config._eval_expr(self.visible_if_expr) + + def get_referenced_symbols(self, refs_from_enclosing=False): + """See Symbol.get_referenced_symbols().""" + return self.all_referenced_syms if refs_from_enclosing else \ + self.referenced_syms + + def __str__(self): + """Returns a string containing various information about the menu.""" + depends_on_str = self.config._expr_val_str(self.orig_deps, + "(no dependencies)") + visible_if_str = self.config._expr_val_str(self.visible_if_expr, + "(no dependencies)") + + additional_deps_str = " " + \ + self.config._expr_val_str(self.deps_from_containing, + "(no additional dependencies)") + + return _lines("Menu", + "Title : " + self.title, + "'depends on' dependencies : " + depends_on_str, + "'visible if' dependencies : " + visible_if_str, + "Additional dependencies from enclosing menus and " + "ifs:", + additional_deps_str, + "Location: {0}:{1}".format(self.filename, self.linenr)) + + # + # Private methods + # + + def __init__(self): + """Menu constructor -- not intended to be called directly by + Kconfiglib clients.""" + + self.title = None + self.dep_expr = None + self.visible_if_expr = None + self.block = [] # List of contained items + self.config = None + self.parent = None + + # Dependency expression without dependencies from enclosing menus and + # ifs propagated + self.orig_deps = None + + # Dependencies inherited from containing menus and ifs + self.deps_from_containing = None + # The set of symbols referenced by this menu (see + # get_referenced_symbols()) + self.referenced_syms = set() + # Like 'referenced_syms', but includes symbols from + # dependencies inherited from enclosing menus and ifs + self.all_referenced_syms = None + + self.filename = None + self.linenr = None + + def _make_conf(self, append_fn): + if self.config._eval_expr(self.dep_expr) != "n" and \ + self.config._eval_expr(self.visible_if_expr) != "n": + append_fn("\n#\n# {0}\n#".format(self.title)) + _make_block_conf(self.block, append_fn) + +class Choice(Item): + + """Represents a choice statement. A choice can be in one of three modes: + + "n" - The choice is not visible and no symbols can be selected. + + "m" - Any number of symbols can be set to "m". The rest will be "n". This + is safe since potentially conflicting options don't actually get + compiled into the kernel simultaneously with "m". + + "y" - One symbol will be "y" while the rest are "n". + + Only tristate choices can be in "m" mode, and the visibility of the choice + is an upper bound on the mode, so that e.g. a choice that depends on a + symbol with value "m" will be in "m" mode. + + The mode changes automatically when a value is assigned to a symbol within + the choice. + + See Symbol.get_visibility() too.""" + + # + # Public interface + # + + def get_config(self): + """Returns the Config instance this choice is from.""" + return self.config + + def get_name(self): + """For named choices, returns the name. Returns None for unnamed + choices. No named choices appear anywhere in the kernel Kconfig files + as of Linux 3.7.0-rc8.""" + return self.name + + def get_type(self): + """Returns the type of the choice. See Symbol.get_type().""" + return self.type + + def get_prompts(self): + """Returns a list of prompts defined for the choice, in the order they + appear in the configuration files. Returns the empty list for choices + with no prompt. + + This list will have a single entry for the vast majority of choices + having prompts, but having multiple prompts for a single choice is + possible through having multiple 'choice' entries for it (though I'm + not sure if that ever happens in practice).""" + return [prompt for prompt, _ in self.orig_prompts] + + def get_help(self): + """Returns the help text of the choice, or None if the choice has no + help text.""" + return self.help + + def get_parent(self): + """Returns the menu or choice statement that contains the choice, or + None if the choice is at the top level. Note that if statements are + treated as syntactic sugar and do not have an explicit class + representation.""" + return self.parent + + def get_def_locations(self): + """Returns a list of (filename, linenr) tuples, where filename (string) + and linenr (int) represent a location where the choice is defined. For + the vast majority of choices (all of them as of Linux 3.7.0-rc8) this + list will only contain one element, but its possible for named choices + to be defined in multiple locations.""" + return self.def_locations + + def get_selection(self): + """Returns the symbol selected (either by the user or through + defaults), or None if either no symbol is selected or the mode is not + "y".""" + if self.cached_selection is not None: + if self.cached_selection == NO_SELECTION: + return None + return self.cached_selection + + if self.get_mode() != "y": + return self._cache_ret(None) + + # User choice available? + if self.user_val is not None and _get_visibility(self.user_val) == "y": + return self._cache_ret(self.user_val) + + if self.optional: + return self._cache_ret(None) + + return self._cache_ret(self.get_selection_from_defaults()) + + def get_selection_from_defaults(self): + """Like Choice.get_selection(), but acts as if no symbol has been + selected by the user and no 'optional' flag is in effect.""" + + if not self.actual_symbols: + return None + + for symbol, cond_expr in self.def_exprs: + if self.config._eval_expr(cond_expr) != "n": + chosen_symbol = symbol + break + else: + chosen_symbol = self.actual_symbols[0] + + # Is the chosen symbol visible? + if _get_visibility(chosen_symbol) != "n": + return chosen_symbol + # Otherwise, pick the first visible symbol + for sym in self.actual_symbols: + if _get_visibility(sym) != "n": + return sym + return None + + def get_user_selection(self): + """If the choice is in "y" mode and has a user-selected symbol, returns + that symbol. Otherwise, returns None.""" + return self.user_val + + def get_items(self): + """Gets all items contained in the choice in the same order as within + the configuration ("items" instead of "symbols" since choices and + comments might appear within choices. This only happens in one place as + of Linux 3.7.0-rc8, in drivers/usb/gadget/Kconfig).""" + return self.block + + def get_symbols(self): + """Returns a list containing the choice's symbols. + + A quirk (perhaps a bug) of Kconfig is that you can put items within a + choice that will not be considered members of the choice insofar as + selection is concerned. This happens for example if one symbol within a + choice 'depends on' the symbol preceding it, or if you put non-symbol + items within choices. + + As of Linux 3.7.0-rc8, this seems to be used intentionally in one + place: drivers/usb/gadget/Kconfig. + + This function returns the "proper" symbols of the choice in the order + they appear in the choice, excluding such items. If you want all items + in the choice, use get_items().""" + return self.actual_symbols + + def get_referenced_symbols(self, refs_from_enclosing=False): + """See Symbol.get_referenced_symbols().""" + return self.all_referenced_syms if refs_from_enclosing else \ + self.referenced_syms + + def get_visibility(self): + """Returns the visibility of the choice statement: one of "n", "m" or + "y". This acts as an upper limit on the mode of the choice (though bool + choices can only have the mode "y"). See the class documentation for an + explanation of modes.""" + return _get_visibility(self) + + def get_mode(self): + """Returns the mode of the choice. See the class documentation for + an explanation of modes.""" + minimum_mode = "n" if self.optional else "m" + mode = self.user_mode if self.user_mode is not None else minimum_mode + mode = self.config._eval_min(mode, _get_visibility(self)) + + # Promote "m" to "y" for boolean choices + if mode == "m" and self.type == BOOL: + return "y" + + return mode + + def is_optional(self): + """Returns True if the choice has the 'optional' flag set (and so will + default to "n" mode).""" + return self.optional + + def __str__(self): + """Returns a string containing various information about the choice + statement.""" + return self.config._get_sym_or_choice_str(self) + + # + # Private methods + # + + def __init__(self): + """Choice constructor -- not intended to be called directly by + Kconfiglib clients.""" + + self.name = None # Yes, choices can be named + self.type = UNKNOWN + self.prompts = [] + self.def_exprs = [] # 'default' properties + self.help = None # Help text + self.block = [] # List of contained items + self.config = None + self.parent = None + + self.user_val = None + self.user_mode = None + + # We need to filter out symbols that appear within the choice block but + # are not considered choice items (see + # Choice._determine_actual_symbols()) This list holds the "actual" + # choice items. + self.actual_symbols = [] + + # The prompts and default values without any dependencies from + # enclosing menus and ifs propagated + self.orig_prompts = [] + self.orig_def_exprs = [] + + # Dependencies inherited from containing menus and ifs + self.deps_from_containing = None + # The set of symbols referenced by this choice (see + # get_referenced_symbols()) + self.referenced_syms = set() + # Like 'referenced_syms', but includes symbols from + # dependencies inherited from enclosing menus and ifs + self.all_referenced_syms = set() + + # See Choice.get_def_locations() + self.def_locations = [] + + # Cached values + self.cached_selection = None + self.cached_visibility = None + + self.optional = False + + def _determine_actual_symbols(self): + """If a symbol's visibility depends on the preceding symbol within a + choice, it is no longer viewed as a choice item. (This is quite + possibly a bug, but some things consciously use it... ugh. It stems + from automatic submenu creation.) In addition, it's possible to have + choices and comments within choices, and those shouldn't be considered + choice items either. Only drivers/usb/gadget/Kconfig seems to depend on + any of this. This method computes the "actual" items in the choice and + sets the is_choice_sym flag on them (retrieved via is_choice_symbol()). + + Don't let this scare you: an earlier version simply checked for a + sequence of symbols where all symbols after the first appeared in the + 'depends on' expression of the first, and that worked fine. The added + complexity is to be future-proof in the event that + drivers/usb/gadget/Kconfig turns even more sinister. It might very well + be overkilling things (especially if that file is refactored ;).""" + + # Items might depend on each other in a tree structure, so we need a + # stack to keep track of the current tentative parent + stack = [] + + for item in self.block: + if not isinstance(item, Symbol): + stack = [] + continue + + while stack: + if item._has_auto_menu_dep_on(stack[-1]): + # The item should not be viewed as a choice item, so don't + # set item.is_choice_sym + stack.append(item) + break + else: + stack.pop() + else: + item.is_choice_sym = True + self.actual_symbols.append(item) + stack.append(item) + + def _cache_ret(self, selection): + # As None is used to indicate the lack of a cached value we can't use + # that to cache the fact that the choice has no selection. Instead, we + # use the symbolic constant NO_SELECTION. + if selection is None: + self.cached_selection = NO_SELECTION + else: + self.cached_selection = selection + + return selection + + def _invalidate(self): + self.cached_selection = None + self.cached_visibility = None + + def _unset_user_value(self): + self._invalidate() + self.user_val = None + self.user_mode = None + + def _make_conf(self, append_fn): + _make_block_conf(self.block, append_fn) + +class Comment(Item): + + """Represents a comment statement.""" + + # + # Public interface + # + + def get_config(self): + """Returns the Config instance this comment is from.""" + return self.config + + def get_text(self): + """Returns the text of the comment.""" + return self.text + + def get_parent(self): + """Returns the menu or choice statement that contains the comment, or + None if the comment is at the top level. Note that if statements are + treated as syntactic sugar and do not have an explicit class + representation.""" + return self.parent + + def get_location(self): + """Returns the location of the comment as a (filename, linenr) tuple, + where filename is a string and linenr an int.""" + return (self.filename, self.linenr) + + def get_visibility(self): + """Returns the visibility of the comment. See also + Symbol.get_visibility().""" + return self.config._eval_expr(self.dep_expr) + + def get_referenced_symbols(self, refs_from_enclosing=False): + """See Symbol.get_referenced_symbols().""" + return self.all_referenced_syms if refs_from_enclosing else \ + self.referenced_syms + + def __str__(self): + """Returns a string containing various information about the + comment.""" + dep_str = self.config._expr_val_str(self.orig_deps, + "(no dependencies)") + + additional_deps_str = " " + \ + self.config._expr_val_str(self.deps_from_containing, + "(no additional dependencies)") + + return _lines("Comment", + "Text: " + str(self.text), + "Dependencies: " + dep_str, + "Additional dependencies from enclosing menus and " + "ifs:", + additional_deps_str, + "Location: {0}:{1}".format(self.filename, self.linenr)) + + # + # Private methods + # + + def __init__(self): + """Comment constructor -- not intended to be called directly by + Kconfiglib clients.""" + + self.text = None + self.dep_expr = None + self.config = None + self.parent = None + + # Dependency expression without dependencies from enclosing menus and + # ifs propagated + self.orig_deps = None + + # Dependencies inherited from containing menus and ifs + self.deps_from_containing = None + # The set of symbols referenced by this comment (see + # get_referenced_symbols()) + self.referenced_syms = set() + # Like 'referenced_syms', but includes symbols from + # dependencies inherited from enclosing menus and ifs + self.all_referenced_syms = None + + self.filename = None + self.linenr = None + + def _make_conf(self, append_fn): + if self.config._eval_expr(self.dep_expr) != "n": + append_fn("\n#\n# {0}\n#".format(self.text)) + +class Kconfig_Syntax_Error(Exception): + """Exception raised for syntax errors.""" + pass + +class Internal_Error(Exception): + """Exception raised for internal errors.""" + pass + +# +# Public functions +# + +def tri_less(v1, v2): + """Returns True if the tristate v1 is less than the tristate v2, where "n", + "m" and "y" are ordered from lowest to highest.""" + return TRI_TO_INT[v1] < TRI_TO_INT[v2] + +def tri_less_eq(v1, v2): + """Returns True if the tristate v1 is less than or equal to the tristate + v2, where "n", "m" and "y" are ordered from lowest to highest.""" + return TRI_TO_INT[v1] <= TRI_TO_INT[v2] + +def tri_greater(v1, v2): + """Returns True if the tristate v1 is greater than the tristate v2, where + "n", "m" and "y" are ordered from lowest to highest.""" + return TRI_TO_INT[v1] > TRI_TO_INT[v2] + +def tri_greater_eq(v1, v2): + """Returns True if the tristate v1 is greater than or equal to the tristate + v2, where "n", "m" and "y" are ordered from lowest to highest.""" + return TRI_TO_INT[v1] >= TRI_TO_INT[v2] + +# +# Internal classes +# + +class _Feed(object): + + """Class for working with sequences in a stream-like fashion; handy for + tokens.""" + + # This would be more helpful on the item classes, but would remove some + # flexibility + __slots__ = ['items', 'length', 'i'] + + def __init__(self, items): + self.items = items + self.length = len(self.items) + self.i = 0 + + def get_next(self): + if self.i >= self.length: + return None + item = self.items[self.i] + self.i += 1 + return item + + def peek_next(self): + return None if self.i >= self.length else self.items[self.i] + + def check(self, token): + """Check if the next token is 'token'. If so, remove it from the token + feed and return True. Otherwise, leave it in and return False.""" + if self.i < self.length and self.items[self.i] == token: + self.i += 1 + return True + return False + + def unget_all(self): + self.i = 0 + +class _FileFeed(object): + + """Feeds lines from a file. Keeps track of the filename and current line + number. Joins any line ending in \\ with the following line. We need to be + careful to get the line number right in the presence of continuation + lines.""" + + __slots__ = ['filename', 'lines', 'length', 'linenr'] + + def __init__(self, filename): + self.filename = _clean_up_path(filename) + with open(filename, "r") as f: + # No interleaving of I/O and processing yet. Don't know if it would + # help. + self.lines = f.readlines() + self.length = len(self.lines) + self.linenr = 0 + + def get_next(self): + if self.linenr >= self.length: + return None + line = self.lines[self.linenr] + self.linenr += 1 + while line.endswith("\\\n"): + line = line[:-2] + self.lines[self.linenr] + self.linenr += 1 + return line + + def peek_next(self): + linenr = self.linenr + if linenr >= self.length: + return None + line = self.lines[linenr] + while line.endswith("\\\n"): + linenr += 1 + line = line[:-2] + self.lines[linenr] + return line + + def unget(self): + self.linenr -= 1 + while self.lines[self.linenr].endswith("\\\n"): + self.linenr -= 1 + + def next_nonblank(self): + """Removes lines up to and including the next non-blank (not all-space) + line and returns it. Returns None if there are no more non-blank + lines.""" + while 1: + line = self.get_next() + if line is None or not line.isspace(): + return line + +# +# Internal functions +# + +def _get_visibility(sc): + """Symbols and Choices have a "visibility" that acts as an upper bound on + the values a user can set for them, corresponding to the visibility in e.g. + 'make menuconfig'. This function calculates the visibility for the Symbol + or Choice 'sc' -- the logic is nearly identical.""" + if sc.cached_visibility is None: + vis = "n" + for _, cond_expr in sc.prompts: + vis = sc.config._eval_max(vis, cond_expr) + + if isinstance(sc, Symbol) and sc.is_choice_sym: + if sc.type == TRISTATE and vis == "m" and \ + sc.parent.get_mode() == "y": + # Choice symbols with visibility "m" are not visible if the + # choice has mode "y" + vis = "n" + else: + vis = sc.config._eval_min(vis, _get_visibility(sc.parent)) + + # Promote "m" to "y" if we're dealing with a non-tristate + if vis == "m" and sc.type != TRISTATE: + vis = "y" + + sc.cached_visibility = vis + + return sc.cached_visibility + +def _make_and(e1, e2): + """Constructs an AND (&&) expression. Performs trivial simplification. + Nones equate to 'y'. + + Note: returns None if e1 == e2 == None.""" + if e1 is None or e1 == "y": + return e2 + if e2 is None or e2 == "y": + return e1 + + # Prefer to merge argument lists if possible to reduce the number of nodes + + if isinstance(e1, tuple) and e1[0] == AND: + if isinstance(e2, tuple) and e2[0] == AND: + return (AND, e1[1] + e2[1]) + return (AND, e1[1] + [e2]) + + if isinstance(e2, tuple) and e2[0] == AND: + return (AND, e2[1] + [e1]) + + return (AND, [e1, e2]) + +def _make_or(e1, e2): + """Constructs an OR (||) expression. Performs trivial simplification and + avoids Nones. Nones equate to 'y', which is usually what we want, but needs + to be kept in mind.""" + + # Perform trivial simplification and avoid None's (which + # correspond to y's) + if e1 is None or e2 is None or e1 == "y" or e2 == "y": + return "y" + if e1 == "n": + return e2 + + # Prefer to merge argument lists if possible to reduce the number of nodes + + if isinstance(e1, tuple) and e1[0] == OR: + if isinstance(e2, tuple) and e2[0] == OR: + return (OR, e1[1] + e2[1]) + return (OR, e1[1] + [e2]) + + if isinstance(e2, tuple) and e2[0] == OR: + return (OR, e2[1] + [e1]) + + return (OR, [e1, e2]) + +def _get_expr_syms_rec(expr, res): + """_get_expr_syms() helper. Recurses through expressions.""" + if isinstance(expr, Symbol): + res.add(expr) + elif isinstance(expr, str): + return + elif expr[0] == AND or expr[0] == OR: + for term in expr[1]: + _get_expr_syms_rec(term, res) + elif expr[0] == NOT: + _get_expr_syms_rec(expr[1], res) + elif expr[0] == EQUAL or expr[0] == UNEQUAL: + if isinstance(expr[1], Symbol): + res.add(expr[1]) + if isinstance(expr[2], Symbol): + res.add(expr[2]) + else: + _internal_error("Internal error while fetching symbols from an " + "expression with token stream {0}.".format(expr)) + +def _get_expr_syms(expr): + """Returns the set() of symbols appearing in expr.""" + res = set() + if expr is not None: + _get_expr_syms_rec(expr, res) + return res + +def _str_val(obj): + """Returns the value of obj as a string. If obj is not a string (constant + symbol), it must be a Symbol.""" + return obj if isinstance(obj, str) else obj.get_value() + +def _make_block_conf(block, append_fn): + """Returns a list of .config strings for a block (list) of items.""" + + # Collect the substrings in a list and later use join() instead of += to + # build the final .config contents. With older Python versions, this yields + # linear instead of quadratic complexity. + for item in block: + item._make_conf(append_fn) + +def _sym_str_string(sym_or_str): + if isinstance(sym_or_str, str): + return '"' + sym_or_str + '"' + return sym_or_str.name + +def _intersperse(lst, op): + """_expr_to_str() helper. Gets the string representation of each expression + in lst and produces a list where op has been inserted between the + elements.""" + if not lst: + return "" + + res = [] + + def handle_sub_expr(expr): + no_parens = isinstance(expr, (str, Symbol)) or \ + expr[0] in (EQUAL, UNEQUAL) or \ + PRECEDENCE[op] <= PRECEDENCE[expr[0]] + if not no_parens: + res.append("(") + res.extend(_expr_to_str_rec(expr)) + if not no_parens: + res.append(")") + + op_str = OP_TO_STR[op] + + handle_sub_expr(lst[0]) + for expr in lst[1:]: + res.append(op_str) + handle_sub_expr(expr) + + return res + +def _expr_to_str_rec(expr): + if expr is None: + return [""] + + if isinstance(expr, (Symbol, str)): + return [_sym_str_string(expr)] + + if expr[0] in (AND, OR): + return _intersperse(expr[1], expr[0]) + + if expr[0] == NOT: + need_parens = not isinstance(expr[1], (str, Symbol)) + + res = ["!"] + if need_parens: + res.append("(") + res.extend(_expr_to_str_rec(expr[1])) + if need_parens: + res.append(")") + return res + + if expr[0] in (EQUAL, UNEQUAL): + return [_sym_str_string(expr[1]), + OP_TO_STR[expr[0]], + _sym_str_string(expr[2])] + +def _expr_to_str(expr): + return "".join(_expr_to_str_rec(expr)) + +def _indentation(line): + """Returns the length of the line's leading whitespace, treating tab stops + as being spaced 8 characters apart.""" + line = line.expandtabs() + return len(line) - len(line.lstrip()) + +def _deindent(line, indent): + """Deindent 'line' by 'indent' spaces.""" + line = line.expandtabs() + if len(line) <= indent: + return line + return line[indent:] + +def _is_base_n(s, n): + try: + int(s, n) + return True + except ValueError: + return False + +def _lines(*args): + """Returns a string consisting of all arguments, with newlines inserted + between them.""" + return "\n".join(args) + +def _comment(s): + """Returns a new string with "#" inserted before each line in 's'.""" + if not s: + return "#" + res = "".join(["#" + line for line in s.splitlines(True)]) + if s.endswith("\n"): + return res + "#" + return res + +def _clean_up_path(path): + """Strips an initial "./" and any trailing slashes from 'path'.""" + if path.startswith("./"): + path = path[2:] + return path.rstrip("/") + +def _build_msg(msg, filename, linenr): + if filename is not None: + msg = "{0}:{1}: ".format(_clean_up_path(filename), linenr) + msg + return msg + +def _stderr_msg(msg, filename, linenr): + sys.stderr.write(_build_msg(msg, filename, linenr) + "\n") + +def _tokenization_error(s, filename, linenr): + loc = "" if filename is None else "{0}:{1}: ".format(filename, linenr) + raise Kconfig_Syntax_Error("{0}Couldn't tokenize '{1}'" + .format(loc, s.strip())) + +def _parse_error(s, msg, filename, linenr): + loc = "" if filename is None else "{0}:{1}: ".format(filename, linenr) + raise Kconfig_Syntax_Error("{0}Couldn't parse '{1}'{2}" + .format(loc, s.strip(), + "." if msg is None else ": " + msg)) + +def _internal_error(msg): + raise Internal_Error(msg + + "\nSorry! You may want to send an email to ulfalizer a.t Google's " + "email service to tell me about this. Include the message above and the " + "stack trace and describe what you were doing.") + +# +# Internal global constants +# + +# Tokens +(T_AND, T_OR, T_NOT, + T_OPEN_PAREN, T_CLOSE_PAREN, + T_EQUAL, T_UNEQUAL, + T_MAINMENU, T_MENU, T_ENDMENU, + T_SOURCE, T_CHOICE, T_ENDCHOICE, + T_COMMENT, T_CONFIG, T_MENUCONFIG, + T_HELP, T_IF, T_ENDIF, T_DEPENDS, T_ON, + T_OPTIONAL, T_PROMPT, T_DEFAULT, + T_BOOL, T_TRISTATE, T_HEX, T_INT, T_STRING, + T_DEF_BOOL, T_DEF_TRISTATE, + T_SELECT, T_IMPLY, T_RANGE, T_OPTION, T_ALLNOCONFIG_Y, T_ENV, + T_DEFCONFIG_LIST, T_MODULES, T_VISIBLE) = range(40) + +# The leading underscore before the function assignments below prevent pydoc +# from listing them. The constants could be hidden too, but they're fairly +# obviously internal anyway, so don't bother spamming the code. + +# Keyword to token map. Note that the get() method is assigned directly as a +# small optimization. +_get_keyword = \ + {"mainmenu": T_MAINMENU, "menu": T_MENU, "endmenu": T_ENDMENU, + "endif": T_ENDIF, "endchoice": T_ENDCHOICE, "source": T_SOURCE, + "choice": T_CHOICE, "config": T_CONFIG, "comment": T_COMMENT, + "menuconfig": T_MENUCONFIG, "help": T_HELP, "if": T_IF, + "depends": T_DEPENDS, "on": T_ON, "optional": T_OPTIONAL, + "prompt": T_PROMPT, "default": T_DEFAULT, "bool": T_BOOL, "boolean": T_BOOL, + "tristate": T_TRISTATE, "int": T_INT, "hex": T_HEX, "def_bool": T_DEF_BOOL, + "def_tristate": T_DEF_TRISTATE, "string": T_STRING, "select": T_SELECT, + "imply" : T_IMPLY, "range": T_RANGE, "option": T_OPTION, + "allnoconfig_y": T_ALLNOCONFIG_Y, "env": T_ENV, + "defconfig_list": T_DEFCONFIG_LIST, "modules": T_MODULES, + "visible": T_VISIBLE}.get + +# Strings to use for True and False +BOOL_STR = {False: "false", True: "true"} + +# Tokens after which identifier-like lexemes are treated as strings. T_CHOICE +# is included to avoid symbols being registered for named choices. +STRING_LEX = frozenset((T_BOOL, T_TRISTATE, T_INT, T_HEX, T_STRING, T_CHOICE, + T_PROMPT, T_MENU, T_COMMENT, T_SOURCE, T_MAINMENU)) + +# Matches the initial token on a line; see _tokenize(). Also eats trailing +# whitespace as an optimization. +_initial_token_re_match = re.compile(r"[^\w]*(\w+)\s*").match + +# Matches an identifier/keyword optionally preceded by whitespace. Also eats +# trailing whitespace as an optimization. +_id_keyword_re_match = re.compile(r"\s*([\w./-]+)\s*").match + +# Regular expression for finding $-references to symbols in strings +_sym_ref_re_search = re.compile(r"\$[A-Za-z0-9_]+").search + +# Integers representing symbol types +UNKNOWN, BOOL, TRISTATE, STRING, HEX, INT = range(6) + +# Strings to use for types +TYPENAME = {UNKNOWN: "unknown", BOOL: "bool", TRISTATE: "tristate", + STRING: "string", HEX: "hex", INT: "int"} + +# Token to type mapping +TOKEN_TO_TYPE = {T_BOOL: BOOL, T_TRISTATE: TRISTATE, T_STRING: STRING, + T_INT: INT, T_HEX: HEX} + +# Default values for symbols of different types (the value the symbol gets if +# it is not assigned a user value and none of its 'default' clauses kick in) +DEFAULT_VALUE = {BOOL: "n", TRISTATE: "n", STRING: "", INT: "", HEX: ""} + +# Indicates that no item is selected in a choice statement +NO_SELECTION = 0 + +# Integers representing expression types +AND, OR, NOT, EQUAL, UNEQUAL = range(5) + +# Map from tristate values to integers +TRI_TO_INT = {"n": 0, "m": 1, "y": 2} + +# Printing-related stuff + +OP_TO_STR = {AND: " && ", OR: " || ", EQUAL: " = ", UNEQUAL: " != "} +PRECEDENCE = {OR: 0, AND: 1, NOT: 2} diff --git a/tools/u-boot-tools/buildman/test.py b/tools/u-boot-tools/buildman/test.py new file mode 100644 index 0000000..de02f61 --- /dev/null +++ b/tools/u-boot-tools/buildman/test.py @@ -0,0 +1,464 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (c) 2012 The Chromium OS Authors. +# + +import os +import shutil +import sys +import tempfile +import time +import unittest + +# Bring in the patman libraries +our_path = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(our_path, '../patman')) + +import board +import bsettings +import builder +import control +import command +import commit +import terminal +import test_util +import toolchain + +use_network = True + +settings_data = ''' +# Buildman settings file + +[toolchain] +main: /usr/sbin + +[toolchain-alias] +x86: i386 x86_64 +''' + +errors = [ + '''main.c: In function 'main_loop': +main.c:260:6: warning: unused variable 'joe' [-Wunused-variable] +''', + '''main.c: In function 'main_loop2': +main.c:295:2: error: 'fred' undeclared (first use in this function) +main.c:295:2: note: each undeclared identifier is reported only once for each function it appears in +make[1]: *** [main.o] Error 1 +make: *** [common/libcommon.o] Error 2 +Make failed +''', + '''arch/arm/dts/socfpga_arria10_socdk_sdmmc.dtb: Warning \ +(avoid_unnecessary_addr_size): /clocks: unnecessary #address-cells/#size-cells \ +without "ranges" or child "reg" property +''', + '''powerpc-linux-ld: warning: dot moved backwards before `.bss' +powerpc-linux-ld: warning: dot moved backwards before `.bss' +powerpc-linux-ld: u-boot: section .text lma 0xfffc0000 overlaps previous sections +powerpc-linux-ld: u-boot: section .rodata lma 0xfffef3ec overlaps previous sections +powerpc-linux-ld: u-boot: section .reloc lma 0xffffa400 overlaps previous sections +powerpc-linux-ld: u-boot: section .data lma 0xffffcd38 overlaps previous sections +powerpc-linux-ld: u-boot: section .u_boot_cmd lma 0xffffeb40 overlaps previous sections +powerpc-linux-ld: u-boot: section .bootpg lma 0xfffff198 overlaps previous sections +''', + '''In file included from %(basedir)sarch/sandbox/cpu/cpu.c:9:0: +%(basedir)sarch/sandbox/include/asm/state.h:44:0: warning: "xxxx" redefined [enabled by default] +%(basedir)sarch/sandbox/include/asm/state.h:43:0: note: this is the location of the previous definition +%(basedir)sarch/sandbox/cpu/cpu.c: In function 'do_reset': +%(basedir)sarch/sandbox/cpu/cpu.c:27:1: error: unknown type name 'blah' +%(basedir)sarch/sandbox/cpu/cpu.c:28:12: error: expected declaration specifiers or '...' before numeric constant +make[2]: *** [arch/sandbox/cpu/cpu.o] Error 1 +make[1]: *** [arch/sandbox/cpu] Error 2 +make[1]: *** Waiting for unfinished jobs.... +In file included from %(basedir)scommon/board_f.c:55:0: +%(basedir)sarch/sandbox/include/asm/state.h:44:0: warning: "xxxx" redefined [enabled by default] +%(basedir)sarch/sandbox/include/asm/state.h:43:0: note: this is the location of the previous definition +make: *** [sub-make] Error 2 +''' +] + + +# hash, subject, return code, list of errors/warnings +commits = [ + ['1234', 'upstream/master, ok', 0, []], + ['5678', 'Second commit, a warning', 0, errors[0:1]], + ['9012', 'Third commit, error', 1, errors[0:2]], + ['3456', 'Fourth commit, warning', 0, [errors[0], errors[2]]], + ['7890', 'Fifth commit, link errors', 1, [errors[0], errors[3]]], + ['abcd', 'Sixth commit, fixes all errors', 0, []], + ['ef01', 'Seventh commit, check directory suppression', 1, [errors[4]]], +] + +boards = [ + ['Active', 'arm', 'armv7', '', 'Tester', 'ARM Board 1', 'board0', ''], + ['Active', 'arm', 'armv7', '', 'Tester', 'ARM Board 2', 'board1', ''], + ['Active', 'powerpc', 'powerpc', '', 'Tester', 'PowerPC board 1', 'board2', ''], + ['Active', 'powerpc', 'mpc83xx', '', 'Tester', 'PowerPC board 2', 'board3', ''], + ['Active', 'sandbox', 'sandbox', '', 'Tester', 'Sandbox board', 'board4', ''], +] + +BASE_DIR = 'base' + +OUTCOME_OK, OUTCOME_WARN, OUTCOME_ERR = range(3) + +class Options: + """Class that holds build options""" + pass + +class TestBuild(unittest.TestCase): + """Test buildman + + TODO: Write tests for the rest of the functionality + """ + def setUp(self): + # Set up commits to build + self.commits = [] + sequence = 0 + for commit_info in commits: + comm = commit.Commit(commit_info[0]) + comm.subject = commit_info[1] + comm.return_code = commit_info[2] + comm.error_list = commit_info[3] + comm.sequence = sequence + sequence += 1 + self.commits.append(comm) + + # Set up boards to build + self.boards = board.Boards() + for brd in boards: + self.boards.AddBoard(board.Board(*brd)) + self.boards.SelectBoards([]) + + # Add some test settings + bsettings.Setup(None) + bsettings.AddFile(settings_data) + + # Set up the toolchains + self.toolchains = toolchain.Toolchains() + self.toolchains.Add('arm-linux-gcc', test=False) + self.toolchains.Add('sparc-linux-gcc', test=False) + self.toolchains.Add('powerpc-linux-gcc', test=False) + self.toolchains.Add('gcc', test=False) + + # Avoid sending any output + terminal.SetPrintTestMode() + self._col = terminal.Color() + + def Make(self, commit, brd, stage, *args, **kwargs): + global base_dir + + result = command.CommandResult() + boardnum = int(brd.target[-1]) + result.return_code = 0 + result.stderr = '' + result.stdout = ('This is the test output for board %s, commit %s' % + (brd.target, commit.hash)) + if ((boardnum >= 1 and boardnum >= commit.sequence) or + boardnum == 4 and commit.sequence == 6): + result.return_code = commit.return_code + result.stderr = (''.join(commit.error_list) + % {'basedir' : base_dir + '/.bm-work/00/'}) + if stage == 'build': + target_dir = None + for arg in args: + if arg.startswith('O='): + target_dir = arg[2:] + + if not os.path.isdir(target_dir): + os.mkdir(target_dir) + + result.combined = result.stdout + result.stderr + return result + + def assertSummary(self, text, arch, plus, boards, outcome=OUTCOME_ERR): + col = self._col + expected_colour = (col.GREEN if outcome == OUTCOME_OK else + col.YELLOW if outcome == OUTCOME_WARN else col.RED) + expect = '%10s: ' % arch + # TODO(sjg@chromium.org): If plus is '', we shouldn't need this + expect += ' ' + col.Color(expected_colour, plus) + expect += ' ' + for board in boards: + expect += col.Color(expected_colour, ' %s' % board) + self.assertEqual(text, expect) + + def testOutput(self): + """Test basic builder operation and output + + This does a line-by-line verification of the summary output. + """ + global base_dir + + base_dir = tempfile.mkdtemp() + if not os.path.isdir(base_dir): + os.mkdir(base_dir) + build = builder.Builder(self.toolchains, base_dir, None, 1, 2, + checkout=False, show_unknown=False) + build.do_make = self.Make + board_selected = self.boards.GetSelectedDict() + + # Build the boards for the pre-defined commits and warnings/errors + # associated with each. This calls our Make() to inject the fake output. + build.BuildBoards(self.commits, board_selected, keep_outputs=False, + verbose=False) + lines = terminal.GetPrintTestLines() + count = 0 + for line in lines: + if line.text.strip(): + count += 1 + + # We should get two starting messages, then an update for every commit + # built. + self.assertEqual(count, len(commits) * len(boards) + 2) + build.SetDisplayOptions(show_errors=True); + build.ShowSummary(self.commits, board_selected) + #terminal.EchoPrintTestLines() + lines = terminal.GetPrintTestLines() + + # Upstream commit: no errors + self.assertEqual(lines[0].text, '01: %s' % commits[0][1]) + + # Second commit: all archs should fail with warnings + self.assertEqual(lines[1].text, '02: %s' % commits[1][1]) + + col = terminal.Color() + self.assertSummary(lines[2].text, 'sandbox', 'w+', ['board4'], + outcome=OUTCOME_WARN) + self.assertSummary(lines[3].text, 'arm', 'w+', ['board1'], + outcome=OUTCOME_WARN) + self.assertSummary(lines[4].text, 'powerpc', 'w+', ['board2', 'board3'], + outcome=OUTCOME_WARN) + + # Second commit: The warnings should be listed + self.assertEqual(lines[5].text, 'w+%s' % + errors[0].rstrip().replace('\n', '\nw+')) + self.assertEqual(lines[5].colour, col.MAGENTA) + + # Third commit: Still fails + self.assertEqual(lines[6].text, '03: %s' % commits[2][1]) + self.assertSummary(lines[7].text, 'sandbox', '+', ['board4']) + self.assertSummary(lines[8].text, 'arm', '', ['board1'], + outcome=OUTCOME_OK) + self.assertSummary(lines[9].text, 'powerpc', '+', ['board2', 'board3']) + + # Expect a compiler error + self.assertEqual(lines[10].text, '+%s' % + errors[1].rstrip().replace('\n', '\n+')) + + # Fourth commit: Compile errors are fixed, just have warning for board3 + self.assertEqual(lines[11].text, '04: %s' % commits[3][1]) + self.assertSummary(lines[12].text, 'sandbox', 'w+', ['board4'], + outcome=OUTCOME_WARN) + expect = '%10s: ' % 'powerpc' + expect += ' ' + col.Color(col.GREEN, '') + expect += ' ' + expect += col.Color(col.GREEN, ' %s' % 'board2') + expect += ' ' + col.Color(col.YELLOW, 'w+') + expect += ' ' + expect += col.Color(col.YELLOW, ' %s' % 'board3') + self.assertEqual(lines[13].text, expect) + + # Compile error fixed + self.assertEqual(lines[14].text, '-%s' % + errors[1].rstrip().replace('\n', '\n-')) + self.assertEqual(lines[14].colour, col.GREEN) + + self.assertEqual(lines[15].text, 'w+%s' % + errors[2].rstrip().replace('\n', '\nw+')) + self.assertEqual(lines[15].colour, col.MAGENTA) + + # Fifth commit + self.assertEqual(lines[16].text, '05: %s' % commits[4][1]) + self.assertSummary(lines[17].text, 'sandbox', '+', ['board4']) + self.assertSummary(lines[18].text, 'powerpc', '', ['board3'], + outcome=OUTCOME_OK) + + # The second line of errors[3] is a duplicate, so buildman will drop it + expect = errors[3].rstrip().split('\n') + expect = [expect[0]] + expect[2:] + self.assertEqual(lines[19].text, '+%s' % + '\n'.join(expect).replace('\n', '\n+')) + + self.assertEqual(lines[20].text, 'w-%s' % + errors[2].rstrip().replace('\n', '\nw-')) + + # Sixth commit + self.assertEqual(lines[21].text, '06: %s' % commits[5][1]) + self.assertSummary(lines[22].text, 'sandbox', '', ['board4'], + outcome=OUTCOME_OK) + + # The second line of errors[3] is a duplicate, so buildman will drop it + expect = errors[3].rstrip().split('\n') + expect = [expect[0]] + expect[2:] + self.assertEqual(lines[23].text, '-%s' % + '\n'.join(expect).replace('\n', '\n-')) + + self.assertEqual(lines[24].text, 'w-%s' % + errors[0].rstrip().replace('\n', '\nw-')) + + # Seventh commit + self.assertEqual(lines[25].text, '07: %s' % commits[6][1]) + self.assertSummary(lines[26].text, 'sandbox', '+', ['board4']) + + # Pick out the correct error lines + expect_str = errors[4].rstrip().replace('%(basedir)s', '').split('\n') + expect = expect_str[3:8] + [expect_str[-1]] + self.assertEqual(lines[27].text, '+%s' % + '\n'.join(expect).replace('\n', '\n+')) + + # Now the warnings lines + expect = [expect_str[0]] + expect_str[10:12] + [expect_str[9]] + self.assertEqual(lines[28].text, 'w+%s' % + '\n'.join(expect).replace('\n', '\nw+')) + + self.assertEqual(len(lines), 29) + shutil.rmtree(base_dir) + + def _testGit(self): + """Test basic builder operation by building a branch""" + base_dir = tempfile.mkdtemp() + if not os.path.isdir(base_dir): + os.mkdir(base_dir) + options = Options() + options.git = os.getcwd() + options.summary = False + options.jobs = None + options.dry_run = False + #options.git = os.path.join(base_dir, 'repo') + options.branch = 'test-buildman' + options.force_build = False + options.list_tool_chains = False + options.count = -1 + options.git_dir = None + options.threads = None + options.show_unknown = False + options.quick = False + options.show_errors = False + options.keep_outputs = False + args = ['tegra20'] + control.DoBuildman(options, args) + shutil.rmtree(base_dir) + + def testBoardSingle(self): + """Test single board selection""" + self.assertEqual(self.boards.SelectBoards(['sandbox']), + ({'all': ['board4'], 'sandbox': ['board4']}, [])) + + def testBoardArch(self): + """Test single board selection""" + self.assertEqual(self.boards.SelectBoards(['arm']), + ({'all': ['board0', 'board1'], + 'arm': ['board0', 'board1']}, [])) + + def testBoardArchSingle(self): + """Test single board selection""" + self.assertEqual(self.boards.SelectBoards(['arm sandbox']), + ({'sandbox': ['board4'], + 'all': ['board0', 'board1', 'board4'], + 'arm': ['board0', 'board1']}, [])) + + + def testBoardArchSingleMultiWord(self): + """Test single board selection""" + self.assertEqual(self.boards.SelectBoards(['arm', 'sandbox']), + ({'sandbox': ['board4'], + 'all': ['board0', 'board1', 'board4'], + 'arm': ['board0', 'board1']}, [])) + + def testBoardSingleAnd(self): + """Test single board selection""" + self.assertEqual(self.boards.SelectBoards(['Tester & arm']), + ({'Tester&arm': ['board0', 'board1'], + 'all': ['board0', 'board1']}, [])) + + def testBoardTwoAnd(self): + """Test single board selection""" + self.assertEqual(self.boards.SelectBoards(['Tester', '&', 'arm', + 'Tester' '&', 'powerpc', + 'sandbox']), + ({'sandbox': ['board4'], + 'all': ['board0', 'board1', 'board2', 'board3', + 'board4'], + 'Tester&powerpc': ['board2', 'board3'], + 'Tester&arm': ['board0', 'board1']}, [])) + + def testBoardAll(self): + """Test single board selection""" + self.assertEqual(self.boards.SelectBoards([]), + ({'all': ['board0', 'board1', 'board2', 'board3', + 'board4']}, [])) + + def testBoardRegularExpression(self): + """Test single board selection""" + self.assertEqual(self.boards.SelectBoards(['T.*r&^Po']), + ({'all': ['board2', 'board3'], + 'T.*r&^Po': ['board2', 'board3']}, [])) + + def testBoardDuplicate(self): + """Test single board selection""" + self.assertEqual(self.boards.SelectBoards(['sandbox sandbox', + 'sandbox']), + ({'all': ['board4'], 'sandbox': ['board4']}, [])) + def CheckDirs(self, build, dirname): + self.assertEqual('base%s' % dirname, build._GetOutputDir(1)) + self.assertEqual('base%s/fred' % dirname, + build.GetBuildDir(1, 'fred')) + self.assertEqual('base%s/fred/done' % dirname, + build.GetDoneFile(1, 'fred')) + self.assertEqual('base%s/fred/u-boot.sizes' % dirname, + build.GetFuncSizesFile(1, 'fred', 'u-boot')) + self.assertEqual('base%s/fred/u-boot.objdump' % dirname, + build.GetObjdumpFile(1, 'fred', 'u-boot')) + self.assertEqual('base%s/fred/err' % dirname, + build.GetErrFile(1, 'fred')) + + def testOutputDir(self): + build = builder.Builder(self.toolchains, BASE_DIR, None, 1, 2, + checkout=False, show_unknown=False) + build.commits = self.commits + build.commit_count = len(self.commits) + subject = self.commits[1].subject.translate(builder.trans_valid_chars) + dirname ='/%02d_of_%02d_g%s_%s' % (2, build.commit_count, commits[1][0], + subject[:20]) + self.CheckDirs(build, dirname) + + def testOutputDirCurrent(self): + build = builder.Builder(self.toolchains, BASE_DIR, None, 1, 2, + checkout=False, show_unknown=False) + build.commits = None + build.commit_count = 0 + self.CheckDirs(build, '/current') + + def testOutputDirNoSubdirs(self): + build = builder.Builder(self.toolchains, BASE_DIR, None, 1, 2, + checkout=False, show_unknown=False, + no_subdirs=True) + build.commits = None + build.commit_count = 0 + self.CheckDirs(build, '') + + def testToolchainAliases(self): + self.assertTrue(self.toolchains.Select('arm') != None) + with self.assertRaises(ValueError): + self.toolchains.Select('no-arch') + with self.assertRaises(ValueError): + self.toolchains.Select('x86') + + self.toolchains = toolchain.Toolchains() + self.toolchains.Add('x86_64-linux-gcc', test=False) + self.assertTrue(self.toolchains.Select('x86') != None) + + self.toolchains = toolchain.Toolchains() + self.toolchains.Add('i386-linux-gcc', test=False) + self.assertTrue(self.toolchains.Select('x86') != None) + + def testToolchainDownload(self): + """Test that we can download toolchains""" + if use_network: + with test_util.capture_sys_output() as (stdout, stderr): + url = self.toolchains.LocateArchUrl('arm') + self.assertRegexpMatches(url, 'https://www.kernel.org/pub/tools/' + 'crosstool/files/bin/x86_64/.*/' + 'x86_64-gcc-.*-nolibc_arm-.*linux-gnueabi.tar.xz') + + +if __name__ == "__main__": + unittest.main() diff --git a/tools/u-boot-tools/buildman/toolchain.py b/tools/u-boot-tools/buildman/toolchain.py new file mode 100644 index 0000000..c62ce13 --- /dev/null +++ b/tools/u-boot-tools/buildman/toolchain.py @@ -0,0 +1,577 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (c) 2012 The Chromium OS Authors. +# + +import re +import glob +from HTMLParser import HTMLParser +import os +import sys +import tempfile +import urllib2 + +import bsettings +import command +import terminal + +(PRIORITY_FULL_PREFIX, PRIORITY_PREFIX_GCC, PRIORITY_PREFIX_GCC_PATH, + PRIORITY_CALC) = range(4) + +# Simple class to collect links from a page +class MyHTMLParser(HTMLParser): + def __init__(self, arch): + """Create a new parser + + After the parser runs, self.links will be set to a list of the links + to .xz archives found in the page, and self.arch_link will be set to + the one for the given architecture (or None if not found). + + Args: + arch: Architecture to search for + """ + HTMLParser.__init__(self) + self.arch_link = None + self.links = [] + self.re_arch = re.compile('[-_]%s-' % arch) + + def handle_starttag(self, tag, attrs): + if tag == 'a': + for tag, value in attrs: + if tag == 'href': + if value and value.endswith('.xz'): + self.links.append(value) + if self.re_arch.search(value): + self.arch_link = value + + +class Toolchain: + """A single toolchain + + Public members: + gcc: Full path to C compiler + path: Directory path containing C compiler + cross: Cross compile string, e.g. 'arm-linux-' + arch: Architecture of toolchain as determined from the first + component of the filename. E.g. arm-linux-gcc becomes arm + priority: Toolchain priority (0=highest, 20=lowest) + """ + def __init__(self, fname, test, verbose=False, priority=PRIORITY_CALC, + arch=None): + """Create a new toolchain object. + + Args: + fname: Filename of the gcc component + test: True to run the toolchain to test it + verbose: True to print out the information + priority: Priority to use for this toolchain, or PRIORITY_CALC to + calculate it + """ + self.gcc = fname + self.path = os.path.dirname(fname) + + # Find the CROSS_COMPILE prefix to use for U-Boot. For example, + # 'arm-linux-gnueabihf-gcc' turns into 'arm-linux-gnueabihf-'. + basename = os.path.basename(fname) + pos = basename.rfind('-') + self.cross = basename[:pos + 1] if pos != -1 else '' + + # The architecture is the first part of the name + pos = self.cross.find('-') + if arch: + self.arch = arch + else: + self.arch = self.cross[:pos] if pos != -1 else 'sandbox' + + env = self.MakeEnvironment(False) + + # As a basic sanity check, run the C compiler with --version + cmd = [fname, '--version'] + if priority == PRIORITY_CALC: + self.priority = self.GetPriority(fname) + else: + self.priority = priority + if test: + result = command.RunPipe([cmd], capture=True, env=env, + raise_on_error=False) + self.ok = result.return_code == 0 + if verbose: + print 'Tool chain test: ', + if self.ok: + print "OK, arch='%s', priority %d" % (self.arch, + self.priority) + else: + print 'BAD' + print 'Command: ', cmd + print result.stdout + print result.stderr + else: + self.ok = True + + def GetPriority(self, fname): + """Return the priority of the toolchain. + + Toolchains are ranked according to their suitability by their + filename prefix. + + Args: + fname: Filename of toolchain + Returns: + Priority of toolchain, PRIORITY_CALC=highest, 20=lowest. + """ + priority_list = ['-elf', '-unknown-linux-gnu', '-linux', + '-none-linux-gnueabi', '-none-linux-gnueabihf', '-uclinux', + '-none-eabi', '-gentoo-linux-gnu', '-linux-gnueabi', + '-linux-gnueabihf', '-le-linux', '-uclinux'] + for prio in range(len(priority_list)): + if priority_list[prio] in fname: + return PRIORITY_CALC + prio + return PRIORITY_CALC + prio + + def GetWrapper(self, show_warning=True): + """Get toolchain wrapper from the setting file. + """ + value = '' + for name, value in bsettings.GetItems('toolchain-wrapper'): + if not value: + print "Warning: Wrapper not found" + if value: + value = value + ' ' + + return value + + def MakeEnvironment(self, full_path): + """Returns an environment for using the toolchain. + + Thie takes the current environment and adds CROSS_COMPILE so that + the tool chain will operate correctly. This also disables localized + output and possibly unicode encoded output of all build tools by + adding LC_ALL=C. + + Args: + full_path: Return the full path in CROSS_COMPILE and don't set + PATH + """ + env = dict(os.environ) + wrapper = self.GetWrapper() + + if full_path: + env['CROSS_COMPILE'] = wrapper + os.path.join(self.path, self.cross) + else: + env['CROSS_COMPILE'] = wrapper + self.cross + env['PATH'] = self.path + ':' + env['PATH'] + + env['LC_ALL'] = 'C' + + return env + + +class Toolchains: + """Manage a list of toolchains for building U-Boot + + We select one toolchain for each architecture type + + Public members: + toolchains: Dict of Toolchain objects, keyed by architecture name + prefixes: Dict of prefixes to check, keyed by architecture. This can + be a full path and toolchain prefix, for example + {'x86', 'opt/i386-linux/bin/i386-linux-'}, or the name of + something on the search path, for example + {'arm', 'arm-linux-gnueabihf-'}. Wildcards are not supported. + paths: List of paths to check for toolchains (may contain wildcards) + """ + + def __init__(self): + self.toolchains = {} + self.prefixes = {} + self.paths = [] + self._make_flags = dict(bsettings.GetItems('make-flags')) + + def GetPathList(self, show_warning=True): + """Get a list of available toolchain paths + + Args: + show_warning: True to show a warning if there are no tool chains. + + Returns: + List of strings, each a path to a toolchain mentioned in the + [toolchain] section of the settings file. + """ + toolchains = bsettings.GetItems('toolchain') + if show_warning and not toolchains: + print ("Warning: No tool chains. Please run 'buildman " + "--fetch-arch all' to download all available toolchains, or " + "add a [toolchain] section to your buildman config file " + "%s. See README for details" % + bsettings.config_fname) + + paths = [] + for name, value in toolchains: + if '*' in value: + paths += glob.glob(value) + else: + paths.append(value) + return paths + + def GetSettings(self, show_warning=True): + """Get toolchain settings from the settings file. + + Args: + show_warning: True to show a warning if there are no tool chains. + """ + self.prefixes = bsettings.GetItems('toolchain-prefix') + self.paths += self.GetPathList(show_warning) + + def Add(self, fname, test=True, verbose=False, priority=PRIORITY_CALC, + arch=None): + """Add a toolchain to our list + + We select the given toolchain as our preferred one for its + architecture if it is a higher priority than the others. + + Args: + fname: Filename of toolchain's gcc driver + test: True to run the toolchain to test it + priority: Priority to use for this toolchain + arch: Toolchain architecture, or None if not known + """ + toolchain = Toolchain(fname, test, verbose, priority, arch) + add_it = toolchain.ok + if toolchain.arch in self.toolchains: + add_it = (toolchain.priority < + self.toolchains[toolchain.arch].priority) + if add_it: + self.toolchains[toolchain.arch] = toolchain + elif verbose: + print ("Toolchain '%s' at priority %d will be ignored because " + "another toolchain for arch '%s' has priority %d" % + (toolchain.gcc, toolchain.priority, toolchain.arch, + self.toolchains[toolchain.arch].priority)) + + def ScanPath(self, path, verbose): + """Scan a path for a valid toolchain + + Args: + path: Path to scan + verbose: True to print out progress information + Returns: + Filename of C compiler if found, else None + """ + fnames = [] + for subdir in ['.', 'bin', 'usr/bin']: + dirname = os.path.join(path, subdir) + if verbose: print " - looking in '%s'" % dirname + for fname in glob.glob(dirname + '/*gcc'): + if verbose: print " - found '%s'" % fname + fnames.append(fname) + return fnames + + def ScanPathEnv(self, fname): + """Scan the PATH environment variable for a given filename. + + Args: + fname: Filename to scan for + Returns: + List of matching pathanames, or [] if none + """ + pathname_list = [] + for path in os.environ["PATH"].split(os.pathsep): + path = path.strip('"') + pathname = os.path.join(path, fname) + if os.path.exists(pathname): + pathname_list.append(pathname) + return pathname_list + + def Scan(self, verbose): + """Scan for available toolchains and select the best for each arch. + + We look for all the toolchains we can file, figure out the + architecture for each, and whether it works. Then we select the + highest priority toolchain for each arch. + + Args: + verbose: True to print out progress information + """ + if verbose: print 'Scanning for tool chains' + for name, value in self.prefixes: + if verbose: print " - scanning prefix '%s'" % value + if os.path.exists(value): + self.Add(value, True, verbose, PRIORITY_FULL_PREFIX, name) + continue + fname = value + 'gcc' + if os.path.exists(fname): + self.Add(fname, True, verbose, PRIORITY_PREFIX_GCC, name) + continue + fname_list = self.ScanPathEnv(fname) + for f in fname_list: + self.Add(f, True, verbose, PRIORITY_PREFIX_GCC_PATH, name) + if not fname_list: + raise ValueError, ("No tool chain found for prefix '%s'" % + value) + for path in self.paths: + if verbose: print " - scanning path '%s'" % path + fnames = self.ScanPath(path, verbose) + for fname in fnames: + self.Add(fname, True, verbose) + + def List(self): + """List out the selected toolchains for each architecture""" + col = terminal.Color() + print col.Color(col.BLUE, 'List of available toolchains (%d):' % + len(self.toolchains)) + if len(self.toolchains): + for key, value in sorted(self.toolchains.iteritems()): + print '%-10s: %s' % (key, value.gcc) + else: + print 'None' + + def Select(self, arch): + """Returns the toolchain for a given architecture + + Args: + args: Name of architecture (e.g. 'arm', 'ppc_8xx') + + returns: + toolchain object, or None if none found + """ + for tag, value in bsettings.GetItems('toolchain-alias'): + if arch == tag: + for alias in value.split(): + if alias in self.toolchains: + return self.toolchains[alias] + + if not arch in self.toolchains: + raise ValueError, ("No tool chain found for arch '%s'" % arch) + return self.toolchains[arch] + + def ResolveReferences(self, var_dict, args): + """Resolve variable references in a string + + This converts ${blah} within the string to the value of blah. + This function works recursively. + + Args: + var_dict: Dictionary containing variables and their values + args: String containing make arguments + Returns: + Resolved string + + >>> bsettings.Setup() + >>> tcs = Toolchains() + >>> tcs.Add('fred', False) + >>> var_dict = {'oblique' : 'OBLIQUE', 'first' : 'fi${second}rst', \ + 'second' : '2nd'} + >>> tcs.ResolveReferences(var_dict, 'this=${oblique}_set') + 'this=OBLIQUE_set' + >>> tcs.ResolveReferences(var_dict, 'this=${oblique}_set${first}nd') + 'this=OBLIQUE_setfi2ndrstnd' + """ + re_var = re.compile('(\$\{[-_a-z0-9A-Z]{1,}\})') + + while True: + m = re_var.search(args) + if not m: + break + lookup = m.group(0)[2:-1] + value = var_dict.get(lookup, '') + args = args[:m.start(0)] + value + args[m.end(0):] + return args + + def GetMakeArguments(self, board): + """Returns 'make' arguments for a given board + + The flags are in a section called 'make-flags'. Flags are named + after the target they represent, for example snapper9260=TESTING=1 + will pass TESTING=1 to make when building the snapper9260 board. + + References to other boards can be added in the string also. For + example: + + [make-flags] + at91-boards=ENABLE_AT91_TEST=1 + snapper9260=${at91-boards} BUILD_TAG=442 + snapper9g45=${at91-boards} BUILD_TAG=443 + + This will return 'ENABLE_AT91_TEST=1 BUILD_TAG=442' for snapper9260 + and 'ENABLE_AT91_TEST=1 BUILD_TAG=443' for snapper9g45. + + A special 'target' variable is set to the board target. + + Args: + board: Board object for the board to check. + Returns: + 'make' flags for that board, or '' if none + """ + self._make_flags['target'] = board.target + arg_str = self.ResolveReferences(self._make_flags, + self._make_flags.get(board.target, '')) + args = arg_str.split(' ') + i = 0 + while i < len(args): + if not args[i]: + del args[i] + else: + i += 1 + return args + + def LocateArchUrl(self, fetch_arch): + """Find a toolchain available online + + Look in standard places for available toolchains. At present the + only standard place is at kernel.org. + + Args: + arch: Architecture to look for, or 'list' for all + Returns: + If fetch_arch is 'list', a tuple: + Machine architecture (e.g. x86_64) + List of toolchains + else + URL containing this toolchain, if avaialble, else None + """ + arch = command.OutputOneLine('uname', '-m') + base = 'https://www.kernel.org/pub/tools/crosstool/files/bin' + versions = ['7.3.0', '6.4.0', '4.9.4'] + links = [] + for version in versions: + url = '%s/%s/%s/' % (base, arch, version) + print 'Checking: %s' % url + response = urllib2.urlopen(url) + html = response.read() + parser = MyHTMLParser(fetch_arch) + parser.feed(html) + if fetch_arch == 'list': + links += parser.links + elif parser.arch_link: + return url + parser.arch_link + if fetch_arch == 'list': + return arch, links + return None + + def Download(self, url): + """Download a file to a temporary directory + + Args: + url: URL to download + Returns: + Tuple: + Temporary directory name + Full path to the downloaded archive file in that directory, + or None if there was an error while downloading + """ + print 'Downloading: %s' % url + leaf = url.split('/')[-1] + tmpdir = tempfile.mkdtemp('.buildman') + response = urllib2.urlopen(url) + fname = os.path.join(tmpdir, leaf) + fd = open(fname, 'wb') + meta = response.info() + size = int(meta.getheaders('Content-Length')[0]) + done = 0 + block_size = 1 << 16 + status = '' + + # Read the file in chunks and show progress as we go + while True: + buffer = response.read(block_size) + if not buffer: + print chr(8) * (len(status) + 1), '\r', + break + + done += len(buffer) + fd.write(buffer) + status = r'%10d MiB [%3d%%]' % (done / 1024 / 1024, + done * 100 / size) + status = status + chr(8) * (len(status) + 1) + print status, + sys.stdout.flush() + fd.close() + if done != size: + print 'Error, failed to download' + os.remove(fname) + fname = None + return tmpdir, fname + + def Unpack(self, fname, dest): + """Unpack a tar file + + Args: + fname: Filename to unpack + dest: Destination directory + Returns: + Directory name of the first entry in the archive, without the + trailing / + """ + stdout = command.Output('tar', 'xvfJ', fname, '-C', dest) + dirs = stdout.splitlines()[1].split('/')[:2] + return '/'.join(dirs) + + def TestSettingsHasPath(self, path): + """Check if buildman will find this toolchain + + Returns: + True if the path is in settings, False if not + """ + paths = self.GetPathList(False) + return path in paths + + def ListArchs(self): + """List architectures with available toolchains to download""" + host_arch, archives = self.LocateArchUrl('list') + re_arch = re.compile('[-a-z0-9.]*[-_]([^-]*)-.*') + arch_set = set() + for archive in archives: + # Remove the host architecture from the start + arch = re_arch.match(archive[len(host_arch):]) + if arch: + if arch.group(1) != '2.0' and arch.group(1) != '64': + arch_set.add(arch.group(1)) + return sorted(arch_set) + + def FetchAndInstall(self, arch): + """Fetch and install a new toolchain + + arch: + Architecture to fetch, or 'list' to list + """ + # Fist get the URL for this architecture + col = terminal.Color() + print col.Color(col.BLUE, "Downloading toolchain for arch '%s'" % arch) + url = self.LocateArchUrl(arch) + if not url: + print ("Cannot find toolchain for arch '%s' - use 'list' to list" % + arch) + return 2 + home = os.environ['HOME'] + dest = os.path.join(home, '.buildman-toolchains') + if not os.path.exists(dest): + os.mkdir(dest) + + # Download the tar file for this toolchain and unpack it + tmpdir, tarfile = self.Download(url) + if not tarfile: + return 1 + print col.Color(col.GREEN, 'Unpacking to: %s' % dest), + sys.stdout.flush() + path = self.Unpack(tarfile, dest) + os.remove(tarfile) + os.rmdir(tmpdir) + print + + # Check that the toolchain works + print col.Color(col.GREEN, 'Testing') + dirpath = os.path.join(dest, path) + compiler_fname_list = self.ScanPath(dirpath, True) + if not compiler_fname_list: + print 'Could not locate C compiler - fetch failed.' + return 1 + if len(compiler_fname_list) != 1: + print col.Color(col.RED, 'Warning, ambiguous toolchains: %s' % + ', '.join(compiler_fname_list)) + toolchain = Toolchain(compiler_fname_list[0], True, True) + + # Make sure that it will be found by buildman + if not self.TestSettingsHasPath(dirpath): + print ("Adding 'download' to config file '%s'" % + bsettings.config_fname) + bsettings.SetItem('toolchain', 'download', '%s/*/*' % dest) + return 0 diff --git a/tools/u-boot-tools/common/.bootm.o.cmd b/tools/u-boot-tools/common/.bootm.o.cmd new file mode 100644 index 0000000..075bd8a --- /dev/null +++ b/tools/u-boot-tools/common/.bootm.o.cmd @@ -0,0 +1,206 @@ +cmd_tools/common/bootm.o := cc -Wp,-MD,tools/common/.bootm.o.d -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -std=gnu11 -include ./include/compiler.h -idirafterinclude -idirafter./arch/arm/include -I./scripts/dtc/libfdt -I./tools -DUSE_HOSTCC -D__KERNEL_STRICT_NAMES -D_GNU_SOURCE -c -o tools/common/bootm.o tools/common/bootm.c + +source_tools/common/bootm.o := tools/common/bootm.c + +deps_tools/common/bootm.o := \ + /usr/include/stdc-predef.h \ + include/compiler.h \ + /usr/lib/gcc/x86_64-linux-gnu/8/include/stddef.h \ + /usr/lib/gcc/x86_64-linux-gnu/8/include/stdint.h \ + /usr/include/stdint.h \ + /usr/include/x86_64-linux-gnu/bits/libc-header-start.h \ + /usr/include/features.h \ + /usr/include/x86_64-linux-gnu/sys/cdefs.h \ + /usr/include/x86_64-linux-gnu/bits/wordsize.h \ + /usr/include/x86_64-linux-gnu/bits/long-double.h \ + /usr/include/x86_64-linux-gnu/gnu/stubs.h \ + /usr/include/x86_64-linux-gnu/gnu/stubs-64.h \ + /usr/include/x86_64-linux-gnu/bits/types.h \ + /usr/include/x86_64-linux-gnu/bits/typesizes.h \ + /usr/include/x86_64-linux-gnu/bits/wchar.h \ + /usr/include/x86_64-linux-gnu/bits/stdint-intn.h \ + /usr/include/x86_64-linux-gnu/bits/stdint-uintn.h \ + /usr/include/errno.h \ + /usr/include/x86_64-linux-gnu/bits/errno.h \ + /usr/include/linux/errno.h \ + /usr/include/x86_64-linux-gnu/asm/errno.h \ + /usr/include/asm-generic/errno.h \ + /usr/include/asm-generic/errno-base.h \ + /usr/include/x86_64-linux-gnu/bits/types/error_t.h \ + /usr/include/stdlib.h \ + /usr/include/x86_64-linux-gnu/bits/waitflags.h \ + /usr/include/x86_64-linux-gnu/bits/waitstatus.h \ + /usr/include/x86_64-linux-gnu/bits/floatn.h \ + /usr/include/x86_64-linux-gnu/bits/floatn-common.h \ + /usr/include/x86_64-linux-gnu/bits/types/locale_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/__locale_t.h \ + /usr/include/x86_64-linux-gnu/sys/types.h \ + /usr/include/x86_64-linux-gnu/bits/types/clock_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/clockid_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/time_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/timer_t.h \ + /usr/include/endian.h \ + /usr/include/x86_64-linux-gnu/bits/endian.h \ + /usr/include/x86_64-linux-gnu/bits/byteswap.h \ + /usr/include/x86_64-linux-gnu/bits/uintn-identity.h \ + /usr/include/x86_64-linux-gnu/sys/select.h \ + /usr/include/x86_64-linux-gnu/bits/select.h \ + /usr/include/x86_64-linux-gnu/bits/types/sigset_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/__sigset_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/struct_timeval.h \ + /usr/include/x86_64-linux-gnu/bits/types/struct_timespec.h \ + /usr/include/x86_64-linux-gnu/bits/pthreadtypes.h \ + /usr/include/x86_64-linux-gnu/bits/thread-shared-types.h \ + /usr/include/x86_64-linux-gnu/bits/pthreadtypes-arch.h \ + /usr/include/alloca.h \ + /usr/include/x86_64-linux-gnu/bits/stdlib-bsearch.h \ + /usr/include/x86_64-linux-gnu/bits/stdlib-float.h \ + /usr/include/stdio.h \ + /usr/lib/gcc/x86_64-linux-gnu/8/include/stdarg.h \ + /usr/include/x86_64-linux-gnu/bits/types/__fpos_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/__mbstate_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/__fpos64_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/__FILE.h \ + /usr/include/x86_64-linux-gnu/bits/types/FILE.h \ + /usr/include/x86_64-linux-gnu/bits/types/struct_FILE.h \ + /usr/include/x86_64-linux-gnu/bits/types/cookie_io_functions_t.h \ + /usr/include/x86_64-linux-gnu/bits/stdio_lim.h \ + /usr/include/x86_64-linux-gnu/bits/sys_errlist.h \ + /usr/include/x86_64-linux-gnu/bits/stdio.h \ + /usr/include/string.h \ + /usr/include/strings.h \ + /usr/include/x86_64-linux-gnu/sys/mman.h \ + /usr/include/x86_64-linux-gnu/bits/mman.h \ + /usr/include/x86_64-linux-gnu/bits/mman-linux.h \ + /usr/include/x86_64-linux-gnu/bits/mman-shared.h \ + /usr/include/fcntl.h \ + /usr/include/x86_64-linux-gnu/bits/fcntl.h \ + /usr/include/x86_64-linux-gnu/bits/fcntl-linux.h \ + /usr/include/x86_64-linux-gnu/bits/types/struct_iovec.h \ + /usr/include/linux/falloc.h \ + /usr/include/x86_64-linux-gnu/bits/stat.h \ + /usr/include/byteswap.h \ + /usr/include/time.h \ + /usr/include/x86_64-linux-gnu/bits/time.h \ + /usr/include/x86_64-linux-gnu/bits/timex.h \ + /usr/include/x86_64-linux-gnu/bits/types/struct_tm.h \ + /usr/include/x86_64-linux-gnu/bits/types/struct_itimerspec.h \ + tools/../common/bootm.c \ + $(wildcard include/config/cmd/usb.h) \ + $(wildcard include/config/sys/bootm/len.h) \ + $(wildcard include/config/lmb.h) \ + $(wildcard include/config/image/format/legacy.h) \ + $(wildcard include/config/android/boot/image.h) \ + $(wildcard include/config/cmd/booti.h) \ + $(wildcard include/config/fpga.h) \ + $(wildcard include/config/gzip.h) \ + $(wildcard include/config/bzip2.h) \ + $(wildcard include/config/sys/malloc/len.h) \ + $(wildcard include/config/lzma.h) \ + $(wildcard include/config/lzo.h) \ + $(wildcard include/config/lz4.h) \ + $(wildcard include/config/netconsole.h) \ + $(wildcard include/config/dm/eth.h) \ + $(wildcard include/config/silent/console.h) \ + $(wildcard include/config/silent/u/boot/only.h) \ + $(wildcard include/config/sys/boot/ramdisk/high.h) \ + $(wildcard include/config/trace.h) \ + tools/mkimage.h \ + tools/os_support.h \ + include/compiler.h \ + /usr/include/x86_64-linux-gnu/sys/stat.h \ + /usr/include/x86_64-linux-gnu/bits/statx.h \ + /usr/include/unistd.h \ + /usr/include/x86_64-linux-gnu/bits/posix_opt.h \ + /usr/include/x86_64-linux-gnu/bits/environments.h \ + /usr/include/x86_64-linux-gnu/bits/confname.h \ + /usr/include/x86_64-linux-gnu/bits/getopt_posix.h \ + /usr/include/x86_64-linux-gnu/bits/getopt_core.h \ + include/u-boot/sha1.h \ + tools/fdt_host.h \ + tools/../include/linux/libfdt.h \ + tools/../include/linux/../../scripts/dtc/libfdt/libfdt.h \ + tools/../include/linux/../../scripts/dtc/libfdt/libfdt_env.h \ + tools/../include/linux/../../scripts/dtc/libfdt/fdt.h \ + tools/../include/fdt_support.h \ + $(wildcard include/config/of/libfdt.h) \ + $(wildcard include/config/arch/fixup/fdt/memory.h) \ + $(wildcard include/config/usb/ehci/fsl.h) \ + $(wildcard include/config/usb/xhci/fsl.h) \ + $(wildcard include/config/sys/fsl/sec/compat.h) \ + $(wildcard include/config/pci.h) \ + $(wildcard include/config/sys/fdt/pad.h) \ + $(wildcard include/config/of/board/setup.h) \ + $(wildcard include/config/of/system/setup.h) \ + $(wildcard include/config/fdt/fixup/partitions.h) \ + $(wildcard include/config/fman/enet.h) \ + $(wildcard include/config/fsl/mc/enet.h) \ + tools/imagetool.h \ + /usr/lib/gcc/x86_64-linux-gnu/8/include/stdbool.h \ + include/command.h \ + $(wildcard include/config/sys/help/cmd/width.h) \ + $(wildcard include/config/sys/longhelp.h) \ + $(wildcard include/config/auto/complete.h) \ + $(wildcard include/config/cmd/run.h) \ + $(wildcard include/config/cmd/memory.h) \ + $(wildcard include/config/cmd/i2c.h) \ + $(wildcard include/config/cmd/itest.h) \ + $(wildcard include/config/cmd/pci.h) \ + $(wildcard include/config/cmd/bootd.h) \ + $(wildcard include/config/cmd/bootm.h) \ + $(wildcard include/config/cmdline.h) \ + include/linker_lists.h \ + include/linux/compiler.h \ + $(wildcard include/config/sparse/rcu/pointer.h) \ + $(wildcard include/config/trace/branch/profiling.h) \ + $(wildcard include/config/profile/all/branches.h) \ + $(wildcard include/config/kasan.h) \ + $(wildcard include/config/enable/must/check.h) \ + $(wildcard include/config/enable/warn/deprecated.h) \ + $(wildcard include/config/kprobes.h) \ + include/bootm.h \ + include/image.h \ + $(wildcard include/config/fit/verbose.h) \ + $(wildcard include/config/fit/enable/rsassa/pss/support.h) \ + $(wildcard include/config/fit/enable/sha256/support.h) \ + $(wildcard include/config/sha1.h) \ + $(wildcard include/config/sha256.h) \ + $(wildcard include/config/fit.h) \ + $(wildcard include/config/spl/build.h) \ + $(wildcard include/config/spl/crc32/support.h) \ + $(wildcard include/config/spl/md5/support.h) \ + $(wildcard include/config/spl/sha1/support.h) \ + $(wildcard include/config/crc32.h) \ + $(wildcard include/config/spl/sha256/support.h) \ + $(wildcard include/config/sys/boot/get/cmdline.h) \ + $(wildcard include/config/timestamp.h) \ + $(wildcard include/config/cmd/date.h) \ + $(wildcard include/config/sys/boot/get/kbd.h) \ + $(wildcard include/config/fit/signature.h) \ + $(wildcard include/config/fit/best/match.h) \ + $(wildcard include/config/spl/fit/image/post/process.h) \ + $(wildcard include/config/fit/image/post/process.h) \ + include/compiler.h \ + /usr/include/x86_64-linux-gnu/asm/byteorder.h \ + /usr/include/linux/byteorder/little_endian.h \ + /usr/include/linux/types.h \ + /usr/include/x86_64-linux-gnu/asm/types.h \ + /usr/include/asm-generic/types.h \ + /usr/include/asm-generic/int-ll64.h \ + /usr/include/x86_64-linux-gnu/asm/bitsperlong.h \ + /usr/include/asm-generic/bitsperlong.h \ + $(wildcard include/config/64bit.h) \ + /usr/include/linux/posix_types.h \ + /usr/include/linux/stddef.h \ + /usr/include/x86_64-linux-gnu/asm/posix_types.h \ + /usr/include/x86_64-linux-gnu/asm/posix_types_64.h \ + /usr/include/asm-generic/posix_types.h \ + /usr/include/linux/swab.h \ + /usr/include/x86_64-linux-gnu/asm/swab.h \ + include/hash.h \ + include/linux/libfdt.h \ + include/fdt_support.h \ + +tools/common/bootm.o: $(deps_tools/common/bootm.o) + +$(deps_tools/common/bootm.o): diff --git a/tools/u-boot-tools/common/.hash.o.cmd b/tools/u-boot-tools/common/.hash.o.cmd new file mode 100644 index 0000000..3006984 --- /dev/null +++ b/tools/u-boot-tools/common/.hash.o.cmd @@ -0,0 +1,180 @@ +cmd_tools/common/hash.o := cc -Wp,-MD,tools/common/.hash.o.d -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -std=gnu11 -include ./include/compiler.h -idirafterinclude -idirafter./arch/arm/include -I./scripts/dtc/libfdt -I./tools -DUSE_HOSTCC -D__KERNEL_STRICT_NAMES -D_GNU_SOURCE -c -o tools/common/hash.o tools/common/hash.c + +source_tools/common/hash.o := tools/common/hash.c + +deps_tools/common/hash.o := \ + /usr/include/stdc-predef.h \ + include/compiler.h \ + /usr/lib/gcc/x86_64-linux-gnu/8/include/stddef.h \ + /usr/lib/gcc/x86_64-linux-gnu/8/include/stdint.h \ + /usr/include/stdint.h \ + /usr/include/x86_64-linux-gnu/bits/libc-header-start.h \ + /usr/include/features.h \ + /usr/include/x86_64-linux-gnu/sys/cdefs.h \ + /usr/include/x86_64-linux-gnu/bits/wordsize.h \ + /usr/include/x86_64-linux-gnu/bits/long-double.h \ + /usr/include/x86_64-linux-gnu/gnu/stubs.h \ + /usr/include/x86_64-linux-gnu/gnu/stubs-64.h \ + /usr/include/x86_64-linux-gnu/bits/types.h \ + /usr/include/x86_64-linux-gnu/bits/typesizes.h \ + /usr/include/x86_64-linux-gnu/bits/wchar.h \ + /usr/include/x86_64-linux-gnu/bits/stdint-intn.h \ + /usr/include/x86_64-linux-gnu/bits/stdint-uintn.h \ + /usr/include/errno.h \ + /usr/include/x86_64-linux-gnu/bits/errno.h \ + /usr/include/linux/errno.h \ + /usr/include/x86_64-linux-gnu/asm/errno.h \ + /usr/include/asm-generic/errno.h \ + /usr/include/asm-generic/errno-base.h \ + /usr/include/x86_64-linux-gnu/bits/types/error_t.h \ + /usr/include/stdlib.h \ + /usr/include/x86_64-linux-gnu/bits/waitflags.h \ + /usr/include/x86_64-linux-gnu/bits/waitstatus.h \ + /usr/include/x86_64-linux-gnu/bits/floatn.h \ + /usr/include/x86_64-linux-gnu/bits/floatn-common.h \ + /usr/include/x86_64-linux-gnu/bits/types/locale_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/__locale_t.h \ + /usr/include/x86_64-linux-gnu/sys/types.h \ + /usr/include/x86_64-linux-gnu/bits/types/clock_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/clockid_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/time_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/timer_t.h \ + /usr/include/endian.h \ + /usr/include/x86_64-linux-gnu/bits/endian.h \ + /usr/include/x86_64-linux-gnu/bits/byteswap.h \ + /usr/include/x86_64-linux-gnu/bits/uintn-identity.h \ + /usr/include/x86_64-linux-gnu/sys/select.h \ + /usr/include/x86_64-linux-gnu/bits/select.h \ + /usr/include/x86_64-linux-gnu/bits/types/sigset_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/__sigset_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/struct_timeval.h \ + /usr/include/x86_64-linux-gnu/bits/types/struct_timespec.h \ + /usr/include/x86_64-linux-gnu/bits/pthreadtypes.h \ + /usr/include/x86_64-linux-gnu/bits/thread-shared-types.h \ + /usr/include/x86_64-linux-gnu/bits/pthreadtypes-arch.h \ + /usr/include/alloca.h \ + /usr/include/x86_64-linux-gnu/bits/stdlib-bsearch.h \ + /usr/include/x86_64-linux-gnu/bits/stdlib-float.h \ + /usr/include/stdio.h \ + /usr/lib/gcc/x86_64-linux-gnu/8/include/stdarg.h \ + /usr/include/x86_64-linux-gnu/bits/types/__fpos_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/__mbstate_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/__fpos64_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/__FILE.h \ + /usr/include/x86_64-linux-gnu/bits/types/FILE.h \ + /usr/include/x86_64-linux-gnu/bits/types/struct_FILE.h \ + /usr/include/x86_64-linux-gnu/bits/types/cookie_io_functions_t.h \ + /usr/include/x86_64-linux-gnu/bits/stdio_lim.h \ + /usr/include/x86_64-linux-gnu/bits/sys_errlist.h \ + /usr/include/x86_64-linux-gnu/bits/stdio.h \ + /usr/include/string.h \ + /usr/include/strings.h \ + /usr/include/x86_64-linux-gnu/sys/mman.h \ + /usr/include/x86_64-linux-gnu/bits/mman.h \ + /usr/include/x86_64-linux-gnu/bits/mman-linux.h \ + /usr/include/x86_64-linux-gnu/bits/mman-shared.h \ + /usr/include/fcntl.h \ + /usr/include/x86_64-linux-gnu/bits/fcntl.h \ + /usr/include/x86_64-linux-gnu/bits/fcntl-linux.h \ + /usr/include/x86_64-linux-gnu/bits/types/struct_iovec.h \ + /usr/include/linux/falloc.h \ + /usr/include/x86_64-linux-gnu/bits/stat.h \ + /usr/include/byteswap.h \ + /usr/include/time.h \ + /usr/include/x86_64-linux-gnu/bits/time.h \ + /usr/include/x86_64-linux-gnu/bits/timex.h \ + /usr/include/x86_64-linux-gnu/bits/types/struct_tm.h \ + /usr/include/x86_64-linux-gnu/bits/types/struct_itimerspec.h \ + tools/../common/hash.c \ + $(wildcard include/config/needs/manual/reloc.h) \ + $(wildcard include/config/sha1.h) \ + $(wildcard include/config/sha/prog/hw/accel.h) \ + $(wildcard include/config/sha256.h) \ + $(wildcard include/config/sha/hw/accel.h) \ + $(wildcard include/config/cmd/sha1sum.h) \ + $(wildcard include/config/crc32/verify.h) \ + $(wildcard include/config/cmd/hash.h) \ + $(wildcard include/config/cmd/crc32.h) \ + $(wildcard include/config/sha1sum/verify.h) \ + $(wildcard include/config/hash/verify.h) \ + tools/mkimage.h \ + tools/os_support.h \ + include/compiler.h \ + /usr/include/x86_64-linux-gnu/sys/stat.h \ + /usr/include/x86_64-linux-gnu/bits/statx.h \ + /usr/include/unistd.h \ + /usr/include/x86_64-linux-gnu/bits/posix_opt.h \ + /usr/include/x86_64-linux-gnu/bits/environments.h \ + /usr/include/x86_64-linux-gnu/bits/confname.h \ + /usr/include/x86_64-linux-gnu/bits/getopt_posix.h \ + /usr/include/x86_64-linux-gnu/bits/getopt_core.h \ + include/u-boot/sha1.h \ + tools/fdt_host.h \ + tools/../include/linux/libfdt.h \ + tools/../include/linux/../../scripts/dtc/libfdt/libfdt.h \ + tools/../include/linux/../../scripts/dtc/libfdt/libfdt_env.h \ + tools/../include/linux/../../scripts/dtc/libfdt/fdt.h \ + tools/../include/fdt_support.h \ + $(wildcard include/config/of/libfdt.h) \ + $(wildcard include/config/arch/fixup/fdt/memory.h) \ + $(wildcard include/config/usb/ehci/fsl.h) \ + $(wildcard include/config/usb/xhci/fsl.h) \ + $(wildcard include/config/sys/fsl/sec/compat.h) \ + $(wildcard include/config/pci.h) \ + $(wildcard include/config/sys/fdt/pad.h) \ + $(wildcard include/config/of/board/setup.h) \ + $(wildcard include/config/of/system/setup.h) \ + $(wildcard include/config/fdt/fixup/partitions.h) \ + $(wildcard include/config/fman/enet.h) \ + $(wildcard include/config/fsl/mc/enet.h) \ + tools/imagetool.h \ + /usr/lib/gcc/x86_64-linux-gnu/8/include/stdbool.h \ + include/image.h \ + $(wildcard include/config/fit/verbose.h) \ + $(wildcard include/config/fit/enable/rsassa/pss/support.h) \ + $(wildcard include/config/fit/enable/sha256/support.h) \ + $(wildcard include/config/fit.h) \ + $(wildcard include/config/spl/build.h) \ + $(wildcard include/config/spl/crc32/support.h) \ + $(wildcard include/config/spl/md5/support.h) \ + $(wildcard include/config/spl/sha1/support.h) \ + $(wildcard include/config/crc32.h) \ + $(wildcard include/config/spl/sha256/support.h) \ + $(wildcard include/config/sys/boot/get/cmdline.h) \ + $(wildcard include/config/lmb.h) \ + $(wildcard include/config/timestamp.h) \ + $(wildcard include/config/cmd/date.h) \ + $(wildcard include/config/image/format/legacy.h) \ + $(wildcard include/config/sys/boot/get/kbd.h) \ + $(wildcard include/config/fit/signature.h) \ + $(wildcard include/config/fit/best/match.h) \ + $(wildcard include/config/android/boot/image.h) \ + $(wildcard include/config/spl/fit/image/post/process.h) \ + $(wildcard include/config/fit/image/post/process.h) \ + include/compiler.h \ + /usr/include/x86_64-linux-gnu/asm/byteorder.h \ + /usr/include/linux/byteorder/little_endian.h \ + /usr/include/linux/types.h \ + /usr/include/x86_64-linux-gnu/asm/types.h \ + /usr/include/asm-generic/types.h \ + /usr/include/asm-generic/int-ll64.h \ + /usr/include/x86_64-linux-gnu/asm/bitsperlong.h \ + /usr/include/asm-generic/bitsperlong.h \ + $(wildcard include/config/64bit.h) \ + /usr/include/linux/posix_types.h \ + /usr/include/linux/stddef.h \ + /usr/include/x86_64-linux-gnu/asm/posix_types.h \ + /usr/include/x86_64-linux-gnu/asm/posix_types_64.h \ + /usr/include/asm-generic/posix_types.h \ + /usr/include/linux/swab.h \ + /usr/include/x86_64-linux-gnu/asm/swab.h \ + include/hash.h \ + include/linux/libfdt.h \ + include/fdt_support.h \ + include/u-boot/crc.h \ + include/u-boot/sha256.h \ + include/u-boot/md5.h \ + +tools/common/hash.o: $(deps_tools/common/hash.o) + +$(deps_tools/common/hash.o): diff --git a/tools/u-boot-tools/common/.image-fit.o.cmd b/tools/u-boot-tools/common/.image-fit.o.cmd new file mode 100644 index 0000000..56939d1 --- /dev/null +++ b/tools/u-boot-tools/common/.image-fit.o.cmd @@ -0,0 +1,178 @@ +cmd_tools/common/image-fit.o := cc -Wp,-MD,tools/common/.image-fit.o.d -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -std=gnu11 -include ./include/compiler.h -idirafterinclude -idirafter./arch/arm/include -I./scripts/dtc/libfdt -I./tools -DUSE_HOSTCC -D__KERNEL_STRICT_NAMES -D_GNU_SOURCE -c -o tools/common/image-fit.o tools/common/image-fit.c + +source_tools/common/image-fit.o := tools/common/image-fit.c + +deps_tools/common/image-fit.o := \ + /usr/include/stdc-predef.h \ + include/compiler.h \ + /usr/lib/gcc/x86_64-linux-gnu/8/include/stddef.h \ + /usr/lib/gcc/x86_64-linux-gnu/8/include/stdint.h \ + /usr/include/stdint.h \ + /usr/include/x86_64-linux-gnu/bits/libc-header-start.h \ + /usr/include/features.h \ + /usr/include/x86_64-linux-gnu/sys/cdefs.h \ + /usr/include/x86_64-linux-gnu/bits/wordsize.h \ + /usr/include/x86_64-linux-gnu/bits/long-double.h \ + /usr/include/x86_64-linux-gnu/gnu/stubs.h \ + /usr/include/x86_64-linux-gnu/gnu/stubs-64.h \ + /usr/include/x86_64-linux-gnu/bits/types.h \ + /usr/include/x86_64-linux-gnu/bits/typesizes.h \ + /usr/include/x86_64-linux-gnu/bits/wchar.h \ + /usr/include/x86_64-linux-gnu/bits/stdint-intn.h \ + /usr/include/x86_64-linux-gnu/bits/stdint-uintn.h \ + /usr/include/errno.h \ + /usr/include/x86_64-linux-gnu/bits/errno.h \ + /usr/include/linux/errno.h \ + /usr/include/x86_64-linux-gnu/asm/errno.h \ + /usr/include/asm-generic/errno.h \ + /usr/include/asm-generic/errno-base.h \ + /usr/include/x86_64-linux-gnu/bits/types/error_t.h \ + /usr/include/stdlib.h \ + /usr/include/x86_64-linux-gnu/bits/waitflags.h \ + /usr/include/x86_64-linux-gnu/bits/waitstatus.h \ + /usr/include/x86_64-linux-gnu/bits/floatn.h \ + /usr/include/x86_64-linux-gnu/bits/floatn-common.h \ + /usr/include/x86_64-linux-gnu/bits/types/locale_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/__locale_t.h \ + /usr/include/x86_64-linux-gnu/sys/types.h \ + /usr/include/x86_64-linux-gnu/bits/types/clock_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/clockid_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/time_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/timer_t.h \ + /usr/include/endian.h \ + /usr/include/x86_64-linux-gnu/bits/endian.h \ + /usr/include/x86_64-linux-gnu/bits/byteswap.h \ + /usr/include/x86_64-linux-gnu/bits/uintn-identity.h \ + /usr/include/x86_64-linux-gnu/sys/select.h \ + /usr/include/x86_64-linux-gnu/bits/select.h \ + /usr/include/x86_64-linux-gnu/bits/types/sigset_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/__sigset_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/struct_timeval.h \ + /usr/include/x86_64-linux-gnu/bits/types/struct_timespec.h \ + /usr/include/x86_64-linux-gnu/bits/pthreadtypes.h \ + /usr/include/x86_64-linux-gnu/bits/thread-shared-types.h \ + /usr/include/x86_64-linux-gnu/bits/pthreadtypes-arch.h \ + /usr/include/alloca.h \ + /usr/include/x86_64-linux-gnu/bits/stdlib-bsearch.h \ + /usr/include/x86_64-linux-gnu/bits/stdlib-float.h \ + /usr/include/stdio.h \ + /usr/lib/gcc/x86_64-linux-gnu/8/include/stdarg.h \ + /usr/include/x86_64-linux-gnu/bits/types/__fpos_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/__mbstate_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/__fpos64_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/__FILE.h \ + /usr/include/x86_64-linux-gnu/bits/types/FILE.h \ + /usr/include/x86_64-linux-gnu/bits/types/struct_FILE.h \ + /usr/include/x86_64-linux-gnu/bits/types/cookie_io_functions_t.h \ + /usr/include/x86_64-linux-gnu/bits/stdio_lim.h \ + /usr/include/x86_64-linux-gnu/bits/sys_errlist.h \ + /usr/include/x86_64-linux-gnu/bits/stdio.h \ + /usr/include/string.h \ + /usr/include/strings.h \ + /usr/include/x86_64-linux-gnu/sys/mman.h \ + /usr/include/x86_64-linux-gnu/bits/mman.h \ + /usr/include/x86_64-linux-gnu/bits/mman-linux.h \ + /usr/include/x86_64-linux-gnu/bits/mman-shared.h \ + /usr/include/fcntl.h \ + /usr/include/x86_64-linux-gnu/bits/fcntl.h \ + /usr/include/x86_64-linux-gnu/bits/fcntl-linux.h \ + /usr/include/x86_64-linux-gnu/bits/types/struct_iovec.h \ + /usr/include/linux/falloc.h \ + /usr/include/x86_64-linux-gnu/bits/stat.h \ + /usr/include/byteswap.h \ + /usr/include/time.h \ + /usr/include/x86_64-linux-gnu/bits/time.h \ + /usr/include/x86_64-linux-gnu/bits/timex.h \ + /usr/include/x86_64-linux-gnu/bits/types/struct_tm.h \ + /usr/include/x86_64-linux-gnu/bits/types/struct_itimerspec.h \ + tools/../common/image-fit.c \ + $(wildcard include/config/spl/build.h) \ + $(wildcard include/config/spl/fit/print.h) \ + $(wildcard include/config/arm64/support/aarch32.h) \ + $(wildcard include/config/sandbox.h) \ + $(wildcard include/config/fit/image/post/process.h) \ + $(wildcard include/config/of/libfdt/overlay.h) \ + tools/mkimage.h \ + tools/os_support.h \ + include/compiler.h \ + /usr/include/x86_64-linux-gnu/sys/stat.h \ + /usr/include/x86_64-linux-gnu/bits/statx.h \ + /usr/include/unistd.h \ + /usr/include/x86_64-linux-gnu/bits/posix_opt.h \ + /usr/include/x86_64-linux-gnu/bits/environments.h \ + /usr/include/x86_64-linux-gnu/bits/confname.h \ + /usr/include/x86_64-linux-gnu/bits/getopt_posix.h \ + /usr/include/x86_64-linux-gnu/bits/getopt_core.h \ + include/u-boot/sha1.h \ + tools/fdt_host.h \ + tools/../include/linux/libfdt.h \ + tools/../include/linux/../../scripts/dtc/libfdt/libfdt.h \ + tools/../include/linux/../../scripts/dtc/libfdt/libfdt_env.h \ + tools/../include/linux/../../scripts/dtc/libfdt/fdt.h \ + tools/../include/fdt_support.h \ + $(wildcard include/config/of/libfdt.h) \ + $(wildcard include/config/arch/fixup/fdt/memory.h) \ + $(wildcard include/config/usb/ehci/fsl.h) \ + $(wildcard include/config/usb/xhci/fsl.h) \ + $(wildcard include/config/sys/fsl/sec/compat.h) \ + $(wildcard include/config/pci.h) \ + $(wildcard include/config/sys/fdt/pad.h) \ + $(wildcard include/config/of/board/setup.h) \ + $(wildcard include/config/of/system/setup.h) \ + $(wildcard include/config/fdt/fixup/partitions.h) \ + $(wildcard include/config/fman/enet.h) \ + $(wildcard include/config/fsl/mc/enet.h) \ + tools/imagetool.h \ + /usr/lib/gcc/x86_64-linux-gnu/8/include/stdbool.h \ + include/image.h \ + $(wildcard include/config/fit/verbose.h) \ + $(wildcard include/config/fit/enable/rsassa/pss/support.h) \ + $(wildcard include/config/fit/enable/sha256/support.h) \ + $(wildcard include/config/sha1.h) \ + $(wildcard include/config/sha256.h) \ + $(wildcard include/config/fit.h) \ + $(wildcard include/config/spl/crc32/support.h) \ + $(wildcard include/config/spl/md5/support.h) \ + $(wildcard include/config/spl/sha1/support.h) \ + $(wildcard include/config/crc32.h) \ + $(wildcard include/config/spl/sha256/support.h) \ + $(wildcard include/config/sys/boot/get/cmdline.h) \ + $(wildcard include/config/lmb.h) \ + $(wildcard include/config/timestamp.h) \ + $(wildcard include/config/cmd/date.h) \ + $(wildcard include/config/image/format/legacy.h) \ + $(wildcard include/config/sys/boot/get/kbd.h) \ + $(wildcard include/config/fit/signature.h) \ + $(wildcard include/config/fit/best/match.h) \ + $(wildcard include/config/android/boot/image.h) \ + $(wildcard include/config/spl/fit/image/post/process.h) \ + include/compiler.h \ + /usr/include/x86_64-linux-gnu/asm/byteorder.h \ + /usr/include/linux/byteorder/little_endian.h \ + /usr/include/linux/types.h \ + /usr/include/x86_64-linux-gnu/asm/types.h \ + /usr/include/asm-generic/types.h \ + /usr/include/asm-generic/int-ll64.h \ + /usr/include/x86_64-linux-gnu/asm/bitsperlong.h \ + /usr/include/asm-generic/bitsperlong.h \ + $(wildcard include/config/64bit.h) \ + /usr/include/linux/posix_types.h \ + /usr/include/linux/stddef.h \ + /usr/include/x86_64-linux-gnu/asm/posix_types.h \ + /usr/include/x86_64-linux-gnu/asm/posix_types_64.h \ + /usr/include/asm-generic/posix_types.h \ + /usr/include/linux/swab.h \ + /usr/include/x86_64-linux-gnu/asm/swab.h \ + include/hash.h \ + include/linux/libfdt.h \ + include/fdt_support.h \ + include/bootstage.h \ + $(wildcard include/config/bootstage.h) \ + $(wildcard include/config/show/boot/progress.h) \ + include/u-boot/crc.h \ + include/u-boot/md5.h \ + include/u-boot/sha256.h \ + +tools/common/image-fit.o: $(deps_tools/common/image-fit.o) + +$(deps_tools/common/image-fit.o): diff --git a/tools/u-boot-tools/common/.image.o.cmd b/tools/u-boot-tools/common/.image.o.cmd new file mode 100644 index 0000000..688b4b4 --- /dev/null +++ b/tools/u-boot-tools/common/.image.o.cmd @@ -0,0 +1,189 @@ +cmd_tools/common/image.o := cc -Wp,-MD,tools/common/.image.o.d -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -std=gnu11 -include ./include/compiler.h -idirafterinclude -idirafter./arch/arm/include -I./scripts/dtc/libfdt -I./tools -DUSE_HOSTCC -D__KERNEL_STRICT_NAMES -D_GNU_SOURCE -c -o tools/common/image.o tools/common/image.c + +source_tools/common/image.o := tools/common/image.c + +deps_tools/common/image.o := \ + /usr/include/stdc-predef.h \ + include/compiler.h \ + /usr/lib/gcc/x86_64-linux-gnu/8/include/stddef.h \ + /usr/lib/gcc/x86_64-linux-gnu/8/include/stdint.h \ + /usr/include/stdint.h \ + /usr/include/x86_64-linux-gnu/bits/libc-header-start.h \ + /usr/include/features.h \ + /usr/include/x86_64-linux-gnu/sys/cdefs.h \ + /usr/include/x86_64-linux-gnu/bits/wordsize.h \ + /usr/include/x86_64-linux-gnu/bits/long-double.h \ + /usr/include/x86_64-linux-gnu/gnu/stubs.h \ + /usr/include/x86_64-linux-gnu/gnu/stubs-64.h \ + /usr/include/x86_64-linux-gnu/bits/types.h \ + /usr/include/x86_64-linux-gnu/bits/typesizes.h \ + /usr/include/x86_64-linux-gnu/bits/wchar.h \ + /usr/include/x86_64-linux-gnu/bits/stdint-intn.h \ + /usr/include/x86_64-linux-gnu/bits/stdint-uintn.h \ + /usr/include/errno.h \ + /usr/include/x86_64-linux-gnu/bits/errno.h \ + /usr/include/linux/errno.h \ + /usr/include/x86_64-linux-gnu/asm/errno.h \ + /usr/include/asm-generic/errno.h \ + /usr/include/asm-generic/errno-base.h \ + /usr/include/x86_64-linux-gnu/bits/types/error_t.h \ + /usr/include/stdlib.h \ + /usr/include/x86_64-linux-gnu/bits/waitflags.h \ + /usr/include/x86_64-linux-gnu/bits/waitstatus.h \ + /usr/include/x86_64-linux-gnu/bits/floatn.h \ + /usr/include/x86_64-linux-gnu/bits/floatn-common.h \ + /usr/include/x86_64-linux-gnu/bits/types/locale_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/__locale_t.h \ + /usr/include/x86_64-linux-gnu/sys/types.h \ + /usr/include/x86_64-linux-gnu/bits/types/clock_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/clockid_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/time_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/timer_t.h \ + /usr/include/endian.h \ + /usr/include/x86_64-linux-gnu/bits/endian.h \ + /usr/include/x86_64-linux-gnu/bits/byteswap.h \ + /usr/include/x86_64-linux-gnu/bits/uintn-identity.h \ + /usr/include/x86_64-linux-gnu/sys/select.h \ + /usr/include/x86_64-linux-gnu/bits/select.h \ + /usr/include/x86_64-linux-gnu/bits/types/sigset_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/__sigset_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/struct_timeval.h \ + /usr/include/x86_64-linux-gnu/bits/types/struct_timespec.h \ + /usr/include/x86_64-linux-gnu/bits/pthreadtypes.h \ + /usr/include/x86_64-linux-gnu/bits/thread-shared-types.h \ + /usr/include/x86_64-linux-gnu/bits/pthreadtypes-arch.h \ + /usr/include/alloca.h \ + /usr/include/x86_64-linux-gnu/bits/stdlib-bsearch.h \ + /usr/include/x86_64-linux-gnu/bits/stdlib-float.h \ + /usr/include/stdio.h \ + /usr/lib/gcc/x86_64-linux-gnu/8/include/stdarg.h \ + /usr/include/x86_64-linux-gnu/bits/types/__fpos_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/__mbstate_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/__fpos64_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/__FILE.h \ + /usr/include/x86_64-linux-gnu/bits/types/FILE.h \ + /usr/include/x86_64-linux-gnu/bits/types/struct_FILE.h \ + /usr/include/x86_64-linux-gnu/bits/types/cookie_io_functions_t.h \ + /usr/include/x86_64-linux-gnu/bits/stdio_lim.h \ + /usr/include/x86_64-linux-gnu/bits/sys_errlist.h \ + /usr/include/x86_64-linux-gnu/bits/stdio.h \ + /usr/include/string.h \ + /usr/include/strings.h \ + /usr/include/x86_64-linux-gnu/sys/mman.h \ + /usr/include/x86_64-linux-gnu/bits/mman.h \ + /usr/include/x86_64-linux-gnu/bits/mman-linux.h \ + /usr/include/x86_64-linux-gnu/bits/mman-shared.h \ + /usr/include/fcntl.h \ + /usr/include/x86_64-linux-gnu/bits/fcntl.h \ + /usr/include/x86_64-linux-gnu/bits/fcntl-linux.h \ + /usr/include/x86_64-linux-gnu/bits/types/struct_iovec.h \ + /usr/include/linux/falloc.h \ + /usr/include/x86_64-linux-gnu/bits/stat.h \ + /usr/include/byteswap.h \ + /usr/include/time.h \ + /usr/include/x86_64-linux-gnu/bits/time.h \ + /usr/include/x86_64-linux-gnu/bits/timex.h \ + /usr/include/x86_64-linux-gnu/bits/types/struct_tm.h \ + /usr/include/x86_64-linux-gnu/bits/types/struct_itimerspec.h \ + tools/../common/image.c \ + $(wildcard include/config/show/boot/progress.h) \ + $(wildcard include/config/cmd/bdi.h) \ + $(wildcard include/config/image/format/legacy.h) \ + $(wildcard include/config/sys/bargsize.h) \ + $(wildcard include/config/lynxkdi.h) \ + $(wildcard include/config/cmd/elf.h) \ + $(wildcard include/config/integrity.h) \ + $(wildcard include/config/bootm/openrtos.h) \ + $(wildcard include/config/sys/load/addr.h) \ + $(wildcard include/config/sys/sdram/base.h) \ + $(wildcard include/config/arm.h) \ + $(wildcard include/config/nr/dram/banks.h) \ + $(wildcard include/config/sys/bootmapsz.h) \ + $(wildcard include/config/hw/watchdog.h) \ + $(wildcard include/config/watchdog.h) \ + $(wildcard include/config/needs/manual/reloc.h) \ + $(wildcard include/config/fit.h) \ + $(wildcard include/config/android/boot/image.h) \ + $(wildcard include/config/support/raw/initrd.h) \ + $(wildcard include/config/sys/boot/ramdisk/high.h) \ + $(wildcard include/config/mp.h) \ + $(wildcard include/config/fpga.h) \ + $(wildcard include/config/sys/boot/get/cmdline.h) \ + $(wildcard include/config/sys/boot/get/kbd.h) \ + $(wildcard include/config/lmb.h) \ + tools/mkimage.h \ + tools/os_support.h \ + include/compiler.h \ + /usr/include/x86_64-linux-gnu/sys/stat.h \ + /usr/include/x86_64-linux-gnu/bits/statx.h \ + /usr/include/unistd.h \ + /usr/include/x86_64-linux-gnu/bits/posix_opt.h \ + /usr/include/x86_64-linux-gnu/bits/environments.h \ + /usr/include/x86_64-linux-gnu/bits/confname.h \ + /usr/include/x86_64-linux-gnu/bits/getopt_posix.h \ + /usr/include/x86_64-linux-gnu/bits/getopt_core.h \ + include/u-boot/sha1.h \ + tools/fdt_host.h \ + tools/../include/linux/libfdt.h \ + tools/../include/linux/../../scripts/dtc/libfdt/libfdt.h \ + tools/../include/linux/../../scripts/dtc/libfdt/libfdt_env.h \ + tools/../include/linux/../../scripts/dtc/libfdt/fdt.h \ + tools/../include/fdt_support.h \ + $(wildcard include/config/of/libfdt.h) \ + $(wildcard include/config/arch/fixup/fdt/memory.h) \ + $(wildcard include/config/usb/ehci/fsl.h) \ + $(wildcard include/config/usb/xhci/fsl.h) \ + $(wildcard include/config/sys/fsl/sec/compat.h) \ + $(wildcard include/config/pci.h) \ + $(wildcard include/config/sys/fdt/pad.h) \ + $(wildcard include/config/of/board/setup.h) \ + $(wildcard include/config/of/system/setup.h) \ + $(wildcard include/config/fdt/fixup/partitions.h) \ + $(wildcard include/config/fman/enet.h) \ + $(wildcard include/config/fsl/mc/enet.h) \ + tools/imagetool.h \ + /usr/lib/gcc/x86_64-linux-gnu/8/include/stdbool.h \ + include/u-boot/md5.h \ + include/image.h \ + $(wildcard include/config/fit/verbose.h) \ + $(wildcard include/config/fit/enable/rsassa/pss/support.h) \ + $(wildcard include/config/fit/enable/sha256/support.h) \ + $(wildcard include/config/sha1.h) \ + $(wildcard include/config/sha256.h) \ + $(wildcard include/config/spl/build.h) \ + $(wildcard include/config/spl/crc32/support.h) \ + $(wildcard include/config/spl/md5/support.h) \ + $(wildcard include/config/spl/sha1/support.h) \ + $(wildcard include/config/crc32.h) \ + $(wildcard include/config/spl/sha256/support.h) \ + $(wildcard include/config/timestamp.h) \ + $(wildcard include/config/cmd/date.h) \ + $(wildcard include/config/fit/signature.h) \ + $(wildcard include/config/fit/best/match.h) \ + $(wildcard include/config/spl/fit/image/post/process.h) \ + $(wildcard include/config/fit/image/post/process.h) \ + include/compiler.h \ + /usr/include/x86_64-linux-gnu/asm/byteorder.h \ + /usr/include/linux/byteorder/little_endian.h \ + /usr/include/linux/types.h \ + /usr/include/x86_64-linux-gnu/asm/types.h \ + /usr/include/asm-generic/types.h \ + /usr/include/asm-generic/int-ll64.h \ + /usr/include/x86_64-linux-gnu/asm/bitsperlong.h \ + /usr/include/asm-generic/bitsperlong.h \ + $(wildcard include/config/64bit.h) \ + /usr/include/linux/posix_types.h \ + /usr/include/linux/stddef.h \ + /usr/include/x86_64-linux-gnu/asm/posix_types.h \ + /usr/include/x86_64-linux-gnu/asm/posix_types_64.h \ + /usr/include/asm-generic/posix_types.h \ + /usr/include/linux/swab.h \ + /usr/include/x86_64-linux-gnu/asm/swab.h \ + include/hash.h \ + include/linux/libfdt.h \ + include/fdt_support.h \ + include/u-boot/crc.h \ + +tools/common/image.o: $(deps_tools/common/image.o) + +$(deps_tools/common/image.o): diff --git a/tools/u-boot-tools/common/bootm.c b/tools/u-boot-tools/common/bootm.c new file mode 100644 index 0000000..2f2a1ca --- /dev/null +++ b/tools/u-boot-tools/common/bootm.c @@ -0,0 +1 @@ +#include <../common/bootm.c> diff --git a/tools/u-boot-tools/common/bootm.o b/tools/u-boot-tools/common/bootm.o new file mode 100644 index 0000000000000000000000000000000000000000..1d1edae03ad80230fe4c163fba0b8eed975a3139 GIT binary patch literal 4448 zcmb<-^>JfjWMqH=Mg}_u1P><4z_5T9!FB*M9T@l-_!u0+eLK%PhWU0La}0It{L8=X zr(;N{N9WaGkIv5?&2Kn-x>HmFJUdNPUVt>Yw!Y<W?POqJ@UVPclH$>M$fMU3B<|5` z3!)4UfK_$7sCal-yQnzuH_Zj9?G90K@ag>L)A=1N?ZF(Q;sDj**(n1t*`xUghv9+f zSjQO0SjRZW_{0424Im?N+3|X38o#^?1A|BBF{n{c%UwF(p$dRZ-tqVU|Nlsar|I#_ zw}6aiF#Hd36T}0U?B*jJJN~0-M(9qP@DgN78bZV|+;Jxp0|SF&m}97`;akTL&(1%g z!5+P`Aax#%Z!{Pg7(9AiKmm}WlHk$Fq5|T1`1IDO1bB41sBnO|b|97nh-D9ADS%iJ zAeM&Vft?bdAnknbYIxwY$7;h%KAq1zI^TO7cToXH>+u#95k>}Z5^xOvU!o$A#gO&i zquWPC0phpjBMOe4$662YcT518(;cEB(Rs|L^Ep_y^#FhC4ydq#OXoLa@tq6|3_jf@ zDgj=-Hd`4O1Q@_NJi0?vJV5Eh@EiZO8Wnf`Z9Xb)|4UR1JUb6$FfcSQcy?ZN3<Ejc z0HVJ6h=Nadh>CzC$kyv%G1t~_CBlx)|NfTny0*S8<#25N_s^Bz^_@raVMdQ$R&c;s z9xmZ^;orvn^7p_0|M}$|7(5z}fP&Jc^9540LL(k(>f6%2j(cDI|NsB>Ry2u{=dTwj zC@6&HCFkcB6r~myXXd3Vs1_^e>FFsj_?8xzC<M6%yM`zv<tG-UD3s(Y6s0ETm!%dl z_<M6P_~a+1WagzaC@6rmz;s1;1~51$=Bbw`q^FiBWacKOrz(_G7Njb2VF<%5&CgTh z0{ISvL-R6o3vyC(Q}aqvQxuStfK5?QO@XTPgqWP4uaJ{il%8s(keQcUl$uzas^IMJ z=jQ1i9~>DR@8s_v;v4Vd>c_yu!OY<9>};i=;gXt^nV6?wp=YdTplhZHVK6X=fGlTV zU|_5YVqmNgV3g)z=a|6Az)-=!z#s#aGX@nXPJ9CWOs;$qeax<W3cV~Id>TEhj(i4f zY%Y8j&Ft=c4oo>*d=}1p29A6hPJ9YZd=gH40#1A!9^6cBTznR87!uyx$=HM$7#IW? z7#L<i?FL6b0|P?|0|UbvsMt!ldptnyaRRx=733Zlkb4|K?(yS$z{Hfv_kziV?*o$) z-w!4yz6NHnU?|@UrU1SVOdfncm|S6kK3sejE*LI!<c3;^g#tN&h2i6WEK&^2;OvJf z!_2^pDagRUz|6pcLmcLNRB2`gHdFxy1_n-095S%O#gT1dMiPfB0NV@Fg)9PLGc!OF z8-xocrNAUBgC&?o5b|IqD}y7LMi44sCMyHDdH}N_geI6`W$*{n2tp6c#PqK*SRB*8 z7Eo~wFpVI<^$-IqgCs%-%yI>PH_m8bP>&nXC+1U>cQRX5c^-02P4@3=Bd@@e9ry z3=9nM&~%Hgp%jOB7Y^}>IK*e-5TA!bd^6OY4xog}z`y_t*?my)0H`=PFEKDMT*YC| z6CC2-afov<Lh>(qI0)hpXGqG=FUi$QW&jtN@t`8JSkKJZ(tttF$JrD_n1Kir5Md4? zz!HWamJx_B1`!q@!V+Rsd`c>)^o$3a#}M!C<L~6?6YuZl7VH`lAL8ib;~LM9o|>1L zn;xH@S^~B(J})shm7$<0Gp{6#AvZNQH@_^Ep`f&+7+E3MP_RO%ium#rsErx<#U=4M z`H3kION$xOGD{##uqYB2W+R#q$ZUq(#GIV`WQMe&)Kmrr1}$g+f=UKZjsFMK2>Jj2 zKO`M9Fo4<^pyCN8?g>>766axHV1S7SK*d4kfbs-PJReCMl(%8x)lhMeIiMyZOdQf$ zVgQ#^(n#hnLsHL&B)%3&9N9fvpyKH6IR+I6*~^b)&RM89y87!-agcgYeF1alJ*YUk zdS+;P2Z@8~HJEx(dIf0#nIni~FF%rcWdDjm#X;(Ykkm^<#X;sHtIvdrgVcjc6PUfP zq2eI*pfVID&IwY;010OWB=Z%K#1)amt&zl)ki@f*#Kn-r>ygBjk;E54#X%IvozUd= z{{l#W0g~Q?k<?#>ii6yb?BADAaS#P+TfqGF4kQ5e7qWUsq;Nn^hakVh;svS|#ATQR zCL!(tiNngb4QS%9@{K{SxH7jSF^NI1xTFX|XTVrRsX2*yC8-r940<VvC5a4rNyWts zdPVsl4oJD7o*^pN0>)*~OU};)wbdB(^72bk_1yhJb&E?9ld~E0z=oz~#HSU33KgiU z$frQz016`X8W7yrXJBCX2?_)x<3V`=iOT>AYfv1))WhU%z^Z;|LjjaWLH2;+9;60T z=74B5aJvH3Mgj2<*b=G_Ap&86%z&^Uqz(fExE4iMPON@IsQpF|<KQGre+OIy!hqQi zqe1=#vC;LPfI27~q76>M{14It5(KqVKy0*NW{?L3D!2^=QiZ@EwFn*q1IT=km;tgN z0|SE+R6n?1h$IJ63*y4)1gQT(V#wGYsvlVl#D?huu|XKr{s4)gyWat-UjZZuQV7K` z|3kS91<*VT3P+e2h`s?zFrcm?h{M3Z0OEpLjWGSN^aC;;-ENSU1ZaaK0U8gWx&~BE uq3Z`FQ>YS<HaI>2RR}AOVG<zwU~CW#D$}v)R{&Yaz`#%sQiO!j^#cGwfGW}e literal 0 HcmV?d00001 diff --git a/tools/u-boot-tools/common/hash.c b/tools/u-boot-tools/common/hash.c new file mode 100644 index 0000000..de5b81f --- /dev/null +++ b/tools/u-boot-tools/common/hash.c @@ -0,0 +1 @@ +#include <../common/hash.c> diff --git a/tools/u-boot-tools/common/hash.o b/tools/u-boot-tools/common/hash.o new file mode 100644 index 0000000000000000000000000000000000000000..f8803dbd390cc58beda6278ea0616239a92df7af GIT binary patch literal 5392 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 Z!UU6OTyQ%GoBkWnj8YEPg&@%N0|1%U1S0?d literal 0 HcmV?d00001 diff --git a/tools/u-boot-tools/common/image-fit.c b/tools/u-boot-tools/common/image-fit.c new file mode 100644 index 0000000..ae738f3 --- /dev/null +++ b/tools/u-boot-tools/common/image-fit.c @@ -0,0 +1 @@ +#include <../common/image-fit.c> diff --git a/tools/u-boot-tools/common/image-fit.o b/tools/u-boot-tools/common/image-fit.o new file mode 100644 index 0000000000000000000000000000000000000000..4d42b812f29ba7ad4506241832a5761f0ceb8626 GIT binary patch literal 31696 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 literal 0 HcmV?d00001 diff --git a/tools/u-boot-tools/common/image.c b/tools/u-boot-tools/common/image.c new file mode 100644 index 0000000..3aef160 --- /dev/null +++ b/tools/u-boot-tools/common/image.c @@ -0,0 +1 @@ +#include <../common/image.c> diff --git a/tools/u-boot-tools/common/image.o b/tools/u-boot-tools/common/image.o new file mode 100644 index 0000000000000000000000000000000000000000..9c4fa0741241645611a4d27786a355db863db74c GIT binary patch literal 21168 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 PVJBprI9Lcwpz8+!rcIp& literal 0 HcmV?d00001 diff --git a/tools/u-boot-tools/concurrencytest/.gitignore b/tools/u-boot-tools/concurrencytest/.gitignore new file mode 100644 index 0000000..0d20b64 --- /dev/null +++ b/tools/u-boot-tools/concurrencytest/.gitignore @@ -0,0 +1 @@ +*.pyc diff --git a/tools/u-boot-tools/concurrencytest/README.md b/tools/u-boot-tools/concurrencytest/README.md new file mode 100644 index 0000000..8e65776 --- /dev/null +++ b/tools/u-boot-tools/concurrencytest/README.md @@ -0,0 +1,74 @@ +concurrencytest +=============== + + + +Python testtools extension for running unittest suites concurrently. + +---- + +Install from PyPI: +``` +pip install concurrencytest +``` + +---- + +Requires: + + * [testtools](https://pypi.python.org/pypi/testtools) : `pip install testtools` + * [python-subunit](https://pypi.python.org/pypi/python-subunit) : `pip install python-subunit` + +---- + +Example: + +```python +import time +import unittest + +from concurrencytest import ConcurrentTestSuite, fork_for_tests + + +class SampleTestCase(unittest.TestCase): + """Dummy tests that sleep for demo.""" + + def test_me_1(self): + time.sleep(0.5) + + def test_me_2(self): + time.sleep(0.5) + + def test_me_3(self): + time.sleep(0.5) + + def test_me_4(self): + time.sleep(0.5) + + +# Load tests from SampleTestCase defined above +suite = unittest.TestLoader().loadTestsFromTestCase(SampleTestCase) +runner = unittest.TextTestRunner() + +# Run tests sequentially +runner.run(suite) + +# Run same tests across 4 processes +suite = unittest.TestLoader().loadTestsFromTestCase(SampleTestCase) +concurrent_suite = ConcurrentTestSuite(suite, fork_for_tests(4)) +runner.run(concurrent_suite) +``` +Output: + +``` +.... +---------------------------------------------------------------------- +Ran 4 tests in 2.003s + +OK +.... +---------------------------------------------------------------------- +Ran 4 tests in 0.504s + +OK +``` diff --git a/tools/u-boot-tools/concurrencytest/concurrencytest.py b/tools/u-boot-tools/concurrencytest/concurrencytest.py new file mode 100644 index 0000000..418d7ee --- /dev/null +++ b/tools/u-boot-tools/concurrencytest/concurrencytest.py @@ -0,0 +1,144 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: GPL-2.0+ +# +# Modified by: Corey Goldberg, 2013 +# +# Original code from: +# Bazaar (bzrlib.tests.__init__.py, v2.6, copied Jun 01 2013) +# Copyright (C) 2005-2011 Canonical Ltd + +"""Python testtools extension for running unittest suites concurrently. + +The `testtools` project provides a ConcurrentTestSuite class, but does +not provide a `make_tests` implementation needed to use it. + +This allows you to parallelize a test run across a configurable number +of worker processes. While this can speed up CPU-bound test runs, it is +mainly useful for IO-bound tests that spend most of their time waiting for +data to arrive from someplace else and can benefit from cocncurrency. + +Unix only. +""" + +import os +import sys +import traceback +import unittest +from itertools import cycle +from multiprocessing import cpu_count + +from subunit import ProtocolTestCase, TestProtocolClient +from subunit.test_results import AutoTimingTestResultDecorator + +from testtools import ConcurrentTestSuite, iterate_tests + + +_all__ = [ + 'ConcurrentTestSuite', + 'fork_for_tests', + 'partition_tests', +] + + +CPU_COUNT = cpu_count() + + +def fork_for_tests(concurrency_num=CPU_COUNT): + """Implementation of `make_tests` used to construct `ConcurrentTestSuite`. + + :param concurrency_num: number of processes to use. + """ + def do_fork(suite): + """Take suite and start up multiple runners by forking (Unix only). + + :param suite: TestSuite object. + + :return: An iterable of TestCase-like objects which can each have + run(result) called on them to feed tests to result. + """ + result = [] + test_blocks = partition_tests(suite, concurrency_num) + # Clear the tests from the original suite so it doesn't keep them alive + suite._tests[:] = [] + for process_tests in test_blocks: + process_suite = unittest.TestSuite(process_tests) + # Also clear each split list so new suite has only reference + process_tests[:] = [] + c2pread, c2pwrite = os.pipe() + pid = os.fork() + if pid == 0: + try: + stream = os.fdopen(c2pwrite, 'wb', 1) + os.close(c2pread) + # Leave stderr and stdout open so we can see test noise + # Close stdin so that the child goes away if it decides to + # read from stdin (otherwise its a roulette to see what + # child actually gets keystrokes for pdb etc). + sys.stdin.close() + subunit_result = AutoTimingTestResultDecorator( + TestProtocolClient(stream) + ) + process_suite.run(subunit_result) + except: + # Try and report traceback on stream, but exit with error + # even if stream couldn't be created or something else + # goes wrong. The traceback is formatted to a string and + # written in one go to avoid interleaving lines from + # multiple failing children. + try: + stream.write(traceback.format_exc()) + finally: + os._exit(1) + os._exit(0) + else: + os.close(c2pwrite) + stream = os.fdopen(c2pread, 'rb', 1) + test = ProtocolTestCase(stream) + result.append(test) + return result + return do_fork + + +def partition_tests(suite, count): + """Partition suite into count lists of tests.""" + # This just assigns tests in a round-robin fashion. On one hand this + # splits up blocks of related tests that might run faster if they shared + # resources, but on the other it avoids assigning blocks of slow tests to + # just one partition. So the slowest partition shouldn't be much slower + # than the fastest. + partitions = [list() for _ in range(count)] + tests = iterate_tests(suite) + for partition, test in zip(cycle(partitions), tests): + partition.append(test) + return partitions + + +if __name__ == '__main__': + import time + + class SampleTestCase(unittest.TestCase): + """Dummy tests that sleep for demo.""" + + def test_me_1(self): + time.sleep(0.5) + + def test_me_2(self): + time.sleep(0.5) + + def test_me_3(self): + time.sleep(0.5) + + def test_me_4(self): + time.sleep(0.5) + + # Load tests from SampleTestCase defined above + suite = unittest.TestLoader().loadTestsFromTestCase(SampleTestCase) + runner = unittest.TextTestRunner() + + # Run tests sequentially + runner.run(suite) + + # Run same tests across 4 processes + suite = unittest.TestLoader().loadTestsFromTestCase(SampleTestCase) + concurrent_suite = ConcurrentTestSuite(suite, fork_for_tests(4)) + runner.run(concurrent_suite) diff --git a/tools/u-boot-tools/default_image.c b/tools/u-boot-tools/default_image.c new file mode 100644 index 0000000..4b7d1ed --- /dev/null +++ b/tools/u-boot-tools/default_image.c @@ -0,0 +1,184 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2008 Semihalf + * + * (C) Copyright 2000-2004 + * DENX Software Engineering + * Wolfgang Denk, wd@denx.de + * + * Updated-by: Prafulla Wadaskar <prafulla@marvell.com> + * default_image specific code abstracted from mkimage.c + * some functions added to address abstraction + * + * All rights reserved. + */ + +#include "imagetool.h" +#include "mkimage.h" + +#include <image.h> +#include <tee/optee.h> +#include <u-boot/crc.h> + +static image_header_t header; + +static int image_check_image_types(uint8_t type) +{ + if (((type > IH_TYPE_INVALID) && (type < IH_TYPE_FLATDT)) || + (type == IH_TYPE_KERNEL_NOLOAD) || (type == IH_TYPE_FIRMWARE_IVT)) + return EXIT_SUCCESS; + else + return EXIT_FAILURE; +} + +static int image_check_params(struct image_tool_params *params) +{ + return ((params->dflag && (params->fflag || params->lflag)) || + (params->fflag && (params->dflag || params->lflag)) || + (params->lflag && (params->dflag || params->fflag))); +} + +static int image_verify_header(unsigned char *ptr, int image_size, + struct image_tool_params *params) +{ + uint32_t len; + const unsigned char *data; + uint32_t checksum; + image_header_t header; + image_header_t *hdr = &header; + + /* + * create copy of header so that we can blank out the + * checksum field for checking - this can't be done + * on the PROT_READ mapped data. + */ + memcpy(hdr, ptr, sizeof(image_header_t)); + + if (be32_to_cpu(hdr->ih_magic) != IH_MAGIC) { + debug("%s: Bad Magic Number: \"%s\" is no valid image\n", + params->cmdname, params->imagefile); + return -FDT_ERR_BADMAGIC; + } + + data = (const unsigned char *)hdr; + len = sizeof(image_header_t); + + checksum = be32_to_cpu(hdr->ih_hcrc); + hdr->ih_hcrc = cpu_to_be32(0); /* clear for re-calculation */ + + if (crc32(0, data, len) != checksum) { + debug("%s: ERROR: \"%s\" has bad header checksum!\n", + params->cmdname, params->imagefile); + return -FDT_ERR_BADSTATE; + } + + data = (const unsigned char *)ptr + sizeof(image_header_t); + len = image_size - sizeof(image_header_t) ; + + checksum = be32_to_cpu(hdr->ih_dcrc); + if (crc32(0, data, len) != checksum) { + debug("%s: ERROR: \"%s\" has corrupted data!\n", + params->cmdname, params->imagefile); + return -FDT_ERR_BADSTRUCTURE; + } + return 0; +} + +static void image_set_header(void *ptr, struct stat *sbuf, int ifd, + struct image_tool_params *params) +{ + uint32_t checksum; + time_t time; + uint32_t imagesize; + uint32_t ep; + uint32_t addr; + + image_header_t * hdr = (image_header_t *)ptr; + + checksum = crc32(0, + (const unsigned char *)(ptr + + sizeof(image_header_t)), + sbuf->st_size - sizeof(image_header_t)); + + time = imagetool_get_source_date(params->cmdname, sbuf->st_mtime); + ep = params->ep; + addr = params->addr; + + if (params->type == IH_TYPE_FIRMWARE_IVT) + /* Add size of CSF minus IVT */ + imagesize = sbuf->st_size - sizeof(image_header_t) + 0x1FE0; + else + imagesize = sbuf->st_size - sizeof(image_header_t); + + if (params->os == IH_OS_TEE) { + addr = optee_image_get_load_addr(hdr); + ep = optee_image_get_entry_point(hdr); + } + + /* Build new header */ + image_set_magic(hdr, IH_MAGIC); + image_set_time(hdr, time); + image_set_size(hdr, imagesize); + image_set_load(hdr, addr); + image_set_ep(hdr, ep); + image_set_dcrc(hdr, checksum); + image_set_os(hdr, params->os); + image_set_arch(hdr, params->arch); + image_set_type(hdr, params->type); + image_set_comp(hdr, params->comp); + + image_set_name(hdr, params->imagename); + + checksum = crc32(0, (const unsigned char *)hdr, + sizeof(image_header_t)); + + image_set_hcrc(hdr, checksum); +} + +static int image_extract_subimage(void *ptr, struct image_tool_params *params) +{ + const image_header_t *hdr = (const image_header_t *)ptr; + ulong file_data; + ulong file_len; + + if (image_check_type(hdr, IH_TYPE_MULTI)) { + ulong idx = params->pflag; + ulong count; + + /* get the number of data files present in the image */ + count = image_multi_count(hdr); + + /* retrieve the "data file" at the idx position */ + image_multi_getimg(hdr, idx, &file_data, &file_len); + + if ((file_len == 0) || (idx >= count)) { + fprintf(stderr, "%s: No such data file %ld in \"%s\"\n", + params->cmdname, idx, params->imagefile); + return -1; + } + } else { + file_data = image_get_data(hdr); + file_len = image_get_size(hdr); + } + + /* save the "data file" into the file system */ + return imagetool_save_subimage(params->outfile, file_data, file_len); +} + +/* + * Default image type parameters definition + */ +U_BOOT_IMAGE_TYPE( + defimage, + "Default Image support", + sizeof(image_header_t), + (void *)&header, + image_check_params, + image_verify_header, + image_print_contents, + image_set_header, + image_extract_subimage, + image_check_image_types, + NULL, + NULL +); diff --git a/tools/u-boot-tools/default_image.o b/tools/u-boot-tools/default_image.o new file mode 100644 index 0000000000000000000000000000000000000000..d9aced3057fce265c044cc9f6d9094a708931a8a GIT binary patch literal 4192 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 literal 0 HcmV?d00001 diff --git a/tools/u-boot-tools/dtoc/.gitignore b/tools/u-boot-tools/dtoc/.gitignore new file mode 100644 index 0000000..0d20b64 --- /dev/null +++ b/tools/u-boot-tools/dtoc/.gitignore @@ -0,0 +1 @@ +*.pyc diff --git a/tools/u-boot-tools/dtoc/dtb_platdata.py b/tools/u-boot-tools/dtoc/dtb_platdata.py new file mode 100644 index 0000000..6cb1259 --- /dev/null +++ b/tools/u-boot-tools/dtoc/dtb_platdata.py @@ -0,0 +1,576 @@ +#!/usr/bin/python +# SPDX-License-Identifier: GPL-2.0+ +# +# Copyright (C) 2017 Google, Inc +# Written by Simon Glass <sjg@chromium.org> +# + +"""Device tree to platform data class + +This supports converting device tree data to C structures definitions and +static data. +""" + +import collections +import copy +import sys + +import fdt +import fdt_util + +# When we see these properties we ignore them - i.e. do not create a structure member +PROP_IGNORE_LIST = [ + '#address-cells', + '#gpio-cells', + '#size-cells', + 'compatible', + 'linux,phandle', + "status", + 'phandle', + 'u-boot,dm-pre-reloc', + 'u-boot,dm-tpl', + 'u-boot,dm-spl', +] + +# C type declarations for the tyues we support +TYPE_NAMES = { + fdt.TYPE_INT: 'fdt32_t', + fdt.TYPE_BYTE: 'unsigned char', + fdt.TYPE_STRING: 'const char *', + fdt.TYPE_BOOL: 'bool', + fdt.TYPE_INT64: 'fdt64_t', +} + +STRUCT_PREFIX = 'dtd_' +VAL_PREFIX = 'dtv_' + +# This holds information about a property which includes phandles. +# +# max_args: integer: Maximum number or arguments that any phandle uses (int). +# args: Number of args for each phandle in the property. The total number of +# phandles is len(args). This is a list of integers. +PhandleInfo = collections.namedtuple('PhandleInfo', ['max_args', 'args']) + + +def conv_name_to_c(name): + """Convert a device-tree name to a C identifier + + This uses multiple replace() calls instead of re.sub() since it is faster + (400ms for 1m calls versus 1000ms for the 're' version). + + Args: + name: Name to convert + Return: + String containing the C version of this name + """ + new = name.replace('@', '_at_') + new = new.replace('-', '_') + new = new.replace(',', '_') + new = new.replace('.', '_') + return new + +def tab_to(num_tabs, line): + """Append tabs to a line of text to reach a tab stop. + + Args: + num_tabs: Tab stop to obtain (0 = column 0, 1 = column 8, etc.) + line: Line of text to append to + + Returns: + line with the correct number of tabs appeneded. If the line already + extends past that tab stop then a single space is appended. + """ + if len(line) >= num_tabs * 8: + return line + ' ' + return line + '\t' * (num_tabs - len(line) // 8) + +def get_value(ftype, value): + """Get a value as a C expression + + For integers this returns a byte-swapped (little-endian) hex string + For bytes this returns a hex string, e.g. 0x12 + For strings this returns a literal string enclosed in quotes + For booleans this return 'true' + + Args: + type: Data type (fdt_util) + value: Data value, as a string of bytes + """ + if ftype == fdt.TYPE_INT: + return '%#x' % fdt_util.fdt32_to_cpu(value) + elif ftype == fdt.TYPE_BYTE: + return '%#x' % ord(value[0]) + elif ftype == fdt.TYPE_STRING: + return '"%s"' % value + elif ftype == fdt.TYPE_BOOL: + return 'true' + elif ftype == fdt.TYPE_INT64: + return '%#x' % value + +def get_compat_name(node): + """Get a node's first compatible string as a C identifier + + Args: + node: Node object to check + Return: + Tuple: + C identifier for the first compatible string + List of C identifiers for all the other compatible strings + (possibly empty) + """ + compat = node.props['compatible'].value + aliases = [] + if isinstance(compat, list): + compat, aliases = compat[0], compat[1:] + return conv_name_to_c(compat), [conv_name_to_c(a) for a in aliases] + + +class DtbPlatdata(object): + """Provide a means to convert device tree binary data to platform data + + The output of this process is C structures which can be used in space- + constrained encvironments where the ~3KB code overhead of device tree + code is not affordable. + + Properties: + _fdt: Fdt object, referencing the device tree + _dtb_fname: Filename of the input device tree binary file + _valid_nodes: A list of Node object with compatible strings + _include_disabled: true to include nodes marked status = "disabled" + _outfile: The current output file (sys.stdout or a real file) + _lines: Stashed list of output lines for outputting in the future + """ + def __init__(self, dtb_fname, include_disabled): + self._fdt = None + self._dtb_fname = dtb_fname + self._valid_nodes = None + self._include_disabled = include_disabled + self._outfile = None + self._lines = [] + self._aliases = {} + + def setup_output(self, fname): + """Set up the output destination + + Once this is done, future calls to self.out() will output to this + file. + + Args: + fname: Filename to send output to, or '-' for stdout + """ + if fname == '-': + self._outfile = sys.stdout + else: + self._outfile = open(fname, 'w') + + def out(self, line): + """Output a string to the output file + + Args: + line: String to output + """ + self._outfile.write(line) + + def buf(self, line): + """Buffer up a string to send later + + Args: + line: String to add to our 'buffer' list + """ + self._lines.append(line) + + def get_buf(self): + """Get the contents of the output buffer, and clear it + + Returns: + The output buffer, which is then cleared for future use + """ + lines = self._lines + self._lines = [] + return lines + + def out_header(self): + """Output a message indicating that this is an auto-generated file""" + self.out('''/* + * DO NOT MODIFY + * + * This file was generated by dtoc from a .dtb (device tree binary) file. + */ + +''') + + def get_phandle_argc(self, prop, node_name): + """Check if a node contains phandles + + We have no reliable way of detecting whether a node uses a phandle + or not. As an interim measure, use a list of known property names. + + Args: + prop: Prop object to check + Return: + Number of argument cells is this is a phandle, else None + """ + if prop.name in ['clocks']: + if not isinstance(prop.value, list): + prop.value = [prop.value] + val = prop.value + i = 0 + + max_args = 0 + args = [] + while i < len(val): + phandle = fdt_util.fdt32_to_cpu(val[i]) + # If we get to the end of the list, stop. This can happen + # since some nodes have more phandles in the list than others, + # but we allocate enough space for the largest list. So those + # nodes with shorter lists end up with zeroes at the end. + if not phandle: + break + target = self._fdt.phandle_to_node.get(phandle) + if not target: + raise ValueError("Cannot parse '%s' in node '%s'" % + (prop.name, node_name)) + prop_name = '#clock-cells' + cells = target.props.get(prop_name) + if not cells: + raise ValueError("Node '%s' has no '%s' property" % + (target.name, prop_name)) + num_args = fdt_util.fdt32_to_cpu(cells.value) + max_args = max(max_args, num_args) + args.append(num_args) + i += 1 + num_args + return PhandleInfo(max_args, args) + return None + + def scan_dtb(self): + """Scan the device tree to obtain a tree of nodes and properties + + Once this is done, self._fdt.GetRoot() can be called to obtain the + device tree root node, and progress from there. + """ + self._fdt = fdt.FdtScan(self._dtb_fname) + + def scan_node(self, root): + """Scan a node and subnodes to build a tree of node and phandle info + + This adds each node to self._valid_nodes. + + Args: + root: Root node for scan + """ + for node in root.subnodes: + if 'compatible' in node.props: + status = node.props.get('status') + if (not self._include_disabled and not status or + status.value != 'disabled'): + self._valid_nodes.append(node) + + # recurse to handle any subnodes + self.scan_node(node) + + def scan_tree(self): + """Scan the device tree for useful information + + This fills in the following properties: + _valid_nodes: A list of nodes we wish to consider include in the + platform data + """ + self._valid_nodes = [] + return self.scan_node(self._fdt.GetRoot()) + + @staticmethod + def get_num_cells(node): + """Get the number of cells in addresses and sizes for this node + + Args: + node: Node to check + + Returns: + Tuple: + Number of address cells for this node + Number of size cells for this node + """ + parent = node.parent + na, ns = 2, 2 + if parent: + na_prop = parent.props.get('#address-cells') + ns_prop = parent.props.get('#size-cells') + if na_prop: + na = fdt_util.fdt32_to_cpu(na_prop.value) + if ns_prop: + ns = fdt_util.fdt32_to_cpu(ns_prop.value) + return na, ns + + def scan_reg_sizes(self): + """Scan for 64-bit 'reg' properties and update the values + + This finds 'reg' properties with 64-bit data and converts the value to + an array of 64-values. This allows it to be output in a way that the + C code can read. + """ + for node in self._valid_nodes: + reg = node.props.get('reg') + if not reg: + continue + na, ns = self.get_num_cells(node) + total = na + ns + + if reg.type != fdt.TYPE_INT: + raise ValueError("Node '%s' reg property is not an int" % + node.name) + if len(reg.value) % total: + raise ValueError("Node '%s' reg property has %d cells " + 'which is not a multiple of na + ns = %d + %d)' % + (node.name, len(reg.value), na, ns)) + reg.na = na + reg.ns = ns + if na != 1 or ns != 1: + reg.type = fdt.TYPE_INT64 + i = 0 + new_value = [] + val = reg.value + if not isinstance(val, list): + val = [val] + while i < len(val): + addr = fdt_util.fdt_cells_to_cpu(val[i:], reg.na) + i += na + size = fdt_util.fdt_cells_to_cpu(val[i:], reg.ns) + i += ns + new_value += [addr, size] + reg.value = new_value + + def scan_structs(self): + """Scan the device tree building up the C structures we will use. + + Build a dict keyed by C struct name containing a dict of Prop + object for each struct field (keyed by property name). Where the + same struct appears multiple times, try to use the 'widest' + property, i.e. the one with a type which can express all others. + + Once the widest property is determined, all other properties are + updated to match that width. + """ + structs = {} + for node in self._valid_nodes: + node_name, _ = get_compat_name(node) + fields = {} + + # Get a list of all the valid properties in this node. + for name, prop in node.props.items(): + if name not in PROP_IGNORE_LIST and name[0] != '#': + fields[name] = copy.deepcopy(prop) + + # If we've seen this node_name before, update the existing struct. + if node_name in structs: + struct = structs[node_name] + for name, prop in fields.items(): + oldprop = struct.get(name) + if oldprop: + oldprop.Widen(prop) + else: + struct[name] = prop + + # Otherwise store this as a new struct. + else: + structs[node_name] = fields + + upto = 0 + for node in self._valid_nodes: + node_name, _ = get_compat_name(node) + struct = structs[node_name] + for name, prop in node.props.items(): + if name not in PROP_IGNORE_LIST and name[0] != '#': + prop.Widen(struct[name]) + upto += 1 + + struct_name, aliases = get_compat_name(node) + for alias in aliases: + self._aliases[alias] = struct_name + + return structs + + def scan_phandles(self): + """Figure out what phandles each node uses + + We need to be careful when outputing nodes that use phandles since + they must come after the declaration of the phandles in the C file. + Otherwise we get a compiler error since the phandle struct is not yet + declared. + + This function adds to each node a list of phandle nodes that the node + depends on. This allows us to output things in the right order. + """ + for node in self._valid_nodes: + node.phandles = set() + for pname, prop in node.props.items(): + if pname in PROP_IGNORE_LIST or pname[0] == '#': + continue + info = self.get_phandle_argc(prop, node.name) + if info: + # Process the list as pairs of (phandle, id) + pos = 0 + for args in info.args: + phandle_cell = prop.value[pos] + phandle = fdt_util.fdt32_to_cpu(phandle_cell) + target_node = self._fdt.phandle_to_node[phandle] + node.phandles.add(target_node) + pos += 1 + args + + + def generate_structs(self, structs): + """Generate struct defintions for the platform data + + This writes out the body of a header file consisting of structure + definitions for node in self._valid_nodes. See the documentation in + README.of-plat for more information. + """ + self.out_header() + self.out('#include <stdbool.h>\n') + self.out('#include <linux/libfdt.h>\n') + + # Output the struct definition + for name in sorted(structs): + self.out('struct %s%s {\n' % (STRUCT_PREFIX, name)) + for pname in sorted(structs[name]): + prop = structs[name][pname] + info = self.get_phandle_argc(prop, structs[name]) + if info: + # For phandles, include a reference to the target + struct_name = 'struct phandle_%d_arg' % info.max_args + self.out('\t%s%s[%d]' % (tab_to(2, struct_name), + conv_name_to_c(prop.name), + len(info.args))) + else: + ptype = TYPE_NAMES[prop.type] + self.out('\t%s%s' % (tab_to(2, ptype), + conv_name_to_c(prop.name))) + if isinstance(prop.value, list): + self.out('[%d]' % len(prop.value)) + self.out(';\n') + self.out('};\n') + + for alias, struct_name in self._aliases.iteritems(): + self.out('#define %s%s %s%s\n'% (STRUCT_PREFIX, alias, + STRUCT_PREFIX, struct_name)) + + def output_node(self, node): + """Output the C code for a node + + Args: + node: node to output + """ + struct_name, _ = get_compat_name(node) + var_name = conv_name_to_c(node.name) + self.buf('static struct %s%s %s%s = {\n' % + (STRUCT_PREFIX, struct_name, VAL_PREFIX, var_name)) + for pname, prop in node.props.items(): + if pname in PROP_IGNORE_LIST or pname[0] == '#': + continue + member_name = conv_name_to_c(prop.name) + self.buf('\t%s= ' % tab_to(3, '.' + member_name)) + + # Special handling for lists + if isinstance(prop.value, list): + self.buf('{') + vals = [] + # For phandles, output a reference to the platform data + # of the target node. + info = self.get_phandle_argc(prop, node.name) + if info: + # Process the list as pairs of (phandle, id) + pos = 0 + for args in info.args: + phandle_cell = prop.value[pos] + phandle = fdt_util.fdt32_to_cpu(phandle_cell) + target_node = self._fdt.phandle_to_node[phandle] + name = conv_name_to_c(target_node.name) + arg_values = [] + for i in range(args): + arg_values.append(str(fdt_util.fdt32_to_cpu(prop.value[pos + 1 + i]))) + pos += 1 + args + vals.append('\t{&%s%s, {%s}}' % (VAL_PREFIX, name, + ', '.join(arg_values))) + for val in vals: + self.buf('\n\t\t%s,' % val) + else: + for val in prop.value: + vals.append(get_value(prop.type, val)) + + # Put 8 values per line to avoid very long lines. + for i in xrange(0, len(vals), 8): + if i: + self.buf(',\n\t\t') + self.buf(', '.join(vals[i:i + 8])) + self.buf('}') + else: + self.buf(get_value(prop.type, prop.value)) + self.buf(',\n') + self.buf('};\n') + + # Add a device declaration + self.buf('U_BOOT_DEVICE(%s) = {\n' % var_name) + self.buf('\t.name\t\t= "%s",\n' % struct_name) + self.buf('\t.platdata\t= &%s%s,\n' % (VAL_PREFIX, var_name)) + self.buf('\t.platdata_size\t= sizeof(%s%s),\n' % (VAL_PREFIX, var_name)) + self.buf('};\n') + self.buf('\n') + + self.out(''.join(self.get_buf())) + + def generate_tables(self): + """Generate device defintions for the platform data + + This writes out C platform data initialisation data and + U_BOOT_DEVICE() declarations for each valid node. Where a node has + multiple compatible strings, a #define is used to make them equivalent. + + See the documentation in doc/driver-model/of-plat.txt for more + information. + """ + self.out_header() + self.out('#include <common.h>\n') + self.out('#include <dm.h>\n') + self.out('#include <dt-structs.h>\n') + self.out('\n') + nodes_to_output = list(self._valid_nodes) + + # Keep outputing nodes until there is none left + while nodes_to_output: + node = nodes_to_output[0] + # Output all the node's dependencies first + for req_node in node.phandles: + if req_node in nodes_to_output: + self.output_node(req_node) + nodes_to_output.remove(req_node) + self.output_node(node) + nodes_to_output.remove(node) + + +def run_steps(args, dtb_file, include_disabled, output): + """Run all the steps of the dtoc tool + + Args: + args: List of non-option arguments provided to the problem + dtb_file: Filename of dtb file to process + include_disabled: True to include disabled nodes + output: Name of output file + """ + if not args: + raise ValueError('Please specify a command: struct, platdata') + + plat = DtbPlatdata(dtb_file, include_disabled) + plat.scan_dtb() + plat.scan_tree() + plat.scan_reg_sizes() + plat.setup_output(output) + structs = plat.scan_structs() + plat.scan_phandles() + + for cmd in args[0].split(','): + if cmd == 'struct': + plat.generate_structs(structs) + elif cmd == 'platdata': + plat.generate_tables() + else: + raise ValueError("Unknown command '%s': (use: struct, platdata)" % + cmd) diff --git a/tools/u-boot-tools/dtoc/dtoc b/tools/u-boot-tools/dtoc/dtoc new file mode 120000 index 0000000..896ca44 --- /dev/null +++ b/tools/u-boot-tools/dtoc/dtoc @@ -0,0 +1 @@ +dtoc.py \ No newline at end of file diff --git a/tools/u-boot-tools/dtoc/dtoc.py b/tools/u-boot-tools/dtoc/dtoc.py new file mode 100755 index 0000000..2277af9 --- /dev/null +++ b/tools/u-boot-tools/dtoc/dtoc.py @@ -0,0 +1,109 @@ +#!/usr/bin/env python2 +# SPDX-License-Identifier: GPL-2.0+ +# +# Copyright (C) 2016 Google, Inc +# Written by Simon Glass <sjg@chromium.org> +# + +"""Device tree to C tool + +This tool converts a device tree binary file (.dtb) into two C files. The +indent is to allow a C program to access data from the device tree without +having to link against libfdt. By putting the data from the device tree into +C structures, normal C code can be used. This helps to reduce the size of the +compiled program. + +Dtoc produces two output files: + + dt-structs.h - contains struct definitions + dt-platdata.c - contains data from the device tree using the struct + definitions, as well as U-Boot driver definitions. + +This tool is used in U-Boot to provide device tree data to SPL without +increasing the code size of SPL. This supports the CONFIG_SPL_OF_PLATDATA +options. For more information about the use of this options and tool please +see doc/driver-model/of-plat.txt +""" + +from optparse import OptionParser +import os +import sys +import unittest + +# Bring in the patman libraries +our_path = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(our_path, '../patman')) + +# Bring in the libfdt module +sys.path.insert(0, 'scripts/dtc/pylibfdt') +sys.path.insert(0, os.path.join(our_path, + '../../build-sandbox_spl/scripts/dtc/pylibfdt')) + +import dtb_platdata +import test_util + +def run_tests(args): + """Run all the test we have for dtoc + + Args: + args: List of positional args provided to dtoc. This can hold a test + name to execute (as in 'dtoc -t test_empty_file', for example) + """ + import test_dtoc + + result = unittest.TestResult() + sys.argv = [sys.argv[0]] + test_name = args and args[0] or None + for module in (test_dtoc.TestDtoc,): + if test_name: + try: + suite = unittest.TestLoader().loadTestsFromName(test_name, module) + except AttributeError: + continue + else: + suite = unittest.TestLoader().loadTestsFromTestCase(module) + suite.run(result) + + print result + for _, err in result.errors: + print err + for _, err in result.failures: + print err + +def RunTestCoverage(): + """Run the tests and check that we get 100% coverage""" + sys.argv = [sys.argv[0]] + test_util.RunTestCoverage('tools/dtoc/dtoc.py', '/dtoc.py', + ['tools/patman/*.py', '*/fdt*', '*test*'], options.build_dir) + + +if __name__ != '__main__': + sys.exit(1) + +parser = OptionParser() +parser.add_option('-B', '--build-dir', type='string', default='b', + help='Directory containing the build output') +parser.add_option('-d', '--dtb-file', action='store', + help='Specify the .dtb input file') +parser.add_option('--include-disabled', action='store_true', + help='Include disabled nodes') +parser.add_option('-o', '--output', action='store', default='-', + help='Select output filename') +parser.add_option('-P', '--processes', type=int, + help='set number of processes to use for running tests') +parser.add_option('-t', '--test', action='store_true', dest='test', + default=False, help='run tests') +parser.add_option('-T', '--test-coverage', action='store_true', + default=False, help='run tests and check for 100% coverage') +(options, args) = parser.parse_args() + +# Run our meagre tests +if options.test: + run_tests(args) + +elif options.test_coverage: + RunTestCoverage() + +else: + dtb_platdata.run_steps(args, options.dtb_file, options.include_disabled, + options.output) diff --git a/tools/u-boot-tools/dtoc/dtoc_test.dts b/tools/u-boot-tools/dtoc/dtoc_test.dts new file mode 100644 index 0000000..b225948 --- /dev/null +++ b/tools/u-boot-tools/dtoc/dtoc_test.dts @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Test device tree file for dtoc + * + * Copyright 2017 Google, Inc + */ + + /dts-v1/; + +/ { +}; diff --git a/tools/u-boot-tools/dtoc/dtoc_test_add_prop.dts b/tools/u-boot-tools/dtoc/dtoc_test_add_prop.dts new file mode 100644 index 0000000..fa296e5 --- /dev/null +++ b/tools/u-boot-tools/dtoc/dtoc_test_add_prop.dts @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Test device tree file for dtoc + * + * Copyright 2018 Google, Inc + */ + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + spl-test { + u-boot,dm-pre-reloc; + compatible = "sandbox,spl-test"; + intval = <1>; + }; + + spl-test2 { + u-boot,dm-pre-reloc; + compatible = "sandbox,spl-test"; + intarray = <5>; + }; +}; diff --git a/tools/u-boot-tools/dtoc/dtoc_test_addr32.dts b/tools/u-boot-tools/dtoc/dtoc_test_addr32.dts new file mode 100644 index 0000000..2390454 --- /dev/null +++ b/tools/u-boot-tools/dtoc/dtoc_test_addr32.dts @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Test device tree file for dtoc + * + * Copyright 2017 Google, Inc + */ + + /dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + test1 { + u-boot,dm-pre-reloc; + compatible = "test1"; + reg = <0x1234 0x5678>; + }; + + test2 { + u-boot,dm-pre-reloc; + compatible = "test2"; + reg = <0x12345678 0x98765432 2 3>; + }; + +}; diff --git a/tools/u-boot-tools/dtoc/dtoc_test_addr32_64.dts b/tools/u-boot-tools/dtoc/dtoc_test_addr32_64.dts new file mode 100644 index 0000000..7599d5b --- /dev/null +++ b/tools/u-boot-tools/dtoc/dtoc_test_addr32_64.dts @@ -0,0 +1,32 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Test device tree file for dtoc + * + * Copyright 2017 Google, Inc + */ + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <2>; + + test1 { + u-boot,dm-pre-reloc; + compatible = "test1"; + reg = <0x1234 0x5678 0x0>; + }; + + test2 { + u-boot,dm-pre-reloc; + compatible = "test2"; + reg = <0x12345678 0x98765432 0x10987654>; + }; + + test3 { + u-boot,dm-pre-reloc; + compatible = "test3"; + reg = <0x12345678 0x98765432 0x10987654 2 0 3>; + }; + +}; diff --git a/tools/u-boot-tools/dtoc/dtoc_test_addr64.dts b/tools/u-boot-tools/dtoc/dtoc_test_addr64.dts new file mode 100644 index 0000000..263d251 --- /dev/null +++ b/tools/u-boot-tools/dtoc/dtoc_test_addr64.dts @@ -0,0 +1,32 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Test device tree file for dtoc + * + * Copyright 2017 Google, Inc + */ + + /dts-v1/; + +/ { + #address-cells = <2>; + #size-cells = <2>; + + test1 { + u-boot,dm-pre-reloc; + compatible = "test1"; + reg = /bits/ 64 <0x1234 0x5678>; + }; + + test2 { + u-boot,dm-pre-reloc; + compatible = "test2"; + reg = /bits/ 64 <0x1234567890123456 0x9876543210987654>; + }; + + test3 { + u-boot,dm-pre-reloc; + compatible = "test3"; + reg = /bits/ 64 <0x1234567890123456 0x9876543210987654 2 3>; + }; + +}; diff --git a/tools/u-boot-tools/dtoc/dtoc_test_addr64_32.dts b/tools/u-boot-tools/dtoc/dtoc_test_addr64_32.dts new file mode 100644 index 0000000..85e4f5f --- /dev/null +++ b/tools/u-boot-tools/dtoc/dtoc_test_addr64_32.dts @@ -0,0 +1,32 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Test device tree file for dtoc + * + * Copyright 2017 Google, Inc + */ + +/dts-v1/; + +/ { + #address-cells = <2>; + #size-cells = <1>; + + test1 { + u-boot,dm-pre-reloc; + compatible = "test1"; + reg = <0x1234 0x0 0x5678>; + }; + + test2 { + u-boot,dm-pre-reloc; + compatible = "test2"; + reg = <0x12345678 0x90123456 0x98765432>; + }; + + test3 { + u-boot,dm-pre-reloc; + compatible = "test3"; + reg = <0x12345678 0x90123456 0x98765432 0 2 3>; + }; + +}; diff --git a/tools/u-boot-tools/dtoc/dtoc_test_aliases.dts b/tools/u-boot-tools/dtoc/dtoc_test_aliases.dts new file mode 100644 index 0000000..e545816 --- /dev/null +++ b/tools/u-boot-tools/dtoc/dtoc_test_aliases.dts @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Test device tree file for dtoc + * + * Copyright 2017 Google, Inc + */ + + /dts-v1/; + +/ { + spl-test { + u-boot,dm-pre-reloc; + compatible = "compat1", "compat2.1-fred", "compat3"; + intval = <1>; + }; + +}; diff --git a/tools/u-boot-tools/dtoc/dtoc_test_bad_reg.dts b/tools/u-boot-tools/dtoc/dtoc_test_bad_reg.dts new file mode 100644 index 0000000..1312acb --- /dev/null +++ b/tools/u-boot-tools/dtoc/dtoc_test_bad_reg.dts @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Test device tree file for dtoc + * + * Copyright 2018 Google, Inc + */ + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + spl-test { + compatible = "test"; + reg = "fre"; + }; +}; diff --git a/tools/u-boot-tools/dtoc/dtoc_test_bad_reg2.dts b/tools/u-boot-tools/dtoc/dtoc_test_bad_reg2.dts new file mode 100644 index 0000000..3e9efa4 --- /dev/null +++ b/tools/u-boot-tools/dtoc/dtoc_test_bad_reg2.dts @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Test device tree file for dtoc + * + * Copyright 2018 Google, Inc + */ + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + spl-test { + compatible = "test"; + reg = <1 2 3>; + }; +}; diff --git a/tools/u-boot-tools/dtoc/dtoc_test_empty.dts b/tools/u-boot-tools/dtoc/dtoc_test_empty.dts new file mode 100644 index 0000000..b225948 --- /dev/null +++ b/tools/u-boot-tools/dtoc/dtoc_test_empty.dts @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Test device tree file for dtoc + * + * Copyright 2017 Google, Inc + */ + + /dts-v1/; + +/ { +}; diff --git a/tools/u-boot-tools/dtoc/dtoc_test_phandle.dts b/tools/u-boot-tools/dtoc/dtoc_test_phandle.dts new file mode 100644 index 0000000..a71acff --- /dev/null +++ b/tools/u-boot-tools/dtoc/dtoc_test_phandle.dts @@ -0,0 +1,42 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Test device tree file for dtoc + * + * Copyright 2017 Google, Inc + */ + + /dts-v1/; + +/ { + phandle: phandle-target { + u-boot,dm-pre-reloc; + compatible = "target"; + intval = <0>; + #clock-cells = <0>; + }; + + phandle_1: phandle2-target { + u-boot,dm-pre-reloc; + compatible = "target"; + intval = <1>; + #clock-cells = <1>; + }; + phandle_2: phandle3-target { + u-boot,dm-pre-reloc; + compatible = "target"; + intval = <2>; + #clock-cells = <2>; + }; + + phandle-source { + u-boot,dm-pre-reloc; + compatible = "source"; + clocks = <&phandle &phandle_1 11 &phandle_2 12 13 &phandle>; + }; + + phandle-source2 { + u-boot,dm-pre-reloc; + compatible = "source"; + clocks = <&phandle>; + }; +}; diff --git a/tools/u-boot-tools/dtoc/dtoc_test_phandle_bad.dts b/tools/u-boot-tools/dtoc/dtoc_test_phandle_bad.dts new file mode 100644 index 0000000..a3ddc59 --- /dev/null +++ b/tools/u-boot-tools/dtoc/dtoc_test_phandle_bad.dts @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Test device tree file for dtoc + * + * Copyright 2018 Google, Inc + */ + +/dts-v1/; + +/ { + phandle-source { + u-boot,dm-pre-reloc; + compatible = "source"; + clocks = <20>; /* Invalid phandle */ + }; +}; diff --git a/tools/u-boot-tools/dtoc/dtoc_test_phandle_bad2.dts b/tools/u-boot-tools/dtoc/dtoc_test_phandle_bad2.dts new file mode 100644 index 0000000..fe25f56 --- /dev/null +++ b/tools/u-boot-tools/dtoc/dtoc_test_phandle_bad2.dts @@ -0,0 +1,22 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Test device tree file for dtoc + * + * Copyright 2018 Google, Inc + */ + +/dts-v1/; + +/ { + phandle: phandle-target { + u-boot,dm-pre-reloc; + compatible = "target"; + intval = <0>; + }; + + phandle-source2 { + u-boot,dm-pre-reloc; + compatible = "source"; + clocks = <&phandle>; + }; +}; diff --git a/tools/u-boot-tools/dtoc/dtoc_test_phandle_reorder.dts b/tools/u-boot-tools/dtoc/dtoc_test_phandle_reorder.dts new file mode 100644 index 0000000..aa71d56 --- /dev/null +++ b/tools/u-boot-tools/dtoc/dtoc_test_phandle_reorder.dts @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Test device tree file for dtoc + * + * Copyright 2018 Google, Inc + */ + +/dts-v1/; + +/ { + + phandle-source2 { + u-boot,dm-pre-reloc; + compatible = "source"; + clocks = <&phandle>; + }; + + phandle: phandle-target { + u-boot,dm-pre-reloc; + compatible = "target"; + #clock-cells = <0>; + }; +}; diff --git a/tools/u-boot-tools/dtoc/dtoc_test_phandle_single.dts b/tools/u-boot-tools/dtoc/dtoc_test_phandle_single.dts new file mode 100644 index 0000000..aacd0b1 --- /dev/null +++ b/tools/u-boot-tools/dtoc/dtoc_test_phandle_single.dts @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Test device tree file for dtoc + * + * Copyright 2018 Google, Inc + */ + +/dts-v1/; + +/ { + phandle: phandle-target { + u-boot,dm-pre-reloc; + compatible = "target"; + intval = <0>; + #clock-cells = <0>; + }; + + phandle-source2 { + u-boot,dm-pre-reloc; + compatible = "source"; + clocks = <&phandle>; + }; +}; diff --git a/tools/u-boot-tools/dtoc/dtoc_test_simple.dts b/tools/u-boot-tools/dtoc/dtoc_test_simple.dts new file mode 100644 index 0000000..165680b --- /dev/null +++ b/tools/u-boot-tools/dtoc/dtoc_test_simple.dts @@ -0,0 +1,62 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Test device tree file for dtoc + * + * Copyright 2017 Google, Inc + */ + + /dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + spl-test { + u-boot,dm-pre-reloc; + compatible = "sandbox,spl-test"; + boolval; + intval = <1>; + intarray = <2 3 4>; + byteval = [05]; + bytearray = [06]; + longbytearray = [09 0a 0b 0c 0d 0e 0f 10 11]; + stringval = "message"; + stringarray = "multi-word", "message"; + notstring = [20 21 22 10 00]; + }; + + spl-test2 { + u-boot,dm-pre-reloc; + compatible = "sandbox,spl-test"; + intval = <3>; + intarray = <5>; + byteval = [08]; + bytearray = [01 23 34]; + longbytearray = [09 0a 0b 0c]; + stringval = "message2"; + stringarray = "another", "multi-word", "message"; + }; + + spl-test3 { + u-boot,dm-pre-reloc; + compatible = "sandbox,spl-test"; + stringarray = "one"; + }; + + spl-test4 { + u-boot,dm-pre-reloc; + compatible = "sandbox,spl-test.2"; + }; + + i2c@0 { + compatible = "sandbox,i2c-test"; + u-boot,dm-pre-reloc; + #address-cells = <1>; + #size-cells = <0>; + pmic@9 { + compatible = "sandbox,pmic-test"; + u-boot,dm-pre-reloc; + reg = <9>; + low-power; + }; + }; +}; diff --git a/tools/u-boot-tools/dtoc/fdt.py b/tools/u-boot-tools/dtoc/fdt.py new file mode 100644 index 0000000..9ad72f8 --- /dev/null +++ b/tools/u-boot-tools/dtoc/fdt.py @@ -0,0 +1,657 @@ +#!/usr/bin/python +# SPDX-License-Identifier: GPL-2.0+ +# +# Copyright (C) 2016 Google, Inc +# Written by Simon Glass <sjg@chromium.org> +# + +import struct +import sys + +import fdt_util +import libfdt +from libfdt import QUIET_NOTFOUND + +# This deals with a device tree, presenting it as an assortment of Node and +# Prop objects, representing nodes and properties, respectively. This file +# contains the base classes and defines the high-level API. You can use +# FdtScan() as a convenience function to create and scan an Fdt. + +# This implementation uses a libfdt Python library to access the device tree, +# so it is fairly efficient. + +# A list of types we support +(TYPE_BYTE, TYPE_INT, TYPE_STRING, TYPE_BOOL, TYPE_INT64) = range(5) + +def CheckErr(errnum, msg): + if errnum: + raise ValueError('Error %d: %s: %s' % + (errnum, libfdt.fdt_strerror(errnum), msg)) + +class Prop: + """A device tree property + + Properties: + name: Property name (as per the device tree) + value: Property value as a string of bytes, or a list of strings of + bytes + type: Value type + """ + def __init__(self, node, offset, name, bytes): + self._node = node + self._offset = offset + self.name = name + self.value = None + self.bytes = str(bytes) + self.dirty = False + if not bytes: + self.type = TYPE_BOOL + self.value = True + return + self.type, self.value = self.BytesToValue(bytes) + + def RefreshOffset(self, poffset): + self._offset = poffset + + def Widen(self, newprop): + """Figure out which property type is more general + + Given a current property and a new property, this function returns the + one that is less specific as to type. The less specific property will + be ble to represent the data in the more specific property. This is + used for things like: + + node1 { + compatible = "fred"; + value = <1>; + }; + node1 { + compatible = "fred"; + value = <1 2>; + }; + + He we want to use an int array for 'value'. The first property + suggests that a single int is enough, but the second one shows that + it is not. Calling this function with these two propertes would + update the current property to be like the second, since it is less + specific. + """ + if newprop.type < self.type: + self.type = newprop.type + + if type(newprop.value) == list and type(self.value) != list: + self.value = [self.value] + + if type(self.value) == list and len(newprop.value) > len(self.value): + val = self.GetEmpty(self.type) + while len(self.value) < len(newprop.value): + self.value.append(val) + + def BytesToValue(self, bytes): + """Converts a string of bytes into a type and value + + Args: + A string containing bytes + + Return: + A tuple: + Type of data + Data, either a single element or a list of elements. Each element + is one of: + TYPE_STRING: string value from the property + TYPE_INT: a byte-swapped integer stored as a 4-byte string + TYPE_BYTE: a byte stored as a single-byte string + """ + bytes = str(bytes) + size = len(bytes) + strings = bytes.split('\0') + is_string = True + count = len(strings) - 1 + if count > 0 and not strings[-1]: + for string in strings[:-1]: + if not string: + is_string = False + break + for ch in string: + if ch < ' ' or ch > '~': + is_string = False + break + else: + is_string = False + if is_string: + if count == 1: + return TYPE_STRING, strings[0] + else: + return TYPE_STRING, strings[:-1] + if size % 4: + if size == 1: + return TYPE_BYTE, bytes[0] + else: + return TYPE_BYTE, list(bytes) + val = [] + for i in range(0, size, 4): + val.append(bytes[i:i + 4]) + if size == 4: + return TYPE_INT, val[0] + else: + return TYPE_INT, val + + @classmethod + def GetEmpty(self, type): + """Get an empty / zero value of the given type + + Returns: + A single value of the given type + """ + if type == TYPE_BYTE: + return chr(0) + elif type == TYPE_INT: + return struct.pack('>I', 0); + elif type == TYPE_STRING: + return '' + else: + return True + + def GetOffset(self): + """Get the offset of a property + + Returns: + The offset of the property (struct fdt_property) within the file + """ + self._node._fdt.CheckCache() + return self._node._fdt.GetStructOffset(self._offset) + + def SetInt(self, val): + """Set the integer value of the property + + The device tree is marked dirty so that the value will be written to + the block on the next sync. + + Args: + val: Integer value (32-bit, single cell) + """ + self.bytes = struct.pack('>I', val); + self.value = self.bytes + self.type = TYPE_INT + self.dirty = True + + def SetData(self, bytes): + """Set the value of a property as bytes + + Args: + bytes: New property value to set + """ + self.bytes = str(bytes) + self.type, self.value = self.BytesToValue(bytes) + self.dirty = True + + def Sync(self, auto_resize=False): + """Sync property changes back to the device tree + + This updates the device tree blob with any changes to this property + since the last sync. + + Args: + auto_resize: Resize the device tree automatically if it does not + have enough space for the update + + Raises: + FdtException if auto_resize is False and there is not enough space + """ + if self._offset is None or self.dirty: + node = self._node + fdt_obj = node._fdt._fdt_obj + if auto_resize: + while fdt_obj.setprop(node.Offset(), self.name, self.bytes, + (libfdt.NOSPACE,)) == -libfdt.NOSPACE: + fdt_obj.resize(fdt_obj.totalsize() + 1024) + fdt_obj.setprop(node.Offset(), self.name, self.bytes) + else: + fdt_obj.setprop(node.Offset(), self.name, self.bytes) + + +class Node: + """A device tree node + + Properties: + offset: Integer offset in the device tree + name: Device tree node tname + path: Full path to node, along with the node name itself + _fdt: Device tree object + subnodes: A list of subnodes for this node, each a Node object + props: A dict of properties for this node, each a Prop object. + Keyed by property name + """ + def __init__(self, fdt, parent, offset, name, path): + self._fdt = fdt + self.parent = parent + self._offset = offset + self.name = name + self.path = path + self.subnodes = [] + self.props = {} + + def GetFdt(self): + """Get the Fdt object for this node + + Returns: + Fdt object + """ + return self._fdt + + def FindNode(self, name): + """Find a node given its name + + Args: + name: Node name to look for + Returns: + Node object if found, else None + """ + for subnode in self.subnodes: + if subnode.name == name: + return subnode + return None + + def Offset(self): + """Returns the offset of a node, after checking the cache + + This should be used instead of self._offset directly, to ensure that + the cache does not contain invalid offsets. + """ + self._fdt.CheckCache() + return self._offset + + def Scan(self): + """Scan a node's properties and subnodes + + This fills in the props and subnodes properties, recursively + searching into subnodes so that the entire tree is built. + """ + fdt_obj = self._fdt._fdt_obj + self.props = self._fdt.GetProps(self) + phandle = fdt_obj.get_phandle(self.Offset()) + if phandle: + self._fdt.phandle_to_node[phandle] = self + + offset = fdt_obj.first_subnode(self.Offset(), QUIET_NOTFOUND) + while offset >= 0: + sep = '' if self.path[-1] == '/' else '/' + name = fdt_obj.get_name(offset) + path = self.path + sep + name + node = Node(self._fdt, self, offset, name, path) + self.subnodes.append(node) + + node.Scan() + offset = fdt_obj.next_subnode(offset, QUIET_NOTFOUND) + + def Refresh(self, my_offset): + """Fix up the _offset for each node, recursively + + Note: This does not take account of property offsets - these will not + be updated. + """ + fdt_obj = self._fdt._fdt_obj + if self._offset != my_offset: + self._offset = my_offset + offset = fdt_obj.first_subnode(self._offset, QUIET_NOTFOUND) + for subnode in self.subnodes: + if subnode.name != fdt_obj.get_name(offset): + raise ValueError('Internal error, node name mismatch %s != %s' % + (subnode.name, fdt_obj.get_name(offset))) + subnode.Refresh(offset) + offset = fdt_obj.next_subnode(offset, QUIET_NOTFOUND) + if offset != -libfdt.FDT_ERR_NOTFOUND: + raise ValueError('Internal error, offset == %d' % offset) + + poffset = fdt_obj.first_property_offset(self._offset, QUIET_NOTFOUND) + while poffset >= 0: + p = fdt_obj.get_property_by_offset(poffset) + prop = self.props.get(p.name) + if not prop: + raise ValueError("Internal error, property '%s' missing, " + 'offset %d' % (p.name, poffset)) + prop.RefreshOffset(poffset) + poffset = fdt_obj.next_property_offset(poffset, QUIET_NOTFOUND) + + def DeleteProp(self, prop_name): + """Delete a property of a node + + The property is deleted and the offset cache is invalidated. + + Args: + prop_name: Name of the property to delete + Raises: + ValueError if the property does not exist + """ + CheckErr(self._fdt._fdt_obj.delprop(self.Offset(), prop_name), + "Node '%s': delete property: '%s'" % (self.path, prop_name)) + del self.props[prop_name] + self._fdt.Invalidate() + + def AddZeroProp(self, prop_name): + """Add a new property to the device tree with an integer value of 0. + + Args: + prop_name: Name of property + """ + self.props[prop_name] = Prop(self, None, prop_name, '\0' * 4) + + def AddEmptyProp(self, prop_name, len): + """Add a property with a fixed data size, for filling in later + + The device tree is marked dirty so that the value will be written to + the blob on the next sync. + + Args: + prop_name: Name of property + len: Length of data in property + """ + value = chr(0) * len + self.props[prop_name] = Prop(self, None, prop_name, value) + + def SetInt(self, prop_name, val): + """Update an integer property int the device tree. + + This is not allowed to change the size of the FDT. + + The device tree is marked dirty so that the value will be written to + the blob on the next sync. + + Args: + prop_name: Name of property + val: Value to set + """ + self.props[prop_name].SetInt(val) + + def SetData(self, prop_name, val): + """Set the data value of a property + + The device tree is marked dirty so that the value will be written to + the blob on the next sync. + + Args: + prop_name: Name of property to set + val: Data value to set + """ + self.props[prop_name].SetData(val) + + def SetString(self, prop_name, val): + """Set the string value of a property + + The device tree is marked dirty so that the value will be written to + the blob on the next sync. + + Args: + prop_name: Name of property to set + val: String value to set (will be \0-terminated in DT) + """ + self.props[prop_name].SetData(val + chr(0)) + + def AddString(self, prop_name, val): + """Add a new string property to a node + + The device tree is marked dirty so that the value will be written to + the blob on the next sync. + + Args: + prop_name: Name of property to add + val: String value of property + """ + self.props[prop_name] = Prop(self, None, prop_name, val + chr(0)) + + def AddSubnode(self, name): + """Add a new subnode to the node + + Args: + name: name of node to add + + Returns: + New subnode that was created + """ + path = self.path + '/' + name + subnode = Node(self._fdt, self, None, name, path) + self.subnodes.append(subnode) + return subnode + + def Sync(self, auto_resize=False): + """Sync node changes back to the device tree + + This updates the device tree blob with any changes to this node and its + subnodes since the last sync. + + Args: + auto_resize: Resize the device tree automatically if it does not + have enough space for the update + + Raises: + FdtException if auto_resize is False and there is not enough space + """ + if self._offset is None: + # The subnode doesn't exist yet, so add it + fdt_obj = self._fdt._fdt_obj + if auto_resize: + while True: + offset = fdt_obj.add_subnode(self.parent._offset, self.name, + (libfdt.NOSPACE,)) + if offset != -libfdt.NOSPACE: + break + fdt_obj.resize(fdt_obj.totalsize() + 1024) + else: + offset = fdt_obj.add_subnode(self.parent._offset, self.name) + self._offset = offset + + # Sync subnodes in reverse so that we don't disturb node offsets for + # nodes that are earlier in the DT. This avoids an O(n^2) rescan of + # node offsets. + for node in reversed(self.subnodes): + node.Sync(auto_resize) + + # Sync properties now, whose offsets should not have been disturbed. + # We do this after subnodes, since this disturbs the offsets of these + # properties. + prop_list = sorted(self.props.values(), key=lambda prop: prop._offset, + reverse=True) + for prop in prop_list: + prop.Sync(auto_resize) + + +class Fdt: + """Provides simple access to a flat device tree blob using libfdts. + + Properties: + fname: Filename of fdt + _root: Root of device tree (a Node object) + """ + def __init__(self, fname): + self._fname = fname + self._cached_offsets = False + self.phandle_to_node = {} + if self._fname: + self._fname = fdt_util.EnsureCompiled(self._fname) + + with open(self._fname) as fd: + self._fdt_obj = libfdt.Fdt(fd.read()) + + @staticmethod + def FromData(data): + """Create a new Fdt object from the given data + + Args: + data: Device-tree data blob + + Returns: + Fdt object containing the data + """ + fdt = Fdt(None) + fdt._fdt_obj = libfdt.Fdt(bytearray(data)) + return fdt + + def LookupPhandle(self, phandle): + """Look up a phandle + + Args: + phandle: Phandle to look up (int) + + Returns: + Node object the phandle points to + """ + return self.phandle_to_node.get(phandle) + + def Scan(self, root='/'): + """Scan a device tree, building up a tree of Node objects + + This fills in the self._root property + + Args: + root: Ignored + + TODO(sjg@chromium.org): Implement the 'root' parameter + """ + self._cached_offsets = True + self._root = self.Node(self, None, 0, '/', '/') + self._root.Scan() + + def GetRoot(self): + """Get the root Node of the device tree + + Returns: + The root Node object + """ + return self._root + + def GetNode(self, path): + """Look up a node from its path + + Args: + path: Path to look up, e.g. '/microcode/update@0' + Returns: + Node object, or None if not found + """ + node = self._root + parts = path.split('/') + if len(parts) < 2: + return None + for part in parts[1:]: + node = node.FindNode(part) + if not node: + return None + return node + + def Flush(self): + """Flush device tree changes back to the file + + If the device tree has changed in memory, write it back to the file. + """ + with open(self._fname, 'wb') as fd: + fd.write(self._fdt_obj.as_bytearray()) + + def Sync(self, auto_resize=False): + """Make sure any DT changes are written to the blob + + Args: + auto_resize: Resize the device tree automatically if it does not + have enough space for the update + + Raises: + FdtException if auto_resize is False and there is not enough space + """ + self._root.Sync(auto_resize) + self.Invalidate() + + def Pack(self): + """Pack the device tree down to its minimum size + + When nodes and properties shrink or are deleted, wasted space can + build up in the device tree binary. + """ + CheckErr(self._fdt_obj.pack(), 'pack') + self.Invalidate() + + def GetContents(self): + """Get the contents of the FDT + + Returns: + The FDT contents as a string of bytes + """ + return self._fdt_obj.as_bytearray() + + def GetFdtObj(self): + """Get the contents of the FDT + + Returns: + The FDT contents as a libfdt.Fdt object + """ + return self._fdt_obj + + def GetProps(self, node): + """Get all properties from a node. + + Args: + node: Full path to node name to look in. + + Returns: + A dictionary containing all the properties, indexed by node name. + The entries are Prop objects. + + Raises: + ValueError: if the node does not exist. + """ + props_dict = {} + poffset = self._fdt_obj.first_property_offset(node._offset, + QUIET_NOTFOUND) + while poffset >= 0: + p = self._fdt_obj.get_property_by_offset(poffset) + prop = Prop(node, poffset, p.name, p) + props_dict[prop.name] = prop + + poffset = self._fdt_obj.next_property_offset(poffset, + QUIET_NOTFOUND) + return props_dict + + def Invalidate(self): + """Mark our offset cache as invalid""" + self._cached_offsets = False + + def CheckCache(self): + """Refresh the offset cache if needed""" + if self._cached_offsets: + return + self.Refresh() + self._cached_offsets = True + + def Refresh(self): + """Refresh the offset cache""" + self._root.Refresh(0) + + def GetStructOffset(self, offset): + """Get the file offset of a given struct offset + + Args: + offset: Offset within the 'struct' region of the device tree + Returns: + Position of @offset within the device tree binary + """ + return self._fdt_obj.off_dt_struct() + offset + + @classmethod + def Node(self, fdt, parent, offset, name, path): + """Create a new node + + This is used by Fdt.Scan() to create a new node using the correct + class. + + Args: + fdt: Fdt object + parent: Parent node, or None if this is the root node + offset: Offset of node + name: Node name + path: Full path to node + """ + node = Node(fdt, parent, offset, name, path) + return node + +def FdtScan(fname): + """Returns a new Fdt object""" + dtb = Fdt(fname) + dtb.Scan() + return dtb diff --git a/tools/u-boot-tools/dtoc/fdt_util.py b/tools/u-boot-tools/dtoc/fdt_util.py new file mode 100644 index 0000000..5fbfc88 --- /dev/null +++ b/tools/u-boot-tools/dtoc/fdt_util.py @@ -0,0 +1,211 @@ +#!/usr/bin/python +# SPDX-License-Identifier: GPL-2.0+ +# +# Copyright (C) 2016 Google, Inc +# Written by Simon Glass <sjg@chromium.org> +# + +# Utility functions for reading from a device tree. Once the upstream pylibfdt +# implementation advances far enough, we should be able to drop these. + +import os +import struct +import sys +import tempfile + +import command +import tools + +VERSION3 = sys.version_info > (3, 0) + +def get_plain_bytes(val): + """Handle Python 3 strings""" + if isinstance(val, bytes): + val = val.decode('utf-8') + return val.encode('raw_unicode_escape') + +def fdt32_to_cpu(val): + """Convert a device tree cell to an integer + + Args: + Value to convert (4-character string representing the cell value) + + Return: + A native-endian integer value + """ + if VERSION3: + # This code is not reached in Python 2 + val = get_plain_bytes(val) # pragma: no cover + return struct.unpack('>I', val)[0] + +def fdt_cells_to_cpu(val, cells): + """Convert one or two cells to a long integer + + Args: + Value to convert (array of one or more 4-character strings) + + Return: + A native-endian long value + """ + if not cells: + return 0 + out = long(fdt32_to_cpu(val[0])) + if cells == 2: + out = out << 32 | fdt32_to_cpu(val[1]) + return out + +def EnsureCompiled(fname, capture_stderr=False): + """Compile an fdt .dts source file into a .dtb binary blob if needed. + + Args: + fname: Filename (if .dts it will be compiled). It not it will be + left alone + + Returns: + Filename of resulting .dtb file + """ + _, ext = os.path.splitext(fname) + if ext != '.dts': + return fname + + dts_input = tools.GetOutputFilename('source.dts') + dtb_output = tools.GetOutputFilename('source.dtb') + + search_paths = [os.path.join(os.getcwd(), 'include')] + root, _ = os.path.splitext(fname) + args = ['-E', '-P', '-x', 'assembler-with-cpp', '-D__ASSEMBLY__'] + args += ['-Ulinux'] + for path in search_paths: + args.extend(['-I', path]) + args += ['-o', dts_input, fname] + command.Run('cc', *args) + + # If we don't have a directory, put it in the tools tempdir + search_list = [] + for path in search_paths: + search_list.extend(['-i', path]) + args = ['-I', 'dts', '-o', dtb_output, '-O', 'dtb', + '-W', 'no-unit_address_vs_reg'] + args.extend(search_list) + args.append(dts_input) + dtc = os.environ.get('DTC') or 'dtc' + command.Run(dtc, *args, capture_stderr=capture_stderr) + return dtb_output + +def GetInt(node, propname, default=None): + """Get an integer from a property + + Args: + node: Node object to read from + propname: property name to read + default: Default value to use if the node/property do not exist + + Returns: + Integer value read, or default if none + """ + prop = node.props.get(propname) + if not prop: + return default + if isinstance(prop.value, list): + raise ValueError("Node '%s' property '%s' has list value: expecting " + "a single integer" % (node.name, propname)) + value = fdt32_to_cpu(prop.value) + return value + +def GetString(node, propname, default=None): + """Get a string from a property + + Args: + node: Node object to read from + propname: property name to read + default: Default value to use if the node/property do not exist + + Returns: + String value read, or default if none + """ + prop = node.props.get(propname) + if not prop: + return default + value = prop.value + if isinstance(value, list): + raise ValueError("Node '%s' property '%s' has list value: expecting " + "a single string" % (node.name, propname)) + return value + +def GetBool(node, propname, default=False): + """Get an boolean from a property + + Args: + node: Node object to read from + propname: property name to read + default: Default value to use if the node/property do not exist + + Returns: + Boolean value read, or default if none (if you set this to True the + function will always return True) + """ + if propname in node.props: + return True + return default + +def GetByte(node, propname, default=None): + """Get an byte from a property + + Args: + node: Node object to read from + propname: property name to read + default: Default value to use if the node/property do not exist + + Returns: + Byte value read, or default if none + """ + prop = node.props.get(propname) + if not prop: + return default + value = prop.value + if isinstance(value, list): + raise ValueError("Node '%s' property '%s' has list value: expecting " + "a single byte" % (node.name, propname)) + if len(value) != 1: + raise ValueError("Node '%s' property '%s' has length %d, expecting %d" % + (node.name, propname, len(value), 1)) + return ord(value[0]) + +def GetPhandleList(node, propname): + """Get a list of phandles from a property + + Args: + node: Node object to read from + propname: property name to read + + Returns: + List of phandles read, each an integer + """ + prop = node.props.get(propname) + if not prop: + return None + value = prop.value + if not isinstance(value, list): + value = [value] + return [fdt32_to_cpu(v) for v in value] + +def GetDatatype(node, propname, datatype): + """Get a value of a given type from a property + + Args: + node: Node object to read from + propname: property name to read + datatype: Type to read (str or int) + + Returns: + value read, or None if none + + Raises: + ValueError if datatype is not str or int + """ + if datatype == str: + return GetString(node, propname) + elif datatype == int: + return GetInt(node, propname) + raise ValueError("fdt_util internal error: Unknown data type '%s'" % + datatype) diff --git a/tools/u-boot-tools/dtoc/test_dtoc.py b/tools/u-boot-tools/dtoc/test_dtoc.py new file mode 100644 index 0000000..11bead1 --- /dev/null +++ b/tools/u-boot-tools/dtoc/test_dtoc.py @@ -0,0 +1,710 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (c) 2012 The Chromium OS Authors. +# + +"""Tests for the dtb_platdata module + +This includes unit tests for some functions and functional tests for the dtoc +tool. +""" + +import collections +import os +import struct +import unittest + +import dtb_platdata +from dtb_platdata import conv_name_to_c +from dtb_platdata import get_compat_name +from dtb_platdata import get_value +from dtb_platdata import tab_to +import fdt +import fdt_util +import test_util +import tools + +our_path = os.path.dirname(os.path.realpath(__file__)) + + +HEADER = '''/* + * DO NOT MODIFY + * + * This file was generated by dtoc from a .dtb (device tree binary) file. + */ + +#include <stdbool.h> +#include <linux/libfdt.h>''' + +C_HEADER = '''/* + * DO NOT MODIFY + * + * This file was generated by dtoc from a .dtb (device tree binary) file. + */ + +#include <common.h> +#include <dm.h> +#include <dt-structs.h> +''' + + + +def get_dtb_file(dts_fname, capture_stderr=False): + """Compile a .dts file to a .dtb + + Args: + dts_fname: Filename of .dts file in the current directory + capture_stderr: True to capture and discard stderr output + + Returns: + Filename of compiled file in output directory + """ + return fdt_util.EnsureCompiled(os.path.join(our_path, dts_fname), + capture_stderr=capture_stderr) + + +class TestDtoc(unittest.TestCase): + """Tests for dtoc""" + @classmethod + def setUpClass(cls): + tools.PrepareOutputDir(None) + + @classmethod + def tearDownClass(cls): + tools._RemoveOutputDir() + + def _WritePythonString(self, fname, data): + """Write a string with tabs expanded as done in this Python file + + Args: + fname: Filename to write to + data: Raw string to convert + """ + data = data.replace('\t', '\\t') + with open(fname, 'w') as fd: + fd.write(data) + + def _CheckStrings(self, expected, actual): + """Check that a string matches its expected value + + If the strings do not match, they are written to the /tmp directory in + the same Python format as is used here in the test. This allows for + easy comparison and update of the tests. + + Args: + expected: Expected string + actual: Actual string + """ + if expected != actual: + self._WritePythonString('/tmp/binman.expected', expected) + self._WritePythonString('/tmp/binman.actual', actual) + print 'Failures written to /tmp/binman.{expected,actual}' + self.assertEquals(expected, actual) + + def test_name(self): + """Test conversion of device tree names to C identifiers""" + self.assertEqual('serial_at_0x12', conv_name_to_c('serial@0x12')) + self.assertEqual('vendor_clock_frequency', + conv_name_to_c('vendor,clock-frequency')) + self.assertEqual('rockchip_rk3399_sdhci_5_1', + conv_name_to_c('rockchip,rk3399-sdhci-5.1')) + + def test_tab_to(self): + """Test operation of tab_to() function""" + self.assertEqual('fred ', tab_to(0, 'fred')) + self.assertEqual('fred\t', tab_to(1, 'fred')) + self.assertEqual('fred was here ', tab_to(1, 'fred was here')) + self.assertEqual('fred was here\t\t', tab_to(3, 'fred was here')) + self.assertEqual('exactly8 ', tab_to(1, 'exactly8')) + self.assertEqual('exactly8\t', tab_to(2, 'exactly8')) + + def test_get_value(self): + """Test operation of get_value() function""" + self.assertEqual('0x45', + get_value(fdt.TYPE_INT, struct.pack('>I', 0x45))) + self.assertEqual('0x45', + get_value(fdt.TYPE_BYTE, struct.pack('<I', 0x45))) + self.assertEqual('0x0', + get_value(fdt.TYPE_BYTE, struct.pack('>I', 0x45))) + self.assertEqual('"test"', get_value(fdt.TYPE_STRING, 'test')) + self.assertEqual('true', get_value(fdt.TYPE_BOOL, None)) + + def test_get_compat_name(self): + """Test operation of get_compat_name() function""" + Prop = collections.namedtuple('Prop', ['value']) + Node = collections.namedtuple('Node', ['props']) + + prop = Prop(['rockchip,rk3399-sdhci-5.1', 'arasan,sdhci-5.1']) + node = Node({'compatible': prop}) + self.assertEqual(('rockchip_rk3399_sdhci_5_1', ['arasan_sdhci_5_1']), + get_compat_name(node)) + + prop = Prop(['rockchip,rk3399-sdhci-5.1']) + node = Node({'compatible': prop}) + self.assertEqual(('rockchip_rk3399_sdhci_5_1', []), + get_compat_name(node)) + + prop = Prop(['rockchip,rk3399-sdhci-5.1', 'arasan,sdhci-5.1', 'third']) + node = Node({'compatible': prop}) + self.assertEqual(('rockchip_rk3399_sdhci_5_1', + ['arasan_sdhci_5_1', 'third']), + get_compat_name(node)) + + def test_empty_file(self): + """Test output from a device tree file with no nodes""" + dtb_file = get_dtb_file('dtoc_test_empty.dts') + output = tools.GetOutputFilename('output') + dtb_platdata.run_steps(['struct'], dtb_file, False, output) + with open(output) as infile: + lines = infile.read().splitlines() + self.assertEqual(HEADER.splitlines(), lines) + + dtb_platdata.run_steps(['platdata'], dtb_file, False, output) + with open(output) as infile: + lines = infile.read().splitlines() + self.assertEqual(C_HEADER.splitlines() + [''], lines) + + def test_simple(self): + """Test output from some simple nodes with various types of data""" + dtb_file = get_dtb_file('dtoc_test_simple.dts') + output = tools.GetOutputFilename('output') + dtb_platdata.run_steps(['struct'], dtb_file, False, output) + with open(output) as infile: + data = infile.read() + self._CheckStrings(HEADER + ''' +struct dtd_sandbox_i2c_test { +}; +struct dtd_sandbox_pmic_test { +\tbool\t\tlow_power; +\tfdt64_t\t\treg[2]; +}; +struct dtd_sandbox_spl_test { +\tbool\t\tboolval; +\tunsigned char\tbytearray[3]; +\tunsigned char\tbyteval; +\tfdt32_t\t\tintarray[4]; +\tfdt32_t\t\tintval; +\tunsigned char\tlongbytearray[9]; +\tunsigned char\tnotstring[5]; +\tconst char *\tstringarray[3]; +\tconst char *\tstringval; +}; +struct dtd_sandbox_spl_test_2 { +}; +''', data) + + dtb_platdata.run_steps(['platdata'], dtb_file, False, output) + with open(output) as infile: + data = infile.read() + self._CheckStrings(C_HEADER + ''' +static struct dtd_sandbox_spl_test dtv_spl_test = { +\t.bytearray\t\t= {0x6, 0x0, 0x0}, +\t.byteval\t\t= 0x5, +\t.intval\t\t\t= 0x1, +\t.notstring\t\t= {0x20, 0x21, 0x22, 0x10, 0x0}, +\t.longbytearray\t\t= {0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, 0x10, +\t\t0x11}, +\t.stringval\t\t= "message", +\t.boolval\t\t= true, +\t.intarray\t\t= {0x2, 0x3, 0x4, 0x0}, +\t.stringarray\t\t= {"multi-word", "message", ""}, +}; +U_BOOT_DEVICE(spl_test) = { +\t.name\t\t= "sandbox_spl_test", +\t.platdata\t= &dtv_spl_test, +\t.platdata_size\t= sizeof(dtv_spl_test), +}; + +static struct dtd_sandbox_spl_test dtv_spl_test2 = { +\t.bytearray\t\t= {0x1, 0x23, 0x34}, +\t.byteval\t\t= 0x8, +\t.intval\t\t\t= 0x3, +\t.longbytearray\t\t= {0x9, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, +\t\t0x0}, +\t.stringval\t\t= "message2", +\t.intarray\t\t= {0x5, 0x0, 0x0, 0x0}, +\t.stringarray\t\t= {"another", "multi-word", "message"}, +}; +U_BOOT_DEVICE(spl_test2) = { +\t.name\t\t= "sandbox_spl_test", +\t.platdata\t= &dtv_spl_test2, +\t.platdata_size\t= sizeof(dtv_spl_test2), +}; + +static struct dtd_sandbox_spl_test dtv_spl_test3 = { +\t.stringarray\t\t= {"one", "", ""}, +}; +U_BOOT_DEVICE(spl_test3) = { +\t.name\t\t= "sandbox_spl_test", +\t.platdata\t= &dtv_spl_test3, +\t.platdata_size\t= sizeof(dtv_spl_test3), +}; + +static struct dtd_sandbox_spl_test_2 dtv_spl_test4 = { +}; +U_BOOT_DEVICE(spl_test4) = { +\t.name\t\t= "sandbox_spl_test_2", +\t.platdata\t= &dtv_spl_test4, +\t.platdata_size\t= sizeof(dtv_spl_test4), +}; + +static struct dtd_sandbox_i2c_test dtv_i2c_at_0 = { +}; +U_BOOT_DEVICE(i2c_at_0) = { +\t.name\t\t= "sandbox_i2c_test", +\t.platdata\t= &dtv_i2c_at_0, +\t.platdata_size\t= sizeof(dtv_i2c_at_0), +}; + +static struct dtd_sandbox_pmic_test dtv_pmic_at_9 = { +\t.low_power\t\t= true, +\t.reg\t\t\t= {0x9, 0x0}, +}; +U_BOOT_DEVICE(pmic_at_9) = { +\t.name\t\t= "sandbox_pmic_test", +\t.platdata\t= &dtv_pmic_at_9, +\t.platdata_size\t= sizeof(dtv_pmic_at_9), +}; + +''', data) + + def test_phandle(self): + """Test output from a node containing a phandle reference""" + dtb_file = get_dtb_file('dtoc_test_phandle.dts') + output = tools.GetOutputFilename('output') + dtb_platdata.run_steps(['struct'], dtb_file, False, output) + with open(output) as infile: + data = infile.read() + self._CheckStrings(HEADER + ''' +struct dtd_source { +\tstruct phandle_2_arg clocks[4]; +}; +struct dtd_target { +\tfdt32_t\t\tintval; +}; +''', data) + + dtb_platdata.run_steps(['platdata'], dtb_file, False, output) + with open(output) as infile: + data = infile.read() + self._CheckStrings(C_HEADER + ''' +static struct dtd_target dtv_phandle_target = { +\t.intval\t\t\t= 0x0, +}; +U_BOOT_DEVICE(phandle_target) = { +\t.name\t\t= "target", +\t.platdata\t= &dtv_phandle_target, +\t.platdata_size\t= sizeof(dtv_phandle_target), +}; + +static struct dtd_target dtv_phandle2_target = { +\t.intval\t\t\t= 0x1, +}; +U_BOOT_DEVICE(phandle2_target) = { +\t.name\t\t= "target", +\t.platdata\t= &dtv_phandle2_target, +\t.platdata_size\t= sizeof(dtv_phandle2_target), +}; + +static struct dtd_target dtv_phandle3_target = { +\t.intval\t\t\t= 0x2, +}; +U_BOOT_DEVICE(phandle3_target) = { +\t.name\t\t= "target", +\t.platdata\t= &dtv_phandle3_target, +\t.platdata_size\t= sizeof(dtv_phandle3_target), +}; + +static struct dtd_source dtv_phandle_source = { +\t.clocks\t\t\t= { +\t\t\t{&dtv_phandle_target, {}}, +\t\t\t{&dtv_phandle2_target, {11}}, +\t\t\t{&dtv_phandle3_target, {12, 13}}, +\t\t\t{&dtv_phandle_target, {}},}, +}; +U_BOOT_DEVICE(phandle_source) = { +\t.name\t\t= "source", +\t.platdata\t= &dtv_phandle_source, +\t.platdata_size\t= sizeof(dtv_phandle_source), +}; + +static struct dtd_source dtv_phandle_source2 = { +\t.clocks\t\t\t= { +\t\t\t{&dtv_phandle_target, {}},}, +}; +U_BOOT_DEVICE(phandle_source2) = { +\t.name\t\t= "source", +\t.platdata\t= &dtv_phandle_source2, +\t.platdata_size\t= sizeof(dtv_phandle_source2), +}; + +''', data) + + def test_phandle_single(self): + """Test output from a node containing a phandle reference""" + dtb_file = get_dtb_file('dtoc_test_phandle_single.dts') + output = tools.GetOutputFilename('output') + dtb_platdata.run_steps(['struct'], dtb_file, False, output) + with open(output) as infile: + data = infile.read() + self._CheckStrings(HEADER + ''' +struct dtd_source { +\tstruct phandle_0_arg clocks[1]; +}; +struct dtd_target { +\tfdt32_t\t\tintval; +}; +''', data) + + def test_phandle_reorder(self): + """Test that phandle targets are generated before their references""" + dtb_file = get_dtb_file('dtoc_test_phandle_reorder.dts') + output = tools.GetOutputFilename('output') + dtb_platdata.run_steps(['platdata'], dtb_file, False, output) + with open(output) as infile: + data = infile.read() + self._CheckStrings(C_HEADER + ''' +static struct dtd_target dtv_phandle_target = { +}; +U_BOOT_DEVICE(phandle_target) = { +\t.name\t\t= "target", +\t.platdata\t= &dtv_phandle_target, +\t.platdata_size\t= sizeof(dtv_phandle_target), +}; + +static struct dtd_source dtv_phandle_source2 = { +\t.clocks\t\t\t= { +\t\t\t{&dtv_phandle_target, {}},}, +}; +U_BOOT_DEVICE(phandle_source2) = { +\t.name\t\t= "source", +\t.platdata\t= &dtv_phandle_source2, +\t.platdata_size\t= sizeof(dtv_phandle_source2), +}; + +''', data) + + def test_phandle_bad(self): + """Test a node containing an invalid phandle fails""" + dtb_file = get_dtb_file('dtoc_test_phandle_bad.dts', + capture_stderr=True) + output = tools.GetOutputFilename('output') + with self.assertRaises(ValueError) as e: + dtb_platdata.run_steps(['struct'], dtb_file, False, output) + self.assertIn("Cannot parse 'clocks' in node 'phandle-source'", + str(e.exception)) + + def test_phandle_bad2(self): + """Test a phandle target missing its #*-cells property""" + dtb_file = get_dtb_file('dtoc_test_phandle_bad2.dts', + capture_stderr=True) + output = tools.GetOutputFilename('output') + with self.assertRaises(ValueError) as e: + dtb_platdata.run_steps(['struct'], dtb_file, False, output) + self.assertIn("Node 'phandle-target' has no '#clock-cells' property", + str(e.exception)) + + def test_aliases(self): + """Test output from a node with multiple compatible strings""" + dtb_file = get_dtb_file('dtoc_test_aliases.dts') + output = tools.GetOutputFilename('output') + dtb_platdata.run_steps(['struct'], dtb_file, False, output) + with open(output) as infile: + data = infile.read() + self._CheckStrings(HEADER + ''' +struct dtd_compat1 { +\tfdt32_t\t\tintval; +}; +#define dtd_compat2_1_fred dtd_compat1 +#define dtd_compat3 dtd_compat1 +''', data) + + dtb_platdata.run_steps(['platdata'], dtb_file, False, output) + with open(output) as infile: + data = infile.read() + self._CheckStrings(C_HEADER + ''' +static struct dtd_compat1 dtv_spl_test = { +\t.intval\t\t\t= 0x1, +}; +U_BOOT_DEVICE(spl_test) = { +\t.name\t\t= "compat1", +\t.platdata\t= &dtv_spl_test, +\t.platdata_size\t= sizeof(dtv_spl_test), +}; + +''', data) + + def test_addresses64(self): + """Test output from a node with a 'reg' property with na=2, ns=2""" + dtb_file = get_dtb_file('dtoc_test_addr64.dts') + output = tools.GetOutputFilename('output') + dtb_platdata.run_steps(['struct'], dtb_file, False, output) + with open(output) as infile: + data = infile.read() + self._CheckStrings(HEADER + ''' +struct dtd_test1 { +\tfdt64_t\t\treg[2]; +}; +struct dtd_test2 { +\tfdt64_t\t\treg[2]; +}; +struct dtd_test3 { +\tfdt64_t\t\treg[4]; +}; +''', data) + + dtb_platdata.run_steps(['platdata'], dtb_file, False, output) + with open(output) as infile: + data = infile.read() + self._CheckStrings(C_HEADER + ''' +static struct dtd_test1 dtv_test1 = { +\t.reg\t\t\t= {0x1234, 0x5678}, +}; +U_BOOT_DEVICE(test1) = { +\t.name\t\t= "test1", +\t.platdata\t= &dtv_test1, +\t.platdata_size\t= sizeof(dtv_test1), +}; + +static struct dtd_test2 dtv_test2 = { +\t.reg\t\t\t= {0x1234567890123456, 0x9876543210987654}, +}; +U_BOOT_DEVICE(test2) = { +\t.name\t\t= "test2", +\t.platdata\t= &dtv_test2, +\t.platdata_size\t= sizeof(dtv_test2), +}; + +static struct dtd_test3 dtv_test3 = { +\t.reg\t\t\t= {0x1234567890123456, 0x9876543210987654, 0x2, 0x3}, +}; +U_BOOT_DEVICE(test3) = { +\t.name\t\t= "test3", +\t.platdata\t= &dtv_test3, +\t.platdata_size\t= sizeof(dtv_test3), +}; + +''', data) + + def test_addresses32(self): + """Test output from a node with a 'reg' property with na=1, ns=1""" + dtb_file = get_dtb_file('dtoc_test_addr32.dts') + output = tools.GetOutputFilename('output') + dtb_platdata.run_steps(['struct'], dtb_file, False, output) + with open(output) as infile: + data = infile.read() + self._CheckStrings(HEADER + ''' +struct dtd_test1 { +\tfdt32_t\t\treg[2]; +}; +struct dtd_test2 { +\tfdt32_t\t\treg[4]; +}; +''', data) + + dtb_platdata.run_steps(['platdata'], dtb_file, False, output) + with open(output) as infile: + data = infile.read() + self._CheckStrings(C_HEADER + ''' +static struct dtd_test1 dtv_test1 = { +\t.reg\t\t\t= {0x1234, 0x5678}, +}; +U_BOOT_DEVICE(test1) = { +\t.name\t\t= "test1", +\t.platdata\t= &dtv_test1, +\t.platdata_size\t= sizeof(dtv_test1), +}; + +static struct dtd_test2 dtv_test2 = { +\t.reg\t\t\t= {0x12345678, 0x98765432, 0x2, 0x3}, +}; +U_BOOT_DEVICE(test2) = { +\t.name\t\t= "test2", +\t.platdata\t= &dtv_test2, +\t.platdata_size\t= sizeof(dtv_test2), +}; + +''', data) + + def test_addresses64_32(self): + """Test output from a node with a 'reg' property with na=2, ns=1""" + dtb_file = get_dtb_file('dtoc_test_addr64_32.dts') + output = tools.GetOutputFilename('output') + dtb_platdata.run_steps(['struct'], dtb_file, False, output) + with open(output) as infile: + data = infile.read() + self._CheckStrings(HEADER + ''' +struct dtd_test1 { +\tfdt64_t\t\treg[2]; +}; +struct dtd_test2 { +\tfdt64_t\t\treg[2]; +}; +struct dtd_test3 { +\tfdt64_t\t\treg[4]; +}; +''', data) + + dtb_platdata.run_steps(['platdata'], dtb_file, False, output) + with open(output) as infile: + data = infile.read() + self._CheckStrings(C_HEADER + ''' +static struct dtd_test1 dtv_test1 = { +\t.reg\t\t\t= {0x123400000000, 0x5678}, +}; +U_BOOT_DEVICE(test1) = { +\t.name\t\t= "test1", +\t.platdata\t= &dtv_test1, +\t.platdata_size\t= sizeof(dtv_test1), +}; + +static struct dtd_test2 dtv_test2 = { +\t.reg\t\t\t= {0x1234567890123456, 0x98765432}, +}; +U_BOOT_DEVICE(test2) = { +\t.name\t\t= "test2", +\t.platdata\t= &dtv_test2, +\t.platdata_size\t= sizeof(dtv_test2), +}; + +static struct dtd_test3 dtv_test3 = { +\t.reg\t\t\t= {0x1234567890123456, 0x98765432, 0x2, 0x3}, +}; +U_BOOT_DEVICE(test3) = { +\t.name\t\t= "test3", +\t.platdata\t= &dtv_test3, +\t.platdata_size\t= sizeof(dtv_test3), +}; + +''', data) + + def test_addresses32_64(self): + """Test output from a node with a 'reg' property with na=1, ns=2""" + dtb_file = get_dtb_file('dtoc_test_addr32_64.dts') + output = tools.GetOutputFilename('output') + dtb_platdata.run_steps(['struct'], dtb_file, False, output) + with open(output) as infile: + data = infile.read() + self._CheckStrings(HEADER + ''' +struct dtd_test1 { +\tfdt64_t\t\treg[2]; +}; +struct dtd_test2 { +\tfdt64_t\t\treg[2]; +}; +struct dtd_test3 { +\tfdt64_t\t\treg[4]; +}; +''', data) + + dtb_platdata.run_steps(['platdata'], dtb_file, False, output) + with open(output) as infile: + data = infile.read() + self._CheckStrings(C_HEADER + ''' +static struct dtd_test1 dtv_test1 = { +\t.reg\t\t\t= {0x1234, 0x567800000000}, +}; +U_BOOT_DEVICE(test1) = { +\t.name\t\t= "test1", +\t.platdata\t= &dtv_test1, +\t.platdata_size\t= sizeof(dtv_test1), +}; + +static struct dtd_test2 dtv_test2 = { +\t.reg\t\t\t= {0x12345678, 0x9876543210987654}, +}; +U_BOOT_DEVICE(test2) = { +\t.name\t\t= "test2", +\t.platdata\t= &dtv_test2, +\t.platdata_size\t= sizeof(dtv_test2), +}; + +static struct dtd_test3 dtv_test3 = { +\t.reg\t\t\t= {0x12345678, 0x9876543210987654, 0x2, 0x3}, +}; +U_BOOT_DEVICE(test3) = { +\t.name\t\t= "test3", +\t.platdata\t= &dtv_test3, +\t.platdata_size\t= sizeof(dtv_test3), +}; + +''', data) + + def test_bad_reg(self): + """Test that a reg property with an invalid type generates an error""" + # Capture stderr since dtc will emit warnings for this file + dtb_file = get_dtb_file('dtoc_test_bad_reg.dts', capture_stderr=True) + output = tools.GetOutputFilename('output') + with self.assertRaises(ValueError) as e: + dtb_platdata.run_steps(['struct'], dtb_file, False, output) + self.assertIn("Node 'spl-test' reg property is not an int", + str(e.exception)) + + def test_bad_reg2(self): + """Test that a reg property with an invalid cell count is detected""" + # Capture stderr since dtc will emit warnings for this file + dtb_file = get_dtb_file('dtoc_test_bad_reg2.dts', capture_stderr=True) + output = tools.GetOutputFilename('output') + with self.assertRaises(ValueError) as e: + dtb_platdata.run_steps(['struct'], dtb_file, False, output) + self.assertIn("Node 'spl-test' reg property has 3 cells which is not a multiple of na + ns = 1 + 1)", + str(e.exception)) + + def test_add_prop(self): + """Test that a subequent node can add a new property to a struct""" + dtb_file = get_dtb_file('dtoc_test_add_prop.dts') + output = tools.GetOutputFilename('output') + dtb_platdata.run_steps(['struct'], dtb_file, False, output) + with open(output) as infile: + data = infile.read() + self._CheckStrings(HEADER + ''' +struct dtd_sandbox_spl_test { +\tfdt32_t\t\tintarray; +\tfdt32_t\t\tintval; +}; +''', data) + + dtb_platdata.run_steps(['platdata'], dtb_file, False, output) + with open(output) as infile: + data = infile.read() + self._CheckStrings(C_HEADER + ''' +static struct dtd_sandbox_spl_test dtv_spl_test = { +\t.intval\t\t\t= 0x1, +}; +U_BOOT_DEVICE(spl_test) = { +\t.name\t\t= "sandbox_spl_test", +\t.platdata\t= &dtv_spl_test, +\t.platdata_size\t= sizeof(dtv_spl_test), +}; + +static struct dtd_sandbox_spl_test dtv_spl_test2 = { +\t.intarray\t\t= 0x5, +}; +U_BOOT_DEVICE(spl_test2) = { +\t.name\t\t= "sandbox_spl_test", +\t.platdata\t= &dtv_spl_test2, +\t.platdata_size\t= sizeof(dtv_spl_test2), +}; + +''', data) + + def testStdout(self): + """Test output to stdout""" + dtb_file = get_dtb_file('dtoc_test_simple.dts') + with test_util.capture_sys_output() as (stdout, stderr): + dtb_platdata.run_steps(['struct'], dtb_file, False, '-') + + def testNoCommand(self): + """Test running dtoc without a command""" + with self.assertRaises(ValueError) as e: + dtb_platdata.run_steps([], '', False, '') + self.assertIn("Please specify a command: struct, platdata", + str(e.exception)) + + def testBadCommand(self): + """Test running dtoc with an invalid command""" + dtb_file = get_dtb_file('dtoc_test_simple.dts') + output = tools.GetOutputFilename('output') + with self.assertRaises(ValueError) as e: + dtb_platdata.run_steps(['invalid-cmd'], dtb_file, False, output) + self.assertIn("Unknown command 'invalid-cmd': (use: struct, platdata)", + str(e.exception)) diff --git a/tools/u-boot-tools/dtoc/test_fdt b/tools/u-boot-tools/dtoc/test_fdt new file mode 120000 index 0000000..7c3b230 --- /dev/null +++ b/tools/u-boot-tools/dtoc/test_fdt @@ -0,0 +1 @@ +test_fdt.py \ No newline at end of file diff --git a/tools/u-boot-tools/dtoc/test_fdt.py b/tools/u-boot-tools/dtoc/test_fdt.py new file mode 100755 index 0000000..8d70dd2 --- /dev/null +++ b/tools/u-boot-tools/dtoc/test_fdt.py @@ -0,0 +1,562 @@ +#!/usr/bin/python +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (c) 2018 Google, Inc +# Written by Simon Glass <sjg@chromium.org> +# + +from optparse import OptionParser +import glob +import os +import sys +import unittest + +# Bring in the patman libraries +our_path = os.path.dirname(os.path.realpath(__file__)) +for dirname in ['../patman', '..']: + sys.path.insert(0, os.path.join(our_path, dirname)) + +import command +import fdt +from fdt import TYPE_BYTE, TYPE_INT, TYPE_STRING, TYPE_BOOL +import fdt_util +from fdt_util import fdt32_to_cpu +import libfdt +import test_util +import tools + +def _GetPropertyValue(dtb, node, prop_name): + """Low-level function to get the property value based on its offset + + This looks directly in the device tree at the property's offset to find + its value. It is useful as a check that the property is in the correct + place. + + Args: + node: Node to look in + prop_name: Property name to find + + Returns: + Tuple: + Prop object found + Value of property as a string (found using property offset) + """ + prop = node.props[prop_name] + + # Add 12, which is sizeof(struct fdt_property), to get to start of data + offset = prop.GetOffset() + 12 + data = dtb.GetContents()[offset:offset + len(prop.value)] + return prop, [chr(x) for x in data] + + +class TestFdt(unittest.TestCase): + """Tests for the Fdt module + + This includes unit tests for some functions and functional tests for the fdt + module. + """ + @classmethod + def setUpClass(cls): + tools.PrepareOutputDir(None) + + @classmethod + def tearDownClass(cls): + tools.FinaliseOutputDir() + + def setUp(self): + self.dtb = fdt.FdtScan('tools/dtoc/dtoc_test_simple.dts') + + def testFdt(self): + """Test that we can open an Fdt""" + self.dtb.Scan() + root = self.dtb.GetRoot() + self.assertTrue(isinstance(root, fdt.Node)) + + def testGetNode(self): + """Test the GetNode() method""" + node = self.dtb.GetNode('/spl-test') + self.assertTrue(isinstance(node, fdt.Node)) + node = self.dtb.GetNode('/i2c@0/pmic@9') + self.assertTrue(isinstance(node, fdt.Node)) + self.assertEqual('pmic@9', node.name) + self.assertIsNone(self.dtb.GetNode('/i2c@0/pmic@9/missing')) + + def testFlush(self): + """Check that we can flush the device tree out to its file""" + fname = self.dtb._fname + with open(fname) as fd: + data = fd.read() + os.remove(fname) + with self.assertRaises(IOError): + open(fname) + self.dtb.Flush() + with open(fname) as fd: + data = fd.read() + + def testPack(self): + """Test that packing a device tree works""" + self.dtb.Pack() + + def testGetFdt(self): + """Tetst that we can access the raw device-tree data""" + self.assertTrue(isinstance(self.dtb.GetContents(), bytearray)) + + def testGetProps(self): + """Tests obtaining a list of properties""" + node = self.dtb.GetNode('/spl-test') + props = self.dtb.GetProps(node) + self.assertEqual(['boolval', 'bytearray', 'byteval', 'compatible', + 'intarray', 'intval', 'longbytearray', 'notstring', + 'stringarray', 'stringval', 'u-boot,dm-pre-reloc'], + sorted(props.keys())) + + def testCheckError(self): + """Tests the ChecKError() function""" + with self.assertRaises(ValueError) as e: + fdt.CheckErr(-libfdt.NOTFOUND, 'hello') + self.assertIn('FDT_ERR_NOTFOUND: hello', str(e.exception)) + + def testGetFdt(self): + node = self.dtb.GetNode('/spl-test') + self.assertEqual(self.dtb, node.GetFdt()) + +class TestNode(unittest.TestCase): + """Test operation of the Node class""" + + @classmethod + def setUpClass(cls): + tools.PrepareOutputDir(None) + + @classmethod + def tearDownClass(cls): + tools.FinaliseOutputDir() + + def setUp(self): + self.dtb = fdt.FdtScan('tools/dtoc/dtoc_test_simple.dts') + self.node = self.dtb.GetNode('/spl-test') + + def testOffset(self): + """Tests that we can obtain the offset of a node""" + self.assertTrue(self.node.Offset() > 0) + + def testDelete(self): + """Tests that we can delete a property""" + node2 = self.dtb.GetNode('/spl-test2') + offset1 = node2.Offset() + self.node.DeleteProp('intval') + offset2 = node2.Offset() + self.assertTrue(offset2 < offset1) + self.node.DeleteProp('intarray') + offset3 = node2.Offset() + self.assertTrue(offset3 < offset2) + with self.assertRaises(libfdt.FdtException): + self.node.DeleteProp('missing') + + def testDeleteGetOffset(self): + """Test that property offset update when properties are deleted""" + self.node.DeleteProp('intval') + prop, value = _GetPropertyValue(self.dtb, self.node, 'longbytearray') + self.assertEqual(prop.value, value) + + def testFindNode(self): + """Tests that we can find a node using the FindNode() functoin""" + node = self.dtb.GetRoot().FindNode('i2c@0') + self.assertEqual('i2c@0', node.name) + subnode = node.FindNode('pmic@9') + self.assertEqual('pmic@9', subnode.name) + self.assertEqual(None, node.FindNode('missing')) + + def testRefreshMissingNode(self): + """Test refreshing offsets when an extra node is present in dtb""" + # Delete it from our tables, not the device tree + del self.dtb._root.subnodes[-1] + with self.assertRaises(ValueError) as e: + self.dtb.Refresh() + self.assertIn('Internal error, offset', str(e.exception)) + + def testRefreshExtraNode(self): + """Test refreshing offsets when an expected node is missing""" + # Delete it from the device tre, not our tables + self.dtb.GetFdtObj().del_node(self.node.Offset()) + with self.assertRaises(ValueError) as e: + self.dtb.Refresh() + self.assertIn('Internal error, node name mismatch ' + 'spl-test != spl-test2', str(e.exception)) + + def testRefreshMissingProp(self): + """Test refreshing offsets when an extra property is present in dtb""" + # Delete it from our tables, not the device tree + del self.node.props['notstring'] + with self.assertRaises(ValueError) as e: + self.dtb.Refresh() + self.assertIn("Internal error, property 'notstring' missing, offset ", + str(e.exception)) + + def testLookupPhandle(self): + """Test looking up a single phandle""" + dtb = fdt.FdtScan('tools/dtoc/dtoc_test_phandle.dts') + node = dtb.GetNode('/phandle-source2') + prop = node.props['clocks'] + target = dtb.GetNode('/phandle-target') + self.assertEqual(target, dtb.LookupPhandle(fdt32_to_cpu(prop.value))) + + +class TestProp(unittest.TestCase): + """Test operation of the Prop class""" + + @classmethod + def setUpClass(cls): + tools.PrepareOutputDir(None) + + @classmethod + def tearDownClass(cls): + tools.FinaliseOutputDir() + + def setUp(self): + self.dtb = fdt.FdtScan('tools/dtoc/dtoc_test_simple.dts') + self.node = self.dtb.GetNode('/spl-test') + self.fdt = self.dtb.GetFdtObj() + + def testMissingNode(self): + self.assertEqual(None, self.dtb.GetNode('missing')) + + def testPhandle(self): + dtb = fdt.FdtScan('tools/dtoc/dtoc_test_phandle.dts') + node = dtb.GetNode('/phandle-source2') + prop = node.props['clocks'] + self.assertTrue(fdt32_to_cpu(prop.value) > 0) + + def _ConvertProp(self, prop_name): + """Helper function to look up a property in self.node and return it + + Args: + Property name to find + + Return fdt.Prop object for this property + """ + p = self.fdt.getprop(self.node.Offset(), prop_name) + return fdt.Prop(self.node, -1, prop_name, p) + + def testMakeProp(self): + """Test we can convert all the the types that are supported""" + prop = self._ConvertProp('boolval') + self.assertEqual(fdt.TYPE_BOOL, prop.type) + self.assertEqual(True, prop.value) + + prop = self._ConvertProp('intval') + self.assertEqual(fdt.TYPE_INT, prop.type) + self.assertEqual(1, fdt32_to_cpu(prop.value)) + + prop = self._ConvertProp('intarray') + self.assertEqual(fdt.TYPE_INT, prop.type) + val = [fdt32_to_cpu(val) for val in prop.value] + self.assertEqual([2, 3, 4], val) + + prop = self._ConvertProp('byteval') + self.assertEqual(fdt.TYPE_BYTE, prop.type) + self.assertEqual(5, ord(prop.value)) + + prop = self._ConvertProp('longbytearray') + self.assertEqual(fdt.TYPE_BYTE, prop.type) + val = [ord(val) for val in prop.value] + self.assertEqual([9, 10, 11, 12, 13, 14, 15, 16, 17], val) + + prop = self._ConvertProp('stringval') + self.assertEqual(fdt.TYPE_STRING, prop.type) + self.assertEqual('message', prop.value) + + prop = self._ConvertProp('stringarray') + self.assertEqual(fdt.TYPE_STRING, prop.type) + self.assertEqual(['multi-word', 'message'], prop.value) + + prop = self._ConvertProp('notstring') + self.assertEqual(fdt.TYPE_BYTE, prop.type) + val = [ord(val) for val in prop.value] + self.assertEqual([0x20, 0x21, 0x22, 0x10, 0], val) + + def testGetEmpty(self): + """Tests the GetEmpty() function for the various supported types""" + self.assertEqual(True, fdt.Prop.GetEmpty(fdt.TYPE_BOOL)) + self.assertEqual(chr(0), fdt.Prop.GetEmpty(fdt.TYPE_BYTE)) + self.assertEqual(chr(0) * 4, fdt.Prop.GetEmpty(fdt.TYPE_INT)) + self.assertEqual('', fdt.Prop.GetEmpty(fdt.TYPE_STRING)) + + def testGetOffset(self): + """Test we can get the offset of a property""" + prop, value = _GetPropertyValue(self.dtb, self.node, 'longbytearray') + self.assertEqual(prop.value, value) + + def testWiden(self): + """Test widening of values""" + node2 = self.dtb.GetNode('/spl-test2') + prop = self.node.props['intval'] + + # No action + prop2 = node2.props['intval'] + prop.Widen(prop2) + self.assertEqual(fdt.TYPE_INT, prop.type) + self.assertEqual(1, fdt32_to_cpu(prop.value)) + + # Convert singla value to array + prop2 = self.node.props['intarray'] + prop.Widen(prop2) + self.assertEqual(fdt.TYPE_INT, prop.type) + self.assertTrue(isinstance(prop.value, list)) + + # A 4-byte array looks like a single integer. When widened by a longer + # byte array, it should turn into an array. + prop = self.node.props['longbytearray'] + prop2 = node2.props['longbytearray'] + self.assertFalse(isinstance(prop2.value, list)) + self.assertEqual(4, len(prop2.value)) + prop2.Widen(prop) + self.assertTrue(isinstance(prop2.value, list)) + self.assertEqual(9, len(prop2.value)) + + # Similarly for a string array + prop = self.node.props['stringval'] + prop2 = node2.props['stringarray'] + self.assertFalse(isinstance(prop.value, list)) + self.assertEqual(7, len(prop.value)) + prop.Widen(prop2) + self.assertTrue(isinstance(prop.value, list)) + self.assertEqual(3, len(prop.value)) + + # Enlarging an existing array + prop = self.node.props['stringarray'] + prop2 = node2.props['stringarray'] + self.assertTrue(isinstance(prop.value, list)) + self.assertEqual(2, len(prop.value)) + prop.Widen(prop2) + self.assertTrue(isinstance(prop.value, list)) + self.assertEqual(3, len(prop.value)) + + def testAdd(self): + """Test adding properties""" + self.fdt.pack() + # This function should automatically expand the device tree + self.node.AddZeroProp('one') + self.node.AddZeroProp('two') + self.node.AddZeroProp('three') + self.dtb.Sync(auto_resize=True) + + # Updating existing properties should be OK, since the device-tree size + # does not change + self.fdt.pack() + self.node.SetInt('one', 1) + self.node.SetInt('two', 2) + self.node.SetInt('three', 3) + self.dtb.Sync(auto_resize=False) + + # This should fail since it would need to increase the device-tree size + self.node.AddZeroProp('four') + with self.assertRaises(libfdt.FdtException) as e: + self.dtb.Sync(auto_resize=False) + self.assertIn('FDT_ERR_NOSPACE', str(e.exception)) + self.dtb.Sync(auto_resize=True) + + def testAddNode(self): + self.fdt.pack() + self.node.AddSubnode('subnode') + with self.assertRaises(libfdt.FdtException) as e: + self.dtb.Sync(auto_resize=False) + self.assertIn('FDT_ERR_NOSPACE', str(e.exception)) + + self.dtb.Sync(auto_resize=True) + offset = self.fdt.path_offset('/spl-test/subnode') + self.assertTrue(offset > 0) + + def testAddMore(self): + """Test various other methods for adding and setting properties""" + self.node.AddZeroProp('one') + self.dtb.Sync(auto_resize=True) + data = self.fdt.getprop(self.node.Offset(), 'one') + self.assertEqual(0, fdt32_to_cpu(data)) + + self.node.SetInt('one', 1) + self.dtb.Sync(auto_resize=False) + data = self.fdt.getprop(self.node.Offset(), 'one') + self.assertEqual(1, fdt32_to_cpu(data)) + + val = '123' + chr(0) + '456' + self.node.AddString('string', val) + self.dtb.Sync(auto_resize=True) + data = self.fdt.getprop(self.node.Offset(), 'string') + self.assertEqual(val + '\0', data) + + self.fdt.pack() + self.node.SetString('string', val + 'x') + with self.assertRaises(libfdt.FdtException) as e: + self.dtb.Sync(auto_resize=False) + self.assertIn('FDT_ERR_NOSPACE', str(e.exception)) + self.node.SetString('string', val[:-1]) + + prop = self.node.props['string'] + prop.SetData(val) + self.dtb.Sync(auto_resize=False) + data = self.fdt.getprop(self.node.Offset(), 'string') + self.assertEqual(val, data) + + self.node.AddEmptyProp('empty', 5) + self.dtb.Sync(auto_resize=True) + prop = self.node.props['empty'] + prop.SetData(val) + self.dtb.Sync(auto_resize=False) + data = self.fdt.getprop(self.node.Offset(), 'empty') + self.assertEqual(val, data) + + self.node.SetData('empty', '123') + self.assertEqual('123', prop.bytes) + + def testFromData(self): + dtb2 = fdt.Fdt.FromData(self.dtb.GetContents()) + self.assertEqual(dtb2.GetContents(), self.dtb.GetContents()) + + self.node.AddEmptyProp('empty', 5) + self.dtb.Sync(auto_resize=True) + self.assertTrue(dtb2.GetContents() != self.dtb.GetContents()) + + +class TestFdtUtil(unittest.TestCase): + """Tests for the fdt_util module + + This module will likely be mostly replaced at some point, once upstream + libfdt has better Python support. For now, this provides tests for current + functionality. + """ + @classmethod + def setUpClass(cls): + tools.PrepareOutputDir(None) + + @classmethod + def tearDownClass(cls): + tools.FinaliseOutputDir() + + def setUp(self): + self.dtb = fdt.FdtScan('tools/dtoc/dtoc_test_simple.dts') + self.node = self.dtb.GetNode('/spl-test') + + def testGetInt(self): + self.assertEqual(1, fdt_util.GetInt(self.node, 'intval')) + self.assertEqual(3, fdt_util.GetInt(self.node, 'missing', 3)) + + with self.assertRaises(ValueError) as e: + self.assertEqual(3, fdt_util.GetInt(self.node, 'intarray')) + self.assertIn("property 'intarray' has list value: expecting a single " + 'integer', str(e.exception)) + + def testGetString(self): + self.assertEqual('message', fdt_util.GetString(self.node, 'stringval')) + self.assertEqual('test', fdt_util.GetString(self.node, 'missing', + 'test')) + + with self.assertRaises(ValueError) as e: + self.assertEqual(3, fdt_util.GetString(self.node, 'stringarray')) + self.assertIn("property 'stringarray' has list value: expecting a " + 'single string', str(e.exception)) + + def testGetBool(self): + self.assertEqual(True, fdt_util.GetBool(self.node, 'boolval')) + self.assertEqual(False, fdt_util.GetBool(self.node, 'missing')) + self.assertEqual(True, fdt_util.GetBool(self.node, 'missing', True)) + self.assertEqual(False, fdt_util.GetBool(self.node, 'missing', False)) + + def testGetByte(self): + self.assertEqual(5, fdt_util.GetByte(self.node, 'byteval')) + self.assertEqual(3, fdt_util.GetByte(self.node, 'missing', 3)) + + with self.assertRaises(ValueError) as e: + fdt_util.GetByte(self.node, 'longbytearray') + self.assertIn("property 'longbytearray' has list value: expecting a " + 'single byte', str(e.exception)) + + with self.assertRaises(ValueError) as e: + fdt_util.GetByte(self.node, 'intval') + self.assertIn("property 'intval' has length 4, expecting 1", + str(e.exception)) + + def testGetPhandleList(self): + dtb = fdt.FdtScan('tools/dtoc/dtoc_test_phandle.dts') + node = dtb.GetNode('/phandle-source2') + self.assertEqual([1], fdt_util.GetPhandleList(node, 'clocks')) + node = dtb.GetNode('/phandle-source') + self.assertEqual([1, 2, 11, 3, 12, 13, 1], + fdt_util.GetPhandleList(node, 'clocks')) + self.assertEqual(None, fdt_util.GetPhandleList(node, 'missing')) + + def testGetDataType(self): + self.assertEqual(1, fdt_util.GetDatatype(self.node, 'intval', int)) + self.assertEqual('message', fdt_util.GetDatatype(self.node, 'stringval', + str)) + with self.assertRaises(ValueError) as e: + self.assertEqual(3, fdt_util.GetDatatype(self.node, 'boolval', + bool)) + def testFdtCellsToCpu(self): + val = self.node.props['intarray'].value + self.assertEqual(0, fdt_util.fdt_cells_to_cpu(val, 0)) + self.assertEqual(2, fdt_util.fdt_cells_to_cpu(val, 1)) + + dtb2 = fdt.FdtScan('tools/dtoc/dtoc_test_addr64.dts') + node2 = dtb2.GetNode('/test1') + val = node2.props['reg'].value + self.assertEqual(0x1234, fdt_util.fdt_cells_to_cpu(val, 2)) + + def testEnsureCompiled(self): + """Test a degenerate case of this function""" + dtb = fdt_util.EnsureCompiled('tools/dtoc/dtoc_test_simple.dts') + self.assertEqual(dtb, fdt_util.EnsureCompiled(dtb)) + + def testGetPlainBytes(self): + self.assertEqual('fred', fdt_util.get_plain_bytes('fred')) + + +def RunTestCoverage(): + """Run the tests and check that we get 100% coverage""" + test_util.RunTestCoverage('tools/dtoc/test_fdt.py', None, + ['tools/patman/*.py', '*test_fdt.py'], options.build_dir) + + +def RunTests(args): + """Run all the test we have for the fdt model + + Args: + args: List of positional args provided to fdt. This can hold a test + name to execute (as in 'fdt -t testFdt', for example) + """ + result = unittest.TestResult() + sys.argv = [sys.argv[0]] + test_name = args and args[0] or None + for module in (TestFdt, TestNode, TestProp, TestFdtUtil): + if test_name: + try: + suite = unittest.TestLoader().loadTestsFromName(test_name, module) + except AttributeError: + continue + else: + suite = unittest.TestLoader().loadTestsFromTestCase(module) + suite.run(result) + + print result + for _, err in result.errors: + print err + for _, err in result.failures: + print err + +if __name__ != '__main__': + sys.exit(1) + +parser = OptionParser() +parser.add_option('-B', '--build-dir', type='string', default='b', + help='Directory containing the build output') +parser.add_option('-P', '--processes', type=int, + help='set number of processes to use for running tests') +parser.add_option('-t', '--test', action='store_true', dest='test', + default=False, help='run tests') +parser.add_option('-T', '--test-coverage', action='store_true', + default=False, help='run tests and check for 100% coverage') +(options, args) = parser.parse_args() + +# Run our meagre tests +if options.test: + RunTests(args) +elif options.test_coverage: + RunTestCoverage() diff --git a/tools/u-boot-tools/dumpimage b/tools/u-boot-tools/dumpimage new file mode 100755 index 0000000000000000000000000000000000000000..ab612e30efe68ade449ca7b809ec133889ed006a GIT binary patch literal 242488 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�H?A0FA~4~{|^bO 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 GxC{XC<joWS literal 0 HcmV?d00001 diff --git a/tools/u-boot-tools/dumpimage.c b/tools/u-boot-tools/dumpimage.c new file mode 100644 index 0000000..7115df0 --- /dev/null +++ b/tools/u-boot-tools/dumpimage.c @@ -0,0 +1,210 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Based on mkimage.c. + * + * Written by Guilherme Maciel Ferreira <guilherme.maciel.ferreira@gmail.com> + */ + +#include "dumpimage.h" +#include <image.h> +#include <version.h> + +static void usage(void); + +/* parameters initialized by core will be used by the image type code */ +static struct image_tool_params params = { + .type = IH_TYPE_KERNEL, +}; + +/* + * dumpimage_extract_subimage - + * + * It scans all registered image types, + * verifies image_header for each supported image type + * if verification is successful, it extracts the desired file, + * indexed by pflag, from the image + * + * returns negative if input image format does not match with any of + * supported image types + */ +static int dumpimage_extract_subimage(struct image_type_params *tparams, + void *ptr, struct stat *sbuf) +{ + int retval = -1; + + if (tparams->verify_header) { + retval = tparams->verify_header((unsigned char *)ptr, + sbuf->st_size, ¶ms); + if (retval != 0) + return -1; + /* + * Extract the file from the image + * if verify is successful + */ + if (tparams->extract_subimage) { + retval = tparams->extract_subimage(ptr, ¶ms); + } else { + fprintf(stderr, + "%s: extract_subimage undefined for %s\n", + params.cmdname, tparams->name); + return -2; + } + } + + return retval; +} + +int main(int argc, char **argv) +{ + int opt; + int ifd = -1; + struct stat sbuf; + char *ptr; + int retval = 0; + struct image_type_params *tparams = NULL; + + params.cmdname = *argv; + + while ((opt = getopt(argc, argv, "li:o:T:p:V")) != -1) { + switch (opt) { + case 'l': + params.lflag = 1; + break; + case 'i': + params.imagefile = optarg; + params.iflag = 1; + break; + case 'o': + params.outfile = optarg; + break; + case 'T': + params.type = genimg_get_type_id(optarg); + if (params.type < 0) { + usage(); + } + break; + case 'p': + params.pflag = strtoul(optarg, &ptr, 10); + if (*ptr) { + fprintf(stderr, + "%s: invalid file position %s\n", + params.cmdname, *argv); + exit(EXIT_FAILURE); + } + break; + case 'V': + printf("dumpimage version %s\n", PLAIN_VERSION); + exit(EXIT_SUCCESS); + default: + usage(); + break; + } + } + + if (optind >= argc) + usage(); + + /* set tparams as per input type_id */ + tparams = imagetool_get_type(params.type); + if (tparams == NULL) { + fprintf(stderr, "%s: unsupported type: %s\n", + params.cmdname, genimg_get_type_name(params.type)); + exit(EXIT_FAILURE); + } + + /* + * check the passed arguments parameters meets the requirements + * as per image type to be generated/listed + */ + if (tparams->check_params) { + if (tparams->check_params(¶ms)) + usage(); + } + + if (params.iflag) + params.datafile = argv[optind]; + else + params.imagefile = argv[optind]; + if (!params.outfile) + params.outfile = params.datafile; + + ifd = open(params.imagefile, O_RDONLY|O_BINARY); + if (ifd < 0) { + fprintf(stderr, "%s: Can't open \"%s\": %s\n", + params.cmdname, params.imagefile, + strerror(errno)); + exit(EXIT_FAILURE); + } + + if (params.lflag || params.iflag) { + if (fstat(ifd, &sbuf) < 0) { + fprintf(stderr, "%s: Can't stat \"%s\": %s\n", + params.cmdname, params.imagefile, + strerror(errno)); + exit(EXIT_FAILURE); + } + + if ((uint32_t)sbuf.st_size < tparams->header_size) { + fprintf(stderr, + "%s: Bad size: \"%s\" is not valid image\n", + params.cmdname, params.imagefile); + exit(EXIT_FAILURE); + } + + ptr = mmap(0, sbuf.st_size, PROT_READ, MAP_SHARED, ifd, 0); + if (ptr == MAP_FAILED) { + fprintf(stderr, "%s: Can't read \"%s\": %s\n", + params.cmdname, params.imagefile, + strerror(errno)); + exit(EXIT_FAILURE); + } + + /* + * Both calls bellow scan through dumpimage registry for all + * supported image types and verify the input image file + * header for match + */ + if (params.iflag) { + /* + * Extract the data files from within the matched + * image type. Returns the error code if not matched + */ + retval = dumpimage_extract_subimage(tparams, ptr, + &sbuf); + } else { + /* + * Print the image information for matched image type + * Returns the error code if not matched + */ + retval = imagetool_verify_print_header(ptr, &sbuf, + tparams, ¶ms); + } + + (void)munmap((void *)ptr, sbuf.st_size); + (void)close(ifd); + + return retval; + } + + (void)close(ifd); + + return EXIT_SUCCESS; +} + +static void usage(void) +{ + fprintf(stderr, "Usage: %s -l image\n" + " -l ==> list image header information\n", + params.cmdname); + fprintf(stderr, + " %s -i image -T type [-p position] [-o outfile] data_file\n" + " -i ==> extract from the 'image' a specific 'data_file'\n" + " -T ==> set image type to 'type'\n" + " -p ==> 'position' (starting at 0) of the 'data_file' inside the 'image'\n", + params.cmdname); + fprintf(stderr, + " %s -V ==> print version information and exit\n", + params.cmdname); + + exit(EXIT_FAILURE); +} diff --git a/tools/u-boot-tools/dumpimage.h b/tools/u-boot-tools/dumpimage.h new file mode 100644 index 0000000..e31d163 --- /dev/null +++ b/tools/u-boot-tools/dumpimage.h @@ -0,0 +1,32 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Based on mkimage.c. + * + * Written by Guilherme Maciel Ferreira <guilherme.maciel.ferreira@gmail.com> + */ + +#ifndef _DUMPIMAGE_H_ +#define _DUMPIMAGE_H_ + +#include "os_support.h" +#include <errno.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/stat.h> +#include <time.h> +#include <unistd.h> +#include <u-boot/sha1.h> +#include "fdt_host.h" +#include "imagetool.h" + +#undef DUMPIMAGE_DEBUG + +#ifdef DUMPIMAGE_DEBUG +#define debug(fmt, args...) printf(fmt, ##args) +#else +#define debug(fmt, args...) +#endif /* DUMPIMAGE_DEBUG */ + +#endif /* _DUMPIMAGE_H_ */ diff --git a/tools/u-boot-tools/dumpimage.o b/tools/u-boot-tools/dumpimage.o new file mode 100644 index 0000000000000000000000000000000000000000..980bb609b57b8b21d049f1350bb4df3cac783fc8 GIT binary patch literal 8056 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 literal 0 HcmV?d00001 diff --git a/tools/u-boot-tools/easylogo/Makefile b/tools/u-boot-tools/easylogo/Makefile new file mode 100644 index 0000000..9278837 --- /dev/null +++ b/tools/u-boot-tools/easylogo/Makefile @@ -0,0 +1,5 @@ +# SPDX-License-Identifier: GPL-2.0+ + +hostprogs-y := easylogo + +always := $(hostprogs-y) diff --git a/tools/u-boot-tools/easylogo/easylogo.c b/tools/u-boot-tools/easylogo/easylogo.c new file mode 100644 index 0000000..4ba86bf --- /dev/null +++ b/tools/u-boot-tools/easylogo/easylogo.c @@ -0,0 +1,610 @@ +/* +** Easylogo TGA->header converter +** ============================== +** (C) 2000 by Paolo Scaffardi (arsenio@tin.it) +** AIRVENT SAM s.p.a - RIMINI(ITALY) +** (C) 2007-2008 Mike Frysinger <vapier@gentoo.org> +** +** This is still under construction! +*/ + +#include <errno.h> +#include <getopt.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <sys/stat.h> + +#pragma pack(1) + +/*#define ENABLE_ASCII_BANNERS */ + +typedef struct { + unsigned char id; + unsigned char ColorMapType; + unsigned char ImageTypeCode; + unsigned short ColorMapOrigin; + unsigned short ColorMapLenght; + unsigned char ColorMapEntrySize; + unsigned short ImageXOrigin; + unsigned short ImageYOrigin; + unsigned short ImageWidth; + unsigned short ImageHeight; + unsigned char ImagePixelSize; + unsigned char ImageDescriptorByte; +} tga_header_t; + +typedef struct { + unsigned char r, g, b; +} rgb_t; + +typedef struct { + unsigned char b, g, r; +} bgr_t; + +typedef struct { + unsigned char Cb, y1, Cr, y2; +} yuyv_t; + +typedef struct { + void *data, *palette; + int width, height, pixels, bpp, pixel_size, size, palette_size, yuyv; +} image_t; + +void *xmalloc (size_t size) +{ + void *ret = malloc (size); + if (!ret) { + fprintf (stderr, "\nerror: malloc(%zu) failed: %s", + size, strerror(errno)); + exit (1); + } + return ret; +} + +void StringUpperCase (char *str) +{ + int count = strlen (str); + char c; + + while (count--) { + c = *str; + if ((c >= 'a') && (c <= 'z')) + *str = 'A' + (c - 'a'); + str++; + } +} + +void StringLowerCase (char *str) +{ + int count = strlen (str); + char c; + + while (count--) { + c = *str; + if ((c >= 'A') && (c <= 'Z')) + *str = 'a' + (c - 'A'); + str++; + } +} +void pixel_rgb_to_yuyv (rgb_t * rgb_pixel, yuyv_t * yuyv_pixel) +{ + unsigned int pR, pG, pB; + + /* Transform (0-255) components to (0-100) */ + pR = rgb_pixel->r * 100 / 255; + pG = rgb_pixel->g * 100 / 255; + pB = rgb_pixel->b * 100 / 255; + + /* Calculate YUV values (0-255) from RGB beetween 0-100 */ + yuyv_pixel->y1 = yuyv_pixel->y2 = 209 * (pR + pG + pB) / 300 + 16; + yuyv_pixel->Cb = pB - (pR / 4) - (pG * 3 / 4) + 128; + yuyv_pixel->Cr = pR - (pG * 3 / 4) - (pB / 4) + 128; + + return; +} + +void printlogo_rgb (rgb_t * data, int w, int h) +{ + int x, y; + + for (y = 0; y < h; y++) { + for (x = 0; x < w; x++, data++) + if ((data->r < + 30) /*&&(data->g == 0)&&(data->b == 0) */ ) + printf (" "); + else + printf ("X"); + printf ("\n"); + } +} + +void printlogo_yuyv (unsigned short *data, int w, int h) +{ + int x, y; + + for (y = 0; y < h; y++) { + for (x = 0; x < w; x++, data++) + if (*data == 0x1080) /* Because of inverted on i386! */ + printf (" "); + else + printf ("X"); + printf ("\n"); + } +} + +static inline unsigned short le16_to_cpu (unsigned short val) +{ + union { + unsigned char pval[2]; + unsigned short val; + } swapped; + + swapped.val = val; + return (swapped.pval[1] << 8) + swapped.pval[0]; +} + +int image_load_tga (image_t * image, char *filename) +{ + FILE *file; + tga_header_t header; + int i; + unsigned char app; + rgb_t *p; + + if ((file = fopen (filename, "rb")) == NULL) + return -1; + + fread (&header, sizeof (header), 1, file); + + /* byte swap: tga is little endian, host is ??? */ + header.ColorMapOrigin = le16_to_cpu (header.ColorMapOrigin); + header.ColorMapLenght = le16_to_cpu (header.ColorMapLenght); + header.ImageXOrigin = le16_to_cpu (header.ImageXOrigin); + header.ImageYOrigin = le16_to_cpu (header.ImageYOrigin); + header.ImageWidth = le16_to_cpu (header.ImageWidth); + header.ImageHeight = le16_to_cpu (header.ImageHeight); + + image->width = header.ImageWidth; + image->height = header.ImageHeight; + + switch (header.ImageTypeCode) { + case 2: /* Uncompressed RGB */ + image->yuyv = 0; + image->palette_size = 0; + image->palette = NULL; + break; + + default: + printf ("Format not supported!\n"); + return -1; + } + + image->bpp = header.ImagePixelSize; + image->pixel_size = ((image->bpp - 1) / 8) + 1; + image->pixels = image->width * image->height; + image->size = image->pixels * image->pixel_size; + image->data = xmalloc (image->size); + + if (image->bpp != 24) { + printf ("Bpp not supported: %d!\n", image->bpp); + return -1; + } + + fread (image->data, image->size, 1, file); + +/* Swapping R and B values */ + + p = image->data; + for (i = 0; i < image->pixels; i++, p++) { + app = p->r; + p->r = p->b; + p->b = app; + } + +/* Swapping image */ + + if (!(header.ImageDescriptorByte & 0x20)) { + unsigned char *temp = xmalloc (image->size); + int linesize = image->pixel_size * image->width; + void *dest = image->data, + *source = temp + image->size - linesize; + + printf ("S"); + if (temp == NULL) { + printf ("Cannot alloc temp buffer!\n"); + return -1; + } + + memcpy (temp, image->data, image->size); + for (i = 0; i < image->height; + i++, dest += linesize, source -= linesize) + memcpy (dest, source, linesize); + + free (temp); + } +#ifdef ENABLE_ASCII_BANNERS + printlogo_rgb (image->data, image->width, image->height); +#endif + + fclose (file); + return 0; +} + +void image_free (image_t * image) +{ + free (image->data); + free (image->palette); +} + +int image_rgb_to_yuyv (image_t * rgb_image, image_t * yuyv_image) +{ + rgb_t *rgb_ptr = (rgb_t *) rgb_image->data; + yuyv_t yuyv; + unsigned short *dest; + int count = 0; + + yuyv_image->pixel_size = 2; + yuyv_image->bpp = 16; + yuyv_image->yuyv = 1; + yuyv_image->width = rgb_image->width; + yuyv_image->height = rgb_image->height; + yuyv_image->pixels = yuyv_image->width * yuyv_image->height; + yuyv_image->size = yuyv_image->pixels * yuyv_image->pixel_size; + dest = (unsigned short *) (yuyv_image->data = + xmalloc (yuyv_image->size)); + yuyv_image->palette = 0; + yuyv_image->palette_size = 0; + + while ((count++) < rgb_image->pixels) { + pixel_rgb_to_yuyv (rgb_ptr++, &yuyv); + + if ((count & 1) == 0) /* Was == 0 */ + memcpy (dest, ((void *) &yuyv) + 2, sizeof (short)); + else + memcpy (dest, (void *) &yuyv, sizeof (short)); + + dest++; + } + +#ifdef ENABLE_ASCII_BANNERS + printlogo_yuyv (yuyv_image->data, yuyv_image->width, + yuyv_image->height); +#endif + return 0; +} + +int image_rgb888_to_rgb565(image_t *rgb888_image, image_t *rgb565_image) +{ + rgb_t *rgb_ptr = (rgb_t *) rgb888_image->data; + unsigned short *dest; + int count = 0; + + rgb565_image->pixel_size = 2; + rgb565_image->bpp = 16; + rgb565_image->yuyv = 0; + rgb565_image->width = rgb888_image->width; + rgb565_image->height = rgb888_image->height; + rgb565_image->pixels = rgb565_image->width * rgb565_image->height; + rgb565_image->size = rgb565_image->pixels * rgb565_image->pixel_size; + dest = (unsigned short *) (rgb565_image->data = + xmalloc(rgb565_image->size)); + rgb565_image->palette = 0; + rgb565_image->palette_size = 0; + + while ((count++) < rgb888_image->pixels) { + + *dest++ = ((rgb_ptr->b & 0xF8) << 8) | + ((rgb_ptr->g & 0xFC) << 3) | + (rgb_ptr->r >> 3); + rgb_ptr++; + } + + return 0; +} + +enum comp_t { + COMP_NONE, + COMP_GZIP, + COMP_LZMA, +}; +static enum comp_t compression = COMP_NONE; +static bool bss_storage = false; + +int image_save_header (image_t * image, char *filename, char *varname) +{ + FILE *file = fopen (filename, "w"); + char app[256], str[256] = "", def_name[64]; + int count = image->size, col = 0; + unsigned char *dataptr = image->data; + + if (file == NULL) + return -1; + + /* Author information */ + fprintf (file, + "/*\n * Generated by EasyLogo, (C) 2000 by Paolo Scaffardi\n *\n"); + fprintf (file, + " * To use this, include it and call: easylogo_plot(screen,&%s, width,x,y)\n *\n", + varname); + fprintf (file, + " * Where:\t'screen'\tis the pointer to the frame buffer\n"); + fprintf (file, " *\t\t'width'\tis the screen width\n"); + fprintf (file, " *\t\t'x'\t\tis the horizontal position\n"); + fprintf (file, " *\t\t'y'\t\tis the vertical position\n */\n\n"); + + /* image compress */ + if (compression != COMP_NONE) { + const char *errstr = NULL; + unsigned char *compressed; + const char *comp_name; + struct stat st; + FILE *compfp; + size_t filename_len = strlen(filename); + char *compfilename = xmalloc(filename_len + 20); + char *compcmd = xmalloc(filename_len + 50); + + sprintf(compfilename, "%s.bin", filename); + switch (compression) { + case COMP_GZIP: + strcpy(compcmd, "gzip"); + comp_name = "GZIP"; + break; + case COMP_LZMA: + strcpy(compcmd, "lzma"); + comp_name = "LZMA"; + break; + default: + errstr = "\nerror: unknown compression method"; + goto done; + } + strcat(compcmd, " > "); + strcat(compcmd, compfilename); + compfp = popen(compcmd, "w"); + if (!compfp) { + errstr = "\nerror: popen() failed"; + goto done; + } + if (fwrite(image->data, image->size, 1, compfp) != 1) { + errstr = "\nerror: writing data to gzip failed"; + goto done; + } + if (pclose(compfp)) { + errstr = "\nerror: gzip process failed"; + goto done; + } + + compfp = fopen(compfilename, "r"); + if (!compfp) { + errstr = "\nerror: open() on gzip data failed"; + goto done; + } + if (stat(compfilename, &st)) { + errstr = "\nerror: stat() on gzip file failed"; + goto done; + } + compressed = xmalloc(st.st_size); + if (fread(compressed, st.st_size, 1, compfp) != 1) { + errstr = "\nerror: reading gzip data failed"; + goto done; + } + fclose(compfp); + + unlink(compfilename); + + dataptr = compressed; + count = st.st_size; + fprintf(file, "#define EASYLOGO_ENABLE_%s %i\n\n", comp_name, count); + if (bss_storage) + fprintf (file, "static unsigned char EASYLOGO_DECOMP_BUFFER[%i];\n\n", image->size); + + done: + free(compfilename); + free(compcmd); + + if (errstr) { + perror (errstr); + return -1; + } + } + + /* Headers */ + fprintf (file, "#include <video_easylogo.h>\n\n"); + /* Macros */ + strcpy (def_name, varname); + StringUpperCase (def_name); + fprintf (file, "#define DEF_%s_WIDTH\t\t%d\n", def_name, + image->width); + fprintf (file, "#define DEF_%s_HEIGHT\t\t%d\n", def_name, + image->height); + fprintf (file, "#define DEF_%s_PIXELS\t\t%d\n", def_name, + image->pixels); + fprintf (file, "#define DEF_%s_BPP\t\t%d\n", def_name, image->bpp); + fprintf (file, "#define DEF_%s_PIXEL_SIZE\t%d\n", def_name, + image->pixel_size); + fprintf (file, "#define DEF_%s_SIZE\t\t%d\n\n", def_name, + image->size); + /* Declaration */ + fprintf (file, "unsigned char DEF_%s_DATA[] = {\n", + def_name); + + /* Data */ + while (count) + switch (col) { + case 0: + sprintf (str, " 0x%02x", *dataptr++); + col++; + count--; + break; + + case 16: + fprintf (file, "%s", str); + if (count > 0) + fprintf (file, ","); + fprintf (file, "\n"); + + col = 0; + break; + + default: + strcpy (app, str); + sprintf (str, "%s, 0x%02x", app, *dataptr++); + col++; + count--; + break; + } + + if (col) + fprintf (file, "%s\n", str); + + /* End of declaration */ + fprintf (file, "};\n\n"); + /* Variable */ + fprintf (file, "fastimage_t %s = {\n", varname); + fprintf (file, " DEF_%s_DATA,\n", def_name); + fprintf (file, " DEF_%s_WIDTH,\n", def_name); + fprintf (file, " DEF_%s_HEIGHT,\n", def_name); + fprintf (file, " DEF_%s_BPP,\n", def_name); + fprintf (file, " DEF_%s_PIXEL_SIZE,\n", def_name); + fprintf (file, " DEF_%s_SIZE\n};\n", def_name); + + fclose (file); + + return 0; +} + +#define DEF_FILELEN 256 + +static void usage (int exit_status) +{ + puts ( + "EasyLogo 1.0 (C) 2000 by Paolo Scaffardi\n" + "\n" + "Syntax: easylogo [options] inputfile [outputvar [outputfile]]\n" + "\n" + "Options:\n" + " -r Output RGB888 instead of YUYV\n" + " -s Output RGB565 instead of YUYV\n" + " -g Compress with gzip\n" + " -l Compress with lzma\n" + " -b Preallocate space in bss for decompressing image\n" + " -h Help output\n" + "\n" + "Where: 'inputfile' is the TGA image to load\n" + " 'outputvar' is the variable name to create\n" + " 'outputfile' is the output header file (default is 'inputfile.h')" + ); + exit (exit_status); +} + +int main (int argc, char *argv[]) +{ + int c; + bool use_rgb888 = false; + bool use_rgb565 = false; + char inputfile[DEF_FILELEN], + outputfile[DEF_FILELEN], varname[DEF_FILELEN]; + + image_t rgb888_logo, rgb565_logo, yuyv_logo; + + while ((c = getopt(argc, argv, "hrsglb")) > 0) { + switch (c) { + case 'h': + usage (0); + break; + case 'r': + use_rgb888 = true; + puts("Using 24-bit RGB888 Output Fromat"); + break; + case 's': + use_rgb565 = true; + puts("Using 16-bit RGB565 Output Fromat"); + break; + case 'g': + compression = COMP_GZIP; + puts("Compressing with gzip"); + break; + case 'l': + compression = COMP_LZMA; + puts("Compressing with lzma"); + break; + case 'b': + bss_storage = true; + puts("Preallocating bss space for decompressing image"); + break; + default: + usage (1); + break; + } + } + + c = argc - optind; + if (c > 4 || c < 1) + usage (1); + + strcpy (inputfile, argv[optind]); + + if (c > 1) + strcpy (varname, argv[optind + 1]); + else { + /* transform "input.tga" to just "input" */ + char *dot; + strcpy (varname, inputfile); + dot = strchr (varname, '.'); + if (dot) + *dot = '\0'; + } + + if (c > 2) + strcpy (outputfile, argv[optind + 2]); + else { + /* just append ".h" to input file name */ + strcpy (outputfile, inputfile); + strcat (outputfile, ".h"); + } + + /* Make sure the output is sent as soon as we printf() */ + setbuf(stdout, NULL); + + printf ("Doing '%s' (%s) from '%s'...", + outputfile, varname, inputfile); + + /* Import TGA logo */ + + printf ("L"); + if (image_load_tga(&rgb888_logo, inputfile) < 0) { + printf ("input file not found!\n"); + exit (1); + } + + /* Convert, save, and free the image */ + + if (!use_rgb888 && !use_rgb565) { + printf ("C"); + image_rgb_to_yuyv(&rgb888_logo, &yuyv_logo); + + printf("S"); + image_save_header(&yuyv_logo, outputfile, varname); + image_free(&yuyv_logo); + } else if (use_rgb565) { + printf("C"); + image_rgb888_to_rgb565(&rgb888_logo, &rgb565_logo); + + printf("S"); + image_save_header(&rgb565_logo, outputfile, varname); + image_free(&rgb565_logo); + } else { + printf("S"); + image_save_header(&rgb888_logo, outputfile, varname); + } + + /* Free original image and copy */ + + image_free(&rgb888_logo); + + printf ("\n"); + + return 0; +} diff --git a/tools/u-boot-tools/easylogo/linux_blackfin.tga b/tools/u-boot-tools/easylogo/linux_blackfin.tga new file mode 100644 index 0000000000000000000000000000000000000000..e2bb17b80b03b9c76a82f45cb062dcb195ad615b GIT binary patch literal 158718 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 SH1a|Klom1Y$O{IV`xpQ*lgpL> literal 0 HcmV?d00001 diff --git a/tools/u-boot-tools/easylogo/linux_logo.tga b/tools/u-boot-tools/easylogo/linux_logo.tga new file mode 100644 index 0000000000000000000000000000000000000000..ac53def05c2790b429cb4ea8957eaaf362fc881f GIT binary patch literal 19244 zcmZQzU}AuQ0EPet2^eRT9t{E15MX3vWMg9!5D?(y<wcblZajWLK@m|gK0baP9v)sk zel|8X201MjGcOKHKW;N`PHiVHB_j?od1hu7R#sL%0YO$)R#Jn2m6cUmR#rsIkWa^w z+t`E8(x1mEhQlq9*E)bt-=0*{$WYG;2Hf1-BBEllipqwz&W@33#s!@+Ilb&D9lWJ; z`D<2jm95|_S;3b%TOhiN+cl5NB1T$8TT)7jpP!#p8wCXg6*P5NjGd(lCvca{<!;)* z+`OHwWv5`{7QwvP0*+A{*6!>aoTM5{jCu|Z4iOO%898}5C1q1je}m*I!T2`r$QGW2 zK90hL3^l8m+jnwKImtQwGVko0{L`<CPdv|Cv5_xfDqm0&gK->>h=Qb)G%*&z)v&X( zi;0VKs+%zR=WtdpVe31<F!2=Av@3#39&t>*AT;rmM8_V1oY|6|i9+Jya9tEKSXfwi z`T0deMGb8n{AwoXmrqqHpCwW<o3nTxZ_OIs`pp7e2bendGfg|oyX-#q@~7Nu-wSO1 zA+Yf~>+HKMy{Gu=x3k1fWDjm;QFUNrW9Q`L;Nalk=jZ3;<>lt)=H%q$f&gw1;OFKM z<m43L<rU!O=HugM=i*~!6J#*<W=(A4Zri~;^%B$KXFMxj^DKHKvGF6{if1gduX9g1 z&EB|CH6l$$O`VmEjcm_yb93|Z@=D7qC>xopIr=(vt}>o{NU3L!@{AKQvrb9PzbLih zw#2IY5^EkwZG0)b@wvd3H)4CfiS7C&u=N+$*552!e(>%6!@ce$*Yq<2En8TUC$Q?c zK^()#$OvIDFfbsq85kH?nV6Uv7}%K@Ss*HyxEZ)*nJvSFvZpJ}x-78hIrq9>B3r&n zZhI@T{S()=PpsQMb8P#>vGlIUoQpO~&!~BX^9zeWw314)u(0s(^2zENN|wzO>%Sl| z`;p}0XJRW~iEnx*wB;So#@GBiJ_zmkEWG=((C&}Idp`>8elN1)qu7pb!rOjwZTrc* z^B2c~Kb+g&axFM7GHH)Q%_2s1TLuP%Pm$fj1_n%Ez`)4B#K_3X%)-LV&cMXOz^lk% z9VMGTM{mJxsTCiDHvbpg^Id5FJHEXixORWx-1Uun#}BbxKLwUN)L;KXXZ9Jf&?0r8 zXl52@fFavLkgcGusbAKuIO7P<g6BMIesZt-$+q!3%hn&v+rKgH{KmfTC)a`RJbS<J z?ENgb?~};>58}JuOKkrvzWtlP&Y#@-e>3j<z`gsG%+`BSlXnPa^)Sd7aB%ZzYHGN- zxdjFW`uO;GdV0Emfs&FE!ZB<tj4Ui{ENraI><o;Y3_J>)rXdRXvrU%VmS6ipXxm?o z{XbX^zvDRgk!Rl*q1`_ucm9;y{!?M|7nLoaWY#=a+4`Dy>In^>2tGj}QhhC>q~hE% zU%F>E_tIzFn}71`_`|;Q7xV6447-0Y?)$-Z;3wDq@7%jT^X>l3zwZ<8;SYlQK8Wr5 zB);>T(4L>%`+ssB{K~!UvFw`58k4qj1r%9D=1!kA=iq_;&!0d4`t|Gk_wV1nef#Rw zt2=k@Y}vA9(xgdgX=x&2Vjzz*GIFqRaIkPPvhXpmOET+vNajs8Sbkk{(`&9>KbZD? zXE^YQW#1R>Jzs@(f0fwzU1sMGiLGCxcYPDv^Ic&7cfMU8rI%iHS$j#tD^yNXmxGfF zWF~Rg(8fW%V>j2_%Y2(&3hny9yXO<<zRxUsKC$il!nOY!&)zS*J3or-_{_KOGt;5> zYzICF?D;IR>l^>B@B9aT@$UaDxb=zt#@ljbb1gD@Pu_m{9|!pJ=g<HD|G$6#zH86k z*u+#}0TE^fMivGZR(1gf23`hrTmHOmwbhpdwm)Os_nC3`ch((0*?0cr*!7)j&o{2U zUwHO?<=Ow0bLV@u?eDnvf8{&!lWoUK;T=y@*Itp-&^2)OCeE)QRV*wlmQ`~(Cmj-4 zdq-^NbHTlDc=vzc-1C8V?`MI1-voDm5!&%tbmv#zy<ZsizhgM~kz?OC{yjf<cYNdC z{e^qaJEeWEH0K<Oop)&8%{O0v|Nry%-`_ufe*gXr@!%gY`xgxS2K)N$yN`zs9L~?o zVFb1CSr{0&7*s8}^E>4?T;kaCoZ;Xn*4;n&w*BVb@rz^6PnLb(84i7AIQWs_z(?jo zU$_tb;N0|9aL;#+W51aXeHJ?KT4%w2X=PO&9v+Zqh{IN%zJgPau&;W^zxN|3@9g=` zvFnG>u3zFie~It<DY*MP&%ST$yFYR5{>*Xs55w-Sth;~k?D@vK|Fh_^calqw7&Oc{ zbnE&5|NsB}`TzU(pWlD~LeQ^2VD_IsKYsoC_2<v8-@kwU{{8#!zwh6F{Qv*|=Z_x? zmMzgSFlJz2V-V34$nKKea+`VkOZFY#xc7hMIQWip|68uT?|Alo;NAC$YyTIPU7tC3 zeih&QTYB4fxy?Vg_Wowr{e$_yXN~>O41%IXL_}DKF2N*a<;15P;aKsQZ}(T$eZQD? z|Ki;FOJwICshz(hcl{99`<3nBXV(3n`S<?h+WU)T?@#u9KiPJD<lpy3?dVJX(ta*Y zD^IV0?&gMfuV4KA`xoT3KYx(PpJ1PYJ^T0Tj~~DP{Q39i?~k9~{`~*H^Uz@(T?+<g zX_2gM@io_&HoX+s{GD&d2mV8Eh4#J?-Tg*l_Xn{(UxfGn;N1O<W7l_qy+4Gvf05kr zon!k?mYqL@4u93(|Ijcnl7oYT$hJK<H@CKlx!lBK!b=~C?fAm8_Xqp#AKbft3hn+O zzWbZ_p3j2&KX4v;&vN)P%ieF?TRw~I`pLEH3-g|LO2<ALuDQTs9s){`3=Hfn%u{<i z|NsAw>{AFE?%!X3{`~m;=j-<$e}Db{^Y71>KR<u}|381hS|J4sw(xS9b=O%oJr~*h zO?vxJsU6>BwtkV@@=1Q%7pYy}1owVp-TQ@c|7XU7pO_DP<T><3X#Y2vy`MFAJh7gz zRL#&tKtO<-o12*A%*)Gb7aXTB@s#AU=hEB13he&DvFAJI-XDB>zX|RABE0XTz`=Js zhhMWC`N**Qi{RETGP}QX?R+P2;<M$c59&F63~X`?4BV_-pb|1YK9Zn+fB*mg>+jz` z-+z4j@%8(^Ki_`;zH;S$R_$b!;>k+uZ*XpUCbjvi%%)$WYrl%G{3yBnt<36ok{jO( zZvV)A;0w!<PYj3NG8}rtdi0I>_J_7RpBN?<NXW<t2@7*@a1i5J1_lORUS6B{T<NJ7 z#aF+R-1&od&kxo;-<kLQVBhzRd;e$N10VSgzT-RehU4&O#=YP9_xupq|CRT^TaB|H zHD>JOl(S}I;Ade51uP>YLvw8zLH~aL^XL0-P+9%+&yO#Ee!l<m<<j|Et9M_BTXRZ& z#RdLN&%{=}=3exIZT@5SS$Dao-x8XATWH}Op*2q=_J0;W_Lcp>Tju?**>*qG-F98i zD?m&}hJk^Bq*|Dlm)A0-SZK}-flZ%9_WtJD`-^GscZU5xnD&2TJ@AcV|0kaPA9?qG z;6M1Cea{c(-Jh8Dy%jj}(rn*7iLhb@CSg`4K4wNxAJEWH?b+iy|Nj4jq+w(dB|ZQ9 z`{U1_zrX){|MmUb-`}s_zdv#0{N_W~VpkkfT5(Qf%QK#NcUWd#VVHWFY2tCNzM~=& zj)_k_DK`I-?B-_*2fv8!`^3Nhi}0Rz`q@<?Qc^_5DY7zNUS89bVvf02I5&S3-uH`l z&v*9SUzqoNVLR}h{lE{lU7vaPe`nkJUTF6>(fz-e4}D@d^i1vQ2d%aREWFB0jG%5B zGY6|zxc}KpCqYFtEdT!b_wUEAU*CWJ{QdXu&)>g){QC9t_wQeS|NQ#@|M$QDKfnF> z@$<*;|NmdTe|Prer49QpC#^f7z3HmJriXlUZ}3k#$lJD@qhK;yLYqLw1c}PUqOBX1 z7F|@>@<MF;d+zODgtmRu^9@4w1@UYa78dvNR_@uyxL4nm-1J0h;}eNZPsMh;5#INS zYx{ft{XYfv|KZsBU2yYfshuBLj=f_&^;Gxpb;*DfCI(Ih1}1J1p|qOPD-Un|`~UyP z@1KAE{rmU-Kg5ra0vW=F`uESDAAkS<{QLj+k6%B3{rvg=-^XuXFI~K`?!bk#t!K5i zT;*7Qn`72R#qJHlq3H|~It+X&48l4L%8m?X;l{HMs;s>&w)r{RmJgg8Uu)Vq67NSe zm3Dz)+LPC4uRLY3^0d{GQx;3l>Mpw|x#Y6kjyHna-m`7^Ab#k-$o6ki+g^&D{UCnu zrhMxR1|eAn1_o6vwbnV4?|*sw=l}nY-@g3>N9FgQKOu=3;$Ju$!uav$->*NQ5c%=z z$M^q#zW@94_{pn17w%{5zNoqL8prmBTnn!WBsH;!%QG;rFf#BkGVp*JV2q;tzL|>C zc53Z;Cb;`8&&DU3)^=#VAc<?{<z?pOrDbL&ucoe|qvM*Crq{kwX7+LUZ7)T4e-+yQ zn|Ie2<)a^ER-R=E&1Vpl=V0edPft2|>BQInKYsoH|MU0nFW-Lt`TzgRw{Op1zWnm- z+dr@%-0u+2g6gARe?k8J^Y7R1pI`p{`uYFwpMU@Fe*If?>aOO2Tl_~}^Q^tc;t&lg zg&3K+8JL6^Ie1xl85p=382A{q9kpg|u|57$v$|hKS%oAI5>)IIooch-nDV-tvirZV zZ27>x|BLLIkCN5%88{R<7}(O>wfB`{)1n|Nnpd{Q3Fs&)@%k|Ns5_|HsdtA3S>W z>B|>T@&@N$P__j(w0`~h1J1~QfBpXd|0k%-`uzXrxBq{C{`-IX$N%Dsk2H=y5We(7 z`uI0)_e2H;1~zt1Ar=-X21ZFnW^N`{J`rgKHbEix(D3y~^~ue=czi7=D(aGyZ8&9v z%Aq&>2R;a&_^7_`CWD%zynx2?-gz&cKl}3k$H(7q|NQ#!|Ig3A-+%x6_5aWB{~ten ze)8<;mv7(xz#3YR#^j%W{~#Ip*YCf7e*XRU<JX^mf4}|z{q^7PFTeji{Qa-z_A}>W zH}uZDFgfr<I6RAinT3IgnUjS@n1Mlxkx_(^K|@JdLP?cJSwq9nn3rDwkITuEVq#)) zFQ{<Xbjsq)OSLOsb#`5ni%L>5v0Slc^WWc~Lh0@IPhWn2`}OPl-#@?qf%*ym{{R2~ z>C4xr&!2z%{27vYfB*dpFR37n>VJR#{r~;<->=_)e*gaY=l74le}4Y|_x1n3D<40v zzjb%>v)B34=P`;(GIMY+f&m*NBM&PpI}?+-nyR~}r?i5inuZ2>J|d{g&M!#JBU&l4 zP&T2K%f_EUP_U-0>GR*;-~a#r^8449pFe;8`}YeRkDx^R>(}q!zdwBX^yJyI&tJd( z{`c<>7(psuNMio==g;52fB%EhGRVK69>m{2KmPsw_V>@%zkj~|`}_a@{~d=9NGixO zF){J+@o{r=v#_#qaB{M9aLCBW=<4VQ3J8b@3lrH?Cg?#l1=?CV3=FIc3@i)`9PHe( zqKeAX7S8|u|Nr~%-@g3*_4D7~-=NAA)W85m;!hBG{qEhPCr`e9{|>2@AaVK|7A8M_ z{rdC!H{8FVQ2GP%@0Z`dzyACC?$?hGzkj{@{>{|~)Snd;6ciE?;sg)>v9hv)hk~Fp zL<R;1XilXH*H~9qnuVQ@flZoI+|j~CNnNv{v;E)y|G)nI`~LUOkH3Gu|M~qBxhwSh z_v^QBA3lBzDW4(U`~%LpkoZOOFDQQhfLb6QzJ2@n^T(Hee}4S`|LNb~fUr<d`OM7B z%gf6JZkaPNF)=eULr4Y&1{(R-&`^(;g^`1SK~GLO!aqb=O|7b-{_p?)KmPvt_UHGn zfB(My{`CXg*Mr34pMU?}fBbm=;lmG~KK%tZv_SC=RsgAwe*OU^Xn6eo{{0&y{{P>P zzkh%I|Nr^-uP?uU|Nj3!J~@ejfq{vMNnBi9OiT>oQx+B$Mn*<<c6J5^217$bszfN7 zA*^hy+#-Aoj0`Ls?50+hvI_DQm6hNB{Qmv#->-lFet;TFDE-{O|3UT9{f7_VefaR_ zA2glAy$lbNf3W-u_3vL$xPAHg<MYoS-~NEoOk`{fBz`5qfQ5yHnVA{lQ_zGC&HT&2 zz#u9s$-u(M#Koa)XrQR9oRE<4{^Lh@-3svnoc#6Y&!;b69z1&V`t4gt{6dm6C=y}G z{?FgP|Ni~^`}Z$6s{cZp*}r~$`SBf8*8lnQ^WWdFhzQ8QH5(h7yu3U+J3A8-6J%zJ zm6es9ot;MU%fP^(rKQEfz#z!Zp(-OQDJE`gWOVJymH+?$fBy0XQlCPK-yeT||M>I! z=ik5Ie*Ady?Ae=l?|%RN`~BxnNQs2Uzkk8r|M=<C??1nP{QmXt|NoDlKKc9mL(($~ z3yX$^1|J_E6B82)3kwIhl9G{;A-=bZmSTzFS{NGhFfbTNO3R6f3-R)8+p+}`JD}qD z*Dp{f=-01rzd`87pFiJ!{(SN3)%%Yh|NQ$0>H|S?C@gY+fg=?;enHjK|NmdUeEIw5 z@7FJ1|NsAg>((tjJw4D&1h`Dr)6)|Y5@Kd%W@TlCBxYr0Wn!F7wi-zZ2~|mPF;-R` z6%{@XuELz$-#>o*`tbuaRQ2oE&tJd3!~FaG_wR2%e!P0~=HutjpknRUFM{bA6m-9T zgNy>3`Tzg_V@Hp2adI*;GD7@lY-}tpF3!Zn#Lmvn$qDLyic@dm3|E-w8E7*wFz9P& z@^f;T8yUZR@(h#?!FllapFf~z1y@f$|Ni;;>({#vA3*KNfBztzU*yCL8L|9}lz;#I z`}gz5k3YYE|N8L*GIYIi=`u*>Wnf_7;NUPcG!zvT<>lpt3_mh6Giz&u9D~cLR1p^y z7v~Y==U`{&W#?dJVF~j0|ML0s|NsC0{{8zK+!y%$AJowL{TEdJeERa`>-X=!|Ni~@ z{X1s<g{T2(1dmaJ68N7#zkmMx_xCTT82t9FIKP0InVE}=3(~~W*4F0a<YZ@OXJKK1 z6wy@i5kW%?jf@x=7{o+HS;51+t5&UoM8}^$pf3K8A3q=+1b86^DYPNY3`n#>*q{dG z-@iY8{P_73R2xG^=6`^5$G?C7UcP*(rm89_DXFEU#mmd9prD|or3J~$kjx9o+5}xo zxdIUp&@7~!oSc9FsBhxv==kZ=CrD}Y@87@ApFV*+4(=R6;uMd6|NZ;-3*6ZL3kKk3 z_`eSyKK%LfXWO=I92^`H5)xKcR#H+@va+%~JUoz=Br7W`H#fJmG->nclz3cAOG{K# zR8dipfq_9lKw#_Et)S7!U%$Yv1`$7g{)FUNNE;I3Ww`et@e3Mkhvi>TdjmA{@aNaB zUymL=diCm6WMm`*0|PrdyMls(rlzKploTT)BNG#}*{rLpONskQ)vK?s&%nT7X=%yD z#l^zH5*{A@_U+q$|Ni~?19I?hFaY)A|NsB<@88egzv2BO<n#>cM1dM;pw1&aN4$CS z=E;*MyLRn@l)Y?hY@D2&YHDgcJUkHpLJ~8Ln$5Vv0TS%|{QRP#q6`cSe0+R$b#-sv zyaAQf|Nen;BDlPQG?_ks{R;6iTKxV6b)EnJ`}gn5moLwsKY#S-(Y0&Wu3o)*?AS4H zZ&0_0i;GKCR8&Yvh>wpC;#p>9W)>C}b#--I4x^noq%L4&WQ4SE9336EZ{L39%9R%{ zUVQ%!YES(INABmZU*EiY_vPESzu;Chq)dhsWuHEOe*OCO)2C0bUAuPl=+WJ~ckkJ= z=ia@03l}bAU|`_p<~B7om6w+n5D<X$hQMouK;zpq>ht0cD~Nv~4&&h9Fg7-xJbCh- zJ$ufaIrHq<v#(#jf=A?jy?OWU?*04EUcC4TZgN1n4BvnLeD&tdt=qRxodkg+M~>{< zx9{-b!zWLkyng+9TwEL^&4`GIK=KO<3k#(43#obGxfs6-sVfU<s6!g?EG#T~dU{Ki zEV+F7^6S^H-@kwV{rmSHzkYrC^5xZ=H}@YteE9e=XrKe!sQ&ij$Cq#4UcGtq`03Mo z_wL=lfB)LGYv<0LyL9Q&wQJXEYHGy5^Zv}t%#fZD#LK+AypV1JBnMH~FGQLKuL0TE z*wob2)~;Rq{rh*&ye_yRhQ#mppFcl-{`?8tr+^G$ftrk<QD<nk=>Px!U%!5R`}Xbo z_wVoByJux(1@R`tckEyQ$+nQ<T1`!jNaxW)nSp_U6nO52lasT)z8>CK`uPhyX##F> zK}Mecf=8j@LzI92{|B|lKz;iEpdk7S8nD{EdpD$5W&%epB;B&Hu|bltzP>)nS{GXQ zhX^wvE@WU}Fg7+mdh{ryZ3${Sf$NxGfBwAv@Zr;!FOVwf&%ghF{{8>^A3TBw?f`;n zG4L$m|NsB%>gpgCLdG%RK4oEHp>qWUaRx3@Utgb*fq|KUAulWI_s<`{e*gUW=kMol ze?EQw@#xW`D_73nxPI>Ci@QI6z6VYD{{Q#$&%Ymk|Nr^_@9*C~zyAFE@#`lflyWjM zK?NWSD<2mZ3oA1t6R2mzz`#J~)&(waB8wYp>o77fs7Xm3*tPxt|NlS!{QLCf|MeTs zckkK1bJym5`_~;kvGLa3<L^E`{`2qmk3auD|NIYXXaD>D``^!RpuxHSPai&Xv9M-i zU=U^J;AUoK;bdk2Ph4|wa7ao@BHJ|>*eXg&B0Ri&%*?iy=GSjs{rmUt!v`<-9XP#x z-_ecx53f6PX!EJ#n~ojXd-lxDhmYTV{PgE1sI~Fy_m|&)LA}5CAKo`NG_W(X2(fYS zFfeelvaqqUFtCD_AnNGo3<eKky20EK)Nf*7V9+x&x_slx(PKxq?mx6>_wly12lM9b z%$&QoblH(f+s>}qclO}%Gq0XJ{`c$K|9`*#{`>v!@4x2OW=;+cRz@Z+W>yYHMnN84 zCPvU&Lt#--$S^FXbptLUCo3y0F3!lrD5|Kqa@(feClAitvOTPIhIalW(e#--$#Z4% zmw9z;sabYl$&SOvPo8-G^wC$)?9#tu$B)a($${p@*x1<E+4;D6S(#YaSV1Egl9E!8 zQhLCBht&a~E-E9Vn2Oq@_3K;Kul4VmtXVNhsbzyy&r!k7Q^HM$WGmM@wXZE$ux;+H zBYV$ZIeOvhtw&GGs%pR;TQ)9U9uHq{XEzT9P(j7c&m*L%3F`A=wQDd4ii(M`3y2Cx z$d#|yWY9c`C#OZCbG6Eni-Ob6vUMHinRrTK@(Jb6ZN?pIVwW83+H-#8@hkf--APE# z0}Vp5u;?1-*S9t278i5!fO^&@`euW{Q+V7UAt}khC&t1dpq}5tP&kdDVWq&lW0LD` zaLqlz(7T<XbpvzVDz5JBVp9()O+Vr~|3K-6BNKL>atn?J`Im_$FesqCrz1EroPmLX zjfF>0KoE~zgF#AKMuweBSkBZzv2q5_{A=86pUCWet#a(O#+ldZ=U*!wexSPVzU0n_ zOjC|BbsvzKf6{gF{;0WIRGdN?7}z*i*wvKeRa6vs_(4-e%G92<r$zY4DJU{<3cI8f zT29+7wC)|>woigPUy1I0q<H3q{+%y6cRs7$_@a61kMN<_40F%3PCG0$eV50wqiWvC zApbJ3@G~(AaDo<Z8_;G}i0WQeQdMK%l~VDH(CJvmG4~GFnwKIw--sP}C4S<S+~xOb zcfKm#{HSpAo5q8GqNm?8Ex*7pb)Un|dwLNi3=AB6EIeYYEJCcHZjpfjX#9lg&ZD}4 z`X-hPj4aIJ8p@UPB<9}`U-L|K^K<@f&xDS>m%8#p_V#auJKq&=eKCCU$K>fB@he|h z_P$i#d`s9hm4Shmn~_ZxTnTAwQhEIe)x8NfP)SvTfq_v}RwKN3v1I8C%^3&ux89ZB z@rY;N3xP9V1dhL!KK{({)+gg#r*#%@a@ut^>FR6Wg*!Rbtr-|Nd05#6nVA?F7!33V zbsYrvECT}rWI;t-Qqt1(TUjL(dEFy4+U6T>zNmTpmB6lh+$+y$Z8#se^(>!rD1(TU zp|j`7E0^AW{Sg!y&A`CO$<DzB-jW6%IYBmgaI!VPBWaT+O{%P@2K97A6@2O@7}ibG zX_}|jx`ZdS#eMR+ta+Omxk2rJCI*J6@bLft|F<?bGcYi)u&_XyQbWnV-~}@iCQL9k zHsR#rH8Qt7aQ<dgaWex0A2Xj4zl3h<%vF=;ujS?wWdg04W3soi`~UxcOG^s_0|R6V zjDdk+2>BN>*s^HRqSUlhZ5>^2FQ02yuJ?8Hax!s>@`)O28@+n?{Mf!jLOgu(Vv=62 zZbd~!|NsAQZ*K>;H=twbL&(3Hnwks@43j5MUcPFTZ)n)0=`-HFds9}F$H~md%gUs$ zs&Mt($sb=oCnY3WSX%e?_MSL?{N1~EkO4{XHZ0H<xgq3VP=A4e!N}NX`|iEZ-+g}g z>P<sO8=ojYGbd>HLr9dTpg8-|wM#Evzqxhi(Z>&8o<4or($XR#A_AG!g^WoKA^$=~ z{y8`~N=qsa?mOAk(xafE$-v3Xz{SYG2J*6`l1zS4@v5b3PaQaO<MPcpbLMDiX+h== zAVt>D%D+(ma&X9sN!jXJD2d3+ODl*-NHBAAFt9N(ure{SFsaDMXND%ssO_oFDbY|@ z7ZVqkkdOfP2U#KU3rQq{vjReM6+9_&GO_S8vd9aFDM`riaB_i0^O%@8*f_bFSh*M& zB$zlAIQV%On8d}!4ZySZkX0y<G3O!VUlkP<$VxCaRu*n9b^(5FHYR2^1||V^E&*0f zJ{EQXW;Q-%77hjm4n_tZc2-##86I9<$e94l%*>FvKX{;_1?C{+>g(%6)|P-4T5>Uf zmie<VFf%f8GqDP>a0#<=^RTkAurM;PgQY-oUm#CFS{m@t=0P>o400cCEG;d~&(9B< z&f{bOd5QzHLk_gsgprk>jgylRGHt-fz{SKS!oe-X!^y=7SyIK$&JG!u<LBp>0q?BA zZOI^#5di~u`h+Z$Vq#(fttnz+Vg<Jt8JQRv7#YPS#2p+R3=Iu6H8lkU1RzTk2ItT^ z{_us&8$#9*fg&25D<RvUK(i#^rL7DM403XEQBhF^1qGp@p?Z3HkWHP6ii%{M>VV%p zw3d~Skbrm@QshEB&H@II{riwYSX^A(&CM+{GczC{Kn^?@tE{XHSqVdH7t+Ffh!-I7 z3-Kl>a>1)jSV2V=XaS_6qN0q93}muiU0od#4zzF#9n3^t*$C<7LFRBFOP86Mnc;0J z1_p*fJ7YppK<Vh{7#bQvN?l092~u!Dx<ZVMjF4GQeSQ5Q9kXa*!ph33t*tF5CnqB# tBPS;(E-tREt*xM-;OOWmBqW5UnD*R|pitK^&tOk~Kivp7Paju31^@+Ii?jd$ literal 0 HcmV?d00001 diff --git a/tools/u-boot-tools/easylogo/runme.sh b/tools/u-boot-tools/easylogo/runme.sh new file mode 100644 index 0000000..625ebaa --- /dev/null +++ b/tools/u-boot-tools/easylogo/runme.sh @@ -0,0 +1,4 @@ +#!/bin/sh +make +./easylogo linux_logo.tga u_boot_logo video_logo.h +mv video_logo.h ../../include diff --git a/tools/u-boot-tools/env/.embedded.o.cmd b/tools/u-boot-tools/env/.embedded.o.cmd new file mode 100644 index 0000000..a986ff2 --- /dev/null +++ b/tools/u-boot-tools/env/.embedded.o.cmd @@ -0,0 +1,287 @@ +cmd_tools/env/embedded.o := cc -Wp,-MD,tools/env/.embedded.o.d -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -std=gnu11 -include ./include/compiler.h -idirafterinclude -idirafter./arch/arm/include -I./scripts/dtc/libfdt -I./tools -DUSE_HOSTCC -D__KERNEL_STRICT_NAMES -D_GNU_SOURCE -c -o tools/env/embedded.o tools/env/embedded.c + +source_tools/env/embedded.o := tools/env/embedded.c + +deps_tools/env/embedded.o := \ + /usr/include/stdc-predef.h \ + include/compiler.h \ + /usr/lib/gcc/x86_64-linux-gnu/8/include/stddef.h \ + /usr/lib/gcc/x86_64-linux-gnu/8/include/stdint.h \ + /usr/include/stdint.h \ + /usr/include/x86_64-linux-gnu/bits/libc-header-start.h \ + /usr/include/features.h \ + /usr/include/x86_64-linux-gnu/sys/cdefs.h \ + /usr/include/x86_64-linux-gnu/bits/wordsize.h \ + /usr/include/x86_64-linux-gnu/bits/long-double.h \ + /usr/include/x86_64-linux-gnu/gnu/stubs.h \ + /usr/include/x86_64-linux-gnu/gnu/stubs-64.h \ + /usr/include/x86_64-linux-gnu/bits/types.h \ + /usr/include/x86_64-linux-gnu/bits/typesizes.h \ + /usr/include/x86_64-linux-gnu/bits/wchar.h \ + /usr/include/x86_64-linux-gnu/bits/stdint-intn.h \ + /usr/include/x86_64-linux-gnu/bits/stdint-uintn.h \ + /usr/include/errno.h \ + /usr/include/x86_64-linux-gnu/bits/errno.h \ + /usr/include/linux/errno.h \ + /usr/include/x86_64-linux-gnu/asm/errno.h \ + /usr/include/asm-generic/errno.h \ + /usr/include/asm-generic/errno-base.h \ + /usr/include/x86_64-linux-gnu/bits/types/error_t.h \ + /usr/include/stdlib.h \ + /usr/include/x86_64-linux-gnu/bits/waitflags.h \ + /usr/include/x86_64-linux-gnu/bits/waitstatus.h \ + /usr/include/x86_64-linux-gnu/bits/floatn.h \ + /usr/include/x86_64-linux-gnu/bits/floatn-common.h \ + /usr/include/x86_64-linux-gnu/bits/types/locale_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/__locale_t.h \ + /usr/include/x86_64-linux-gnu/sys/types.h \ + /usr/include/x86_64-linux-gnu/bits/types/clock_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/clockid_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/time_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/timer_t.h \ + /usr/include/endian.h \ + /usr/include/x86_64-linux-gnu/bits/endian.h \ + /usr/include/x86_64-linux-gnu/bits/byteswap.h \ + /usr/include/x86_64-linux-gnu/bits/uintn-identity.h \ + /usr/include/x86_64-linux-gnu/sys/select.h \ + /usr/include/x86_64-linux-gnu/bits/select.h \ + /usr/include/x86_64-linux-gnu/bits/types/sigset_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/__sigset_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/struct_timeval.h \ + /usr/include/x86_64-linux-gnu/bits/types/struct_timespec.h \ + /usr/include/x86_64-linux-gnu/bits/pthreadtypes.h \ + /usr/include/x86_64-linux-gnu/bits/thread-shared-types.h \ + /usr/include/x86_64-linux-gnu/bits/pthreadtypes-arch.h \ + /usr/include/alloca.h \ + /usr/include/x86_64-linux-gnu/bits/stdlib-bsearch.h \ + /usr/include/x86_64-linux-gnu/bits/stdlib-float.h \ + /usr/include/stdio.h \ + /usr/lib/gcc/x86_64-linux-gnu/8/include/stdarg.h \ + /usr/include/x86_64-linux-gnu/bits/types/__fpos_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/__mbstate_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/__fpos64_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/__FILE.h \ + /usr/include/x86_64-linux-gnu/bits/types/FILE.h \ + /usr/include/x86_64-linux-gnu/bits/types/struct_FILE.h \ + /usr/include/x86_64-linux-gnu/bits/types/cookie_io_functions_t.h \ + /usr/include/x86_64-linux-gnu/bits/stdio_lim.h \ + /usr/include/x86_64-linux-gnu/bits/sys_errlist.h \ + /usr/include/x86_64-linux-gnu/bits/stdio.h \ + /usr/include/string.h \ + /usr/include/strings.h \ + /usr/include/x86_64-linux-gnu/sys/mman.h \ + /usr/include/x86_64-linux-gnu/bits/mman.h \ + /usr/include/x86_64-linux-gnu/bits/mman-linux.h \ + /usr/include/x86_64-linux-gnu/bits/mman-shared.h \ + /usr/include/fcntl.h \ + /usr/include/x86_64-linux-gnu/bits/fcntl.h \ + /usr/include/x86_64-linux-gnu/bits/fcntl-linux.h \ + /usr/include/x86_64-linux-gnu/bits/types/struct_iovec.h \ + /usr/include/linux/falloc.h \ + /usr/include/x86_64-linux-gnu/bits/stat.h \ + /usr/include/byteswap.h \ + /usr/include/time.h \ + /usr/include/x86_64-linux-gnu/bits/time.h \ + /usr/include/x86_64-linux-gnu/bits/timex.h \ + /usr/include/x86_64-linux-gnu/bits/types/struct_tm.h \ + /usr/include/x86_64-linux-gnu/bits/types/struct_itimerspec.h \ + tools/../env/embedded.c \ + $(wildcard include/config/build/envcrc.h) \ + $(wildcard include/config/env/addr/redund.h) \ + $(wildcard include/config/env/offset.h) \ + include/config.h \ + $(wildcard include/config/boarddir.h) \ + include/config_defaults.h \ + $(wildcard include/config/defaults/h/.h) \ + $(wildcard include/config/bootm/linux.h) \ + $(wildcard include/config/bootm/netbsd.h) \ + $(wildcard include/config/bootm/plan9.h) \ + $(wildcard include/config/bootm/rtems.h) \ + $(wildcard include/config/bootm/vxworks.h) \ + $(wildcard include/config/gzip.h) \ + $(wildcard include/config/zlib.h) \ + include/config_uncmd_spl.h \ + $(wildcard include/config/uncmd/spl/h//.h) \ + $(wildcard include/config/spl/build.h) \ + $(wildcard include/config/spl/dm.h) \ + $(wildcard include/config/dm/serial.h) \ + $(wildcard include/config/dm/gpio.h) \ + $(wildcard include/config/dm/i2c.h) \ + $(wildcard include/config/dm/spi.h) \ + $(wildcard include/config/dm/warn.h) \ + $(wildcard include/config/dm/stdio.h) \ + include/configs/zynq-common.h \ + $(wildcard include/config/zynq/common/h.h) \ + $(wildcard include/config/cpu/freq/hz.h) \ + $(wildcard include/config/remake/elf.h) \ + $(wildcard include/config/sys/l2cache/off.h) \ + $(wildcard include/config/sys/l2/pl310.h) \ + $(wildcard include/config/sys/pl310/base.h) \ + $(wildcard include/config/sys/timerbase.h) \ + $(wildcard include/config/sys/timer/counts/down.h) \ + $(wildcard include/config/sys/timer/counter.h) \ + $(wildcard include/config/sys/baudrate/table.h) \ + $(wildcard include/config/arm/dcc.h) \ + $(wildcard include/config/zynq/gem.h) \ + $(wildcard include/config/sys/fault/echo/link/down.h) \ + $(wildcard include/config/bootp/may/fail.h) \ + $(wildcard include/config/zynq/qspi.h) \ + $(wildcard include/config/sf/default/speed.h) \ + $(wildcard include/config/mtd/nor/flash.h) \ + $(wildcard include/config/sys/flash/base.h) \ + $(wildcard include/config/sys/flash/size.h) \ + $(wildcard include/config/sys/max/flash/banks.h) \ + $(wildcard include/config/sys/max/flash/sect.h) \ + $(wildcard include/config/sys/flash/erase/tout.h) \ + $(wildcard include/config/sys/flash/write/tout.h) \ + $(wildcard include/config/flash/show/progress.h) \ + $(wildcard include/config/sys/flash/empty/info.h) \ + $(wildcard include/config/nand/zynq.h) \ + $(wildcard include/config/sys/max/nand/device.h) \ + $(wildcard include/config/sys/nand/onfi/detection.h) \ + $(wildcard include/config/usb/ehci/zynq.h) \ + $(wildcard include/config/ehci/is/tdi.h) \ + $(wildcard include/config/sys/dfu/data/buf/size.h) \ + $(wildcard include/config/thor/reset/off.h) \ + $(wildcard include/config/mmc/sdhci/zynq.h) \ + $(wildcard include/config/sys/i2c/zynq.h) \ + $(wildcard include/config/sys/i2c.h) \ + $(wildcard include/config/env/is/in/eeprom.h) \ + $(wildcard include/config/sys/i2c/eeprom/addr/len.h) \ + $(wildcard include/config/sys/i2c/eeprom/addr.h) \ + $(wildcard include/config/sys/eeprom/page/write/bits.h) \ + $(wildcard include/config/sys/eeprom/page/write/delay/ms.h) \ + $(wildcard include/config/sys/eeprom/size.h) \ + $(wildcard include/config/sys/i2c/mux/addr.h) \ + $(wildcard include/config/sys/i2c/mux/eeprom/sel.h) \ + $(wildcard include/config/extra/env/settings.h) \ + $(wildcard include/config/env/overwrite.h) \ + $(wildcard include/config/bootdelay.h) \ + $(wildcard include/config/preboot.h) \ + $(wildcard include/config/sys/load/addr.h) \ + $(wildcard include/config/cmd/mmc.h) \ + $(wildcard include/config/cmd/usb.h) \ + $(wildcard include/config/cmd/pxe.h) \ + $(wildcard include/config/cmd/dhcp.h) \ + $(wildcard include/config/clocks.h) \ + $(wildcard include/config/sys/maxargs.h) \ + $(wildcard include/config/sys/cbsize.h) \ + $(wildcard include/config/sys/pbsize.h) \ + $(wildcard include/config/sys/prompt.h) \ + $(wildcard include/config/sys/memtest/start.h) \ + $(wildcard include/config/sys/memtest/end.h) \ + $(wildcard include/config/sys/init/ram/addr.h) \ + $(wildcard include/config/sys/init/ram/size.h) \ + $(wildcard include/config/sys/init/sp/addr.h) \ + $(wildcard include/config/sys/bootm/len.h) \ + $(wildcard include/config/sys/mmc/max/device.h) \ + $(wildcard include/config/sys/ldscript.h) \ + $(wildcard include/config/sys/mmcsd/fs/boot/partition.h) \ + $(wildcard include/config/spl/fs/load/payload/name.h) \ + $(wildcard include/config/sys/dcache/off.h) \ + $(wildcard include/config/sys/spl/args/addr.h) \ + $(wildcard include/config/spl/fs/load/args/name.h) \ + $(wildcard include/config/spl/fs/load/kernel/name.h) \ + $(wildcard include/config/sys/mmcsd/raw/mode/args/sector.h) \ + $(wildcard include/config/sys/mmcsd/raw/mode/args/sectors.h) \ + $(wildcard include/config/sys/mmcsd/raw/mode/kernel/sector.h) \ + $(wildcard include/config/sys/spi/u/boot/offs.h) \ + $(wildcard include/config/sys/spi/args/offs.h) \ + $(wildcard include/config/sys/spi/args/size.h) \ + $(wildcard include/config/sys/spi/kernel/offs.h) \ + $(wildcard include/config/spl/text/base.h) \ + $(wildcard include/config/spl/max/size.h) \ + $(wildcard include/config/sys/spl/malloc/start.h) \ + $(wildcard include/config/spl/stack/r/addr.h) \ + $(wildcard include/config/sys/spl/malloc/size.h) \ + $(wildcard include/config/spl/stack.h) \ + $(wildcard include/config/spl/bss/start/addr.h) \ + $(wildcard include/config/spl/bss/max/size.h) \ + $(wildcard include/config/spl/load/fit/address.h) \ + $(wildcard include/config/sys/uboot/start.h) \ + $(wildcard include/config/sys/text/base.h) \ + include/config_distro_bootcmd.h \ + $(wildcard include/config/cmd/distro/bootcmd/h.h) \ + $(wildcard include/config/sandbox.h) \ + $(wildcard include/config/cmd/ubifs.h) \ + $(wildcard include/config/efi/loader.h) \ + $(wildcard include/config/arm64.h) \ + $(wildcard include/config/arm.h) \ + $(wildcard include/config/x86/run/32bit.h) \ + $(wildcard include/config/x86/run/64bit.h) \ + $(wildcard include/config/arch/rv32i.h) \ + $(wildcard include/config/arch/rv64i.h) \ + $(wildcard include/config/sata.h) \ + $(wildcard include/config/scsi.h) \ + $(wildcard include/config/ide.h) \ + $(wildcard include/config/dm/pci.h) \ + $(wildcard include/config/cmd/virtio.h) \ + $(wildcard include/config/x86.h) \ + $(wildcard include/config/cmd/dhcp/or/pxe.h) \ + $(wildcard include/config/bootcommand.h) \ + arch/arm/include/asm/config.h \ + $(wildcard include/config/h/.h) \ + $(wildcard include/config/lmb.h) \ + $(wildcard include/config/sys/boot/ramdisk/high.h) \ + $(wildcard include/config/arch/ls1021a.h) \ + $(wildcard include/config/cpu/pxa27x.h) \ + $(wildcard include/config/cpu/monahans.h) \ + $(wildcard include/config/cpu/pxa25x.h) \ + $(wildcard include/config/fsl/layerscape.h) \ + include/config_fallbacks.h \ + $(wildcard include/config/fallbacks/h.h) \ + $(wildcard include/config/spl.h) \ + $(wildcard include/config/spl/pad/to.h) \ + $(wildcard include/config/cmd/kgdb.h) \ + include/environment.h \ + $(wildcard include/config/env/is/in/flash.h) \ + $(wildcard include/config/env/addr.h) \ + $(wildcard include/config/env/offset/redund.h) \ + $(wildcard include/config/env/sect/size.h) \ + $(wildcard include/config/env/size.h) \ + $(wildcard include/config/env/size/redund.h) \ + $(wildcard include/config/sys/monitor/base.h) \ + $(wildcard include/config/sys/monitor/len.h) \ + $(wildcard include/config/sys/redundand/environment.h) \ + $(wildcard include/config/env/is/embedded.h) \ + $(wildcard include/config/env/is/in/mmc.h) \ + $(wildcard include/config/env/is/in/nand.h) \ + $(wildcard include/config/env/offset/oob.h) \ + $(wildcard include/config/env/is/in/ubi.h) \ + $(wildcard include/config/env/ubi/part.h) \ + $(wildcard include/config/env/ubi/volume.h) \ + $(wildcard include/config/env/ubi/volume/redund.h) \ + $(wildcard include/config/cmd/ubi.h) \ + $(wildcard include/config/env/is/in/onenand.h) \ + $(wildcard include/config/env/is/in/spi/flash.h) \ + $(wildcard include/config/needs/manual/reloc.h) \ + $(wildcard include/config/sys/mmc/env/part.h) \ + $(wildcard include/config/cmd/saveenv.h) \ + include/compiler.h \ + include/env_attr.h \ + include/env_callback.h \ + $(wildcard include/config/env/callback/list/static.h) \ + $(wildcard include/config/silent/console.h) \ + $(wildcard include/config/splashimage/guard.h) \ + $(wildcard include/config/regex.h) \ + $(wildcard include/config/cmd/dns.h) \ + $(wildcard include/config/cmd/net.h) \ + include/env_flags.h \ + $(wildcard include/config/env/flags/list/static.h) \ + $(wildcard include/config/overwrite/ethaddr/once.h) \ + $(wildcard include/config/cmd/env/flags.h) \ + include/linker_lists.h \ + include/linux/compiler.h \ + $(wildcard include/config/sparse/rcu/pointer.h) \ + $(wildcard include/config/trace/branch/profiling.h) \ + $(wildcard include/config/profile/all/branches.h) \ + $(wildcard include/config/kasan.h) \ + $(wildcard include/config/enable/must/check.h) \ + $(wildcard include/config/enable/warn/deprecated.h) \ + $(wildcard include/config/kprobes.h) \ + /usr/include/search.h \ + include/linux/stringify.h \ + +tools/env/embedded.o: $(deps_tools/env/embedded.o) + +$(deps_tools/env/embedded.o): diff --git a/tools/u-boot-tools/env/.gitignore b/tools/u-boot-tools/env/.gitignore new file mode 100644 index 0000000..8d28b2b --- /dev/null +++ b/tools/u-boot-tools/env/.gitignore @@ -0,0 +1,3 @@ +embedded.c +fw_printenv +fw_printenv_unstripped diff --git a/tools/u-boot-tools/env/Makefile b/tools/u-boot-tools/env/Makefile new file mode 100644 index 0000000..b627796 --- /dev/null +++ b/tools/u-boot-tools/env/Makefile @@ -0,0 +1,37 @@ +# SPDX-License-Identifier: GPL-2.0+ +# +# (C) Copyright 2002-2006 +# Wolfgang Denk, DENX Software Engineering, wd@denx.de. + +# fw_printenv is supposed to run on the target system, which means it should be +# built with cross tools. Although it may look weird, we only replace "HOSTCC" +# with "CC" here for the maximum code reuse of scripts/Makefile.host. +override HOSTCC = $(CC) + +# Compile for a hosted environment on the target +HOST_EXTRACFLAGS = -I$(srctree)/tools \ + $(patsubst -I%,-idirafter%, $(filter -I%, $(UBOOTINCLUDE))) \ + -idirafter $(srctree)/tools/env \ + -DUSE_HOSTCC \ + -DTEXT_BASE=$(TEXT_BASE) + +ifeq ($(MTD_VERSION),old) +HOST_EXTRACFLAGS += -DMTD_OLD +endif + +always := fw_printenv +hostprogs-y := fw_printenv + +lib-y += fw_env.o \ + crc32.o ctype.o linux_string.o \ + env_attr.o env_flags.o + +fw_printenv-objs := fw_env_main.o $(lib-y) + +quiet_cmd_crosstools_strip = STRIP $^ + cmd_crosstools_strip = $(STRIP) $^; touch $@ + +$(obj)/.strip: $(obj)/fw_printenv + $(call cmd,crosstools_strip) + +always += .strip diff --git a/tools/u-boot-tools/env/README b/tools/u-boot-tools/env/README new file mode 100644 index 0000000..7092513 --- /dev/null +++ b/tools/u-boot-tools/env/README @@ -0,0 +1,63 @@ + +This is a demo implementation of a Linux command line tool to access +the U-Boot's environment variables. + +In order to cross-compile fw_printenv, run + make CROSS_COMPILE=<your cross-compiler prefix> envtools +in the root directory of the U-Boot distribution. For example, + make CROSS_COMPILE=arm-linux- envtools + +You should then create a symlink from fw_setenv to fw_printenv. They use +the same program and its function depends on its basename. + +For the run-time utility configuration uncomment the line +#define CONFIG_FILE "/etc/fw_env.config" +in fw_env.h. + +For building against older versions of the MTD headers (meaning before +v2.6.8-rc1) it is required to pass the argument "MTD_VERSION=old" to +make. + +See comments in the fw_env.config file for definitions for the +particular board. + +Configuration can also be done via #defines in the fw_env.h file. The +following lines are relevant: + +#define HAVE_REDUND /* For systems with 2 env sectors */ +#define DEVICE1_NAME "/dev/mtd1" +#define DEVICE2_NAME "/dev/mtd2" +#define DEVICE1_OFFSET 0x0000 +#define ENV1_SIZE 0x4000 +#define DEVICE1_ESIZE 0x4000 +#define DEVICE1_ENVSECTORS 2 +#define DEVICE2_OFFSET 0x0000 +#define ENV2_SIZE 0x4000 +#define DEVICE2_ESIZE 0x4000 +#define DEVICE2_ENVSECTORS 2 + +Un-define HAVE_REDUND, if you want to use the utilities on a system +that does not have support for redundant environment enabled. +If HAVE_REDUND is undefined, DEVICE2_NAME is ignored, +as is ENV2_SIZE and DEVICE2_ESIZE. + +The DEVICEx_NAME constants define which MTD character devices are to +be used to access the environment. + +The DEVICEx_OFFSET constants define the environment offset within the +MTD character device. + +ENVx_SIZE defines the size in bytes taken by the environment, which +may be less then flash sector size, if the environment takes less +then 1 sector. + +DEVICEx_ESIZE defines the size of the first sector in the flash +partition where the environment resides. + +DEVICEx_ENVSECTORS defines the number of sectors that may be used for +this environment instance. On NAND this is used to limit the range +within which bad blocks are skipped, on NOR it is not used. + +To prevent losing changes to the environment and to prevent confusing the MTD +drivers, a lock file at /var/lock/fw_printenv.lock is used to serialize access +to the environment. diff --git a/tools/u-boot-tools/env/crc32.c b/tools/u-boot-tools/env/crc32.c new file mode 100644 index 0000000..34f8178 --- /dev/null +++ b/tools/u-boot-tools/env/crc32.c @@ -0,0 +1 @@ +#include "../../lib/crc32.c" diff --git a/tools/u-boot-tools/env/ctype.c b/tools/u-boot-tools/env/ctype.c new file mode 100644 index 0000000..21050e9 --- /dev/null +++ b/tools/u-boot-tools/env/ctype.c @@ -0,0 +1 @@ +#include "../../lib/ctype.c" diff --git a/tools/u-boot-tools/env/embedded.c b/tools/u-boot-tools/env/embedded.c new file mode 100644 index 0000000..68cb30f --- /dev/null +++ b/tools/u-boot-tools/env/embedded.c @@ -0,0 +1 @@ +#include <../env/embedded.c> diff --git a/tools/u-boot-tools/env/embedded.o b/tools/u-boot-tools/env/embedded.o new file mode 100644 index 0000000000000000000000000000000000000000..f3b65f22917931145684e84cee483887e921df02 GIT binary patch literal 928 zcmb<-^>JfjWMqH=Mg}_u1P><4z>t6>=l~XWVBlonU|?`}cD7Q`a7j(dOw3cT&@<LE z&^6P9Fu=-i5Fq1N7(V{TCe4i11q=+#3{2RRGcYqS;}B=XA<lv+o|>DKnv#;5qL<9T zpjTX(TauW>pjTW{1fervtdi7<5(d4L#F9h?y`<t|2EF9`+}zZ>5(d4z{E}2XcfU~G z;*!MVY)lKt6Om?M0EY$}ns!JWpb0|NB8w_Q#n3~DSpDh@44_!X2w`IN>p<-{Kz0-Z z0|QLIESfkdU4VpOv?)|SNDLXrB6E<~Fnur~5FcBrNq{O?0j1d)7#Kj91Im|x(wq#S obO+@iQ5+zH85kINki<Yts4$2L#)3HXi$E2+f>j|1s4!dr03PNkqW}N^ literal 0 HcmV?d00001 diff --git a/tools/u-boot-tools/env/env_attr.c b/tools/u-boot-tools/env/env_attr.c new file mode 100644 index 0000000..4d85363 --- /dev/null +++ b/tools/u-boot-tools/env/env_attr.c @@ -0,0 +1 @@ +#include "../../env/attr.c" diff --git a/tools/u-boot-tools/env/env_flags.c b/tools/u-boot-tools/env/env_flags.c new file mode 100644 index 0000000..71e13e2 --- /dev/null +++ b/tools/u-boot-tools/env/env_flags.c @@ -0,0 +1 @@ +#include "../../env/flags.c" diff --git a/tools/u-boot-tools/env/fw_env.c b/tools/u-boot-tools/env/fw_env.c new file mode 100644 index 0000000..a5d7595 --- /dev/null +++ b/tools/u-boot-tools/env/fw_env.c @@ -0,0 +1,1802 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2000-2010 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * (C) Copyright 2008 + * Guennadi Liakhovetski, DENX Software Engineering, lg@denx.de. + */ + +#define _GNU_SOURCE + +#include <compiler.h> +#include <errno.h> +#include <env_flags.h> +#include <fcntl.h> +#include <libgen.h> +#include <linux/fs.h> +#include <linux/stringify.h> +#include <ctype.h> +#include <stdio.h> +#include <stdlib.h> +#include <stddef.h> +#include <string.h> +#include <sys/types.h> +#include <sys/ioctl.h> +#include <sys/stat.h> +#include <unistd.h> +#include <dirent.h> + +#ifdef MTD_OLD +# include <stdint.h> +# include <linux/mtd/mtd.h> +#else +# define __user /* nothing */ +# include <mtd/mtd-user.h> +#endif + +#include <mtd/ubi-user.h> + +#include "fw_env_private.h" +#include "fw_env.h" + +struct env_opts default_opts = { +#ifdef CONFIG_FILE + .config_file = CONFIG_FILE +#endif +}; + +#define DIV_ROUND_UP(n, d) (((n) + (d) - 1) / (d)) + +#define min(x, y) ({ \ + typeof(x) _min1 = (x); \ + typeof(y) _min2 = (y); \ + (void) (&_min1 == &_min2); \ + _min1 < _min2 ? _min1 : _min2; }) + +struct envdev_s { + const char *devname; /* Device name */ + long long devoff; /* Device offset */ + ulong env_size; /* environment size */ + ulong erase_size; /* device erase size */ + ulong env_sectors; /* number of environment sectors */ + uint8_t mtd_type; /* type of the MTD device */ + int is_ubi; /* set if we use UBI volume */ +}; + +static struct envdev_s envdevices[2] = { + { + .mtd_type = MTD_ABSENT, + }, { + .mtd_type = MTD_ABSENT, + }, +}; + +static int dev_current; + +#define DEVNAME(i) envdevices[(i)].devname +#define DEVOFFSET(i) envdevices[(i)].devoff +#define ENVSIZE(i) envdevices[(i)].env_size +#define DEVESIZE(i) envdevices[(i)].erase_size +#define ENVSECTORS(i) envdevices[(i)].env_sectors +#define DEVTYPE(i) envdevices[(i)].mtd_type +#define IS_UBI(i) envdevices[(i)].is_ubi + +#define CUR_ENVSIZE ENVSIZE(dev_current) + +static unsigned long usable_envsize; +#define ENV_SIZE usable_envsize + +struct env_image_single { + uint32_t crc; /* CRC32 over data bytes */ + char data[]; +}; + +struct env_image_redundant { + uint32_t crc; /* CRC32 over data bytes */ + unsigned char flags; /* active or obsolete */ + char data[]; +}; + +enum flag_scheme { + FLAG_NONE, + FLAG_BOOLEAN, + FLAG_INCREMENTAL, +}; + +struct environment { + void *image; + uint32_t *crc; + unsigned char *flags; + char *data; + enum flag_scheme flag_scheme; +}; + +static struct environment environment = { + .flag_scheme = FLAG_NONE, +}; + +static int have_redund_env; + +static unsigned char active_flag = 1; +/* obsolete_flag must be 0 to efficiently set it on NOR flash without erasing */ +static unsigned char obsolete_flag = 0; + +#define DEFAULT_ENV_INSTANCE_STATIC +#include <env_default.h> + +#define UBI_DEV_START "/dev/ubi" +#define UBI_SYSFS "/sys/class/ubi" +#define UBI_VOL_NAME_PATT "ubi%d_%d" + +static int is_ubi_devname(const char *devname) +{ + return !strncmp(devname, UBI_DEV_START, sizeof(UBI_DEV_START) - 1); +} + +static int ubi_check_volume_sysfs_name(const char *volume_sysfs_name, + const char *volname) +{ + char path[256]; + FILE *file; + char *name; + int ret; + + strcpy(path, UBI_SYSFS "/"); + strcat(path, volume_sysfs_name); + strcat(path, "/name"); + + file = fopen(path, "r"); + if (!file) + return -1; + + ret = fscanf(file, "%ms", &name); + fclose(file); + if (ret <= 0 || !name) { + fprintf(stderr, + "Failed to read from file %s, ret = %d, name = %s\n", + path, ret, name); + return -1; + } + + if (!strcmp(name, volname)) { + free(name); + return 0; + } + free(name); + + return -1; +} + +static int ubi_get_volnum_by_name(int devnum, const char *volname) +{ + DIR *sysfs_ubi; + struct dirent *dirent; + int ret; + int tmp_devnum; + int volnum; + + sysfs_ubi = opendir(UBI_SYSFS); + if (!sysfs_ubi) + return -1; + +#ifdef DEBUG + fprintf(stderr, "Looking for volume name \"%s\"\n", volname); +#endif + + while (1) { + dirent = readdir(sysfs_ubi); + if (!dirent) + return -1; + + ret = sscanf(dirent->d_name, UBI_VOL_NAME_PATT, + &tmp_devnum, &volnum); + if (ret == 2 && devnum == tmp_devnum) { + if (ubi_check_volume_sysfs_name(dirent->d_name, + volname) == 0) + return volnum; + } + } + + return -1; +} + +static int ubi_get_devnum_by_devname(const char *devname) +{ + int devnum; + int ret; + + ret = sscanf(devname + sizeof(UBI_DEV_START) - 1, "%d", &devnum); + if (ret != 1) + return -1; + + return devnum; +} + +static const char *ubi_get_volume_devname(const char *devname, + const char *volname) +{ + char *volume_devname; + int volnum; + int devnum; + int ret; + + devnum = ubi_get_devnum_by_devname(devname); + if (devnum < 0) + return NULL; + + volnum = ubi_get_volnum_by_name(devnum, volname); + if (volnum < 0) + return NULL; + + ret = asprintf(&volume_devname, "%s_%d", devname, volnum); + if (ret < 0) + return NULL; + +#ifdef DEBUG + fprintf(stderr, "Found ubi volume \"%s:%s\" -> %s\n", + devname, volname, volume_devname); +#endif + + return volume_devname; +} + +static void ubi_check_dev(unsigned int dev_id) +{ + char *devname = (char *)DEVNAME(dev_id); + char *pname; + const char *volname = NULL; + const char *volume_devname; + + if (!is_ubi_devname(DEVNAME(dev_id))) + return; + + IS_UBI(dev_id) = 1; + + for (pname = devname; *pname != '\0'; pname++) { + if (*pname == ':') { + *pname = '\0'; + volname = pname + 1; + break; + } + } + + if (volname) { + /* Let's find real volume device name */ + volume_devname = ubi_get_volume_devname(devname, volname); + if (!volume_devname) { + fprintf(stderr, "Didn't found ubi volume \"%s\"\n", + volname); + return; + } + + free(devname); + DEVNAME(dev_id) = volume_devname; + } +} + +static int ubi_update_start(int fd, int64_t bytes) +{ + if (ioctl(fd, UBI_IOCVOLUP, &bytes)) + return -1; + return 0; +} + +static int ubi_read(int fd, void *buf, size_t count) +{ + ssize_t ret; + + while (count > 0) { + ret = read(fd, buf, count); + if (ret > 0) { + count -= ret; + buf += ret; + + continue; + } + + if (ret == 0) { + /* + * Happens in case of too short volume data size. If we + * return error status we will fail it will be treated + * as UBI device error. + * + * Leave catching this error to CRC check. + */ + fprintf(stderr, "Warning: end of data on ubi volume\n"); + return 0; + } else if (errno == EBADF) { + /* + * Happens in case of corrupted volume. The same as + * above, we cannot return error now, as we will still + * be able to successfully write environment later. + */ + fprintf(stderr, "Warning: corrupted volume?\n"); + return 0; + } else if (errno == EINTR) { + continue; + } + + fprintf(stderr, "Cannot read %u bytes from ubi volume, %s\n", + (unsigned int)count, strerror(errno)); + return -1; + } + + return 0; +} + +static int ubi_write(int fd, const void *buf, size_t count) +{ + ssize_t ret; + + while (count > 0) { + ret = write(fd, buf, count); + if (ret <= 0) { + if (ret < 0 && errno == EINTR) + continue; + + fprintf(stderr, "Cannot write %u bytes to ubi volume\n", + (unsigned int)count); + return -1; + } + + count -= ret; + buf += ret; + } + + return 0; +} + +static int flash_io(int mode); +static int parse_config(struct env_opts *opts); + +#if defined(CONFIG_FILE) +static int get_config(char *); +#endif + +static char *skip_chars(char *s) +{ + for (; *s != '\0'; s++) { + if (isblank(*s) || *s == '=') + return s; + } + return NULL; +} + +static char *skip_blanks(char *s) +{ + for (; *s != '\0'; s++) { + if (!isblank(*s)) + return s; + } + return NULL; +} + +/* + * s1 is either a simple 'name', or a 'name=value' pair. + * s2 is a 'name=value' pair. + * If the names match, return the value of s2, else NULL. + */ +static char *envmatch(char *s1, char *s2) +{ + if (s1 == NULL || s2 == NULL) + return NULL; + + while (*s1 == *s2++) + if (*s1++ == '=') + return s2; + if (*s1 == '\0' && *(s2 - 1) == '=') + return s2; + return NULL; +} + +/** + * Search the environment for a variable. + * Return the value, if found, or NULL, if not found. + */ +char *fw_getenv(char *name) +{ + char *env, *nxt; + + for (env = environment.data; *env; env = nxt + 1) { + char *val; + + for (nxt = env; *nxt; ++nxt) { + if (nxt >= &environment.data[ENV_SIZE]) { + fprintf(stderr, "## Error: " + "environment not terminated\n"); + return NULL; + } + } + val = envmatch(name, env); + if (!val) + continue; + return val; + } + return NULL; +} + +/* + * Search the default environment for a variable. + * Return the value, if found, or NULL, if not found. + */ +char *fw_getdefenv(char *name) +{ + char *env, *nxt; + + for (env = default_environment; *env; env = nxt + 1) { + char *val; + + for (nxt = env; *nxt; ++nxt) { + if (nxt >= &default_environment[ENV_SIZE]) { + fprintf(stderr, "## Error: " + "default environment not terminated\n"); + return NULL; + } + } + val = envmatch(name, env); + if (!val) + continue; + return val; + } + return NULL; +} + +/* + * Print the current definition of one, or more, or all + * environment variables + */ +int fw_printenv(int argc, char *argv[], int value_only, struct env_opts *opts) +{ + int i, rc = 0; + + if (value_only && argc != 1) { + fprintf(stderr, + "## Error: `-n'/`--noheader' option requires exactly one argument\n"); + return -1; + } + + if (!opts) + opts = &default_opts; + + if (fw_env_open(opts)) + return -1; + + if (argc == 0) { /* Print all env variables */ + char *env, *nxt; + for (env = environment.data; *env; env = nxt + 1) { + for (nxt = env; *nxt; ++nxt) { + if (nxt >= &environment.data[ENV_SIZE]) { + fprintf(stderr, "## Error: " + "environment not terminated\n"); + return -1; + } + } + + printf("%s\n", env); + } + fw_env_close(opts); + return 0; + } + + for (i = 0; i < argc; ++i) { /* print a subset of env variables */ + char *name = argv[i]; + char *val = NULL; + + val = fw_getenv(name); + if (!val) { + fprintf(stderr, "## Error: \"%s\" not defined\n", name); + rc = -1; + continue; + } + + if (value_only) { + puts(val); + break; + } + + printf("%s=%s\n", name, val); + } + + fw_env_close(opts); + + return rc; +} + +int fw_env_flush(struct env_opts *opts) +{ + if (!opts) + opts = &default_opts; + + /* + * Update CRC + */ + *environment.crc = crc32(0, (uint8_t *) environment.data, ENV_SIZE); + + /* write environment back to flash */ + if (flash_io(O_RDWR)) { + fprintf(stderr, "Error: can't write fw_env to flash\n"); + return -1; + } + + return 0; +} + +/* + * Set/Clear a single variable in the environment. + * This is called in sequence to update the environment + * in RAM without updating the copy in flash after each set + */ +int fw_env_write(char *name, char *value) +{ + int len; + char *env, *nxt; + char *oldval = NULL; + int deleting, creating, overwriting; + + /* + * search if variable with this name already exists + */ + for (nxt = env = environment.data; *env; env = nxt + 1) { + for (nxt = env; *nxt; ++nxt) { + if (nxt >= &environment.data[ENV_SIZE]) { + fprintf(stderr, "## Error: " + "environment not terminated\n"); + errno = EINVAL; + return -1; + } + } + oldval = envmatch(name, env); + if (oldval) + break; + } + + deleting = (oldval && !(value && strlen(value))); + creating = (!oldval && (value && strlen(value))); + overwriting = (oldval && (value && strlen(value))); + + /* check for permission */ + if (deleting) { + if (env_flags_validate_varaccess(name, + ENV_FLAGS_VARACCESS_PREVENT_DELETE)) { + printf("Can't delete \"%s\"\n", name); + errno = EROFS; + return -1; + } + } else if (overwriting) { + if (env_flags_validate_varaccess(name, + ENV_FLAGS_VARACCESS_PREVENT_OVERWR)) { + printf("Can't overwrite \"%s\"\n", name); + errno = EROFS; + return -1; + } else if (env_flags_validate_varaccess(name, + ENV_FLAGS_VARACCESS_PREVENT_NONDEF_OVERWR)) { + const char *defval = fw_getdefenv(name); + + if (defval == NULL) + defval = ""; + if (strcmp(oldval, defval) + != 0) { + printf("Can't overwrite \"%s\"\n", name); + errno = EROFS; + return -1; + } + } + } else if (creating) { + if (env_flags_validate_varaccess(name, + ENV_FLAGS_VARACCESS_PREVENT_CREATE)) { + printf("Can't create \"%s\"\n", name); + errno = EROFS; + return -1; + } + } else + /* Nothing to do */ + return 0; + + if (deleting || overwriting) { + if (*++nxt == '\0') { + *env = '\0'; + } else { + for (;;) { + *env = *nxt++; + if ((*env == '\0') && (*nxt == '\0')) + break; + ++env; + } + } + *++env = '\0'; + } + + /* Delete only ? */ + if (!value || !strlen(value)) + return 0; + + /* + * Append new definition at the end + */ + for (env = environment.data; *env || *(env + 1); ++env) + ; + if (env > environment.data) + ++env; + /* + * Overflow when: + * "name" + "=" + "val" +"\0\0" > CUR_ENVSIZE - (env-environment) + */ + len = strlen(name) + 2; + /* add '=' for first arg, ' ' for all others */ + len += strlen(value) + 1; + + if (len > (&environment.data[ENV_SIZE] - env)) { + fprintf(stderr, + "Error: environment overflow, \"%s\" deleted\n", name); + return -1; + } + + while ((*env = *name++) != '\0') + env++; + *env = '='; + while ((*++env = *value++) != '\0') + ; + + /* end is marked with double '\0' */ + *++env = '\0'; + + return 0; +} + +/* + * Deletes or sets environment variables. Returns -1 and sets errno error codes: + * 0 - OK + * EINVAL - need at least 1 argument + * EROFS - certain variables ("ethaddr", "serial#") cannot be + * modified or deleted + * + */ +int fw_env_set(int argc, char *argv[], struct env_opts *opts) +{ + int i; + size_t len; + char *name, **valv; + char *oldval; + char *value = NULL; + int valc; + int ret; + + if (!opts) + opts = &default_opts; + + if (argc < 1) { + fprintf(stderr, "## Error: variable name missing\n"); + errno = EINVAL; + return -1; + } + + if (fw_env_open(opts)) { + fprintf(stderr, "Error: environment not initialized\n"); + return -1; + } + + name = argv[0]; + valv = argv + 1; + valc = argc - 1; + + if (env_flags_validate_env_set_params(name, valv, valc) < 0) { + fw_env_close(opts); + return -1; + } + + len = 0; + for (i = 0; i < valc; ++i) { + char *val = valv[i]; + size_t val_len = strlen(val); + + if (value) + value[len - 1] = ' '; + oldval = value; + value = realloc(value, len + val_len + 1); + if (!value) { + fprintf(stderr, + "Cannot malloc %zu bytes: %s\n", + len, strerror(errno)); + free(oldval); + return -1; + } + + memcpy(value + len, val, val_len); + len += val_len; + value[len++] = '\0'; + } + + fw_env_write(name, value); + + free(value); + + ret = fw_env_flush(opts); + fw_env_close(opts); + + return ret; +} + +/* + * Parse a file and configure the u-boot variables. + * The script file has a very simple format, as follows: + * + * Each line has a couple with name, value: + * <white spaces>variable_name<white spaces>variable_value + * + * Both variable_name and variable_value are interpreted as strings. + * Any character after <white spaces> and before ending \r\n is interpreted + * as variable's value (no comment allowed on these lines !) + * + * Comments are allowed if the first character in the line is # + * + * Returns -1 and sets errno error codes: + * 0 - OK + * -1 - Error + */ +int fw_parse_script(char *fname, struct env_opts *opts) +{ + FILE *fp; + char *line = NULL; + size_t linesize = 0; + char *name; + char *val; + int lineno = 0; + int len; + int ret = 0; + + if (!opts) + opts = &default_opts; + + if (fw_env_open(opts)) { + fprintf(stderr, "Error: environment not initialized\n"); + return -1; + } + + if (strcmp(fname, "-") == 0) + fp = stdin; + else { + fp = fopen(fname, "r"); + if (fp == NULL) { + fprintf(stderr, "I cannot open %s for reading\n", + fname); + return -1; + } + } + + while ((len = getline(&line, &linesize, fp)) != -1) { + lineno++; + + /* + * Read a whole line from the file. If the line is not + * terminated, reports an error and exit. + */ + if (line[len - 1] != '\n') { + fprintf(stderr, + "Line %d not correctly terminated\n", + lineno); + ret = -1; + break; + } + + /* Drop ending line feed / carriage return */ + line[--len] = '\0'; + if (len && line[len - 1] == '\r') + line[--len] = '\0'; + + /* Skip comment or empty lines */ + if (len == 0 || line[0] == '#') + continue; + + /* + * Search for variable's name remove leading whitespaces + */ + name = skip_blanks(line); + if (!name) + continue; + + /* The first white space is the end of variable name */ + val = skip_chars(name); + len = strlen(name); + if (val) { + *val++ = '\0'; + if ((val - name) < len) + val = skip_blanks(val); + else + val = NULL; + } +#ifdef DEBUG + fprintf(stderr, "Setting %s : %s\n", + name, val ? val : " removed"); +#endif + + if (env_flags_validate_type(name, val) < 0) { + ret = -1; + break; + } + + /* + * If there is an error setting a variable, + * try to save the environment and returns an error + */ + if (fw_env_write(name, val)) { + fprintf(stderr, + "fw_env_write returns with error : %s\n", + strerror(errno)); + ret = -1; + break; + } + + } + free(line); + + /* Close file if not stdin */ + if (strcmp(fname, "-") != 0) + fclose(fp); + + ret |= fw_env_flush(opts); + + fw_env_close(opts); + + return ret; +} + +/** + * environment_end() - compute offset of first byte right after environment + * @dev - index of enviroment buffer + * Return: + * device offset of first byte right after environment + */ +off_t environment_end(int dev) +{ + /* environment is block aligned */ + return DEVOFFSET(dev) + ENVSECTORS(dev) * DEVESIZE(dev); +} + +/* + * Test for bad block on NAND, just returns 0 on NOR, on NAND: + * 0 - block is good + * > 0 - block is bad + * < 0 - failed to test + */ +static int flash_bad_block(int fd, uint8_t mtd_type, loff_t blockstart) +{ + if (mtd_type == MTD_NANDFLASH) { + int badblock = ioctl(fd, MEMGETBADBLOCK, &blockstart); + + if (badblock < 0) { + perror("Cannot read bad block mark"); + return badblock; + } + + if (badblock) { +#ifdef DEBUG + fprintf(stderr, "Bad block at 0x%llx, skipping\n", + (unsigned long long)blockstart); +#endif + return badblock; + } + } + + return 0; +} + +/* + * Read data from flash at an offset into a provided buffer. On NAND it skips + * bad blocks but makes sure it stays within ENVSECTORS (dev) starting from + * the DEVOFFSET (dev) block. On NOR the loop is only run once. + */ +static int flash_read_buf(int dev, int fd, void *buf, size_t count, + off_t offset) +{ + size_t blocklen; /* erase / write length - one block on NAND, + 0 on NOR */ + size_t processed = 0; /* progress counter */ + size_t readlen = count; /* current read length */ + off_t block_seek; /* offset inside the current block to the start + of the data */ + loff_t blockstart; /* running start of the current block - + MEMGETBADBLOCK needs 64 bits */ + int rc; + + blockstart = (offset / DEVESIZE(dev)) * DEVESIZE(dev); + + /* Offset inside a block */ + block_seek = offset - blockstart; + + if (DEVTYPE(dev) == MTD_NANDFLASH) { + /* + * NAND: calculate which blocks we are reading. We have + * to read one block at a time to skip bad blocks. + */ + blocklen = DEVESIZE(dev); + + /* Limit to one block for the first read */ + if (readlen > blocklen - block_seek) + readlen = blocklen - block_seek; + } else { + blocklen = 0; + } + + /* This only runs once on NOR flash */ + while (processed < count) { + rc = flash_bad_block(fd, DEVTYPE(dev), blockstart); + if (rc < 0) /* block test failed */ + return -1; + + if (blockstart + block_seek + readlen > environment_end(dev)) { + /* End of range is reached */ + fprintf(stderr, "Too few good blocks within range\n"); + return -1; + } + + if (rc) { /* block is bad */ + blockstart += blocklen; + continue; + } + + /* + * If a block is bad, we retry in the next block at the same + * offset - see env/nand.c::writeenv() + */ + lseek(fd, blockstart + block_seek, SEEK_SET); + + rc = read(fd, buf + processed, readlen); + if (rc != readlen) { + fprintf(stderr, "Read error on %s: %s\n", + DEVNAME(dev), strerror(errno)); + return -1; + } +#ifdef DEBUG + fprintf(stderr, "Read 0x%x bytes at 0x%llx on %s\n", + rc, (unsigned long long)blockstart + block_seek, + DEVNAME(dev)); +#endif + processed += readlen; + readlen = min(blocklen, count - processed); + block_seek = 0; + blockstart += blocklen; + } + + return processed; +} + +/* + * Write count bytes from begin of environment, but stay within + * ENVSECTORS(dev) sectors of + * DEVOFFSET (dev). Similar to the read case above, on NOR and dataflash we + * erase and write the whole data at once. + */ +static int flash_write_buf(int dev, int fd, void *buf, size_t count) +{ + void *data; + struct erase_info_user erase; + size_t blocklen; /* length of NAND block / NOR erase sector */ + size_t erase_len; /* whole area that can be erased - may include + bad blocks */ + size_t erasesize; /* erase / write length - one block on NAND, + whole area on NOR */ + size_t processed = 0; /* progress counter */ + size_t write_total; /* total size to actually write - excluding + bad blocks */ + off_t erase_offset; /* offset to the first erase block (aligned) + below offset */ + off_t block_seek; /* offset inside the erase block to the start + of the data */ + loff_t blockstart; /* running start of the current block - + MEMGETBADBLOCK needs 64 bits */ + int rc; + + /* + * For mtd devices only offset and size of the environment do matter + */ + if (DEVTYPE(dev) == MTD_ABSENT) { + blocklen = count; + erase_len = blocklen; + blockstart = DEVOFFSET(dev); + block_seek = 0; + write_total = blocklen; + } else { + blocklen = DEVESIZE(dev); + + erase_offset = DEVOFFSET(dev); + + /* Maximum area we may use */ + erase_len = environment_end(dev) - erase_offset; + + blockstart = erase_offset; + + /* Offset inside a block */ + block_seek = DEVOFFSET(dev) - erase_offset; + + /* + * Data size we actually write: from the start of the block + * to the start of the data, then count bytes of data, and + * to the end of the block + */ + write_total = ((block_seek + count + blocklen - 1) / + blocklen) * blocklen; + } + + /* + * Support data anywhere within erase sectors: read out the complete + * area to be erased, replace the environment image, write the whole + * block back again. + */ + if (write_total > count) { + data = malloc(erase_len); + if (!data) { + fprintf(stderr, + "Cannot malloc %zu bytes: %s\n", + erase_len, strerror(errno)); + return -1; + } + + rc = flash_read_buf(dev, fd, data, write_total, erase_offset); + if (write_total != rc) + return -1; + +#ifdef DEBUG + fprintf(stderr, "Preserving data "); + if (block_seek != 0) + fprintf(stderr, "0x%x - 0x%lx", 0, block_seek - 1); + if (block_seek + count != write_total) { + if (block_seek != 0) + fprintf(stderr, " and "); + fprintf(stderr, "0x%lx - 0x%lx", + (unsigned long)block_seek + count, + (unsigned long)write_total - 1); + } + fprintf(stderr, "\n"); +#endif + /* Overwrite the old environment */ + memcpy(data + block_seek, buf, count); + } else { + /* + * We get here, iff offset is block-aligned and count is a + * multiple of blocklen - see write_total calculation above + */ + data = buf; + } + + if (DEVTYPE(dev) == MTD_NANDFLASH) { + /* + * NAND: calculate which blocks we are writing. We have + * to write one block at a time to skip bad blocks. + */ + erasesize = blocklen; + } else { + erasesize = erase_len; + } + + erase.length = erasesize; + + /* This only runs once on NOR flash and SPI-dataflash */ + while (processed < write_total) { + rc = flash_bad_block(fd, DEVTYPE(dev), blockstart); + if (rc < 0) /* block test failed */ + return rc; + + if (blockstart + erasesize > environment_end(dev)) { + fprintf(stderr, "End of range reached, aborting\n"); + return -1; + } + + if (rc) { /* block is bad */ + blockstart += blocklen; + continue; + } + + if (DEVTYPE(dev) != MTD_ABSENT) { + erase.start = blockstart; + ioctl(fd, MEMUNLOCK, &erase); + /* These do not need an explicit erase cycle */ + if (DEVTYPE(dev) != MTD_DATAFLASH) + if (ioctl(fd, MEMERASE, &erase) != 0) { + fprintf(stderr, + "MTD erase error on %s: %s\n", + DEVNAME(dev), strerror(errno)); + return -1; + } + } + + if (lseek(fd, blockstart, SEEK_SET) == -1) { + fprintf(stderr, + "Seek error on %s: %s\n", + DEVNAME(dev), strerror(errno)); + return -1; + } +#ifdef DEBUG + fprintf(stderr, "Write 0x%llx bytes at 0x%llx\n", + (unsigned long long)erasesize, + (unsigned long long)blockstart); +#endif + if (write(fd, data + processed, erasesize) != erasesize) { + fprintf(stderr, "Write error on %s: %s\n", + DEVNAME(dev), strerror(errno)); + return -1; + } + + if (DEVTYPE(dev) != MTD_ABSENT) + ioctl(fd, MEMLOCK, &erase); + + processed += erasesize; + block_seek = 0; + blockstart += erasesize; + } + + if (write_total > count) + free(data); + + return processed; +} + +/* + * Set obsolete flag at offset - NOR flash only + */ +static int flash_flag_obsolete(int dev, int fd, off_t offset) +{ + int rc; + struct erase_info_user erase; + + erase.start = DEVOFFSET(dev); + erase.length = DEVESIZE(dev); + /* This relies on the fact, that obsolete_flag == 0 */ + rc = lseek(fd, offset, SEEK_SET); + if (rc < 0) { + fprintf(stderr, "Cannot seek to set the flag on %s\n", + DEVNAME(dev)); + return rc; + } + ioctl(fd, MEMUNLOCK, &erase); + rc = write(fd, &obsolete_flag, sizeof(obsolete_flag)); + ioctl(fd, MEMLOCK, &erase); + if (rc < 0) + perror("Could not set obsolete flag"); + + return rc; +} + +static int flash_write(int fd_current, int fd_target, int dev_target) +{ + int rc; + + switch (environment.flag_scheme) { + case FLAG_NONE: + break; + case FLAG_INCREMENTAL: + (*environment.flags)++; + break; + case FLAG_BOOLEAN: + *environment.flags = active_flag; + break; + default: + fprintf(stderr, "Unimplemented flash scheme %u\n", + environment.flag_scheme); + return -1; + } + +#ifdef DEBUG + fprintf(stderr, "Writing new environment at 0x%llx on %s\n", + DEVOFFSET(dev_target), DEVNAME(dev_target)); +#endif + + if (IS_UBI(dev_target)) { + if (ubi_update_start(fd_target, CUR_ENVSIZE) < 0) + return 0; + return ubi_write(fd_target, environment.image, CUR_ENVSIZE); + } + + rc = flash_write_buf(dev_target, fd_target, environment.image, + CUR_ENVSIZE); + if (rc < 0) + return rc; + + if (environment.flag_scheme == FLAG_BOOLEAN) { + /* Have to set obsolete flag */ + off_t offset = DEVOFFSET(dev_current) + + offsetof(struct env_image_redundant, flags); +#ifdef DEBUG + fprintf(stderr, + "Setting obsolete flag in environment at 0x%llx on %s\n", + DEVOFFSET(dev_current), DEVNAME(dev_current)); +#endif + flash_flag_obsolete(dev_current, fd_current, offset); + } + + return 0; +} + +static int flash_read(int fd) +{ + int rc; + + if (IS_UBI(dev_current)) { + DEVTYPE(dev_current) = MTD_ABSENT; + + return ubi_read(fd, environment.image, CUR_ENVSIZE); + } + + rc = flash_read_buf(dev_current, fd, environment.image, CUR_ENVSIZE, + DEVOFFSET(dev_current)); + if (rc != CUR_ENVSIZE) + return -1; + + return 0; +} + +static int flash_open_tempfile(const char **dname, const char **target_temp) +{ + char *dup_name = strdup(DEVNAME(dev_current)); + char *temp_name = NULL; + int rc = -1; + + if (!dup_name) + return -1; + + *dname = dirname(dup_name); + if (!*dname) + goto err; + + rc = asprintf(&temp_name, "%s/XXXXXX", *dname); + if (rc == -1) + goto err; + + rc = mkstemp(temp_name); + if (rc == -1) { + /* fall back to in place write */ + fprintf(stderr, + "Can't create %s: %s\n", temp_name, strerror(errno)); + free(temp_name); + } else { + *target_temp = temp_name; + /* deliberately leak dup_name as dname /might/ point into + * it and we need it for our caller + */ + dup_name = NULL; + } + +err: + if (dup_name) + free(dup_name); + + return rc; +} + +static int flash_io_write(int fd_current) +{ + int fd_target = -1, rc, dev_target; + const char *dname, *target_temp = NULL; + + if (have_redund_env) { + /* switch to next partition for writing */ + dev_target = !dev_current; + /* dev_target: fd_target, erase_target */ + fd_target = open(DEVNAME(dev_target), O_RDWR); + if (fd_target < 0) { + fprintf(stderr, + "Can't open %s: %s\n", + DEVNAME(dev_target), strerror(errno)); + rc = -1; + goto exit; + } + } else { + struct stat sb; + + if (fstat(fd_current, &sb) == 0 && S_ISREG(sb.st_mode)) { + /* if any part of flash_open_tempfile() fails we fall + * back to in-place writes + */ + fd_target = flash_open_tempfile(&dname, &target_temp); + } + dev_target = dev_current; + if (fd_target == -1) + fd_target = fd_current; + } + + rc = flash_write(fd_current, fd_target, dev_target); + + if (fsync(fd_current) && !(errno == EINVAL || errno == EROFS)) { + fprintf(stderr, + "fsync failed on %s: %s\n", + DEVNAME(dev_current), strerror(errno)); + } + + if (fd_current != fd_target) { + if (fsync(fd_target) && + !(errno == EINVAL || errno == EROFS)) { + fprintf(stderr, + "fsync failed on %s: %s\n", + DEVNAME(dev_current), strerror(errno)); + } + + if (close(fd_target)) { + fprintf(stderr, + "I/O error on %s: %s\n", + DEVNAME(dev_target), strerror(errno)); + rc = -1; + } + + if (target_temp) { + int dir_fd; + + dir_fd = open(dname, O_DIRECTORY | O_RDONLY); + if (dir_fd == -1) + fprintf(stderr, + "Can't open %s: %s\n", + dname, strerror(errno)); + + if (rename(target_temp, DEVNAME(dev_target))) { + fprintf(stderr, + "rename failed %s => %s: %s\n", + target_temp, DEVNAME(dev_target), + strerror(errno)); + rc = -1; + } + + if (dir_fd != -1 && fsync(dir_fd)) + fprintf(stderr, + "fsync failed on %s: %s\n", + dname, strerror(errno)); + + if (dir_fd != -1 && close(dir_fd)) + fprintf(stderr, + "I/O error on %s: %s\n", + dname, strerror(errno)); + } + } + exit: + return rc; +} + +static int flash_io(int mode) +{ + int fd_current, rc; + + /* dev_current: fd_current, erase_current */ + fd_current = open(DEVNAME(dev_current), mode); + if (fd_current < 0) { + fprintf(stderr, + "Can't open %s: %s\n", + DEVNAME(dev_current), strerror(errno)); + return -1; + } + + if (mode == O_RDWR) { + rc = flash_io_write(fd_current); + } else { + rc = flash_read(fd_current); + } + + if (close(fd_current)) { + fprintf(stderr, + "I/O error on %s: %s\n", + DEVNAME(dev_current), strerror(errno)); + return -1; + } + + return rc; +} + +/* + * Prevent confusion if running from erased flash memory + */ +int fw_env_open(struct env_opts *opts) +{ + int crc0, crc0_ok; + unsigned char flag0; + void *addr0 = NULL; + + int crc1, crc1_ok; + unsigned char flag1; + void *addr1 = NULL; + + int ret; + + struct env_image_single *single; + struct env_image_redundant *redundant; + + if (!opts) + opts = &default_opts; + + if (parse_config(opts)) /* should fill envdevices */ + return -EINVAL; + + addr0 = calloc(1, CUR_ENVSIZE); + if (addr0 == NULL) { + fprintf(stderr, + "Not enough memory for environment (%ld bytes)\n", + CUR_ENVSIZE); + ret = -ENOMEM; + goto open_cleanup; + } + + /* read environment from FLASH to local buffer */ + environment.image = addr0; + + if (have_redund_env) { + redundant = addr0; + environment.crc = &redundant->crc; + environment.flags = &redundant->flags; + environment.data = redundant->data; + } else { + single = addr0; + environment.crc = &single->crc; + environment.flags = NULL; + environment.data = single->data; + } + + dev_current = 0; + if (flash_io(O_RDONLY)) { + ret = -EIO; + goto open_cleanup; + } + + crc0 = crc32(0, (uint8_t *)environment.data, ENV_SIZE); + + crc0_ok = (crc0 == *environment.crc); + if (!have_redund_env) { + if (!crc0_ok) { + fprintf(stderr, + "Warning: Bad CRC, using default environment\n"); + memcpy(environment.data, default_environment, + sizeof(default_environment)); + } + } else { + flag0 = *environment.flags; + + dev_current = 1; + addr1 = calloc(1, CUR_ENVSIZE); + if (addr1 == NULL) { + fprintf(stderr, + "Not enough memory for environment (%ld bytes)\n", + CUR_ENVSIZE); + ret = -ENOMEM; + goto open_cleanup; + } + redundant = addr1; + + /* + * have to set environment.image for flash_read(), careful - + * other pointers in environment still point inside addr0 + */ + environment.image = addr1; + if (flash_io(O_RDONLY)) { + ret = -EIO; + goto open_cleanup; + } + + /* Check flag scheme compatibility */ + if (DEVTYPE(dev_current) == MTD_NORFLASH && + DEVTYPE(!dev_current) == MTD_NORFLASH) { + environment.flag_scheme = FLAG_BOOLEAN; + } else if (DEVTYPE(dev_current) == MTD_NANDFLASH && + DEVTYPE(!dev_current) == MTD_NANDFLASH) { + environment.flag_scheme = FLAG_INCREMENTAL; + } else if (DEVTYPE(dev_current) == MTD_DATAFLASH && + DEVTYPE(!dev_current) == MTD_DATAFLASH) { + environment.flag_scheme = FLAG_BOOLEAN; + } else if (DEVTYPE(dev_current) == MTD_UBIVOLUME && + DEVTYPE(!dev_current) == MTD_UBIVOLUME) { + environment.flag_scheme = FLAG_INCREMENTAL; + } else if (DEVTYPE(dev_current) == MTD_ABSENT && + DEVTYPE(!dev_current) == MTD_ABSENT && + IS_UBI(dev_current) == IS_UBI(!dev_current)) { + environment.flag_scheme = FLAG_INCREMENTAL; + } else { + fprintf(stderr, "Incompatible flash types!\n"); + ret = -EINVAL; + goto open_cleanup; + } + + crc1 = crc32(0, (uint8_t *)redundant->data, ENV_SIZE); + + crc1_ok = (crc1 == redundant->crc); + flag1 = redundant->flags; + + if (crc0_ok && !crc1_ok) { + dev_current = 0; + } else if (!crc0_ok && crc1_ok) { + dev_current = 1; + } else if (!crc0_ok && !crc1_ok) { + fprintf(stderr, + "Warning: Bad CRC, using default environment\n"); + memcpy(environment.data, default_environment, + sizeof(default_environment)); + dev_current = 0; + } else { + switch (environment.flag_scheme) { + case FLAG_BOOLEAN: + if (flag0 == active_flag && + flag1 == obsolete_flag) { + dev_current = 0; + } else if (flag0 == obsolete_flag && + flag1 == active_flag) { + dev_current = 1; + } else if (flag0 == flag1) { + dev_current = 0; + } else if (flag0 == 0xFF) { + dev_current = 0; + } else if (flag1 == 0xFF) { + dev_current = 1; + } else { + dev_current = 0; + } + break; + case FLAG_INCREMENTAL: + if (flag0 == 255 && flag1 == 0) + dev_current = 1; + else if ((flag1 == 255 && flag0 == 0) || + flag0 >= flag1) + dev_current = 0; + else /* flag1 > flag0 */ + dev_current = 1; + break; + default: + fprintf(stderr, "Unknown flag scheme %u\n", + environment.flag_scheme); + return -1; + } + } + + /* + * If we are reading, we don't need the flag and the CRC any + * more, if we are writing, we will re-calculate CRC and update + * flags before writing out + */ + if (dev_current) { + environment.image = addr1; + environment.crc = &redundant->crc; + environment.flags = &redundant->flags; + environment.data = redundant->data; + free(addr0); + } else { + environment.image = addr0; + /* Other pointers are already set */ + free(addr1); + } +#ifdef DEBUG + fprintf(stderr, "Selected env in %s\n", DEVNAME(dev_current)); +#endif + } + return 0; + + open_cleanup: + if (addr0) + free(addr0); + + if (addr1) + free(addr0); + + return ret; +} + +/* + * Simply free allocated buffer with environment + */ +int fw_env_close(struct env_opts *opts) +{ + if (environment.image) + free(environment.image); + + environment.image = NULL; + + return 0; +} + +static int check_device_config(int dev) +{ + struct stat st; + int32_t lnum = 0; + int fd, rc = 0; + + /* Fills in IS_UBI(), converts DEVNAME() with ubi volume name */ + ubi_check_dev(dev); + + fd = open(DEVNAME(dev), O_RDONLY); + if (fd < 0) { + fprintf(stderr, + "Cannot open %s: %s\n", DEVNAME(dev), strerror(errno)); + return -1; + } + + rc = fstat(fd, &st); + if (rc < 0) { + fprintf(stderr, "Cannot stat the file %s\n", DEVNAME(dev)); + goto err; + } + + if (IS_UBI(dev)) { + rc = ioctl(fd, UBI_IOCEBISMAP, &lnum); + if (rc < 0) { + fprintf(stderr, "Cannot get UBI information for %s\n", + DEVNAME(dev)); + goto err; + } + } else if (S_ISCHR(st.st_mode)) { + struct mtd_info_user mtdinfo; + rc = ioctl(fd, MEMGETINFO, &mtdinfo); + if (rc < 0) { + fprintf(stderr, "Cannot get MTD information for %s\n", + DEVNAME(dev)); + goto err; + } + if (mtdinfo.type != MTD_NORFLASH && + mtdinfo.type != MTD_NANDFLASH && + mtdinfo.type != MTD_DATAFLASH && + mtdinfo.type != MTD_UBIVOLUME) { + fprintf(stderr, "Unsupported flash type %u on %s\n", + mtdinfo.type, DEVNAME(dev)); + goto err; + } + DEVTYPE(dev) = mtdinfo.type; + if (DEVESIZE(dev) == 0) + /* Assume the erase size is the same as the env-size */ + DEVESIZE(dev) = ENVSIZE(dev); + } else { + uint64_t size; + DEVTYPE(dev) = MTD_ABSENT; + if (DEVESIZE(dev) == 0) + /* Assume the erase size to be 512 bytes */ + DEVESIZE(dev) = 0x200; + + /* + * Check for negative offsets, treat it as backwards offset + * from the end of the block device + */ + if (DEVOFFSET(dev) < 0) { + rc = ioctl(fd, BLKGETSIZE64, &size); + if (rc < 0) { + fprintf(stderr, + "Could not get block device size on %s\n", + DEVNAME(dev)); + goto err; + } + + DEVOFFSET(dev) = DEVOFFSET(dev) + size; +#ifdef DEBUG + fprintf(stderr, + "Calculated device offset 0x%llx on %s\n", + DEVOFFSET(dev), DEVNAME(dev)); +#endif + } + } + + if (ENVSECTORS(dev) == 0) + /* Assume enough sectors to cover the environment */ + ENVSECTORS(dev) = DIV_ROUND_UP(ENVSIZE(dev), DEVESIZE(dev)); + + if (DEVOFFSET(dev) % DEVESIZE(dev) != 0) { + fprintf(stderr, + "Environment does not start on (erase) block boundary\n"); + errno = EINVAL; + return -1; + } + + if (ENVSIZE(dev) > ENVSECTORS(dev) * DEVESIZE(dev)) { + fprintf(stderr, + "Environment does not fit into available sectors\n"); + errno = EINVAL; + return -1; + } + + err: + close(fd); + return rc; +} + +static int parse_config(struct env_opts *opts) +{ + int rc; + + if (!opts) + opts = &default_opts; + +#if defined(CONFIG_FILE) + /* Fills in DEVNAME(), ENVSIZE(), DEVESIZE(). Or don't. */ + if (get_config(opts->config_file)) { + fprintf(stderr, "Cannot parse config file '%s': %m\n", + opts->config_file); + return -1; + } +#else + DEVNAME(0) = DEVICE1_NAME; + DEVOFFSET(0) = DEVICE1_OFFSET; + ENVSIZE(0) = ENV1_SIZE; + + /* Set defaults for DEVESIZE, ENVSECTORS later once we + * know DEVTYPE + */ +#ifdef DEVICE1_ESIZE + DEVESIZE(0) = DEVICE1_ESIZE; +#endif +#ifdef DEVICE1_ENVSECTORS + ENVSECTORS(0) = DEVICE1_ENVSECTORS; +#endif + +#ifdef HAVE_REDUND + DEVNAME(1) = DEVICE2_NAME; + DEVOFFSET(1) = DEVICE2_OFFSET; + ENVSIZE(1) = ENV2_SIZE; + + /* Set defaults for DEVESIZE, ENVSECTORS later once we + * know DEVTYPE + */ +#ifdef DEVICE2_ESIZE + DEVESIZE(1) = DEVICE2_ESIZE; +#endif +#ifdef DEVICE2_ENVSECTORS + ENVSECTORS(1) = DEVICE2_ENVSECTORS; +#endif + have_redund_env = 1; +#endif +#endif + rc = check_device_config(0); + if (rc < 0) + return rc; + + if (have_redund_env) { + rc = check_device_config(1); + if (rc < 0) + return rc; + + if (ENVSIZE(0) != ENVSIZE(1)) { + fprintf(stderr, + "Redundant environments have unequal size"); + return -1; + } + } + + usable_envsize = CUR_ENVSIZE - sizeof(uint32_t); + if (have_redund_env) + usable_envsize -= sizeof(char); + + return 0; +} + +#if defined(CONFIG_FILE) +static int get_config(char *fname) +{ + FILE *fp; + int i = 0; + int rc; + char *line = NULL; + size_t linesize = 0; + char *devname; + + fp = fopen(fname, "r"); + if (fp == NULL) + return -1; + + while (i < 2 && getline(&line, &linesize, fp) != -1) { + /* Skip comment strings */ + if (line[0] == '#') + continue; + + rc = sscanf(line, "%ms %lli %lx %lx %lx", + &devname, + &DEVOFFSET(i), + &ENVSIZE(i), &DEVESIZE(i), &ENVSECTORS(i)); + + if (rc < 3) + continue; + + DEVNAME(i) = devname; + + /* Set defaults for DEVESIZE, ENVSECTORS later once we + * know DEVTYPE + */ + + i++; + } + free(line); + fclose(fp); + + have_redund_env = i - 1; + if (!i) { /* No valid entries found */ + errno = EINVAL; + return -1; + } else + return 0; +} +#endif diff --git a/tools/u-boot-tools/env/fw_env.config b/tools/u-boot-tools/env/fw_env.config new file mode 100644 index 0000000..053895a --- /dev/null +++ b/tools/u-boot-tools/env/fw_env.config @@ -0,0 +1,38 @@ +# Configuration file for fw_(printenv/setenv) utility. +# Up to two entries are valid, in this case the redundant +# environment sector is assumed present. +# Notice, that the "Number of sectors" is not required on NOR and SPI-dataflash. +# Futhermore, if the Flash sector size is omitted, this value is assumed to +# be the same as the Environment size, which is valid for NOR and SPI-dataflash +# Device offset must be prefixed with 0x to be parsed as a hexadecimal value. + +# NOR example +# MTD device name Device offset Env. size Flash sector size Number of sectors +/dev/mtd1 0x0000 0x4000 0x4000 +/dev/mtd2 0x0000 0x4000 0x4000 + +# MTD SPI-dataflash example +# MTD device name Device offset Env. size Flash sector size Number of sectors +#/dev/mtd5 0x4200 0x4200 +#/dev/mtd6 0x4200 0x4200 + +# NAND example +#/dev/mtd0 0x4000 0x4000 0x20000 2 + +# On a block device a negative offset is treated as a backwards offset from the +# end of the device/partition, rather than a forwards offset from the start. + +# Block device example +#/dev/mmcblk0 0xc0000 0x20000 +#/dev/mmcblk0 -0x20000 0x20000 + +# VFAT example +#/boot/uboot.env 0x0000 0x4000 + +# UBI volume +#/dev/ubi0_0 0x0 0x1f000 0x1f000 +#/dev/ubi0_1 0x0 0x1f000 0x1f000 + +# UBI volume by name +#/dev/ubi0:env 0x0 0x1f000 0x1f000 +#/dev/ubi0:env-redund 0x0 0x1f000 0x1f000 diff --git a/tools/u-boot-tools/env/fw_env.h b/tools/u-boot-tools/env/fw_env.h new file mode 100644 index 0000000..b250e2f --- /dev/null +++ b/tools/u-boot-tools/env/fw_env.h @@ -0,0 +1,163 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * (C) Copyright 2002-2008 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + */ + +#include <stdint.h> + +/* + * Programs using the library must check which API is available, + * that varies depending on the U-Boot version. + * This can be changed in future + */ +#define FW_ENV_API_VERSION 1 + +struct env_opts { +#ifdef CONFIG_FILE + char *config_file; +#endif + char *lockname; +}; + +/** + * fw_printenv() - print one or several environment variables + * + * @argc: number of variables names to be printed, prints all if 0 + * @argv: array of variable names to be printed, if argc != 0 + * @value_only: do not repeat the variable name, print the bare value, + * only one variable allowed with this option, argc must be 1 + * @opts: encryption key, configuration file, defaults are used if NULL + * + * Description: + * Uses fw_env_open, fw_getenv + * + * Return: + * 0 on success, -1 on failure (modifies errno) + */ +int fw_printenv(int argc, char *argv[], int value_only, struct env_opts *opts); + +/** + * fw_env_set() - adds or removes one variable to the environment + * + * @argc: number of strings in argv, argv[0] is variable name, + * argc==1 means erase variable, argc > 1 means add a variable + * @argv: argv[0] is variable name, argv[1..argc-1] are concatenated separated + * by single blank and set as the new value of the variable + * @opts: how to retrieve environment from flash, defaults are used if NULL + * + * Description: + * Uses fw_env_open, fw_env_write, fw_env_flush + * + * Return: + * 0 on success, -1 on failure (modifies errno) + * + * ERRORS: + * EROFS - some variables ("ethaddr", "serial#") cannot be modified + */ +int fw_env_set(int argc, char *argv[], struct env_opts *opts); + +/** + * fw_parse_script() - adds or removes multiple variables with a batch script + * + * @fname: batch script file name + * @opts: encryption key, configuration file, defaults are used if NULL + * + * Description: + * Uses fw_env_open, fw_env_write, fw_env_flush + * + * Return: + * 0 success, -1 on failure (modifies errno) + * + * Script Syntax: + * + * key [ [space]+ value] + * + * lines starting with '#' treated as comment + * + * A variable without value will be deleted. Any number of spaces are allowed + * between key and value. The value starts with the first non-space character + * and ends with newline. No comments allowed on these lines. Spaces inside + * the value are preserved verbatim. + * + * Script Example: + * + * netdev eth0 + * + * kernel_addr 400000 + * + * foo spaces are copied verbatim + * + * # delete variable bar + * + * bar + */ +int fw_parse_script(char *fname, struct env_opts *opts); + + +/** + * fw_env_open() - read enviroment from flash into RAM cache + * + * @opts: encryption key, configuration file, defaults are used if NULL + * + * Return: + * 0 on success, -1 on failure (modifies errno) + */ +int fw_env_open(struct env_opts *opts); + +/** + * fw_getenv() - lookup variable in the RAM cache + * + * @name: variable to be searched + * Return: + * pointer to start of value, NULL if not found + */ +char *fw_getenv(char *name); + +/** + * fw_env_write() - modify a variable held in the RAM cache + * + * @name: variable + * @value: delete variable if NULL, otherwise create or overwrite the variable + * + * This is called in sequence to update the environment in RAM without updating + * the copy in flash after each set + * + * Return: + * 0 on success, -1 on failure (modifies errno) + * + * ERRORS: + * EROFS - some variables ("ethaddr", "serial#") cannot be modified + */ +int fw_env_write(char *name, char *value); + +/** + * fw_env_flush - write the environment from RAM cache back to flash + * + * @opts: encryption key, configuration file, defaults are used if NULL + * + * Return: + * 0 on success, -1 on failure (modifies errno) + */ +int fw_env_flush(struct env_opts *opts); + +/** + * fw_env_close - free allocated structure and close env + * + * @opts: encryption key, configuration file, defaults are used if NULL + * + * Return: + * 0 on success, -1 on failure (modifies errno) + */ +int fw_env_close(struct env_opts *opts); + + +/** + * fw_env_version - return the current version of the library + * + * Return: + * version string of the library + */ +char *fw_env_version(void); + +unsigned long crc32(unsigned long, const unsigned char *, unsigned); diff --git a/tools/u-boot-tools/env/fw_env_main.c b/tools/u-boot-tools/env/fw_env_main.c new file mode 100644 index 0000000..26ba662 --- /dev/null +++ b/tools/u-boot-tools/env/fw_env_main.c @@ -0,0 +1,283 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2000-2008 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + */ + +/* + * Command line user interface to firmware (=U-Boot) environment. + * + * Implements: + * fw_printenv [ -a key ] [[ -n name ] | [ name ... ]] + * - prints the value of a single environment variable + * "name", the ``name=value'' pairs of one or more + * environment variables "name", or the whole + * environment if no names are specified. + * fw_setenv [ -a key ] name [ value ... ] + * - If a name without any values is given, the variable + * with this name is deleted from the environment; + * otherwise, all "value" arguments are concatenated, + * separated by single blank characters, and the + * resulting string is assigned to the environment + * variable "name" + * + * If '-a key' is specified, the env block is encrypted with AES 128 CBC. + * The 'key' argument is in the format of 32 hexadecimal numbers (16 bytes + * of AES key), eg. '-a aabbccddeeff00112233445566778899'. + */ + +#include <fcntl.h> +#include <getopt.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <sys/file.h> +#include <unistd.h> +#include <version.h> +#include "fw_env_private.h" +#include "fw_env.h" + +#define CMD_PRINTENV "fw_printenv" +#define CMD_SETENV "fw_setenv" +static int do_printenv; + +static struct option long_options[] = { + {"config", required_argument, NULL, 'c'}, + {"help", no_argument, NULL, 'h'}, + {"script", required_argument, NULL, 's'}, + {"noheader", no_argument, NULL, 'n'}, + {"lock", required_argument, NULL, 'l'}, + {"version", no_argument, NULL, 'v'}, + {NULL, 0, NULL, 0} +}; + +static struct env_opts env_opts; + +/* setenv options */ +static int noheader; + +/* getenv options */ +static char *script_file; + +void usage_printenv(void) +{ + + fprintf(stderr, + "Usage: fw_printenv [OPTIONS]... [VARIABLE]...\n" + "Print variables from U-Boot environment\n" + "\n" + " -h, --help print this help.\n" + " -v, --version display version\n" +#ifdef CONFIG_FILE + " -c, --config configuration file, default:" CONFIG_FILE "\n" +#endif + " -n, --noheader do not repeat variable name in output\n" + " -l, --lock lock node, default:/var/lock\n" + "\n"); +} + +void usage_env_set(void) +{ + fprintf(stderr, + "Usage: fw_setenv [OPTIONS]... [VARIABLE]...\n" + "Modify variables in U-Boot environment\n" + "\n" + " -h, --help print this help.\n" + " -v, --version display version\n" +#ifdef CONFIG_FILE + " -c, --config configuration file, default:" CONFIG_FILE "\n" +#endif + " -l, --lock lock node, default:/var/lock\n" + " -s, --script batch mode to minimize writes\n" + "\n" + "Examples:\n" + " fw_setenv foo bar set variable foo equal bar\n" + " fw_setenv foo clear variable foo\n" + " fw_setenv --script file run batch script\n" + "\n" + "Script Syntax:\n" + " key [space] value\n" + " lines starting with '#' are treated as comment\n" + "\n" + " A variable without value will be deleted. Any number of spaces are\n" + " allowed between key and value. Space inside of the value is treated\n" + " as part of the value itself.\n" + "\n" + "Script Example:\n" + " netdev eth0\n" + " kernel_addr 400000\n" + " foo empty empty empty empty empty empty\n" + " bar\n" + "\n"); +} + +static void parse_common_args(int argc, char *argv[]) +{ + int c; + +#ifdef CONFIG_FILE + env_opts.config_file = CONFIG_FILE; +#endif + + while ((c = getopt_long(argc, argv, ":a:c:l:h:v", long_options, NULL)) != + EOF) { + switch (c) { +#ifdef CONFIG_FILE + case 'c': + env_opts.config_file = optarg; + break; +#endif + case 'l': + env_opts.lockname = optarg; + break; + case 'h': + do_printenv ? usage_printenv() : usage_env_set(); + exit(EXIT_SUCCESS); + break; + case 'v': + fprintf(stderr, "Compiled with " U_BOOT_VERSION "\n"); + exit(EXIT_SUCCESS); + break; + default: + /* ignore unknown options */ + break; + } + } + + /* Reset getopt for the next pass. */ + opterr = 1; + optind = 1; +} + +int parse_printenv_args(int argc, char *argv[]) +{ + int c; + + parse_common_args(argc, argv); + + while ((c = getopt_long(argc, argv, "a:c:ns:l:h:v", long_options, NULL)) + != EOF) { + switch (c) { + case 'n': + noheader = 1; + break; + case 'a': + case 'c': + case 'h': + case 'l': + /* ignore common options */ + break; + default: /* '?' */ + usage_printenv(); + exit(EXIT_FAILURE); + break; + } + } + return 0; +} + +int parse_setenv_args(int argc, char *argv[]) +{ + int c; + + parse_common_args(argc, argv); + + while ((c = getopt_long(argc, argv, "a:c:ns:l:h:v", long_options, NULL)) + != EOF) { + switch (c) { + case 's': + script_file = optarg; + break; + case 'a': + case 'c': + case 'h': + case 'l': + /* ignore common options */ + break; + default: /* '?' */ + usage_env_set(); + exit(EXIT_FAILURE); + break; + } + } + return 0; +} + +int main(int argc, char *argv[]) +{ + char *lockname = "/var/lock/" CMD_PRINTENV ".lock"; + int lockfd = -1; + int retval = EXIT_SUCCESS; + char *_cmdname; + + _cmdname = *argv; + if (strrchr(_cmdname, '/') != NULL) + _cmdname = strrchr(_cmdname, '/') + 1; + + if (strcmp(_cmdname, CMD_PRINTENV) == 0) { + do_printenv = 1; + } else if (strcmp(_cmdname, CMD_SETENV) == 0) { + do_printenv = 0; + } else { + fprintf(stderr, + "Identity crisis - may be called as `%s' or as `%s' but not as `%s'\n", + CMD_PRINTENV, CMD_SETENV, _cmdname); + exit(EXIT_FAILURE); + } + + if (do_printenv) { + if (parse_printenv_args(argc, argv)) + exit(EXIT_FAILURE); + } else { + if (parse_setenv_args(argc, argv)) + exit(EXIT_FAILURE); + } + + /* shift parsed flags, jump to non-option arguments */ + argc -= optind; + argv += optind; + + if (env_opts.lockname) { + lockname = malloc(strlen(env_opts.lockname) + + sizeof(CMD_PRINTENV) + 10); + if (!lockname) { + fprintf(stderr, "Unable allocate memory"); + exit(EXIT_FAILURE); + } + + sprintf(lockname, "%s/%s.lock", + env_opts.lockname, CMD_PRINTENV); + } + + lockfd = open(lockname, O_WRONLY | O_CREAT | O_TRUNC, 0666); + if (-1 == lockfd) { + fprintf(stderr, "Error opening lock file %s\n", lockname); + return EXIT_FAILURE; + } + + if (-1 == flock(lockfd, LOCK_EX)) { + fprintf(stderr, "Error locking file %s\n", lockname); + close(lockfd); + return EXIT_FAILURE; + } + + if (do_printenv) { + if (fw_printenv(argc, argv, noheader, &env_opts) != 0) + retval = EXIT_FAILURE; + } else { + if (!script_file) { + if (fw_env_set(argc, argv, &env_opts) != 0) + retval = EXIT_FAILURE; + } else { + if (fw_parse_script(script_file, &env_opts) != 0) + retval = EXIT_FAILURE; + } + } + + if (env_opts.lockname) + free(lockname); + + flock(lockfd, LOCK_UN); + close(lockfd); + return retval; +} diff --git a/tools/u-boot-tools/env/fw_env_private.h b/tools/u-boot-tools/env/fw_env_private.h new file mode 100644 index 0000000..86be16d --- /dev/null +++ b/tools/u-boot-tools/env/fw_env_private.h @@ -0,0 +1,54 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * (C) Copyright 2002-2008 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + */ + +/* Pull in the current config to define the default environment */ +#include <linux/kconfig.h> + +#ifndef __ASSEMBLY__ +#define __ASSEMBLY__ /* get only #defines from config.h */ +#include <config.h> +#undef __ASSEMBLY__ +#else +#include <config.h> +#endif + +/* + * To build the utility with the static configuration + * comment out the next line. + * See included "fw_env.config" sample file + * for notes on configuration. + */ +#define CONFIG_FILE "/etc/fw_env.config" + +#ifndef CONFIG_FILE +#define HAVE_REDUND /* For systems with 2 env sectors */ +#define DEVICE1_NAME "/dev/mtd1" +#define DEVICE2_NAME "/dev/mtd2" +#define DEVICE1_OFFSET 0x0000 +#define ENV1_SIZE 0x4000 +#define DEVICE1_ESIZE 0x4000 +#define DEVICE1_ENVSECTORS 2 +#define DEVICE2_OFFSET 0x0000 +#define ENV2_SIZE 0x4000 +#define DEVICE2_ESIZE 0x4000 +#define DEVICE2_ENVSECTORS 2 +#endif + +#ifndef CONFIG_BAUDRATE +#define CONFIG_BAUDRATE 115200 +#endif + +#ifndef CONFIG_BOOTDELAY +#define CONFIG_BOOTDELAY 5 /* autoboot after 5 seconds */ +#endif + +#ifndef CONFIG_BOOTCOMMAND +#define CONFIG_BOOTCOMMAND \ + "bootp; " \ + "setenv bootargs root=/dev/nfs nfsroot=${serverip}:${rootpath} "\ + "ip=${ipaddr}:${serverip}:${gatewayip}:${netmask}:${hostname}::off; "\ + "bootm" +#endif diff --git a/tools/u-boot-tools/env/linux_string.c b/tools/u-boot-tools/env/linux_string.c new file mode 100644 index 0000000..6c01add --- /dev/null +++ b/tools/u-boot-tools/env/linux_string.c @@ -0,0 +1 @@ +#include "../../lib/linux_string.c" diff --git a/tools/u-boot-tools/envcrc b/tools/u-boot-tools/envcrc new file mode 100755 index 0000000000000000000000000000000000000000..23d57ef4f623e1bc6229d3a9dc29d78ecb0ed6a9 GIT binary patch literal 25480 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 CP8sF^ literal 0 HcmV?d00001 diff --git a/tools/u-boot-tools/envcrc.c b/tools/u-boot-tools/envcrc.c new file mode 100644 index 0000000..5059492 --- /dev/null +++ b/tools/u-boot-tools/envcrc.c @@ -0,0 +1,130 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2001 + * Paolo Scaffardi, AIRVENT SAM s.p.a - RIMINI(ITALY), arsenio@tin.it + */ + +#include <errno.h> +#include <stdio.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include <linux/kconfig.h> + +#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__ + +#if defined(CONFIG_ENV_IS_IN_FLASH) +# ifndef CONFIG_ENV_ADDR +# define CONFIG_ENV_ADDR (CONFIG_SYS_FLASH_BASE + CONFIG_ENV_OFFSET) +# endif +# ifndef CONFIG_ENV_OFFSET +# define CONFIG_ENV_OFFSET (CONFIG_ENV_ADDR - CONFIG_SYS_FLASH_BASE) +# endif +# if !defined(CONFIG_ENV_ADDR_REDUND) && defined(CONFIG_ENV_OFFSET_REDUND) +# define CONFIG_ENV_ADDR_REDUND (CONFIG_SYS_FLASH_BASE + CONFIG_ENV_OFFSET_REDUND) +# endif +# ifndef CONFIG_ENV_SIZE +# define CONFIG_ENV_SIZE CONFIG_ENV_SECT_SIZE +# endif +# if defined(CONFIG_ENV_ADDR_REDUND) && !defined(CONFIG_ENV_SIZE_REDUND) +# define CONFIG_ENV_SIZE_REDUND CONFIG_ENV_SIZE +# endif +# if (CONFIG_ENV_ADDR >= CONFIG_SYS_MONITOR_BASE) && \ + ((CONFIG_ENV_ADDR + CONFIG_ENV_SIZE) <= (CONFIG_SYS_MONITOR_BASE + CONFIG_SYS_MONITOR_LEN)) +# define ENV_IS_EMBEDDED +# endif +# if defined(CONFIG_ENV_ADDR_REDUND) || defined(CONFIG_ENV_OFFSET_REDUND) +# define CONFIG_SYS_REDUNDAND_ENVIRONMENT +# endif +#endif /* CONFIG_ENV_IS_IN_FLASH */ + +#if defined(ENV_IS_EMBEDDED) && !defined(CONFIG_BUILD_ENVCRC) +# define CONFIG_BUILD_ENVCRC +#endif + +#ifdef CONFIG_SYS_REDUNDAND_ENVIRONMENT +# define ENV_HEADER_SIZE (sizeof(uint32_t) + 1) +#else +# define ENV_HEADER_SIZE (sizeof(uint32_t)) +#endif + +#define ENV_SIZE (CONFIG_ENV_SIZE - ENV_HEADER_SIZE) + + +#ifdef CONFIG_BUILD_ENVCRC +# include <environment.h> +extern unsigned int env_size; +extern env_t environment; +#endif /* CONFIG_BUILD_ENVCRC */ + +extern uint32_t crc32 (uint32_t, const unsigned char *, unsigned int); + +int main (int argc, char **argv) +{ +#ifdef CONFIG_BUILD_ENVCRC + unsigned char pad = 0x00; + uint32_t crc; + unsigned char *envptr = (unsigned char *)&environment, + *dataptr = envptr + ENV_HEADER_SIZE; + unsigned int datasize = ENV_SIZE; + unsigned int eoe; + + if (argv[1] && !strncmp(argv[1], "--binary", 8)) { + int ipad = 0xff; + if (argv[1][8] == '=') + sscanf(argv[1] + 9, "%i", &ipad); + pad = ipad; + } + + if (pad) { + /* find the end of env */ + for (eoe = 0; eoe < datasize - 1; ++eoe) + if (!dataptr[eoe] && !dataptr[eoe+1]) { + eoe += 2; + break; + } + if (eoe < datasize - 1) + memset(dataptr + eoe, pad, datasize - eoe); + } + + crc = crc32 (0, dataptr, datasize); + + /* Check if verbose mode is activated passing a parameter to the program */ + if (argc > 1) { + if (!strncmp(argv[1], "--binary", 8)) { + int le = (argc > 2 ? !strcmp(argv[2], "le") : 1); + size_t i, start, end, step; + if (le) { + start = 0; + end = ENV_HEADER_SIZE; + step = 1; + } else { + start = ENV_HEADER_SIZE - 1; + end = -1; + step = -1; + } + for (i = start; i != end; i += step) + printf("%c", (crc & (0xFF << (i * 8))) >> (i * 8)); + if (fwrite(dataptr, 1, datasize, stdout) != datasize) + fprintf(stderr, "fwrite() failed: %s\n", strerror(errno)); + } else { + printf("CRC32 from offset %08X to %08X of environment = %08X\n", + (unsigned int) (dataptr - envptr), + (unsigned int) (dataptr - envptr) + datasize, + crc); + } + } else { + printf ("0x%08X\n", crc); + } +#else + printf ("0\n"); +#endif + return EXIT_SUCCESS; +} diff --git a/tools/u-boot-tools/envcrc.o b/tools/u-boot-tools/envcrc.o new file mode 100644 index 0000000000000000000000000000000000000000..a85ab9d69c8b6a681c30a31ebd5298acbef95934 GIT binary patch literal 1656 zcmb<-^>JfjWMqH=Mg}_u1P><4!0>|!!FB*M9T@l+co_`9Yy{!a{D#A$*A^u30z?=d z@Mu25ahSo~+1W}#!zDE-GcixWLeE&wK-WwY!T>2_5CM}642)Gl42%^5jM6;p91|EB zz~VAcIdKr}!Y2^K#{ptt#UQt{Fns)vO_CX_&lnh(8IU}Oq>-6{8HtBrGBdCsI7lpJ z238~<g2~K)-M!2VoCrlw78jVtWj;Gt7Mb8@U|<kJvKJgi43KaWKvoE4YcVh|U=wFZ z%_~bTO4dte(DQLNV8~6(%wvdm_wjdf^ojR(a|?D2i4Spf@^Ou4C@3u{W?+E11Qc>0 zU;OzG0X$H9KtAAQU|@iWD?r6T>e--dFr`;qnOl;W#GqGPQUswhV62kViV_CBl*E!m z2EC->Vg|jUd=Lku(ooNkL9Zw^Cs7ZiNUyjgv8bf9fI%-gKQ}iuuY^G_FTW&J&)qLn zx40xRIUA}rH6uQ)C^0t`Y%yhoGy?-THKF*0fq?;%H!#fxrAuT{MW}xC6h^Fmbp{4- zsv=jv4%B`lWJe*n9~4i>QZV<!XplQWY;^wzKsCYAA(V@e=BybQz_|!y4onS*wt!j; zD_6J~7{I9;mV+cfiWnFe{GjfKse=kLq(d39P#Q*o+zMjD=y0fhkQg#<LFOQ_VftV~ zIZ*vj=fZ>;I-o3ccfs5bl?J;Llt<A0p8+)x=1-9SLHP@&A0`fR4^%14NHDzvOfoP8 zfD|w=Fo5zS$P9G-AT!a^4oE8p)KGm82MI%sgNq0<fXYoc7tUya>Mw(fKp5!y0hgj@ AJ^%m! literal 0 HcmV?d00001 diff --git a/tools/u-boot-tools/fdt_host.h b/tools/u-boot-tools/fdt_host.h new file mode 100644 index 0000000..99b009b --- /dev/null +++ b/tools/u-boot-tools/fdt_host.h @@ -0,0 +1,32 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * (C) Copyright 2008 Semihalf + */ + +#ifndef __FDT_HOST_H__ +#define __FDT_HOST_H__ + +/* Make sure to include u-boot version of libfdt include files */ +#include "../include/linux/libfdt.h" +#include "../include/fdt_support.h" + +/** + * fdt_remove_unused_strings() - Remove any unused strings from an FDT + * + * This creates a new device tree in @new with unused strings removed. The + * called can then use fdt_pack() to minimise the space consumed. + * + * @old: Old device tree blog + * @new: Place to put new device tree blob, which must be as large as + * @old + * @return + * 0, on success + * -FDT_ERR_BADOFFSET, corrupt device tree + * -FDT_ERR_NOSPACE, out of space, which should not happen unless there + * is something very wrong with the device tree input + */ +int fdt_remove_unused_strings(const void *old, void *new); + +int fit_check_sign(const void *working_fdt, const void *key); + +#endif /* __FDT_HOST_H__ */ diff --git a/tools/u-boot-tools/fdtgrep b/tools/u-boot-tools/fdtgrep new file mode 100755 index 0000000000000000000000000000000000000000..17c3e6d2704816838a684db2beed0928bc1e34bd GIT binary patch literal 68744 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 literal 0 HcmV?d00001 diff --git a/tools/u-boot-tools/fdtgrep.c b/tools/u-boot-tools/fdtgrep.c new file mode 100644 index 0000000..8f44f59 --- /dev/null +++ b/tools/u-boot-tools/fdtgrep.c @@ -0,0 +1,1236 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2013, Google Inc. + * Written by Simon Glass <sjg@chromium.org> + * + * Perform a grep of an FDT either displaying the source subset or producing + * a new .dtb subset which can be used as required. + */ + +#include <assert.h> +#include <ctype.h> +#include <errno.h> +#include <getopt.h> +#include <fcntl.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "fdt_host.h" +#include "libfdt_internal.h" + +/* Define DEBUG to get some debugging output on stderr */ +#ifdef DEBUG +#define debug(a, b...) fprintf(stderr, a, ## b) +#else +#define debug(a, b...) +#endif + +/* A linked list of values we are grepping for */ +struct value_node { + int type; /* Types this value matches (FDT_IS... mask) */ + int include; /* 1 to include matches, 0 to exclude */ + const char *string; /* String to match */ + struct value_node *next; /* Pointer to next node, or NULL */ +}; + +/* Output formats we support */ +enum output_t { + OUT_DTS, /* Device tree source */ + OUT_DTB, /* Valid device tree binary */ + OUT_BIN, /* Fragment of .dtb, for hashing */ +}; + +/* Holds information which controls our output and options */ +struct display_info { + enum output_t output; /* Output format */ + int add_aliases; /* Add aliases node to output */ + int all; /* Display all properties/nodes */ + int colour; /* Display output in ANSI colour */ + int region_list; /* Output a region list */ + int flags; /* Flags (FDT_REG_...) */ + int list_strings; /* List strings in string table */ + int show_offset; /* Show offset */ + int show_addr; /* Show address */ + int header; /* Output an FDT header */ + int diff; /* Show +/- diff markers */ + int include_root; /* Include the root node and all properties */ + int remove_strings; /* Remove unused strings */ + int show_dts_version; /* Put '/dts-v1/;' on the first line */ + int types_inc; /* Mask of types that we include (FDT_IS...) */ + int types_exc; /* Mask of types that we exclude (FDT_IS...) */ + int invert; /* Invert polarity of match */ + struct value_node *value_head; /* List of values to match */ + const char *output_fname; /* Output filename */ + FILE *fout; /* File to write dts/dtb output */ +}; + +static void report_error(const char *where, int err) +{ + fprintf(stderr, "Error at '%s': %s\n", where, fdt_strerror(err)); +} + +/* Supported ANSI colours */ +enum { + COL_BLACK, + COL_RED, + COL_GREEN, + COL_YELLOW, + COL_BLUE, + COL_MAGENTA, + COL_CYAN, + COL_WHITE, + + COL_NONE = -1, +}; + +/** + * print_ansi_colour() - Print out the ANSI sequence for a colour + * + * @fout: Output file + * @col: Colour to output (COL_...), or COL_NONE to reset colour + */ +static void print_ansi_colour(FILE *fout, int col) +{ + if (col == COL_NONE) + fprintf(fout, "\033[0m"); + else + fprintf(fout, "\033[1;%dm", col + 30); +} + + +/** + * value_add() - Add a new value to our list of things to grep for + * + * @disp: Display structure, holding info about our options + * @headp: Pointer to header pointer of list + * @type: Type of this value (FDT_IS_...) + * @include: 1 if we want to include matches, 0 to exclude + * @str: String value to match + */ +static int value_add(struct display_info *disp, struct value_node **headp, + int type, int include, const char *str) +{ + struct value_node *node; + + /* + * Keep track of which types we are excluding/including. We don't + * allow both including and excluding things, because it doesn't make + * sense. 'Including' means that everything not mentioned is + * excluded. 'Excluding' means that everything not mentioned is + * included. So using the two together would be meaningless. + */ + if (include) + disp->types_inc |= type; + else + disp->types_exc |= type; + if (disp->types_inc & disp->types_exc & type) { + fprintf(stderr, + "Cannot use both include and exclude for '%s'\n", str); + return -1; + } + + str = strdup(str); + if (!str) + goto err_mem; + node = malloc(sizeof(*node)); + if (!node) + goto err_mem; + node->next = *headp; + node->type = type; + node->include = include; + node->string = str; + *headp = node; + + return 0; +err_mem: + fprintf(stderr, "Out of memory\n"); + return -1; +} + +static bool util_is_printable_string(const void *data, int len) +{ + const char *s = data; + const char *ss, *se; + + /* zero length is not */ + if (len == 0) + return 0; + + /* must terminate with zero */ + if (s[len - 1] != '\0') + return 0; + + se = s + len; + + while (s < se) { + ss = s; + while (s < se && *s && isprint((unsigned char)*s)) + s++; + + /* not zero, or not done yet */ + if (*s != '\0' || s == ss) + return 0; + + s++; + } + + return 1; +} + +static void utilfdt_print_data(const char *data, int len) +{ + int i; + const char *p = data; + const char *s; + + /* no data, don't print */ + if (len == 0) + return; + + if (util_is_printable_string(data, len)) { + printf(" = "); + + s = data; + do { + printf("\"%s\"", s); + s += strlen(s) + 1; + if (s < data + len) + printf(", "); + } while (s < data + len); + + } else if ((len % 4) == 0) { + const uint32_t *cell = (const uint32_t *)data; + + printf(" = <"); + for (i = 0, len /= 4; i < len; i++) + printf("0x%08x%s", fdt32_to_cpu(cell[i]), + i < (len - 1) ? " " : ""); + printf(">"); + } else { + printf(" = ["); + for (i = 0; i < len; i++) + printf("%02x%s", *p++, i < len - 1 ? " " : ""); + printf("]"); + } +} + +/** + * display_fdt_by_regions() - Display regions of an FDT source + * + * This dumps an FDT as source, but only certain regions of it. This is the + * final stage of the grep - we have a list of regions we want to display, + * and this function displays them. + * + * @disp: Display structure, holding info about our options + * @blob: FDT blob to display + * @region: List of regions to display + * @count: Number of regions + */ +static int display_fdt_by_regions(struct display_info *disp, const void *blob, + struct fdt_region region[], int count) +{ + struct fdt_region *reg = region, *reg_end = region + count; + uint32_t off_mem_rsvmap = fdt_off_mem_rsvmap(blob); + int base = fdt_off_dt_struct(blob); + int version = fdt_version(blob); + int offset, nextoffset; + int tag, depth, shift; + FILE *f = disp->fout; + uint64_t addr, size; + int in_region; + int file_ofs; + int i; + + if (disp->show_dts_version) + fprintf(f, "/dts-v1/;\n"); + + if (disp->header) { + fprintf(f, "// magic:\t\t0x%x\n", fdt_magic(blob)); + fprintf(f, "// totalsize:\t\t0x%x (%d)\n", fdt_totalsize(blob), + fdt_totalsize(blob)); + fprintf(f, "// off_dt_struct:\t0x%x\n", + fdt_off_dt_struct(blob)); + fprintf(f, "// off_dt_strings:\t0x%x\n", + fdt_off_dt_strings(blob)); + fprintf(f, "// off_mem_rsvmap:\t0x%x\n", off_mem_rsvmap); + fprintf(f, "// version:\t\t%d\n", version); + fprintf(f, "// last_comp_version:\t%d\n", + fdt_last_comp_version(blob)); + if (version >= 2) { + fprintf(f, "// boot_cpuid_phys:\t0x%x\n", + fdt_boot_cpuid_phys(blob)); + } + if (version >= 3) { + fprintf(f, "// size_dt_strings:\t0x%x\n", + fdt_size_dt_strings(blob)); + } + if (version >= 17) { + fprintf(f, "// size_dt_struct:\t0x%x\n", + fdt_size_dt_struct(blob)); + } + fprintf(f, "\n"); + } + + if (disp->flags & FDT_REG_ADD_MEM_RSVMAP) { + const struct fdt_reserve_entry *p_rsvmap; + + p_rsvmap = (const struct fdt_reserve_entry *) + ((const char *)blob + off_mem_rsvmap); + for (i = 0; ; i++) { + addr = fdt64_to_cpu(p_rsvmap[i].address); + size = fdt64_to_cpu(p_rsvmap[i].size); + if (addr == 0 && size == 0) + break; + + fprintf(f, "/memreserve/ %llx %llx;\n", + (unsigned long long)addr, + (unsigned long long)size); + } + } + + depth = 0; + nextoffset = 0; + shift = 4; /* 4 spaces per indent */ + do { + const struct fdt_property *prop; + const char *name; + int show; + int len; + + offset = nextoffset; + + /* + * Work out the file offset of this offset, and decide + * whether it is in the region list or not + */ + file_ofs = base + offset; + if (reg < reg_end && file_ofs >= reg->offset + reg->size) + reg++; + in_region = reg < reg_end && file_ofs >= reg->offset && + file_ofs < reg->offset + reg->size; + tag = fdt_next_tag(blob, offset, &nextoffset); + + if (tag == FDT_END) + break; + show = in_region || disp->all; + if (show && disp->diff) + fprintf(f, "%c", in_region ? '+' : '-'); + + if (!show) { + /* Do this here to avoid 'if (show)' in every 'case' */ + if (tag == FDT_BEGIN_NODE) + depth++; + else if (tag == FDT_END_NODE) + depth--; + continue; + } + if (tag != FDT_END) { + if (disp->show_addr) + fprintf(f, "%4x: ", file_ofs); + if (disp->show_offset) + fprintf(f, "%4x: ", file_ofs - base); + } + + /* Green means included, red means excluded */ + if (disp->colour) + print_ansi_colour(f, in_region ? COL_GREEN : COL_RED); + + switch (tag) { + case FDT_PROP: + prop = fdt_get_property_by_offset(blob, offset, NULL); + name = fdt_string(blob, fdt32_to_cpu(prop->nameoff)); + fprintf(f, "%*s%s", depth * shift, "", name); + utilfdt_print_data(prop->data, + fdt32_to_cpu(prop->len)); + fprintf(f, ";"); + break; + + case FDT_NOP: + fprintf(f, "%*s// [NOP]", depth * shift, ""); + break; + + case FDT_BEGIN_NODE: + name = fdt_get_name(blob, offset, &len); + fprintf(f, "%*s%s {", depth++ * shift, "", + *name ? name : "/"); + break; + + case FDT_END_NODE: + fprintf(f, "%*s};", --depth * shift, ""); + break; + } + + /* Reset colour back to normal before end of line */ + if (disp->colour) + print_ansi_colour(f, COL_NONE); + fprintf(f, "\n"); + } while (1); + + /* Print a list of strings if requested */ + if (disp->list_strings) { + const char *str; + int str_base = fdt_off_dt_strings(blob); + + for (offset = 0; offset < fdt_size_dt_strings(blob); + offset += strlen(str) + 1) { + str = fdt_string(blob, offset); + int len = strlen(str) + 1; + int show; + + /* Only print strings that are in the region */ + file_ofs = str_base + offset; + in_region = reg < reg_end && + file_ofs >= reg->offset && + file_ofs + len < reg->offset + + reg->size; + show = in_region || disp->all; + if (show && disp->diff) + printf("%c", in_region ? '+' : '-'); + if (disp->show_addr) + printf("%4x: ", file_ofs); + if (disp->show_offset) + printf("%4x: ", offset); + printf("%s\n", str); + } + } + + return 0; +} + +/** + * dump_fdt_regions() - Dump regions of an FDT as binary data + * + * This dumps an FDT as binary, but only certain regions of it. This is the + * final stage of the grep - we have a list of regions we want to dump, + * and this function dumps them. + * + * The output of this function may or may not be a valid FDT. To ensure it + * is, these disp->flags must be set: + * + * FDT_REG_SUPERNODES: ensures that subnodes are preceded by their + * parents. Without this option, fragments of subnode data may be + * output without the supernodes above them. This is useful for + * hashing but cannot produce a valid FDT. + * FDT_REG_ADD_STRING_TAB: Adds a string table to the end of the FDT. + * Without this none of the properties will have names + * FDT_REG_ADD_MEM_RSVMAP: Adds a mem_rsvmap table - an FDT is invalid + * without this. + * + * @disp: Display structure, holding info about our options + * @blob: FDT blob to display + * @region: List of regions to display + * @count: Number of regions + * @out: Output destination + */ +static int dump_fdt_regions(struct display_info *disp, const void *blob, + struct fdt_region region[], int count, char *out) +{ + struct fdt_header *fdt; + int size, struct_start; + int ptr; + int i; + + /* Set up a basic header (even if we don't actually write it) */ + fdt = (struct fdt_header *)out; + memset(fdt, '\0', sizeof(*fdt)); + fdt_set_magic(fdt, FDT_MAGIC); + struct_start = FDT_ALIGN(sizeof(struct fdt_header), + sizeof(struct fdt_reserve_entry)); + fdt_set_off_mem_rsvmap(fdt, struct_start); + fdt_set_version(fdt, FDT_LAST_SUPPORTED_VERSION); + fdt_set_last_comp_version(fdt, FDT_FIRST_SUPPORTED_VERSION); + + /* + * Calculate the total size of the regions we are writing out. The + * first will be the mem_rsvmap if the FDT_REG_ADD_MEM_RSVMAP flag + * is set. The last will be the string table if FDT_REG_ADD_STRING_TAB + * is set. + */ + for (i = size = 0; i < count; i++) + size += region[i].size; + + /* Bring in the mem_rsvmap section from the old file if requested */ + if (count > 0 && (disp->flags & FDT_REG_ADD_MEM_RSVMAP)) { + struct_start += region[0].size; + size -= region[0].size; + } + fdt_set_off_dt_struct(fdt, struct_start); + + /* Update the header to have the correct offsets/sizes */ + if (count >= 2 && (disp->flags & FDT_REG_ADD_STRING_TAB)) { + int str_size; + + str_size = region[count - 1].size; + fdt_set_size_dt_struct(fdt, size - str_size); + fdt_set_off_dt_strings(fdt, struct_start + size - str_size); + fdt_set_size_dt_strings(fdt, str_size); + fdt_set_totalsize(fdt, struct_start + size); + } + + /* Write the header if required */ + ptr = 0; + if (disp->header) { + ptr = sizeof(*fdt); + while (ptr < fdt_off_mem_rsvmap(fdt)) + out[ptr++] = '\0'; + } + + /* Output all the nodes including any mem_rsvmap/string table */ + for (i = 0; i < count; i++) { + struct fdt_region *reg = ®ion[i]; + + memcpy(out + ptr, (const char *)blob + reg->offset, reg->size); + ptr += reg->size; + } + + return ptr; +} + +/** + * show_region_list() - Print out a list of regions + * + * The list includes the region offset (absolute offset from start of FDT + * blob in bytes) and size + * + * @reg: List of regions to print + * @count: Number of regions + */ +static void show_region_list(struct fdt_region *reg, int count) +{ + int i; + + printf("Regions: %d\n", count); + for (i = 0; i < count; i++, reg++) { + printf("%d: %-10x %-10x\n", i, reg->offset, + reg->offset + reg->size); + } +} + +static int check_type_include(void *priv, int type, const char *data, int size) +{ + struct display_info *disp = priv; + struct value_node *val; + int match, none_match = FDT_IS_ANY; + + /* If none of our conditions mention this type, we know nothing */ + debug("type=%x, data=%s\n", type, data ? data : "(null)"); + if (!((disp->types_inc | disp->types_exc) & type)) { + debug(" - not in any condition\n"); + return -1; + } + + /* + * Go through the list of conditions. For inclusive conditions, we + * return 1 at the first match. For exclusive conditions, we must + * check that there are no matches. + */ + if (data) { + for (val = disp->value_head; val; val = val->next) { + if (!(type & val->type)) + continue; + match = fdt_stringlist_contains(data, size, + val->string); + debug(" - val->type=%x, str='%s', match=%d\n", + val->type, val->string, match); + if (match && val->include) { + debug(" - match inc %s\n", val->string); + return 1; + } + if (match) + none_match &= ~val->type; + } + } + + /* + * If this is an exclusive condition, and nothing matches, then we + * should return 1. + */ + if ((type & disp->types_exc) && (none_match & type)) { + debug(" - match exc\n"); + /* + * Allow FDT_IS_COMPAT to make the final decision in the + * case where there is no specific type + */ + if (type == FDT_IS_NODE && disp->types_exc == FDT_ANY_GLOBAL) { + debug(" - supressed exc node\n"); + return -1; + } + return 1; + } + + /* + * Allow FDT_IS_COMPAT to make the final decision in the + * case where there is no specific type (inclusive) + */ + if (type == FDT_IS_NODE && disp->types_inc == FDT_ANY_GLOBAL) + return -1; + + debug(" - no match, types_inc=%x, types_exc=%x, none_match=%x\n", + disp->types_inc, disp->types_exc, none_match); + + return 0; +} + +/** + * h_include() - Include handler function for fdt_find_regions() + * + * This function decides whether to include or exclude a node, property or + * compatible string. The function is defined by fdt_find_regions(). + * + * The algorithm is documented in the code - disp->invert is 0 for normal + * operation, and 1 to invert the sense of all matches. + * + * See + */ +static int h_include(void *priv, const void *fdt, int offset, int type, + const char *data, int size) +{ + struct display_info *disp = priv; + int inc, len; + + inc = check_type_include(priv, type, data, size); + if (disp->include_root && type == FDT_IS_PROP && offset == 0 && inc) + return 1; + + /* + * If the node name does not tell us anything, check the + * compatible string + */ + if (inc == -1 && type == FDT_IS_NODE) { + debug(" - checking compatible2\n"); + data = fdt_getprop(fdt, offset, "compatible", &len); + inc = check_type_include(priv, FDT_IS_COMPAT, data, len); + } + + /* If we still have no idea, check for properties in the node */ + if (inc != 1 && type == FDT_IS_NODE && + (disp->types_inc & FDT_NODE_HAS_PROP)) { + debug(" - checking node '%s'\n", + fdt_get_name(fdt, offset, NULL)); + for (offset = fdt_first_property_offset(fdt, offset); + offset > 0 && inc != 1; + offset = fdt_next_property_offset(fdt, offset)) { + const struct fdt_property *prop; + const char *str; + + prop = fdt_get_property_by_offset(fdt, offset, NULL); + if (!prop) + continue; + str = fdt_string(fdt, fdt32_to_cpu(prop->nameoff)); + inc = check_type_include(priv, FDT_NODE_HAS_PROP, str, + strlen(str)); + } + if (inc == -1) + inc = 0; + } + + switch (inc) { + case 1: + inc = !disp->invert; + break; + case 0: + inc = disp->invert; + break; + } + debug(" - returning %d\n", inc); + + return inc; +} + +static int h_cmp_region(const void *v1, const void *v2) +{ + const struct fdt_region *region1 = v1, *region2 = v2; + + return region1->offset - region2->offset; +} + +static int fdtgrep_find_regions(const void *fdt, + int (*include_func)(void *priv, const void *fdt, int offset, + int type, const char *data, int size), + struct display_info *disp, struct fdt_region *region, + int max_regions, char *path, int path_len, int flags) +{ + struct fdt_region_state state; + int count; + int ret; + + count = 0; + ret = fdt_first_region(fdt, include_func, disp, + ®ion[count++], path, path_len, + disp->flags, &state); + while (ret == 0) { + ret = fdt_next_region(fdt, include_func, disp, + count < max_regions ? ®ion[count] : NULL, + path, path_len, disp->flags, &state); + if (!ret) + count++; + } + if (ret && ret != -FDT_ERR_NOTFOUND) + return ret; + + /* Find all the aliases and add those regions back in */ + if (disp->add_aliases && count < max_regions) { + int new_count; + + new_count = fdt_add_alias_regions(fdt, region, count, + max_regions, &state); + if (new_count == -FDT_ERR_NOTFOUND) { + /* No alias node found */ + } else if (new_count < 0) { + return new_count; + } else if (new_count <= max_regions) { + /* + * The alias regions will now be at the end of the list. + * Sort the regions by offset to get things into the + * right order + */ + count = new_count; + qsort(region, count, sizeof(struct fdt_region), + h_cmp_region); + } + } + + return count; +} + +int utilfdt_read_err_len(const char *filename, char **buffp, off_t *len) +{ + int fd = 0; /* assume stdin */ + char *buf = NULL; + off_t bufsize = 1024, offset = 0; + int ret = 0; + + *buffp = NULL; + if (strcmp(filename, "-") != 0) { + fd = open(filename, O_RDONLY); + if (fd < 0) + return errno; + } + + /* Loop until we have read everything */ + buf = malloc(bufsize); + if (!buf) + return -ENOMEM; + do { + /* Expand the buffer to hold the next chunk */ + if (offset == bufsize) { + bufsize *= 2; + buf = realloc(buf, bufsize); + if (!buf) + return -ENOMEM; + } + + ret = read(fd, &buf[offset], bufsize - offset); + if (ret < 0) { + ret = errno; + break; + } + offset += ret; + } while (ret != 0); + + /* Clean up, including closing stdin; return errno on error */ + close(fd); + if (ret) + free(buf); + else + *buffp = buf; + *len = bufsize; + return ret; +} + +int utilfdt_read_err(const char *filename, char **buffp) +{ + off_t len; + return utilfdt_read_err_len(filename, buffp, &len); +} + +char *utilfdt_read_len(const char *filename, off_t *len) +{ + char *buff; + int ret = utilfdt_read_err_len(filename, &buff, len); + + if (ret) { + fprintf(stderr, "Couldn't open blob from '%s': %s\n", filename, + strerror(ret)); + return NULL; + } + /* Successful read */ + return buff; +} + +char *utilfdt_read(const char *filename) +{ + off_t len; + return utilfdt_read_len(filename, &len); +} + +/** + * Run the main fdtgrep operation, given a filename and valid arguments + * + * @param disp Display information / options + * @param filename Filename of blob file + * @param return 0 if ok, -ve on error + */ +static int do_fdtgrep(struct display_info *disp, const char *filename) +{ + struct fdt_region *region = NULL; + int max_regions; + int count = 100; + char path[1024]; + char *blob; + int i, ret; + + blob = utilfdt_read(filename); + if (!blob) + return -1; + ret = fdt_check_header(blob); + if (ret) { + fprintf(stderr, "Error: %s\n", fdt_strerror(ret)); + return ret; + } + + /* Allow old files, but they are untested */ + if (fdt_version(blob) < 17 && disp->value_head) { + fprintf(stderr, + "Warning: fdtgrep does not fully support version %d files\n", + fdt_version(blob)); + } + + /* + * We do two passes, since we don't know how many regions we need. + * The first pass will count the regions, but if it is too many, + * we do another pass to actually record them. + */ + for (i = 0; i < 2; i++) { + region = malloc(count * sizeof(struct fdt_region)); + if (!region) { + fprintf(stderr, "Out of memory for %d regions\n", + count); + return -1; + } + max_regions = count; + count = fdtgrep_find_regions(blob, + h_include, disp, + region, max_regions, path, sizeof(path), + disp->flags); + if (count < 0) { + report_error("fdt_find_regions", count); + free(region); + return -1; + } + if (count <= max_regions) + break; + free(region); + fprintf(stderr, "Internal error with fdtgrep_find_region)(\n"); + return -1; + } + + /* Optionally print a list of regions */ + if (disp->region_list) + show_region_list(region, count); + + /* Output either source .dts or binary .dtb */ + if (disp->output == OUT_DTS) { + ret = display_fdt_by_regions(disp, blob, region, count); + } else { + void *fdt; + /* Allow reserved memory section to expand slightly */ + int size = fdt_totalsize(blob) + 16; + + fdt = malloc(size); + if (!fdt) { + fprintf(stderr, "Out_of_memory\n"); + ret = -1; + goto err; + } + size = dump_fdt_regions(disp, blob, region, count, fdt); + if (disp->remove_strings) { + void *out; + + out = malloc(size); + if (!out) { + fprintf(stderr, "Out_of_memory\n"); + ret = -1; + goto err; + } + ret = fdt_remove_unused_strings(fdt, out); + if (ret < 0) { + fprintf(stderr, + "Failed to remove unused strings: err=%d\n", + ret); + goto err; + } + free(fdt); + fdt = out; + ret = fdt_pack(fdt); + if (ret < 0) { + fprintf(stderr, "Failed to pack: err=%d\n", + ret); + goto err; + } + size = fdt_totalsize(fdt); + } + + if (size != fwrite(fdt, 1, size, disp->fout)) { + fprintf(stderr, "Write failure, %d bytes\n", size); + free(fdt); + ret = 1; + goto err; + } + free(fdt); + } +err: + free(blob); + free(region); + + return ret; +} + +static const char usage_synopsis[] = + "fdtgrep - extract portions from device tree\n" + "\n" + "Usage:\n" + " fdtgrep <options> <dt file>|-\n\n" + "Output formats are:\n" + "\tdts - device tree soure text\n" + "\tdtb - device tree blob (sets -Hmt automatically)\n" + "\tbin - device tree fragment (may not be a valid .dtb)"; + +/* Helper for usage_short_opts string constant */ +#define USAGE_COMMON_SHORT_OPTS "hV" + +/* Helper for aligning long_opts array */ +#define a_argument required_argument + +/* Helper for usage_long_opts option array */ +#define USAGE_COMMON_LONG_OPTS \ + {"help", no_argument, NULL, 'h'}, \ + {"version", no_argument, NULL, 'V'}, \ + {NULL, no_argument, NULL, 0x0} + +/* Helper for usage_opts_help array */ +#define USAGE_COMMON_OPTS_HELP \ + "Print this help and exit", \ + "Print version and exit", \ + NULL + +/* Helper for getopt case statements */ +#define case_USAGE_COMMON_FLAGS \ + case 'h': usage(NULL); \ + case 'V': util_version(); \ + case '?': usage("unknown option"); + +static const char usage_short_opts[] = + "haAc:b:C:defg:G:HIlLmn:N:o:O:p:P:rRsStTv" + USAGE_COMMON_SHORT_OPTS; +static struct option const usage_long_opts[] = { + {"show-address", no_argument, NULL, 'a'}, + {"colour", no_argument, NULL, 'A'}, + {"include-node-with-prop", a_argument, NULL, 'b'}, + {"include-compat", a_argument, NULL, 'c'}, + {"exclude-compat", a_argument, NULL, 'C'}, + {"diff", no_argument, NULL, 'd'}, + {"enter-node", no_argument, NULL, 'e'}, + {"show-offset", no_argument, NULL, 'f'}, + {"include-match", a_argument, NULL, 'g'}, + {"exclude-match", a_argument, NULL, 'G'}, + {"show-header", no_argument, NULL, 'H'}, + {"show-version", no_argument, NULL, 'I'}, + {"list-regions", no_argument, NULL, 'l'}, + {"list-strings", no_argument, NULL, 'L'}, + {"include-mem", no_argument, NULL, 'm'}, + {"include-node", a_argument, NULL, 'n'}, + {"exclude-node", a_argument, NULL, 'N'}, + {"include-prop", a_argument, NULL, 'p'}, + {"exclude-prop", a_argument, NULL, 'P'}, + {"remove-strings", no_argument, NULL, 'r'}, + {"include-root", no_argument, NULL, 'R'}, + {"show-subnodes", no_argument, NULL, 's'}, + {"skip-supernodes", no_argument, NULL, 'S'}, + {"show-stringtab", no_argument, NULL, 't'}, + {"show-aliases", no_argument, NULL, 'T'}, + {"out", a_argument, NULL, 'o'}, + {"out-format", a_argument, NULL, 'O'}, + {"invert-match", no_argument, NULL, 'v'}, + USAGE_COMMON_LONG_OPTS, +}; +static const char * const usage_opts_help[] = { + "Display address", + "Show all nodes/tags, colour those that match", + "Include contains containing property", + "Compatible nodes to include in grep", + "Compatible nodes to exclude in grep", + "Diff: Mark matching nodes with +, others with -", + "Enter direct subnode names of matching nodes", + "Display offset", + "Node/property/compatible string to include in grep", + "Node/property/compatible string to exclude in grep", + "Output a header", + "Put \"/dts-v1/;\" on first line of dts output", + "Output a region list", + "List strings in string table", + "Include mem_rsvmap section in binary output", + "Node to include in grep", + "Node to exclude in grep", + "Property to include in grep", + "Property to exclude in grep", + "Remove unused strings from string table", + "Include root node and all properties", + "Show all subnodes matching nodes", + "Don't include supernodes of matching nodes", + "Include string table in binary output", + "Include matching aliases in output", + "-o <output file>", + "-O <output format>", + "Invert the sense of matching (select non-matching lines)", + USAGE_COMMON_OPTS_HELP +}; + +/** + * Call getopt_long() with standard options + * + * Since all util code runs getopt in the same way, provide a helper. + */ +#define util_getopt_long() getopt_long(argc, argv, usage_short_opts, \ + usage_long_opts, NULL) + +void util_usage(const char *errmsg, const char *synopsis, + const char *short_opts, struct option const long_opts[], + const char * const opts_help[]) +{ + FILE *fp = errmsg ? stderr : stdout; + const char a_arg[] = "<arg>"; + size_t a_arg_len = strlen(a_arg) + 1; + size_t i; + int optlen; + + fprintf(fp, + "Usage: %s\n" + "\n" + "Options: -[%s]\n", synopsis, short_opts); + + /* prescan the --long opt length to auto-align */ + optlen = 0; + for (i = 0; long_opts[i].name; ++i) { + /* +1 is for space between --opt and help text */ + int l = strlen(long_opts[i].name) + 1; + if (long_opts[i].has_arg == a_argument) + l += a_arg_len; + if (optlen < l) + optlen = l; + } + + for (i = 0; long_opts[i].name; ++i) { + /* helps when adding new applets or options */ + assert(opts_help[i] != NULL); + + /* first output the short flag if it has one */ + if (long_opts[i].val > '~') + fprintf(fp, " "); + else + fprintf(fp, " -%c, ", long_opts[i].val); + + /* then the long flag */ + if (long_opts[i].has_arg == no_argument) { + fprintf(fp, "--%-*s", optlen, long_opts[i].name); + } else { + fprintf(fp, "--%s %s%*s", long_opts[i].name, a_arg, + (int)(optlen - strlen(long_opts[i].name) - + a_arg_len), ""); + } + + /* finally the help text */ + fprintf(fp, "%s\n", opts_help[i]); + } + + if (errmsg) { + fprintf(fp, "\nError: %s\n", errmsg); + exit(EXIT_FAILURE); + } else { + exit(EXIT_SUCCESS); + } +} + +/** + * Show usage and exit + * + * If you name all your usage variables with usage_xxx, then you can call this + * help macro rather than expanding all arguments yourself. + * + * @param errmsg If non-NULL, an error message to display + */ +#define usage(errmsg) \ + util_usage(errmsg, usage_synopsis, usage_short_opts, \ + usage_long_opts, usage_opts_help) + +void util_version(void) +{ + printf("Version: %s\n", "(U-Boot)"); + exit(0); +} + +static void scan_args(struct display_info *disp, int argc, char *argv[]) +{ + int opt; + + while ((opt = util_getopt_long()) != EOF) { + int type = 0; + int inc = 1; + + switch (opt) { + case_USAGE_COMMON_FLAGS + case 'a': + disp->show_addr = 1; + break; + case 'A': + disp->all = 1; + break; + case 'b': + type = FDT_NODE_HAS_PROP; + break; + case 'C': + inc = 0; + /* no break */ + case 'c': + type = FDT_IS_COMPAT; + break; + case 'd': + disp->diff = 1; + break; + case 'e': + disp->flags |= FDT_REG_DIRECT_SUBNODES; + break; + case 'f': + disp->show_offset = 1; + break; + case 'G': + inc = 0; + /* no break */ + case 'g': + type = FDT_ANY_GLOBAL; + break; + case 'H': + disp->header = 1; + break; + case 'l': + disp->region_list = 1; + break; + case 'L': + disp->list_strings = 1; + break; + case 'm': + disp->flags |= FDT_REG_ADD_MEM_RSVMAP; + break; + case 'N': + inc = 0; + /* no break */ + case 'n': + type = FDT_IS_NODE; + break; + case 'o': + disp->output_fname = optarg; + break; + case 'O': + if (!strcmp(optarg, "dtb")) + disp->output = OUT_DTB; + else if (!strcmp(optarg, "dts")) + disp->output = OUT_DTS; + else if (!strcmp(optarg, "bin")) + disp->output = OUT_BIN; + else + usage("Unknown output format"); + break; + case 'P': + inc = 0; + /* no break */ + case 'p': + type = FDT_IS_PROP; + break; + case 'r': + disp->remove_strings = 1; + break; + case 'R': + disp->include_root = 1; + break; + case 's': + disp->flags |= FDT_REG_ALL_SUBNODES; + break; + case 'S': + disp->flags &= ~FDT_REG_SUPERNODES; + break; + case 't': + disp->flags |= FDT_REG_ADD_STRING_TAB; + break; + case 'T': + disp->add_aliases = 1; + break; + case 'v': + disp->invert = 1; + break; + case 'I': + disp->show_dts_version = 1; + break; + } + + if (type && value_add(disp, &disp->value_head, type, inc, + optarg)) + usage("Cannot add value"); + } + + if (disp->invert && disp->types_exc) + usage("-v has no meaning when used with 'exclude' conditions"); +} + +int main(int argc, char *argv[]) +{ + char *filename = NULL; + struct display_info disp; + int ret; + + /* set defaults */ + memset(&disp, '\0', sizeof(disp)); + disp.flags = FDT_REG_SUPERNODES; /* Default flags */ + + scan_args(&disp, argc, argv); + + /* Show matched lines in colour if we can */ + disp.colour = disp.all && isatty(0); + + /* Any additional arguments can match anything, just like -g */ + while (optind < argc - 1) { + if (value_add(&disp, &disp.value_head, FDT_IS_ANY, 1, + argv[optind++])) + usage("Cannot add value"); + } + + if (optind < argc) + filename = argv[optind++]; + if (!filename) + usage("Missing filename"); + + /* If a valid .dtb is required, set flags to ensure we get one */ + if (disp.output == OUT_DTB) { + disp.header = 1; + disp.flags |= FDT_REG_ADD_MEM_RSVMAP | FDT_REG_ADD_STRING_TAB; + } + + if (disp.output_fname) { + disp.fout = fopen(disp.output_fname, "w"); + if (!disp.fout) + usage("Cannot open output file"); + } else { + disp.fout = stdout; + } + + /* Run the grep and output the results */ + ret = do_fdtgrep(&disp, filename); + if (disp.output_fname) + fclose(disp.fout); + if (ret) + return 1; + + return 0; +} diff --git a/tools/u-boot-tools/fdtgrep.o b/tools/u-boot-tools/fdtgrep.o new file mode 100644 index 0000000000000000000000000000000000000000..0a68319e2faf6bcdc1ce989a5b29c037ac3de9e7 GIT binary patch literal 28544 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 D^x97> literal 0 HcmV?d00001 diff --git a/tools/u-boot-tools/file2include.c b/tools/u-boot-tools/file2include.c new file mode 100644 index 0000000..775440c --- /dev/null +++ b/tools/u-boot-tools/file2include.c @@ -0,0 +1,103 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Convert a file image to a C define + * + * Copyright (c) 2017 Heinrich Schuchardt <xypron.glpk@gmx.de> + * + * For testing EFI disk management we need an in memory image of + * a disk. + * + * The tool file2include converts a file to a C include. The file + * is separated into strings of 8 bytes. Only the non-zero strings + * are written to the include. The output format has been designed + * to maintain readability. + * + * As the disk image needed for testing contains mostly zeroes a high + * compression ratio can be attained. + */ +#include <stdio.h> +#include <stdlib.h> +#include <stdint.h> + +/* Size of the blocks written to the compressed file */ +#define BLOCK_SIZE 8 + +int main(int argc, char *argv[]) +{ + FILE *file; + int ret; + unsigned char *buf; + size_t count, i, j; + + /* Provide usage help */ + if (argc != 2) { + printf("Usage:\n%s FILENAME\n", argv[0]); + return EXIT_FAILURE; + } + /* Open file */ + file = fopen(argv[1], "r"); + if (!file) { + perror("fopen"); + return EXIT_FAILURE; + } + /* Get file length */ + ret = fseek(file, 0, SEEK_END); + if (ret < 0) { + perror("fseek"); + return EXIT_FAILURE; + } + count = ftell(file); + if (!count) { + fprintf(stderr, "File %s has length 0\n", argv[1]); + return EXIT_FAILURE; + } + rewind(file); + /* Read file */ + buf = malloc(count); + if (!buf) { + perror("calloc"); + return EXIT_FAILURE; + } + count = fread(buf, 1, count, file); + + /* Generate output */ + printf("/* SPDX-License-Identifier: GPL-2.0+ */\n"); + printf("/*\n"); + printf(" * Non-zero %u byte strings of a disk image\n", BLOCK_SIZE); + printf(" *\n"); + printf(" * Generated with tools/file2include\n"); + printf(" */\n\n"); + printf("#define EFI_ST_DISK_IMG { 0x%08zx, { \\\n", count); + + for (i = 0; i < count; i += BLOCK_SIZE) { + int c = 0; + + for (j = i; j < i + BLOCK_SIZE && j < count; ++j) { + if (buf[j]) + c = 1; + } + if (!c) + continue; + printf("\t{0x%08zx, \"", i); + for (j = i; j < i + BLOCK_SIZE && j < count; ++j) + printf("\\x%02x", buf[j]); + printf("\"}, /* "); + for (j = i; j < i + BLOCK_SIZE && j < count; ++j) { + if (buf[j] != '*' && buf[j] >= 0x20 && buf[j] <= 0x7e) + printf("%c", buf[j]); + else + printf("."); + } + printf(" */ \\\n"); + } + printf("\t{0, NULL} } }\n"); + + /* Release resources */ + free(buf); + ret = fclose(file); + if (ret) { + perror("fclose"); + return EXIT_FAILURE; + } + return EXIT_SUCCESS; +} diff --git a/tools/u-boot-tools/fit_check_sign.c b/tools/u-boot-tools/fit_check_sign.c new file mode 100644 index 0000000..62adc75 --- /dev/null +++ b/tools/u-boot-tools/fit_check_sign.c @@ -0,0 +1,96 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2014 + * DENX Software Engineering + * Heiko Schocher <hs@denx.de> + * + * Based on: + * (C) Copyright 2008 Semihalf + * + * (C) Copyright 2000-2004 + * DENX Software Engineering + * Wolfgang Denk, wd@denx.de + * + * Updated-by: Prafulla Wadaskar <prafulla@marvell.com> + * FIT image specific code abstracted from mkimage.c + * some functions added to address abstraction + * + * All rights reserved. + */ + +#include "mkimage.h" +#include "fit_common.h" +#include <image.h> +#include <u-boot/crc.h> + +void usage(char *cmdname) +{ + fprintf(stderr, "Usage: %s -f fit file -k key file\n" + " -f ==> set fit file which should be checked'\n" + " -k ==> set key file which contains the key'\n", + cmdname); + exit(EXIT_FAILURE); +} + +int main(int argc, char **argv) +{ + int ffd = -1; + int kfd = -1; + struct stat fsbuf; + struct stat ksbuf; + void *fit_blob; + char *fdtfile = NULL; + char *keyfile = NULL; + char cmdname[256]; + int ret; + void *key_blob; + int c; + + strncpy(cmdname, *argv, sizeof(cmdname) - 1); + cmdname[sizeof(cmdname) - 1] = '\0'; + while ((c = getopt(argc, argv, "f:k:")) != -1) + switch (c) { + case 'f': + fdtfile = optarg; + break; + case 'k': + keyfile = optarg; + break; + default: + usage(cmdname); + break; + } + + if (!fdtfile) { + fprintf(stderr, "%s: Missing fdt file\n", *argv); + usage(*argv); + } + if (!keyfile) { + fprintf(stderr, "%s: Missing key file\n", *argv); + usage(*argv); + } + + ffd = mmap_fdt(cmdname, fdtfile, 0, &fit_blob, &fsbuf, false); + if (ffd < 0) + return EXIT_FAILURE; + kfd = mmap_fdt(cmdname, keyfile, 0, &key_blob, &ksbuf, false); + if (kfd < 0) + return EXIT_FAILURE; + + image_set_host_blob(key_blob); + ret = fit_check_sign(fit_blob, key_blob); + if (!ret) { + ret = EXIT_SUCCESS; + fprintf(stderr, "Signature check OK\n"); + } else { + ret = EXIT_FAILURE; + fprintf(stderr, "Signature check Bad (error %d)\n", ret); + } + + (void) munmap((void *)fit_blob, fsbuf.st_size); + (void) munmap((void *)key_blob, ksbuf.st_size); + + close(ffd); + close(kfd); + exit(ret); +} diff --git a/tools/u-boot-tools/fit_common.c b/tools/u-boot-tools/fit_common.c new file mode 100644 index 0000000..d96085e --- /dev/null +++ b/tools/u-boot-tools/fit_common.c @@ -0,0 +1,106 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2014 + * DENX Software Engineering + * Heiko Schocher <hs@denx.de> + * + * (C) Copyright 2008 Semihalf + * + * (C) Copyright 2000-2004 + * DENX Software Engineering + * Wolfgang Denk, wd@denx.de + * + * Updated-by: Prafulla Wadaskar <prafulla@marvell.com> + * FIT image specific code abstracted from mkimage.c + * some functions added to address abstraction + * + * All rights reserved. + */ + +#include "imagetool.h" +#include "mkimage.h" +#include "fit_common.h" +#include <image.h> +#include <u-boot/crc.h> + +int fit_verify_header(unsigned char *ptr, int image_size, + struct image_tool_params *params) +{ + return fdt_check_header(ptr); +} + +int fit_check_image_types(uint8_t type) +{ + if (type == IH_TYPE_FLATDT) + return EXIT_SUCCESS; + else + return EXIT_FAILURE; +} + +int mmap_fdt(const char *cmdname, const char *fname, size_t size_inc, + void **blobp, struct stat *sbuf, bool delete_on_error) +{ + void *ptr; + int fd; + + /* Load FIT blob into memory (we need to write hashes/signatures) */ + fd = open(fname, O_RDWR | O_BINARY); + + if (fd < 0) { + fprintf(stderr, "%s: Can't open %s: %s\n", + cmdname, fname, strerror(errno)); + goto err; + } + + if (fstat(fd, sbuf) < 0) { + fprintf(stderr, "%s: Can't stat %s: %s\n", + cmdname, fname, strerror(errno)); + goto err; + } + + if (size_inc) { + sbuf->st_size += size_inc; + if (ftruncate(fd, sbuf->st_size)) { + fprintf(stderr, "%s: Can't expand %s: %s\n", + cmdname, fname, strerror(errno)); + goto err; + } + } + + errno = 0; + ptr = mmap(0, sbuf->st_size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); + if ((ptr == MAP_FAILED) || (errno != 0)) { + fprintf(stderr, "%s: Can't read %s: %s\n", + cmdname, fname, strerror(errno)); + goto err; + } + + /* check if ptr has a valid blob */ + if (fdt_check_header(ptr)) { + fprintf(stderr, "%s: Invalid FIT blob\n", cmdname); + goto err; + } + + /* expand if needed */ + if (size_inc) { + int ret; + + ret = fdt_open_into(ptr, ptr, sbuf->st_size); + if (ret) { + fprintf(stderr, "%s: Cannot expand FDT: %s\n", + cmdname, fdt_strerror(ret)); + goto err; + } + } + + *blobp = ptr; + return fd; + +err: + if (fd >= 0) + close(fd); + if (delete_on_error) + unlink(fname); + + return -1; +} diff --git a/tools/u-boot-tools/fit_common.h b/tools/u-boot-tools/fit_common.h new file mode 100644 index 0000000..71e792e --- /dev/null +++ b/tools/u-boot-tools/fit_common.h @@ -0,0 +1,32 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * (C) Copyright 2014 + */ + +#ifndef _FIT_COMMON_H_ +#define _FIT_COMMON_H_ + +#include "imagetool.h" +#include "mkimage.h" +#include <image.h> + +int fit_verify_header(unsigned char *ptr, int image_size, + struct image_tool_params *params); + +int fit_check_image_types(uint8_t type); + +/** + * Map an FDT into memory, optionally increasing its size + * + * @cmdname: Tool name (for displaying with error messages) + * @fname: Filename containing FDT + * @size_inc: Amount to increase size by (0 = leave it alone) + * @blobp: Returns pointer to FDT blob + * @sbuf: File status information is stored here + * @delete_on_error: true to delete the file if we get an error + * @return 0 if OK, -1 on error. + */ +int mmap_fdt(const char *cmdname, const char *fname, size_t size_inc, + void **blobp, struct stat *sbuf, bool delete_on_error); + +#endif /* _FIT_COMMON_H_ */ diff --git a/tools/u-boot-tools/fit_common.o b/tools/u-boot-tools/fit_common.o new file mode 100644 index 0000000000000000000000000000000000000000..2535d80eed761949039a29489df82e5178639bb2 GIT binary patch literal 3800 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 XG^i}crr!W$Ap-+L21pSSM%NDj$!xL9 literal 0 HcmV?d00001 diff --git a/tools/u-boot-tools/fit_image.c b/tools/u-boot-tools/fit_image.c new file mode 100644 index 0000000..4b62635 --- /dev/null +++ b/tools/u-boot-tools/fit_image.c @@ -0,0 +1,835 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2008 Semihalf + * + * (C) Copyright 2000-2004 + * DENX Software Engineering + * Wolfgang Denk, wd@denx.de + * + * Updated-by: Prafulla Wadaskar <prafulla@marvell.com> + * FIT image specific code abstracted from mkimage.c + * some functions added to address abstraction + * + * All rights reserved. + */ + +#include "imagetool.h" +#include "fit_common.h" +#include "mkimage.h" +#include <image.h> +#include <stdarg.h> +#include <version.h> +#include <u-boot/crc.h> + +static image_header_t header; + +static int fit_add_file_data(struct image_tool_params *params, size_t size_inc, + const char *tmpfile) +{ + int tfd, destfd = 0; + void *dest_blob = NULL; + off_t destfd_size = 0; + struct stat sbuf; + void *ptr; + int ret = 0; + + tfd = mmap_fdt(params->cmdname, tmpfile, size_inc, &ptr, &sbuf, true); + if (tfd < 0) + return -EIO; + + if (params->keydest) { + struct stat dest_sbuf; + + destfd = mmap_fdt(params->cmdname, params->keydest, size_inc, + &dest_blob, &dest_sbuf, false); + if (destfd < 0) { + ret = -EIO; + goto err_keydest; + } + destfd_size = dest_sbuf.st_size; + } + + /* for first image creation, add a timestamp at offset 0 i.e., root */ + if (params->datafile) { + time_t time = imagetool_get_source_date(params->cmdname, + sbuf.st_mtime); + ret = fit_set_timestamp(ptr, 0, time); + } + + if (!ret) { + ret = fit_add_verification_data(params->keydir, dest_blob, ptr, + params->comment, + params->require_keys, + params->engine_id, + params->cmdname); + } + + if (dest_blob) { + munmap(dest_blob, destfd_size); + close(destfd); + } + +err_keydest: + munmap(ptr, sbuf.st_size); + close(tfd); + + return ret; +} + +/** + * fit_calc_size() - Calculate the approximate size of the FIT we will generate + */ +static int fit_calc_size(struct image_tool_params *params) +{ + struct content_info *cont; + int size, total_size; + + size = imagetool_get_filesize(params, params->datafile); + if (size < 0) + return -1; + total_size = size; + + if (params->fit_ramdisk) { + size = imagetool_get_filesize(params, params->fit_ramdisk); + if (size < 0) + return -1; + total_size += size; + } + + for (cont = params->content_head; cont; cont = cont->next) { + size = imagetool_get_filesize(params, cont->fname); + if (size < 0) + return -1; + + /* Add space for properties */ + total_size += size + 300; + } + + /* Add plenty of space for headers, properties, nodes, etc. */ + total_size += 4096; + + return total_size; +} + +static int fdt_property_file(struct image_tool_params *params, + void *fdt, const char *name, const char *fname) +{ + struct stat sbuf; + void *ptr; + int ret; + int fd; + + fd = open(fname, O_RDWR | O_BINARY); + if (fd < 0) { + fprintf(stderr, "%s: Can't open %s: %s\n", + params->cmdname, fname, strerror(errno)); + return -1; + } + + if (fstat(fd, &sbuf) < 0) { + fprintf(stderr, "%s: Can't stat %s: %s\n", + params->cmdname, fname, strerror(errno)); + goto err; + } + + ret = fdt_property_placeholder(fdt, "data", sbuf.st_size, &ptr); + if (ret) + goto err; + ret = read(fd, ptr, sbuf.st_size); + if (ret != sbuf.st_size) { + fprintf(stderr, "%s: Can't read %s: %s\n", + params->cmdname, fname, strerror(errno)); + goto err; + } + close(fd); + + return 0; +err: + close(fd); + return -1; +} + +static int fdt_property_strf(void *fdt, const char *name, const char *fmt, ...) +{ + char str[100]; + va_list ptr; + + va_start(ptr, fmt); + vsnprintf(str, sizeof(str), fmt, ptr); + va_end(ptr); + return fdt_property_string(fdt, name, str); +} + +static void get_basename(char *str, int size, const char *fname) +{ + const char *p, *start, *end; + int len; + + /* + * Use the base name as the 'name' field. So for example: + * + * "arch/arm/dts/sun7i-a20-bananapro.dtb" + * becomes "sun7i-a20-bananapro" + */ + p = strrchr(fname, '/'); + start = p ? p + 1 : fname; + p = strrchr(fname, '.'); + end = p ? p : fname + strlen(fname); + len = end - start; + if (len >= size) + len = size - 1; + memcpy(str, start, len); + str[len] = '\0'; +} + +/** + * fit_write_images() - Write out a list of images to the FIT + * + * We always include the main image (params->datafile). If there are device + * tree files, we include an fdt- node for each of those too. + */ +static int fit_write_images(struct image_tool_params *params, char *fdt) +{ + struct content_info *cont; + const char *typename; + char str[100]; + int upto; + int ret; + + fdt_begin_node(fdt, "images"); + + /* First the main image */ + typename = genimg_get_type_short_name(params->fit_image_type); + snprintf(str, sizeof(str), "%s-1", typename); + fdt_begin_node(fdt, str); + fdt_property_string(fdt, FIT_DESC_PROP, params->imagename); + fdt_property_string(fdt, FIT_TYPE_PROP, typename); + fdt_property_string(fdt, FIT_ARCH_PROP, + genimg_get_arch_short_name(params->arch)); + fdt_property_string(fdt, FIT_OS_PROP, + genimg_get_os_short_name(params->os)); + fdt_property_string(fdt, FIT_COMP_PROP, + genimg_get_comp_short_name(params->comp)); + fdt_property_u32(fdt, FIT_LOAD_PROP, params->addr); + fdt_property_u32(fdt, FIT_ENTRY_PROP, params->ep); + + /* + * Put data last since it is large. SPL may only load the first part + * of the DT, so this way it can access all the above fields. + */ + ret = fdt_property_file(params, fdt, FIT_DATA_PROP, params->datafile); + if (ret) + return ret; + fdt_end_node(fdt); + + /* Now the device tree files if available */ + upto = 0; + for (cont = params->content_head; cont; cont = cont->next) { + if (cont->type != IH_TYPE_FLATDT) + continue; + snprintf(str, sizeof(str), "%s-%d", FIT_FDT_PROP, ++upto); + fdt_begin_node(fdt, str); + + get_basename(str, sizeof(str), cont->fname); + fdt_property_string(fdt, FIT_DESC_PROP, str); + ret = fdt_property_file(params, fdt, FIT_DATA_PROP, + cont->fname); + if (ret) + return ret; + fdt_property_string(fdt, FIT_TYPE_PROP, typename); + fdt_property_string(fdt, FIT_ARCH_PROP, + genimg_get_arch_short_name(params->arch)); + fdt_property_string(fdt, FIT_COMP_PROP, + genimg_get_comp_short_name(IH_COMP_NONE)); + fdt_end_node(fdt); + } + + /* And a ramdisk file if available */ + if (params->fit_ramdisk) { + fdt_begin_node(fdt, FIT_RAMDISK_PROP "-1"); + + fdt_property_string(fdt, FIT_TYPE_PROP, FIT_RAMDISK_PROP); + fdt_property_string(fdt, FIT_OS_PROP, + genimg_get_os_short_name(params->os)); + + ret = fdt_property_file(params, fdt, FIT_DATA_PROP, + params->fit_ramdisk); + if (ret) + return ret; + + fdt_end_node(fdt); + } + + fdt_end_node(fdt); + + return 0; +} + +/** + * fit_write_configs() - Write out a list of configurations to the FIT + * + * If there are device tree files, we include a configuration for each, which + * selects the main image (params->datafile) and its corresponding device + * tree file. + * + * Otherwise we just create a configuration with the main image in it. + */ +static void fit_write_configs(struct image_tool_params *params, char *fdt) +{ + struct content_info *cont; + const char *typename; + char str[100]; + int upto; + + fdt_begin_node(fdt, "configurations"); + fdt_property_string(fdt, FIT_DEFAULT_PROP, "conf-1"); + + upto = 0; + for (cont = params->content_head; cont; cont = cont->next) { + if (cont->type != IH_TYPE_FLATDT) + continue; + typename = genimg_get_type_short_name(cont->type); + snprintf(str, sizeof(str), "conf-%d", ++upto); + fdt_begin_node(fdt, str); + + get_basename(str, sizeof(str), cont->fname); + fdt_property_string(fdt, FIT_DESC_PROP, str); + + typename = genimg_get_type_short_name(params->fit_image_type); + snprintf(str, sizeof(str), "%s-1", typename); + fdt_property_string(fdt, typename, str); + + if (params->fit_ramdisk) + fdt_property_string(fdt, FIT_RAMDISK_PROP, + FIT_RAMDISK_PROP "-1"); + + snprintf(str, sizeof(str), FIT_FDT_PROP "-%d", upto); + fdt_property_string(fdt, FIT_FDT_PROP, str); + fdt_end_node(fdt); + } + + if (!upto) { + fdt_begin_node(fdt, "conf-1"); + typename = genimg_get_type_short_name(params->fit_image_type); + snprintf(str, sizeof(str), "%s-1", typename); + fdt_property_string(fdt, typename, str); + + if (params->fit_ramdisk) + fdt_property_string(fdt, FIT_RAMDISK_PROP, + FIT_RAMDISK_PROP "-1"); + + fdt_end_node(fdt); + } + + fdt_end_node(fdt); +} + +static int fit_build_fdt(struct image_tool_params *params, char *fdt, int size) +{ + int ret; + + ret = fdt_create(fdt, size); + if (ret) + return ret; + fdt_finish_reservemap(fdt); + fdt_begin_node(fdt, ""); + fdt_property_strf(fdt, FIT_DESC_PROP, + "%s image with one or more FDT blobs", + genimg_get_type_name(params->fit_image_type)); + fdt_property_strf(fdt, "creator", "U-Boot mkimage %s", PLAIN_VERSION); + fdt_property_u32(fdt, "#address-cells", 1); + ret = fit_write_images(params, fdt); + if (ret) + return ret; + fit_write_configs(params, fdt); + fdt_end_node(fdt); + ret = fdt_finish(fdt); + if (ret) + return ret; + + return fdt_totalsize(fdt); +} + +static int fit_build(struct image_tool_params *params, const char *fname) +{ + char *buf; + int size; + int ret; + int fd; + + size = fit_calc_size(params); + if (size < 0) + return -1; + buf = malloc(size); + if (!buf) { + fprintf(stderr, "%s: Out of memory (%d bytes)\n", + params->cmdname, size); + return -1; + } + ret = fit_build_fdt(params, buf, size); + if (ret < 0) { + fprintf(stderr, "%s: Failed to build FIT image\n", + params->cmdname); + goto err_buf; + } + size = ret; + fd = open(fname, O_RDWR | O_CREAT | O_TRUNC | O_BINARY, 0666); + if (fd < 0) { + fprintf(stderr, "%s: Can't open %s: %s\n", + params->cmdname, fname, strerror(errno)); + goto err_buf; + } + ret = write(fd, buf, size); + if (ret != size) { + fprintf(stderr, "%s: Can't write %s: %s\n", + params->cmdname, fname, strerror(errno)); + goto err; + } + close(fd); + free(buf); + + return 0; +err: + close(fd); +err_buf: + free(buf); + return -1; +} + +/** + * fit_extract_data() - Move all data outside the FIT + * + * This takes a normal FIT file and removes all the 'data' properties from it. + * The data is placed in an area after the FIT so that it can be accessed + * using an offset into that area. The 'data' properties turn into + * 'data-offset' properties. + * + * This function cannot cope with FITs with 'data-offset' properties. All + * data must be in 'data' properties on entry. + */ +static int fit_extract_data(struct image_tool_params *params, const char *fname) +{ + void *buf; + int buf_ptr; + int fit_size, new_size; + int fd; + struct stat sbuf; + void *fdt; + int ret; + int images; + int node; + + fd = mmap_fdt(params->cmdname, fname, 0, &fdt, &sbuf, false); + if (fd < 0) + return -EIO; + fit_size = fdt_totalsize(fdt); + + /* Allocate space to hold the image data we will extract */ + buf = malloc(fit_size); + if (!buf) { + ret = -ENOMEM; + goto err_munmap; + } + buf_ptr = 0; + + images = fdt_path_offset(fdt, FIT_IMAGES_PATH); + if (images < 0) { + debug("%s: Cannot find /images node: %d\n", __func__, images); + ret = -EINVAL; + goto err_munmap; + } + + for (node = fdt_first_subnode(fdt, images); + node >= 0; + node = fdt_next_subnode(fdt, node)) { + const char *data; + int len; + + data = fdt_getprop(fdt, node, FIT_DATA_PROP, &len); + if (!data) + continue; + memcpy(buf + buf_ptr, data, len); + debug("Extracting data size %x\n", len); + + ret = fdt_delprop(fdt, node, FIT_DATA_PROP); + if (ret) { + ret = -EPERM; + goto err_munmap; + } + if (params->external_offset > 0) { + /* An external offset positions the data absolutely. */ + fdt_setprop_u32(fdt, node, FIT_DATA_POSITION_PROP, + params->external_offset + buf_ptr); + } else { + fdt_setprop_u32(fdt, node, FIT_DATA_OFFSET_PROP, + buf_ptr); + } + fdt_setprop_u32(fdt, node, FIT_DATA_SIZE_PROP, len); + + buf_ptr += (len + 3) & ~3; + } + + /* Pack the FDT and place the data after it */ + fdt_pack(fdt); + + debug("Size reduced from %x to %x\n", fit_size, fdt_totalsize(fdt)); + debug("External data size %x\n", buf_ptr); + new_size = fdt_totalsize(fdt); + new_size = (new_size + 3) & ~3; + munmap(fdt, sbuf.st_size); + + if (ftruncate(fd, new_size)) { + debug("%s: Failed to truncate file: %s\n", __func__, + strerror(errno)); + ret = -EIO; + goto err; + } + + /* Check if an offset for the external data was set. */ + if (params->external_offset > 0) { + if (params->external_offset < new_size) { + debug("External offset %x overlaps FIT length %x", + params->external_offset, new_size); + ret = -EINVAL; + goto err; + } + new_size = params->external_offset; + } + if (lseek(fd, new_size, SEEK_SET) < 0) { + debug("%s: Failed to seek to end of file: %s\n", __func__, + strerror(errno)); + ret = -EIO; + goto err; + } + if (write(fd, buf, buf_ptr) != buf_ptr) { + debug("%s: Failed to write external data to file %s\n", + __func__, strerror(errno)); + ret = -EIO; + goto err; + } + free(buf); + close(fd); + return 0; + +err_munmap: + munmap(fdt, sbuf.st_size); +err: + if (buf) + free(buf); + close(fd); + return ret; +} + +static int fit_import_data(struct image_tool_params *params, const char *fname) +{ + void *fdt, *old_fdt; + int fit_size, new_size, size, data_base; + int fd; + struct stat sbuf; + int ret; + int images; + int node; + + fd = mmap_fdt(params->cmdname, fname, 0, &old_fdt, &sbuf, false); + if (fd < 0) + return -EIO; + fit_size = fdt_totalsize(old_fdt); + data_base = (fit_size + 3) & ~3; + + /* Allocate space to hold the new FIT */ + size = sbuf.st_size + 16384; + fdt = malloc(size); + if (!fdt) { + fprintf(stderr, "%s: Failed to allocate memory (%d bytes)\n", + __func__, size); + ret = -ENOMEM; + goto err_has_fd; + } + ret = fdt_open_into(old_fdt, fdt, size); + if (ret) { + debug("%s: Failed to expand FIT: %s\n", __func__, + fdt_strerror(errno)); + ret = -EINVAL; + goto err_has_fd; + } + + images = fdt_path_offset(fdt, FIT_IMAGES_PATH); + if (images < 0) { + debug("%s: Cannot find /images node: %d\n", __func__, images); + ret = -EINVAL; + goto err_has_fd; + } + + for (node = fdt_first_subnode(fdt, images); + node >= 0; + node = fdt_next_subnode(fdt, node)) { + int buf_ptr; + int len; + + buf_ptr = fdtdec_get_int(fdt, node, "data-offset", -1); + len = fdtdec_get_int(fdt, node, "data-size", -1); + if (buf_ptr == -1 || len == -1) + continue; + debug("Importing data size %x\n", len); + + ret = fdt_setprop(fdt, node, "data", fdt + data_base + buf_ptr, + len); + if (ret) { + debug("%s: Failed to write property: %s\n", __func__, + fdt_strerror(ret)); + ret = -EINVAL; + goto err_has_fd; + } + } + + /* Close the old fd so we can re-use it. */ + close(fd); + + /* Pack the FDT and place the data after it */ + fdt_pack(fdt); + + new_size = fdt_totalsize(fdt); + debug("Size expanded from %x to %x\n", fit_size, new_size); + + fd = open(fname, O_RDWR | O_CREAT | O_TRUNC | O_BINARY, 0666); + if (fd < 0) { + fprintf(stderr, "%s: Can't open %s: %s\n", + params->cmdname, fname, strerror(errno)); + ret = -EIO; + goto err_no_fd; + } + if (write(fd, fdt, new_size) != new_size) { + debug("%s: Failed to write external data to file %s\n", + __func__, strerror(errno)); + ret = -EIO; + goto err_has_fd; + } + + ret = 0; + +err_has_fd: + close(fd); +err_no_fd: + munmap(old_fdt, sbuf.st_size); + free(fdt); + return ret; +} + +/** + * fit_handle_file - main FIT file processing function + * + * fit_handle_file() runs dtc to convert .its to .itb, includes + * binary data, updates timestamp property and calculates hashes. + * + * datafile - .its file + * imagefile - .itb file + * + * returns: + * only on success, otherwise calls exit (EXIT_FAILURE); + */ +static int fit_handle_file(struct image_tool_params *params) +{ + char tmpfile[MKIMAGE_MAX_TMPFILE_LEN]; + char cmd[MKIMAGE_MAX_DTC_CMDLINE_LEN]; + size_t size_inc; + int ret; + + /* Flattened Image Tree (FIT) format handling */ + debug ("FIT format handling\n"); + + /* call dtc to include binary properties into the tmp file */ + if (strlen (params->imagefile) + + strlen (MKIMAGE_TMPFILE_SUFFIX) + 1 > sizeof (tmpfile)) { + fprintf (stderr, "%s: Image file name (%s) too long, " + "can't create tmpfile", + params->imagefile, params->cmdname); + return (EXIT_FAILURE); + } + sprintf (tmpfile, "%s%s", params->imagefile, MKIMAGE_TMPFILE_SUFFIX); + + /* We either compile the source file, or use the existing FIT image */ + if (params->auto_its) { + if (fit_build(params, tmpfile)) { + fprintf(stderr, "%s: failed to build FIT\n", + params->cmdname); + return EXIT_FAILURE; + } + *cmd = '\0'; + } else if (params->datafile) { + /* dtc -I dts -O dtb -p 500 -o tmpfile datafile */ + snprintf(cmd, sizeof(cmd), "%s %s -o \"%s\" \"%s\"", + MKIMAGE_DTC, params->dtc, tmpfile, params->datafile); + debug("Trying to execute \"%s\"\n", cmd); + } else { + snprintf(cmd, sizeof(cmd), "cp \"%s\" \"%s\"", + params->imagefile, tmpfile); + } + if (*cmd && system(cmd) == -1) { + fprintf (stderr, "%s: system(%s) failed: %s\n", + params->cmdname, cmd, strerror(errno)); + goto err_system; + } + + /* Move the data so it is internal to the FIT, if needed */ + ret = fit_import_data(params, tmpfile); + if (ret) + goto err_system; + + /* + * Set hashes for images in the blob. Unfortunately we may need more + * space in either FDT, so keep trying until we succeed. + * + * Note: this is pretty inefficient for signing, since we must + * calculate the signature every time. It would be better to calculate + * all the data and then store it in a separate step. However, this + * would be considerably more complex to implement. Generally a few + * steps of this loop is enough to sign with several keys. + */ + for (size_inc = 0; size_inc < 64 * 1024; size_inc += 1024) { + ret = fit_add_file_data(params, size_inc, tmpfile); + if (!ret || ret != -ENOSPC) + break; + } + + if (ret) { + fprintf(stderr, "%s Can't add hashes to FIT blob: %d\n", + params->cmdname, ret); + goto err_system; + } + + /* Move the data so it is external to the FIT, if requested */ + if (params->external_data) { + ret = fit_extract_data(params, tmpfile); + if (ret) + goto err_system; + } + + if (rename (tmpfile, params->imagefile) == -1) { + fprintf (stderr, "%s: Can't rename %s to %s: %s\n", + params->cmdname, tmpfile, params->imagefile, + strerror (errno)); + unlink (tmpfile); + unlink (params->imagefile); + return EXIT_FAILURE; + } + return EXIT_SUCCESS; + +err_system: + unlink(tmpfile); + return -1; +} + +/** + * fit_image_extract - extract a FIT component image + * @fit: pointer to the FIT format image header + * @image_noffset: offset of the component image node + * @file_name: name of the file to store the FIT sub-image + * + * returns: + * zero in case of success or a negative value if fail. + */ +static int fit_image_extract( + const void *fit, + int image_noffset, + const char *file_name) +{ + const void *file_data; + size_t file_size = 0; + + /* get the "data" property of component at offset "image_noffset" */ + fit_image_get_data(fit, image_noffset, &file_data, &file_size); + + /* save the "file_data" into the file specified by "file_name" */ + return imagetool_save_subimage(file_name, (ulong) file_data, file_size); +} + +/** + * fit_extract_contents - retrieve a sub-image component from the FIT image + * @ptr: pointer to the FIT format image header + * @params: command line parameters + * + * returns: + * zero in case of success or a negative value if fail. + */ +static int fit_extract_contents(void *ptr, struct image_tool_params *params) +{ + int images_noffset; + int noffset; + int ndepth; + const void *fit = ptr; + int count = 0; + const char *p; + + /* Indent string is defined in header image.h */ + p = IMAGE_INDENT_STRING; + + if (!fit_check_format(fit)) { + printf("Bad FIT image format\n"); + return -1; + } + + /* 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 -1; + } + + /* Avoid any overrun */ + count = fit_get_subimage_count(fit, images_noffset); + if ((params->pflag < 0) || (count <= params->pflag)) { + printf("No such component at '%d'\n", params->pflag); + return -1; + } + + /* Process its subnodes, extract the desired component from image */ + 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. + */ + if (params->pflag == count) { + printf("Extracted:\n%s Image %u (%s)\n", p, + count, fit_get_name(fit, noffset, NULL)); + + fit_image_print(fit, noffset, p); + + return fit_image_extract(fit, noffset, + params->outfile); + } + + count++; + } + } + + return 0; +} + +static int fit_check_params(struct image_tool_params *params) +{ + if (params->auto_its) + return 0; + return ((params->dflag && (params->fflag || params->lflag)) || + (params->fflag && (params->dflag || params->lflag)) || + (params->lflag && (params->dflag || params->fflag))); +} + +U_BOOT_IMAGE_TYPE( + fitimage, + "FIT Image support", + sizeof(image_header_t), + (void *)&header, + fit_check_params, + fit_verify_header, + fit_print_contents, + NULL, + fit_extract_contents, + fit_check_image_types, + fit_handle_file, + NULL /* FIT images use DTB header */ +); diff --git a/tools/u-boot-tools/fit_image.o b/tools/u-boot-tools/fit_image.o new file mode 100644 index 0000000000000000000000000000000000000000..ba6cb9e94b0ca5204e264e5ff639996c1d5a4248 GIT binary patch literal 20672 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 PYX$~}c`$`g8eKmC&@j+b literal 0 HcmV?d00001 diff --git a/tools/u-boot-tools/fit_info.c b/tools/u-boot-tools/fit_info.c new file mode 100644 index 0000000..45e0b31 --- /dev/null +++ b/tools/u-boot-tools/fit_info.c @@ -0,0 +1,108 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2014 + * DENX Software Engineering + * Heiko Schocher <hs@denx.de> + * + * fit_info: print the offset and the len of a property from + * node in a fit file. + * + * Based on: + * (C) Copyright 2008 Semihalf + * + * (C) Copyright 2000-2004 + * DENX Software Engineering + * Wolfgang Denk, wd@denx.de + * + * Updated-by: Prafulla Wadaskar <prafulla@marvell.com> + * FIT image specific code abstracted from mkimage.c + * some functions added to address abstraction + * + * All rights reserved. + */ + +#include "mkimage.h" +#include "fit_common.h" +#include <image.h> +#include <u-boot/crc.h> + +void usage(char *cmdname) +{ + fprintf(stderr, "Usage: %s -f fit file -n node -p property\n" + " -f ==> set fit file which is used'\n" + " -n ==> set node name'\n" + " -p ==> set property name'\n", + cmdname); + exit(EXIT_FAILURE); +} + +int main(int argc, char **argv) +{ + int ffd = -1; + struct stat fsbuf; + void *fit_blob; + int len; + int nodeoffset; /* node offset from libfdt */ + const void *nodep; /* property node pointer */ + char *fdtfile = NULL; + char *nodename = NULL; + char *propertyname = NULL; + char cmdname[256]; + int c; + + strncpy(cmdname, *argv, sizeof(cmdname) - 1); + cmdname[sizeof(cmdname) - 1] = '\0'; + while ((c = getopt(argc, argv, "f:n:p:")) != -1) + switch (c) { + case 'f': + fdtfile = optarg; + break; + case 'n': + nodename = optarg; + break; + case 'p': + propertyname = optarg; + break; + default: + usage(cmdname); + break; + } + + if (!fdtfile) { + fprintf(stderr, "%s: Missing fdt file\n", *argv); + usage(*argv); + } + if (!nodename) { + fprintf(stderr, "%s: Missing node name\n", *argv); + usage(*argv); + } + if (!propertyname) { + fprintf(stderr, "%s: Missing property name\n", *argv); + usage(*argv); + } + ffd = mmap_fdt(cmdname, fdtfile, 0, &fit_blob, &fsbuf, false); + + if (ffd < 0) { + printf("Could not open %s\n", fdtfile); + exit(EXIT_FAILURE); + } + + nodeoffset = fdt_path_offset(fit_blob, nodename); + if (nodeoffset < 0) { + printf("%s not found.", nodename); + exit(EXIT_FAILURE); + } + nodep = fdt_getprop(fit_blob, nodeoffset, propertyname, &len); + if (len == 0) { + printf("len == 0 %s\n", propertyname); + exit(EXIT_FAILURE); + } + + printf("NAME: %s\n", fit_get_name(fit_blob, nodeoffset, NULL)); + printf("LEN: %d\n", len); + printf("OFF: %d\n", (int)(nodep - fit_blob)); + (void) munmap((void *)fit_blob, fsbuf.st_size); + + close(ffd); + exit(EXIT_SUCCESS); +} diff --git a/tools/u-boot-tools/gdb/Makefile b/tools/u-boot-tools/gdb/Makefile new file mode 100644 index 0000000..24152dc --- /dev/null +++ b/tools/u-boot-tools/gdb/Makefile @@ -0,0 +1,44 @@ +# SPDX-License-Identifier: GPL-2.0+ +# +# (C) Copyright 2006 +# Wolfgang Denk, DENX Software Engineering, wd@denx.de. +# +# (C) Copyright 2000 +# Murray Jensen <Murray.Jensen@csiro.au> + +ifneq ($(HOSTOS),cygwin) + +# Location of a usable BFD library, where we define "usable" as +# "built for ${HOST}, supports ${TARGET}". Sensible values are +# - When cross-compiling: the root of the cross-environment +# - Linux/ppc (native): /usr +# - NetBSD/ppc (native): you lose ... (must extract these from the +# binutils build directory, plus the native and U-Boot include +# files don't like each other) + +ifeq ($(HOSTOS),darwin) +BFD_ROOT_DIR = /usr/local/tools +else +ifeq ($(HOSTARCH),$(ARCH)) +# native +BFD_ROOT_DIR = /usr +else +#BFD_ROOT_DIR = /LinuxPPC/CDK # Linux/i386 +#BFD_ROOT_DIR = /usr/pkg/cross # NetBSD/i386 +BFD_ROOT_DIR = /opt/powerpc +endif +endif + +# +# Use native tools and options +# +HOST_EXTRACFLAGS := -I$(BFD_ROOT_DIR)/include -pedantic + +hostprogs-y := gdbsend gdbcont + +gdbsend-objs := gdbsend.o error.o remote.o serial.o +gdbcont-objs := gdbcont.o error.o remote.o serial.o + +always := $(hostprogs-y) + +endif # cygwin diff --git a/tools/u-boot-tools/gdb/error.c b/tools/u-boot-tools/gdb/error.c new file mode 100644 index 0000000..22a9b43 --- /dev/null +++ b/tools/u-boot-tools/gdb/error.c @@ -0,0 +1,64 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2000 + * Murray Jensen <Murray.Jensen@csiro.au> + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include "error.h" + +char *pname; + +void +Warning(char *fmt, ...) +{ + va_list args; + + fprintf(stderr, "%s: WARNING: ", pname); + + va_start(args, fmt); + vfprintf(stderr, fmt, args); + va_end(args); + + fprintf(stderr, "\n"); +} + +void +Error(char *fmt, ...) +{ + va_list args; + + fprintf(stderr, "%s: ERROR: ", pname); + + va_start(args, fmt); + vfprintf(stderr, fmt, args); + va_end(args); + + fprintf(stderr, "\n"); + + exit(1); +} + +void +Perror(char *fmt, ...) +{ + va_list args; + int e = errno; + char *p; + + fprintf(stderr, "%s: ERROR: ", pname); + + va_start(args, fmt); + vfprintf(stderr, fmt, args); + va_end(args); + + if ((p = strerror(e)) == NULL || *p == '\0') + fprintf(stderr, ": Unknown Error (%d)\n", e); + else + fprintf(stderr, ": %s\n", p); + + exit(1); +} diff --git a/tools/u-boot-tools/gdb/error.h b/tools/u-boot-tools/gdb/error.h new file mode 100644 index 0000000..252e89f --- /dev/null +++ b/tools/u-boot-tools/gdb/error.h @@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * (C) Copyright 2000 + * Murray Jensen <Murray.Jensen@csiro.au> + */ + +#include <stdarg.h> + +extern char *pname; + +extern void Warning(char *, ...); +extern void Error(char *, ...); +extern void Perror(char *, ...); diff --git a/tools/u-boot-tools/gdb/gdbcont.c b/tools/u-boot-tools/gdb/gdbcont.c new file mode 100644 index 0000000..9291a71 --- /dev/null +++ b/tools/u-boot-tools/gdb/gdbcont.c @@ -0,0 +1,70 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2000 + * Murray Jensen <Murray.Jensen@csiro.au> + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include "serial.h" +#include "error.h" +#include "remote.h" + +char *serialdev = "/dev/term/b"; +speed_t speed = B230400; +int verbose = 0; + +int +main(int ac, char **av) +{ + int c, sfd; + + if ((pname = strrchr(av[0], '/')) == NULL) + pname = av[0]; + else + pname++; + + while ((c = getopt(ac, av, "b:p:v")) != EOF) + switch (c) { + + case 'b': + if ((speed = cvtspeed(optarg)) == B0) + Error("can't decode baud rate specified in -b option"); + break; + + case 'p': + serialdev = optarg; + break; + + case 'v': + verbose = 1; + break; + + default: + usage: + fprintf(stderr, "Usage: %s [-b bps] [-p dev] [-v]\n", pname); + exit(1); + } + if (optind != ac) + goto usage; + + if (verbose) + fprintf(stderr, "Opening serial port and sending continue...\n"); + + if ((sfd = serialopen(serialdev, speed)) < 0) + Perror("open of serial device '%s' failed", serialdev); + + remote_desc = sfd; + remote_reset(); + remote_continue(); + + if (serialclose(sfd) < 0) + Perror("close of serial device '%s' failed", serialdev); + + if (verbose) + fprintf(stderr, "Done.\n"); + + return (0); +} diff --git a/tools/u-boot-tools/gdb/gdbsend.c b/tools/u-boot-tools/gdb/gdbsend.c new file mode 100644 index 0000000..19c4dcb --- /dev/null +++ b/tools/u-boot-tools/gdb/gdbsend.c @@ -0,0 +1,120 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2000 + * Murray Jensen <Murray.Jensen@csiro.au> + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <fcntl.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> +#include "serial.h" +#include "error.h" +#include "remote.h" + +char *serialdev = "/dev/term/b"; +speed_t speed = B230400; +int verbose = 0, docont = 0; +unsigned long addr = 0x10000UL; + +int +main(int ac, char **av) +{ + int c, sfd, ifd; + char *ifn, *image; + struct stat ist; + + if ((pname = strrchr(av[0], '/')) == NULL) + pname = av[0]; + else + pname++; + + while ((c = getopt(ac, av, "a:b:cp:v")) != EOF) + switch (c) { + + case 'a': { + char *ep; + + addr = strtol(optarg, &ep, 0); + if (ep == optarg || *ep != '\0') + Error("can't decode address specified in -a option"); + break; + } + + case 'b': + if ((speed = cvtspeed(optarg)) == B0) + Error("can't decode baud rate specified in -b option"); + break; + + case 'c': + docont = 1; + break; + + case 'p': + serialdev = optarg; + break; + + case 'v': + verbose = 1; + break; + + default: + usage: + fprintf(stderr, + "Usage: %s [-a addr] [-b bps] [-c] [-p dev] [-v] imagefile\n", + pname); + exit(1); + } + + if (optind != ac - 1) + goto usage; + ifn = av[optind++]; + + if (verbose) + fprintf(stderr, "Opening file and reading image...\n"); + + if ((ifd = open(ifn, O_RDONLY)) < 0) + Perror("can't open kernel image file '%s'", ifn); + + if (fstat(ifd, &ist) < 0) + Perror("fstat '%s' failed", ifn); + + if ((image = (char *)malloc(ist.st_size)) == NULL) + Perror("can't allocate %ld bytes for image", ist.st_size); + + if ((c = read(ifd, image, ist.st_size)) < 0) + Perror("read of %d bytes from '%s' failed", ist.st_size, ifn); + + if (c != ist.st_size) + Error("read of %ld bytes from '%s' failed (%d)", ist.st_size, ifn, c); + + if (close(ifd) < 0) + Perror("close of '%s' failed", ifn); + + if (verbose) + fprintf(stderr, "Opening serial port and sending image...\n"); + + if ((sfd = serialopen(serialdev, speed)) < 0) + Perror("open of serial device '%s' failed", serialdev); + + remote_desc = sfd; + remote_reset(); + remote_write_bytes(addr, image, ist.st_size); + + if (docont) { + if (verbose) + fprintf(stderr, "[continue]"); + remote_continue(); + } + + if (serialclose(sfd) < 0) + Perror("close of serial device '%s' failed", serialdev); + + if (verbose) + fprintf(stderr, "Done.\n"); + + return (0); +} diff --git a/tools/u-boot-tools/gdb/remote.c b/tools/u-boot-tools/gdb/remote.c new file mode 100644 index 0000000..3cd0421 --- /dev/null +++ b/tools/u-boot-tools/gdb/remote.c @@ -0,0 +1,915 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * taken from gdb/remote.c + * + * I am only interested in the write to memory stuff - everything else + * has been ripped out + * + * all the copyright notices etc have been left in + */ + +/* enough so that it will compile */ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> + +/*nicked from gcc..*/ + +#ifndef alloca +#ifdef __GNUC__ +#define alloca __builtin_alloca +#else /* not GNU C. */ +#if (!defined (__STDC__) && defined (sparc)) || defined (__sparc__) || defined (__sparc) || defined (__sgi) +#include <alloca.h> +#else /* not sparc */ +#if defined (MSDOS) && !defined (__TURBOC__) +#include <malloc.h> +#else /* not MSDOS, or __TURBOC__ */ +#if defined(_AIX) +#include <malloc.h> + #pragma alloca +#else /* not MSDOS, __TURBOC__, or _AIX */ +#ifdef __hpux +#endif /* __hpux */ +#endif /* not _AIX */ +#endif /* not MSDOS, or __TURBOC__ */ +#endif /* not sparc. */ +#endif /* not GNU C. */ +#ifdef __cplusplus +extern "C" { +#endif + void* alloca(size_t); +#ifdef __cplusplus +} +#endif +#endif /* alloca not defined. */ + + +#include "serial.h" +#include "error.h" +#include "remote.h" +#define REGISTER_BYTES 0 +#define fprintf_unfiltered fprintf +#define fprintf_filtered fprintf +#define fputs_unfiltered fputs +#define fputs_filtered fputs +#define fputc_unfiltered fputc +#define fputc_filtered fputc +#define printf_unfiltered printf +#define printf_filtered printf +#define puts_unfiltered puts +#define puts_filtered puts +#define putchar_unfiltered putchar +#define putchar_filtered putchar +#define fputstr_unfiltered(a,b,c) fputs((a), (c)) +#define gdb_stdlog stderr +#define SERIAL_READCHAR(fd,timo) serialreadchar((fd), (timo)) +#define SERIAL_WRITE(fd, addr, len) serialwrite((fd), (addr), (len)) +#define error Error +#define perror_with_name Perror +#define gdb_flush fflush +#define max(a,b) (((a)>(b))?(a):(b)) +#define min(a,b) (((a)<(b))?(a):(b)) +#define target_mourn_inferior() {} +#define ULONGEST unsigned long +#define CORE_ADDR unsigned long + +static int putpkt (char *); +static int putpkt_binary(char *, int); +static void getpkt (char *, int); + +static int remote_debug = 0, remote_register_buf_size = 0, watchdog = 0; + +int remote_desc = -1, remote_timeout = 10; + +static void +fputstrn_unfiltered(char *s, int n, int x, FILE *fp) +{ + while (n-- > 0) + fputc(*s++, fp); +} + +void +remote_reset(void) +{ + SERIAL_WRITE(remote_desc, "+", 1); +} + +void +remote_continue(void) +{ + putpkt("c"); +} + +/* Remote target communications for serial-line targets in custom GDB protocol + Copyright 1988, 91, 92, 93, 94, 95, 96, 97, 98, 1999 + Free Software Foundation, Inc. + + This file is part of GDB. + */ +/* *INDENT-OFF* */ +/* Remote communication protocol. + + A debug packet whose contents are <data> + is encapsulated for transmission in the form: + + $ <data> # CSUM1 CSUM2 + + <data> must be ASCII alphanumeric and cannot include characters + '$' or '#'. If <data> starts with two characters followed by + ':', then the existing stubs interpret this as a sequence number. + + CSUM1 and CSUM2 are ascii hex representation of an 8-bit + checksum of <data>, the most significant nibble is sent first. + the hex digits 0-9,a-f are used. + + Receiver responds with: + + + - if CSUM is correct and ready for next packet + - - if CSUM is incorrect + + <data> is as follows: + Most values are encoded in ascii hex digits. Signal numbers are according + to the numbering in target.h. + + Request Packet + + set thread Hct... Set thread for subsequent operations. + c = 'c' for thread used in step and + continue; t... can be -1 for all + threads. + c = 'g' for thread used in other + operations. If zero, pick a thread, + any thread. + reply OK for success + ENN for an error. + + read registers g + reply XX....X Each byte of register data + is described by two hex digits. + Registers are in the internal order + for GDB, and the bytes in a register + are in the same order the machine uses. + or ENN for an error. + + write regs GXX..XX Each byte of register data + is described by two hex digits. + reply OK for success + ENN for an error + + write reg Pn...=r... Write register n... with value r..., + which contains two hex digits for each + byte in the register (target byte + order). + reply OK for success + ENN for an error + (not supported by all stubs). + + read mem mAA..AA,LLLL AA..AA is address, LLLL is length. + reply XX..XX XX..XX is mem contents + Can be fewer bytes than requested + if able to read only part of the data. + or ENN NN is errno + + write mem MAA..AA,LLLL:XX..XX + AA..AA is address, + LLLL is number of bytes, + XX..XX is data + reply OK for success + ENN for an error (this includes the case + where only part of the data was + written). + + write mem XAA..AA,LLLL:XX..XX + (binary) AA..AA is address, + LLLL is number of bytes, + XX..XX is binary data + reply OK for success + ENN for an error + + continue cAA..AA AA..AA is address to resume + If AA..AA is omitted, + resume at same address. + + step sAA..AA AA..AA is address to resume + If AA..AA is omitted, + resume at same address. + + continue with Csig;AA..AA Continue with signal sig (hex signal + signal number). If ;AA..AA is omitted, + resume at same address. + + step with Ssig;AA..AA Like 'C' but step not continue. + signal + + last signal ? Reply the current reason for stopping. + This is the same reply as is generated + for step or cont : SAA where AA is the + signal number. + + detach D Reply OK. + + There is no immediate reply to step or cont. + The reply comes when the machine stops. + It is SAA AA is the signal number. + + or... TAAn...:r...;n...:r...;n...:r...; + AA = signal number + n... = register number (hex) + r... = register contents + n... = `thread' + r... = thread process ID. This is + a hex integer. + n... = other string not starting + with valid hex digit. + gdb should ignore this n,r pair + and go on to the next. This way + we can extend the protocol. + or... WAA The process exited, and AA is + the exit status. This is only + applicable for certains sorts of + targets. + or... XAA The process terminated with signal + AA. + or (obsolete) NAA;tttttttt;dddddddd;bbbbbbbb + AA = signal number + tttttttt = address of symbol "_start" + dddddddd = base of data section + bbbbbbbb = base of bss section. + Note: only used by Cisco Systems + targets. The difference between this + reply and the "qOffsets" query is that + the 'N' packet may arrive spontaneously + whereas the 'qOffsets' is a query + initiated by the host debugger. + or... OXX..XX XX..XX is hex encoding of ASCII data. This + can happen at any time while the + program is running and the debugger + should continue to wait for + 'W', 'T', etc. + + thread alive TXX Find out if the thread XX is alive. + reply OK thread is still alive + ENN thread is dead + + remote restart RXX Restart the remote server + + extended ops ! Use the extended remote protocol. + Sticky -- only needs to be set once. + + kill request k + + toggle debug d toggle debug flag (see 386 & 68k stubs) + reset r reset -- see sparc stub. + reserved <other> On other requests, the stub should + ignore the request and send an empty + response ($#<checksum>). This way + we can extend the protocol and GDB + can tell whether the stub it is + talking to uses the old or the new. + search tAA:PP,MM Search backwards starting at address + AA for a match with pattern PP and + mask MM. PP and MM are 4 bytes. + Not supported by all stubs. + + general query qXXXX Request info about XXXX. + general set QXXXX=yyyy Set value of XXXX to yyyy. + query sect offs qOffsets Get section offsets. Reply is + Text=xxx;Data=yyy;Bss=zzz + + Responses can be run-length encoded to save space. A '*' means that + the next character is an ASCII encoding giving a repeat count which + stands for that many repititions of the character preceding the '*'. + The encoding is n+29, yielding a printable character where n >=3 + (which is where rle starts to win). Don't use an n > 126. + + So + "0* " means the same as "0000". */ +/* *INDENT-ON* */ + +/* This variable (available to the user via "set remotebinarydownload") + dictates whether downloads are sent in binary (via the 'X' packet). + We assume that the stub can, and attempt to do it. This will be cleared if + the stub does not understand it. This switch is still needed, though + in cases when the packet is supported in the stub, but the connection + does not allow it (i.e., 7-bit serial connection only). */ +static int remote_binary_download = 1; + +/* Have we already checked whether binary downloads work? */ +static int remote_binary_checked; + +/* Maximum number of bytes to read/write at once. The value here + is chosen to fill up a packet (the headers account for the 32). */ +#define MAXBUFBYTES(N) (((N)-32)/2) + +/* Having this larger than 400 causes us to be incompatible with m68k-stub.c + and i386-stub.c. Normally, no one would notice because it only matters + for writing large chunks of memory (e.g. in downloads). Also, this needs + to be more than 400 if required to hold the registers (see below, where + we round it up based on REGISTER_BYTES). */ +/* Round up PBUFSIZ to hold all the registers, at least. */ +#define PBUFSIZ ((REGISTER_BYTES > MAXBUFBYTES (400)) \ + ? (REGISTER_BYTES * 2 + 32) \ + : 400) + + +/* This variable sets the number of bytes to be written to the target + in a single packet. Normally PBUFSIZ is satisfactory, but some + targets need smaller values (perhaps because the receiving end + is slow). */ + +static int remote_write_size = 0x7fffffff; + +/* This variable sets the number of bits in an address that are to be + sent in a memory ("M" or "m") packet. Normally, after stripping + leading zeros, the entire address would be sent. This variable + restricts the address to REMOTE_ADDRESS_SIZE bits. HISTORY: The + initial implementation of remote.c restricted the address sent in + memory packets to ``host::sizeof long'' bytes - (typically 32 + bits). Consequently, for 64 bit targets, the upper 32 bits of an + address was never sent. Since fixing this bug may cause a break in + some remote targets this variable is principly provided to + facilitate backward compatibility. */ + +static int remote_address_size; + +/* Convert hex digit A to a number. */ + +static int +fromhex (int a) +{ + if (a >= '0' && a <= '9') + return a - '0'; + else if (a >= 'a' && a <= 'f') + return a - 'a' + 10; + else if (a >= 'A' && a <= 'F') + return a - 'A' + 10; + else { + error ("Reply contains invalid hex digit %d", a); + return -1; + } +} + +/* Convert number NIB to a hex digit. */ + +static int +tohex (int nib) +{ + if (nib < 10) + return '0' + nib; + else + return 'a' + nib - 10; +} + +/* Return the number of hex digits in num. */ + +static int +hexnumlen (ULONGEST num) +{ + int i; + + for (i = 0; num != 0; i++) + num >>= 4; + + return max (i, 1); +} + +/* Set BUF to the hex digits representing NUM. */ + +static int +hexnumstr (char *buf, ULONGEST num) +{ + int i; + int len = hexnumlen (num); + + buf[len] = '\0'; + + for (i = len - 1; i >= 0; i--) + { + buf[i] = "0123456789abcdef"[(num & 0xf)]; + num >>= 4; + } + + return len; +} + +/* Mask all but the least significant REMOTE_ADDRESS_SIZE bits. */ + +static CORE_ADDR +remote_address_masked (CORE_ADDR addr) +{ + if (remote_address_size > 0 + && remote_address_size < (sizeof (ULONGEST) * 8)) + { + /* Only create a mask when that mask can safely be constructed + in a ULONGEST variable. */ + ULONGEST mask = 1; + mask = (mask << remote_address_size) - 1; + addr &= mask; + } + return addr; +} + +/* Determine whether the remote target supports binary downloading. + This is accomplished by sending a no-op memory write of zero length + to the target at the specified address. It does not suffice to send + the whole packet, since many stubs strip the eighth bit and subsequently + compute a wrong checksum, which causes real havoc with remote_write_bytes. + + NOTE: This can still lose if the serial line is not eight-bit clean. In + cases like this, the user should clear "remotebinarydownload". */ +static void +check_binary_download (CORE_ADDR addr) +{ + if (remote_binary_download && !remote_binary_checked) + { + char *buf = alloca (PBUFSIZ); + char *p; + remote_binary_checked = 1; + + p = buf; + *p++ = 'X'; + p += hexnumstr (p, (ULONGEST) addr); + *p++ = ','; + p += hexnumstr (p, (ULONGEST) 0); + *p++ = ':'; + *p = '\0'; + + putpkt_binary (buf, (int) (p - buf)); + getpkt (buf, 0); + + if (buf[0] == '\0') + remote_binary_download = 0; + } + + if (remote_debug) + { + if (remote_binary_download) + fprintf_unfiltered (gdb_stdlog, + "binary downloading suppported by target\n"); + else + fprintf_unfiltered (gdb_stdlog, + "binary downloading NOT suppported by target\n"); + } +} + +/* Write memory data directly to the remote machine. + This does not inform the data cache; the data cache uses this. + MEMADDR is the address in the remote memory space. + MYADDR is the address of the buffer in our space. + LEN is the number of bytes. + + Returns number of bytes transferred, or 0 for error. */ + +int +remote_write_bytes (memaddr, myaddr, len) + CORE_ADDR memaddr; + char *myaddr; + int len; +{ + unsigned char *buf = alloca (PBUFSIZ); + int max_buf_size; /* Max size of packet output buffer */ + int origlen; + extern int verbose; + + /* Verify that the target can support a binary download */ + check_binary_download (memaddr); + + /* Chop the transfer down if necessary */ + + max_buf_size = min (remote_write_size, PBUFSIZ); + if (remote_register_buf_size != 0) + max_buf_size = min (max_buf_size, remote_register_buf_size); + + /* Subtract header overhead from max payload size - $M<memaddr>,<len>:#nn */ + max_buf_size -= 2 + hexnumlen (memaddr + len - 1) + 1 + hexnumlen (len) + 4; + + origlen = len; + while (len > 0) + { + unsigned char *p, *plen; + int todo; + int i; + + /* construct "M"<memaddr>","<len>":" */ + /* sprintf (buf, "M%lx,%x:", (unsigned long) memaddr, todo); */ + memaddr = remote_address_masked (memaddr); + p = buf; + if (remote_binary_download) + { + *p++ = 'X'; + todo = min (len, max_buf_size); + } + else + { + *p++ = 'M'; + todo = min (len, max_buf_size / 2); /* num bytes that will fit */ + } + + p += hexnumstr ((char *)p, (ULONGEST) memaddr); + *p++ = ','; + + plen = p; /* remember where len field goes */ + p += hexnumstr ((char *)p, (ULONGEST) todo); + *p++ = ':'; + *p = '\0'; + + /* We send target system values byte by byte, in increasing byte + addresses, each byte encoded as two hex characters (or one + binary character). */ + if (remote_binary_download) + { + int escaped = 0; + for (i = 0; + (i < todo) && (i + escaped) < (max_buf_size - 2); + i++) + { + switch (myaddr[i] & 0xff) + { + case '$': + case '#': + case 0x7d: + /* These must be escaped */ + escaped++; + *p++ = 0x7d; + *p++ = (myaddr[i] & 0xff) ^ 0x20; + break; + default: + *p++ = myaddr[i] & 0xff; + break; + } + } + + if (i < todo) + { + /* Escape chars have filled up the buffer prematurely, + and we have actually sent fewer bytes than planned. + Fix-up the length field of the packet. */ + + /* FIXME: will fail if new len is a shorter string than + old len. */ + + plen += hexnumstr ((char *)plen, (ULONGEST) i); + *plen++ = ':'; + } + } + else + { + for (i = 0; i < todo; i++) + { + *p++ = tohex ((myaddr[i] >> 4) & 0xf); + *p++ = tohex (myaddr[i] & 0xf); + } + *p = '\0'; + } + + putpkt_binary ((char *)buf, (int) (p - buf)); + getpkt ((char *)buf, 0); + + if (buf[0] == 'E') + { + /* There is no correspondance between what the remote protocol uses + for errors and errno codes. We would like a cleaner way of + representing errors (big enough to include errno codes, bfd_error + codes, and others). But for now just return EIO. */ + errno = EIO; + return 0; + } + + /* Increment by i, not by todo, in case escape chars + caused us to send fewer bytes than we'd planned. */ + myaddr += i; + memaddr += i; + len -= i; + + if (verbose) + putc('.', stderr); + } + return origlen; +} + +/* Stuff for dealing with the packets which are part of this protocol. + See comment at top of file for details. */ + +/* Read a single character from the remote end, masking it down to 7 bits. */ + +static int +readchar (int timeout) +{ + int ch; + + ch = SERIAL_READCHAR (remote_desc, timeout); + + switch (ch) + { + case SERIAL_EOF: + error ("Remote connection closed"); + case SERIAL_ERROR: + perror_with_name ("Remote communication error"); + case SERIAL_TIMEOUT: + return ch; + default: + return ch & 0x7f; + } +} + +static int +putpkt (buf) + char *buf; +{ + return putpkt_binary (buf, strlen (buf)); +} + +/* Send a packet to the remote machine, with error checking. The data + of the packet is in BUF. The string in BUF can be at most PBUFSIZ - 5 + to account for the $, # and checksum, and for a possible /0 if we are + debugging (remote_debug) and want to print the sent packet as a string */ + +static int +putpkt_binary (buf, cnt) + char *buf; + int cnt; +{ + int i; + unsigned char csum = 0; + char *buf2 = alloca (PBUFSIZ); + char *junkbuf = alloca (PBUFSIZ); + + int ch; + int tcount = 0; + char *p; + + /* Copy the packet into buffer BUF2, encapsulating it + and giving it a checksum. */ + + if (cnt > BUFSIZ - 5) /* Prosanity check */ + abort (); + + p = buf2; + *p++ = '$'; + + for (i = 0; i < cnt; i++) + { + csum += buf[i]; + *p++ = buf[i]; + } + *p++ = '#'; + *p++ = tohex ((csum >> 4) & 0xf); + *p++ = tohex (csum & 0xf); + + /* Send it over and over until we get a positive ack. */ + + while (1) + { + int started_error_output = 0; + + if (remote_debug) + { + *p = '\0'; + fprintf_unfiltered (gdb_stdlog, "Sending packet: "); + fputstrn_unfiltered (buf2, p - buf2, 0, gdb_stdlog); + fprintf_unfiltered (gdb_stdlog, "..."); + gdb_flush (gdb_stdlog); + } + if (SERIAL_WRITE (remote_desc, buf2, p - buf2)) + perror_with_name ("putpkt: write failed"); + + /* read until either a timeout occurs (-2) or '+' is read */ + while (1) + { + ch = readchar (remote_timeout); + + if (remote_debug) + { + switch (ch) + { + case '+': + case SERIAL_TIMEOUT: + case '$': + if (started_error_output) + { + putchar_unfiltered ('\n'); + started_error_output = 0; + } + } + } + + switch (ch) + { + case '+': + if (remote_debug) + fprintf_unfiltered (gdb_stdlog, "Ack\n"); + return 1; + case SERIAL_TIMEOUT: + tcount++; + if (tcount > 3) + return 0; + break; /* Retransmit buffer */ + case '$': + { + /* It's probably an old response, and we're out of sync. + Just gobble up the packet and ignore it. */ + getpkt (junkbuf, 0); + continue; /* Now, go look for + */ + } + default: + if (remote_debug) + { + if (!started_error_output) + { + started_error_output = 1; + fprintf_unfiltered (gdb_stdlog, "putpkt: Junk: "); + } + fputc_unfiltered (ch & 0177, gdb_stdlog); + } + continue; + } + break; /* Here to retransmit */ + } + +#if 0 + /* This is wrong. If doing a long backtrace, the user should be + able to get out next time we call QUIT, without anything as + violent as interrupt_query. If we want to provide a way out of + here without getting to the next QUIT, it should be based on + hitting ^C twice as in remote_wait. */ + if (quit_flag) + { + quit_flag = 0; + interrupt_query (); + } +#endif + } +} + +/* Come here after finding the start of the frame. Collect the rest + into BUF, verifying the checksum, length, and handling run-length + compression. Returns 0 on any error, 1 on success. */ + +static int +read_frame (char *buf) +{ + unsigned char csum; + char *bp; + int c; + + csum = 0; + bp = buf; + + while (1) + { + c = readchar (remote_timeout); + + switch (c) + { + case SERIAL_TIMEOUT: + if (remote_debug) + fputs_filtered ("Timeout in mid-packet, retrying\n", gdb_stdlog); + return 0; + case '$': + if (remote_debug) + fputs_filtered ("Saw new packet start in middle of old one\n", + gdb_stdlog); + return 0; /* Start a new packet, count retries */ + case '#': + { + unsigned char pktcsum; + + *bp = '\000'; + + pktcsum = fromhex (readchar (remote_timeout)) << 4; + pktcsum |= fromhex (readchar (remote_timeout)); + + if (csum == pktcsum) + { + return 1; + } + + if (remote_debug) + { + fprintf_filtered (gdb_stdlog, + "Bad checksum, sentsum=0x%x, csum=0x%x, buf=", + pktcsum, csum); + fputs_filtered (buf, gdb_stdlog); + fputs_filtered ("\n", gdb_stdlog); + } + return 0; + } + case '*': /* Run length encoding */ + csum += c; + c = readchar (remote_timeout); + csum += c; + c = c - ' ' + 3; /* Compute repeat count */ + + if (c > 0 && c < 255 && bp + c - 1 < buf + PBUFSIZ - 1) + { + memset (bp, *(bp - 1), c); + bp += c; + continue; + } + + *bp = '\0'; + printf_filtered ("Repeat count %d too large for buffer: ", c); + puts_filtered (buf); + puts_filtered ("\n"); + return 0; + default: + if (bp < buf + PBUFSIZ - 1) + { + *bp++ = c; + csum += c; + continue; + } + + *bp = '\0'; + puts_filtered ("Remote packet too long: "); + puts_filtered (buf); + puts_filtered ("\n"); + + return 0; + } + } +} + +/* Read a packet from the remote machine, with error checking, and + store it in BUF. BUF is expected to be of size PBUFSIZ. If + FOREVER, wait forever rather than timing out; this is used while + the target is executing user code. */ + +static void +getpkt (buf, forever) + char *buf; + int forever; +{ + int c; + int tries; + int timeout; + int val; + + strcpy (buf, "timeout"); + + if (forever) + { + timeout = watchdog > 0 ? watchdog : -1; + } + + else + timeout = remote_timeout; + +#define MAX_TRIES 3 + + for (tries = 1; tries <= MAX_TRIES; tries++) + { + /* This can loop forever if the remote side sends us characters + continuously, but if it pauses, we'll get a zero from readchar + because of timeout. Then we'll count that as a retry. */ + + /* Note that we will only wait forever prior to the start of a packet. + After that, we expect characters to arrive at a brisk pace. They + should show up within remote_timeout intervals. */ + + do + { + c = readchar (timeout); + + if (c == SERIAL_TIMEOUT) + { + if (forever) /* Watchdog went off. Kill the target. */ + { + target_mourn_inferior (); + error ("Watchdog has expired. Target detached.\n"); + } + if (remote_debug) + fputs_filtered ("Timed out.\n", gdb_stdlog); + goto retry; + } + } + while (c != '$'); + + /* We've found the start of a packet, now collect the data. */ + + val = read_frame (buf); + + if (val == 1) + { + if (remote_debug) + { + fprintf_unfiltered (gdb_stdlog, "Packet received: "); + fputstr_unfiltered (buf, 0, gdb_stdlog); + fprintf_unfiltered (gdb_stdlog, "\n"); + } + SERIAL_WRITE (remote_desc, "+", 1); + return; + } + + /* Try the whole thing again. */ + retry: + SERIAL_WRITE (remote_desc, "-", 1); + } + + /* We have tried hard enough, and just can't receive the packet. Give up. */ + + printf_unfiltered ("Ignoring packet error, continuing...\n"); + SERIAL_WRITE (remote_desc, "+", 1); +} diff --git a/tools/u-boot-tools/gdb/remote.h b/tools/u-boot-tools/gdb/remote.h new file mode 100644 index 0000000..2a68402 --- /dev/null +++ b/tools/u-boot-tools/gdb/remote.h @@ -0,0 +1,11 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * (C) Copyright 2000 + * Murray Jensen <Murray.Jensen@csiro.au> + */ + +extern int remote_desc, remote_timeout; + +extern void remote_reset(void); +extern void remote_continue(void); +extern int remote_write_bytes(unsigned long, char *, int); diff --git a/tools/u-boot-tools/gdb/serial.c b/tools/u-boot-tools/gdb/serial.c new file mode 100644 index 0000000..34ac609 --- /dev/null +++ b/tools/u-boot-tools/gdb/serial.c @@ -0,0 +1,132 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2000 + * Murray Jensen <Murray.Jensen@csiro.au> + */ + +#include <unistd.h> +#include <string.h> +#include <fcntl.h> +#include <sys/time.h> +#include "serial.h" + +#if defined(__sun__) || \ + defined(__OpenBSD__) || \ + defined(__FreeBSD__) || \ + defined(__NetBSD__) || \ + defined(__APPLE__) +static struct termios tios = { BRKINT, 0, B115200|CS8|CREAD, 0, { 0 } }; +#else +static struct termios tios = { BRKINT, 0, B115200|CS8|CREAD, 0, 0 }; +#endif + +static struct speedmap { + char *str; + speed_t val; +} speedmap[] = { + { "50", B50 }, { "75", B75 }, { "110", B110 }, + { "134", B134 }, { "150", B150 }, { "200", B200 }, + { "300", B300 }, { "600", B600 }, { "1200", B1200 }, + { "1800", B1800 }, { "2400", B2400 }, { "4800", B4800 }, + { "9600", B9600 }, { "19200", B19200 }, { "38400", B38400 }, + { "57600", B57600 }, +#ifdef B76800 + { "76800", B76800 }, +#endif + { "115200", B115200 }, +#ifdef B153600 + { "153600", B153600 }, +#endif + { "230400", B230400 }, +#ifdef B307200 + { "307200", B307200 }, +#endif +#ifdef B460800 + { "460800", B460800 } +#endif +}; +static int nspeeds = sizeof speedmap / sizeof speedmap[0]; + +speed_t +cvtspeed(char *str) +{ + struct speedmap *smp = speedmap, *esmp = &speedmap[nspeeds]; + + while (smp < esmp) { + if (strcmp(str, smp->str) == 0) + return (smp->val); + smp++; + } + return B0; +} + +int +serialopen(char *device, speed_t speed) +{ + int fd; + + if (cfsetospeed(&tios, speed) < 0) + return -1; + + if ((fd = open(device, O_RDWR)) < 0) + return -1; + + if (tcsetattr(fd, TCSAFLUSH, &tios) < 0) { + (void)close(fd); + return -1; + } + + return fd; +} + +int +serialreadchar(int fd, int timeout) +{ + fd_set fds; + struct timeval tv; + int n; + char ch; + + tv.tv_sec = timeout; + tv.tv_usec = 0; + + FD_ZERO(&fds); + FD_SET(fd, &fds); + + /* this is a fucking horrible quick hack - fix this */ + + if ((n = select(fd + 1, &fds, 0, 0, &tv)) < 0) + return SERIAL_ERROR; + + if (n == 0) + return SERIAL_TIMEOUT; + + if ((n = read(fd, &ch, 1)) < 0) + return SERIAL_ERROR; + + if (n == 0) + return SERIAL_EOF; + + return ch; +} + +int +serialwrite(int fd, char *buf, int len) +{ + int n; + + do { + n = write(fd, buf, len); + if (n < 0) + return 1; + len -= n; + buf += n; + } while (len > 0); + return 0; +} + +int +serialclose(int fd) +{ + return close(fd); +} diff --git a/tools/u-boot-tools/gdb/serial.h b/tools/u-boot-tools/gdb/serial.h new file mode 100644 index 0000000..c45d1df --- /dev/null +++ b/tools/u-boot-tools/gdb/serial.h @@ -0,0 +1,17 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * (C) Copyright 2000 + * Murray Jensen <Murray.Jensen@csiro.au> + */ + +#include <termios.h> + +#define SERIAL_ERROR -1 /* General error, see errno for details */ +#define SERIAL_TIMEOUT -2 +#define SERIAL_EOF -3 + +extern speed_t cvtspeed(char *); +extern int serialopen(char *, speed_t); +extern int serialreadchar(int, int); +extern int serialwrite(int, char *, int); +extern int serialclose(int); diff --git a/tools/u-boot-tools/gen_eth_addr b/tools/u-boot-tools/gen_eth_addr new file mode 100755 index 0000000000000000000000000000000000000000..393f237668a4015581498bb9efea37d91ae576ee GIT binary patch literal 16800 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 GG5`P#Yn3Me literal 0 HcmV?d00001 diff --git a/tools/u-boot-tools/gen_eth_addr.c b/tools/u-boot-tools/gen_eth_addr.c new file mode 100644 index 0000000..ad36f3f --- /dev/null +++ b/tools/u-boot-tools/gen_eth_addr.c @@ -0,0 +1,33 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2001 + * Murray Jensen <Murray.Jensen@cmst.csiro.au> + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <time.h> + +int +main(int argc, char *argv[]) +{ + unsigned long ethaddr_low, ethaddr_high; + + srand(time(0) + (getpid() << 8)); + + /* + * setting the 2nd LSB in the most significant byte of + * the address makes it a locally administered ethernet + * address + */ + ethaddr_high = (rand() & 0xfeff) | 0x0200; + ethaddr_low = rand(); + + printf("%02lx:%02lx:%02lx:%02lx:%02lx:%02lx\n", + ethaddr_high >> 8, ethaddr_high & 0xff, + ethaddr_low >> 24, (ethaddr_low >> 16) & 0xff, + (ethaddr_low >> 8) & 0xff, ethaddr_low & 0xff); + + return (0); +} diff --git a/tools/u-boot-tools/gen_ethaddr_crc b/tools/u-boot-tools/gen_ethaddr_crc new file mode 100755 index 0000000000000000000000000000000000000000..1e9bb782f63dad5b57a4fa8980a3d40c0581f83a GIT binary patch literal 17064 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 NtswI3Xd2MC3;-l^@@)VB literal 0 HcmV?d00001 diff --git a/tools/u-boot-tools/gen_ethaddr_crc.c b/tools/u-boot-tools/gen_ethaddr_crc.c new file mode 100644 index 0000000..e73d042 --- /dev/null +++ b/tools/u-boot-tools/gen_ethaddr_crc.c @@ -0,0 +1,75 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2016 + * Olliver Schinagl <oliver@schinagl.nl> + */ + +#include <ctype.h> +#include <stdbool.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <u-boot/crc.h> + +#define ARP_HLEN 6 /* Length of hardware address */ +#define ARP_HLEN_ASCII (ARP_HLEN * 2) + (ARP_HLEN - 1) /* with separators */ +#define ARP_HLEN_LAZY (ARP_HLEN * 2) /* separatorless hardware address length */ + +uint8_t nibble_to_hex(const char *nibble, bool lo) +{ + return (strtol(nibble, NULL, 16) << (lo ? 0 : 4)) & (lo ? 0x0f : 0xf0); +} + +int process_mac(const char *mac_address) +{ + uint8_t ethaddr[ARP_HLEN + 1] = { 0x00 }; + uint_fast8_t i = 0; + + while (*mac_address != '\0') { + char nibble[2] = { 0x00, '\n' }; /* for strtol */ + + nibble[0] = *mac_address++; + if (isxdigit(nibble[0])) { + if (isupper(nibble[0])) + nibble[0] = tolower(nibble[0]); + ethaddr[i >> 1] |= nibble_to_hex(nibble, (i % 2) != 0); + i++; + } + } + + for (i = 0; i < ARP_HLEN; i++) + printf("%.2x", ethaddr[i]); + printf("%.2x\n", crc8(0, ethaddr, ARP_HLEN)); + + return 0; +} + +void print_usage(char *cmdname) +{ + printf("Usage: %s <mac_address>\n", cmdname); + puts("<mac_address> may be with or without separators."); + puts("Valid seperators are ':' and '-'."); + puts("<mac_address> digits are in base 16.\n"); +} + +int main(int argc, char *argv[]) +{ + if (argc < 2) { + print_usage(argv[0]); + return 1; + } + + if (!((strlen(argv[1]) == ARP_HLEN_ASCII) || (strlen(argv[1]) == ARP_HLEN_LAZY))) { + puts("The MAC address is not valid.\n"); + print_usage(argv[0]); + return 1; + } + + if (process_mac(argv[1])) { + puts("Failed to calculate the MAC's checksum."); + return 1; + } + + return 0; +} diff --git a/tools/u-boot-tools/gen_ethaddr_crc.o b/tools/u-boot-tools/gen_ethaddr_crc.o new file mode 100644 index 0000000000000000000000000000000000000000..f603e643e52d4e838d000dca8b7847b7c908ec29 GIT binary patch literal 3792 zcmb<-^>JfjWMqH=Mg}_u1P><4z;J;J!FB*M9T)@{_!)wC2{14)bbdDc_5vj8(Rrx( z0l&w=M=UM3qxoG99d-;eJm47W7!n%n(fmfhgMXj=aTgU%kkYhH7Zu)PE-L&CEw}kw z?l3Sgcy|7AHGJ#Qo1?-GHq5j0*aXjRwqq_T99*8wM;JR|RM`2qvqXG5B2eP;?FeHD zKUkSZH$ykej1CtScCcFvzj<`Nhe*I3<M{0;BfrZbm(Ev?%|{t8ylQ?_&*37%>B+xO zMy2I;=`8+jB`Tagy*VnJ9=*CC$N6-=_U%n&d(F(h&Df*!z2O0m=EIC&Lp?3;l)m!m ze7B2@fq}vBKUmD8*A}Fy^O)fQ2*3G=fZ>7YSjQO0SjV`-{PGSAhTmS#2hkqQZ#X<U z|3P)Y)I;<`^kT9-nvZb21X-Y}XH>xeBDferixbmRtrS#?6>M@7lj9RpQi@WGi|ydC zgd(btn^>ukl&VmkS(2fUUj(M}OG^}rQwtJ{5=-)niuFLogK$`4PG$;7A{8Q`kXV$e zpl+qEkeHXEpsuS9Q;2GQN@jXyNikS$W}ZS)VsWa1p_v}YF(DbL3cilc3Qz|sWELyr z<(DXwfvf>3a7)b0Nlj5G$yZ2D%t<cINi0cKD1qovFIGs-NKMWzF3pA6%D@on(fJjW zlsKCIGuD}VbjNXcbl!&qT=NgX5;l+KA3UYPFtOGHrOKeti;g`E4RMIl?pg*gV+Say zzrKqs2N62O;O^{frJ&)Gnv|KCr(mIHtY@HWrU_wy!bt=~FfcGMRs}IIRtPXk^RRPF zU}RvBU|?X7fyya>Xh%MQW~OL94h9AWP`<GMi7_xR@PKS~;uGj&a^aKcWp?6I=wWf> z(`aLL<TGeybK!Gfdd<aW;LNAt$fw}MC*i~=;Kaw_%FO_>w+3nkIMKQA36$`0Fz7Ha zFie4pXMku&K7lrn+0D!@d<wZ-d=id)0*-tfj@%$g+!*967KV@iv8iCjS~4&&Ff%Y? zQ_jH5z=A`Z6^A$|Phm<iGq7U{GJxy_r7%n>W(G`83WJql+6#&=1_o9JbQxKwdUTVN zz~Y$hRELVg;up+iU;xJp0|OgF0!#o(8-i&zh7K@|Ak4r_Hii{o8kJyX02iDng4PTS z3_?ip0#0+FvXp^=0aP-hC}v<_h{qwG1~mt!ADliJ7#NChsBgd_4oZXA?46B6{Q{`H z2B3t;z`y`b(+ms@YjCLF2v(0s58$-Uz`(E#hkAze)V%oAk_=G!6Q5j^te4E7=i_Vu zA`C%<5r{Ab5hftQ6hxST2y=$K%%r58)cBJ8_>9yFhIn@$e<w$ucz-vyVAqiN5Jx8; z*La5FlA@CQ9EO6T{N&W);`rReWQO?o<dVvQ)cB<MoO}eYBtIv=JhcccR#23gSCYn% zT$F6V0A|ORf=Y9Sg3^*=hTO!=Jdm+Dsd=EdgQY)EP7-BcVEFSN0wO??3=H5LCXOUt zk0cIC+c5Prki?PAxeOI&VqjnZ<tCW=w@`7Ay^=`g!<q^paVaEm4ru&=%mKL>W{x0K z9HbuE94n|eNIegdIgU_qboCxkagcgmB=x>fagcgYxeK#51S$?v&xfQw5-N_aJ^?BY zQqPa1J`*aAu3ii&eB_YSYaxk))WF<h0~H6E56T}faYv{)y7^v6;?hXw!{Qxejv$hF zERuTUaDe4!ka`e?Dq}!)4@exQ4wSD!YCvKj49l0W^aBz@<UR%l2EF3S+>*p320c(# z0Mi*TR#9qBqFzaAMG1pmN@7VOgI-c`F@s)FK8OQSZm4I7%C%t7gX_^NE=epZDJ@{o zOU}>DP0cG|(96p&N!4@r3)L+yNlea$s!h#^Pb*5yO$9lc3K$d)peRQ#;X!pb$oO24 zB!~gUptyuG!4xR2Kye9E4;Ex#U|0YRv>8zSpfn6(g4zWjHK4o-qSZic3UJ*9;vw)_ zkYWTE&O#RkwF5wAqSwg8>Ic<JATy2NHb59K{VEVHoCK)_abPsa-yk-+{u>}c1_p); zAjL=+<W>+DD$NiA^*?&}ZvZ7c1_lOLc@NS9!sy}e4=ocx?f}Wdum!a0gw?B{@CQW+ z%zjw-CqeBufN6x%63_sJv<skuP_-Z)%>CI&VjyuCHihbk@nJMfAB+#me;_facVWT| z7Et{M;35zP%>57^14A_q|385GAJz^5`5#nH!}P=akIin7**~E6zkoUnRHlO3ROtFa r)<Bhlw88NMXoT*Ci$EA4`yea`2`baD>30BCP7DkTT@Yn(5?wz4WrkYG literal 0 HcmV?d00001 diff --git a/tools/u-boot-tools/genboardscfg.py b/tools/u-boot-tools/genboardscfg.py new file mode 100755 index 0000000..e9bbd15 --- /dev/null +++ b/tools/u-boot-tools/genboardscfg.py @@ -0,0 +1,447 @@ +#!/usr/bin/env python2 +# SPDX-License-Identifier: GPL-2.0+ +# +# Author: Masahiro Yamada <yamada.m@jp.panasonic.com> +# + +""" +Converter from Kconfig and MAINTAINERS to a board database. + +Run 'tools/genboardscfg.py' to create a board database. + +Run 'tools/genboardscfg.py -h' for available options. + +Python 2.6 or later, but not Python 3.x is necessary to run this script. +""" + +import errno +import fnmatch +import glob +import multiprocessing +import optparse +import os +import sys +import tempfile +import time + +sys.path.insert(1, os.path.join(os.path.dirname(__file__), 'buildman')) +import kconfiglib + +### constant variables ### +OUTPUT_FILE = 'boards.cfg' +CONFIG_DIR = 'configs' +SLEEP_TIME = 0.03 +COMMENT_BLOCK = '''# +# List of boards +# Automatically generated by %s: don't edit +# +# Status, Arch, CPU, SoC, Vendor, Board, Target, Options, Maintainers + +''' % __file__ + +### helper functions ### +def try_remove(f): + """Remove a file ignoring 'No such file or directory' error.""" + try: + os.remove(f) + except OSError as exception: + # Ignore 'No such file or directory' error + if exception.errno != errno.ENOENT: + raise + +def check_top_directory(): + """Exit if we are not at the top of source directory.""" + for f in ('README', 'Licenses'): + if not os.path.exists(f): + sys.exit('Please run at the top of source directory.') + +def output_is_new(output): + """Check if the output file is up to date. + + Returns: + True if the given output file exists and is newer than any of + *_defconfig, MAINTAINERS and Kconfig*. False otherwise. + """ + try: + ctime = os.path.getctime(output) + except OSError as exception: + if exception.errno == errno.ENOENT: + # return False on 'No such file or directory' error + return False + else: + raise + + for (dirpath, dirnames, filenames) in os.walk(CONFIG_DIR): + for filename in fnmatch.filter(filenames, '*_defconfig'): + if fnmatch.fnmatch(filename, '.*'): + continue + filepath = os.path.join(dirpath, filename) + if ctime < os.path.getctime(filepath): + return False + + for (dirpath, dirnames, filenames) in os.walk('.'): + for filename in filenames: + if (fnmatch.fnmatch(filename, '*~') or + not fnmatch.fnmatch(filename, 'Kconfig*') and + not filename == 'MAINTAINERS'): + continue + filepath = os.path.join(dirpath, filename) + if ctime < os.path.getctime(filepath): + return False + + # Detect a board that has been removed since the current board database + # was generated + with open(output) as f: + for line in f: + if line[0] == '#' or line == '\n': + continue + defconfig = line.split()[6] + '_defconfig' + if not os.path.exists(os.path.join(CONFIG_DIR, defconfig)): + return False + + return True + +### classes ### +class KconfigScanner: + + """Kconfig scanner.""" + + ### constant variable only used in this class ### + _SYMBOL_TABLE = { + 'arch' : 'SYS_ARCH', + 'cpu' : 'SYS_CPU', + 'soc' : 'SYS_SOC', + 'vendor' : 'SYS_VENDOR', + 'board' : 'SYS_BOARD', + 'config' : 'SYS_CONFIG_NAME', + 'options' : 'SYS_EXTRA_OPTIONS' + } + + def __init__(self): + """Scan all the Kconfig files and create a Config object.""" + # Define environment variables referenced from Kconfig + os.environ['srctree'] = os.getcwd() + os.environ['UBOOTVERSION'] = 'dummy' + os.environ['KCONFIG_OBJDIR'] = '' + self._conf = kconfiglib.Config(print_warnings=False) + + def __del__(self): + """Delete a leftover temporary file before exit. + + The scan() method of this class creates a temporay file and deletes + it on success. If scan() method throws an exception on the way, + the temporary file might be left over. In that case, it should be + deleted in this destructor. + """ + if hasattr(self, '_tmpfile') and self._tmpfile: + try_remove(self._tmpfile) + + def scan(self, defconfig): + """Load a defconfig file to obtain board parameters. + + Arguments: + defconfig: path to the defconfig file to be processed + + Returns: + A dictionary of board parameters. It has a form of: + { + 'arch': <arch_name>, + 'cpu': <cpu_name>, + 'soc': <soc_name>, + 'vendor': <vendor_name>, + 'board': <board_name>, + 'target': <target_name>, + 'config': <config_header_name>, + 'options': <extra_options> + } + """ + # strip special prefixes and save it in a temporary file + fd, self._tmpfile = tempfile.mkstemp() + with os.fdopen(fd, 'w') as f: + for line in open(defconfig): + colon = line.find(':CONFIG_') + if colon == -1: + f.write(line) + else: + f.write(line[colon + 1:]) + + warnings = self._conf.load_config(self._tmpfile) + if warnings: + for warning in warnings: + print '%s: %s' % (defconfig, warning) + + try_remove(self._tmpfile) + self._tmpfile = None + + params = {} + + # Get the value of CONFIG_SYS_ARCH, CONFIG_SYS_CPU, ... etc. + # Set '-' if the value is empty. + for key, symbol in self._SYMBOL_TABLE.items(): + value = self._conf.get_symbol(symbol).get_value() + if value: + params[key] = value + else: + params[key] = '-' + + defconfig = os.path.basename(defconfig) + params['target'], match, rear = defconfig.partition('_defconfig') + assert match and not rear, '%s : invalid defconfig' % defconfig + + # fix-up for aarch64 + if params['arch'] == 'arm' and params['cpu'] == 'armv8': + params['arch'] = 'aarch64' + + # fix-up options field. It should have the form: + # <config name>[:comma separated config options] + if params['options'] != '-': + params['options'] = params['config'] + ':' + \ + params['options'].replace(r'\"', '"') + elif params['config'] != params['target']: + params['options'] = params['config'] + + return params + +def scan_defconfigs_for_multiprocess(queue, defconfigs): + """Scan defconfig files and queue their board parameters + + This function is intended to be passed to + multiprocessing.Process() constructor. + + Arguments: + queue: An instance of multiprocessing.Queue(). + The resulting board parameters are written into it. + defconfigs: A sequence of defconfig files to be scanned. + """ + kconf_scanner = KconfigScanner() + for defconfig in defconfigs: + queue.put(kconf_scanner.scan(defconfig)) + +def read_queues(queues, params_list): + """Read the queues and append the data to the paramers list""" + for q in queues: + while not q.empty(): + params_list.append(q.get()) + +def scan_defconfigs(jobs=1): + """Collect board parameters for all defconfig files. + + This function invokes multiple processes for faster processing. + + Arguments: + jobs: The number of jobs to run simultaneously + """ + all_defconfigs = [] + for (dirpath, dirnames, filenames) in os.walk(CONFIG_DIR): + for filename in fnmatch.filter(filenames, '*_defconfig'): + if fnmatch.fnmatch(filename, '.*'): + continue + all_defconfigs.append(os.path.join(dirpath, filename)) + + total_boards = len(all_defconfigs) + processes = [] + queues = [] + for i in range(jobs): + defconfigs = all_defconfigs[total_boards * i / jobs : + total_boards * (i + 1) / jobs] + q = multiprocessing.Queue(maxsize=-1) + p = multiprocessing.Process(target=scan_defconfigs_for_multiprocess, + args=(q, defconfigs)) + p.start() + processes.append(p) + queues.append(q) + + # The resulting data should be accumulated to this list + params_list = [] + + # Data in the queues should be retrieved preriodically. + # Otherwise, the queues would become full and subprocesses would get stuck. + while any([p.is_alive() for p in processes]): + read_queues(queues, params_list) + # sleep for a while until the queues are filled + time.sleep(SLEEP_TIME) + + # Joining subprocesses just in case + # (All subprocesses should already have been finished) + for p in processes: + p.join() + + # retrieve leftover data + read_queues(queues, params_list) + + return params_list + +class MaintainersDatabase: + + """The database of board status and maintainers.""" + + def __init__(self): + """Create an empty database.""" + self.database = {} + + def get_status(self, target): + """Return the status of the given board. + + The board status is generally either 'Active' or 'Orphan'. + Display a warning message and return '-' if status information + is not found. + + Returns: + 'Active', 'Orphan' or '-'. + """ + if not target in self.database: + print >> sys.stderr, "WARNING: no status info for '%s'" % target + return '-' + + tmp = self.database[target][0] + if tmp.startswith('Maintained'): + return 'Active' + elif tmp.startswith('Supported'): + return 'Active' + elif tmp.startswith('Orphan'): + return 'Orphan' + else: + print >> sys.stderr, ("WARNING: %s: unknown status for '%s'" % + (tmp, target)) + return '-' + + def get_maintainers(self, target): + """Return the maintainers of the given board. + + Returns: + Maintainers of the board. If the board has two or more maintainers, + they are separated with colons. + """ + if not target in self.database: + print >> sys.stderr, "WARNING: no maintainers for '%s'" % target + return '' + + return ':'.join(self.database[target][1]) + + def parse_file(self, file): + """Parse a MAINTAINERS file. + + Parse a MAINTAINERS file and accumulates board status and + maintainers information. + + Arguments: + file: MAINTAINERS file to be parsed + """ + targets = [] + maintainers = [] + status = '-' + for line in open(file): + # Check also commented maintainers + if line[:3] == '#M:': + line = line[1:] + tag, rest = line[:2], line[2:].strip() + if tag == 'M:': + maintainers.append(rest) + elif tag == 'F:': + # expand wildcard and filter by 'configs/*_defconfig' + for f in glob.glob(rest): + front, match, rear = f.partition('configs/') + if not front and match: + front, match, rear = rear.rpartition('_defconfig') + if match and not rear: + targets.append(front) + elif tag == 'S:': + status = rest + elif line == '\n': + for target in targets: + self.database[target] = (status, maintainers) + targets = [] + maintainers = [] + status = '-' + if targets: + for target in targets: + self.database[target] = (status, maintainers) + +def insert_maintainers_info(params_list): + """Add Status and Maintainers information to the board parameters list. + + Arguments: + params_list: A list of the board parameters + """ + database = MaintainersDatabase() + for (dirpath, dirnames, filenames) in os.walk('.'): + if 'MAINTAINERS' in filenames: + database.parse_file(os.path.join(dirpath, 'MAINTAINERS')) + + for i, params in enumerate(params_list): + target = params['target'] + params['status'] = database.get_status(target) + params['maintainers'] = database.get_maintainers(target) + params_list[i] = params + +def format_and_output(params_list, output): + """Write board parameters into a file. + + Columnate the board parameters, sort lines alphabetically, + and then write them to a file. + + Arguments: + params_list: The list of board parameters + output: The path to the output file + """ + FIELDS = ('status', 'arch', 'cpu', 'soc', 'vendor', 'board', 'target', + 'options', 'maintainers') + + # First, decide the width of each column + max_length = dict([ (f, 0) for f in FIELDS]) + for params in params_list: + for f in FIELDS: + max_length[f] = max(max_length[f], len(params[f])) + + output_lines = [] + for params in params_list: + line = '' + for f in FIELDS: + # insert two spaces between fields like column -t would + line += ' ' + params[f].ljust(max_length[f]) + output_lines.append(line.strip()) + + # ignore case when sorting + output_lines.sort(key=str.lower) + + with open(output, 'w') as f: + f.write(COMMENT_BLOCK + '\n'.join(output_lines) + '\n') + +def gen_boards_cfg(output, jobs=1, force=False): + """Generate a board database file. + + Arguments: + output: The name of the output file + jobs: The number of jobs to run simultaneously + force: Force to generate the output even if it is new + """ + check_top_directory() + + if not force and output_is_new(output): + print "%s is up to date. Nothing to do." % output + sys.exit(0) + + params_list = scan_defconfigs(jobs) + insert_maintainers_info(params_list) + format_and_output(params_list, output) + +def main(): + try: + cpu_count = multiprocessing.cpu_count() + except NotImplementedError: + cpu_count = 1 + + parser = optparse.OptionParser() + # Add options here + parser.add_option('-f', '--force', action="store_true", default=False, + help='regenerate the output even if it is new') + parser.add_option('-j', '--jobs', type='int', default=cpu_count, + help='the number of jobs to run simultaneously') + parser.add_option('-o', '--output', default=OUTPUT_FILE, + help='output file [default=%s]' % OUTPUT_FILE) + (options, args) = parser.parse_args() + + gen_boards_cfg(options.output, jobs=options.jobs, force=options.force) + +if __name__ == '__main__': + main() diff --git a/tools/u-boot-tools/getline.c b/tools/u-boot-tools/getline.c new file mode 100644 index 0000000..64f1260 --- /dev/null +++ b/tools/u-boot-tools/getline.c @@ -0,0 +1,89 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* getline.c -- Replacement for GNU C library function getline + * + * Copyright (C) 1993, 1996, 2001, 2002 Free Software Foundation, Inc. + */ + +/* Written by Jan Brittenson, bson@gnu.ai.mit.edu. */ + +#include <assert.h> +#include <stdio.h> + +/* Always add at least this many bytes when extending the buffer. */ +#define MIN_CHUNK 64 + +/* Read up to (and including) a TERMINATOR from STREAM into *LINEPTR + + OFFSET (and null-terminate it). *LINEPTR is a pointer returned from + malloc (or NULL), pointing to *N characters of space. It is realloc'd + as necessary. Return the number of characters read (not including the + null terminator), or -1 on error or EOF. + NOTE: There is another getstr() function declared in <curses.h>. */ +static int getstr(char **lineptr, size_t *n, FILE *stream, + char terminator, size_t offset) +{ + int nchars_avail; /* Allocated but unused chars in *LINEPTR. */ + char *read_pos; /* Where we're reading into *LINEPTR. */ + int ret; + + if (!lineptr || !n || !stream) + return -1; + + if (!*lineptr) { + *n = MIN_CHUNK; + *lineptr = malloc(*n); + if (!*lineptr) + return -1; + } + + nchars_avail = *n - offset; + read_pos = *lineptr + offset; + + for (;;) { + register int c = getc(stream); + + /* We always want at least one char left in the buffer, since we + always (unless we get an error while reading the first char) + NUL-terminate the line buffer. */ + + assert(*n - nchars_avail == read_pos - *lineptr); + if (nchars_avail < 2) { + if (*n > MIN_CHUNK) + *n *= 2; + else + *n += MIN_CHUNK; + + nchars_avail = *n + *lineptr - read_pos; + *lineptr = realloc(*lineptr, *n); + if (!*lineptr) + return -1; + read_pos = *n - nchars_avail + *lineptr; + assert(*n - nchars_avail == read_pos - *lineptr); + } + + if (c == EOF || ferror (stream)) { + /* Return partial line, if any. */ + if (read_pos == *lineptr) + return -1; + else + break; + } + + *read_pos++ = c; + nchars_avail--; + + if (c == terminator) + /* Return the line. */ + break; + } + + /* Done - NUL terminate and return the number of chars read. */ + *read_pos = '\0'; + + ret = read_pos - (*lineptr + offset); + return ret; +} + +int getline (char **lineptr, size_t *n, FILE *stream) +{ + return getstr(lineptr, n, stream, '\n', 0); +} diff --git a/tools/u-boot-tools/getline.h b/tools/u-boot-tools/getline.h new file mode 100644 index 0000000..a2f35b9 --- /dev/null +++ b/tools/u-boot-tools/getline.h @@ -0,0 +1 @@ +int getline(char **lineptr, size_t *n, FILE *stream); diff --git a/tools/u-boot-tools/gpheader.h b/tools/u-boot-tools/gpheader.h new file mode 100644 index 0000000..d5bf86e --- /dev/null +++ b/tools/u-boot-tools/gpheader.h @@ -0,0 +1,39 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * (C) Copyright 2014 + * Texas Instruments Incorporated + * Refactored common functions in to gpimage-common.c. Include this common + * header file + * + * (C) Copyright 2010 + * Linaro LTD, www.linaro.org + * Author: John Rigby <john.rigby@linaro.org> + * Based on TI's signGP.c + * + * (C) Copyright 2009 + * Stefano Babic, DENX Software Engineering, sbabic@denx.de. + * + * (C) Copyright 2008 + * Marvell Semiconductor <www.marvell.com> + * Written-by: Prafulla Wadaskar <prafulla@marvell.com> + */ + +#ifndef _GPIMAGE_H_ +#define _GPIMAGE_H_ + +/* common headers for gpimage and omapimage formats */ +struct gp_header { + uint32_t size; + uint32_t load_addr; +}; +#define GPIMAGE_HDR_SIZE (sizeof(struct gp_header)) + +/* common functions across gpimage and omapimage handlers */ +int valid_gph_size(uint32_t size); +int valid_gph_load_addr(uint32_t load_addr); +int gph_verify_header(struct gp_header *gph, int be); +void gph_print_header(const struct gp_header *gph, int be); +void gph_set_header(struct gp_header *gph, uint32_t size, uint32_t load_addr, + int be); +int gpimage_check_params(struct image_tool_params *params); +#endif diff --git a/tools/u-boot-tools/gpimage-common.c b/tools/u-boot-tools/gpimage-common.c new file mode 100644 index 0000000..fc6406b --- /dev/null +++ b/tools/u-boot-tools/gpimage-common.c @@ -0,0 +1,80 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2014 + * Texas Instruments Incorporated + * Refactored common functions in to gpimage-common.c. + * + * (C) Copyright 2010 + * Linaro LTD, www.linaro.org + * Author: John Rigby <john.rigby@linaro.org> + * Based on TI's signGP.c + * + * (C) Copyright 2009 + * Stefano Babic, DENX Software Engineering, sbabic@denx.de. + * + * (C) Copyright 2008 + * Marvell Semiconductor <www.marvell.com> + * Written-by: Prafulla Wadaskar <prafulla@marvell.com> + */ + +#include "imagetool.h" +#include <compiler.h> +#include <image.h> +#include "gpheader.h" + +/* Helper to convert size and load_addr to big endian */ +void to_be32(uint32_t *gph_size, uint32_t *gph_load_addr) +{ + *gph_size = cpu_to_be32(*gph_size); + *gph_load_addr = cpu_to_be32(*gph_load_addr); +} + +int gph_verify_header(struct gp_header *gph, int be) +{ + uint32_t gph_size = gph->size; + uint32_t gph_load_addr = gph->load_addr; + + if (be) + to_be32(&gph_size, &gph_load_addr); + + if (!gph_size || !gph_load_addr) + return -1; + + return 0; +} + +void gph_print_header(const struct gp_header *gph, int be) +{ + uint32_t gph_size = gph->size, gph_load_addr = gph->load_addr; + + if (be) + to_be32(&gph_size, &gph_load_addr); + + if (!gph_size) { + fprintf(stderr, "Error: invalid image size %x\n", gph_size); + exit(EXIT_FAILURE); + } + + if (!gph_load_addr) { + fprintf(stderr, "Error: invalid image load address %x\n", + gph_load_addr); + exit(EXIT_FAILURE); + } + printf("GP Header: Size %x LoadAddr %x\n", gph_size, gph_load_addr); +} + +void gph_set_header(struct gp_header *gph, uint32_t size, uint32_t load_addr, + int be) +{ + gph->size = size; + gph->load_addr = load_addr; + if (be) + to_be32(&gph->size, &gph->load_addr); +} + +int gpimage_check_params(struct image_tool_params *params) +{ + return (params->dflag && (params->fflag || params->lflag)) || + (params->fflag && (params->dflag || params->lflag)) || + (params->lflag && (params->dflag || params->fflag)); +} diff --git a/tools/u-boot-tools/gpimage-common.o b/tools/u-boot-tools/gpimage-common.o new file mode 100644 index 0000000000000000000000000000000000000000..709b0fbe1f4e5f6fb287dd4cabc1320c14fef0f8 GIT binary patch literal 2824 zcmb<-^>JfjWMqH=Mg}_u1P><4z|g^tU^{@B4h(z@ybRs!{3kluyV>|pbg~`hmuK*3 zek0J$-W|@;>7v5X8KT0|`mI#D^+1V!cZdp);epmm{8J8mzv0n*MBp&LybA+^M{kJ= zk4JBf3dakOsogFr9Ir3%%QNiw|NsC0*ViX_G{2ERH&46uKuI*nyw*!4CY{GTdTl{k z4G(}#@Mu0FaX3wnU%mw-h}HZ99^GIa9=)a@HBgQFLEeTK0g*Pm1XTbQL`bJ0Oi!E8 zY2F#m(t5I#{csx8Kjkbhpa1*+pI^QKWOz4!w>wAc$rAb210~!$KvqEf-5o9f4vnb? z;QZDDrHqGRg04kH`9)R=nR#W2IhiR6nYoGSsS3rJRjCT96<iDq3|QrJ@)J`O5>rx& zQj3eB>f8eqJW>-=Qj4q<f+4yUeDV`h98*$?AS&FQovjo!TvC%V6Y~@-^o;ckbj>s& z43PUpKm-E=17lSX17n2%qcjgY#{@<O1_1^J1{tUvFO-&GU|=wSvi(7{3!g+97axZk zHv<Dm&IcsMz`!sWM7!`Q6ms!#cymL{$^eNmFfd3$X^_1&P__k>#z%odmWAQte{5=) zv8HGS24)6kY|0s!8CY<Lv*HkE!y(RrL!1*sTnH?}#-IVF5rjCHiD^D0>~XmVDYJt0 zGcYJJFfa%qg##?kG@;_4_yj2h#~T9!gEdq<0VD{EGX@3*P#nV4LZ!e|76T+6!5kza z35R$l4)H=9;$=|xz`OuUC#_I%1yBNDU|@i!mGlB|p3zOt&&|!x(@SR1^Kmu;5r!bb zfT1KmJ}K4Mh#|coBfc!PC^M}xJ_DRlz#;`jnRz8Jfp~Wxe<w$ucz-vyVAqiN5Jx8; z*La5Fl9be<B8D`u(lmzDip&zQ*5XvSMyMO&lQUA2v*QaAixP8-p<&|<r9mkT6c>L$ z<=y}P|J$JAObiSRpi}}A?}dtk)C)2&Fu=rDBZ(uMvjZv)G6$5lVCwfHiG#`!nD{ZM zILLfqB=avIiG%VaO#OAJIJ)@{ki@x>%y|kGM>qdHk~p&Y3Q%#7`5+ACGMGRaATbbz zrNsa=aag*|K@*3iA8_H!z`y`91B7Aehe5BnGPfi#i9xTpqzFQ1z*t48If;5DsTCy* zdMSw|i41y4#l;MIMfo5ONV%b&Au88`K@XJtQu9g}^z!mcQuW;ZLUoHv5|gtT^uUIt zX2hozCFZ6=9Z3NN3NKI=KrbmEbu|M+1V|Fed|0@_grpf5z@;8cJxsg;TExJ@2Nb6u zqhKW)s9XoBRRh%#44^U<#6#d2AjJqSoP{o|!@$4*G84T-CRV>8)P5tl4G;!QzZ`@M zCt>!(Xpp}_Y;^tLDw~0U!2_-n!T`Ay!~*F7(V%h@#D>*>ATcloB`yXA23ar%L4eeP zcrZEw>VA+IGM0tvM-~IIVfsL95YC0_M^~-@6_@~0goI)4hY5k|Ur^ozg#%0sM9+XG zs0t8=fq?-O|DbXSrXLpnAooD6f~tg28=!(3P@0E<fdN!bq3Z{ki5~YL6K;SU#lXNY d3&cUfAp4NG44^U*n|=XkRalO!7#dc{LI6lyeP93p literal 0 HcmV?d00001 diff --git a/tools/u-boot-tools/gpimage.c b/tools/u-boot-tools/gpimage.c new file mode 100644 index 0000000..27de4cf --- /dev/null +++ b/tools/u-boot-tools/gpimage.c @@ -0,0 +1,75 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2014 + * Texas Instruments Incorporated + * Add gpimage format for keystone devices to format spl image. This is + * Based on omapimage.c + * + * (C) Copyright 2010 + * Linaro LTD, www.linaro.org + * Author: John Rigby <john.rigby@linaro.org> + * Based on TI's signGP.c + * + * (C) Copyright 2009 + * Stefano Babic, DENX Software Engineering, sbabic@denx.de. + * + * (C) Copyright 2008 + * Marvell Semiconductor <www.marvell.com> + * Written-by: Prafulla Wadaskar <prafulla@marvell.com> + */ + +#include "imagetool.h" +#include <compiler.h> +#include <image.h> +#include "gpheader.h" + +static uint8_t gpimage_header[GPIMAGE_HDR_SIZE]; + +/* to be in keystone gpimage */ +static int gpimage_check_image_types(uint8_t type) +{ + if (type == IH_TYPE_GPIMAGE) + return EXIT_SUCCESS; + return EXIT_FAILURE; +} + +static int gpimage_verify_header(unsigned char *ptr, int image_size, + struct image_tool_params *params) +{ + struct gp_header *gph = (struct gp_header *)ptr; + + return gph_verify_header(gph, 1); +} + +static void gpimage_print_header(const void *ptr) +{ + const struct gp_header *gph = (struct gp_header *)ptr; + + gph_print_header(gph, 1); +} + +static void gpimage_set_header(void *ptr, struct stat *sbuf, int ifd, + struct image_tool_params *params) +{ + struct gp_header *gph = (struct gp_header *)ptr; + + gph_set_header(gph, sbuf->st_size - GPIMAGE_HDR_SIZE, params->addr, 1); +} + +/* + * gpimage parameters + */ +U_BOOT_IMAGE_TYPE( + gpimage, + "TI KeyStone GP Image support", + GPIMAGE_HDR_SIZE, + (void *)&gpimage_header, + gpimage_check_params, + gpimage_verify_header, + gpimage_print_header, + gpimage_set_header, + NULL, + gpimage_check_image_types, + NULL, + NULL +); diff --git a/tools/u-boot-tools/gpimage.o b/tools/u-boot-tools/gpimage.o new file mode 100644 index 0000000000000000000000000000000000000000..6fd9defc342ddfda4e0869fa0ee257cd31f35db7 GIT binary patch literal 2808 zcmb<-^>JfjWMqH=Mg}_u1P><4z_5c2!FB*M9T)@|1Q-ktI5hkh<ez%ra2mh73j>2k zcbP$Vpu<j(8O`rFUV@aSrRnj@w}4m-6ZU~5AfjOP2tHUIPK0<Wc&Andm*nTAD!2zI zc;+Uirz#Ye78K+cmB1AtF*uOeRAIV1J6kDexTGd!Cgv$v=o#x7=$dIl7$EaSKm-E= z17lSX17n2%qcjgY#{@<O1_1^J1{tUv7nBBx89>=EeIPLxs2H-k2sAM$7lQ%?BMZaF z{}>Vs3=GUzV-O_Hj7>QMGXo2TI5z_W0~3-tFgL0~#X;@|X@dF30xAykCrrIBR2-6K z7#LvUAic~CtRM;nCxgNiNgNihRbX)#kAVReP9V+94D28Z12Z!q)Walu!R8?Ri>!i~ z0px3#N(9Z!z=Pl*v6xYO1Cf}+07(N79)jG2L;N5P@k=<wpWqN@NH53)<q^GP7%M(G zBQ-fY9xNJPQdy8%43{ZREs4)aO-xBGf(sQCW#(ZBmZcVDrd7g}B20=eC@G4ETF8JT z3=@LVFpcr<KK@RQKJor;Zo#f0@ga^*KCbZ$=>-|_C=SVpM{`6*JgNg=#zH(+kXV$M zTMP?f1t<d)v%(Av41fMZfEkiFD6PW8-H^mZk<>$l!6}~?#zCS%`3^ZX!14h|9Ar04 zsRWb(5*LHAK@`lLAaN!J1_oHZ&;Ut-(<Uh2F)+aL5sZeZhuH_y2NQ>CXMmXxG8=?p z;wDfANDPEw=6Ilq!|aPe6NlNypjTX(TauW>pjTW{1fervtfJJMM7@&KiV_CBl*E!m z2EC->Vg@~sC?bt8=oRIIBthB@^$a1>VC5i%40_4=xw)x%B@B9b`6a1(?tY=V#U+W! z*-&+<8S!aFiMgpz$5B9m!U+_1NYxtHG?+iVz&r#23on?6Gy?;;)P$*riJyQb0$8~6 zLE{uw&cM=z8dMy;MDBtaghqqhiY%nVzyL1Y(DhG%DuUSu<$~41*dQ8|FF<CJtKSfZ z{{^5$I?SIS|AT~J?tzIrLj7+9atsol0cFcVX&41^D~JuFL!tW7-G2b2hyk^J011KO z8$`o!01o$SfE>%fz_0?U5afQ4+d&v41ap5p)P7ig0m*@|1k{0q+@Ar}57L8-O`-ac z#XxLy;aaGEP+kVf!>|QZA;^4~7>I_sAH)XX3LO5w0QG+YND%`A1IYiN@(!jS=6{ei z=yrp&EC5L`FfeF9CHWZ`7=)l{6(kSS&k1g)fJ9L73221=L=^^?$H+p0IP@DpGjuz$ KVrW<)3jqLcRO^KR literal 0 HcmV?d00001 diff --git a/tools/u-boot-tools/ifdtool.c b/tools/u-boot-tools/ifdtool.c new file mode 100644 index 0000000..3a39b7b --- /dev/null +++ b/tools/u-boot-tools/ifdtool.c @@ -0,0 +1,1109 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * ifdtool - Manage Intel Firmware Descriptor information + * + * Copyright 2014 Google, Inc + * + * From Coreboot project, but it got a serious code clean-up + * and a few new features + */ + +#include <assert.h> +#include <fcntl.h> +#include <getopt.h> +#include <stdbool.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <linux/libfdt.h> +#include "ifdtool.h" + +#undef DEBUG + +#ifdef DEBUG +#define debug(fmt, args...) printf(fmt, ##args) +#else +#define debug(fmt, args...) +#endif + +#define FD_SIGNATURE 0x0FF0A55A +#define FLREG_BASE(reg) ((reg & 0x00000fff) << 12); +#define FLREG_LIMIT(reg) (((reg & 0x0fff0000) >> 4) | 0xfff); + +struct input_file { + char *fname; + unsigned int addr; +}; + +/** + * find_fd() - Find the flash description in the ROM image + * + * @image: Pointer to image + * @size: Size of image in bytes + * @return pointer to structure, or NULL if not found + */ +static struct fdbar_t *find_fd(char *image, int size) +{ + uint32_t *ptr, *end; + + /* Scan for FD signature */ + for (ptr = (uint32_t *)image, end = ptr + size / 4; ptr < end; ptr++) { + if (*ptr == FD_SIGNATURE) + break; + } + + if (ptr == end) { + printf("No Flash Descriptor found in this image\n"); + return NULL; + } + + debug("Found Flash Descriptor signature at 0x%08lx\n", + (char *)ptr - image); + + return (struct fdbar_t *)ptr; +} + +/** + * get_region() - Get information about the selected region + * + * @frba: Flash region list + * @region_type: Type of region (0..MAX_REGIONS-1) + * @region: Region information is written here + * @return 0 if OK, else -ve + */ +static int get_region(struct frba_t *frba, int region_type, + struct region_t *region) +{ + if (region_type >= MAX_REGIONS) { + fprintf(stderr, "Invalid region type.\n"); + return -1; + } + + region->base = FLREG_BASE(frba->flreg[region_type]); + region->limit = FLREG_LIMIT(frba->flreg[region_type]); + region->size = region->limit - region->base + 1; + + return 0; +} + +static const char *region_name(int region_type) +{ + static const char *const regions[] = { + "Flash Descriptor", + "BIOS", + "Intel ME", + "GbE", + "Platform Data" + }; + + assert(region_type < MAX_REGIONS); + + return regions[region_type]; +} + +static const char *region_filename(int region_type) +{ + static const char *const region_filenames[] = { + "flashregion_0_flashdescriptor.bin", + "flashregion_1_bios.bin", + "flashregion_2_intel_me.bin", + "flashregion_3_gbe.bin", + "flashregion_4_platform_data.bin" + }; + + assert(region_type < MAX_REGIONS); + + return region_filenames[region_type]; +} + +static int dump_region(int num, struct frba_t *frba) +{ + struct region_t region; + int ret; + + ret = get_region(frba, num, ®ion); + if (ret) + return ret; + + printf(" Flash Region %d (%s): %08x - %08x %s\n", + num, region_name(num), region.base, region.limit, + region.size < 1 ? "(unused)" : ""); + + return ret; +} + +static void dump_frba(struct frba_t *frba) +{ + int i; + + printf("Found Region Section\n"); + for (i = 0; i < MAX_REGIONS; i++) { + printf("FLREG%d: 0x%08x\n", i, frba->flreg[i]); + dump_region(i, frba); + } +} + +static void decode_spi_frequency(unsigned int freq) +{ + switch (freq) { + case SPI_FREQUENCY_20MHZ: + printf("20MHz"); + break; + case SPI_FREQUENCY_33MHZ: + printf("33MHz"); + break; + case SPI_FREQUENCY_50MHZ: + printf("50MHz"); + break; + default: + printf("unknown<%x>MHz", freq); + } +} + +static void decode_component_density(unsigned int density) +{ + switch (density) { + case COMPONENT_DENSITY_512KB: + printf("512KiB"); + break; + case COMPONENT_DENSITY_1MB: + printf("1MiB"); + break; + case COMPONENT_DENSITY_2MB: + printf("2MiB"); + break; + case COMPONENT_DENSITY_4MB: + printf("4MiB"); + break; + case COMPONENT_DENSITY_8MB: + printf("8MiB"); + break; + case COMPONENT_DENSITY_16MB: + printf("16MiB"); + break; + default: + printf("unknown<%x>MiB", density); + } +} + +static void dump_fcba(struct fcba_t *fcba) +{ + printf("\nFound Component Section\n"); + printf("FLCOMP 0x%08x\n", fcba->flcomp); + printf(" Dual Output Fast Read Support: %ssupported\n", + (fcba->flcomp & (1 << 30)) ? "" : "not "); + printf(" Read ID/Read Status Clock Frequency: "); + decode_spi_frequency((fcba->flcomp >> 27) & 7); + printf("\n Write/Erase Clock Frequency: "); + decode_spi_frequency((fcba->flcomp >> 24) & 7); + printf("\n Fast Read Clock Frequency: "); + decode_spi_frequency((fcba->flcomp >> 21) & 7); + printf("\n Fast Read Support: %ssupported", + (fcba->flcomp & (1 << 20)) ? "" : "not "); + printf("\n Read Clock Frequency: "); + decode_spi_frequency((fcba->flcomp >> 17) & 7); + printf("\n Component 2 Density: "); + decode_component_density((fcba->flcomp >> 3) & 7); + printf("\n Component 1 Density: "); + decode_component_density(fcba->flcomp & 7); + printf("\n"); + printf("FLILL 0x%08x\n", fcba->flill); + printf(" Invalid Instruction 3: 0x%02x\n", + (fcba->flill >> 24) & 0xff); + printf(" Invalid Instruction 2: 0x%02x\n", + (fcba->flill >> 16) & 0xff); + printf(" Invalid Instruction 1: 0x%02x\n", + (fcba->flill >> 8) & 0xff); + printf(" Invalid Instruction 0: 0x%02x\n", + fcba->flill & 0xff); + printf("FLPB 0x%08x\n", fcba->flpb); + printf(" Flash Partition Boundary Address: 0x%06x\n\n", + (fcba->flpb & 0xfff) << 12); +} + +static void dump_fpsba(struct fpsba_t *fpsba) +{ + int i; + + printf("Found PCH Strap Section\n"); + for (i = 0; i < MAX_STRAPS; i++) + printf("PCHSTRP%-2d: 0x%08x\n", i, fpsba->pchstrp[i]); +} + +static const char *get_enabled(int flag) +{ + return flag ? "enabled" : "disabled"; +} + +static void decode_flmstr(uint32_t flmstr) +{ + printf(" Platform Data Region Write Access: %s\n", + get_enabled(flmstr & (1 << 28))); + printf(" GbE Region Write Access: %s\n", + get_enabled(flmstr & (1 << 27))); + printf(" Intel ME Region Write Access: %s\n", + get_enabled(flmstr & (1 << 26))); + printf(" Host CPU/BIOS Region Write Access: %s\n", + get_enabled(flmstr & (1 << 25))); + printf(" Flash Descriptor Write Access: %s\n", + get_enabled(flmstr & (1 << 24))); + + printf(" Platform Data Region Read Access: %s\n", + get_enabled(flmstr & (1 << 20))); + printf(" GbE Region Read Access: %s\n", + get_enabled(flmstr & (1 << 19))); + printf(" Intel ME Region Read Access: %s\n", + get_enabled(flmstr & (1 << 18))); + printf(" Host CPU/BIOS Region Read Access: %s\n", + get_enabled(flmstr & (1 << 17))); + printf(" Flash Descriptor Read Access: %s\n", + get_enabled(flmstr & (1 << 16))); + + printf(" Requester ID: 0x%04x\n\n", + flmstr & 0xffff); +} + +static void dump_fmba(struct fmba_t *fmba) +{ + printf("Found Master Section\n"); + printf("FLMSTR1: 0x%08x (Host CPU/BIOS)\n", fmba->flmstr1); + decode_flmstr(fmba->flmstr1); + printf("FLMSTR2: 0x%08x (Intel ME)\n", fmba->flmstr2); + decode_flmstr(fmba->flmstr2); + printf("FLMSTR3: 0x%08x (GbE)\n", fmba->flmstr3); + decode_flmstr(fmba->flmstr3); +} + +static void dump_fmsba(struct fmsba_t *fmsba) +{ + int i; + + printf("Found Processor Strap Section\n"); + for (i = 0; i < 4; i++) + printf("????: 0x%08x\n", fmsba->data[0]); +} + +static void dump_jid(uint32_t jid) +{ + printf(" SPI Component Device ID 1: 0x%02x\n", + (jid >> 16) & 0xff); + printf(" SPI Component Device ID 0: 0x%02x\n", + (jid >> 8) & 0xff); + printf(" SPI Component Vendor ID: 0x%02x\n", + jid & 0xff); +} + +static void dump_vscc(uint32_t vscc) +{ + printf(" Lower Erase Opcode: 0x%02x\n", + vscc >> 24); + printf(" Lower Write Enable on Write Status: 0x%02x\n", + vscc & (1 << 20) ? 0x06 : 0x50); + printf(" Lower Write Status Required: %s\n", + vscc & (1 << 19) ? "Yes" : "No"); + printf(" Lower Write Granularity: %d bytes\n", + vscc & (1 << 18) ? 64 : 1); + printf(" Lower Block / Sector Erase Size: "); + switch ((vscc >> 16) & 0x3) { + case 0: + printf("256 Byte\n"); + break; + case 1: + printf("4KB\n"); + break; + case 2: + printf("8KB\n"); + break; + case 3: + printf("64KB\n"); + break; + } + + printf(" Upper Erase Opcode: 0x%02x\n", + (vscc >> 8) & 0xff); + printf(" Upper Write Enable on Write Status: 0x%02x\n", + vscc & (1 << 4) ? 0x06 : 0x50); + printf(" Upper Write Status Required: %s\n", + vscc & (1 << 3) ? "Yes" : "No"); + printf(" Upper Write Granularity: %d bytes\n", + vscc & (1 << 2) ? 64 : 1); + printf(" Upper Block / Sector Erase Size: "); + switch (vscc & 0x3) { + case 0: + printf("256 Byte\n"); + break; + case 1: + printf("4KB\n"); + break; + case 2: + printf("8KB\n"); + break; + case 3: + printf("64KB\n"); + break; + } +} + +static void dump_vtba(struct vtba_t *vtba, int vtl) +{ + int i; + int num = (vtl >> 1) < 8 ? (vtl >> 1) : 8; + + printf("ME VSCC table:\n"); + for (i = 0; i < num; i++) { + printf(" JID%d: 0x%08x\n", i, vtba->entry[i].jid); + dump_jid(vtba->entry[i].jid); + printf(" VSCC%d: 0x%08x\n", i, vtba->entry[i].vscc); + dump_vscc(vtba->entry[i].vscc); + } + printf("\n"); +} + +static void dump_oem(uint8_t *oem) +{ + int i, j; + printf("OEM Section:\n"); + for (i = 0; i < 4; i++) { + printf("%02x:", i << 4); + for (j = 0; j < 16; j++) + printf(" %02x", oem[(i<<4)+j]); + printf("\n"); + } + printf("\n"); +} + +/** + * dump_fd() - Display a dump of the full flash description + * + * @image: Pointer to image + * @size: Size of image in bytes + * @return 0 if OK, -1 on error + */ +static int dump_fd(char *image, int size) +{ + struct fdbar_t *fdb = find_fd(image, size); + + if (!fdb) + return -1; + + printf("FLMAP0: 0x%08x\n", fdb->flmap0); + printf(" NR: %d\n", (fdb->flmap0 >> 24) & 7); + printf(" FRBA: 0x%x\n", ((fdb->flmap0 >> 16) & 0xff) << 4); + printf(" NC: %d\n", ((fdb->flmap0 >> 8) & 3) + 1); + printf(" FCBA: 0x%x\n", ((fdb->flmap0) & 0xff) << 4); + + printf("FLMAP1: 0x%08x\n", fdb->flmap1); + printf(" ISL: 0x%02x\n", (fdb->flmap1 >> 24) & 0xff); + printf(" FPSBA: 0x%x\n", ((fdb->flmap1 >> 16) & 0xff) << 4); + printf(" NM: %d\n", (fdb->flmap1 >> 8) & 3); + printf(" FMBA: 0x%x\n", ((fdb->flmap1) & 0xff) << 4); + + printf("FLMAP2: 0x%08x\n", fdb->flmap2); + printf(" PSL: 0x%04x\n", (fdb->flmap2 >> 8) & 0xffff); + printf(" FMSBA: 0x%x\n", ((fdb->flmap2) & 0xff) << 4); + + printf("FLUMAP1: 0x%08x\n", fdb->flumap1); + printf(" Intel ME VSCC Table Length (VTL): %d\n", + (fdb->flumap1 >> 8) & 0xff); + printf(" Intel ME VSCC Table Base Address (VTBA): 0x%06x\n\n", + (fdb->flumap1 & 0xff) << 4); + dump_vtba((struct vtba_t *) + (image + ((fdb->flumap1 & 0xff) << 4)), + (fdb->flumap1 >> 8) & 0xff); + dump_oem((uint8_t *)image + 0xf00); + dump_frba((struct frba_t *)(image + (((fdb->flmap0 >> 16) & 0xff) + << 4))); + dump_fcba((struct fcba_t *)(image + (((fdb->flmap0) & 0xff) << 4))); + dump_fpsba((struct fpsba_t *) + (image + (((fdb->flmap1 >> 16) & 0xff) << 4))); + dump_fmba((struct fmba_t *)(image + (((fdb->flmap1) & 0xff) << 4))); + dump_fmsba((struct fmsba_t *)(image + (((fdb->flmap2) & 0xff) << 4))); + + return 0; +} + +/** + * write_regions() - Write each region from an image to its own file + * + * The filename to use in each case is fixed - see region_filename() + * + * @image: Pointer to image + * @size: Size of image in bytes + * @return 0 if OK, -ve on error + */ +static int write_regions(char *image, int size) +{ + struct fdbar_t *fdb; + struct frba_t *frba; + int ret = 0; + int i; + + fdb = find_fd(image, size); + if (!fdb) + return -1; + + frba = (struct frba_t *)(image + (((fdb->flmap0 >> 16) & 0xff) << 4)); + + for (i = 0; i < MAX_REGIONS; i++) { + struct region_t region; + int region_fd; + + ret = get_region(frba, i, ®ion); + if (ret) + return ret; + dump_region(i, frba); + if (region.size <= 0) + continue; + region_fd = open(region_filename(i), + O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | + S_IWUSR | S_IRGRP | S_IROTH); + if (write(region_fd, image + region.base, region.size) != + region.size) { + perror("Error while writing"); + ret = -1; + } + close(region_fd); + } + + return ret; +} + +static int perror_fname(const char *fmt, const char *fname) +{ + char msg[strlen(fmt) + strlen(fname) + 1]; + + sprintf(msg, fmt, fname); + perror(msg); + + return -1; +} + +/** + * write_image() - Write the image to a file + * + * @filename: Filename to use for the image + * @image: Pointer to image + * @size: Size of image in bytes + * @return 0 if OK, -ve on error + */ +static int write_image(char *filename, char *image, int size) +{ + int new_fd; + + debug("Writing new image to %s\n", filename); + + new_fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | + S_IWUSR | S_IRGRP | S_IROTH); + if (new_fd < 0) + return perror_fname("Could not open file '%s'", filename); + if (write(new_fd, image, size) != size) + return perror_fname("Could not write file '%s'", filename); + close(new_fd); + + return 0; +} + +/** + * set_spi_frequency() - Set the SPI frequency to use when booting + * + * Several frequencies are supported, some of which work with fast devices. + * For SPI emulators, the slowest (SPI_FREQUENCY_20MHZ) is often used. The + * Intel boot system uses this information somehow on boot. + * + * The image is updated with the supplied value + * + * @image: Pointer to image + * @size: Size of image in bytes + * @freq: SPI frequency to use + */ +static void set_spi_frequency(char *image, int size, enum spi_frequency freq) +{ + struct fdbar_t *fdb = find_fd(image, size); + struct fcba_t *fcba; + + fcba = (struct fcba_t *)(image + (((fdb->flmap0) & 0xff) << 4)); + + /* clear bits 21-29 */ + fcba->flcomp &= ~0x3fe00000; + /* Read ID and Read Status Clock Frequency */ + fcba->flcomp |= freq << 27; + /* Write and Erase Clock Frequency */ + fcba->flcomp |= freq << 24; + /* Fast Read Clock Frequency */ + fcba->flcomp |= freq << 21; +} + +/** + * set_em100_mode() - Set a SPI frequency that will work with Dediprog EM100 + * + * @image: Pointer to image + * @size: Size of image in bytes + */ +static void set_em100_mode(char *image, int size) +{ + struct fdbar_t *fdb = find_fd(image, size); + struct fcba_t *fcba; + + fcba = (struct fcba_t *)(image + (((fdb->flmap0) & 0xff) << 4)); + fcba->flcomp &= ~(1 << 30); + set_spi_frequency(image, size, SPI_FREQUENCY_20MHZ); +} + +/** + * lock_descriptor() - Lock the NE descriptor so it cannot be updated + * + * @image: Pointer to image + * @size: Size of image in bytes + */ +static void lock_descriptor(char *image, int size) +{ + struct fdbar_t *fdb = find_fd(image, size); + struct fmba_t *fmba; + + /* + * TODO: Dynamically take Platform Data Region and GbE Region into + * account. + */ + fmba = (struct fmba_t *)(image + (((fdb->flmap1) & 0xff) << 4)); + fmba->flmstr1 = 0x0a0b0000; + fmba->flmstr2 = 0x0c0d0000; + fmba->flmstr3 = 0x08080118; +} + +/** + * unlock_descriptor() - Lock the NE descriptor so it can be updated + * + * @image: Pointer to image + * @size: Size of image in bytes + */ +static void unlock_descriptor(char *image, int size) +{ + struct fdbar_t *fdb = find_fd(image, size); + struct fmba_t *fmba; + + fmba = (struct fmba_t *)(image + (((fdb->flmap1) & 0xff) << 4)); + fmba->flmstr1 = 0xffff0000; + fmba->flmstr2 = 0xffff0000; + fmba->flmstr3 = 0x08080118; +} + +/** + * open_for_read() - Open a file for reading + * + * @fname: Filename to open + * @sizep: Returns file size in bytes + * @return 0 if OK, -1 on error + */ +int open_for_read(const char *fname, int *sizep) +{ + int fd = open(fname, O_RDONLY); + struct stat buf; + + if (fd == -1) + return perror_fname("Could not open file '%s'", fname); + if (fstat(fd, &buf) == -1) + return perror_fname("Could not stat file '%s'", fname); + *sizep = buf.st_size; + debug("File %s is %d bytes\n", fname, *sizep); + + return fd; +} + +/** + * inject_region() - Add a file to an image region + * + * This puts a file into a particular region of the flash. Several pre-defined + * regions are used. + * + * @image: Pointer to image + * @size: Size of image in bytes + * @region_type: Region where the file should be added + * @region_fname: Filename to add to the image + * @return 0 if OK, -ve on error + */ +int inject_region(char *image, int size, int region_type, char *region_fname) +{ + struct fdbar_t *fdb = find_fd(image, size); + struct region_t region; + struct frba_t *frba; + int region_size; + int offset = 0; + int region_fd; + int ret; + + if (!fdb) + exit(EXIT_FAILURE); + frba = (struct frba_t *)(image + (((fdb->flmap0 >> 16) & 0xff) << 4)); + + ret = get_region(frba, region_type, ®ion); + if (ret) + return -1; + if (region.size <= 0xfff) { + fprintf(stderr, "Region %s is disabled in target. Not injecting.\n", + region_name(region_type)); + return -1; + } + + region_fd = open_for_read(region_fname, ®ion_size); + if (region_fd < 0) + return region_fd; + + if ((region_size > region.size) || + ((region_type != 1) && (region_size > region.size))) { + fprintf(stderr, "Region %s is %d(0x%x) bytes. File is %d(0x%x) bytes. Not injecting.\n", + region_name(region_type), region.size, + region.size, region_size, region_size); + return -1; + } + + if ((region_type == 1) && (region_size < region.size)) { + fprintf(stderr, "Region %s is %d(0x%x) bytes. File is %d(0x%x) bytes. Padding before injecting.\n", + region_name(region_type), region.size, + region.size, region_size, region_size); + offset = region.size - region_size; + memset(image + region.base, 0xff, offset); + } + + if (size < region.base + offset + region_size) { + fprintf(stderr, "Output file is too small. (%d < %d)\n", + size, region.base + offset + region_size); + return -1; + } + + if (read(region_fd, image + region.base + offset, region_size) + != region_size) { + perror("Could not read file"); + return -1; + } + + close(region_fd); + + debug("Adding %s as the %s section\n", region_fname, + region_name(region_type)); + + return 0; +} + +/** + * write_data() - Write some raw data into a region + * + * This puts a file into a particular place in the flash, ignoring the + * regions. Be careful not to overwrite something important. + * + * @image: Pointer to image + * @size: Size of image in bytes + * @addr: x86 ROM address to put file. The ROM ends at + * 0xffffffff so use an address relative to that. For an + * 8MB ROM the start address is 0xfff80000. + * @write_fname: Filename to add to the image + * @offset_uboot_top: Offset of the top of U-Boot + * @offset_uboot_start: Offset of the start of U-Boot + * @return number of bytes written if OK, -ve on error + */ +static int write_data(char *image, int size, unsigned int addr, + const char *write_fname, int offset_uboot_top, + int offset_uboot_start) +{ + int write_fd, write_size; + int offset; + + write_fd = open_for_read(write_fname, &write_size); + if (write_fd < 0) + return write_fd; + + offset = (uint32_t)(addr + size); + if (offset_uboot_top) { + if (offset_uboot_start < offset && + offset_uboot_top >= offset) { + fprintf(stderr, "U-Boot image overlaps with region '%s'\n", + write_fname); + fprintf(stderr, + "U-Boot finishes at offset %x, file starts at %x\n", + offset_uboot_top, offset); + return -EXDEV; + } + if (offset_uboot_start > offset && + offset_uboot_start <= offset + write_size) { + fprintf(stderr, "U-Boot image overlaps with region '%s'\n", + write_fname); + fprintf(stderr, + "U-Boot starts at offset %x, file finishes at %x\n", + offset_uboot_start, offset + write_size); + return -EXDEV; + } + } + debug("Writing %s to offset %#x\n", write_fname, offset); + + if (offset < 0 || offset + write_size > size) { + fprintf(stderr, "Output file is too small. (%d < %d)\n", + size, offset + write_size); + return -1; + } + + if (read(write_fd, image + offset, write_size) != write_size) { + perror("Could not read file"); + return -1; + } + + close(write_fd); + + return write_size; +} + +static void print_version(void) +{ + printf("ifdtool v%s -- ", IFDTOOL_VERSION); + printf("Copyright (C) 2014 Google Inc.\n\n"); + printf("SPDX-License-Identifier: GPL-2.0+\n"); +} + +static void print_usage(const char *name) +{ + printf("usage: %s [-vhdix?] <filename> [<outfile>]\n", name); + printf("\n" + " -d | --dump: dump intel firmware descriptor\n" + " -x | --extract: extract intel fd modules\n" + " -i | --inject <region>:<module> inject file <module> into region <region>\n" + " -w | --write <addr>:<file> write file to appear at memory address <addr>\n" + " multiple files can be written simultaneously\n" + " -s | --spifreq <20|33|50> set the SPI frequency\n" + " -e | --em100 set SPI frequency to 20MHz and disable\n" + " Dual Output Fast Read Support\n" + " -l | --lock Lock firmware descriptor and ME region\n" + " -u | --unlock Unlock firmware descriptor and ME region\n" + " -r | --romsize Specify ROM size\n" + " -D | --write-descriptor <file> Write descriptor at base\n" + " -c | --create Create a new empty image\n" + " -v | --version: print the version\n" + " -h | --help: print this help\n\n" + "<region> is one of Descriptor, BIOS, ME, GbE, Platform\n" + "\n"); +} + +/** + * get_two_words() - Convert a string into two words separated by : + * + * The supplied string is split at ':', two substrings are allocated and + * returned. + * + * @str: String to split + * @firstp: Returns first string + * @secondp: Returns second string + * @return 0 if OK, -ve if @str does not have a : + */ +static int get_two_words(const char *str, char **firstp, char **secondp) +{ + const char *p; + + p = strchr(str, ':'); + if (!p) + return -1; + *firstp = strdup(str); + (*firstp)[p - str] = '\0'; + *secondp = strdup(p + 1); + + return 0; +} + +int main(int argc, char *argv[]) +{ + int opt, option_index = 0; + int mode_dump = 0, mode_extract = 0, mode_inject = 0; + int mode_spifreq = 0, mode_em100 = 0, mode_locked = 0; + int mode_unlocked = 0, mode_write = 0, mode_write_descriptor = 0; + int create = 0; + char *region_type_string = NULL, *inject_fname = NULL; + char *desc_fname = NULL, *addr_str = NULL; + int region_type = -1, inputfreq = 0; + enum spi_frequency spifreq = SPI_FREQUENCY_20MHZ; + struct input_file input_file[WRITE_MAX], *ifile, *fdt = NULL; + unsigned char wr_idx, wr_num = 0; + int rom_size = -1; + bool write_it; + char *filename; + char *outfile = NULL; + struct stat buf; + int size = 0; + bool have_uboot = false; + int bios_fd; + char *image; + int ret; + static struct option long_options[] = { + {"create", 0, NULL, 'c'}, + {"dump", 0, NULL, 'd'}, + {"descriptor", 1, NULL, 'D'}, + {"em100", 0, NULL, 'e'}, + {"extract", 0, NULL, 'x'}, + {"fdt", 1, NULL, 'f'}, + {"inject", 1, NULL, 'i'}, + {"lock", 0, NULL, 'l'}, + {"romsize", 1, NULL, 'r'}, + {"spifreq", 1, NULL, 's'}, + {"unlock", 0, NULL, 'u'}, + {"uboot", 1, NULL, 'U'}, + {"write", 1, NULL, 'w'}, + {"version", 0, NULL, 'v'}, + {"help", 0, NULL, 'h'}, + {0, 0, 0, 0} + }; + + while ((opt = getopt_long(argc, argv, "cdD:ef:hi:lr:s:uU:vw:x?", + long_options, &option_index)) != EOF) { + switch (opt) { + case 'c': + create = 1; + break; + case 'd': + mode_dump = 1; + break; + case 'D': + mode_write_descriptor = 1; + desc_fname = optarg; + break; + case 'e': + mode_em100 = 1; + break; + case 'i': + if (get_two_words(optarg, ®ion_type_string, + &inject_fname)) { + print_usage(argv[0]); + exit(EXIT_FAILURE); + } + if (!strcasecmp("Descriptor", region_type_string)) + region_type = 0; + else if (!strcasecmp("BIOS", region_type_string)) + region_type = 1; + else if (!strcasecmp("ME", region_type_string)) + region_type = 2; + else if (!strcasecmp("GbE", region_type_string)) + region_type = 3; + else if (!strcasecmp("Platform", region_type_string)) + region_type = 4; + if (region_type == -1) { + fprintf(stderr, "No such region type: '%s'\n\n", + region_type_string); + print_usage(argv[0]); + exit(EXIT_FAILURE); + } + mode_inject = 1; + break; + case 'l': + mode_locked = 1; + break; + case 'r': + rom_size = strtol(optarg, NULL, 0); + debug("ROM size %d\n", rom_size); + break; + case 's': + /* Parse the requested SPI frequency */ + inputfreq = strtol(optarg, NULL, 0); + switch (inputfreq) { + case 20: + spifreq = SPI_FREQUENCY_20MHZ; + break; + case 33: + spifreq = SPI_FREQUENCY_33MHZ; + break; + case 50: + spifreq = SPI_FREQUENCY_50MHZ; + break; + default: + fprintf(stderr, "Invalid SPI Frequency: %d\n", + inputfreq); + print_usage(argv[0]); + exit(EXIT_FAILURE); + } + mode_spifreq = 1; + break; + case 'u': + mode_unlocked = 1; + break; + case 'v': + print_version(); + exit(EXIT_SUCCESS); + break; + case 'w': + case 'U': + case 'f': + ifile = &input_file[wr_num]; + mode_write = 1; + if (wr_num < WRITE_MAX) { + if (get_two_words(optarg, &addr_str, + &ifile->fname)) { + print_usage(argv[0]); + exit(EXIT_FAILURE); + } + ifile->addr = strtoll(optarg, NULL, 0); + wr_num++; + } else { + fprintf(stderr, + "The number of files to write simultaneously exceeds the limitation (%d)\n", + WRITE_MAX); + } + break; + case 'x': + mode_extract = 1; + break; + case 'h': + case '?': + default: + print_usage(argv[0]); + exit(EXIT_SUCCESS); + break; + } + } + + if (mode_locked == 1 && mode_unlocked == 1) { + fprintf(stderr, "Locking/Unlocking FD and ME are mutually exclusive\n"); + exit(EXIT_FAILURE); + } + + if (mode_inject == 1 && mode_write == 1) { + fprintf(stderr, "Inject/Write are mutually exclusive\n"); + exit(EXIT_FAILURE); + } + + if ((mode_dump + mode_extract + mode_inject + + (mode_spifreq | mode_em100 | mode_unlocked | + mode_locked)) > 1) { + fprintf(stderr, "You may not specify more than one mode.\n\n"); + print_usage(argv[0]); + exit(EXIT_FAILURE); + } + + if ((mode_dump + mode_extract + mode_inject + mode_spifreq + + mode_em100 + mode_locked + mode_unlocked + mode_write + + mode_write_descriptor) == 0 && !create) { + fprintf(stderr, "You need to specify a mode.\n\n"); + print_usage(argv[0]); + exit(EXIT_FAILURE); + } + + if (create && rom_size == -1) { + fprintf(stderr, "You need to specify a rom size when creating.\n\n"); + exit(EXIT_FAILURE); + } + + if (optind + 1 != argc) { + fprintf(stderr, "You need to specify a file.\n\n"); + print_usage(argv[0]); + exit(EXIT_FAILURE); + } + + if (have_uboot && !fdt) { + fprintf(stderr, + "You must supply a device tree file for U-Boot\n\n"); + print_usage(argv[0]); + exit(EXIT_FAILURE); + } + + filename = argv[optind]; + if (optind + 2 != argc) + outfile = argv[optind + 1]; + + if (create) + bios_fd = open(filename, O_WRONLY | O_CREAT, 0666); + else + bios_fd = open(filename, outfile ? O_RDONLY : O_RDWR); + + if (bios_fd == -1) { + perror("Could not open file"); + exit(EXIT_FAILURE); + } + + if (!create) { + if (fstat(bios_fd, &buf) == -1) { + perror("Could not stat file"); + exit(EXIT_FAILURE); + } + size = buf.st_size; + } + + debug("File %s is %d bytes\n", filename, size); + + if (rom_size == -1) + rom_size = size; + + image = malloc(rom_size); + if (!image) { + printf("Out of memory.\n"); + exit(EXIT_FAILURE); + } + + memset(image, '\xff', rom_size); + if (!create && read(bios_fd, image, size) != size) { + perror("Could not read file"); + exit(EXIT_FAILURE); + } + if (size != rom_size) { + debug("ROM size changed to %d bytes\n", rom_size); + size = rom_size; + } + + write_it = true; + ret = 0; + if (mode_dump) { + ret = dump_fd(image, size); + write_it = false; + } + + if (mode_extract) { + ret = write_regions(image, size); + write_it = false; + } + + if (mode_write_descriptor) + ret = write_data(image, size, -size, desc_fname, 0, 0); + + if (mode_inject) + ret = inject_region(image, size, region_type, inject_fname); + + if (mode_write) { + int offset_uboot_top = 0; + int offset_uboot_start = 0; + + for (wr_idx = 0; wr_idx < wr_num; wr_idx++) { + ifile = &input_file[wr_idx]; + ret = write_data(image, size, ifile->addr, + ifile->fname, offset_uboot_top, + offset_uboot_start); + if (ret < 0) + break; + } + } + + if (mode_spifreq) + set_spi_frequency(image, size, spifreq); + + if (mode_em100) + set_em100_mode(image, size); + + if (mode_locked) + lock_descriptor(image, size); + + if (mode_unlocked) + unlock_descriptor(image, size); + + if (write_it) { + if (outfile) { + ret = write_image(outfile, image, size); + } else { + if (lseek(bios_fd, 0, SEEK_SET)) { + perror("Error while seeking"); + ret = -1; + } + if (write(bios_fd, image, size) != size) { + perror("Error while writing"); + ret = -1; + } + } + } + + free(image); + close(bios_fd); + + return ret < 0 ? 1 : 0; +} diff --git a/tools/u-boot-tools/ifdtool.h b/tools/u-boot-tools/ifdtool.h new file mode 100644 index 0000000..bb70b2f --- /dev/null +++ b/tools/u-boot-tools/ifdtool.h @@ -0,0 +1,89 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * ifdtool - Manage Intel Firmware Descriptor information + * + * Copyright (C) 2011 The ChromiumOS Authors. + * + * From Coreboot project + */ + +#include <stdint.h> + +#define __packed __attribute__((packed)) + +#define IFDTOOL_VERSION "1.1-U-Boot" + +#define WRITE_MAX 16 + +enum spi_frequency { + SPI_FREQUENCY_20MHZ = 0, + SPI_FREQUENCY_33MHZ = 1, + SPI_FREQUENCY_50MHZ = 4, +}; + +enum component_density { + COMPONENT_DENSITY_512KB = 0, + COMPONENT_DENSITY_1MB = 1, + COMPONENT_DENSITY_2MB = 2, + COMPONENT_DENSITY_4MB = 3, + COMPONENT_DENSITY_8MB = 4, + COMPONENT_DENSITY_16MB = 5, +}; + +/* flash descriptor */ +struct __packed fdbar_t { + uint32_t flvalsig; + uint32_t flmap0; + uint32_t flmap1; + uint32_t flmap2; + uint8_t reserved[0xefc - 0x20]; + uint32_t flumap1; +}; + +#define MAX_REGIONS 5 + +/* regions */ +struct __packed frba_t { + uint32_t flreg[MAX_REGIONS]; +}; + +/* component section */ +struct __packed fcba_t { + uint32_t flcomp; + uint32_t flill; + uint32_t flpb; +}; + +#define MAX_STRAPS 18 + +/* pch strap */ +struct __packed fpsba_t { + uint32_t pchstrp[MAX_STRAPS]; +}; + +/* master */ +struct __packed fmba_t { + uint32_t flmstr1; + uint32_t flmstr2; + uint32_t flmstr3; +}; + +/* processor strap */ +struct __packed fmsba_t { + uint32_t data[8]; +}; + +/* ME VSCC */ +struct vscc_t { + uint32_t jid; + uint32_t vscc; +}; + +struct vtba_t { + /* Actual number of entries specified in vtl */ + struct vscc_t entry[8]; +}; + +struct region_t { + int base, limit, size; +}; diff --git a/tools/u-boot-tools/image-host.c b/tools/u-boot-tools/image-host.c new file mode 100644 index 0000000..88b3295 --- /dev/null +++ b/tools/u-boot-tools/image-host.c @@ -0,0 +1,754 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2013, Google Inc. + * + * (C) Copyright 2008 Semihalf + * + * (C) Copyright 2000-2006 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + */ + +#include "mkimage.h" +#include <bootm.h> +#include <image.h> +#include <version.h> + +/** + * fit_set_hash_value - set hash value in requested has node + * @fit: pointer to the FIT format image header + * @noffset: hash node offset + * @value: hash value to be set + * @value_len: hash value length + * + * fit_set_hash_value() attempts to set hash value in a node at offset + * given and returns operation status to the caller. + * + * returns + * 0, on success + * -1, on failure + */ +static int fit_set_hash_value(void *fit, int noffset, uint8_t *value, + int value_len) +{ + int ret; + + ret = fdt_setprop(fit, noffset, FIT_VALUE_PROP, value, value_len); + if (ret) { + printf("Can't set hash '%s' property for '%s' node(%s)\n", + FIT_VALUE_PROP, fit_get_name(fit, noffset, NULL), + fdt_strerror(ret)); + return ret == -FDT_ERR_NOSPACE ? -ENOSPC : -EIO; + } + + return 0; +} + +/** + * fit_image_process_hash - Process a single subnode of the images/ node + * + * Check each subnode and process accordingly. For hash nodes we generate + * a hash of the supplised data and store it in the node. + * + * @fit: pointer to the FIT format image header + * @image_name: name of image being processes (used to display errors) + * @noffset: subnode offset + * @data: data to process + * @size: size of data in bytes + * @return 0 if ok, -1 on error + */ +static int fit_image_process_hash(void *fit, const char *image_name, + int noffset, const void *data, size_t size) +{ + uint8_t value[FIT_MAX_HASH_LEN]; + const char *node_name; + int value_len; + char *algo; + int ret; + + node_name = fit_get_name(fit, noffset, NULL); + + if (fit_image_hash_get_algo(fit, noffset, &algo)) { + printf("Can't get hash algo property for '%s' hash node in '%s' image node\n", + node_name, image_name); + return -ENOENT; + } + + if (calculate_hash(data, size, algo, value, &value_len)) { + printf("Unsupported hash algorithm (%s) for '%s' hash node in '%s' image node\n", + algo, node_name, image_name); + return -EPROTONOSUPPORT; + } + + ret = fit_set_hash_value(fit, noffset, value, value_len); + if (ret) { + printf("Can't set hash value for '%s' hash node in '%s' image node\n", + node_name, image_name); + return ret; + } + + return 0; +} + +/** + * fit_image_write_sig() - write the signature to a FIT + * + * This writes the signature and signer data to the FIT. + * + * @fit: pointer to the FIT format image header + * @noffset: hash node offset + * @value: signature value to be set + * @value_len: signature value length + * @comment: Text comment to write (NULL for none) + * + * returns + * 0, on success + * -FDT_ERR_..., on failure + */ +static int fit_image_write_sig(void *fit, int noffset, uint8_t *value, + int value_len, const char *comment, const char *region_prop, + int region_proplen, const char *cmdname) +{ + int string_size; + int ret; + + /* + * Get the current string size, before we update the FIT and add + * more + */ + string_size = fdt_size_dt_strings(fit); + + ret = fdt_setprop(fit, noffset, FIT_VALUE_PROP, value, value_len); + if (!ret) { + ret = fdt_setprop_string(fit, noffset, "signer-name", + "mkimage"); + } + if (!ret) { + ret = fdt_setprop_string(fit, noffset, "signer-version", + PLAIN_VERSION); + } + if (comment && !ret) + ret = fdt_setprop_string(fit, noffset, "comment", comment); + if (!ret) { + time_t timestamp = imagetool_get_source_date(cmdname, + time(NULL)); + + ret = fit_set_timestamp(fit, noffset, timestamp); + } + if (region_prop && !ret) { + uint32_t strdata[2]; + + ret = fdt_setprop(fit, noffset, "hashed-nodes", + region_prop, region_proplen); + /* This is a legacy offset, it is unused, and must remain 0. */ + strdata[0] = 0; + strdata[1] = cpu_to_fdt32(string_size); + if (!ret) { + ret = fdt_setprop(fit, noffset, "hashed-strings", + strdata, sizeof(strdata)); + } + } + + return ret; +} + +static int fit_image_setup_sig(struct image_sign_info *info, + const char *keydir, void *fit, const char *image_name, + int noffset, const char *require_keys, const char *engine_id) +{ + const char *node_name; + char *algo_name; + const char *padding_name; + + node_name = fit_get_name(fit, noffset, NULL); + if (fit_image_hash_get_algo(fit, noffset, &algo_name)) { + printf("Can't get algo property for '%s' signature node in '%s' image node\n", + node_name, image_name); + return -1; + } + + padding_name = fdt_getprop(fit, noffset, "padding", NULL); + + memset(info, '\0', sizeof(*info)); + info->keydir = keydir; + info->keyname = fdt_getprop(fit, noffset, "key-name-hint", NULL); + info->fit = fit; + info->node_offset = noffset; + info->name = strdup(algo_name); + info->checksum = image_get_checksum_algo(algo_name); + info->crypto = image_get_crypto_algo(algo_name); + info->padding = image_get_padding_algo(padding_name); + info->require_keys = require_keys; + info->engine_id = engine_id; + if (!info->checksum || !info->crypto) { + printf("Unsupported signature algorithm (%s) for '%s' signature node in '%s' image node\n", + algo_name, node_name, image_name); + return -1; + } + + return 0; +} + +/** + * fit_image_process_sig- Process a single subnode of the images/ node + * + * Check each subnode and process accordingly. For signature nodes we + * generate a signed hash of the supplised data and store it in the node. + * + * @keydir: Directory containing keys to use for signing + * @keydest: Destination FDT blob to write public keys into + * @fit: pointer to the FIT format image header + * @image_name: name of image being processes (used to display errors) + * @noffset: subnode offset + * @data: data to process + * @size: size of data in bytes + * @comment: Comment to add to signature nodes + * @require_keys: Mark all keys as 'required' + * @engine_id: Engine to use for signing + * @return 0 if ok, -1 on error + */ +static int fit_image_process_sig(const char *keydir, void *keydest, + void *fit, const char *image_name, + int noffset, const void *data, size_t size, + const char *comment, int require_keys, const char *engine_id, + const char *cmdname) +{ + struct image_sign_info info; + struct image_region region; + const char *node_name; + uint8_t *value; + uint value_len; + int ret; + + if (fit_image_setup_sig(&info, keydir, fit, image_name, noffset, + require_keys ? "image" : NULL, engine_id)) + return -1; + + node_name = fit_get_name(fit, noffset, NULL); + region.data = data; + region.size = size; + ret = info.crypto->sign(&info, ®ion, 1, &value, &value_len); + if (ret) { + printf("Failed to sign '%s' signature node in '%s' image node: %d\n", + node_name, image_name, ret); + + /* We allow keys to be missing */ + if (ret == -ENOENT) + return 0; + return -1; + } + + ret = fit_image_write_sig(fit, noffset, value, value_len, comment, + NULL, 0, cmdname); + if (ret) { + if (ret == -FDT_ERR_NOSPACE) + return -ENOSPC; + printf("Can't write signature for '%s' signature node in '%s' conf node: %s\n", + node_name, image_name, fdt_strerror(ret)); + return -1; + } + free(value); + + /* Get keyname again, as FDT has changed and invalidated our pointer */ + info.keyname = fdt_getprop(fit, noffset, "key-name-hint", NULL); + + /* + * Write the public key into the supplied FDT file; this might fail + * several times, since we try signing with successively increasing + * size values + */ + if (keydest) { + ret = info.crypto->add_verify_data(&info, keydest); + if (ret) { + printf("Failed to add verification data for '%s' signature node in '%s' image node\n", + node_name, image_name); + return ret; + } + } + + return 0; +} + +/** + * fit_image_add_verification_data() - calculate/set verig. data for image node + * + * This adds hash and signature values for an component image node. + * + * All existing hash subnodes are checked, if algorithm property is set to + * one of the supported hash algorithms, hash value is computed and + * corresponding hash node property is set, for example: + * + * Input component image node structure: + * + * o image-1 (at image_noffset) + * | - data = [binary data] + * o hash-1 + * |- algo = "sha1" + * + * Output component image node structure: + * + * o image-1 (at image_noffset) + * | - data = [binary data] + * o hash-1 + * |- algo = "sha1" + * |- value = sha1(data) + * + * For signature details, please see doc/uImage.FIT/signature.txt + * + * @keydir Directory containing *.key and *.crt files (or NULL) + * @keydest FDT Blob to write public keys into (NULL if none) + * @fit: Pointer to the FIT format image header + * @image_noffset: Requested component image node + * @comment: Comment to add to signature nodes + * @require_keys: Mark all keys as 'required' + * @engine_id: Engine to use for signing + * @return: 0 on success, <0 on failure + */ +int fit_image_add_verification_data(const char *keydir, void *keydest, + void *fit, int image_noffset, const char *comment, + int require_keys, const char *engine_id, const char *cmdname) +{ + const char *image_name; + const void *data; + size_t size; + int noffset; + + /* Get image data and data length */ + if (fit_image_get_data(fit, image_noffset, &data, &size)) { + printf("Can't get image data/size\n"); + return -1; + } + + image_name = fit_get_name(fit, image_noffset, NULL); + + /* 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 *node_name; + int ret = 0; + + /* + * Check subnode name, must be equal to "hash" or "signature". + * Multiple hash nodes require unique unit node + * names, e.g. hash-1, hash-2, signature-1, etc. + */ + node_name = fit_get_name(fit, noffset, NULL); + if (!strncmp(node_name, FIT_HASH_NODENAME, + strlen(FIT_HASH_NODENAME))) { + ret = fit_image_process_hash(fit, image_name, noffset, + data, size); + } else if (IMAGE_ENABLE_SIGN && keydir && + !strncmp(node_name, FIT_SIG_NODENAME, + strlen(FIT_SIG_NODENAME))) { + ret = fit_image_process_sig(keydir, keydest, + fit, image_name, noffset, data, size, + comment, require_keys, engine_id, cmdname); + } + if (ret) + return ret; + } + + return 0; +} + +struct strlist { + int count; + char **strings; +}; + +static void strlist_init(struct strlist *list) +{ + memset(list, '\0', sizeof(*list)); +} + +static void strlist_free(struct strlist *list) +{ + int i; + + for (i = 0; i < list->count; i++) + free(list->strings[i]); + free(list->strings); +} + +static int strlist_add(struct strlist *list, const char *str) +{ + char *dup; + + dup = strdup(str); + list->strings = realloc(list->strings, + (list->count + 1) * sizeof(char *)); + if (!list || !str) + return -1; + list->strings[list->count++] = dup; + + return 0; +} + +static const char *fit_config_get_image_list(void *fit, int noffset, + int *lenp, int *allow_missingp) +{ + static const char default_list[] = FIT_KERNEL_PROP "\0" + FIT_FDT_PROP; + const char *prop; + + /* If there is an "image" property, use that */ + prop = fdt_getprop(fit, noffset, "sign-images", lenp); + if (prop) { + *allow_missingp = 0; + return *lenp ? prop : NULL; + } + + /* Default image list */ + *allow_missingp = 1; + *lenp = sizeof(default_list); + + return default_list; +} + +static int fit_config_get_hash_list(void *fit, int conf_noffset, + int sig_offset, struct strlist *node_inc) +{ + int allow_missing; + const char *prop, *iname, *end; + const char *conf_name, *sig_name; + char name[200], path[200]; + int image_count; + int ret, len; + + conf_name = fit_get_name(fit, conf_noffset, NULL); + sig_name = fit_get_name(fit, sig_offset, NULL); + + /* + * Build a list of nodes we need to hash. We always need the root + * node and the configuration. + */ + strlist_init(node_inc); + snprintf(name, sizeof(name), "%s/%s", FIT_CONFS_PATH, conf_name); + if (strlist_add(node_inc, "/") || + strlist_add(node_inc, name)) + goto err_mem; + + /* Get a list of images that we intend to sign */ + prop = fit_config_get_image_list(fit, sig_offset, &len, + &allow_missing); + if (!prop) + return 0; + + /* Locate the images */ + end = prop + len; + image_count = 0; + for (iname = prop; iname < end; iname += strlen(iname) + 1) { + int noffset; + int image_noffset; + int hash_count; + + image_noffset = fit_conf_get_prop_node(fit, conf_noffset, + iname); + if (image_noffset < 0) { + printf("Failed to find image '%s' in configuration '%s/%s'\n", + iname, conf_name, sig_name); + if (allow_missing) + continue; + + return -ENOENT; + } + + ret = fdt_get_path(fit, image_noffset, path, sizeof(path)); + if (ret < 0) + goto err_path; + if (strlist_add(node_inc, path)) + goto err_mem; + + snprintf(name, sizeof(name), "%s/%s", FIT_CONFS_PATH, + conf_name); + + /* Add all this image's hashes */ + hash_count = 0; + for (noffset = fdt_first_subnode(fit, image_noffset); + noffset >= 0; + noffset = fdt_next_subnode(fit, noffset)) { + const char *name = fit_get_name(fit, noffset, NULL); + + if (strncmp(name, FIT_HASH_NODENAME, + strlen(FIT_HASH_NODENAME))) + continue; + ret = fdt_get_path(fit, noffset, path, sizeof(path)); + if (ret < 0) + goto err_path; + if (strlist_add(node_inc, path)) + goto err_mem; + hash_count++; + } + + if (!hash_count) { + printf("Failed to find any hash nodes in configuration '%s/%s' image '%s' - without these it is not possible to verify this image\n", + conf_name, sig_name, iname); + return -ENOMSG; + } + + image_count++; + } + + if (!image_count) { + printf("Failed to find any images for configuration '%s/%s'\n", + conf_name, sig_name); + return -ENOMSG; + } + + return 0; + +err_mem: + printf("Out of memory processing configuration '%s/%s'\n", conf_name, + sig_name); + return -ENOMEM; + +err_path: + printf("Failed to get path for image '%s' in configuration '%s/%s': %s\n", + iname, conf_name, sig_name, fdt_strerror(ret)); + return -ENOENT; +} + +static int fit_config_get_data(void *fit, int conf_noffset, int noffset, + struct image_region **regionp, int *region_countp, + char **region_propp, int *region_proplen) +{ + char * const exc_prop[] = {"data"}; + struct strlist node_inc; + struct image_region *region; + struct fdt_region fdt_regions[100]; + const char *conf_name, *sig_name; + char path[200]; + int count, i; + char *region_prop; + int ret, len; + + conf_name = fit_get_name(fit, conf_noffset, NULL); + sig_name = fit_get_name(fit, noffset, NULL); + debug("%s: conf='%s', sig='%s'\n", __func__, conf_name, sig_name); + + /* Get a list of nodes we want to hash */ + ret = fit_config_get_hash_list(fit, conf_noffset, noffset, &node_inc); + if (ret) + return ret; + + /* Get a list of regions to hash */ + count = fdt_find_regions(fit, node_inc.strings, node_inc.count, + exc_prop, ARRAY_SIZE(exc_prop), + fdt_regions, ARRAY_SIZE(fdt_regions), + path, sizeof(path), 1); + if (count < 0) { + printf("Failed to hash configuration '%s/%s': %s\n", conf_name, + sig_name, fdt_strerror(ret)); + return -EIO; + } + if (count == 0) { + printf("No data to hash for configuration '%s/%s': %s\n", + conf_name, sig_name, fdt_strerror(ret)); + return -EINVAL; + } + + /* Build our list of data blocks */ + region = fit_region_make_list(fit, fdt_regions, count, NULL); + if (!region) { + printf("Out of memory hashing configuration '%s/%s'\n", + conf_name, sig_name); + return -ENOMEM; + } + + /* Create a list of all hashed properties */ + debug("Hash nodes:\n"); + for (i = len = 0; i < node_inc.count; i++) { + debug(" %s\n", node_inc.strings[i]); + len += strlen(node_inc.strings[i]) + 1; + } + region_prop = malloc(len); + if (!region_prop) { + printf("Out of memory setting up regions for configuration '%s/%s'\n", + conf_name, sig_name); + return -ENOMEM; + } + for (i = len = 0; i < node_inc.count; + len += strlen(node_inc.strings[i]) + 1, i++) + strcpy(region_prop + len, node_inc.strings[i]); + strlist_free(&node_inc); + + *region_countp = count; + *regionp = region; + *region_propp = region_prop; + *region_proplen = len; + + return 0; +} + +static int fit_config_process_sig(const char *keydir, void *keydest, + void *fit, const char *conf_name, int conf_noffset, + int noffset, const char *comment, int require_keys, + const char *engine_id, const char *cmdname) +{ + struct image_sign_info info; + const char *node_name; + struct image_region *region; + char *region_prop; + int region_proplen; + int region_count; + uint8_t *value; + uint value_len; + int ret; + + node_name = fit_get_name(fit, noffset, NULL); + if (fit_config_get_data(fit, conf_noffset, noffset, ®ion, + ®ion_count, ®ion_prop, ®ion_proplen)) + return -1; + + if (fit_image_setup_sig(&info, keydir, fit, conf_name, noffset, + require_keys ? "conf" : NULL, engine_id)) + return -1; + + ret = info.crypto->sign(&info, region, region_count, &value, + &value_len); + free(region); + if (ret) { + printf("Failed to sign '%s' signature node in '%s' conf node\n", + node_name, conf_name); + + /* We allow keys to be missing */ + if (ret == -ENOENT) + return 0; + return -1; + } + + ret = fit_image_write_sig(fit, noffset, value, value_len, comment, + region_prop, region_proplen, cmdname); + if (ret) { + if (ret == -FDT_ERR_NOSPACE) + return -ENOSPC; + printf("Can't write signature for '%s' signature node in '%s' conf node: %s\n", + node_name, conf_name, fdt_strerror(ret)); + return -1; + } + free(value); + free(region_prop); + + /* Get keyname again, as FDT has changed and invalidated our pointer */ + info.keyname = fdt_getprop(fit, noffset, "key-name-hint", NULL); + + /* Write the public key into the supplied FDT file */ + if (keydest) { + ret = info.crypto->add_verify_data(&info, keydest); + if (ret) { + printf("Failed to add verification data for '%s' signature node in '%s' configuration node\n", + node_name, conf_name); + } + return ret; + } + + return 0; +} + +static int fit_config_add_verification_data(const char *keydir, void *keydest, + void *fit, int conf_noffset, const char *comment, + int require_keys, const char *engine_id, const char *cmdname) +{ + const char *conf_name; + int noffset; + + conf_name = fit_get_name(fit, conf_noffset, NULL); + + /* Process all hash subnodes of the configuration node */ + for (noffset = fdt_first_subnode(fit, conf_noffset); + noffset >= 0; + noffset = fdt_next_subnode(fit, noffset)) { + const char *node_name; + int ret = 0; + + node_name = fit_get_name(fit, noffset, NULL); + if (!strncmp(node_name, FIT_SIG_NODENAME, + strlen(FIT_SIG_NODENAME))) { + ret = fit_config_process_sig(keydir, keydest, + fit, conf_name, conf_noffset, noffset, comment, + require_keys, engine_id, cmdname); + } + if (ret) + return ret; + } + + return 0; +} + +int fit_add_verification_data(const char *keydir, void *keydest, void *fit, + const char *comment, int require_keys, + const char *engine_id, const char *cmdname) +{ + int images_noffset, confs_noffset; + int noffset; + int ret; + + /* 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 images_noffset; + } + + /* Process its subnodes, print out component images details */ + for (noffset = fdt_first_subnode(fit, images_noffset); + noffset >= 0; + noffset = fdt_next_subnode(fit, noffset)) { + /* + * Direct child node of the images parent node, + * i.e. component image node. + */ + ret = fit_image_add_verification_data(keydir, keydest, + fit, noffset, comment, require_keys, engine_id, + cmdname); + if (ret) + return ret; + } + + /* If there are no keys, we can't sign configurations */ + if (!IMAGE_ENABLE_SIGN || !keydir) + return 0; + + /* Find configurations parent node offset */ + confs_noffset = fdt_path_offset(fit, FIT_CONFS_PATH); + if (confs_noffset < 0) { + printf("Can't find images parent node '%s' (%s)\n", + FIT_CONFS_PATH, fdt_strerror(confs_noffset)); + return -ENOENT; + } + + /* Process its subnodes, print out component images details */ + for (noffset = fdt_first_subnode(fit, confs_noffset); + noffset >= 0; + noffset = fdt_next_subnode(fit, noffset)) { + ret = fit_config_add_verification_data(keydir, keydest, + fit, noffset, comment, + require_keys, + engine_id, cmdname); + if (ret) + return ret; + } + + return 0; +} + +#ifdef CONFIG_FIT_SIGNATURE +int fit_check_sign(const void *fit, const void *key) +{ + int cfg_noffset; + int ret; + + cfg_noffset = fit_conf_get_node(fit, NULL); + if (!cfg_noffset) + return -1; + + printf("Verifying Hash Integrity ... "); + ret = fit_config_verify(fit, cfg_noffset); + if (ret) + return ret; + ret = bootm_host_load_images(fit, cfg_noffset); + + return ret; +} +#endif diff --git a/tools/u-boot-tools/image-host.o b/tools/u-boot-tools/image-host.o new file mode 100644 index 0000000000000000000000000000000000000000..f0516461273a93b368a636d977634a0e2bf38a49 GIT binary patch literal 4360 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 literal 0 HcmV?d00001 diff --git a/tools/u-boot-tools/imagetool.c b/tools/u-boot-tools/imagetool.c new file mode 100644 index 0000000..b3e628f --- /dev/null +++ b/tools/u-boot-tools/imagetool.c @@ -0,0 +1,136 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2013 + * + * Written by Guilherme Maciel Ferreira <guilherme.maciel.ferreira@gmail.com> + */ + +#include "imagetool.h" + +#include <image.h> + +struct image_type_params *imagetool_get_type(int type) +{ + struct image_type_params **curr; + INIT_SECTION(image_type); + + struct image_type_params **start = __start_image_type; + struct image_type_params **end = __stop_image_type; + + for (curr = start; curr != end; curr++) { + if ((*curr)->check_image_type) { + if (!(*curr)->check_image_type(type)) + return *curr; + } + } + return NULL; +} + +int imagetool_verify_print_header( + void *ptr, + struct stat *sbuf, + struct image_type_params *tparams, + struct image_tool_params *params) +{ + int retval = -1; + struct image_type_params **curr; + INIT_SECTION(image_type); + + struct image_type_params **start = __start_image_type; + struct image_type_params **end = __stop_image_type; + + for (curr = start; curr != end; curr++) { + if ((*curr)->verify_header) { + retval = (*curr)->verify_header((unsigned char *)ptr, + sbuf->st_size, params); + + if (retval == 0) { + /* + * Print the image information if verify is + * successful + */ + if ((*curr)->print_header) { + if (!params->quiet) + (*curr)->print_header(ptr); + } else { + fprintf(stderr, + "%s: print_header undefined for %s\n", + params->cmdname, (*curr)->name); + } + break; + } + } + } + + return retval; +} + +int imagetool_save_subimage( + const char *file_name, + ulong file_data, + ulong file_len) +{ + int dfd; + + dfd = open(file_name, O_RDWR | O_CREAT | O_TRUNC | O_BINARY, + S_IRUSR | S_IWUSR); + if (dfd < 0) { + fprintf(stderr, "Can't open \"%s\": %s\n", + file_name, strerror(errno)); + return -1; + } + + if (write(dfd, (void *)file_data, file_len) != (ssize_t)file_len) { + fprintf(stderr, "Write error on \"%s\": %s\n", + file_name, strerror(errno)); + close(dfd); + return -1; + } + + close(dfd); + + return 0; +} + +int imagetool_get_filesize(struct image_tool_params *params, const char *fname) +{ + struct stat sbuf; + int fd; + + fd = open(fname, O_RDONLY | O_BINARY); + if (fd < 0) { + fprintf(stderr, "%s: Can't open %s: %s\n", + params->cmdname, fname, strerror(errno)); + return -1; + } + + if (fstat(fd, &sbuf) < 0) { + fprintf(stderr, "%s: Can't stat %s: %s\n", + params->cmdname, fname, strerror(errno)); + close(fd); + return -1; + } + close(fd); + + return sbuf.st_size; +} + +time_t imagetool_get_source_date( + const char *cmdname, + time_t fallback) +{ + char *source_date_epoch = getenv("SOURCE_DATE_EPOCH"); + + if (source_date_epoch == NULL) + return fallback; + + time_t time = (time_t) strtol(source_date_epoch, NULL, 10); + + if (gmtime(&time) == NULL) { + fprintf(stderr, "%s: SOURCE_DATE_EPOCH is not valid\n", + cmdname); + time = 0; + } + + return time; +} diff --git a/tools/u-boot-tools/imagetool.h b/tools/u-boot-tools/imagetool.h new file mode 100644 index 0000000..7147142 --- /dev/null +++ b/tools/u-boot-tools/imagetool.h @@ -0,0 +1,309 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * (C) Copyright 2013 + * + * Written by Guilherme Maciel Ferreira <guilherme.maciel.ferreira@gmail.com> + */ + +#ifndef _IMAGETOOL_H_ +#define _IMAGETOOL_H_ + +#include "os_support.h" +#include <errno.h> +#include <fcntl.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <time.h> +#include <unistd.h> +#include <u-boot/sha1.h> + +#include "fdt_host.h" + +#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) + +#define IH_ARCH_DEFAULT IH_ARCH_INVALID + +/* Information about a file that needs to be placed into the FIT */ +struct content_info { + struct content_info *next; + int type; /* File type (IH_TYPE_...) */ + const char *fname; +}; + +/* + * This structure defines all such variables those are initialized by + * mkimage and dumpimage main core and need to be referred by image + * type specific functions + */ +struct image_tool_params { + int dflag; + int eflag; + int fflag; + int iflag; + int lflag; + int pflag; + int vflag; + int xflag; + int skipcpy; + int os; + int arch; + int type; + int comp; + char *dtc; + unsigned int addr; + unsigned int ep; + char *imagename; + char *imagename2; + char *datafile; + char *imagefile; + char *cmdname; + const char *outfile; /* Output filename */ + const char *keydir; /* Directory holding private keys */ + const char *keydest; /* Destination .dtb for public key */ + const char *comment; /* Comment to add to signature node */ + int require_keys; /* 1 to mark signing keys as 'required' */ + int file_size; /* Total size of output file */ + int orig_file_size; /* Original size for file before padding */ + bool auto_its; /* Automatically create the .its file */ + int fit_image_type; /* Image type to put into the FIT */ + char *fit_ramdisk; /* Ramdisk file to include */ + struct content_info *content_head; /* List of files to include */ + struct content_info *content_tail; + bool external_data; /* Store data outside the FIT */ + bool quiet; /* Don't output text in normal operation */ + unsigned int external_offset; /* Add padding to external data */ + const char *engine_id; /* Engine to use for signing */ +}; + +/* + * image type specific variables and callback functions + */ +struct image_type_params { + /* name is an identification tag string for added support */ + char *name; + /* + * header size is local to the specific image type to be supported, + * mkimage core treats this as number of bytes + */ + uint32_t header_size; + /* Image type header pointer */ + void *hdr; + /* + * There are several arguments that are passed on the command line + * and are registered as flags in image_tool_params structure. + * This callback function can be used to check the passed arguments + * are in-lined with the image type to be supported + * + * Returns 1 if parameter check is successful + */ + int (*check_params) (struct image_tool_params *); + /* + * This function is used by list command (i.e. mkimage -l <filename>) + * image type verification code must be put here + * + * Returns 0 if image header verification is successful + * otherwise, returns respective negative error codes + */ + int (*verify_header) (unsigned char *, int, struct image_tool_params *); + /* Prints image information abstracting from image header */ + void (*print_header) (const void *); + /* + * The header or image contents need to be set as per image type to + * be generated using this callback function. + * further output file post processing (for ex. checksum calculation, + * padding bytes etc..) can also be done in this callback function. + */ + void (*set_header) (void *, struct stat *, int, + struct image_tool_params *); + /* + * This function is used by the command to retrieve a component + * (sub-image) from the image (i.e. dumpimage -i <image> -p <position> + * <sub-image-name>). + * Thus the code to extract a file from an image must be put here. + * + * Returns 0 if the file was successfully retrieved from the image, + * or a negative value on error. + */ + int (*extract_subimage)(void *, struct image_tool_params *); + /* + * Some image generation support for ex (default image type) supports + * more than one type_ids, this callback function is used to check + * whether input (-T <image_type>) is supported by registered image + * generation/list low level code + */ + int (*check_image_type) (uint8_t); + /* This callback function will be executed if fflag is defined */ + int (*fflag_handle) (struct image_tool_params *); + /* + * This callback function will be executed for variable size record + * It is expected to build this header in memory and return its length + * and a pointer to it by using image_type_params.header_size and + * image_type_params.hdr. The return value shall indicate if an + * additional padding should be used when copying the data image + * by returning the padding length. + */ + int (*vrec_header) (struct image_tool_params *, + struct image_type_params *); +}; + +/** + * imagetool_get_type() - find the image type params for a given image type + * + * It scans all registers image type supports + * checks the input type for each supported image type + * + * if successful, + * returns respective image_type_params pointer if success + * if input type_id is not supported by any of image_type_support + * returns NULL + */ +struct image_type_params *imagetool_get_type(int type); + +/* + * imagetool_verify_print_header() - verifies the image header + * + * Scan registered image types and verify the image_header for each + * supported image type. If verification is successful, this prints + * the respective header. + * + * @return 0 on success, negative if input image format does not match with + * any of supported image types + */ +int imagetool_verify_print_header( + void *ptr, + struct stat *sbuf, + struct image_type_params *tparams, + struct image_tool_params *params); + +/** + * imagetool_save_subimage - store data into a file + * @file_name: name of the destination file + * @file_data: data to be written + * @file_len: the amount of data to store + * + * imagetool_save_subimage() store file_len bytes of data pointed by file_data + * into the file name by file_name. + * + * returns: + * zero in case of success or a negative value if fail. + */ +int imagetool_save_subimage( + const char *file_name, + ulong file_data, + ulong file_len); + +/** + * imagetool_get_filesize() - Utility function to obtain the size of a file + * + * This function prints a message if an error occurs, showing the error that + * was obtained. + * + * @params: mkimage parameters + * @fname: filename to check + * @return size of file, or -ve value on error + */ +int imagetool_get_filesize(struct image_tool_params *params, const char *fname); + +/** + * imagetool_get_source_date() - Get timestamp for build output. + * + * Gets a timestamp for embedding it in a build output. If set + * SOURCE_DATE_EPOCH is used. Else the given fallback value is returned. Prints + * an error message if SOURCE_DATE_EPOCH contains an invalid value and returns + * 0. + * + * @cmdname: command name + * @fallback: timestamp to use if SOURCE_DATE_EPOCH isn't set + * @return timestamp based on SOURCE_DATE_EPOCH + */ +time_t imagetool_get_source_date( + const char *cmdname, + time_t fallback); + +/* + * There is a c file associated with supported image type low level code + * for ex. default_image.c, fit_image.c + */ + + +void pbl_load_uboot(int fd, struct image_tool_params *mparams); +int zynqmpbif_copy_image(int fd, struct image_tool_params *mparams); +int imx8image_copy_image(int fd, struct image_tool_params *mparams); +int imx8mimage_copy_image(int fd, struct image_tool_params *mparams); + +#define ___cat(a, b) a ## b +#define __cat(a, b) ___cat(a, b) + +/* we need some special handling for this host tool running eventually on + * Darwin. The Mach-O section handling is a bit different than ELF section + * handling. The differnces in detail are: + * a) we have segments which have sections + * b) we need a API call to get the respective section symbols */ +#if defined(__MACH__) +#include <mach-o/getsect.h> + +#define INIT_SECTION(name) do { \ + unsigned long name ## _len; \ + char *__cat(pstart_, name) = getsectdata("__TEXT", \ + #name, &__cat(name, _len)); \ + char *__cat(pstop_, name) = __cat(pstart_, name) + \ + __cat(name, _len); \ + __cat(__start_, name) = (void *)__cat(pstart_, name); \ + __cat(__stop_, name) = (void *)__cat(pstop_, name); \ + } while (0) +#define SECTION(name) __attribute__((section("__TEXT, " #name))) + +struct image_type_params **__start_image_type, **__stop_image_type; +#else +#define INIT_SECTION(name) /* no-op for ELF */ +#define SECTION(name) __attribute__((section(#name))) + +/* We construct a table of pointers in an ELF section (pointers generally + * go unpadded by gcc). ld creates boundary syms for us. */ +extern struct image_type_params *__start_image_type[], *__stop_image_type[]; +#endif /* __MACH__ */ + +#if !defined(__used) +# if __GNUC__ == 3 && __GNUC_MINOR__ < 3 +# define __used __attribute__((__unused__)) +# else +# define __used __attribute__((__used__)) +# endif +#endif + +#define U_BOOT_IMAGE_TYPE( \ + _id, \ + _name, \ + _header_size, \ + _header, \ + _check_params, \ + _verify_header, \ + _print_header, \ + _set_header, \ + _extract_subimage, \ + _check_image_type, \ + _fflag_handle, \ + _vrec_header \ + ) \ + static struct image_type_params __cat(image_type_, _id) = \ + { \ + .name = _name, \ + .header_size = _header_size, \ + .hdr = _header, \ + .check_params = _check_params, \ + .verify_header = _verify_header, \ + .print_header = _print_header, \ + .set_header = _set_header, \ + .extract_subimage = _extract_subimage, \ + .check_image_type = _check_image_type, \ + .fflag_handle = _fflag_handle, \ + .vrec_header = _vrec_header \ + }; \ + static struct image_type_params *SECTION(image_type) __used \ + __cat(image_type_ptr_, _id) = &__cat(image_type_, _id) + +#endif /* _IMAGETOOL_H_ */ diff --git a/tools/u-boot-tools/imagetool.o b/tools/u-boot-tools/imagetool.o new file mode 100644 index 0000000000000000000000000000000000000000..e0f874ea0d0516653eac14e7b18f1f94f0d7a6dd GIT binary patch literal 4688 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 literal 0 HcmV?d00001 diff --git a/tools/u-boot-tools/img2brec.sh b/tools/u-boot-tools/img2brec.sh new file mode 100755 index 0000000..0fcdba2 --- /dev/null +++ b/tools/u-boot-tools/img2brec.sh @@ -0,0 +1,388 @@ +#!/bin/sh + +# This script converts binary files (u-boot.bin) into so called +# bootstrap records that are accepted by Motorola's MC9328MX1/L +# (a.k.a. DragaonBall i.MX) in "Bootstrap Mode" +# +# The code for the SynchFlash programming routines is taken from +# Bootloader\Bin\SyncFlash\programBoot_b.txt contained in +# Motorolas LINUX_BSP_0_3_8.tar.gz +# +# The script could easily extended for AMD flash routines. +# +# 2004-06-23 - steven.scholz@imc-berlin.de + +################################################################################# +# From the posting to the U-Boot-Users mailing list, 23 Jun 2004: +# =============================================================== +# I just hacked a simple script that converts u-boot.bin into a text file +# containg processor init code, SynchFlash programming code and U-Boot data in +# form of so called b-records. +# +# This can be used to programm U-Boot into (Synch)Flash using the Bootstrap +# Mode of the MC9328MX1/L +# +# 0AFE1F3410202E2E2E000000002073756363656564/ +# 0AFE1F44102E0A0000206661696C656420210A0000/ +# 0AFE100000 +# ... +# MX1ADS Sync-flash Programming Utility v0.5 2002/08/21 +# +# Source address (stored in 0x0AFE0000): 0x0A000000 +# Target address (stored in 0x0AFE0004): 0x0C000000 +# Size (stored in 0x0AFE0008): 0x0001A320 +# +# Press any key to start programming ... +# Erasing ... +# Blank checking ... +# Programming ... +# Verifying flash ... succeed. +# +# Programming finished. +# +# So no need for a BDI2000 anymore... ;-) +# +# This is working on my MX1ADS eval board. Hope this could be useful for +# someone. +################################################################################# + +if [ "$#" -lt 1 -o "$#" -gt 2 ] ; then + echo "Usage: $0 infile [outfile]" >&2 + echo " $0 u-boot.bin [u-boot.brec]" >&2 + exit 1 +fi + +if [ "$#" -ge 1 ] ; then + INFILE=$1 +fi + +if [ ! -f $INFILE ] ; then + echo "Error: file '$INFILE' does not exist." >&2 + exit 1 +fi + +FILESIZE=`filesize $INFILE` + +output_init() +{ +echo "\ +******************************************** +* Initialize I/O Pad Driving Strength * +******************************************** +0021B80CC4000003AB +******************************************** +* Initialize SDRAM * +******************************************** +00221000C492120200 ; pre-charge command +08200000E4 ; special read + +00221000C4A2120200 ; auto-refresh command +08000000E4 ; 8 special read +08000000E4 ; 8 special read +08000000E4 ; 8 special read +08000000E4 ; 8 special read +08000000E4 ; 8 special read +08000000E4 ; 8 special read +08000000E4 ; 8 special read +08000000E4 ; 8 special read + +00221000C4B2120200 ; set mode register +08111800E4 ; special read + +00221000C482124200 ; set normal mode + " +} + +output_uboot() +{ +echo "\ +******************************************** +* U-Boot image as bootstrap records * +* will be stored in SDRAM at 0x0A000000 * +******************************************** + " + +cat $INFILE | \ +hexdump -v -e "\"0A0%05.5_ax10\" 16/1 \"%02x\"\"\r\n\"" | \ +tr [:lower:] [:upper:] +} + +output_flashprog() +{ +echo "\ +******************************************** +* Address of arguments to flashProg * +* ---------------------------------------- * +* Source : 0x0A000000 * +* Destination : 0x0C000000 * " + +# get the real size of the U-Boot image +printf "* Size : 0x%08X *\r\n" $FILESIZE +printf "********************************************\r\n" +printf "0AFE0000CC0A0000000C000000%08X\r\n" $FILESIZE + +#;0AFE0000CC0A0000000C00000000006000 + +echo "\ +******************************************** +* Flash Program * +******************************************** +0AFE10001008D09FE5AC0000EA00F0A0E1A42DFE0A +0AFE1010100080FE0A0DC0A0E100D82DE904B04CE2 +0AFE1020109820A0E318309FE5003093E5033082E0 +0AFE103010003093E5013003E2FF3003E20300A0E1 +0AFE10401000A81BE9A01DFE0A0DC0A0E100D82DE9 +0AFE10501004B04CE204D04DE20030A0E10D304BE5 +0AFE1060109820A0E330309FE5003093E5033082E0 +0AFE107010003093E5013903E2000053E3F7FFFF0A +0AFE1080104020A0E310309FE5003093E5032082E0 +0AFE1090100D305BE5003082E500A81BE9A01DFE0A +0AFE10A0100DC0A0E100D82DE904B04CE20000A0E1 +0AFE10B010D7FFFFEB0030A0E1FF3003E2000053E3 +0AFE10C010FAFFFF0A10309FE5003093E5003093E5 +0AFE10D010FF3003E20300A0E100A81BE9A01DFE0A +0AFE10E0100DC0A0E100D82DE904B04CE204D04DE2 +0AFE10F0100030A0E10D304BE50D305BE52332A0E1 +0AFE1100100E304BE50E305BE5090053E30300009A +0AFE1110100E305BE5373083E20E304BE5020000EA +0AFE1120100E305BE5303083E20E304BE50E305BE5 +0AFE1130100300A0E1C3FFFFEB0D305BE50F3003E2 +0AFE1140100E304BE50E305BE5090053E30300009A +0AFE1150100E305BE5373083E20E304BE5020000EA +0AFE1160100E305BE5303083E20E304BE50E305BE5 +0AFE1170100300A0E1B3FFFFEB00A81BE90DC0A0E1 +0AFE11801000D82DE904B04CE21CD04DE210000BE5 +0AFE11901014100BE518200BE588009FE5E50200EB +0AFE11A01010301BE51C300BE514301BE520300BE5 +0AFE11B0100030A0E324300BE524201BE518301BE5 +0AFE11C010030052E10000003A120000EA1C004BE2 +0AFE11D010002090E520104BE2003091E500C093E5 +0AFE11E010043083E2003081E5003092E5042082E2 +0AFE11F010002080E50C0053E10200000A0030A0E3 +0AFE12001028300BE5050000EA24301BE5043083E2 +0AFE12101024300BE5E7FFFFEA0130A0E328300BE5 +0AFE12201028001BE500A81BE9E81EFE0A0DC0A0E1 +0AFE12301000D82DE904B04CE214D04DE210000BE5 +0AFE12401014100BE56C009FE5BA0200EB10301BE5 +0AFE12501018300BE50030A0E31C300BE51C201BE5 +0AFE12601014301BE5030052E10000003A0D0000EA +0AFE12701018304BE2002093E5001092E5042082E2 +0AFE128010002083E5010071E30200000A0030A0E3 +0AFE12901020300BE5050000EA1C301BE5043083E2 +0AFE12A0101C300BE5ECFFFFEA0130A0E320300BE5 +0AFE12B01020001BE500A81BE9001FFE0A0DC0A0E1 +0AFE12C01000D82DE904B04CE224D04DE20130A0E3 +0AFE12D01024300BE5A4229FE58139A0E3023A83E2 +0AFE12E010003082E59820A0E390329FE5003093E5 +0AFE12F010033082E0003093E5023903E2000053E3 +0AFE1300100300001A74229FE58139A0E3033A83E2 +0AFE131010003082E568029FE5860200EBAF36A0E3 +0AFE1320100E3883E2003093E510300BE554029FE5 +0AFE133010800200EB10301BE5233CA0E1FF3003E2 +0AFE1340100300A0E165FFFFEB10301BE52338A0E1 +0AFE135010FF3003E20300A0E160FFFFEB10301BE5 +0AFE1360102334A0E1FF3003E20300A0E15BFFFFEB +0AFE13701010305BE50300A0E158FFFFEB0A00A0E3 +0AFE13801030FFFFEB0D00A0E32EFFFFEBAF36A0E3 +0AFE1390100E3883E2043083E2003093E514300BE5 +0AFE13A010E4019FE5630200EB14301BE5233CA0E1 +0AFE13B010FF3003E20300A0E148FFFFEB14301BE5 +0AFE13C0102338A0E1FF3003E20300A0E143FFFFEB +0AFE13D01014301BE52334A0E1FF3003E20300A0E1 +0AFE13E0103EFFFFEB14305BE50300A0E13BFFFFEB +0AFE13F0100A00A0E313FFFFEB0D00A0E311FFFFEB +0AFE140010AF36A0E30E3883E2083083E2003093E5 +0AFE14101018300BE574019FE5460200EB18301BE5 +0AFE142010233CA0E1FF3003E20300A0E12BFFFFEB +0AFE14301018301BE52338A0E1FF3003E20300A0E1 +0AFE14401026FFFFEB18301BE52334A0E1FF3003E2 +0AFE1450100300A0E121FFFFEB18305BE50300A0E1 +0AFE1460101EFFFFEB0A00A0E3F6FEFFEB0D00A0E3 +0AFE147010F4FEFFEBE6FEFFEB0030A0E1FF3003E2 +0AFE148010000053E30000001A020000EA03FFFFEB +0AFE1490102D004BE5F6FFFFEAF4009FE5250200EB +0AFE14A010FEFEFFEB2D004BE5CD0000EBC00000EB +0AFE14B010E0009FE51F0200EB18301BE528300BE5 +0AFE14C01014301BE52C300BE52C001BE5100100EB +0AFE14D01028301BE5013643E228300BE52C301BE5 +0AFE14E010013683E22C300BE528301BE5000053E3 +0AFE14F010F4FFFFCAAE0000EB14001BE518101BE5 +0AFE15001049FFFFEB0030A0E1FF3003E2000053E3 +0AFE151010E6FFFF0A80009FE5060200EB10001BE5 +0AFE15201014101BE518201BE5D00000EB10001BE5 +0AFE15301014101BE518201BE50FFFFFEB0030A0E1 +0AFE154010FF3003E2000053E30200000A4C009FE5 +0AFE155010F80100EB010000EA44009FE5F50100EB +0AFE156010930000EB3C009FE5F20100EB0000A0E3 +0AFE157010A4FEFFEB0030A0E30300A0E100A81BE9 +0AFE158010A01DFE0AA41DFE0AE01DFE0A0C1EFE0A +0AFE159010381EFE0A641EFE0A181FFE0A281FFE0A +0AFE15A0103C1FFE0A481FFE0AB41EFE0A0DC0A0E1 +0AFE15B01000D82DE904B04CE204D04DE210000BE5 +0AFE15C01010301BE5013043E210300BE5010073E3 +0AFE15D010FAFFFF1A00A81BE90DC0A0E100D82DE9 +0AFE15E01004B04CE208D04DE210000BE510301BE5 +0AFE15F01014300BE514301BE50300A0E100A81BE9 +0AFE1600100DC0A0E100D82DE904B04CE204D04DE2 +0AFE1610102228A0E3012A82E2042082E2E134A0E3 +0AFE162010023883E2033C83E2003082E50333A0E3 +0AFE163010053983E2003093E510300BE500A81BE9 +0AFE1640100DC0A0E100D82DE904B04CE204D04DE2 +0AFE1650102228A0E3012A82E2042082E29134A0E3 +0AFE166010023883E2033C83E2003082E5C136A0E3 +0AFE167010003093E510300BE52228A0E3012A82E2 +0AFE168010042082E2E134A0E3023883E2033C83E2 +0AFE169010003082E50333A0E3073983E20020A0E3 +0AFE16A010002083E52228A0E3012A82E2042082E2 +0AFE16B0108134A0E3023883E2033C83E2003082E5 +0AFE16C0100333A0E3003093E510300BE5CBFFFFEB +0AFE16D01010301BE50300A0E100A81BE90DC0A0E1 +0AFE16E01000D82DE904B04CE208D04DE2D3FFFFEB +0AFE16F0100030A0E110300BE510301BE5023503E2 +0AFE170010000053E30500000A10301BE5073703E2 +0AFE171010000053E30100000A10001BE5ADFFFFEB +0AFE17201010301BE5803003E2000053E30500000A +0AFE17301010301BE51C3003E2000053E30100000A +0AFE17401010001BE5A3FFFFEB10201BE50235A0E3 +0AFE175010803083E2030052E10200001A0130A0E3 +0AFE17601014300BE5010000EA0030A0E314300BE5 +0AFE17701014001BE500A81BE90DC0A0E100D82DE9 +0AFE17801004B04CE204D04DE22228A0E3012A82E2 +0AFE179010042082E29134A0E3023883E2033C83E2 +0AFE17A010003082E5C136A0E3003093E510300BE5 +0AFE17B01000A81BE90DC0A0E100D82DE904B04CE2 +0AFE17C010ECFFFFEB2228A0E3012A82E2042082E2 +0AFE17D0108134A0E3023883E2033C83E2003082E5 +0AFE17E01000A81BE90DC0A0E100D82DE904B04CE2 +0AFE17F01004D04DE22228A0E3012A82E2042082E2 +0AFE1800102238A0E3013A83E2043083E2003093E5 +0AFE181010023183E3003082E52228A0E3012A82E2 +0AFE1820102238A0E3013A83E2003093E5023183E3 +0AFE183010003082E5FA0FA0E35BFFFFEB2228A0E3 +0AFE184010012A82E2042082E2B134A0E3023883E2 +0AFE185010033C83E2003082E50333A0E3233983E2 +0AFE186010033B83E2003093E510300BE500A81BE9 +0AFE1870100DC0A0E100D82DE904B04CE21CD04DE2 +0AFE18801010000BE514100BE518200BE50030A0E3 +0AFE1890101C300BE51C201BE518301BE5030052E1 +0AFE18A0100000003A190000EAB2FFFFEB2228A0E3 +0AFE18B010012A82E2042082E2F134A0E3023883E2 +0AFE18C010033C83E2003082E514201BE51C301BE5 +0AFE18D010031082E010201BE51C301BE5033082E0 +0AFE18E010003093E5003081E57BFFFFEB0030A0E1 +0AFE18F010FF3003E2000053E3FAFFFF0AACFFFFEB +0AFE1900101C301BE5043083E21C300BE5E0FFFFEA +0AFE19101000A81BE90DC0A0E100D82DE904B04CE2 +0AFE1920100CD04DE210000BE52228A0E3012A82E2 +0AFE193010042082E28134A0E3023883E2033C83E2 +0AFE194010003082E510301BE5003093E514300BE5 +0AFE1950102228A0E3012A82E2042082E29134A0E3 +0AFE196010023883E2033C83E2003082E510301BE5 +0AFE197010003093E518300BE52228A0E3012A82E2 +0AFE198010042082E2E134A0E3023883E2033C83E2 +0AFE199010003082E50229A0E310301BE5032082E0 +0AFE19A0100030A0E3003082E52228A0E3012A82E2 +0AFE19B010042082E28134A0E3023883E2033C83E2 +0AFE19C010003082E510201BE50D3AA0E3D03083E2 +0AFE19D010033883E1003082E53FFFFFEB0030A0E1 +0AFE19E010FF3003E2000053E3FAFFFF0A70FFFFEB +0AFE19F01000A81BE90DC0A0E100D82DE904B04CE2 +0AFE1A00105CFFFFEB2228A0E3012A82E2042082E2 +0AFE1A1010E134A0E3023883E2033C83E2003082E5 +0AFE1A20100333A0E3033983E20020A0E3002083E5 +0AFE1A30102228A0E3012A82E2042082E28134A0E3 +0AFE1A4010023883E2033C83E2003082E50323A0E3 +0AFE1A5010032982E20339A0E3C03083E2033883E1 +0AFE1A6010003082E500A81BE90DC0A0E100D82DE9 +0AFE1A701004B04CE23FFFFFEB2228A0E3012A82E2 +0AFE1A8010042082E2E134A0E3023883E2033C83E2 +0AFE1A9010003082E50333A0E30A3983E20020A0E3 +0AFE1AA010002083E52228A0E3012A82E2042082E2 +0AFE1AB0108134A0E3023883E2033C83E2003082E5 +0AFE1AC0100323A0E30A2982E20339A0E3C03083E2 +0AFE1AD010033883E1003082E500A81BE90DC0A0E1 +0AFE1AE01000D82DE904B04CE28729A0E3222E82E2 +0AFE1AF0108739A0E3223E83E2003093E51E3CC3E3 +0AFE1B0010003082E58729A0E38E2F82E28739A0E3 +0AFE1B10108E3F83E2003093E51E3CC3E3003082E5 +0AFE1B20108139A0E3823D83E20520A0E3002083E5 +0AFE1B30108129A0E3822D82E2042082E20139A0E3 +0AFE1B4010273083E2003082E58139A0E3823D83E2 +0AFE1B50100C3083E20120A0E3002083E58129A0E3 +0AFE1B6010822D82E2102082E22A3DA0E3013083E2 +0AFE1B7010003082E58139A0E3823D83E2243083E2 +0AFE1B80100F20A0E3002083E58139A0E3823D83E2 +0AFE1B9010283083E28A20A0E3002083E58139A0E3 +0AFE1BA010823D83E22C3083E20820A0E3002083E5 +0AFE1BB01000A81BE90DC0A0E100D82DE904B04CE2 +0AFE1BC0108139A0E3823D83E2183083E2003093E5 +0AFE1BD010013003E2FF3003E20300A0E100A81BE9 +0AFE1BE0100DC0A0E100D82DE904B04CE204D04DE2 +0AFE1BF0100030A0E10D304BE58139A0E3823D83E2 +0AFE1C0010183083E2003093E5013903E2000053E3 +0AFE1C1010F8FFFF0A8139A0E3813D83E20D205BE5 +0AFE1C2010002083E50D305BE50A0053E30A00001A +0AFE1C30108139A0E3823D83E2183083E2003093E5 +0AFE1C4010013903E2000053E3F8FFFF0A8139A0E3 +0AFE1C5010813D83E20D20A0E3002083E500A81BE9 +0AFE1C60100DC0A0E100D82DE904B04CE20000A0E1 +0AFE1C7010CFFFFFEB0030A0E1FF3003E2000053E3 +0AFE1C8010FAFFFF0A8139A0E3023A83E2003093E5 +0AFE1C9010FF3003E20300A0E100A81BE90DC0A0E1 +0AFE1CA01000D82DE904B04CE204D04DE20030A0E1 +0AFE1CB0100D304BE50D305BE52332A0E10E304BE5 +0AFE1CC0100E305BE5090053E30300009A0E305BE5 +0AFE1CD010373083E20E304BE5020000EA0E305BE5 +0AFE1CE010303083E20E304BE50E305BE50300A0E1 +0AFE1CF010BAFFFFEB0D305BE50F3003E20E304BE5 +0AFE1D00100E305BE5090053E30300009A0E305BE5 +0AFE1D1010373083E20E304BE5020000EA0E305BE5 +0AFE1D2010303083E20E304BE50E305BE50300A0E1 +0AFE1D3010AAFFFFEB00A81BE90DC0A0E100D82DE9 +0AFE1D401004B04CE204D04DE210000BE510301BE5 +0AFE1D50100030D3E5000053E30000001A080000EA +0AFE1D601010104BE2003091E50320A0E10020D2E5 +0AFE1D7010013083E2003081E50200A0E197FFFFEB +0AFE1D8008F1FFFFEA00A81BE9 +0AFE1DA4100A0D4D58314144532053796E632D666C +0AFE1DB4106173682050726F6772616D6D696E6720 +0AFE1DC4105574696C6974792076302E3520323030 +0AFE1DD410322F30382F32310A0D000000536F7572 +0AFE1DE41063652061646472657373202873746F72 +0AFE1DF410656420696E2030783041464530303030 +0AFE1E0410293A2030780000005461726765742061 +0AFE1E1410646472657373202873746F7265642069 +0AFE1E24106E2030783041464530303034293A2030 +0AFE1E34107800000053697A652020202020202020 +0AFE1E44102020202873746F72656420696E203078 +0AFE1E54103041464530303038293A203078000000 +0AFE1E6410507265737320616E79206B657920746F +0AFE1E74102073746172742070726F6772616D6D69 +0AFE1E84106E67202E2E2E00000A0D45726173696E +0AFE1E94106720666C617368202E2E2E000A0D5072 +0AFE1EA4106F6772616D6D696E67202E2E2E000000 +0AFE1EB4100A0D50726F6772616D6D696E67206669 +0AFE1EC4106E69736865642E0A0D50726573732027 +0AFE1ED410612720746F20636F6E74696E7565202E +0AFE1EE4102E2E2E000A0D566572696679696E6720 +0AFE1EF410666C617368202E2E2E0000000A0D426C +0AFE1F0410616E6B20636865636B696E67202E2E2E +0AFE1F1410000000000A45726173696E67202E2E2E +0AFE1F2410000000000A50726F6772616D6D696E67 +0AFE1F3410202E2E2E000000002073756363656564 +0AFE1F44102E0A0000206661696C656420210A0000 +0AFE100000 + " +} + +######################################################### + +if [ "$#" -eq 2 ] ; then + output_init > $2 + output_uboot >> $2 + output_flashprog >> $2 +else + output_init; + output_uboot; + output_flashprog; +fi diff --git a/tools/u-boot-tools/img2srec b/tools/u-boot-tools/img2srec new file mode 100755 index 0000000000000000000000000000000000000000..c5b3dbf1f3ab3770fd5ca4d8a17251e8dffbced2 GIT binary patch literal 17280 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 W>(P{egc*K<I>ey52~7hUmjM96qHTZx literal 0 HcmV?d00001 diff --git a/tools/u-boot-tools/img2srec.c b/tools/u-boot-tools/img2srec.c new file mode 100644 index 0000000..75efd76 --- /dev/null +++ b/tools/u-boot-tools/img2srec.c @@ -0,0 +1,372 @@ +/************************************************************************* +| COPYRIGHT (c) 2000 BY ABATRON AG +|************************************************************************* +| +| PROJECT NAME: Linux Image to S-record Conversion Utility +| FILENAME : img2srec.c +| +| COMPILER : GCC +| +| TARGET OS : LINUX / UNIX +| TARGET HW : - +| +| PROGRAMMER : Abatron / RD +| CREATION : 07.07.00 +| +|************************************************************************* +| +| DESCRIPTION : +| +| Utility to convert a Linux Boot Image to S-record: +| ================================================== +| +| This command line utility can be used to convert a Linux boot image +| (zimage.initrd) to S-Record format used for flash programming. +| This conversion takes care of the special sections "IMAGE" and INITRD". +| +| img2srec [-o offset] image > image.srec +| +| +| Build the utility: +| ================== +| +| To build the utility use GCC as follows: +| +| gcc img2srec.c -o img2srec +| +| +|************************************************************************* +| +| +| UPDATES : +| +| DATE NAME CHANGES +| ----------------------------------------------------------- +| Latest update +| +| 07.07.00 aba Initial release +| +|*************************************************************************/ + +/************************************************************************* +| INCLUDES +|*************************************************************************/ + +#include "os_support.h" +#include <stdbool.h> +#include <stddef.h> +#include <stdio.h> +#include <stdlib.h> +#include <ctype.h> +#include <string.h> +#include <elf.h> +#include <unistd.h> +#include <errno.h> + +/************************************************************************* +| FUNCTIONS +|*************************************************************************/ + +static char* ExtractHex (uint32_t* value, char* getPtr) +{ + uint32_t num; + uint32_t digit; + uint8_t c; + + while (*getPtr == ' ') getPtr++; + num = 0; + for (;;) { + c = *getPtr; + if ((c >= '0') && (c <= '9')) digit = (uint32_t)(c - '0'); + else if ((c >= 'A') && (c <= 'F')) digit = (uint32_t)(c - 'A' + 10); + else if ((c >= 'a') && (c <= 'f')) digit = (uint32_t)(c - 'a' + 10); + else break; + num <<= 4; + num += digit; + getPtr++; + } /* for */ + *value = num; + return getPtr; +} /* ExtractHex */ + +static char* ExtractDecimal (uint32_t* value, char* getPtr) +{ + uint32_t num; + uint32_t digit; + uint8_t c; + + while (*getPtr == ' ') getPtr++; + num = 0; + for (;;) { + c = *getPtr; + if ((c >= '0') && (c <= '9')) digit = (uint32_t)(c - '0'); + else break; + num *= 10; + num += digit; + getPtr++; + } /* for */ + *value = num; + return getPtr; +} /* ExtractDecimal */ + + +static void ExtractNumber (uint32_t* value, char* getPtr) +{ + bool neg = false; + + while (*getPtr == ' ') getPtr++; + if (*getPtr == '-') { + neg = true; + getPtr++; + } /* if */ + if ((*getPtr == '0') && ((*(getPtr+1) == 'x') || (*(getPtr+1) == 'X'))) { + getPtr +=2; + (void)ExtractHex(value, getPtr); + } /* if */ + else { + (void)ExtractDecimal(value, getPtr); + } /* else */ + if (neg) *value = -(*value); +} /* ExtractNumber */ + + +static uint8_t* ExtractWord(uint16_t* value, uint8_t* buffer) +{ + uint16_t x; + x = (uint16_t)*buffer++; + x = (x<<8) + (uint16_t)*buffer++; + *value = x; + return buffer; +} /* ExtractWord */ + + +static uint8_t* ExtractLong(uint32_t* value, uint8_t* buffer) +{ + uint32_t x; + x = (uint32_t)*buffer++; + x = (x<<8) + (uint32_t)*buffer++; + x = (x<<8) + (uint32_t)*buffer++; + x = (x<<8) + (uint32_t)*buffer++; + *value = x; + return buffer; +} /* ExtractLong */ + + +static uint8_t* ExtractBlock(uint16_t count, uint8_t* data, uint8_t* buffer) +{ + while (count--) *data++ = *buffer++; + return buffer; +} /* ExtractBlock */ + + +static char* WriteHex(char* pa, uint8_t value, uint16_t* pCheckSum) +{ + uint16_t temp; + + static char ByteToHex[] = "0123456789ABCDEF"; + + *pCheckSum += value; + temp = value / 16; + *pa++ = ByteToHex[temp]; + temp = value % 16; + *pa++ = ByteToHex[temp]; + return pa; +} + + +static char* BuildSRecord(char* pa, uint16_t sType, uint32_t addr, + const uint8_t* data, int nCount) +{ + uint16_t addrLen; + uint16_t sRLen; + uint16_t checkSum; + uint16_t i; + + switch (sType) { + case 0: + case 1: + case 9: + addrLen = 2; + break; + case 2: + case 8: + addrLen = 3; + break; + case 3: + case 7: + addrLen = 4; + break; + default: + return pa; + } /* switch */ + + *pa++ = 'S'; + *pa++ = (char)(sType + '0'); + sRLen = addrLen + nCount + 1; + checkSum = 0; + pa = WriteHex(pa, (uint8_t)sRLen, &checkSum); + + /* Write address field */ + for (i = 1; i <= addrLen; i++) { + pa = WriteHex(pa, (uint8_t)(addr >> (8 * (addrLen - i))), &checkSum); + } /* for */ + + /* Write code/data fields */ + for (i = 0; i < nCount; i++) { + pa = WriteHex(pa, *data++, &checkSum); + } /* for */ + + /* Write checksum field */ + checkSum = ~checkSum; + pa = WriteHex(pa, (uint8_t)checkSum, &checkSum); + *pa++ = '\0'; + return pa; +} + + +static void ConvertELF(char* fileName, uint32_t loadOffset) +{ + FILE* file; + int i; + int rxCount; + uint8_t rxBlock[1024]; + uint32_t loadSize; + uint32_t firstAddr; + uint32_t loadAddr; + uint32_t loadDiff = 0; + Elf32_Ehdr elfHeader; + Elf32_Shdr sectHeader[32]; + uint8_t* getPtr; + char srecLine[128]; + char *hdr_name; + + + /* open file */ + if ((file = fopen(fileName,"rb")) == NULL) { + fprintf (stderr, "Can't open %s: %s\n", fileName, strerror(errno)); + return; + } /* if */ + + /* read ELF header */ + rxCount = fread(rxBlock, 1, sizeof elfHeader, file); + getPtr = ExtractBlock(sizeof elfHeader.e_ident, elfHeader.e_ident, rxBlock); + getPtr = ExtractWord(&elfHeader.e_type, getPtr); + getPtr = ExtractWord(&elfHeader.e_machine, getPtr); + getPtr = ExtractLong((uint32_t *)&elfHeader.e_version, getPtr); + getPtr = ExtractLong((uint32_t *)&elfHeader.e_entry, getPtr); + getPtr = ExtractLong((uint32_t *)&elfHeader.e_phoff, getPtr); + getPtr = ExtractLong((uint32_t *)&elfHeader.e_shoff, getPtr); + getPtr = ExtractLong((uint32_t *)&elfHeader.e_flags, getPtr); + getPtr = ExtractWord(&elfHeader.e_ehsize, getPtr); + getPtr = ExtractWord(&elfHeader.e_phentsize, getPtr); + getPtr = ExtractWord(&elfHeader.e_phnum, getPtr); + getPtr = ExtractWord(&elfHeader.e_shentsize, getPtr); + getPtr = ExtractWord(&elfHeader.e_shnum, getPtr); + getPtr = ExtractWord(&elfHeader.e_shstrndx, getPtr); + if ( (rxCount != sizeof elfHeader) + || (elfHeader.e_ident[0] != ELFMAG0) + || (elfHeader.e_ident[1] != ELFMAG1) + || (elfHeader.e_ident[2] != ELFMAG2) + || (elfHeader.e_ident[3] != ELFMAG3) + || (elfHeader.e_type != ET_EXEC) + ) { + fclose(file); + fprintf (stderr, "*** illegal file format\n"); + return; + } /* if */ + + /* read all section headers */ + fseek(file, elfHeader.e_shoff, SEEK_SET); + for (i = 0; i < elfHeader.e_shnum; i++) { + rxCount = fread(rxBlock, 1, sizeof sectHeader[0], file); + getPtr = ExtractLong((uint32_t *)§Header[i].sh_name, rxBlock); + getPtr = ExtractLong((uint32_t *)§Header[i].sh_type, getPtr); + getPtr = ExtractLong((uint32_t *)§Header[i].sh_flags, getPtr); + getPtr = ExtractLong((uint32_t *)§Header[i].sh_addr, getPtr); + getPtr = ExtractLong((uint32_t *)§Header[i].sh_offset, getPtr); + getPtr = ExtractLong((uint32_t *)§Header[i].sh_size, getPtr); + getPtr = ExtractLong((uint32_t *)§Header[i].sh_link, getPtr); + getPtr = ExtractLong((uint32_t *)§Header[i].sh_info, getPtr); + getPtr = ExtractLong((uint32_t *)§Header[i].sh_addralign, getPtr); + getPtr = ExtractLong((uint32_t *)§Header[i].sh_entsize, getPtr); + if (rxCount != sizeof sectHeader[0]) { + fclose(file); + fprintf (stderr, "*** illegal file format\n"); + return; + } /* if */ + } /* for */ + + if ((hdr_name = strrchr(fileName, '/')) == NULL) { + hdr_name = fileName; + } else { + ++hdr_name; + } + /* write start record */ + (void)BuildSRecord(srecLine, 0, 0, (uint8_t *)hdr_name, strlen(hdr_name)); + printf("%s\r\n",srecLine); + + /* write data records */ + firstAddr = ~0; + loadAddr = 0; + for (i = 0; i < elfHeader.e_shnum; i++) { + if ( (sectHeader[i].sh_type == SHT_PROGBITS) + && (sectHeader[i].sh_size != 0) + ) { + loadSize = sectHeader[i].sh_size; + if (sectHeader[i].sh_flags != 0) { + loadAddr = sectHeader[i].sh_addr; + loadDiff = loadAddr - sectHeader[i].sh_offset; + } /* if */ + else { + loadAddr = sectHeader[i].sh_offset + loadDiff; + } /* else */ + + if (loadAddr < firstAddr) + firstAddr = loadAddr; + + /* build s-records */ + loadSize = sectHeader[i].sh_size; + fseek(file, sectHeader[i].sh_offset, SEEK_SET); + while (loadSize) { + rxCount = fread(rxBlock, 1, (loadSize > 32) ? 32 : loadSize, file); + if (rxCount < 0) { + fclose(file); + fprintf (stderr, "*** illegal file format\n"); + return; + } /* if */ + (void)BuildSRecord(srecLine, 3, loadAddr + loadOffset, rxBlock, rxCount); + loadSize -= rxCount; + loadAddr += rxCount; + printf("%s\r\n",srecLine); + } /* while */ + } /* if */ + } /* for */ + + /* add end record */ + (void)BuildSRecord(srecLine, 7, firstAddr + loadOffset, 0, 0); + printf("%s\r\n",srecLine); + fclose(file); +} /* ConvertELF */ + + +/************************************************************************* +| MAIN +|*************************************************************************/ + +int main( int argc, char *argv[ ]) +{ + uint32_t offset; + + if (argc == 2) { + ConvertELF(argv[1], 0); + } /* if */ + else if ((argc == 4) && (strcmp(argv[1], "-o") == 0)) { + ExtractNumber(&offset, argv[2]); + ConvertELF(argv[3], offset); + } /* if */ + else { + fprintf (stderr, "Usage: img2srec [-o offset] <image>\n"); + } /* if */ + + return 0; +} /* main */ diff --git a/tools/u-boot-tools/imx8image.c b/tools/u-boot-tools/imx8image.c new file mode 100644 index 0000000..0d856b9 --- /dev/null +++ b/tools/u-boot-tools/imx8image.c @@ -0,0 +1,992 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2018 NXP + * + * Peng Fan <peng.fan@nxp.com> + */ + +#include "imx8image.h" + +static int p_idx; +static int sector_size; +static soc_type_t soc; +static int container = -1; +static int32_t core_type = CFG_CORE_INVALID; +static bool emmc_fastboot; +static image_t param_stack[IMG_STACK_SIZE]; +static uint8_t fuse_version; +static uint16_t sw_version; +static uint32_t custom_partition; +static uint32_t scfw_flags; + +int imx8image_check_params(struct image_tool_params *params) +{ + return 0; +} + +static void imx8image_set_header(void *ptr, struct stat *sbuf, int ifd, + struct image_tool_params *params) +{ +} + +static void imx8image_print_header(const void *ptr) +{ +} + +static int imx8image_check_image_types(uint8_t type) +{ + return (type == IH_TYPE_IMX8IMAGE) ? EXIT_SUCCESS : EXIT_FAILURE; +} + +static table_entry_t imx8image_cmds[] = { + {CMD_BOOT_FROM, "BOOT_FROM", "boot command", }, + {CMD_FUSE_VERSION, "FUSE_VERSION", "fuse version", }, + {CMD_SW_VERSION, "SW_VERSION", "sw version", }, + {CMD_MSG_BLOCK, "MSG_BLOCK", "msg block", }, + {CMD_FILEOFF, "FILEOFF", "fileoff", }, + {CMD_FLAG, "FLAG", "flag", }, + {CMD_APPEND, "APPEND", "append a container", }, + {CMD_PARTITION, "PARTITION", "new partition", }, + {CMD_SOC_TYPE, "SOC_TYPE", "soc type", }, + {CMD_CONTAINER, "CONTAINER", "new container", }, + {CMD_IMAGE, "IMAGE", "new image", }, + {CMD_DATA, "DATA", "new data", }, + {-1, "", "", }, +}; + +static table_entry_t imx8image_core_entries[] = { + {CFG_SCU, "SCU", "scu core", }, + {CFG_M40, "M40", "M4 core 0", }, + {CFG_M41, "M41", "M4 core 1", }, + {CFG_A35, "A35", "A35 core", }, + {CFG_A53, "A53", "A53 core", }, + {CFG_A72, "A72", "A72 core", }, + {-1, "", "", }, +}; + +static table_entry_t imx8image_sector_size[] = { + {0x400, "sd", "sd/emmc",}, + {0x400, "emmc_fastboot", "emmc fastboot",}, + {0x400, "fspi", "flexspi", }, + {0x1000, "nand_4k", "nand 4K", }, + {0x2000, "nand_8k", "nand 8K", }, + {0x4000, "nand_16k", "nand 16K", }, + {-1, "", "Invalid", }, +}; + +static void parse_cfg_cmd(image_t *param_stack, int32_t cmd, char *token, + char *name, int lineno) +{ + switch (cmd) { + case CMD_BOOT_FROM: + sector_size = get_table_entry_id(imx8image_sector_size, + "imximage boot option", + token); + if (!strncmp("emmc_fastboot", token, 13)) + emmc_fastboot = true; + break; + case CMD_FUSE_VERSION: + fuse_version = (uint8_t)(strtoll(token, NULL, 0) & 0xFF); + break; + case CMD_SW_VERSION: + sw_version = (uint8_t)(strtoll(token, NULL, 0) & 0xFFFF); + break; + case CMD_FILEOFF: + param_stack[p_idx].option = FILEOFF; + param_stack[p_idx++].dst = (uint32_t)strtoll(token, NULL, 0); + break; + case CMD_MSG_BLOCK: + param_stack[p_idx].option = MSG_BLOCK; + param_stack[p_idx].filename = token; + break; + case CMD_FLAG: + param_stack[p_idx].option = FLAG; + param_stack[p_idx++].entry = (uint32_t)strtoll(token, NULL, 0); + break; + case CMD_APPEND: + param_stack[p_idx].option = APPEND; + param_stack[p_idx++].filename = token; + break; + case CMD_PARTITION: + param_stack[p_idx].option = PARTITION; + param_stack[p_idx++].entry = (uint32_t)strtoll(token, NULL, 0); + break; + case CMD_SOC_TYPE: + if (!strncmp(token, "IMX8QX", 6)) { + soc = QX; + } else if (!strncmp(token, "IMX8QM", 6)) { + soc = QM; + } else { + fprintf(stderr, "Unknown CMD_SOC_TYPE"); + exit(EXIT_FAILURE); + } + break; + case CMD_IMAGE: + case CMD_DATA: + core_type = get_table_entry_id(imx8image_core_entries, + "imx8image core entries", + token); + if (core_type < 0) { + fprintf(stderr, "Wrong IMAGE core_type %s\n", token); + exit(EXIT_FAILURE); + } + break; + default: + break; + } +} + +static void parse_cfg_fld(image_t *param_stack, int32_t *cmd, char *token, + char *name, int lineno, int fld) +{ + switch (fld) { + case CFG_COMMAND: + *cmd = get_table_entry_id(imx8image_cmds, "imx8image cmds", + token); + if (*cmd < 0) { + fprintf(stderr, "Error: %s[%d] - Invalid command (%s)\n", name, lineno, token); + exit(EXIT_FAILURE); + } + + if (*cmd == CMD_CONTAINER) { + fprintf(stdout, "New Container: \t%d\n", ++container); + param_stack[p_idx++].option = NEW_CONTAINER; + } + break; + case CFG_CORE_TYPE: + parse_cfg_cmd(param_stack, *cmd, token, name, lineno); + break; + case CFG_IMAGE_NAME: + if (*cmd == CMD_MSG_BLOCK) { + if (!strncmp(token, "fuse", 4)) { + param_stack[p_idx].ext = SC_R_OTP; + } else if (!strncmp(token, "debug", 5)) { + param_stack[p_idx].ext = SC_R_DEBUG; + } else if (!strncmp(token, "field", 5)) { + param_stack[p_idx].ext = SC_R_ROM_0; + } else { + fprintf(stderr, "MSG type not found %s\n", token); + exit(EXIT_FAILURE); + } + break; + } + switch (core_type) { + case CFG_SCU: + param_stack[p_idx].option = SCFW; + param_stack[p_idx++].filename = token; + break; + case CFG_M40: + param_stack[p_idx].option = M40; + param_stack[p_idx].ext = 0; + param_stack[p_idx].filename = token; + break; + case CFG_M41: + param_stack[p_idx].option = M41; + param_stack[p_idx].ext = 1; + param_stack[p_idx].filename = token; + break; + case CFG_A35: + param_stack[p_idx].ext = CORE_CA35; + param_stack[p_idx].option = + (*cmd == CMD_DATA) ? DATA : AP; + param_stack[p_idx].filename = token; + break; + case CFG_A53: + param_stack[p_idx].ext = CORE_CA53; + param_stack[p_idx].option = + (*cmd == CMD_DATA) ? DATA : AP; + param_stack[p_idx].filename = token; + break; + case CFG_A72: + param_stack[p_idx].ext = CORE_CA72; + param_stack[p_idx].option = + (*cmd == CMD_DATA) ? DATA : AP; + param_stack[p_idx].filename = token; + break; + } + break; + case CFG_LOAD_ADDR: + if (*cmd == CMD_MSG_BLOCK) { + param_stack[p_idx++].entry = + (uint32_t)strtoll(token, NULL, 0); + break; + } + switch (core_type) { + case CFG_SCU: + break; + case CFG_M40: + case CFG_M41: + case CFG_A35: + case CFG_A53: + case CFG_A72: + param_stack[p_idx++].entry = + (uint32_t)strtoll(token, NULL, 0); + break; + } + default: + break; + } +} + +static uint32_t parse_cfg_file(image_t *param_stack, char *name) +{ + FILE *fd = NULL; + char *line = NULL; + char *token, *saveptr1, *saveptr2; + int lineno = 0; + int fld; + size_t len; + int32_t cmd; + + fd = fopen(name, "r"); + if (fd == 0) { + fprintf(stderr, "Error: %s - Can't open cfg file\n", name); + exit(EXIT_FAILURE); + } + + /* + * Very simple parsing, line starting with # are comments + * and are dropped + */ + while ((getline(&line, &len, fd)) > 0) { + lineno++; + + token = strtok_r(line, "\r\n", &saveptr1); + if (!token) + continue; + + /* Check inside the single line */ + for (fld = CFG_COMMAND, cmd = CFG_INVALID, + line = token; ; line = NULL, fld++) { + token = strtok_r(line, " \t", &saveptr2); + if (!token) + break; + + /* Drop all text starting with '#' as comments */ + if (token[0] == '#') + break; + + parse_cfg_fld(param_stack, &cmd, token, name, lineno, + fld); + } + } + + return 0; +} + +static void check_file(struct stat *sbuf, char *filename) +{ + int tmp_fd = open(filename, O_RDONLY | O_BINARY); + + if (tmp_fd < 0) { + fprintf(stderr, "%s: Can't open: %s\n", + filename, strerror(errno)); + exit(EXIT_FAILURE); + } + + if (fstat(tmp_fd, sbuf) < 0) { + fprintf(stderr, "%s: Can't stat: %s\n", + filename, strerror(errno)); + exit(EXIT_FAILURE); + } + + close(tmp_fd); +} + +static void copy_file_aligned(int ifd, const char *datafile, int offset, + int align) +{ + int dfd; + struct stat sbuf; + unsigned char *ptr; + uint8_t zeros[0x4000]; + int size; + int ret; + + if (align > 0x4000) { + fprintf(stderr, "Wrong alignment requested %d\n", align); + exit(EXIT_FAILURE); + } + + memset(zeros, 0, sizeof(zeros)); + + dfd = open(datafile, O_RDONLY | O_BINARY); + if (dfd < 0) { + fprintf(stderr, "Can't open %s: %s\n", + datafile, strerror(errno)); + exit(EXIT_FAILURE); + } + + if (fstat(dfd, &sbuf) < 0) { + fprintf(stderr, "Can't stat %s: %s\n", + datafile, strerror(errno)); + exit(EXIT_FAILURE); + } + + if (sbuf.st_size == 0) + goto close; + + ptr = mmap(0, sbuf.st_size, PROT_READ, MAP_SHARED, dfd, 0); + if (ptr == MAP_FAILED) { + fprintf(stderr, "Can't read %s: %s\n", + datafile, strerror(errno)); + exit(EXIT_FAILURE); + } + + size = sbuf.st_size; + ret = lseek(ifd, offset, SEEK_SET); + if (ret < 0) { + fprintf(stderr, "%s: lseek error %s\n", + __func__, strerror(errno)); + exit(EXIT_FAILURE); + } + + if (write(ifd, ptr, size) != size) { + fprintf(stderr, "Write error %s\n", strerror(errno)); + exit(EXIT_FAILURE); + } + + align = ALIGN(size, align) - size; + + if (write(ifd, (char *)&zeros, align) != align) { + fprintf(stderr, "Write error: %s\n", strerror(errno)); + exit(EXIT_FAILURE); + } + + munmap((void *)ptr, sbuf.st_size); +close: + close(dfd); +} + +static void copy_file (int ifd, const char *datafile, int pad, int offset) +{ + int dfd; + struct stat sbuf; + unsigned char *ptr; + int tail; + int zero = 0; + uint8_t zeros[4096]; + int size, ret; + + memset(zeros, 0, sizeof(zeros)); + + dfd = open(datafile, O_RDONLY | O_BINARY); + if (dfd < 0) { + fprintf(stderr, "Can't open %s: %s\n", + datafile, strerror(errno)); + exit(EXIT_FAILURE); + } + + if (fstat(dfd, &sbuf) < 0) { + fprintf(stderr, "Can't stat %s: %s\n", + datafile, strerror(errno)); + exit(EXIT_FAILURE); + } + + if (sbuf.st_size == 0) + goto close; + + ptr = mmap(0, sbuf.st_size, PROT_READ, MAP_SHARED, dfd, 0); + if (ptr == MAP_FAILED) { + fprintf(stderr, "Can't read %s: %s\n", + datafile, strerror(errno)); + exit(EXIT_FAILURE); + } + + size = sbuf.st_size; + ret = lseek(ifd, offset, SEEK_SET); + if (ret < 0) { + fprintf(stderr, "%s: lseek error %s\n", + __func__, strerror(errno)); + exit(EXIT_FAILURE); + } + + if (write(ifd, ptr, size) != size) { + fprintf(stderr, "Write error %s\n", + strerror(errno)); + exit(EXIT_FAILURE); + } + + tail = size % 4; + pad = pad - size; + if (pad == 1 && tail != 0) { + if (write(ifd, (char *)&zero, 4 - tail) != 4 - tail) { + fprintf(stderr, "Write error on %s\n", + strerror(errno)); + exit(EXIT_FAILURE); + } + } else if (pad > 1) { + while (pad > 0) { + int todo = sizeof(zeros); + + if (todo > pad) + todo = pad; + if (write(ifd, (char *)&zeros, todo) != todo) { + fprintf(stderr, "Write error: %s\n", + strerror(errno)); + exit(EXIT_FAILURE); + } + pad -= todo; + } + } + + munmap((void *)ptr, sbuf.st_size); +close: + close(dfd); +} + +uint64_t read_dcd_offset(char *filename) +{ + int dfd; + struct stat sbuf; + uint8_t *ptr; + uint64_t offset = 0; + + dfd = open(filename, O_RDONLY | O_BINARY); + if (dfd < 0) { + fprintf(stderr, "Can't open %s: %s\n", filename, strerror(errno)); + exit(EXIT_FAILURE); + } + + if (fstat(dfd, &sbuf) < 0) { + fprintf(stderr, "Can't stat %s: %s\n", filename, strerror(errno)); + exit(EXIT_FAILURE); + } + + ptr = mmap(0, sbuf.st_size, PROT_READ, MAP_SHARED, dfd, 0); + if (ptr == MAP_FAILED) { + fprintf(stderr, "Can't read %s: %s\n", filename, strerror(errno)); + exit(EXIT_FAILURE); + } + + offset = *(uint32_t *)(ptr + DCD_ENTRY_ADDR_IN_SCFW); + + munmap((void *)ptr, sbuf.st_size); + close(dfd); + + return offset; +} + +static void set_image_hash(boot_img_t *img, char *filename, uint32_t hash_type) +{ + FILE *fp = NULL; + char sha_command[512]; + char hash[2 * HASH_MAX_LEN + 1]; + int i, ret; + + if (img->size == 0) + sprintf(sha_command, "sha%dsum /dev/null", hash_type); + else + sprintf(sha_command, "dd if=/dev/zero of=tmp_pad bs=%d count=1;\ + dd if=\'%s\' of=tmp_pad conv=notrunc;\ + sha%dsum tmp_pad; rm -f tmp_pad", + img->size, filename, hash_type); + + switch (hash_type) { + case HASH_TYPE_SHA_256: + img->hab_flags |= IMG_FLAG_HASH_SHA256; + break; + case HASH_TYPE_SHA_384: + img->hab_flags |= IMG_FLAG_HASH_SHA384; + break; + case HASH_TYPE_SHA_512: + img->hab_flags |= IMG_FLAG_HASH_SHA512; + break; + default: + fprintf(stderr, "Wrong hash type selected (%d) !!!\n\n", + hash_type); + exit(EXIT_FAILURE); + break; + } + memset(img->hash, 0, HASH_MAX_LEN); + + fp = popen(sha_command, "r"); + if (!fp) { + fprintf(stderr, "Failed to run command hash\n"); + exit(EXIT_FAILURE); + } + + if (!fgets(hash, hash_type / 4 + 1, fp)) { + fprintf(stderr, "Failed to hash file: %s\n", filename); + exit(EXIT_FAILURE); + } + + for (i = 0; i < strlen(hash) / 2; i++) { + ret = sscanf(hash + 2 * i, "%02hhx", &img->hash[i]); + if (ret < 0) { + fprintf(stderr, "Failed sscanf hash: %d\n", ret); + exit(EXIT_FAILURE); + } + } + + pclose(fp); +} + +static void set_image_array_entry(flash_header_v3_t *container, + soc_type_t soc, const image_t *image_stack, + uint32_t offset, uint32_t size, + char *tmp_filename, bool dcd_skip) +{ + uint64_t entry = image_stack->entry; + uint64_t core = image_stack->ext; + uint32_t meta; + char *tmp_name = ""; + option_type_t type = image_stack->option; + boot_img_t *img = &container->img[container->num_images]; + + img->offset = offset; /* Is re-adjusted later */ + img->size = size; + + set_image_hash(img, tmp_filename, IMAGE_HASH_ALGO_DEFAULT); + + switch (type) { + case SECO: + img->hab_flags |= IMG_TYPE_SECO; + img->hab_flags |= CORE_SECO << BOOT_IMG_FLAGS_CORE_SHIFT; + tmp_name = "SECO"; + img->dst = 0x20C00000; + img->entry = 0x20000000; + break; + case AP: + if (soc == QX && core == CORE_CA35) { + meta = IMAGE_A35_DEFAULT_META(custom_partition); + } else if (soc == QM && core == CORE_CA53) { + meta = IMAGE_A53_DEFAULT_META(custom_partition); + } else if (soc == QM && core == CORE_CA72) { + meta = IMAGE_A72_DEFAULT_META(custom_partition); + } else { + fprintf(stderr, "Error: invalid AP core id: %lu\n", + core); + exit(EXIT_FAILURE); + } + img->hab_flags |= IMG_TYPE_EXEC; + /* On B0, only core id = 4 is valid */ + img->hab_flags |= CORE_CA53 << BOOT_IMG_FLAGS_CORE_SHIFT; + tmp_name = "AP"; + img->dst = entry; + img->entry = entry; + img->meta = meta; + custom_partition = 0; + break; + case M40: + case M41: + if (core == 0) { + core = CORE_CM4_0; + meta = IMAGE_M4_0_DEFAULT_META(custom_partition); + } else if (core == 1) { + core = CORE_CM4_1; + meta = IMAGE_M4_1_DEFAULT_META(custom_partition); + } else { + fprintf(stderr, "Error: invalid m4 core id: %lu\n", core); + exit(EXIT_FAILURE); + } + img->hab_flags |= IMG_TYPE_EXEC; + img->hab_flags |= core << BOOT_IMG_FLAGS_CORE_SHIFT; + tmp_name = "M4"; + if ((entry & 0x7) != 0) { + fprintf(stderr, "\n\nWarning: M4 Destination address is not 8 byte aligned\n\n"); + exit(EXIT_FAILURE); + } + img->dst = entry; + img->entry = entry; + img->meta = meta; + custom_partition = 0; + break; + case DATA: + img->hab_flags |= IMG_TYPE_DATA; + img->hab_flags |= CORE_CA35 << BOOT_IMG_FLAGS_CORE_SHIFT; + tmp_name = "DATA"; + img->dst = entry; + break; + case MSG_BLOCK: + img->hab_flags |= IMG_TYPE_DATA; + img->hab_flags |= CORE_CA35 << BOOT_IMG_FLAGS_CORE_SHIFT; + img->meta = core << BOOT_IMG_META_MU_RID_SHIFT; + tmp_name = "MSG_BLOCK"; + img->dst = entry; + break; + case SCFW: + img->hab_flags |= scfw_flags & 0xFFFF0000; + img->hab_flags |= IMG_TYPE_EXEC; + img->hab_flags |= CORE_SC << BOOT_IMG_FLAGS_CORE_SHIFT; + tmp_name = "SCFW"; + img->dst = 0x1FFE0000; + img->entry = 0x1FFE0000; + + /* Lets add the DCD now */ + if (!dcd_skip) { + container->num_images++; + img = &container->img[container->num_images]; + img->hab_flags |= IMG_TYPE_DCD_DDR; + img->hab_flags |= CORE_SC << BOOT_IMG_FLAGS_CORE_SHIFT; + set_image_hash(img, "/dev/null", + IMAGE_HASH_ALGO_DEFAULT); + img->offset = offset + img->size; + img->entry = read_dcd_offset(tmp_filename); + img->dst = img->entry - 1; + } + break; + default: + fprintf(stderr, "unrecognized image type (%d)\n", type); + exit(EXIT_FAILURE); + } + + fprintf(stdout, "%s file_offset = 0x%x size = 0x%x\n", tmp_name, offset, size); + + container->num_images++; +} + +void set_container(flash_header_v3_t *container, uint16_t sw_version, + uint32_t alignment, uint32_t flags, uint16_t fuse_version) +{ + container->sig_blk_hdr.tag = 0x90; + container->sig_blk_hdr.length = sizeof(sig_blk_hdr_t); + container->sw_version = sw_version; + container->padding = alignment; + container->fuse_version = fuse_version; + container->flags = flags; + fprintf(stdout, "container flags: 0x%x\n", container->flags); +} + +static int get_container_image_start_pos(image_t *image_stack, uint32_t align) +{ + image_t *img_sp = image_stack; + /*8K total container header*/ + int file_off = CONTAINER_IMAGE_ARRAY_START_OFFSET; + FILE *fd = NULL; + flash_header_v3_t header; + int ret; + + while (img_sp->option != NO_IMG) { + if (img_sp->option == APPEND) { + fd = fopen(img_sp->filename, "r"); + if (!fd) { + fprintf(stderr, "Fail open first container file %s\n", img_sp->filename); + exit(EXIT_FAILURE); + } + + ret = fread(&header, sizeof(header), 1, fd); + if (ret != 1) { + printf("Failure Read header %d\n", ret); + exit(EXIT_FAILURE); + } + + fclose(fd); + + if (header.tag != IVT_HEADER_TAG_B0) { + fprintf(stderr, "header tag missmatched \n"); + exit(EXIT_FAILURE); + } else { + file_off += + header.img[header.num_images - 1].size; + file_off = ALIGN(file_off, align); + } + } + + img_sp++; + } + + return file_off; +} + +static void set_imx_hdr_v3(imx_header_v3_t *imxhdr, uint32_t cont_id) +{ + flash_header_v3_t *fhdr_v3 = &imxhdr->fhdr[cont_id]; + + /* Set magic number, Only >= B0 supported */ + fhdr_v3->tag = IVT_HEADER_TAG_B0; + fhdr_v3->version = IVT_VERSION_B0; +} + +static uint8_t *flatten_container_header(imx_header_v3_t *imx_header, + uint8_t containers_count, + uint32_t *size_out, + uint32_t file_offset) +{ + uint8_t *flat = NULL; + uint8_t *ptr = NULL; + uint16_t size = 0; + int i, j; + + /* Compute size of all container headers */ + for (i = 0; i < containers_count; i++) { + flash_header_v3_t *container = &imx_header->fhdr[i]; + + container->sig_blk_offset = HEADER_IMG_ARRAY_OFFSET + + container->num_images * IMG_ARRAY_ENTRY_SIZE; + + container->length = HEADER_IMG_ARRAY_OFFSET + + (IMG_ARRAY_ENTRY_SIZE * container->num_images) + + sizeof(sig_blk_hdr_t); + + /* Print info needed by CST to sign the container header */ + fprintf(stdout, "CST: CONTAINER %d offset: 0x%x\n", + i, file_offset + size); + fprintf(stdout, "CST: CONTAINER %d: Signature Block: offset is at 0x%x\n", i, + file_offset + size + container->length - + SIGNATURE_BLOCK_HEADER_LENGTH); + + size += ALIGN(container->length, container->padding); + } + + flat = calloc(size, sizeof(uint8_t)); + if (!flat) { + fprintf(stderr, "Failed to allocate memory (%d)\n", size); + exit(EXIT_FAILURE); + } + + ptr = flat; + *size_out = size; + + for (i = 0; i < containers_count; i++) { + flash_header_v3_t *container = &imx_header->fhdr[i]; + uint32_t container_start_offset = ptr - flat; + + /* Append container header */ + append(ptr, container, HEADER_IMG_ARRAY_OFFSET); + + /* Adjust images offset to start from container headers start */ + for (j = 0; j < container->num_images; j++) { + container->img[j].offset -= + container_start_offset + file_offset; + } + /* Append each image array entry */ + for (j = 0; j < container->num_images; j++) + append(ptr, &container->img[j], sizeof(boot_img_t)); + + append(ptr, &container->sig_blk_hdr, sizeof(sig_blk_hdr_t)); + + /* Padding for container (if necessary) */ + ptr += ALIGN(container->length, container->padding) - + container->length; + } + + return flat; +} + +static int build_container(soc_type_t soc, uint32_t sector_size, + bool emmc_fastboot, image_t *image_stack, + bool dcd_skip, uint8_t fuse_version, + uint16_t sw_version, int ofd) +{ + static imx_header_v3_t imx_header; + image_t *img_sp = image_stack; + int file_off; + uint8_t *tmp; + struct stat sbuf; + char *tmp_filename = NULL; + uint32_t size = 0; + uint32_t file_padding = 0; + int ret; + + int container = -1; + int cont_img_count = 0; /* indexes to arrange the container */ + + memset((char *)&imx_header, 0, sizeof(imx_header_v3_t)); + + if (!image_stack) { + fprintf(stderr, "Empty image stack "); + exit(EXIT_FAILURE); + } + + if (soc == QX) + fprintf(stdout, "Platform:\ti.MX8QXP B0\n"); + else if (soc == QM) + fprintf(stdout, "Platform:\ti.MX8QM B0\n"); + + set_imx_hdr_v3(&imx_header, 0); + set_imx_hdr_v3(&imx_header, 1); + + file_off = get_container_image_start_pos(image_stack, sector_size); + fprintf(stdout, "container image offset (aligned):%x\n", file_off); + + /* step through image stack and generate the header */ + img_sp = image_stack; + + /* stop once we reach null terminator */ + while (img_sp->option != NO_IMG) { + switch (img_sp->option) { + case AP: + case M40: + case M41: + case SCFW: + case DATA: + case MSG_BLOCK: + if (container < 0) { + fprintf(stderr, "No container found\n"); + exit(EXIT_FAILURE); + } + check_file(&sbuf, img_sp->filename); + tmp_filename = img_sp->filename; + set_image_array_entry(&imx_header.fhdr[container], + soc, img_sp, file_off, + ALIGN(sbuf.st_size, sector_size), + tmp_filename, dcd_skip); + img_sp->src = file_off; + + file_off += ALIGN(sbuf.st_size, sector_size); + cont_img_count++; + break; + + case SECO: + if (container < 0) { + fprintf(stderr, "No container found\n"); + exit(EXIT_FAILURE); + } + check_file(&sbuf, img_sp->filename); + tmp_filename = img_sp->filename; + set_image_array_entry(&imx_header.fhdr[container], + soc, + img_sp, + file_off, + sbuf.st_size, + tmp_filename, dcd_skip); + img_sp->src = file_off; + + file_off += sbuf.st_size; + cont_img_count++; + break; + + case NEW_CONTAINER: + container++; + set_container(&imx_header.fhdr[container], sw_version, + CONTAINER_ALIGNMENT, + CONTAINER_FLAGS_DEFAULT, + fuse_version); + /* reset img count when moving to new container */ + cont_img_count = 0; + scfw_flags = 0; + break; + + case APPEND: + /* + * nothing to do here, the container is appended + * in the output + */ + break; + case FLAG: + /* + * override the flags for scfw in current container + * mask off bottom 16 bits. + */ + scfw_flags = img_sp->entry & 0xFFFF0000; + break; + case FILEOFF: + if (file_off > img_sp->dst) { + fprintf(stderr, "FILEOFF address less than current file offset!!!\n"); + exit(EXIT_FAILURE); + } + if (img_sp->dst != ALIGN(img_sp->dst, sector_size)) { + fprintf(stderr, "FILEOFF address is not aligned to sector size!!!\n"); + exit(EXIT_FAILURE); + } + file_off = img_sp->dst; + break; + case PARTITION: + /* + * keep custom partition until next executable image + * use a global var for default behaviour + */ + custom_partition = img_sp->entry; + break; + default: + fprintf(stderr, "unrecognized option in input stack (%d)\n", img_sp->option); + exit(EXIT_FAILURE); + } + img_sp++; /* advance index */ + } + + /* Append container (if specified) */ + img_sp = image_stack; + do { + if (img_sp->option == APPEND) { + copy_file(ofd, img_sp->filename, 0, 0); + file_padding += FIRST_CONTAINER_HEADER_LENGTH; + } + img_sp++; + } while (img_sp->option != NO_IMG); + + /* Add padding or skip appended container */ + ret = lseek(ofd, file_padding, SEEK_SET); + if (ret < 0) { + fprintf(stderr, "%s: lseek error %s\n", + __func__, strerror(errno)); + exit(EXIT_FAILURE); + } + + if (container >= 0) { + /* Note: Image offset are not contained in the image */ + tmp = flatten_container_header(&imx_header, container + 1, + &size, file_padding); + /* Write image header */ + if (write(ofd, tmp, size) != size) { + fprintf(stderr, "error writing image hdr\n"); + exit(EXIT_FAILURE); + } + + /* Clean-up memory used by the headers */ + free(tmp); + } + + /* + * step through the image stack again this time copying + * images to final bin, stop once we reach null terminator. + */ + img_sp = image_stack; + while (img_sp->option != NO_IMG) { + if (img_sp->option == M40 || img_sp->option == M41 || + img_sp->option == AP || img_sp->option == DATA || + img_sp->option == SCD || img_sp->option == SCFW || + img_sp->option == SECO || img_sp->option == MSG_BLOCK) { + copy_file_aligned(ofd, img_sp->filename, img_sp->src, + sector_size); + } + img_sp++; + } + + return 0; +} + +int imx8image_copy_image(int outfd, struct image_tool_params *mparams) +{ + image_t *img_sp = param_stack; + + /* + * SECO FW is a container image, this is to calculate the + * 2nd container offset. + */ + fprintf(stdout, "parsing %s\n", mparams->imagename); + parse_cfg_file(img_sp, mparams->imagename); + + if (sector_size == 0) { + fprintf(stderr, "Wrong sector size\n"); + exit(EXIT_FAILURE); + } + + fprintf(stdout, "CONTAINER Sector size:\t%08x\n", sector_size); + fprintf(stdout, "CONTAINER FUSE VERSION:\t0x%02x\n", fuse_version); + fprintf(stdout, "CONTAINER SW VERSION:\t0x%04x\n", sw_version); + + build_container(soc, sector_size, emmc_fastboot, + img_sp, true, fuse_version, sw_version, outfd); + + return 0; +} + +/* + * imx8image parameters + */ +U_BOOT_IMAGE_TYPE( + imx8image, + "NXP i.MX8 Boot Image support", + 0, + NULL, + imx8image_check_params, + NULL, + imx8image_print_header, + imx8image_set_header, + NULL, + imx8image_check_image_types, + NULL, + NULL +); diff --git a/tools/u-boot-tools/imx8image.o b/tools/u-boot-tools/imx8image.o new file mode 100644 index 0000000000000000000000000000000000000000..ebfb3c7ead367849a386f19f3d624fc06f0c8659 GIT binary patch literal 30048 zcmb<-^>JfjWMqH=Mg}_u1P><4z>rXYU^{@B4h$j;!VHE74yUE*@yoY>#nL7q^Z4Z* z7|{8K2OJvytMN}ga5#-$-i3j|G2AiCG1M_6wDWARN8_6n4h#&PAu1e=KN%bt82I~R z85tOSdY7myU|?YI?EJP1Bx?B0vGcD_=kpgJD-FN-bbdEH0A_a{Zau)?!OzIR;L#hR zBG7qkKgcIwDUifb1_lO??h+LTkJfLc@}2j=5+035Kz2sQI>tE0I>tH1L*3<Sc+#=+ z#!ip{yFi5Df3R}T&f^}<KmPN#JOvq+T%y8Zc*&*ny${G(umX@pPe6`MKIhZ<&!zLd zN9QSzP9GHxu&hV7j|zu}<q7`QM+^)MPQ5Os|G(e$>3rtW`Cjp;i{@94<hLH3uOLb- zU+}lS0@(sG+ouy^|4Y~Z{~@ey3$QYu&QBh_rl64U=sf7rZ3|*Sg9G7vuu5ziUY~_( z={yBBg#^75k&X4}6$M!hHN~Uz;LCh8m%^nELG8qA6+-pv3rGoUf@6r`0ng4qp&p%I z4Zj6@biM~WqV+(9f=}loXpDCrh6&nq-Ur3(OOVsRV$eYF=zImYfT&PI(eU~#BE^GJ zuV?31$B<Bu&cC1(Jb{UUp*x%flxQa~Ffg=U;&1)Iz`(Gp0h}zsG3C?w9P0N*UkOld zOXGikz@s--!Kd>%zo4rE)M@;J&I0^`&I<g3&IVw|dv+f2Xg$E+!odj249`3|5A922 zU|{g+{0^1|%bj6hU~n~j>(Q$V^7CssuU-*t?_QfckIr{cJ3X6^GJ;ZRBUtN)ZVr#; z11!B0|Nr~<@B2l?Q;m-r8X6i(UUXhO_)_5ep@s$q27Z@|9=#q+9{er`JFht~T=QtY z!R({?!Q<i!Ps^i4b75K0qwxsH^Pm6+Wu?R5{O!^C(QyaJ?_f_ueFk>!%m4rW|A!`p z&O=Zcs2dSU7plMq9${d;$nv}(kM5FYU|`q>%BUW_;5>k)EDcsdIffe^*vY`ezyOLk z#}LQPKcT@Mou3WAfeIf1P|2|dT-Y3UQQ-j@?9uuDv&U)=XlVfow~gS4M3gU%okziz zf#gy^a-jSQDq>Vzps5p7#t1MlzybzT#=uKVSHqL8oiDJIGsht1OdKNvgW)B^Z!VqB zJ-TC59N<<ZfJ(;X-#(onDWA??U<n_~AN;My85kHkZyfv~fAED|uZTqRBWBA(C7jK_ z|MR!hF)%QI7`+o27#P0aeyuoR7X!%Co$p+r<*A1)NT5`$@imAp$!YxkpPzxD{)R{L zZ4a<2kIq|Qt2`_Zm-vBMusj7ThH&O@P<`OhTcX0Vi<N<a0d8aS4_5xx4TzEslA2%c z`-fW6K~soF=b_j0Q0(oz1(zZ+FC%GqeHN!#uQ#Dcp%-wkC!<P(v$jX)p_hOE{r?Y7 zZ=r_Y3=cqyh8YYD3}8L22P%?0FpDexj;YX4FmN?IY4{(d0s?CUrHPUVP$g#I(RsXk zg##1RL}>c-XgmV4#G~^?bS$J0ghdWK{}5SCpcQyv+p(GTdJ|IV%r6hA&pXe7t8}op zo8M^gZ;R)4>DKCYQAzOWu2SeMQ4#Ryyyw%Mq~X%(q9WmO@ELoji;9d#=M4|We;x-P zu={{2%uYW6kKP(~0hdlU5ueU;FDCu^|KG9suONT>P9{)31!r@Y&UZebI@P5cWKc58 zb`QoM{~z$nGk6?)%<g#b2^XwhVtn1~t>NO*nXKc}SuFuARa`nhLX!@(+-Y#-X7p%2 zqJflE`Q;nH<-ozm?A@Sb%YZO3&F}!XvxdiUX9)&|fAY;Igd1v9WEeb-J1c<roz5Di z)A_f#azm|v_!ZQ+X+FRR@}U6#wi_L;-2B@PfE)|*w+G{EkKP(qfo5+F(HDz;{{P?Y zECTYLfD7XXpU(50&H~_g@aPUv;ehzK(^<iz`30j#r?bRM_5c6>PXHSQO{!o&r$O9z zKmp<|1_f}r3s0ZEtxxz{uQM<(Ks>igfq{YH-~$m}v862FF!W%2%^j@Ke2Td<Si|w# zad!SzaFYxah7enPI;#~ropp|b!v+*mFXtntf6wMWEc{arG}ovIF!HxfV_;xtIl$l2 z2eJ^{VCcL6vh7bOGehg8U4KG_8TefeA^8DetOwXP&Y;+XTKUopRDm3P%#Mf=P#l9) zyp;a;|3A3?@ofIX$lrPaWFOdty9}Tn)cVT8@KWkOIIFraFn}D$#6RUw%YhP|mP;iX z-AJxu3<0~2F$C<om$48{kQT3J^B-3J)=40LUE*))0@;J4lMSMi4XRW5KfK0sfoDi) z(!;FMEb&)qKAn%CDGQpKF$_Yeczp-C{)1F~p`BlYJAZpLzKLLFVCb|_fmWPpojxj3 zJ5Mn%Fnsn{4N9{z9U&@`&A%D>Ta?%s7@BQVnD|?)*kB3f7&uhhcQYt3FhJN)svXJ$ zHCRA}d*?fs-rWD4IVv1Jy(KCNKE0}-#Pa=~52U{CEKy<c>3sTH2$tWPj~aBlGPE8j zDQf<~Tl&GHyOzVJ^B*|<!`eZ%Yzzz@od?0~^L-l3h?Y?E4@Ul0RyL3Tq?gc8qoUl- z-@1g2fq{RUi;A{K^HBp(wwVFSb<8m=Cf%+KE{w00V^mmL50v(G=csT%0~KDmKtmUj zE?O_~x6EQ?VCa0|)0+eG47l)j-18q4!JRHD9H2}I^_fSfi;93x=L_G?ub!R9ntuuM zw>yBeUhqt2Y4?E?R}KuG-L4X#I@a<qfBSk?P)Ef_MZlw1gb~tZ_<qwv@sx+<1^(7- zklw?d%|{I!yF(dTFO{x^ILYv~546oE0IFvZ3QAO(fAN;S_v|j^=&n(b0ZB9-W?*4p zc=6%e|Nl@!?tr8^?}39C)Q~yB%D~{*{O3P^s}`s|3yx%uURh9-Iqs=(WCBHukxO@u ziVQTqntuqEi+l1fK2j{`(Ot~Z9W2oK64d^Y`Skz4NAqEWZsyhlrMEm9kAPB};Q^!q z7TOPNKEmkH3vSDST*$wzL`Bjw`N%E-aE%Bq>pe0LfFjw0@dW>N9~G&z&Iy9d3=Ciu z4jlzt(9CeGL716=VM1D`2gmUS6=ntokXMg8uz(DL#v#bhh=K*wLQnhm|NqM^|Nj4< z;Mnb|0CE6GO`73>PO#5GN}NGTCPIofP`?!vI4wFX3=E)v_38ZnBI)!0|DMTzJeuEd z_;&kpcx3(uxi*UtR8S{$-t_1N8`WI`N(`?+R(LQTe{uH5|NkDzhnZtk5<ELyAAoF) zj00JB<?sLhFO|Th7`XlcM@GvYW`up8K0)m34p9l{d;zi+YUTGEAp5fzU3y(a|3mGA z2Cbvw0f^1#J8yQnsEEAq1>1kQ+eJmhvorJosAfrscZ}!_5&f@t0puFS!ywnVfm}1? z@BjZ3Jdwf&5jbif`I5h&3i=46BmcG|oh&M@El>DcjF~|FcW_pBHGJ!8_}>wf|F~WG zw;zROFle;7_L@j|HXZ>5*}(_wE}b8H0|c56D5N=dPEmRC=l_3~&R=Pcoh>Ri|NQ?C zjYg;eE}g#|`L{!4poTd%A7^yz^<nz}Rq5CrqVfl>sMkf{|My#p7aSRnx>(w%@b_o@ z|Nq~WfBRQ(|G}s8yDzju1uJJhGcho@bn~b{P5145<Z5`}Gk?C@2N(Wrald^!FM9S? z3HWF}bmSLga&5iM-vVmKG2Zm({9t$qmOl<LYJy6I1{cd)rJr1SqyBqze)8$O0V;qz zdwZBbeMOJX9}_<F=QDlg&-W7WXnxV)(fq36Gk<>2g3tW<Q5!z<=O=-z&APxZ=l~Kg zdhnS)zv=_OpvMG$!GHz)f)N||1rrYN3uav47c6+dFIe%x!}4J18qem#jGhM{v->dK z=il!4k$?L%5oQJkkH%x5(EiMy?{}luMGzb${DS<NCp>2O^oFntIQBX)I_>~<sT?g2 z^7r%n|NkFUNceRA^67jAE+~8~Kk&DfgZd%R&cn;gzd;ooI2Z2{!&ZI0JoXcz0#s3g z!yH=mfDL`Q<mdnYU{Uyx3#8YZ#R%%zeseYa|FQ&B%tE^xIEFkRW%0{rzyAO4JO(u% zni`O6T1dgt9m)ZXTP#W-zA?n+8)TD6QYK8CX%bXX1`bwO+Z_^Iuoe?E3_A~E_}kU+ ztt0>TtI%M1ITc(IxiB#70F|R@o#n6qOY3Y0mBNMxI;VpQ+s;$R+QH?oONTpGr#}lc zkAeqa!0j<m|JxBV;s@$?do;g^@aPWnfZEepqoM%S0(P}Ww=+!2<G70osO`q!(RmzH zrD{MFfSMOhoS^ZLqaM93Dj-!Xpjrw#00L4e0g^EAf%(-%MF7-4=q^!_@aV1qmBuf3 zalv|KpfQxb2kfBww?@SPG>mA`?4rWL2z4LC&d%c=tp`f>g3LC6q%*^l9=$Ot4j%B< z0;mH6Rty?2Y&}pip}|7Aq#M*zY5vJrlFr|s!p^|ZdXm2t)OhiL4}U-tDp;pu=Mhj% zY{kLA(ENjozoiM>+JSXl4gZ6@3JwskYKT4jEh_*2|8KBR=5LYy4=RoS@bI_q1Zjj+ ztuCF9Jd#-^dN7{&{{T|&`gHz+`VHA`Xvuznzr&D&fdSN>KzI$8&8z-FZC?2A|Nmxi z#$w}d2h~`Ru7zhZOPdel2T<?Kg@M5%8C)HD^s+d39DKmy!T7@CxPt~L>I^SAHvi$_ zpK_>I<SH9zC_})fvx0-L{aP^l75{;K&cff~&H@=cfsLMeB!h|^hUN$Vd%YMvdPRaf z4nAV(1m|3jP8U%H!~Y0tP=gngE?Q3fh5B&+-~a!2g4_yM)O?W9@c+v{e?h$taD9#G zzvB!bAA@`eDpSF|HBdUl2#4dK1`7j79Vi`xduv$LfmDD@e0dtuD1r1x;P!%Bq)2fE zPGpe4Za&25!T6#R92Op(w?M&r95f^b8JvKnq+5TWzP<Pd8lJ6pKy9E;ObiT&@lR0V zVt^(cpU!`um+?<OAOr5>6|pif;FN9s$jZO~X@fB_FfhEF3l2Htbiw1$>7t_IaquCF z2jk1*jQkAns6-5>gPK+<;0AK@ga3}r2N+!-3D=|3QQ@W6-~a#9Ccwf2l*KT8f1Cjn z`Je!JDGhc3B3+`{4=N#ER{uwjU(nE;Pv?J+UNq&PQWB3lJvtpFpl*G+=+FQEi136O z2+q^|+YY2bJoK{m56EU0co=}?J3!IT@G=o(0IYw9nweqaf;&Opgi85z{{OrTVk|7K zTIK%y{}1V}F}@T683!MqL}Uo0v;?*tmQFVPf~J#|zd-G0P=CMW9H<`*c3txersf|k z{Ot?>{Qv*50PG8t`~fd9k&H(w6hKjPoIwy)Jj3j?gV|~N3z`#sI{!gaAtDL3f~SJe zGT3n!R*h89FfBHlQ9O3@C)8sXe*XWD952vf#PB~fL=mL`u7U+VXbP&X8e!&EfXu~T z<b=Z1`VdyD2~(@^^Z$RxJ)rUloc~^i{RSl-Ncpf66f0N*s|ge+SOP1Fk%8gmIavJ< zDa)aCG}QmS0gQ$RAjJtd0HJvfGkkgj7-4Y>QSQ-s>$nRm18Afjq7xK8i9ev>9s%l{ z5R4`xm~zb@@bvfc@BjZVLBTtr`6pA!>*k-#{4Gf!G4EfXpm1SefOMKc{df(KzhTWH zNT~^#PUPPPDIp<ph6kX&1v`{~J8n5psieTa9hBu5pk{P~&4n2BqVgT6OMD~(r56m# zzu@?Rreu`zv~@G6CyXV2-hj$aJ#gve0xMI&HH;@H_#k$6`>3$^cK$Ft0P5grAbSM4 z2^*pUlY+)CMyt@H8|<rHLiih|h(^Whtw`*ZFzq7vwSz`cKy@DohZp7Nr7I-nWTxlk zrskC>6r~oHrWTi^rYNYUa4|S1=Bbw`<QJsoDX11(DX131go;ZNOVEUhQWI0)LLk*S z#i^;;3aLd!`9)xz;YFDxso41tqd}SwHlmAxtb~XaXC$ho6qn{I=%=KX>F1T^<S-O5 zxFu%hq^2m8<SP`F<|!oS=jJBnr6^=17H4oF$$|I^X_+~x5R+65j50DRVDiPq$%%Ps zU_Dk~?<2c79~1;!48g9>{tS)*48A4|E{-9N48Fnc@lHPe&fW~c&Tioh2p5&+6{RNU zr{`r>rKTuk<|d}6DwI?fq$+5rrf7081mq-^q~#aoT5)FT`9@d-Mg%B08DI+gf<-}I zD=kV@2m*z0MrvY8Y7y9ZP-aPDx<YPdadB>9NpePNiUJpdU%o<eeqKppW?pKMLRx-l zUJ4fjl0Z&kda;#)L4|4s7Xu`E%8N2fGV{`*?#xIj;$kRBEGh;Gf#RH(i$Q^tAv3qa z0^*V6+>~MlztnOCXP7Nk3Y?(yl9^ipQIV9N4@x&BnfZAPskyny@o9<0B_KftPq23( zlrKYQUUpu7d7gr^uS<NezjJ&@WPmHOMfpXk3aNP|MVYC^43MPi>Fen3s*s#tlo}5X zPmuf4N{dq&Qc{yj(;3n-Q*%;4u>uM0y!;YK06<e9D6to(CYR(FDHLZ`rE)Pi`}>7B zdiuErDFh>gtvFQ;EGkeW!xh3@gMvN%{j4|*DpU<jD!3T@A_5e^v8Uh!@~tO0j*CkR z3i69e7>ZLEic|DKUI+VM0q*~_;(|<uw4Brm5S^EpmlAK1%>bemOuWH#yakkR0p%N- zK?MxWycrlg^U4x)GE*3WokJOllS{!N!Qg9Rz~E~FW-EYLhH#!CgQKx22!kaV98HZu z7|Jm>VsJD!f^eMt{X^p2g8Y3MK+y!tKn!l7!LIR8uQPzcS)nYos2CKj!QpW6;&QlX zZgILoQciwyHiMg|kE_3%8v`h}<)@`FxcNA`Go<AtrZYGO1i1RSFeDZfq~@h4BqGu( zLx5vYh-ZkWzaK+hYPmu|Vo^yZI0?a%Msa>JIARg;2384A+zjC40Tu%%L@+BQu>>@a z4=U~$Qc@H$(`>=nv?{eIUm-uuwj{S8z92D0A*t9_HANvgzcjDJ*3g=hlM|v=UA0&p zSy^&^UYTuPeo0YjUNT4(yflKEW35n>tDu{v0Of*hWngdx6@yj^nUL61a0~#4x<Y1( zm4a$cDHpnYE;KUX@?2cuiA8ytdFfUPz9tGTsl_Fkd5NH~QAkWlDM~FaR>&+?0Ht;d zg`~=oR7i=Qn!*KYSA(3OS`04s;`7teic?DzY{A(doClz6s6tR#11a#+GK-2!5OD+6 z4$eg&<B*CIa5yVK&D8+cB&jKyR-nR#fdQ5Z;I7O8IkhAsF;5}6w5SMF<ABl!RJ)?0 zBGefe`k=mp8V@Swk+Lz8K9piNzW^LUnRyDCc?G2<3dJRf$=T2n80-rM2It@qD+NR$ zpqc`83aos@khf9@&P>lsECChBPN0Nt1=RtHfW#7TxN<STs+5v^g~Xhk{N%)vRE6Bs z-29?SsP)kFpjxb;tAMEU6q3`@!2t~m23WdKEsj=AiB-^5fM$PK{#4LVE!G4nMtA{~ zV^Iq$qY97;sE3C+m7HHt84pTQ@zA6K^)Fl;%7;-&rI|S?@rd#RCQdgR)Q!Mq8L8r+ z9x*m^Koic`#373eF=UY2h8U7q1VAleEW+U74s7C}2_tOcxV#JMPGM6I8o9zIKGfaM zj?GgH3=AAN#5r+@<I3^eIMjo>h}fiXhNru;vz3B|OKMVPVxEG9p0S>Ru9+r;0Wt^F z1p(2FRY44l6#|UXJnS437#To|o)~065)2Fsu(SY5y5Nyk1_sb-J+LfD%mvIs6^nq1 z!TjgLz`&3L6;lBXbvf|~^fNi}N%S#0@hSAOIPq!pusZS?w6Qt!Sv0e|^F3%}WGdm} zvvA}yaOBf);!|+qlW^h_aN^@|2ZfCW0|UbzsCjK5+KEq~hsljkqK(;&PobG5g^N$Z z5v<&m8<d5>y;TMVhUK7);KV1;2eP}D*_BVBhsBXkqm9*t&!Cyjo$mlE(_b#Im5xYO zx^a6jFfizV^fE9oq{H3siRykAkoz4$?)Tw)pvV-B)%o7sAh%aQ%>u1|WpLyZXk+r= zlW1mk=i9)@l*+{?;ld~2$j9Lbws#3s?F>*vAi~X+4<+1OK=!)uB`|HmYOM!1IQ&0A z&9Vk1RIphtsD41ifhXStcIG@LE<OuqaDbr30n=H&8SK7%3)l<!X0SW*O<=F!o5AkS zw}8EpZw9*)-vo9@8cG0#6$1l<H7JfiD=irq7)qdGDxk!H(+%KobmyDE$ozu?s~bE) z=>Tdh1_kma3&Y3%7!u(043fo^VTPx4R5@nwNHnS#sQ=HzzzEJKAQ_m6N(>APAaPi6 z4ig70VgZT6@(;-CAblXr3?2~&u@E>EY7Qvvf<(c229)-|d5@jp0Z5R6fdMoE0b-Uy z)fYg+7Mv$Q5@7#gmcpRHN0579UIyn21_p-NP;)jw3wm%`XJBC12(=fqh8JWk%>DbJ z>IFdQiGhIuoJK(k!1iL6l2^gzurLHbodmT8>>ox37KRWs@sCjRH$WpBoNgHy7<d^$ z<qb?X1Kd1WsCWX@90n+@2^ME(5P(JqsN4cM(;O_$&F}y!ZULp;pyCQp{tA#{1_lPu zsxOc`Q=kPg%mwLS^(+hpXyUn0@eDNaLa2BI)P2xsW2k|OM?uA*)-!a1#km<0pzeUV zXEIovg&_yc{P|Gv1gJR799tGpxPb~1h(R#-gLupgn6=beusH~EXf!h%1B*i=g@J(q zR^CI*L5PF+FwD%r2V<hp%nYDe4HO}U+h7N?GJxirk!2w9#L6ItEXcsX@D!>ZO_<>o zR2)`E!Q|gV#lv9&Q2Gm0JR8b~Q9q&L)i41l{SPYM1LeagW^jbEGJrO0z{Ef_2UL6u zh=YWAq2i!L%Sgfu!cg&l$bt+E43c1RXd*$9lLw2lGO&W(g^*B%ii7rRAVe7$w4vfB zkp)3>Oi*!9enOTqhl)F(nQsdgXJdes4=_E>U~yIkFEsU@P;q}Waet_|0h)LyR2-H+ zVdh6e#ZA%FCql)o(ZnI;87o61ns_c)JsZOgn88rG7%C20P7D<SQ<YHh3NQ~r)I-HR z(9CZIi?cGkL=*1@i?cBZfN6v|6Ty5o1_LmS5T6d_voe5|n;=9P80JF7`_Rl^3>IgD zmv1mVE5YKd46wEZOng09oQ(li?!m;jLd930nZFw<UVtWk5G>Bha1~AbI8=NOn)q3$ zIC_WVGFY6AAp=aqT*JU{6U=9Y<^zQIeW*Ajy(7e*Ld7+}G(!9}n9mB$2MF<xQ1K~X z8X^82%x7gN1k(udzhFKqLj{;dh%<wWT2=->G;vO-cruzeKUDlEnz$%b{1lqFG*sLQ zO<WNw4y)f`E>Z`JvoYL2Q?CmaXJwED(=gQx48~wSE5ije^_F09Hiivo;`UJS4m5FB zusAEjTQqTRsQ79$@j$5fH8k;Xs5m5_A>0XKF@tBZK`aE0Wd^m85Qz<}9n=P8hO|!+ zDj}>c9OBX}*ws(Op<W$_`dK*C&&MJD7l-	O7$mi0{QAehi2BSsdb5afsi-A^r%5 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 literal 0 HcmV?d00001 diff --git a/tools/u-boot-tools/imx8m_image.sh b/tools/u-boot-tools/imx8m_image.sh new file mode 100755 index 0000000..6346fb6 --- /dev/null +++ b/tools/u-boot-tools/imx8m_image.sh @@ -0,0 +1,43 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-2.0+ +# +# script to check whether the file exists in imximage.cfg for i.MX8M +# + +file=$1 + +post_process=$2 + +blobs=`awk '/^SIGNED_HDMI/ {print $2} /^LOADER/ {print $2} /^SECOND_LOADER/ {print $2} /^DDR_FW/ {print $2}' $file` +for f in $blobs; do + tmp=$srctree/$f + + if [ $f == "spl/u-boot-spl-ddr.bin" ] || [ $f == "u-boot.itb" ]; then + continue + fi + + if [ -f $f ]; then + continue + fi + + if [ ! -f $tmp ]; then + echo "WARNING '$tmp' not found, resulting binary is not-functional" >&2 + exit 1 + fi + + sed -in "s;$f;$tmp;" $file +done + +if [ $post_process == 1 ]; then + if [ -f $srctree/lpddr4_pmu_train_1d_imem.bin ]; then + objcopy -I binary -O binary --pad-to 0x8000 --gap-fill=0x0 $srctree/lpddr4_pmu_train_1d_imem.bin lpddr4_pmu_train_1d_imem_pad.bin + objcopy -I binary -O binary --pad-to 0x4000 --gap-fill=0x0 $srctree/lpddr4_pmu_train_1d_dmem.bin lpddr4_pmu_train_1d_dmem_pad.bin + objcopy -I binary -O binary --pad-to 0x8000 --gap-fill=0x0 $srctree/lpddr4_pmu_train_2d_imem.bin lpddr4_pmu_train_2d_imem_pad.bin + cat lpddr4_pmu_train_1d_imem_pad.bin lpddr4_pmu_train_1d_dmem_pad.bin > lpddr4_pmu_train_1d_fw.bin + cat lpddr4_pmu_train_2d_imem_pad.bin $srctree/lpddr4_pmu_train_2d_dmem.bin > lpddr4_pmu_train_2d_fw.bin + cat spl/u-boot-spl.bin lpddr4_pmu_train_1d_fw.bin lpddr4_pmu_train_2d_fw.bin > spl/u-boot-spl-ddr.bin + rm -f lpddr4_pmu_train_1d_fw.bin lpddr4_pmu_train_2d_fw.bin lpddr4_pmu_train_1d_imem_pad.bin lpddr4_pmu_train_1d_dmem_pad.bin lpddr4_pmu_train_2d_imem_pad.bin + fi +fi + +exit 0 diff --git a/tools/u-boot-tools/imx8mimage.c b/tools/u-boot-tools/imx8mimage.c new file mode 100644 index 0000000..50a256c --- /dev/null +++ b/tools/u-boot-tools/imx8mimage.c @@ -0,0 +1,623 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2018 NXP + * + * Peng Fan <peng.fan@nxp.com> + */ + + +#include "imagetool.h" +#include <image.h> +#include "imximage.h" +#include "compiler.h" + +static uint32_t ap_start_addr, sld_start_addr, sld_src_off; +static char *ap_img, *sld_img, *signed_hdmi; +static imx_header_v3_t imx_header[2]; /* At most there are 3 IVT headers */ +static uint32_t rom_image_offset; +static uint32_t sector_size = 0x200; +static uint32_t image_off; +static uint32_t sld_header_off; +static uint32_t ivt_offset; +static uint32_t using_fit; + +#define CSF_SIZE 0x2000 +#define HDMI_IVT_ID 0 +#define IMAGE_IVT_ID 1 + +#define HDMI_FW_SIZE 0x17000 /* Use Last 0x1000 for IVT and CSF */ +#define ALIGN_SIZE 0x1000 +#define ALIGN(x,a) __ALIGN_MASK((x), (__typeof__(x))(a) - 1, a) +#define __ALIGN_MASK(x,mask,mask2) (((x) + (mask)) / (mask2) * (mask2)) + +static uint32_t get_cfg_value(char *token, char *name, int linenr) +{ + char *endptr; + uint32_t value; + + errno = 0; + value = strtoul(token, &endptr, 16); + if (errno || token == endptr) { + fprintf(stderr, "Error: %s[%d] - Invalid hex data(%s)\n", + name, linenr, token); + exit(EXIT_FAILURE); + } + return value; +} + +int imx8mimage_check_params(struct image_tool_params *params) +{ + return 0; +} + +static void imx8mimage_set_header(void *ptr, struct stat *sbuf, int ifd, + struct image_tool_params *params) +{ +} + +static void imx8mimage_print_header(const void *ptr) +{ +} + +static int imx8mimage_check_image_types(uint8_t type) +{ + return (type == IH_TYPE_IMX8MIMAGE) ? EXIT_SUCCESS : EXIT_FAILURE; +} + +static table_entry_t imx8mimage_cmds[] = { + {CMD_BOOT_FROM, "BOOT_FROM", "boot command", }, + {CMD_FIT, "FIT", "fit image", }, + {CMD_SIGNED_HDMI, "SIGNED_HDMI", "signed hdmi image", }, + {CMD_LOADER, "LOADER", "loader image", }, + {CMD_SECOND_LOADER, "SECOND_LOADER", "2nd loader image", }, + {CMD_DDR_FW, "DDR_FW", "ddr firmware", }, + {-1, "", "", }, +}; + +static table_entry_t imx8mimage_ivt_offset[] = { + {0x400, "sd", "sd/emmc",}, + {0x400, "emmc_fastboot", "emmc fastboot",}, + {0x1000, "fspi", "flexspi", }, + {-1, "", "Invalid", }, +}; + +static void parse_cfg_cmd(int32_t cmd, char *token, char *name, int lineno) +{ + switch (cmd) { + case CMD_BOOT_FROM: + ivt_offset = get_table_entry_id(imx8mimage_ivt_offset, + "imx8mimage ivt offset", + token); + if (!strncmp(token, "sd", 2)) + rom_image_offset = 0x8000; + break; + case CMD_LOADER: + ap_img = token; + break; + case CMD_SECOND_LOADER: + sld_img = token; + break; + case CMD_SIGNED_HDMI: + signed_hdmi = token; + case CMD_FIT: + using_fit = 1; + break; + case CMD_DDR_FW: + /* Do nothing */ + break; + } +} + +static void parse_cfg_fld(int32_t *cmd, char *token, + char *name, int lineno, int fld) +{ + switch (fld) { + case CFG_COMMAND: + *cmd = get_table_entry_id(imx8mimage_cmds, + "imx8mimage commands", token); + if (*cmd < 0) { + fprintf(stderr, "Error: %s[%d] - Invalid command" "(%s)\n", + name, lineno, token); + exit(EXIT_FAILURE); + } + break; + case CFG_REG_SIZE: + parse_cfg_cmd(*cmd, token, name, lineno); + break; + case CFG_REG_ADDRESS: + switch (*cmd) { + case CMD_LOADER: + ap_start_addr = get_cfg_value(token, name, lineno); + break; + case CMD_SECOND_LOADER: + sld_start_addr = get_cfg_value(token, name, lineno); + break; + } + break; + case CFG_REG_VALUE: + switch (*cmd) { + case CMD_SECOND_LOADER: + sld_src_off = get_cfg_value(token, name, lineno); + break; + } + default: + break; + } +} + +static uint32_t parse_cfg_file(char *name) +{ + FILE *fd = NULL; + char *line = NULL; + char *token, *saveptr1, *saveptr2; + int lineno = 0; + int fld; + size_t len; + int32_t cmd; + + fd = fopen(name, "r"); + if (fd == 0) { + fprintf(stderr, "Error: %s - Can't open cfg file\n", name); + exit(EXIT_FAILURE); + } + + /* + * Very simple parsing, line starting with # are comments + * and are dropped + */ + while ((getline(&line, &len, fd)) > 0) { + lineno++; + + token = strtok_r(line, "\r\n", &saveptr1); + if (!token) + continue; + + /* Check inside the single line */ + for (fld = CFG_COMMAND, cmd = CFG_INVALID, + line = token; ; line = NULL, fld++) { + token = strtok_r(line, " \t", &saveptr2); + if (!token) + break; + + /* Drop all text starting with '#' as comments */ + if (token[0] == '#') + break; + + parse_cfg_fld(&cmd, token, name, lineno, fld); + } + } + + return 0; +} + +static void fill_zero(int ifd, int size, int offset) +{ + int fill_size; + uint8_t zeros[4096]; + int ret; + + memset(zeros, 0, sizeof(zeros)); + + ret = lseek(ifd, offset, SEEK_SET); + if (ret < 0) { + fprintf(stderr, "%s seek: %s\n", __func__, strerror(errno)); + exit(EXIT_FAILURE); + } + + while (size) { + if (size > 4096) + fill_size = 4096; + else + fill_size = size; + + if (write(ifd, (char *)&zeros, fill_size) != fill_size) { + fprintf(stderr, "Write error: %s\n", + strerror(errno)); + exit(EXIT_FAILURE); + } + + size -= fill_size; + }; +} + +static void copy_file(int ifd, const char *datafile, int pad, int offset, + int datafile_offset) +{ + int dfd; + struct stat sbuf; + unsigned char *ptr; + int tail; + int zero = 0; + uint8_t zeros[4096]; + int size, ret; + + memset(zeros, 0, sizeof(zeros)); + + dfd = open(datafile, O_RDONLY | O_BINARY); + if (dfd < 0) { + fprintf(stderr, "Can't open %s: %s\n", + datafile, strerror(errno)); + exit(EXIT_FAILURE); + } + + if (fstat(dfd, &sbuf) < 0) { + fprintf(stderr, "Can't stat %s: %s\n", + datafile, strerror(errno)); + exit(EXIT_FAILURE); + } + + ptr = mmap(0, sbuf.st_size, PROT_READ, MAP_SHARED, dfd, 0); + if (ptr == MAP_FAILED) { + fprintf(stderr, "Can't read %s: %s\n", + datafile, strerror(errno)); + exit(EXIT_FAILURE); + } + + size = sbuf.st_size - datafile_offset; + ret = lseek(ifd, offset, SEEK_SET); + if (ret < 0) { + fprintf(stderr, "lseek ifd fail\n"); + exit(EXIT_FAILURE); + } + + if (write(ifd, ptr + datafile_offset, size) != size) { + fprintf(stderr, "Write error %s\n", + strerror(errno)); + exit(EXIT_FAILURE); + } + + tail = size % 4; + pad = pad - size; + if (pad == 1 && tail != 0) { + if (write(ifd, (char *)&zero, 4 - tail) != 4 - tail) { + fprintf(stderr, "Write error on %s\n", + strerror(errno)); + exit(EXIT_FAILURE); + } + } else if (pad > 1) { + while (pad > 0) { + int todo = sizeof(zeros); + + if (todo > pad) + todo = pad; + if (write(ifd, (char *)&zeros, todo) != todo) { + fprintf(stderr, "Write error: %s\n", + strerror(errno)); + exit(EXIT_FAILURE); + } + pad -= todo; + } + } + + munmap((void *)ptr, sbuf.st_size); + close(dfd); +} + +/* Return this IVT offset in the final output file */ +static int generate_ivt_for_fit(int fd, int fit_offset, uint32_t ep, + uint32_t *fit_load_addr) +{ + image_header_t image_header; + int ret; + + uint32_t fit_size, load_addr; + int align_len = 64 - 1; /* 64 is cacheline size */ + + ret = lseek(fd, fit_offset, SEEK_SET); + if (ret < 0) { + fprintf(stderr, "lseek fd fail for fit\n"); + exit(EXIT_FAILURE); + } + + if (read(fd, (char *)&image_header, sizeof(image_header_t)) != + sizeof(image_header_t)) { + fprintf(stderr, "generate_ivt_for_fit read failed: %s\n", + strerror(errno)); + exit(EXIT_FAILURE); + } + + if (be32_to_cpu(image_header.ih_magic) != FDT_MAGIC) { + fprintf(stderr, "%s error: not a FIT file\n", __func__); + exit(EXIT_FAILURE); + } + + fit_size = fdt_totalsize(&image_header); + fit_size = (fit_size + 3) & ~3; + + fit_size = ALIGN(fit_size, ALIGN_SIZE); + + ret = lseek(fd, fit_offset + fit_size, SEEK_SET); + if (ret < 0) { + fprintf(stderr, "lseek fd fail for fit\n"); + exit(EXIT_FAILURE); + } + + /* + * ep is the u-boot entry. SPL loads the FIT before the u-boot + * address. 0x2000 is for CSF_SIZE + */ + load_addr = (ep - (fit_size + CSF_SIZE) - 512 - align_len) & + ~align_len; + + flash_header_v2_t ivt_header = { { 0xd1, 0x2000, 0x40 }, + load_addr, 0, 0, 0, + (load_addr + fit_size), + (load_addr + fit_size + 0x20), + 0 }; + + if (write(fd, &ivt_header, sizeof(flash_header_v2_t)) != + sizeof(flash_header_v2_t)) { + fprintf(stderr, "IVT writing error on fit image\n"); + exit(EXIT_FAILURE); + } + + *fit_load_addr = load_addr; + + return fit_offset + fit_size; +} + +static void dump_header_v2(imx_header_v3_t *imx_header, int index) +{ + const char *ivt_name[2] = {"HDMI FW", "LOADER IMAGE"}; + + fprintf(stdout, "========= IVT HEADER [%s] =========\n", + ivt_name[index]); + fprintf(stdout, "header.tag: \t\t0x%x\n", + imx_header[index].fhdr.header.tag); + fprintf(stdout, "header.length: \t\t0x%x\n", + imx_header[index].fhdr.header.length); + fprintf(stdout, "header.version: \t0x%x\n", + imx_header[index].fhdr.header.version); + fprintf(stdout, "entry: \t\t\t0x%x\n", + imx_header[index].fhdr.entry); + fprintf(stdout, "reserved1: \t\t0x%x\n", + imx_header[index].fhdr.reserved1); + fprintf(stdout, "dcd_ptr: \t\t0x%x\n", + imx_header[index].fhdr.dcd_ptr); + fprintf(stdout, "boot_data_ptr: \t\t0x%x\n", + imx_header[index].fhdr.boot_data_ptr); + fprintf(stdout, "self: \t\t\t0x%x\n", + imx_header[index].fhdr.self); + fprintf(stdout, "csf: \t\t\t0x%x\n", + imx_header[index].fhdr.csf); + fprintf(stdout, "reserved2: \t\t0x%x\n", + imx_header[index].fhdr.reserved2); + + fprintf(stdout, "boot_data.start: \t0x%x\n", + imx_header[index].boot_data.start); + fprintf(stdout, "boot_data.size: \t0x%x\n", + imx_header[index].boot_data.size); + fprintf(stdout, "boot_data.plugin: \t0x%x\n", + imx_header[index].boot_data.plugin); +} + +void build_image(int ofd) +{ + int file_off, header_hdmi_off = 0, header_image_off; + int hdmi_fd, ap_fd, sld_fd; + uint32_t sld_load_addr = 0; + uint32_t csf_off, sld_csf_off = 0; + int ret; + struct stat sbuf; + + if (!ap_img) { + fprintf(stderr, "No LOADER image specificed\n"); + exit(EXIT_FAILURE); + } + + file_off = 0; + + if (signed_hdmi) { + header_hdmi_off = file_off + ivt_offset; + + hdmi_fd = open(signed_hdmi, O_RDONLY | O_BINARY); + if (hdmi_fd < 0) { + fprintf(stderr, "%s: Can't open: %s\n", + signed_hdmi, strerror(errno)); + exit(EXIT_FAILURE); + } + + if (fstat(hdmi_fd, &sbuf) < 0) { + fprintf(stderr, "%s: Can't stat: %s\n", + signed_hdmi, strerror(errno)); + exit(EXIT_FAILURE); + } + close(hdmi_fd); + + /* + * Aligned to 104KB = 92KB FW image + 0x8000 + * (IVT and alignment) + 0x4000 (second IVT + CSF) + */ + file_off += ALIGN(sbuf.st_size, + HDMI_FW_SIZE + 0x2000 + 0x1000); + } + + header_image_off = file_off + ivt_offset; + + ap_fd = open(ap_img, O_RDONLY | O_BINARY); + if (ap_fd < 0) { + fprintf(stderr, "%s: Can't open: %s\n", + ap_img, strerror(errno)); + exit(EXIT_FAILURE); + } + if (fstat(ap_fd, &sbuf) < 0) { + fprintf(stderr, "%s: Can't stat: %s\n", + ap_img, strerror(errno)); + exit(EXIT_FAILURE); + } + close(ap_fd); + + imx_header[IMAGE_IVT_ID].fhdr.header.tag = IVT_HEADER_TAG; /* 0xD1 */ + imx_header[IMAGE_IVT_ID].fhdr.header.length = + cpu_to_be16(sizeof(flash_header_v2_t)); + imx_header[IMAGE_IVT_ID].fhdr.header.version = IVT_VERSION_V3; /* 0x41 */ + imx_header[IMAGE_IVT_ID].fhdr.entry = ap_start_addr; + imx_header[IMAGE_IVT_ID].fhdr.self = ap_start_addr - + sizeof(imx_header_v3_t); + imx_header[IMAGE_IVT_ID].fhdr.dcd_ptr = 0; + imx_header[IMAGE_IVT_ID].fhdr.boot_data_ptr = + imx_header[IMAGE_IVT_ID].fhdr.self + + offsetof(imx_header_v3_t, boot_data); + imx_header[IMAGE_IVT_ID].boot_data.start = + imx_header[IMAGE_IVT_ID].fhdr.self - ivt_offset; + imx_header[IMAGE_IVT_ID].boot_data.size = + ALIGN(sbuf.st_size + sizeof(imx_header_v3_t) + ivt_offset, + sector_size); + + image_off = header_image_off + sizeof(imx_header_v3_t); + file_off += imx_header[IMAGE_IVT_ID].boot_data.size; + + imx_header[IMAGE_IVT_ID].boot_data.plugin = 0; + imx_header[IMAGE_IVT_ID].fhdr.csf = + imx_header[IMAGE_IVT_ID].boot_data.start + + imx_header[IMAGE_IVT_ID].boot_data.size; + + imx_header[IMAGE_IVT_ID].boot_data.size += CSF_SIZE; /* 8K region dummy CSF */ + + csf_off = file_off; + file_off += CSF_SIZE; + + /* Second boot loader image */ + if (sld_img) { + if (!using_fit) { + fprintf(stderr, "Not support no fit\n"); + exit(EXIT_FAILURE); + } else { + sld_header_off = sld_src_off - rom_image_offset; + /* + * Record the second bootloader relative offset in + * image's IVT reserved1 + */ + imx_header[IMAGE_IVT_ID].fhdr.reserved1 = + sld_header_off - header_image_off; + sld_fd = open(sld_img, O_RDONLY | O_BINARY); + if (sld_fd < 0) { + fprintf(stderr, "%s: Can't open: %s\n", + sld_img, strerror(errno)); + exit(EXIT_FAILURE); + } + + if (fstat(sld_fd, &sbuf) < 0) { + fprintf(stderr, "%s: Can't stat: %s\n", + sld_img, strerror(errno)); + exit(EXIT_FAILURE); + } + + close(sld_fd); + + file_off = sld_header_off; + file_off += sbuf.st_size + sizeof(image_header_t); + } + } + + if (signed_hdmi) { + header_hdmi_off -= ivt_offset; + ret = lseek(ofd, header_hdmi_off, SEEK_SET); + if (ret < 0) { + fprintf(stderr, "lseek ofd fail for hdmi\n"); + exit(EXIT_FAILURE); + } + + /* The signed HDMI FW has 0x400 IVT offset, need remove it */ + copy_file(ofd, signed_hdmi, 0, header_hdmi_off, 0x400); + } + + /* Main Image */ + header_image_off -= ivt_offset; + image_off -= ivt_offset; + ret = lseek(ofd, header_image_off, SEEK_SET); + if (ret < 0) { + fprintf(stderr, "lseek ofd fail\n"); + exit(EXIT_FAILURE); + } + + /* Write image header */ + if (write(ofd, &imx_header[IMAGE_IVT_ID], sizeof(imx_header_v3_t)) != + sizeof(imx_header_v3_t)) { + fprintf(stderr, "error writing image hdr\n"); + exit(1); + } + + copy_file(ofd, ap_img, 0, image_off, 0); + + csf_off -= ivt_offset; + fill_zero(ofd, CSF_SIZE, csf_off); + + if (sld_img) { + sld_header_off -= ivt_offset; + ret = lseek(ofd, sld_header_off, SEEK_SET); + if (ret < 0) { + fprintf(stderr, "lseek ofd fail for sld_img\n"); + exit(EXIT_FAILURE); + } + + /* Write image header */ + if (!using_fit) { + /* TODO */ + } else { + copy_file(ofd, sld_img, 0, sld_header_off, 0); + sld_csf_off = + generate_ivt_for_fit(ofd, sld_header_off, + sld_start_addr, + &sld_load_addr) + 0x20; + } + } + + if (!signed_hdmi) + dump_header_v2(imx_header, 0); + dump_header_v2(imx_header, 1); + + fprintf(stdout, "========= OFFSET dump ========="); + if (signed_hdmi) { + fprintf(stdout, "\nSIGNED HDMI FW:\n"); + fprintf(stdout, " header_hdmi_off \t0x%x\n", + header_hdmi_off); + } + + fprintf(stdout, "\nLoader IMAGE:\n"); + fprintf(stdout, " header_image_off \t0x%x\n image_off \t\t0x%x\n csf_off \t\t0x%x\n", + header_image_off, image_off, csf_off); + fprintf(stdout, " spl hab block: \t0x%x 0x%x 0x%x\n", + imx_header[IMAGE_IVT_ID].fhdr.self, header_image_off, + csf_off - header_image_off); + + fprintf(stdout, "\nSecond Loader IMAGE:\n"); + fprintf(stdout, " sld_header_off \t0x%x\n", + sld_header_off); + fprintf(stdout, " sld_csf_off \t\t0x%x\n", + sld_csf_off); + fprintf(stdout, " sld hab block: \t0x%x 0x%x 0x%x\n", + sld_load_addr, sld_header_off, sld_csf_off - sld_header_off); +} + +int imx8mimage_copy_image(int outfd, struct image_tool_params *mparams) +{ + /* + * SECO FW is a container image, this is to calculate the + * 2nd container offset. + */ + fprintf(stdout, "parsing %s\n", mparams->imagename); + parse_cfg_file(mparams->imagename); + + build_image(outfd); + + return 0; +} + +/* + * imx8mimage parameters + */ +U_BOOT_IMAGE_TYPE( + imx8mimage, + "NXP i.MX8M Boot Image support", + 0, + NULL, + imx8mimage_check_params, + NULL, + imx8mimage_print_header, + imx8mimage_set_header, + NULL, + imx8mimage_check_image_types, + NULL, + NULL +); diff --git a/tools/u-boot-tools/imx8mimage.o b/tools/u-boot-tools/imx8mimage.o new file mode 100644 index 0000000000000000000000000000000000000000..33f2bdc367edd696c1bed64c20303aaa52a383df GIT binary patch literal 21728 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 Gbo~Hv>*|F7 literal 0 HcmV?d00001 diff --git a/tools/u-boot-tools/imx_cntr_image.sh b/tools/u-boot-tools/imx_cntr_image.sh new file mode 100755 index 0000000..972b95c --- /dev/null +++ b/tools/u-boot-tools/imx_cntr_image.sh @@ -0,0 +1,29 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-2.0+ +# +# script to check whether the file exists in imximage.cfg for i.MX8 +# +# usage: $0 <imximage.cfg> + +file=$1 + +blobs=`awk '/^APPEND/ {print $2} /^IMAGE/ || /^DATA/ {print $3}' $file` +for f in $blobs; do + tmp=$srctree/$f + if [ $f = "u-boot-dtb.bin" ]; then + continue + fi + + if [ -f $f ]; then + continue + fi + + if [ ! -f $tmp ]; then + echo "WARNING '$tmp' not found, resulting binary is not-functional" >&2 + exit 1 + fi + + sed -in "s;$f;$tmp;" $file +done + +exit 0 diff --git a/tools/u-boot-tools/imximage.c b/tools/u-boot-tools/imximage.c new file mode 100644 index 0000000..d7c0b6e --- /dev/null +++ b/tools/u-boot-tools/imximage.c @@ -0,0 +1,996 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2009 + * Stefano Babic, DENX Software Engineering, sbabic@denx.de. + * + * (C) Copyright 2008 + * Marvell Semiconductor <www.marvell.com> + * Written-by: Prafulla Wadaskar <prafulla@marvell.com> + */ + +#include "imagetool.h" +#include <image.h> +#include "imximage.h" + +#define UNDEFINED 0xFFFFFFFF + +/* + * Supported commands for configuration file + */ +static table_entry_t imximage_cmds[] = { + {CMD_BOOT_FROM, "BOOT_FROM", "boot command", }, + {CMD_BOOT_OFFSET, "BOOT_OFFSET", "Boot offset", }, + {CMD_WRITE_DATA, "DATA", "Reg Write Data", }, + {CMD_WRITE_CLR_BIT, "CLR_BIT", "Reg clear bit", }, + {CMD_WRITE_SET_BIT, "SET_BIT", "Reg set bit", }, + {CMD_CHECK_BITS_SET, "CHECK_BITS_SET", "Reg Check bits set", }, + {CMD_CHECK_BITS_CLR, "CHECK_BITS_CLR", "Reg Check bits clr", }, + {CMD_CSF, "CSF", "Command Sequence File", }, + {CMD_IMAGE_VERSION, "IMAGE_VERSION", "image version", }, + {CMD_PLUGIN, "PLUGIN", "file plugin_addr", }, + {-1, "", "", }, +}; + +/* + * Supported Boot options for configuration file + * this is needed to set the correct flash offset + */ +static table_entry_t imximage_boot_offset[] = { + {FLASH_OFFSET_ONENAND, "onenand", "OneNAND Flash",}, + {FLASH_OFFSET_NAND, "nand", "NAND Flash", }, + {FLASH_OFFSET_NOR, "nor", "NOR Flash", }, + {FLASH_OFFSET_SATA, "sata", "SATA Disk", }, + {FLASH_OFFSET_SD, "sd", "SD Card", }, + {FLASH_OFFSET_SPI, "spi", "SPI Flash", }, + {FLASH_OFFSET_QSPI, "qspi", "QSPI NOR Flash",}, + {-1, "", "Invalid", }, +}; + +/* + * Supported Boot options for configuration file + * this is needed to determine the initial load size + */ +static table_entry_t imximage_boot_loadsize[] = { + {FLASH_LOADSIZE_ONENAND, "onenand", "OneNAND Flash",}, + {FLASH_LOADSIZE_NAND, "nand", "NAND Flash", }, + {FLASH_LOADSIZE_NOR, "nor", "NOR Flash", }, + {FLASH_LOADSIZE_SATA, "sata", "SATA Disk", }, + {FLASH_LOADSIZE_SD, "sd", "SD Card", }, + {FLASH_LOADSIZE_SPI, "spi", "SPI Flash", }, + {FLASH_LOADSIZE_QSPI, "qspi", "QSPI NOR Flash",}, + {-1, "", "Invalid", }, +}; + +/* + * IMXIMAGE version definition for i.MX chips + */ +static table_entry_t imximage_versions[] = { + {IMXIMAGE_V1, "", " (i.MX25/35/51 compatible)", }, + {IMXIMAGE_V2, "", " (i.MX53/6/7 compatible)", }, + {-1, "", " (Invalid)", }, +}; + +static struct imx_header imximage_header; +static uint32_t imximage_version; +/* + * Image Vector Table Offset + * Initialized to a wrong not 4-bytes aligned address to + * check if it is was set by the cfg file. + */ +static uint32_t imximage_ivt_offset = UNDEFINED; +static uint32_t imximage_csf_size = UNDEFINED; +/* Initial Load Region Size */ +static uint32_t imximage_init_loadsize; +static uint32_t imximage_iram_free_start; +static uint32_t imximage_plugin_size; +static uint32_t plugin_image; + +static set_dcd_val_t set_dcd_val; +static set_dcd_param_t set_dcd_param; +static set_dcd_rst_t set_dcd_rst; +static set_imx_hdr_t set_imx_hdr; +static uint32_t max_dcd_entries; +static uint32_t *header_size_ptr; +static uint32_t *csf_ptr; + +static uint32_t get_cfg_value(char *token, char *name, int linenr) +{ + char *endptr; + uint32_t value; + + errno = 0; + value = strtoul(token, &endptr, 16); + if (errno || (token == endptr)) { + fprintf(stderr, "Error: %s[%d] - Invalid hex data(%s)\n", + name, linenr, token); + exit(EXIT_FAILURE); + } + return value; +} + +static uint32_t detect_imximage_version(struct imx_header *imx_hdr) +{ + imx_header_v1_t *hdr_v1 = &imx_hdr->header.hdr_v1; + imx_header_v2_t *hdr_v2 = &imx_hdr->header.hdr_v2; + flash_header_v1_t *fhdr_v1 = &hdr_v1->fhdr; + flash_header_v2_t *fhdr_v2 = &hdr_v2->fhdr; + + /* Try to detect V1 */ + if ((fhdr_v1->app_code_barker == APP_CODE_BARKER) && + (hdr_v1->dcd_table.preamble.barker == DCD_BARKER)) + return IMXIMAGE_V1; + + /* Try to detect V2 */ + if ((fhdr_v2->header.tag == IVT_HEADER_TAG) && + (hdr_v2->data.dcd_table.header.tag == DCD_HEADER_TAG)) + return IMXIMAGE_V2; + + if ((fhdr_v2->header.tag == IVT_HEADER_TAG) && + hdr_v2->boot_data.plugin) + return IMXIMAGE_V2; + + return IMXIMAGE_VER_INVALID; +} + +static void err_imximage_version(int version) +{ + fprintf(stderr, + "Error: Unsupported imximage version:%d\n", version); + + exit(EXIT_FAILURE); +} + +static void set_dcd_val_v1(struct imx_header *imxhdr, char *name, int lineno, + int fld, uint32_t value, uint32_t off) +{ + dcd_v1_t *dcd_v1 = &imxhdr->header.hdr_v1.dcd_table; + + switch (fld) { + case CFG_REG_SIZE: + /* Byte, halfword, word */ + if ((value != 1) && (value != 2) && (value != 4)) { + fprintf(stderr, "Error: %s[%d] - " + "Invalid register size " "(%d)\n", + name, lineno, value); + exit(EXIT_FAILURE); + } + dcd_v1->addr_data[off].type = value; + break; + case CFG_REG_ADDRESS: + dcd_v1->addr_data[off].addr = value; + break; + case CFG_REG_VALUE: + dcd_v1->addr_data[off].value = value; + break; + default: + break; + + } +} + +static struct dcd_v2_cmd *gd_last_cmd; + +static void set_dcd_param_v2(struct imx_header *imxhdr, uint32_t dcd_len, + int32_t cmd) +{ + dcd_v2_t *dcd_v2 = &imxhdr->header.hdr_v2.data.dcd_table; + struct dcd_v2_cmd *d = gd_last_cmd; + struct dcd_v2_cmd *d2; + int len; + + if (!d) + d = &dcd_v2->dcd_cmd; + d2 = d; + len = be16_to_cpu(d->write_dcd_command.length); + if (len > 4) + d2 = (struct dcd_v2_cmd *)(((char *)d) + len); + + switch (cmd) { + case CMD_WRITE_DATA: + if ((d->write_dcd_command.tag == DCD_WRITE_DATA_COMMAND_TAG) && + (d->write_dcd_command.param == DCD_WRITE_DATA_PARAM)) + break; + d = d2; + d->write_dcd_command.tag = DCD_WRITE_DATA_COMMAND_TAG; + d->write_dcd_command.length = cpu_to_be16(4); + d->write_dcd_command.param = DCD_WRITE_DATA_PARAM; + break; + case CMD_WRITE_CLR_BIT: + if ((d->write_dcd_command.tag == DCD_WRITE_DATA_COMMAND_TAG) && + (d->write_dcd_command.param == DCD_WRITE_CLR_BIT_PARAM)) + break; + d = d2; + d->write_dcd_command.tag = DCD_WRITE_DATA_COMMAND_TAG; + d->write_dcd_command.length = cpu_to_be16(4); + d->write_dcd_command.param = DCD_WRITE_CLR_BIT_PARAM; + break; + case CMD_WRITE_SET_BIT: + if ((d->write_dcd_command.tag == DCD_WRITE_DATA_COMMAND_TAG) && + (d->write_dcd_command.param == DCD_WRITE_SET_BIT_PARAM)) + break; + d = d2; + d->write_dcd_command.tag = DCD_WRITE_DATA_COMMAND_TAG; + d->write_dcd_command.length = cpu_to_be16(4); + d->write_dcd_command.param = DCD_WRITE_SET_BIT_PARAM; + break; + /* + * Check data command only supports one entry, + */ + case CMD_CHECK_BITS_SET: + d = d2; + d->write_dcd_command.tag = DCD_CHECK_DATA_COMMAND_TAG; + d->write_dcd_command.length = cpu_to_be16(4); + d->write_dcd_command.param = DCD_CHECK_BITS_SET_PARAM; + break; + case CMD_CHECK_BITS_CLR: + d = d2; + d->write_dcd_command.tag = DCD_CHECK_DATA_COMMAND_TAG; + d->write_dcd_command.length = cpu_to_be16(4); + d->write_dcd_command.param = DCD_CHECK_BITS_CLR_PARAM; + break; + default: + break; + } + gd_last_cmd = d; +} + +static void set_dcd_val_v2(struct imx_header *imxhdr, char *name, int lineno, + int fld, uint32_t value, uint32_t off) +{ + struct dcd_v2_cmd *d = gd_last_cmd; + int len; + + len = be16_to_cpu(d->write_dcd_command.length); + off = (len - 4) >> 3; + + switch (fld) { + case CFG_REG_ADDRESS: + d->addr_data[off].addr = cpu_to_be32(value); + break; + case CFG_REG_VALUE: + d->addr_data[off].value = cpu_to_be32(value); + off++; + d->write_dcd_command.length = cpu_to_be16((off << 3) + 4); + break; + default: + break; + + } +} + +/* + * Complete setting up the rest field of DCD of V1 + * such as barker code and DCD data length. + */ +static void set_dcd_rst_v1(struct imx_header *imxhdr, uint32_t dcd_len, + char *name, int lineno) +{ + dcd_v1_t *dcd_v1 = &imxhdr->header.hdr_v1.dcd_table; + + dcd_v1->preamble.barker = DCD_BARKER; + dcd_v1->preamble.length = dcd_len * sizeof(dcd_type_addr_data_t); +} + +/* + * Complete setting up the reset field of DCD of V2 + * such as DCD tag, version, length, etc. + */ +static void set_dcd_rst_v2(struct imx_header *imxhdr, uint32_t dcd_len, + char *name, int lineno) +{ + if (!imxhdr->header.hdr_v2.boot_data.plugin) { + dcd_v2_t *dcd_v2 = &imxhdr->header.hdr_v2.data.dcd_table; + struct dcd_v2_cmd *d = gd_last_cmd; + int len; + + if (!d) + d = &dcd_v2->dcd_cmd; + len = be16_to_cpu(d->write_dcd_command.length); + if (len > 4) + d = (struct dcd_v2_cmd *)(((char *)d) + len); + + len = (char *)d - (char *)&dcd_v2->header; + dcd_v2->header.tag = DCD_HEADER_TAG; + dcd_v2->header.length = cpu_to_be16(len); + dcd_v2->header.version = DCD_VERSION; + } +} + +static void set_imx_hdr_v1(struct imx_header *imxhdr, uint32_t dcd_len, + uint32_t entry_point, uint32_t flash_offset) +{ + imx_header_v1_t *hdr_v1 = &imxhdr->header.hdr_v1; + flash_header_v1_t *fhdr_v1 = &hdr_v1->fhdr; + dcd_v1_t *dcd_v1 = &hdr_v1->dcd_table; + uint32_t hdr_base; + uint32_t header_length = (((char *)&dcd_v1->addr_data[dcd_len].addr) + - ((char *)imxhdr)); + + /* Set magic number */ + fhdr_v1->app_code_barker = APP_CODE_BARKER; + + hdr_base = entry_point - imximage_init_loadsize + flash_offset; + fhdr_v1->app_dest_ptr = hdr_base - flash_offset; + fhdr_v1->app_code_jump_vector = entry_point; + + fhdr_v1->dcd_ptr_ptr = hdr_base + offsetof(flash_header_v1_t, dcd_ptr); + fhdr_v1->dcd_ptr = hdr_base + offsetof(imx_header_v1_t, dcd_table); + + /* Security feature are not supported */ + fhdr_v1->app_code_csf = 0; + fhdr_v1->super_root_key = 0; + header_size_ptr = (uint32_t *)(((char *)imxhdr) + header_length - 4); +} + +static void set_imx_hdr_v2(struct imx_header *imxhdr, uint32_t dcd_len, + uint32_t entry_point, uint32_t flash_offset) +{ + imx_header_v2_t *hdr_v2 = &imxhdr->header.hdr_v2; + flash_header_v2_t *fhdr_v2 = &hdr_v2->fhdr; + uint32_t hdr_base; + + /* Set magic number */ + fhdr_v2->header.tag = IVT_HEADER_TAG; /* 0xD1 */ + fhdr_v2->header.length = cpu_to_be16(sizeof(flash_header_v2_t)); + fhdr_v2->header.version = IVT_VERSION; /* 0x40 */ + + if (!hdr_v2->boot_data.plugin) { + fhdr_v2->entry = entry_point; + fhdr_v2->reserved1 = 0; + fhdr_v2->reserved1 = 0; + hdr_base = entry_point - imximage_init_loadsize + + flash_offset; + fhdr_v2->self = hdr_base; + if (dcd_len > 0) + fhdr_v2->dcd_ptr = hdr_base + + offsetof(imx_header_v2_t, data); + else + fhdr_v2->dcd_ptr = 0; + fhdr_v2->boot_data_ptr = hdr_base + + offsetof(imx_header_v2_t, boot_data); + hdr_v2->boot_data.start = entry_point - imximage_init_loadsize; + + fhdr_v2->csf = 0; + + header_size_ptr = &hdr_v2->boot_data.size; + csf_ptr = &fhdr_v2->csf; + } else { + imx_header_v2_t *next_hdr_v2; + flash_header_v2_t *next_fhdr_v2; + + if (imximage_csf_size != 0) { + fprintf(stderr, "Error: Header v2: SECURE_BOOT is only supported in DCD mode!"); + exit(EXIT_FAILURE); + } + + fhdr_v2->entry = imximage_iram_free_start + + flash_offset + sizeof(flash_header_v2_t) + + sizeof(boot_data_t); + + fhdr_v2->reserved1 = 0; + fhdr_v2->reserved2 = 0; + fhdr_v2->self = imximage_iram_free_start + flash_offset; + + fhdr_v2->dcd_ptr = 0; + + fhdr_v2->boot_data_ptr = fhdr_v2->self + + offsetof(imx_header_v2_t, boot_data); + + hdr_v2->boot_data.start = imximage_iram_free_start; + /* + * The actural size of plugin image is "imximage_plugin_size + + * sizeof(flash_header_v2_t) + sizeof(boot_data_t)", plus the + * flash_offset space.The ROM code only need to copy this size + * to run the plugin code. However, later when copy the whole + * U-Boot image to DDR, the ROM code use memcpy to copy the + * first part of the image, and use the storage read function + * to get the remaining part. This requires the dividing point + * must be multiple of storage sector size. Here we set the + * first section to be MAX_PLUGIN_CODE_SIZE(64KB) for this + * purpose. + */ + hdr_v2->boot_data.size = MAX_PLUGIN_CODE_SIZE; + + /* Security feature are not supported */ + fhdr_v2->csf = 0; + + next_hdr_v2 = (imx_header_v2_t *)((char *)hdr_v2 + + imximage_plugin_size); + + next_fhdr_v2 = &next_hdr_v2->fhdr; + + next_fhdr_v2->header.tag = IVT_HEADER_TAG; /* 0xD1 */ + next_fhdr_v2->header.length = + cpu_to_be16(sizeof(flash_header_v2_t)); + next_fhdr_v2->header.version = IVT_VERSION; /* 0x40 */ + + next_fhdr_v2->entry = entry_point; + hdr_base = entry_point - sizeof(struct imx_header); + next_fhdr_v2->reserved1 = 0; + next_fhdr_v2->reserved2 = 0; + next_fhdr_v2->self = hdr_base + imximage_plugin_size; + + next_fhdr_v2->dcd_ptr = 0; + next_fhdr_v2->boot_data_ptr = next_fhdr_v2->self + + offsetof(imx_header_v2_t, boot_data); + + next_hdr_v2->boot_data.start = hdr_base - flash_offset; + + header_size_ptr = &next_hdr_v2->boot_data.size; + + next_hdr_v2->boot_data.plugin = 0; + + next_fhdr_v2->csf = 0; + } +} + +static void set_hdr_func(void) +{ + switch (imximage_version) { + case IMXIMAGE_V1: + set_dcd_val = set_dcd_val_v1; + set_dcd_param = NULL; + set_dcd_rst = set_dcd_rst_v1; + set_imx_hdr = set_imx_hdr_v1; + max_dcd_entries = MAX_HW_CFG_SIZE_V1; + break; + case IMXIMAGE_V2: + gd_last_cmd = NULL; + set_dcd_val = set_dcd_val_v2; + set_dcd_param = set_dcd_param_v2; + set_dcd_rst = set_dcd_rst_v2; + set_imx_hdr = set_imx_hdr_v2; + max_dcd_entries = MAX_HW_CFG_SIZE_V2; + break; + default: + err_imximage_version(imximage_version); + break; + } +} + +static void print_hdr_v1(struct imx_header *imx_hdr) +{ + imx_header_v1_t *hdr_v1 = &imx_hdr->header.hdr_v1; + flash_header_v1_t *fhdr_v1 = &hdr_v1->fhdr; + dcd_v1_t *dcd_v1 = &hdr_v1->dcd_table; + uint32_t size, length, ver; + + size = dcd_v1->preamble.length; + if (size > (MAX_HW_CFG_SIZE_V1 * sizeof(dcd_type_addr_data_t))) { + fprintf(stderr, + "Error: Image corrupt DCD size %d exceed maximum %d\n", + (uint32_t)(size / sizeof(dcd_type_addr_data_t)), + MAX_HW_CFG_SIZE_V1); + exit(EXIT_FAILURE); + } + + length = dcd_v1->preamble.length / sizeof(dcd_type_addr_data_t); + ver = detect_imximage_version(imx_hdr); + + printf("Image Type: Freescale IMX Boot Image\n"); + printf("Image Ver: %x", ver); + printf("%s\n", get_table_entry_name(imximage_versions, NULL, ver)); + printf("Data Size: "); + genimg_print_size(dcd_v1->addr_data[length].type); + printf("Load Address: %08x\n", (uint32_t)fhdr_v1->app_dest_ptr); + printf("Entry Point: %08x\n", (uint32_t)fhdr_v1->app_code_jump_vector); +} + +static void print_hdr_v2(struct imx_header *imx_hdr) +{ + imx_header_v2_t *hdr_v2 = &imx_hdr->header.hdr_v2; + flash_header_v2_t *fhdr_v2 = &hdr_v2->fhdr; + dcd_v2_t *dcd_v2 = &hdr_v2->data.dcd_table; + uint32_t size, version, plugin; + + plugin = hdr_v2->boot_data.plugin; + if (!plugin) { + size = be16_to_cpu(dcd_v2->header.length); + if (size > (MAX_HW_CFG_SIZE_V2 * sizeof(dcd_addr_data_t))) { + fprintf(stderr, + "Error: Image corrupt DCD size %d exceed maximum %d\n", + (uint32_t)(size / sizeof(dcd_addr_data_t)), + MAX_HW_CFG_SIZE_V2); + exit(EXIT_FAILURE); + } + } + + version = detect_imximage_version(imx_hdr); + + printf("Image Type: Freescale IMX Boot Image\n"); + printf("Image Ver: %x", version); + printf("%s\n", get_table_entry_name(imximage_versions, NULL, version)); + printf("Mode: %s\n", plugin ? "PLUGIN" : "DCD"); + if (!plugin) { + printf("Data Size: "); + genimg_print_size(hdr_v2->boot_data.size); + printf("Load Address: %08x\n", (uint32_t)fhdr_v2->boot_data_ptr); + printf("Entry Point: %08x\n", (uint32_t)fhdr_v2->entry); + if (fhdr_v2->csf) { + uint16_t dcdlen; + int offs; + + dcdlen = hdr_v2->data.dcd_table.header.length; + offs = (char *)&hdr_v2->data.dcd_table + - (char *)hdr_v2; + + /* + * The HAB block is the first part of the image, from + * start of IVT header (fhdr_v2->self) to the start of + * the CSF block (fhdr_v2->csf). So HAB size is + * calculated as: + * HAB_size = fhdr_v2->csf - fhdr_v2->self + */ + printf("HAB Blocks: 0x%08x 0x%08x 0x%08x\n", + (uint32_t)fhdr_v2->self, 0, + (uint32_t)(fhdr_v2->csf - fhdr_v2->self)); + printf("DCD Blocks: 0x00910000 0x%08x 0x%08x\n", + offs, be16_to_cpu(dcdlen)); + } + } else { + imx_header_v2_t *next_hdr_v2; + flash_header_v2_t *next_fhdr_v2; + + /*First Header*/ + printf("Plugin Data Size: "); + genimg_print_size(hdr_v2->boot_data.size); + printf("Plugin Code Size: "); + genimg_print_size(imximage_plugin_size); + printf("Plugin Load Address: %08x\n", hdr_v2->boot_data.start); + printf("Plugin Entry Point: %08x\n", (uint32_t)fhdr_v2->entry); + + /*Second Header*/ + next_hdr_v2 = (imx_header_v2_t *)((char *)hdr_v2 + + imximage_plugin_size); + next_fhdr_v2 = &next_hdr_v2->fhdr; + printf("U-Boot Data Size: "); + genimg_print_size(next_hdr_v2->boot_data.size); + printf("U-Boot Load Address: %08x\n", + next_hdr_v2->boot_data.start); + printf("U-Boot Entry Point: %08x\n", + (uint32_t)next_fhdr_v2->entry); + } +} + +static void copy_plugin_code(struct imx_header *imxhdr, char *plugin_file) +{ + int ifd; + struct stat sbuf; + char *plugin_buf = imxhdr->header.hdr_v2.data.plugin_code; + char *ptr; + + ifd = open(plugin_file, O_RDONLY|O_BINARY); + if (ifd < 0) { + fprintf(stderr, "Can't open %s: %s\n", + plugin_file, + strerror(errno)); + exit(EXIT_FAILURE); + } + + if (fstat(ifd, &sbuf) < 0) { + fprintf(stderr, "Can't stat %s: %s\n", + plugin_file, + strerror(errno)); + exit(EXIT_FAILURE); + } + + ptr = mmap(0, sbuf.st_size, PROT_READ, MAP_SHARED, ifd, 0); + if (ptr == MAP_FAILED) { + fprintf(stderr, "Can't read %s: %s\n", + plugin_file, + strerror(errno)); + exit(EXIT_FAILURE); + } + + if (sbuf.st_size > MAX_PLUGIN_CODE_SIZE) { + printf("plugin binary size too large\n"); + exit(EXIT_FAILURE); + } + + memcpy(plugin_buf, ptr, sbuf.st_size); + imximage_plugin_size = sbuf.st_size; + + (void) munmap((void *)ptr, sbuf.st_size); + (void) close(ifd); + + imxhdr->header.hdr_v2.boot_data.plugin = 1; +} + +static void parse_cfg_cmd(struct imx_header *imxhdr, int32_t cmd, char *token, + char *name, int lineno, int fld, int dcd_len) +{ + int value; + static int cmd_ver_first = ~0; + + switch (cmd) { + case CMD_IMAGE_VERSION: + imximage_version = get_cfg_value(token, name, lineno); + if (cmd_ver_first == 0) { + fprintf(stderr, "Error: %s[%d] - IMAGE_VERSION " + "command need be the first before other " + "valid command in the file\n", name, lineno); + exit(EXIT_FAILURE); + } + cmd_ver_first = 1; + set_hdr_func(); + break; + case CMD_BOOT_FROM: + imximage_ivt_offset = get_table_entry_id(imximage_boot_offset, + "imximage boot option", token); + if (imximage_ivt_offset == -1) { + fprintf(stderr, "Error: %s[%d] -Invalid boot device" + "(%s)\n", name, lineno, token); + exit(EXIT_FAILURE); + } + + imximage_init_loadsize = + get_table_entry_id(imximage_boot_loadsize, + "imximage boot option", token); + + if (imximage_init_loadsize == -1) { + fprintf(stderr, + "Error: %s[%d] -Invalid boot device(%s)\n", + name, lineno, token); + exit(EXIT_FAILURE); + } + + /* + * The SOC loads from the storage starting at address 0 + * then ensures that the load size contains the offset + */ + if (imximage_init_loadsize < imximage_ivt_offset) + imximage_init_loadsize = imximage_ivt_offset; + if (unlikely(cmd_ver_first != 1)) + cmd_ver_first = 0; + break; + case CMD_BOOT_OFFSET: + imximage_ivt_offset = get_cfg_value(token, name, lineno); + if (unlikely(cmd_ver_first != 1)) + cmd_ver_first = 0; + break; + case CMD_WRITE_DATA: + case CMD_WRITE_CLR_BIT: + case CMD_WRITE_SET_BIT: + case CMD_CHECK_BITS_SET: + case CMD_CHECK_BITS_CLR: + value = get_cfg_value(token, name, lineno); + if (set_dcd_param) + (*set_dcd_param)(imxhdr, dcd_len, cmd); + (*set_dcd_val)(imxhdr, name, lineno, fld, value, dcd_len); + if (unlikely(cmd_ver_first != 1)) + cmd_ver_first = 0; + break; + case CMD_CSF: + if (imximage_version != 2) { + fprintf(stderr, + "Error: %s[%d] - CSF only supported for VERSION 2(%s)\n", + name, lineno, token); + exit(EXIT_FAILURE); + } + imximage_csf_size = get_cfg_value(token, name, lineno); + if (unlikely(cmd_ver_first != 1)) + cmd_ver_first = 0; + break; + case CMD_PLUGIN: + plugin_image = 1; + copy_plugin_code(imxhdr, token); + break; + } +} + +static void parse_cfg_fld(struct imx_header *imxhdr, int32_t *cmd, + char *token, char *name, int lineno, int fld, int *dcd_len) +{ + int value; + + switch (fld) { + case CFG_COMMAND: + *cmd = get_table_entry_id(imximage_cmds, + "imximage commands", token); + if (*cmd < 0) { + fprintf(stderr, "Error: %s[%d] - Invalid command" + "(%s)\n", name, lineno, token); + exit(EXIT_FAILURE); + } + break; + case CFG_REG_SIZE: + parse_cfg_cmd(imxhdr, *cmd, token, name, lineno, fld, *dcd_len); + break; + case CFG_REG_ADDRESS: + case CFG_REG_VALUE: + switch(*cmd) { + case CMD_WRITE_DATA: + case CMD_WRITE_CLR_BIT: + case CMD_WRITE_SET_BIT: + case CMD_CHECK_BITS_SET: + case CMD_CHECK_BITS_CLR: + + value = get_cfg_value(token, name, lineno); + if (set_dcd_param) + (*set_dcd_param)(imxhdr, *dcd_len, *cmd); + (*set_dcd_val)(imxhdr, name, lineno, fld, value, + *dcd_len); + + if (fld == CFG_REG_VALUE) { + (*dcd_len)++; + if (*dcd_len > max_dcd_entries) { + fprintf(stderr, "Error: %s[%d] -" + "DCD table exceeds maximum size(%d)\n", + name, lineno, max_dcd_entries); + exit(EXIT_FAILURE); + } + } + break; + case CMD_PLUGIN: + value = get_cfg_value(token, name, lineno); + imximage_iram_free_start = value; + break; + default: + break; + } + break; + default: + break; + } +} +static uint32_t parse_cfg_file(struct imx_header *imxhdr, char *name) +{ + FILE *fd = NULL; + char *line = NULL; + char *token, *saveptr1, *saveptr2; + int lineno = 0; + int fld; + size_t len; + int dcd_len = 0; + int32_t cmd; + + fd = fopen(name, "r"); + if (fd == 0) { + fprintf(stderr, "Error: %s - Can't open DCD file\n", name); + exit(EXIT_FAILURE); + } + + /* + * Very simple parsing, line starting with # are comments + * and are dropped + */ + while ((getline(&line, &len, fd)) > 0) { + lineno++; + + token = strtok_r(line, "\r\n", &saveptr1); + if (token == NULL) + continue; + + /* Check inside the single line */ + for (fld = CFG_COMMAND, cmd = CMD_INVALID, + line = token; ; line = NULL, fld++) { + token = strtok_r(line, " \t", &saveptr2); + if (token == NULL) + break; + + /* Drop all text starting with '#' as comments */ + if (token[0] == '#') + break; + + parse_cfg_fld(imxhdr, &cmd, token, name, + lineno, fld, &dcd_len); + } + + } + + (*set_dcd_rst)(imxhdr, dcd_len, name, lineno); + fclose(fd); + + /* Exit if there is no BOOT_FROM field specifying the flash_offset */ + if (imximage_ivt_offset == FLASH_OFFSET_UNDEFINED) { + fprintf(stderr, "Error: No BOOT_FROM tag in %s\n", name); + exit(EXIT_FAILURE); + } + return dcd_len; +} + + +static int imximage_check_image_types(uint8_t type) +{ + if (type == IH_TYPE_IMXIMAGE) + return EXIT_SUCCESS; + else + return EXIT_FAILURE; +} + +static int imximage_verify_header(unsigned char *ptr, int image_size, + struct image_tool_params *params) +{ + struct imx_header *imx_hdr = (struct imx_header *) ptr; + + if (detect_imximage_version(imx_hdr) == IMXIMAGE_VER_INVALID) + return -FDT_ERR_BADSTRUCTURE; + + return 0; +} + +static void imximage_print_header(const void *ptr) +{ + struct imx_header *imx_hdr = (struct imx_header *) ptr; + uint32_t version = detect_imximage_version(imx_hdr); + + switch (version) { + case IMXIMAGE_V1: + print_hdr_v1(imx_hdr); + break; + case IMXIMAGE_V2: + print_hdr_v2(imx_hdr); + break; + default: + err_imximage_version(version); + break; + } +} + +static void imximage_set_header(void *ptr, struct stat *sbuf, int ifd, + struct image_tool_params *params) +{ + struct imx_header *imxhdr = (struct imx_header *)ptr; + uint32_t dcd_len; + uint32_t header_size; + + /* + * In order to not change the old imx cfg file + * by adding VERSION command into it, here need + * set up function ptr group to V1 by default. + */ + imximage_version = IMXIMAGE_V1; + /* Be able to detect if the cfg file has no BOOT_FROM tag */ + imximage_ivt_offset = FLASH_OFFSET_UNDEFINED; + imximage_csf_size = 0; + set_hdr_func(); + + /* Parse dcd configuration file */ + dcd_len = parse_cfg_file(imxhdr, params->imagename); + + if (imximage_version == IMXIMAGE_V1) + header_size = sizeof(flash_header_v1_t); + else { + header_size = sizeof(flash_header_v2_t) + sizeof(boot_data_t); + if (!plugin_image) + header_size += sizeof(dcd_v2_t); + else + header_size += MAX_PLUGIN_CODE_SIZE; + } + + if (imximage_init_loadsize < imximage_ivt_offset + header_size) + imximage_init_loadsize = imximage_ivt_offset + header_size; + + /* Set the imx header */ + (*set_imx_hdr)(imxhdr, dcd_len, params->ep, imximage_ivt_offset); + + /* + * ROM bug alert + * + * MX53 only loads 512 byte multiples in case of SD boot. + * MX53 only loads NAND page multiples in case of NAND boot and + * supports up to 4096 byte large pages, thus align to 4096. + * + * The remaining fraction of a block bytes would not be loaded! + */ + *header_size_ptr = ROUND((sbuf->st_size + imximage_ivt_offset), 4096); + + if (csf_ptr && imximage_csf_size) { + *csf_ptr = params->ep - imximage_init_loadsize + + *header_size_ptr; + *header_size_ptr += imximage_csf_size; + } +} + +int imximage_check_params(struct image_tool_params *params) +{ + if (!params) + return CFG_INVALID; + if (!strlen(params->imagename)) { + fprintf(stderr, "Error: %s - Configuration file not specified, " + "it is needed for imximage generation\n", + params->cmdname); + return CFG_INVALID; + } + /* + * Check parameters: + * XIP is not allowed and verify that incompatible + * parameters are not sent at the same time + * For example, if list is required a data image must not be provided + */ + return (params->dflag && (params->fflag || params->lflag)) || + (params->fflag && (params->dflag || params->lflag)) || + (params->lflag && (params->dflag || params->fflag)) || + (params->xflag) || !(strlen(params->imagename)); +} + +static int imximage_generate(struct image_tool_params *params, + struct image_type_params *tparams) +{ + struct imx_header *imxhdr; + size_t alloc_len; + struct stat sbuf; + char *datafile = params->datafile; + uint32_t pad_len, header_size; + + memset(&imximage_header, 0, sizeof(imximage_header)); + + /* + * In order to not change the old imx cfg file + * by adding VERSION command into it, here need + * set up function ptr group to V1 by default. + */ + imximage_version = IMXIMAGE_V1; + /* Be able to detect if the cfg file has no BOOT_FROM tag */ + imximage_ivt_offset = FLASH_OFFSET_UNDEFINED; + imximage_csf_size = 0; + set_hdr_func(); + + /* Parse dcd configuration file */ + parse_cfg_file(&imximage_header, params->imagename); + + if (imximage_version == IMXIMAGE_V1) + header_size = sizeof(imx_header_v1_t); + else { + header_size = sizeof(flash_header_v2_t) + sizeof(boot_data_t); + if (!plugin_image) + header_size += sizeof(dcd_v2_t); + else + header_size += MAX_PLUGIN_CODE_SIZE; + } + + if (imximage_init_loadsize < imximage_ivt_offset + header_size) + imximage_init_loadsize = imximage_ivt_offset + header_size; + + alloc_len = imximage_init_loadsize - imximage_ivt_offset; + + if (alloc_len < header_size) { + fprintf(stderr, "%s: header error\n", + params->cmdname); + exit(EXIT_FAILURE); + } + + imxhdr = malloc(alloc_len); + + if (!imxhdr) { + fprintf(stderr, "%s: malloc return failure: %s\n", + params->cmdname, strerror(errno)); + exit(EXIT_FAILURE); + } + + memset(imxhdr, 0, alloc_len); + + tparams->header_size = alloc_len; + tparams->hdr = imxhdr; + + /* determine data image file length */ + + if (stat(datafile, &sbuf) < 0) { + fprintf(stderr, "%s: Can't stat %s: %s\n", + params->cmdname, datafile, strerror(errno)); + exit(EXIT_FAILURE); + } + + pad_len = ROUND(sbuf.st_size, 4096) - sbuf.st_size; + + return pad_len; +} + + +/* + * imximage parameters + */ +U_BOOT_IMAGE_TYPE( + imximage, + "Freescale i.MX Boot Image support", + 0, + NULL, + imximage_check_params, + imximage_verify_header, + imximage_print_header, + imximage_set_header, + NULL, + imximage_check_image_types, + NULL, + imximage_generate +); diff --git a/tools/u-boot-tools/imximage.o b/tools/u-boot-tools/imximage.o new file mode 100644 index 0000000000000000000000000000000000000000..8d06d2b4bec88ad1c67cae73893f1b377736a3ba GIT binary patch literal 23240 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 literal 0 HcmV?d00001 diff --git a/tools/u-boot-tools/jtagconsole b/tools/u-boot-tools/jtagconsole new file mode 100755 index 0000000..d404fac --- /dev/null +++ b/tools/u-boot-tools/jtagconsole @@ -0,0 +1,39 @@ +#!/bin/sh + +usage() { + ( + echo "Usage: $0 [board IP] [board port]" + echo "" + echo "If IP is not specified, 'localhost' will be used" + echo "If port is not specified, '2001' will be used" + [ -z "$*" ] && exit 0 + echo "" + echo "ERROR: $*" + exit 1 + ) 1>&2 + exit $? +} + +while [ -n "$1" ] ; do + case $1 in + -h|--help) usage;; + --) break;; + -*) usage "Invalid option $1";; + *) break;; + esac + shift +done + +ip=${1:-localhost} +port=${2:-2001} + +if [ -z "${ip}" ] || [ -n "$3" ] ; then + usage "Invalid number of arguments" +fi + +trap "stty icanon echo opost intr ^C" 0 2 3 5 10 13 15 +echo "NOTE: the interrupt signal (normally ^C) has been remapped to ^T" + +stty -icanon -echo -opost intr ^T +nc ${ip} ${port} +exit 0 diff --git a/tools/u-boot-tools/k3_fit_atf.sh b/tools/u-boot-tools/k3_fit_atf.sh new file mode 100755 index 0000000..430b5ca --- /dev/null +++ b/tools/u-boot-tools/k3_fit_atf.sh @@ -0,0 +1,99 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-2.0+ +# +# script to generate FIT image source for K3 Family boards with +# ATF, OPTEE, SPL and multiple device trees (given on the command line). +# Inspired from board/sunxi/mksunxi_fit_atf.sh +# +# usage: $0 <dt_name> [<dt_name> [<dt_name] ...] + +[ -z "$ATF" ] && ATF="bl31.bin" + +if [ ! -f $ATF ]; then + echo "WARNING ATF file $ATF NOT found, resulting binary is non-functional" >&2 + ATF=/dev/null +fi + +[ -z "$TEE" ] && TEE="bl32.bin" + +if [ ! -f $TEE ]; then + echo "WARNING OPTEE file $TEE NOT found, resulting might be non-functional" >&2 + TEE=/dev/null +fi + +cat << __HEADER_EOF +/dts-v1/; + +/ { + description = "Configuration to load ATF and SPL"; + #address-cells = <1>; + + images { + atf { + description = "ARM Trusted Firmware"; + data = /incbin/("$ATF"); + type = "firmware"; + arch = "arm64"; + compression = "none"; + os = "arm-trusted-firmware"; + load = <0x70000000>; + entry = <0x70000000>; + }; + tee { + description = "OPTEE"; + data = /incbin/("$TEE"); + type = "tee"; + arch = "arm64"; + compression = "none"; + os = "tee"; + load = <0x9e800000>; + entry = <0x9e800000>; + }; + spl { + description = "SPL (64-bit)"; + data = /incbin/("spl/u-boot-spl-nodtb.bin"); + type = "standalone"; + os = "U-Boot"; + arch = "arm64"; + compression = "none"; + load = <0x80080000>; + entry = <0x80080000>; + }; +__HEADER_EOF + +for dtname in $* +do + cat << __FDT_IMAGE_EOF + $(basename $dtname) { + description = "$(basename $dtname .dtb)"; + data = /incbin/("$dtname"); + type = "flat_dt"; + arch = "arm"; + compression = "none"; + }; +__FDT_IMAGE_EOF +done + +cat << __CONF_HEADER_EOF + }; + configurations { + default = "$(basename $1)"; + +__CONF_HEADER_EOF + +for dtname in $* +do + cat << __CONF_SECTION_EOF + $(basename $dtname) { + description = "$(basename $dtname .dtb)"; + firmware = "atf"; + loadables = "tee", "spl"; + fdt = "$(basename $dtname)"; + }; +__CONF_SECTION_EOF +done + +cat << __ITS_EOF + }; +}; +__ITS_EOF diff --git a/tools/u-boot-tools/k3_x509template.txt b/tools/u-boot-tools/k3_x509template.txt new file mode 100644 index 0000000..bd3a9ab --- /dev/null +++ b/tools/u-boot-tools/k3_x509template.txt @@ -0,0 +1,48 @@ + [ req ] + distinguished_name = req_distinguished_name + x509_extensions = v3_ca + prompt = no + dirstring_type = nobmp + + [ req_distinguished_name ] + C = US + ST = TX + L = Dallas + O = Texas Instruments Incorporated + OU = Processors + CN = TI Support + emailAddress = support@ti.com + + [ v3_ca ] + basicConstraints = CA:true + 1.3.6.1.4.1.294.1.1 = ASN1:SEQUENCE:boot_seq + 1.3.6.1.4.1.294.1.2 = ASN1:SEQUENCE:image_integrity + 1.3.6.1.4.1.294.1.3 = ASN1:SEQUENCE:swrv +# 1.3.6.1.4.1.294.1.4 = ASN1:SEQUENCE:encryption + 1.3.6.1.4.1.294.1.8 = ASN1:SEQUENCE:debug + + [ boot_seq ] + certType = INTEGER:TEST_CERT_TYPE + bootCore = INTEGER:TEST_BOOT_CORE + bootCoreOpts = INTEGER:TEST_BOOT_ARCH_WIDTH + destAddr = FORMAT:HEX,OCT:TEST_BOOT_ADDR + imageSize = INTEGER:TEST_IMAGE_LENGTH + + [ image_integrity ] + shaType = OID:2.16.840.1.101.3.4.2.3 + shaValue = FORMAT:HEX,OCT:TEST_IMAGE_SHA_VAL + + [ swrv ] + swrv = INTEGER:0 + +# [ encryption ] +# initalVector = FORMAT:HEX,OCT:TEST_IMAGE_ENC_IV +# randomString = FORMAT:HEX,OCT:TEST_IMAGE_ENC_RS +# iterationCnt = INTEGER:TEST_IMAGE_KEY_DERIVE_INDEX +# salt = FORMAT:HEX,OCT:TEST_IMAGE_KEY_DERIVE_SALT + + [ debug ] + debugType = INTEGER:4 + coreDbgEn = INTEGER:0 + coreDbgSecEn = INTEGER:0 + debugUID = FORMAT:HEX,OCT:0000000000000000000000000000000000000000000000000000000000000000 diff --git a/tools/u-boot-tools/kermit/README b/tools/u-boot-tools/kermit/README new file mode 100644 index 0000000..2b3f0b5 --- /dev/null +++ b/tools/u-boot-tools/kermit/README @@ -0,0 +1,49 @@ +# SPDX-License-Identifier: GPL-2.0+ +# +# (C) Copyright 2001 +# Wolfgang Denk, DENX Software Engineering, wd@denx.de. + +This directory contains scripts that help to perform certain actions +that need to be done frequently when working with U-Boot. + +They are meant as EXAMPLE code, so it is very likely that you will +have to modify them before use. + + +Short description: +================== + +dot.kermrc: + + Example for "~/.kermrc" Kermit init file for use with U-Boot + + by Wolfgang Denk, 24 Jun 2001 + +flash_param: + + "kermit" script to automatically initialize the environment + variables on your target. This is most useful during + development when your environment variables are stored in an + embedded flash sector which is erased whenever you install a + new U-Boot image. + + by Swen Anderson, 10 May 2001 + +send_cmd: + + send_cmd U_BOOT_COMMAND + + "kermit" script to send a U-Boot command and print the + results. When used from a shell with history (like the bash) + this indirectly adds kind of history to U-Boot ;-) + + by Swen Anderson, 10 May 2001 + +send_image: + + send_image FILE_NAME OFFSET + + "kermit" script to automatically download a file to the + target using the "loadb" command (kermit binary protocol) + + by Swen Anderson, 10 May 2001 diff --git a/tools/u-boot-tools/kermit/dot.kermrc b/tools/u-boot-tools/kermit/dot.kermrc new file mode 100644 index 0000000..0fc6c15 --- /dev/null +++ b/tools/u-boot-tools/kermit/dot.kermrc @@ -0,0 +1,16 @@ +set line /dev/ttyS0 +set speed 115200 +set carrier-watch off +set handshake none +set flow-control none +robust +set file type bin +set file name lit +set rec pack 1000 +set send pack 1000 +set window 5 +set prompt Kermit> +define sz !sz \%1 \%2 \%3 \%4 \%5 \%6 \%7 \%8 \%9 < \v(line) > \v(line) +define rz !rz \%1 \%2 \%3 \%4 \%5 \%6 \%7 \%8 \%9 < \v(line) > \v(line) +define sx !sx \%1 \%2 \%3 \%4 \%5 \%6 \%7 \%8 \%9 < \v(line) > \v(line) +define rx !rx \%1 \%2 \%3 \%4 \%5 \%6 \%7 \%8 \%9 < \v(line) > \v(line) diff --git a/tools/u-boot-tools/kermit/flash_param b/tools/u-boot-tools/kermit/flash_param new file mode 100644 index 0000000..847f99e --- /dev/null +++ b/tools/u-boot-tools/kermit/flash_param @@ -0,0 +1,60 @@ +#!/usr/bin/kermit + +# usage: ./flash_param parameters +# Parameters: IP Address ETH Address ERIC Number +# Format: xxx.xxx.xxx.xxx xx:xx:xx:xx:xx:xx xxxx + +set line /dev/ttyS0 +set speed 115200 +set serial 8N1 +set carrier-watch off +set handshake none +#set flow-control none +set flow-control xon/xoff +#robust +set file type bin +set file name lit +set rec pack 1000 +set send pack 1000 +set window 5 +set prompt Kermit> +#robust +# Milliseconds to pause between each OUTPUT character +set output pacing 1 + +out \13 +in 10 => +#first erase the environment memory within NVRAM +out mw f0000000 0 200\13 +in 10 => +out reset\13 +in 5 autoboot +out \13\13 +in 10 => +#set additional env parameter +out setenv ethaddr \%2\13 +in 10 => +out setenv serial# ERIC 1.0 \%3\13 +in 10 => +out setenv eric_id \%3\13 +in 10 => +#out setenv prec_videocard_bus unknown\13 +#in 10 => +#out setenv prec_bios_type unknown\13 +#in 10 => +out setenv eric_passwd .eRIC.\13 +in 10 => +#out setenv bootargs root=/dev/ram ramdisk_size=8192 init=/sbin/init ip=\%1:192.168.1.100:192.168.1.254:255.255.255.0\13 +#out setenv bootargs root=/dev/ram ramdisk_size=8192 init=/sbin/init ip=\%1:192.168.0.1\13 +#out setenv bootargs root=/dev/ram ramdisk_size=8192 init=/sbin/init ip=\%1\13 +out setenv bootargs console=/dev/ttyS0,115200 root=/dev/nfs nfsroot=192.168.1.26:/eric_root_devel ip=\%1:192.168.1.26\13 +in 10 => +out setenv bootcmd bootm FFC00000\13 +in 10 => +out saveenv\13 +in 10 => +out reset\13 +in 5 autoboot +out \13\13 +in 10 => +quit +exit 0 diff --git a/tools/u-boot-tools/kermit/send_cmd b/tools/u-boot-tools/kermit/send_cmd new file mode 100644 index 0000000..4131331 --- /dev/null +++ b/tools/u-boot-tools/kermit/send_cmd @@ -0,0 +1,21 @@ +#!/usr/bin/kermit + +set line /dev/ttyS0 +set speed 115200 +set serial 8N1 +set carrier-watch off +set handshake none +set flow-control none +robust +set file type bin +set file name lit +set rec pack 1000 +set send pack 1000 +set window 5 +set prompt Kermit> + +#out \13 +#in 10 => +out \%1 \%2 \%3 \%4 \%5 \%6 \%7\13 +in 10 => +quit +exit 0 diff --git a/tools/u-boot-tools/kermit/send_image b/tools/u-boot-tools/kermit/send_image new file mode 100644 index 0000000..9b89d6b --- /dev/null +++ b/tools/u-boot-tools/kermit/send_image @@ -0,0 +1,26 @@ +#!/usr/bin/kermit + +# usage: send_image FILE_NAME OFFSET +# e.g. send_image output.bin 1F00000 +set line /dev/ttyS0 +set speed 115200 +set serial 8N1 +set carrier-watch off +set handshake none +set flow-control none +robust +set file type bin +set file name lit +set rec pack 1000 +set send pack 1000 +set window 5 +set prompt Kermit> + +out \13 +in 10 => +out loadb \%2 \13 +in 10 download ... +send \%1 +out \13 +in 10 ## Start Addr +quit +exit 0 diff --git a/tools/u-boot-tools/kwbimage.c b/tools/u-boot-tools/kwbimage.c new file mode 100644 index 0000000..a88a383 --- /dev/null +++ b/tools/u-boot-tools/kwbimage.c @@ -0,0 +1,1757 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Image manipulator for Marvell SoCs + * supports Kirkwood, Dove, Armada 370, Armada XP, and Armada 38x + * + * (C) Copyright 2013 Thomas Petazzoni + * <thomas.petazzoni@free-electrons.com> + * + * Not implemented: support for the register headers in v1 images + */ + +#include "imagetool.h" +#include <limits.h> +#include <image.h> +#include <stdarg.h> +#include <stdint.h> +#include "kwbimage.h" + +#ifdef CONFIG_KWB_SECURE +#include <openssl/bn.h> +#include <openssl/rsa.h> +#include <openssl/pem.h> +#include <openssl/err.h> +#include <openssl/evp.h> + +#if OPENSSL_VERSION_NUMBER < 0x10100000L || \ + (defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x2070000fL) +static void RSA_get0_key(const RSA *r, + const BIGNUM **n, const BIGNUM **e, const BIGNUM **d) +{ + if (n != NULL) + *n = r->n; + if (e != NULL) + *e = r->e; + if (d != NULL) + *d = r->d; +} + +#elif !defined(LIBRESSL_VERSION_NUMBER) +void EVP_MD_CTX_cleanup(EVP_MD_CTX *ctx) +{ + EVP_MD_CTX_reset(ctx); +} +#endif +#endif + +static struct image_cfg_element *image_cfg; +static int cfgn; +#ifdef CONFIG_KWB_SECURE +static int verbose_mode; +#endif + +struct boot_mode { + unsigned int id; + const char *name; +}; + +/* + * SHA2-256 hash + */ +struct hash_v1 { + uint8_t hash[32]; +}; + +struct boot_mode boot_modes[] = { + { 0x4D, "i2c" }, + { 0x5A, "spi" }, + { 0x8B, "nand" }, + { 0x78, "sata" }, + { 0x9C, "pex" }, + { 0x69, "uart" }, + { 0xAE, "sdio" }, + {}, +}; + +struct nand_ecc_mode { + unsigned int id; + const char *name; +}; + +struct nand_ecc_mode nand_ecc_modes[] = { + { 0x00, "default" }, + { 0x01, "hamming" }, + { 0x02, "rs" }, + { 0x03, "disabled" }, + {}, +}; + +/* Used to identify an undefined execution or destination address */ +#define ADDR_INVALID ((uint32_t)-1) + +#define BINARY_MAX_ARGS 8 + +/* In-memory representation of a line of the configuration file */ + +enum image_cfg_type { + IMAGE_CFG_VERSION = 0x1, + IMAGE_CFG_BOOT_FROM, + IMAGE_CFG_DEST_ADDR, + IMAGE_CFG_EXEC_ADDR, + IMAGE_CFG_NAND_BLKSZ, + IMAGE_CFG_NAND_BADBLK_LOCATION, + IMAGE_CFG_NAND_ECC_MODE, + IMAGE_CFG_NAND_PAGESZ, + IMAGE_CFG_BINARY, + IMAGE_CFG_PAYLOAD, + IMAGE_CFG_DATA, + IMAGE_CFG_BAUDRATE, + IMAGE_CFG_DEBUG, + IMAGE_CFG_KAK, + IMAGE_CFG_CSK, + IMAGE_CFG_CSK_INDEX, + IMAGE_CFG_JTAG_DELAY, + IMAGE_CFG_BOX_ID, + IMAGE_CFG_FLASH_ID, + IMAGE_CFG_SEC_COMMON_IMG, + IMAGE_CFG_SEC_SPECIALIZED_IMG, + IMAGE_CFG_SEC_BOOT_DEV, + IMAGE_CFG_SEC_FUSE_DUMP, + + IMAGE_CFG_COUNT +} type; + +static const char * const id_strs[] = { + [IMAGE_CFG_VERSION] = "VERSION", + [IMAGE_CFG_BOOT_FROM] = "BOOT_FROM", + [IMAGE_CFG_DEST_ADDR] = "DEST_ADDR", + [IMAGE_CFG_EXEC_ADDR] = "EXEC_ADDR", + [IMAGE_CFG_NAND_BLKSZ] = "NAND_BLKSZ", + [IMAGE_CFG_NAND_BADBLK_LOCATION] = "NAND_BADBLK_LOCATION", + [IMAGE_CFG_NAND_ECC_MODE] = "NAND_ECC_MODE", + [IMAGE_CFG_NAND_PAGESZ] = "NAND_PAGE_SIZE", + [IMAGE_CFG_BINARY] = "BINARY", + [IMAGE_CFG_PAYLOAD] = "PAYLOAD", + [IMAGE_CFG_DATA] = "DATA", + [IMAGE_CFG_BAUDRATE] = "BAUDRATE", + [IMAGE_CFG_DEBUG] = "DEBUG", + [IMAGE_CFG_KAK] = "KAK", + [IMAGE_CFG_CSK] = "CSK", + [IMAGE_CFG_CSK_INDEX] = "CSK_INDEX", + [IMAGE_CFG_JTAG_DELAY] = "JTAG_DELAY", + [IMAGE_CFG_BOX_ID] = "BOX_ID", + [IMAGE_CFG_FLASH_ID] = "FLASH_ID", + [IMAGE_CFG_SEC_COMMON_IMG] = "SEC_COMMON_IMG", + [IMAGE_CFG_SEC_SPECIALIZED_IMG] = "SEC_SPECIALIZED_IMG", + [IMAGE_CFG_SEC_BOOT_DEV] = "SEC_BOOT_DEV", + [IMAGE_CFG_SEC_FUSE_DUMP] = "SEC_FUSE_DUMP" +}; + +struct image_cfg_element { + enum image_cfg_type type; + union { + unsigned int version; + unsigned int bootfrom; + struct { + const char *file; + unsigned int args[BINARY_MAX_ARGS]; + unsigned int nargs; + } binary; + const char *payload; + unsigned int dstaddr; + unsigned int execaddr; + unsigned int nandblksz; + unsigned int nandbadblklocation; + unsigned int nandeccmode; + unsigned int nandpagesz; + struct ext_hdr_v0_reg regdata; + unsigned int baudrate; + unsigned int debug; + const char *key_name; + int csk_idx; + uint8_t jtag_delay; + uint32_t boxid; + uint32_t flashid; + bool sec_specialized_img; + unsigned int sec_boot_dev; + const char *name; + }; +}; + +#define IMAGE_CFG_ELEMENT_MAX 256 + +/* + * Utility functions to manipulate boot mode and ecc modes (convert + * them back and forth between description strings and the + * corresponding numerical identifiers). + */ + +static const char *image_boot_mode_name(unsigned int id) +{ + int i; + + for (i = 0; boot_modes[i].name; i++) + if (boot_modes[i].id == id) + return boot_modes[i].name; + return NULL; +} + +int image_boot_mode_id(const char *boot_mode_name) +{ + int i; + + for (i = 0; boot_modes[i].name; i++) + if (!strcmp(boot_modes[i].name, boot_mode_name)) + return boot_modes[i].id; + + return -1; +} + +int image_nand_ecc_mode_id(const char *nand_ecc_mode_name) +{ + int i; + + for (i = 0; nand_ecc_modes[i].name; i++) + if (!strcmp(nand_ecc_modes[i].name, nand_ecc_mode_name)) + return nand_ecc_modes[i].id; + return -1; +} + +static struct image_cfg_element * +image_find_option(unsigned int optiontype) +{ + int i; + + for (i = 0; i < cfgn; i++) { + if (image_cfg[i].type == optiontype) + return &image_cfg[i]; + } + + return NULL; +} + +static unsigned int +image_count_options(unsigned int optiontype) +{ + int i; + unsigned int count = 0; + + for (i = 0; i < cfgn; i++) + if (image_cfg[i].type == optiontype) + count++; + + return count; +} + +#if defined(CONFIG_KWB_SECURE) + +static int image_get_csk_index(void) +{ + struct image_cfg_element *e; + + e = image_find_option(IMAGE_CFG_CSK_INDEX); + if (!e) + return -1; + + return e->csk_idx; +} + +static bool image_get_spezialized_img(void) +{ + struct image_cfg_element *e; + + e = image_find_option(IMAGE_CFG_SEC_SPECIALIZED_IMG); + if (!e) + return false; + + return e->sec_specialized_img; +} + +#endif + +/* + * Compute a 8-bit checksum of a memory area. This algorithm follows + * the requirements of the Marvell SoC BootROM specifications. + */ +static uint8_t image_checksum8(void *start, uint32_t len) +{ + uint8_t csum = 0; + uint8_t *p = start; + + /* check len and return zero checksum if invalid */ + if (!len) + return 0; + + do { + csum += *p; + p++; + } while (--len); + + return csum; +} + +size_t kwbimage_header_size(unsigned char *ptr) +{ + if (image_version((void *)ptr) == 0) + return sizeof(struct main_hdr_v0); + else + return KWBHEADER_V1_SIZE((struct main_hdr_v1 *)ptr); +} + +/* + * Verify checksum over a complete header that includes the checksum field. + * Return 1 when OK, otherwise 0. + */ +static int main_hdr_checksum_ok(void *hdr) +{ + /* Offsets of checksum in v0 and v1 headers are the same */ + struct main_hdr_v0 *main_hdr = (struct main_hdr_v0 *)hdr; + uint8_t checksum; + + checksum = image_checksum8(hdr, kwbimage_header_size(hdr)); + /* Calculated checksum includes the header checksum field. Compensate + * for that. + */ + checksum -= main_hdr->checksum; + + return checksum == main_hdr->checksum; +} + +static uint32_t image_checksum32(void *start, uint32_t len) +{ + uint32_t csum = 0; + uint32_t *p = start; + + /* check len and return zero checksum if invalid */ + if (!len) + return 0; + + if (len % sizeof(uint32_t)) { + fprintf(stderr, "Length %d is not in multiple of %zu\n", + len, sizeof(uint32_t)); + return 0; + } + + do { + csum += *p; + p++; + len -= sizeof(uint32_t); + } while (len > 0); + + return csum; +} + +static uint8_t baudrate_to_option(unsigned int baudrate) +{ + switch (baudrate) { + case 2400: + return MAIN_HDR_V1_OPT_BAUD_2400; + case 4800: + return MAIN_HDR_V1_OPT_BAUD_4800; + case 9600: + return MAIN_HDR_V1_OPT_BAUD_9600; + case 19200: + return MAIN_HDR_V1_OPT_BAUD_19200; + case 38400: + return MAIN_HDR_V1_OPT_BAUD_38400; + case 57600: + return MAIN_HDR_V1_OPT_BAUD_57600; + case 115200: + return MAIN_HDR_V1_OPT_BAUD_115200; + default: + return MAIN_HDR_V1_OPT_BAUD_DEFAULT; + } +} + +#if defined(CONFIG_KWB_SECURE) +static void kwb_msg(const char *fmt, ...) +{ + if (verbose_mode) { + va_list ap; + + va_start(ap, fmt); + vfprintf(stdout, fmt, ap); + va_end(ap); + } +} + +static int openssl_err(const char *msg) +{ + unsigned long ssl_err = ERR_get_error(); + + fprintf(stderr, "%s", msg); + fprintf(stderr, ": %s\n", + ERR_error_string(ssl_err, 0)); + + return -1; +} + +static int kwb_load_rsa_key(const char *keydir, const char *name, RSA **p_rsa) +{ + char path[PATH_MAX]; + RSA *rsa; + FILE *f; + + if (!keydir) + keydir = "."; + + snprintf(path, sizeof(path), "%s/%s.key", keydir, name); + f = fopen(path, "r"); + if (!f) { + fprintf(stderr, "Couldn't open RSA private key: '%s': %s\n", + path, strerror(errno)); + return -ENOENT; + } + + rsa = PEM_read_RSAPrivateKey(f, 0, NULL, ""); + if (!rsa) { + openssl_err("Failure reading private key"); + fclose(f); + return -EPROTO; + } + fclose(f); + *p_rsa = rsa; + + return 0; +} + +static int kwb_load_cfg_key(struct image_tool_params *params, + unsigned int cfg_option, const char *key_name, + RSA **p_key) +{ + struct image_cfg_element *e_key; + RSA *key; + int res; + + *p_key = NULL; + + e_key = image_find_option(cfg_option); + if (!e_key) { + fprintf(stderr, "%s not configured\n", key_name); + return -ENOENT; + } + + res = kwb_load_rsa_key(params->keydir, e_key->key_name, &key); + if (res < 0) { + fprintf(stderr, "Failed to load %s\n", key_name); + return -ENOENT; + } + + *p_key = key; + + return 0; +} + +static int kwb_load_kak(struct image_tool_params *params, RSA **p_kak) +{ + return kwb_load_cfg_key(params, IMAGE_CFG_KAK, "KAK", p_kak); +} + +static int kwb_load_csk(struct image_tool_params *params, RSA **p_csk) +{ + return kwb_load_cfg_key(params, IMAGE_CFG_CSK, "CSK", p_csk); +} + +static int kwb_compute_pubkey_hash(struct pubkey_der_v1 *pk, + struct hash_v1 *hash) +{ + EVP_MD_CTX *ctx; + unsigned int key_size; + unsigned int hash_size; + int ret = 0; + + if (!pk || !hash || pk->key[0] != 0x30 || pk->key[1] != 0x82) + return -EINVAL; + + key_size = (pk->key[2] << 8) + pk->key[3] + 4; + + ctx = EVP_MD_CTX_create(); + if (!ctx) + return openssl_err("EVP context creation failed"); + + EVP_MD_CTX_init(ctx); + if (!EVP_DigestInit(ctx, EVP_sha256())) { + ret = openssl_err("Digest setup failed"); + goto hash_err_ctx; + } + + if (!EVP_DigestUpdate(ctx, pk->key, key_size)) { + ret = openssl_err("Hashing data failed"); + goto hash_err_ctx; + } + + if (!EVP_DigestFinal(ctx, hash->hash, &hash_size)) { + ret = openssl_err("Could not obtain hash"); + goto hash_err_ctx; + } + + EVP_MD_CTX_cleanup(ctx); + +hash_err_ctx: + EVP_MD_CTX_destroy(ctx); + return ret; +} + +static int kwb_import_pubkey(RSA **key, struct pubkey_der_v1 *src, char *keyname) +{ + RSA *rsa; + const unsigned char *ptr; + + if (!key || !src) + goto fail; + + ptr = src->key; + rsa = d2i_RSAPublicKey(key, &ptr, sizeof(src->key)); + if (!rsa) { + openssl_err("error decoding public key"); + goto fail; + } + + return 0; +fail: + fprintf(stderr, "Failed to decode %s pubkey\n", keyname); + return -EINVAL; +} + +static int kwb_export_pubkey(RSA *key, struct pubkey_der_v1 *dst, FILE *hashf, + char *keyname) +{ + int size_exp, size_mod, size_seq; + const BIGNUM *key_e, *key_n; + uint8_t *cur; + char *errmsg = "Failed to encode %s\n"; + + RSA_get0_key(key, NULL, &key_e, NULL); + RSA_get0_key(key, &key_n, NULL, NULL); + + if (!key || !key_e || !key_n || !dst) { + fprintf(stderr, "export pk failed: (%p, %p, %p, %p)", + key, key_e, key_n, dst); + fprintf(stderr, errmsg, keyname); + return -EINVAL; + } + + /* + * According to the specs, the key should be PKCS#1 DER encoded. + * But unfortunately the really required encoding seems to be different; + * it violates DER...! (But it still conformes to BER.) + * (Length always in long form w/ 2 byte length code; no leading zero + * when MSB of first byte is set...) + * So we cannot use the encoding func provided by OpenSSL and have to + * do the encoding manually. + */ + + size_exp = BN_num_bytes(key_e); + size_mod = BN_num_bytes(key_n); + size_seq = 4 + size_mod + 4 + size_exp; + + if (size_mod > 256) { + fprintf(stderr, "export pk failed: wrong mod size: %d\n", + size_mod); + fprintf(stderr, errmsg, keyname); + return -EINVAL; + } + + if (4 + size_seq > sizeof(dst->key)) { + fprintf(stderr, "export pk failed: seq too large (%d, %lu)\n", + 4 + size_seq, sizeof(dst->key)); + fprintf(stderr, errmsg, keyname); + return -ENOBUFS; + } + + cur = dst->key; + + /* PKCS#1 (RFC3447) RSAPublicKey structure */ + *cur++ = 0x30; /* SEQUENCE */ + *cur++ = 0x82; + *cur++ = (size_seq >> 8) & 0xFF; + *cur++ = size_seq & 0xFF; + /* Modulus */ + *cur++ = 0x02; /* INTEGER */ + *cur++ = 0x82; + *cur++ = (size_mod >> 8) & 0xFF; + *cur++ = size_mod & 0xFF; + BN_bn2bin(key_n, cur); + cur += size_mod; + /* Exponent */ + *cur++ = 0x02; /* INTEGER */ + *cur++ = 0x82; + *cur++ = (size_exp >> 8) & 0xFF; + *cur++ = size_exp & 0xFF; + BN_bn2bin(key_e, cur); + + if (hashf) { + struct hash_v1 pk_hash; + int i; + int ret = 0; + + ret = kwb_compute_pubkey_hash(dst, &pk_hash); + if (ret < 0) { + fprintf(stderr, errmsg, keyname); + return ret; + } + + fprintf(hashf, "SHA256 = "); + for (i = 0 ; i < sizeof(pk_hash.hash); ++i) + fprintf(hashf, "%02X", pk_hash.hash[i]); + fprintf(hashf, "\n"); + } + + return 0; +} + +int kwb_sign(RSA *key, void *data, int datasz, struct sig_v1 *sig, char *signame) +{ + EVP_PKEY *evp_key; + EVP_MD_CTX *ctx; + unsigned int sig_size; + int size; + int ret = 0; + + evp_key = EVP_PKEY_new(); + if (!evp_key) + return openssl_err("EVP_PKEY object creation failed"); + + if (!EVP_PKEY_set1_RSA(evp_key, key)) { + ret = openssl_err("EVP key setup failed"); + goto err_key; + } + + size = EVP_PKEY_size(evp_key); + if (size > sizeof(sig->sig)) { + fprintf(stderr, "Buffer to small for signature (%d bytes)\n", + size); + ret = -ENOBUFS; + goto err_key; + } + + ctx = EVP_MD_CTX_create(); + if (!ctx) { + ret = openssl_err("EVP context creation failed"); + goto err_key; + } + EVP_MD_CTX_init(ctx); + if (!EVP_SignInit(ctx, EVP_sha256())) { + ret = openssl_err("Signer setup failed"); + goto err_ctx; + } + + if (!EVP_SignUpdate(ctx, data, datasz)) { + ret = openssl_err("Signing data failed"); + goto err_ctx; + } + + if (!EVP_SignFinal(ctx, sig->sig, &sig_size, evp_key)) { + ret = openssl_err("Could not obtain signature"); + goto err_ctx; + } + + EVP_MD_CTX_cleanup(ctx); + EVP_MD_CTX_destroy(ctx); + EVP_PKEY_free(evp_key); + + return 0; + +err_ctx: + EVP_MD_CTX_destroy(ctx); +err_key: + EVP_PKEY_free(evp_key); + fprintf(stderr, "Failed to create %s signature\n", signame); + return ret; +} + +int kwb_verify(RSA *key, void *data, int datasz, struct sig_v1 *sig, + char *signame) +{ + EVP_PKEY *evp_key; + EVP_MD_CTX *ctx; + int size; + int ret = 0; + + evp_key = EVP_PKEY_new(); + if (!evp_key) + return openssl_err("EVP_PKEY object creation failed"); + + if (!EVP_PKEY_set1_RSA(evp_key, key)) { + ret = openssl_err("EVP key setup failed"); + goto err_key; + } + + size = EVP_PKEY_size(evp_key); + if (size > sizeof(sig->sig)) { + fprintf(stderr, "Invalid signature size (%d bytes)\n", + size); + ret = -EINVAL; + goto err_key; + } + + ctx = EVP_MD_CTX_create(); + if (!ctx) { + ret = openssl_err("EVP context creation failed"); + goto err_key; + } + EVP_MD_CTX_init(ctx); + if (!EVP_VerifyInit(ctx, EVP_sha256())) { + ret = openssl_err("Verifier setup failed"); + goto err_ctx; + } + + if (!EVP_VerifyUpdate(ctx, data, datasz)) { + ret = openssl_err("Hashing data failed"); + goto err_ctx; + } + + if (!EVP_VerifyFinal(ctx, sig->sig, sizeof(sig->sig), evp_key)) { + ret = openssl_err("Could not verify signature"); + goto err_ctx; + } + + EVP_MD_CTX_cleanup(ctx); + EVP_MD_CTX_destroy(ctx); + EVP_PKEY_free(evp_key); + + return 0; + +err_ctx: + EVP_MD_CTX_destroy(ctx); +err_key: + EVP_PKEY_free(evp_key); + fprintf(stderr, "Failed to verify %s signature\n", signame); + return ret; +} + +int kwb_sign_and_verify(RSA *key, void *data, int datasz, struct sig_v1 *sig, + char *signame) +{ + if (kwb_sign(key, data, datasz, sig, signame) < 0) + return -1; + + if (kwb_verify(key, data, datasz, sig, signame) < 0) + return -1; + + return 0; +} + + +int kwb_dump_fuse_cmds_38x(FILE *out, struct secure_hdr_v1 *sec_hdr) +{ + struct hash_v1 kak_pub_hash; + struct image_cfg_element *e; + unsigned int fuse_line; + int i, idx; + uint8_t *ptr; + uint32_t val; + int ret = 0; + + if (!out || !sec_hdr) + return -EINVAL; + + ret = kwb_compute_pubkey_hash(&sec_hdr->kak, &kak_pub_hash); + if (ret < 0) + goto done; + + fprintf(out, "# burn KAK pub key hash\n"); + ptr = kak_pub_hash.hash; + for (fuse_line = 26; fuse_line <= 30; ++fuse_line) { + fprintf(out, "fuse prog -y %u 0 ", fuse_line); + + for (i = 4; i-- > 0;) + fprintf(out, "%02hx", (ushort)ptr[i]); + ptr += 4; + fprintf(out, " 00"); + + if (fuse_line < 30) { + for (i = 3; i-- > 0;) + fprintf(out, "%02hx", (ushort)ptr[i]); + ptr += 3; + } else { + fprintf(out, "000000"); + } + + fprintf(out, " 1\n"); + } + + fprintf(out, "# burn CSK selection\n"); + + idx = image_get_csk_index(); + if (idx < 0 || idx > 15) { + ret = -EINVAL; + goto done; + } + if (idx > 0) { + for (fuse_line = 31; fuse_line < 31 + idx; ++fuse_line) + fprintf(out, "fuse prog -y %u 0 00000001 00000000 1\n", + fuse_line); + } else { + fprintf(out, "# CSK index is 0; no mods needed\n"); + } + + e = image_find_option(IMAGE_CFG_BOX_ID); + if (e) { + fprintf(out, "# set box ID\n"); + fprintf(out, "fuse prog -y 48 0 %08x 00000000 1\n", e->boxid); + } + + e = image_find_option(IMAGE_CFG_FLASH_ID); + if (e) { + fprintf(out, "# set flash ID\n"); + fprintf(out, "fuse prog -y 47 0 %08x 00000000 1\n", e->flashid); + } + + fprintf(out, "# enable secure mode "); + fprintf(out, "(must be the last fuse line written)\n"); + + val = 1; + e = image_find_option(IMAGE_CFG_SEC_BOOT_DEV); + if (!e) { + fprintf(stderr, "ERROR: secured mode boot device not given\n"); + ret = -EINVAL; + goto done; + } + + if (e->sec_boot_dev > 0xff) { + fprintf(stderr, "ERROR: secured mode boot device invalid\n"); + ret = -EINVAL; + goto done; + } + + val |= (e->sec_boot_dev << 8); + + fprintf(out, "fuse prog -y 24 0 %08x 0103e0a9 1\n", val); + + fprintf(out, "# lock (unused) fuse lines (0-23)s\n"); + for (fuse_line = 0; fuse_line < 24; ++fuse_line) + fprintf(out, "fuse prog -y %u 2 1\n", fuse_line); + + fprintf(out, "# OK, that's all :-)\n"); + +done: + return ret; +} + +static int kwb_dump_fuse_cmds(struct secure_hdr_v1 *sec_hdr) +{ + int ret = 0; + struct image_cfg_element *e; + + e = image_find_option(IMAGE_CFG_SEC_FUSE_DUMP); + if (!e) + return 0; + + if (!strcmp(e->name, "a38x")) { + FILE *out = fopen("kwb_fuses_a38x.txt", "w+"); + + kwb_dump_fuse_cmds_38x(out, sec_hdr); + fclose(out); + goto done; + } + + ret = -ENOSYS; + +done: + return ret; +} + +#endif + +static void *image_create_v0(size_t *imagesz, struct image_tool_params *params, + int payloadsz) +{ + struct image_cfg_element *e; + size_t headersz; + struct main_hdr_v0 *main_hdr; + uint8_t *image; + int has_ext = 0; + + /* + * Calculate the size of the header and the size of the + * payload + */ + headersz = sizeof(struct main_hdr_v0); + + if (image_count_options(IMAGE_CFG_DATA) > 0) { + has_ext = 1; + headersz += sizeof(struct ext_hdr_v0); + } + + if (image_count_options(IMAGE_CFG_PAYLOAD) > 1) { + fprintf(stderr, "More than one payload, not possible\n"); + return NULL; + } + + image = malloc(headersz); + if (!image) { + fprintf(stderr, "Cannot allocate memory for image\n"); + return NULL; + } + + memset(image, 0, headersz); + + main_hdr = (struct main_hdr_v0 *)image; + + /* Fill in the main header */ + main_hdr->blocksize = + cpu_to_le32(payloadsz + sizeof(uint32_t) - headersz); + main_hdr->srcaddr = cpu_to_le32(headersz); + main_hdr->ext = has_ext; + main_hdr->destaddr = cpu_to_le32(params->addr); + main_hdr->execaddr = cpu_to_le32(params->ep); + + e = image_find_option(IMAGE_CFG_BOOT_FROM); + if (e) + main_hdr->blockid = e->bootfrom; + e = image_find_option(IMAGE_CFG_NAND_ECC_MODE); + if (e) + main_hdr->nandeccmode = e->nandeccmode; + e = image_find_option(IMAGE_CFG_NAND_PAGESZ); + if (e) + main_hdr->nandpagesize = cpu_to_le16(e->nandpagesz); + main_hdr->checksum = image_checksum8(image, + sizeof(struct main_hdr_v0)); + + /* Generate the ext header */ + if (has_ext) { + struct ext_hdr_v0 *ext_hdr; + int cfgi, datai; + + ext_hdr = (struct ext_hdr_v0 *) + (image + sizeof(struct main_hdr_v0)); + ext_hdr->offset = cpu_to_le32(0x40); + + for (cfgi = 0, datai = 0; cfgi < cfgn; cfgi++) { + e = &image_cfg[cfgi]; + if (e->type != IMAGE_CFG_DATA) + continue; + + ext_hdr->rcfg[datai].raddr = + cpu_to_le32(e->regdata.raddr); + ext_hdr->rcfg[datai].rdata = + cpu_to_le32(e->regdata.rdata); + datai++; + } + + ext_hdr->checksum = image_checksum8(ext_hdr, + sizeof(struct ext_hdr_v0)); + } + + *imagesz = headersz; + return image; +} + +static size_t image_headersz_v1(int *hasext) +{ + struct image_cfg_element *binarye; + size_t headersz; + + /* + * Calculate the size of the header and the size of the + * payload + */ + headersz = sizeof(struct main_hdr_v1); + + if (image_count_options(IMAGE_CFG_BINARY) > 1) { + fprintf(stderr, "More than one binary blob, not supported\n"); + return 0; + } + + if (image_count_options(IMAGE_CFG_PAYLOAD) > 1) { + fprintf(stderr, "More than one payload, not possible\n"); + return 0; + } + + binarye = image_find_option(IMAGE_CFG_BINARY); + if (binarye) { + int ret; + struct stat s; + + ret = stat(binarye->binary.file, &s); + if (ret < 0) { + char cwd[PATH_MAX]; + char *dir = cwd; + + memset(cwd, 0, sizeof(cwd)); + if (!getcwd(cwd, sizeof(cwd))) { + dir = "current working directory"; + perror("getcwd() failed"); + } + + fprintf(stderr, + "Didn't find the file '%s' in '%s' which is mandatory to generate the image\n" + "This file generally contains the DDR3 training code, and should be extracted from an existing bootable\n" + "image for your board. See 'kwbimage -x' to extract it from an existing image.\n", + binarye->binary.file, dir); + return 0; + } + + headersz += sizeof(struct opt_hdr_v1) + + s.st_size + + (binarye->binary.nargs + 2) * sizeof(uint32_t); + if (hasext) + *hasext = 1; + } + +#if defined(CONFIG_KWB_SECURE) + if (image_get_csk_index() >= 0) { + headersz += sizeof(struct secure_hdr_v1); + if (hasext) + *hasext = 1; + } +#endif + +#if defined(CONFIG_SYS_U_BOOT_OFFS) + if (headersz > CONFIG_SYS_U_BOOT_OFFS) { + fprintf(stderr, + "Error: Image header (incl. SPL image) too big!\n"); + fprintf(stderr, "header=0x%x CONFIG_SYS_U_BOOT_OFFS=0x%x!\n", + (int)headersz, CONFIG_SYS_U_BOOT_OFFS); + fprintf(stderr, "Increase CONFIG_SYS_U_BOOT_OFFS!\n"); + return 0; + } + + headersz = CONFIG_SYS_U_BOOT_OFFS; +#endif + + /* + * The payload should be aligned on some reasonable + * boundary + */ + return ALIGN_SUP(headersz, 4096); +} + +int add_binary_header_v1(uint8_t *cur) +{ + struct image_cfg_element *binarye; + struct opt_hdr_v1 *hdr = (struct opt_hdr_v1 *)cur; + uint32_t *args; + size_t binhdrsz; + struct stat s; + int argi; + FILE *bin; + int ret; + + binarye = image_find_option(IMAGE_CFG_BINARY); + + if (!binarye) + return 0; + + hdr->headertype = OPT_HDR_V1_BINARY_TYPE; + + bin = fopen(binarye->binary.file, "r"); + if (!bin) { + fprintf(stderr, "Cannot open binary file %s\n", + binarye->binary.file); + return -1; + } + + if (fstat(fileno(bin), &s)) { + fprintf(stderr, "Cannot stat binary file %s\n", + binarye->binary.file); + goto err_close; + } + + binhdrsz = sizeof(struct opt_hdr_v1) + + (binarye->binary.nargs + 2) * sizeof(uint32_t) + + s.st_size; + + /* + * The size includes the binary image size, rounded + * up to a 4-byte boundary. Plus 4 bytes for the + * next-header byte and 3-byte alignment at the end. + */ + binhdrsz = ALIGN_SUP(binhdrsz, 4) + 4; + hdr->headersz_lsb = cpu_to_le16(binhdrsz & 0xFFFF); + hdr->headersz_msb = (binhdrsz & 0xFFFF0000) >> 16; + + cur += sizeof(struct opt_hdr_v1); + + args = (uint32_t *)cur; + *args = cpu_to_le32(binarye->binary.nargs); + args++; + for (argi = 0; argi < binarye->binary.nargs; argi++) + args[argi] = cpu_to_le32(binarye->binary.args[argi]); + + cur += (binarye->binary.nargs + 1) * sizeof(uint32_t); + + ret = fread(cur, s.st_size, 1, bin); + if (ret != 1) { + fprintf(stderr, + "Could not read binary image %s\n", + binarye->binary.file); + goto err_close; + } + + fclose(bin); + + cur += ALIGN_SUP(s.st_size, 4); + + /* + * For now, we don't support more than one binary + * header, and no other header types are + * supported. So, the binary header is necessarily the + * last one + */ + *((uint32_t *)cur) = 0x00000000; + + cur += sizeof(uint32_t); + + return 0; + +err_close: + fclose(bin); + + return -1; +} + +#if defined(CONFIG_KWB_SECURE) + +int export_pub_kak_hash(RSA *kak, struct secure_hdr_v1 *secure_hdr) +{ + FILE *hashf; + int res; + + hashf = fopen("pub_kak_hash.txt", "w"); + + res = kwb_export_pubkey(kak, &secure_hdr->kak, hashf, "KAK"); + + fclose(hashf); + + return res < 0 ? 1 : 0; +} + +int kwb_sign_csk_with_kak(struct image_tool_params *params, + struct secure_hdr_v1 *secure_hdr, RSA *csk) +{ + RSA *kak = NULL; + RSA *kak_pub = NULL; + int csk_idx = image_get_csk_index(); + struct sig_v1 tmp_sig; + + if (csk_idx >= 16) { + fprintf(stderr, "Invalid CSK index %d\n", csk_idx); + return 1; + } + + if (kwb_load_kak(params, &kak) < 0) + return 1; + + if (export_pub_kak_hash(kak, secure_hdr)) + return 1; + + if (kwb_import_pubkey(&kak_pub, &secure_hdr->kak, "KAK") < 0) + return 1; + + if (kwb_export_pubkey(csk, &secure_hdr->csk[csk_idx], NULL, "CSK") < 0) + return 1; + + if (kwb_sign_and_verify(kak, &secure_hdr->csk, + sizeof(secure_hdr->csk) + + sizeof(secure_hdr->csksig), + &tmp_sig, "CSK") < 0) + return 1; + + if (kwb_verify(kak_pub, &secure_hdr->csk, + sizeof(secure_hdr->csk) + + sizeof(secure_hdr->csksig), + &tmp_sig, "CSK (2)") < 0) + return 1; + + secure_hdr->csksig = tmp_sig; + + return 0; +} + +int add_secure_header_v1(struct image_tool_params *params, uint8_t *ptr, + int payloadsz, size_t headersz, uint8_t *image, + struct secure_hdr_v1 *secure_hdr) +{ + struct image_cfg_element *e_jtagdelay; + struct image_cfg_element *e_boxid; + struct image_cfg_element *e_flashid; + RSA *csk = NULL; + unsigned char *image_ptr; + size_t image_size; + struct sig_v1 tmp_sig; + bool specialized_img = image_get_spezialized_img(); + + kwb_msg("Create secure header content\n"); + + e_jtagdelay = image_find_option(IMAGE_CFG_JTAG_DELAY); + e_boxid = image_find_option(IMAGE_CFG_BOX_ID); + e_flashid = image_find_option(IMAGE_CFG_FLASH_ID); + + if (kwb_load_csk(params, &csk) < 0) + return 1; + + secure_hdr->headertype = OPT_HDR_V1_SECURE_TYPE; + secure_hdr->headersz_msb = 0; + secure_hdr->headersz_lsb = cpu_to_le16(sizeof(struct secure_hdr_v1)); + if (e_jtagdelay) + secure_hdr->jtag_delay = e_jtagdelay->jtag_delay; + if (e_boxid && specialized_img) + secure_hdr->boxid = cpu_to_le32(e_boxid->boxid); + if (e_flashid && specialized_img) + secure_hdr->flashid = cpu_to_le32(e_flashid->flashid); + + if (kwb_sign_csk_with_kak(params, secure_hdr, csk)) + return 1; + + image_ptr = ptr + headersz; + image_size = payloadsz - headersz; + + if (kwb_sign_and_verify(csk, image_ptr, image_size, + &secure_hdr->imgsig, "image") < 0) + return 1; + + if (kwb_sign_and_verify(csk, image, headersz, &tmp_sig, "header") < 0) + return 1; + + secure_hdr->hdrsig = tmp_sig; + + kwb_dump_fuse_cmds(secure_hdr); + + return 0; +} +#endif + +static void *image_create_v1(size_t *imagesz, struct image_tool_params *params, + uint8_t *ptr, int payloadsz) +{ + struct image_cfg_element *e; + struct main_hdr_v1 *main_hdr; +#if defined(CONFIG_KWB_SECURE) + struct secure_hdr_v1 *secure_hdr = NULL; +#endif + size_t headersz; + uint8_t *image, *cur; + int hasext = 0; + uint8_t *next_ext = NULL; + + /* + * Calculate the size of the header and the size of the + * payload + */ + headersz = image_headersz_v1(&hasext); + if (headersz == 0) + return NULL; + + image = malloc(headersz); + if (!image) { + fprintf(stderr, "Cannot allocate memory for image\n"); + return NULL; + } + + memset(image, 0, headersz); + + main_hdr = (struct main_hdr_v1 *)image; + cur = image; + cur += sizeof(struct main_hdr_v1); + next_ext = &main_hdr->ext; + + /* Fill the main header */ + main_hdr->blocksize = + cpu_to_le32(payloadsz - headersz + sizeof(uint32_t)); + main_hdr->headersz_lsb = cpu_to_le16(headersz & 0xFFFF); + main_hdr->headersz_msb = (headersz & 0xFFFF0000) >> 16; + main_hdr->destaddr = cpu_to_le32(params->addr) + - sizeof(image_header_t); + main_hdr->execaddr = cpu_to_le32(params->ep); + main_hdr->srcaddr = cpu_to_le32(headersz); + main_hdr->ext = hasext; + main_hdr->version = 1; + e = image_find_option(IMAGE_CFG_BOOT_FROM); + if (e) + main_hdr->blockid = e->bootfrom; + e = image_find_option(IMAGE_CFG_NAND_BLKSZ); + if (e) + main_hdr->nandblocksize = e->nandblksz / (64 * 1024); + e = image_find_option(IMAGE_CFG_NAND_BADBLK_LOCATION); + if (e) + main_hdr->nandbadblklocation = e->nandbadblklocation; + e = image_find_option(IMAGE_CFG_BAUDRATE); + if (e) + main_hdr->options = baudrate_to_option(e->baudrate); + e = image_find_option(IMAGE_CFG_DEBUG); + if (e) + main_hdr->flags = e->debug ? 0x1 : 0; + +#if defined(CONFIG_KWB_SECURE) + if (image_get_csk_index() >= 0) { + /* + * only reserve the space here; we fill the header later since + * we need the header to be complete to compute the signatures + */ + secure_hdr = (struct secure_hdr_v1 *)cur; + cur += sizeof(struct secure_hdr_v1); + next_ext = &secure_hdr->next; + } +#endif + *next_ext = 1; + + if (add_binary_header_v1(cur)) + return NULL; + +#if defined(CONFIG_KWB_SECURE) + if (secure_hdr && add_secure_header_v1(params, ptr, payloadsz, + headersz, image, secure_hdr)) + return NULL; +#endif + + /* Calculate and set the header checksum */ + main_hdr->checksum = image_checksum8(main_hdr, headersz); + + *imagesz = headersz; + return image; +} + +int recognize_keyword(char *keyword) +{ + int kw_id; + + for (kw_id = 1; kw_id < IMAGE_CFG_COUNT; ++kw_id) + if (!strcmp(keyword, id_strs[kw_id])) + return kw_id; + + return 0; +} + +static int image_create_config_parse_oneline(char *line, + struct image_cfg_element *el) +{ + char *keyword, *saveptr, *value1, *value2; + char delimiters[] = " \t"; + int keyword_id, ret, argi; + char *unknown_msg = "Ignoring unknown line '%s'\n"; + + keyword = strtok_r(line, delimiters, &saveptr); + keyword_id = recognize_keyword(keyword); + + if (!keyword_id) { + fprintf(stderr, unknown_msg, line); + return 0; + } + + el->type = keyword_id; + + value1 = strtok_r(NULL, delimiters, &saveptr); + + if (!value1) { + fprintf(stderr, "Parameter missing in line '%s'\n", line); + return -1; + } + + switch (keyword_id) { + case IMAGE_CFG_VERSION: + el->version = atoi(value1); + break; + case IMAGE_CFG_BOOT_FROM: + ret = image_boot_mode_id(value1); + + if (ret < 0) { + fprintf(stderr, "Invalid boot media '%s'\n", value1); + return -1; + } + el->bootfrom = ret; + break; + case IMAGE_CFG_NAND_BLKSZ: + el->nandblksz = strtoul(value1, NULL, 16); + break; + case IMAGE_CFG_NAND_BADBLK_LOCATION: + el->nandbadblklocation = strtoul(value1, NULL, 16); + break; + case IMAGE_CFG_NAND_ECC_MODE: + ret = image_nand_ecc_mode_id(value1); + + if (ret < 0) { + fprintf(stderr, "Invalid NAND ECC mode '%s'\n", value1); + return -1; + } + el->nandeccmode = ret; + break; + case IMAGE_CFG_NAND_PAGESZ: + el->nandpagesz = strtoul(value1, NULL, 16); + break; + case IMAGE_CFG_BINARY: + argi = 0; + + el->binary.file = strdup(value1); + while (1) { + char *value = strtok_r(NULL, delimiters, &saveptr); + + if (!value) + break; + el->binary.args[argi] = strtoul(value, NULL, 16); + argi++; + if (argi >= BINARY_MAX_ARGS) { + fprintf(stderr, + "Too many arguments for BINARY\n"); + return -1; + } + } + el->binary.nargs = argi; + break; + case IMAGE_CFG_DATA: + value2 = strtok_r(NULL, delimiters, &saveptr); + + if (!value1 || !value2) { + fprintf(stderr, + "Invalid number of arguments for DATA\n"); + return -1; + } + + el->regdata.raddr = strtoul(value1, NULL, 16); + el->regdata.rdata = strtoul(value2, NULL, 16); + break; + case IMAGE_CFG_BAUDRATE: + el->baudrate = strtoul(value1, NULL, 10); + break; + case IMAGE_CFG_DEBUG: + el->debug = strtoul(value1, NULL, 10); + break; + case IMAGE_CFG_KAK: + el->key_name = strdup(value1); + break; + case IMAGE_CFG_CSK: + el->key_name = strdup(value1); + break; + case IMAGE_CFG_CSK_INDEX: + el->csk_idx = strtol(value1, NULL, 0); + break; + case IMAGE_CFG_JTAG_DELAY: + el->jtag_delay = strtoul(value1, NULL, 0); + break; + case IMAGE_CFG_BOX_ID: + el->boxid = strtoul(value1, NULL, 0); + break; + case IMAGE_CFG_FLASH_ID: + el->flashid = strtoul(value1, NULL, 0); + break; + case IMAGE_CFG_SEC_SPECIALIZED_IMG: + el->sec_specialized_img = true; + break; + case IMAGE_CFG_SEC_COMMON_IMG: + el->sec_specialized_img = false; + break; + case IMAGE_CFG_SEC_BOOT_DEV: + el->sec_boot_dev = strtoul(value1, NULL, 0); + break; + case IMAGE_CFG_SEC_FUSE_DUMP: + el->name = strdup(value1); + break; + default: + fprintf(stderr, unknown_msg, line); + } + + return 0; +} + +/* + * Parse the configuration file 'fcfg' into the array of configuration + * elements 'image_cfg', and return the number of configuration + * elements in 'cfgn'. + */ +static int image_create_config_parse(FILE *fcfg) +{ + int ret; + int cfgi = 0; + + /* Parse the configuration file */ + while (!feof(fcfg)) { + char *line; + char buf[256]; + + /* Read the current line */ + memset(buf, 0, sizeof(buf)); + line = fgets(buf, sizeof(buf), fcfg); + if (!line) + break; + + /* Ignore useless lines */ + if (line[0] == '\n' || line[0] == '#') + continue; + + /* Strip final newline */ + if (line[strlen(line) - 1] == '\n') + line[strlen(line) - 1] = 0; + + /* Parse the current line */ + ret = image_create_config_parse_oneline(line, + &image_cfg[cfgi]); + if (ret) + return ret; + + cfgi++; + + if (cfgi >= IMAGE_CFG_ELEMENT_MAX) { + fprintf(stderr, + "Too many configuration elements in .cfg file\n"); + return -1; + } + } + + cfgn = cfgi; + return 0; +} + +static int image_get_version(void) +{ + struct image_cfg_element *e; + + e = image_find_option(IMAGE_CFG_VERSION); + if (!e) + return -1; + + return e->version; +} + +static void kwbimage_set_header(void *ptr, struct stat *sbuf, int ifd, + struct image_tool_params *params) +{ + FILE *fcfg; + void *image = NULL; + int version; + size_t headersz = 0; + uint32_t checksum; + int ret; + int size; + + fcfg = fopen(params->imagename, "r"); + if (!fcfg) { + fprintf(stderr, "Could not open input file %s\n", + params->imagename); + exit(EXIT_FAILURE); + } + + image_cfg = malloc(IMAGE_CFG_ELEMENT_MAX * + sizeof(struct image_cfg_element)); + if (!image_cfg) { + fprintf(stderr, "Cannot allocate memory\n"); + fclose(fcfg); + exit(EXIT_FAILURE); + } + + memset(image_cfg, 0, + IMAGE_CFG_ELEMENT_MAX * sizeof(struct image_cfg_element)); + rewind(fcfg); + + ret = image_create_config_parse(fcfg); + fclose(fcfg); + if (ret) { + free(image_cfg); + exit(EXIT_FAILURE); + } + + /* The MVEBU BootROM does not allow non word aligned payloads */ + sbuf->st_size = ALIGN_SUP(sbuf->st_size, 4); + + version = image_get_version(); + switch (version) { + /* + * Fallback to version 0 if no version is provided in the + * cfg file + */ + case -1: + case 0: + image = image_create_v0(&headersz, params, sbuf->st_size); + break; + + case 1: + image = image_create_v1(&headersz, params, ptr, sbuf->st_size); + break; + + default: + fprintf(stderr, "Unsupported version %d\n", version); + free(image_cfg); + exit(EXIT_FAILURE); + } + + if (!image) { + fprintf(stderr, "Could not create image\n"); + free(image_cfg); + exit(EXIT_FAILURE); + } + + free(image_cfg); + + /* Build and add image checksum header */ + checksum = + cpu_to_le32(image_checksum32((uint32_t *)ptr, sbuf->st_size)); + size = write(ifd, &checksum, sizeof(uint32_t)); + if (size != sizeof(uint32_t)) { + fprintf(stderr, "Error:%s - Checksum write %d bytes %s\n", + params->cmdname, size, params->imagefile); + exit(EXIT_FAILURE); + } + + sbuf->st_size += sizeof(uint32_t); + + /* Finally copy the header into the image area */ + memcpy(ptr, image, headersz); + + free(image); +} + +static void kwbimage_print_header(const void *ptr) +{ + struct main_hdr_v0 *mhdr = (struct main_hdr_v0 *)ptr; + + printf("Image Type: MVEBU Boot from %s Image\n", + image_boot_mode_name(mhdr->blockid)); + printf("Image version:%d\n", image_version((void *)ptr)); + printf("Data Size: "); + genimg_print_size(mhdr->blocksize - sizeof(uint32_t)); + printf("Load Address: %08x\n", mhdr->destaddr); + printf("Entry Point: %08x\n", mhdr->execaddr); +} + +static int kwbimage_check_image_types(uint8_t type) +{ + if (type == IH_TYPE_KWBIMAGE) + return EXIT_SUCCESS; + + return EXIT_FAILURE; +} + +static int kwbimage_verify_header(unsigned char *ptr, int image_size, + struct image_tool_params *params) +{ + uint8_t checksum; + size_t header_size = kwbimage_header_size(ptr); + + if (header_size > image_size) + return -FDT_ERR_BADSTRUCTURE; + + if (!main_hdr_checksum_ok(ptr)) + return -FDT_ERR_BADSTRUCTURE; + + /* Only version 0 extended header has checksum */ + if (image_version((void *)ptr) == 0) { + struct ext_hdr_v0 *ext_hdr; + + ext_hdr = (struct ext_hdr_v0 *) + (ptr + sizeof(struct main_hdr_v0)); + checksum = image_checksum8(ext_hdr, + sizeof(struct ext_hdr_v0) + - sizeof(uint8_t)); + if (checksum != ext_hdr->checksum) + return -FDT_ERR_BADSTRUCTURE; + } + + return 0; +} + +static int kwbimage_generate(struct image_tool_params *params, + struct image_type_params *tparams) +{ + FILE *fcfg; + int alloc_len; + int version; + void *hdr; + int ret; + + fcfg = fopen(params->imagename, "r"); + if (!fcfg) { + fprintf(stderr, "Could not open input file %s\n", + params->imagename); + exit(EXIT_FAILURE); + } + + image_cfg = malloc(IMAGE_CFG_ELEMENT_MAX * + sizeof(struct image_cfg_element)); + if (!image_cfg) { + fprintf(stderr, "Cannot allocate memory\n"); + fclose(fcfg); + exit(EXIT_FAILURE); + } + + memset(image_cfg, 0, + IMAGE_CFG_ELEMENT_MAX * sizeof(struct image_cfg_element)); + rewind(fcfg); + + ret = image_create_config_parse(fcfg); + fclose(fcfg); + if (ret) { + free(image_cfg); + exit(EXIT_FAILURE); + } + + version = image_get_version(); + switch (version) { + /* + * Fallback to version 0 if no version is provided in the + * cfg file + */ + case -1: + case 0: + alloc_len = sizeof(struct main_hdr_v0) + + sizeof(struct ext_hdr_v0); + break; + + case 1: + alloc_len = image_headersz_v1(NULL); + break; + + default: + fprintf(stderr, "Unsupported version %d\n", version); + free(image_cfg); + exit(EXIT_FAILURE); + } + + free(image_cfg); + + hdr = malloc(alloc_len); + if (!hdr) { + fprintf(stderr, "%s: malloc return failure: %s\n", + params->cmdname, strerror(errno)); + exit(EXIT_FAILURE); + } + + memset(hdr, 0, alloc_len); + tparams->header_size = alloc_len; + tparams->hdr = hdr; + + /* + * The resulting image needs to be 4-byte aligned. At least + * the Marvell hdrparser tool complains if its unaligned. + * By returning 1 here in this function, called via + * tparams->vrec_header() in mkimage.c, mkimage will + * automatically pad the the resulting image to a 4-byte + * size if necessary. + */ + return 1; +} + +/* + * Report Error if xflag is set in addition to default + */ +static int kwbimage_check_params(struct image_tool_params *params) +{ + if (!strlen(params->imagename)) { + char *msg = "Configuration file for kwbimage creation omitted"; + + fprintf(stderr, "Error:%s - %s\n", params->cmdname, msg); + return CFG_INVALID; + } + + return (params->dflag && (params->fflag || params->lflag)) || + (params->fflag && (params->dflag || params->lflag)) || + (params->lflag && (params->dflag || params->fflag)) || + (params->xflag) || !(strlen(params->imagename)); +} + +/* + * kwbimage type parameters definition + */ +U_BOOT_IMAGE_TYPE( + kwbimage, + "Marvell MVEBU Boot Image support", + 0, + NULL, + kwbimage_check_params, + kwbimage_verify_header, + kwbimage_print_header, + kwbimage_set_header, + NULL, + kwbimage_check_image_types, + NULL, + kwbimage_generate +); diff --git a/tools/u-boot-tools/kwbimage.h b/tools/u-boot-tools/kwbimage.h new file mode 100644 index 0000000..25bc08c --- /dev/null +++ b/tools/u-boot-tools/kwbimage.h @@ -0,0 +1,202 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * (C) Copyright 2008 + * Marvell Semiconductor <www.marvell.com> + * Written-by: Prafulla Wadaskar <prafulla@marvell.com> + */ + +#ifndef _KWBIMAGE_H_ +#define _KWBIMAGE_H_ + +#include <compiler.h> +#include <stdint.h> + +#define KWBIMAGE_MAX_CONFIG ((0x1dc - 0x20)/sizeof(struct reg_config)) +#define MAX_TEMPBUF_LEN 32 + +/* NAND ECC Mode */ +#define IBR_HDR_ECC_DEFAULT 0x00 +#define IBR_HDR_ECC_FORCED_HAMMING 0x01 +#define IBR_HDR_ECC_FORCED_RS 0x02 +#define IBR_HDR_ECC_DISABLED 0x03 + +/* Boot Type - block ID */ +#define IBR_HDR_I2C_ID 0x4D +#define IBR_HDR_SPI_ID 0x5A +#define IBR_HDR_NAND_ID 0x8B +#define IBR_HDR_SATA_ID 0x78 +#define IBR_HDR_PEX_ID 0x9C +#define IBR_HDR_UART_ID 0x69 +#define IBR_DEF_ATTRIB 0x00 + +#define ALIGN_SUP(x, a) (((x) + (a - 1)) & ~(a - 1)) + +/* Structure of the main header, version 0 (Kirkwood, Dove) */ +struct main_hdr_v0 { + uint8_t blockid; /* 0x0 */ + uint8_t nandeccmode; /* 0x1 */ + uint16_t nandpagesize; /* 0x2-0x3 */ + uint32_t blocksize; /* 0x4-0x7 */ + uint32_t rsvd1; /* 0x8-0xB */ + uint32_t srcaddr; /* 0xC-0xF */ + uint32_t destaddr; /* 0x10-0x13 */ + uint32_t execaddr; /* 0x14-0x17 */ + uint8_t satapiomode; /* 0x18 */ + uint8_t rsvd3; /* 0x19 */ + uint16_t ddrinitdelay; /* 0x1A-0x1B */ + uint16_t rsvd2; /* 0x1C-0x1D */ + uint8_t ext; /* 0x1E */ + uint8_t checksum; /* 0x1F */ +}; + +struct ext_hdr_v0_reg { + uint32_t raddr; + uint32_t rdata; +}; + +#define EXT_HDR_V0_REG_COUNT ((0x1dc - 0x20) / sizeof(struct ext_hdr_v0_reg)) + +struct ext_hdr_v0 { + uint32_t offset; + uint8_t reserved[0x20 - sizeof(uint32_t)]; + struct ext_hdr_v0_reg rcfg[EXT_HDR_V0_REG_COUNT]; + uint8_t reserved2[7]; + uint8_t checksum; +}; + +struct kwb_header { + struct main_hdr_v0 kwb_hdr; + struct ext_hdr_v0 kwb_exthdr; +}; + +/* Structure of the main header, version 1 (Armada 370/38x/XP) */ +struct main_hdr_v1 { + uint8_t blockid; /* 0x0 */ + uint8_t flags; /* 0x1 */ + uint16_t reserved2; /* 0x2-0x3 */ + uint32_t blocksize; /* 0x4-0x7 */ + uint8_t version; /* 0x8 */ + uint8_t headersz_msb; /* 0x9 */ + uint16_t headersz_lsb; /* 0xA-0xB */ + uint32_t srcaddr; /* 0xC-0xF */ + uint32_t destaddr; /* 0x10-0x13 */ + uint32_t execaddr; /* 0x14-0x17 */ + uint8_t options; /* 0x18 */ + uint8_t nandblocksize; /* 0x19 */ + uint8_t nandbadblklocation; /* 0x1A */ + uint8_t reserved4; /* 0x1B */ + uint16_t reserved5; /* 0x1C-0x1D */ + uint8_t ext; /* 0x1E */ + uint8_t checksum; /* 0x1F */ +}; + +/* + * Main header options + */ +#define MAIN_HDR_V1_OPT_BAUD_DEFAULT 0 +#define MAIN_HDR_V1_OPT_BAUD_2400 0x1 +#define MAIN_HDR_V1_OPT_BAUD_4800 0x2 +#define MAIN_HDR_V1_OPT_BAUD_9600 0x3 +#define MAIN_HDR_V1_OPT_BAUD_19200 0x4 +#define MAIN_HDR_V1_OPT_BAUD_38400 0x5 +#define MAIN_HDR_V1_OPT_BAUD_57600 0x6 +#define MAIN_HDR_V1_OPT_BAUD_115200 0x7 + +/* + * Header for the optional headers, version 1 (Armada 370, Armada XP) + */ +struct opt_hdr_v1 { + uint8_t headertype; + uint8_t headersz_msb; + uint16_t headersz_lsb; + char data[0]; +}; + +/* + * Public Key data in DER format + */ +struct pubkey_der_v1 { + uint8_t key[524]; +}; + +/* + * Signature (RSA 2048) + */ +struct sig_v1 { + uint8_t sig[256]; +}; + +/* + * Structure of secure header (Armada 38x) + */ +struct secure_hdr_v1 { + uint8_t headertype; /* 0x0 */ + uint8_t headersz_msb; /* 0x1 */ + uint16_t headersz_lsb; /* 0x2 - 0x3 */ + uint32_t reserved1; /* 0x4 - 0x7 */ + struct pubkey_der_v1 kak; /* 0x8 - 0x213 */ + uint8_t jtag_delay; /* 0x214 */ + uint8_t reserved2; /* 0x215 */ + uint16_t reserved3; /* 0x216 - 0x217 */ + uint32_t boxid; /* 0x218 - 0x21B */ + uint32_t flashid; /* 0x21C - 0x21F */ + struct sig_v1 hdrsig; /* 0x220 - 0x31F */ + struct sig_v1 imgsig; /* 0x320 - 0x41F */ + struct pubkey_der_v1 csk[16]; /* 0x420 - 0x24DF */ + struct sig_v1 csksig; /* 0x24E0 - 0x25DF */ + uint8_t next; /* 0x25E0 */ + uint8_t reserved4; /* 0x25E1 */ + uint16_t reserved5; /* 0x25E2 - 0x25E3 */ +}; + +/* + * Various values for the opt_hdr_v1->headertype field, describing the + * different types of optional headers. The "secure" header contains + * informations related to secure boot (encryption keys, etc.). The + * "binary" header contains ARM binary code to be executed prior to + * executing the main payload (usually the bootloader). This is + * typically used to execute DDR3 training code. The "register" header + * allows to describe a set of (address, value) tuples that are + * generally used to configure the DRAM controller. + */ +#define OPT_HDR_V1_SECURE_TYPE 0x1 +#define OPT_HDR_V1_BINARY_TYPE 0x2 +#define OPT_HDR_V1_REGISTER_TYPE 0x3 + +#define KWBHEADER_V1_SIZE(hdr) \ + (((hdr)->headersz_msb << 16) | le16_to_cpu((hdr)->headersz_lsb)) + +enum kwbimage_cmd { + CMD_INVALID, + CMD_BOOT_FROM, + CMD_NAND_ECC_MODE, + CMD_NAND_PAGE_SIZE, + CMD_SATA_PIO_MODE, + CMD_DDR_INIT_DELAY, + CMD_DATA +}; + +enum kwbimage_cmd_types { + CFG_INVALID = -1, + CFG_COMMAND, + CFG_DATA0, + CFG_DATA1 +}; + +/* + * functions + */ +void init_kwb_image_type (void); + +/* + * Byte 8 of the image header contains the version number. In the v0 + * header, byte 8 was reserved, and always set to 0. In the v1 header, + * byte 8 has been changed to a proper field, set to 1. + */ +static inline unsigned int image_version(void *header) +{ + unsigned char *ptr = header; + return ptr[8]; +} + +#endif /* _KWBIMAGE_H_ */ diff --git a/tools/u-boot-tools/kwbimage.o b/tools/u-boot-tools/kwbimage.o new file mode 100644 index 0000000000000000000000000000000000000000..5bddaa2ae5368bd770a51bbf2c7389b4a85f2d9b GIT binary patch literal 19992 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 Gx_$uB2CcpT literal 0 HcmV?d00001 diff --git a/tools/u-boot-tools/kwboot.c b/tools/u-boot-tools/kwboot.c new file mode 100644 index 0000000..4be094c --- /dev/null +++ b/tools/u-boot-tools/kwboot.c @@ -0,0 +1,867 @@ +/* + * Boot a Marvell SoC, with Xmodem over UART0. + * supports Kirkwood, Dove, Armada 370, Armada XP + * + * (c) 2012 Daniel Stodden <daniel.stodden@gmail.com> + * + * References: marvell.com, "88F6180, 88F6190, 88F6192, and 88F6281 + * Integrated Controller: Functional Specifications" December 2, + * 2008. Chapter 24.2 "BootROM Firmware". + */ + +#include "kwbimage.h" +#include "mkimage.h" + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <stdarg.h> +#include <image.h> +#include <libgen.h> +#include <fcntl.h> +#include <errno.h> +#include <unistd.h> +#include <stdint.h> +#include <termios.h> +#include <sys/mman.h> +#include <sys/stat.h> + +#ifdef __GNUC__ +#define PACKED __attribute((packed)) +#else +#define PACKED +#endif + +/* + * Marvell BootROM UART Sensing + */ + +static unsigned char kwboot_msg_boot[] = { + 0xBB, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77 +}; + +static unsigned char kwboot_msg_debug[] = { + 0xDD, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77 +}; + +/* Defines known to work on Kirkwood */ +#define KWBOOT_MSG_REQ_DELAY 10 /* ms */ +#define KWBOOT_MSG_RSP_TIMEO 50 /* ms */ + +/* Defines known to work on Armada XP */ +#define KWBOOT_MSG_REQ_DELAY_AXP 1000 /* ms */ +#define KWBOOT_MSG_RSP_TIMEO_AXP 1000 /* ms */ + +/* + * Xmodem Transfers + */ + +#define SOH 1 /* sender start of block header */ +#define EOT 4 /* sender end of block transfer */ +#define ACK 6 /* target block ack */ +#define NAK 21 /* target block negative ack */ +#define CAN 24 /* target/sender transfer cancellation */ + +struct kwboot_block { + uint8_t soh; + uint8_t pnum; + uint8_t _pnum; + uint8_t data[128]; + uint8_t csum; +} PACKED; + +#define KWBOOT_BLK_RSP_TIMEO 1000 /* ms */ + +static int kwboot_verbose; + +static int msg_req_delay = KWBOOT_MSG_REQ_DELAY; +static int msg_rsp_timeo = KWBOOT_MSG_RSP_TIMEO; +static int blk_rsp_timeo = KWBOOT_BLK_RSP_TIMEO; + +static void +kwboot_printv(const char *fmt, ...) +{ + va_list ap; + + if (kwboot_verbose) { + va_start(ap, fmt); + vprintf(fmt, ap); + va_end(ap); + fflush(stdout); + } +} + +static void +__spinner(void) +{ + const char seq[] = { '-', '\\', '|', '/' }; + const int div = 8; + static int state, bs; + + if (state % div == 0) { + fputc(bs, stdout); + fputc(seq[state / div % sizeof(seq)], stdout); + fflush(stdout); + } + + bs = '\b'; + state++; +} + +static void +kwboot_spinner(void) +{ + if (kwboot_verbose) + __spinner(); +} + +static void +__progress(int pct, char c) +{ + const int width = 70; + static const char *nl = ""; + static int pos; + + if (pos % width == 0) + printf("%s%3d %% [", nl, pct); + + fputc(c, stdout); + + nl = "]\n"; + pos++; + + if (pct == 100) { + while (pos++ < width) + fputc(' ', stdout); + fputs(nl, stdout); + } + + fflush(stdout); + +} + +static void +kwboot_progress(int _pct, char c) +{ + static int pct; + + if (_pct != -1) + pct = _pct; + + if (kwboot_verbose) + __progress(pct, c); +} + +static int +kwboot_tty_recv(int fd, void *buf, size_t len, int timeo) +{ + int rc, nfds; + fd_set rfds; + struct timeval tv; + ssize_t n; + + rc = -1; + + FD_ZERO(&rfds); + FD_SET(fd, &rfds); + + tv.tv_sec = 0; + tv.tv_usec = timeo * 1000; + if (tv.tv_usec > 1000000) { + tv.tv_sec += tv.tv_usec / 1000000; + tv.tv_usec %= 1000000; + } + + do { + nfds = select(fd + 1, &rfds, NULL, NULL, &tv); + if (nfds < 0) + goto out; + if (!nfds) { + errno = ETIMEDOUT; + goto out; + } + + n = read(fd, buf, len); + if (n <= 0) + goto out; + + buf = (char *)buf + n; + len -= n; + } while (len > 0); + + rc = 0; +out: + return rc; +} + +static int +kwboot_tty_send(int fd, const void *buf, size_t len) +{ + int rc; + ssize_t n; + + if (!buf) + return 0; + + rc = -1; + + do { + n = write(fd, buf, len); + if (n < 0) + goto out; + + buf = (char *)buf + n; + len -= n; + } while (len > 0); + + rc = tcdrain(fd); +out: + return rc; +} + +static int +kwboot_tty_send_char(int fd, unsigned char c) +{ + return kwboot_tty_send(fd, &c, 1); +} + +static speed_t +kwboot_tty_speed(int baudrate) +{ + switch (baudrate) { + case 115200: + return B115200; + case 57600: + return B57600; + case 38400: + return B38400; + case 19200: + return B19200; + case 9600: + return B9600; + } + + return -1; +} + +static int +kwboot_open_tty(const char *path, speed_t speed) +{ + int rc, fd; + struct termios tio; + + rc = -1; + + fd = open(path, O_RDWR|O_NOCTTY|O_NDELAY); + if (fd < 0) + goto out; + + memset(&tio, 0, sizeof(tio)); + + tio.c_iflag = 0; + tio.c_cflag = CREAD|CLOCAL|CS8; + + tio.c_cc[VMIN] = 1; + tio.c_cc[VTIME] = 10; + + cfsetospeed(&tio, speed); + cfsetispeed(&tio, speed); + + rc = tcsetattr(fd, TCSANOW, &tio); + if (rc) + goto out; + + rc = fd; +out: + if (rc < 0) { + if (fd >= 0) + close(fd); + } + + return rc; +} + +static int +kwboot_bootmsg(int tty, void *msg) +{ + int rc; + char c; + int count; + + if (msg == NULL) + kwboot_printv("Please reboot the target into UART boot mode..."); + else + kwboot_printv("Sending boot message. Please reboot the target..."); + + do { + rc = tcflush(tty, TCIOFLUSH); + if (rc) + break; + + for (count = 0; count < 128; count++) { + rc = kwboot_tty_send(tty, msg, 8); + if (rc) { + usleep(msg_req_delay * 1000); + continue; + } + } + + rc = kwboot_tty_recv(tty, &c, 1, msg_rsp_timeo); + + kwboot_spinner(); + + } while (rc || c != NAK); + + kwboot_printv("\n"); + + return rc; +} + +static int +kwboot_debugmsg(int tty, void *msg) +{ + int rc; + + kwboot_printv("Sending debug message. Please reboot the target..."); + + do { + char buf[16]; + + rc = tcflush(tty, TCIOFLUSH); + if (rc) + break; + + rc = kwboot_tty_send(tty, msg, 8); + if (rc) { + usleep(msg_req_delay * 1000); + continue; + } + + rc = kwboot_tty_recv(tty, buf, 16, msg_rsp_timeo); + + kwboot_spinner(); + + } while (rc); + + kwboot_printv("\n"); + + return rc; +} + +static int +kwboot_xm_makeblock(struct kwboot_block *block, const void *data, + size_t size, int pnum) +{ + const size_t blksz = sizeof(block->data); + size_t n; + int i; + + block->soh = SOH; + block->pnum = pnum; + block->_pnum = ~block->pnum; + + n = size < blksz ? size : blksz; + memcpy(&block->data[0], data, n); + memset(&block->data[n], 0, blksz - n); + + block->csum = 0; + for (i = 0; i < n; i++) + block->csum += block->data[i]; + + return n; +} + +static int +kwboot_xm_sendblock(int fd, struct kwboot_block *block) +{ + int rc, retries; + char c; + + retries = 16; + do { + rc = kwboot_tty_send(fd, block, sizeof(*block)); + if (rc) + break; + + do { + rc = kwboot_tty_recv(fd, &c, 1, blk_rsp_timeo); + if (rc) + break; + + if (c != ACK && c != NAK && c != CAN) + printf("%c", c); + + } while (c != ACK && c != NAK && c != CAN); + + if (c != ACK) + kwboot_progress(-1, '+'); + + } while (c == NAK && retries-- > 0); + + rc = -1; + + switch (c) { + case ACK: + rc = 0; + break; + case NAK: + errno = EBADMSG; + break; + case CAN: + errno = ECANCELED; + break; + default: + errno = EPROTO; + break; + } + + return rc; +} + +static int +kwboot_xmodem(int tty, const void *_data, size_t size) +{ + const uint8_t *data = _data; + int rc, pnum, N, err; + + pnum = 1; + N = 0; + + kwboot_printv("Sending boot image...\n"); + + sleep(2); /* flush isn't effective without it */ + tcflush(tty, TCIOFLUSH); + + do { + struct kwboot_block block; + int n; + + n = kwboot_xm_makeblock(&block, + data + N, size - N, + pnum++); + if (n < 0) + goto can; + + if (!n) + break; + + rc = kwboot_xm_sendblock(tty, &block); + if (rc) + goto out; + + N += n; + kwboot_progress(N * 100 / size, '.'); + } while (1); + + rc = kwboot_tty_send_char(tty, EOT); + +out: + return rc; + +can: + err = errno; + kwboot_tty_send_char(tty, CAN); + errno = err; + goto out; +} + +static int +kwboot_term_pipe(int in, int out, char *quit, int *s) +{ + ssize_t nin, nout; + char _buf[128], *buf = _buf; + + nin = read(in, buf, sizeof(buf)); + if (nin <= 0) + return -1; + + if (quit) { + int i; + + for (i = 0; i < nin; i++) { + if (*buf == quit[*s]) { + (*s)++; + if (!quit[*s]) + return 0; + buf++; + nin--; + } else + while (*s > 0) { + nout = write(out, quit, *s); + if (nout <= 0) + return -1; + (*s) -= nout; + } + } + } + + while (nin > 0) { + nout = write(out, buf, nin); + if (nout <= 0) + return -1; + nin -= nout; + } + + return 0; +} + +static int +kwboot_terminal(int tty) +{ + int rc, in, s; + char *quit = "\34c"; + struct termios otio, tio; + + rc = -1; + + in = STDIN_FILENO; + if (isatty(in)) { + rc = tcgetattr(in, &otio); + if (!rc) { + tio = otio; + cfmakeraw(&tio); + rc = tcsetattr(in, TCSANOW, &tio); + } + if (rc) { + perror("tcsetattr"); + goto out; + } + + kwboot_printv("[Type Ctrl-%c + %c to quit]\r\n", + quit[0]|0100, quit[1]); + } else + in = -1; + + rc = 0; + s = 0; + + do { + fd_set rfds; + int nfds = 0; + + FD_SET(tty, &rfds); + nfds = nfds < tty ? tty : nfds; + + if (in >= 0) { + FD_SET(in, &rfds); + nfds = nfds < in ? in : nfds; + } + + nfds = select(nfds + 1, &rfds, NULL, NULL, NULL); + if (nfds < 0) + break; + + if (FD_ISSET(tty, &rfds)) { + rc = kwboot_term_pipe(tty, STDOUT_FILENO, NULL, NULL); + if (rc) + break; + } + + if (FD_ISSET(in, &rfds)) { + rc = kwboot_term_pipe(in, tty, quit, &s); + if (rc) + break; + } + } while (quit[s] != 0); + + tcsetattr(in, TCSANOW, &otio); +out: + return rc; +} + +static void * +kwboot_mmap_image(const char *path, size_t *size, int prot) +{ + int rc, fd, flags; + struct stat st; + void *img; + + rc = -1; + img = NULL; + + fd = open(path, O_RDONLY); + if (fd < 0) + goto out; + + rc = fstat(fd, &st); + if (rc) + goto out; + + flags = (prot & PROT_WRITE) ? MAP_PRIVATE : MAP_SHARED; + + img = mmap(NULL, st.st_size, prot, flags, fd, 0); + if (img == MAP_FAILED) { + img = NULL; + goto out; + } + + rc = 0; + *size = st.st_size; +out: + if (rc && img) { + munmap(img, st.st_size); + img = NULL; + } + if (fd >= 0) + close(fd); + + return img; +} + +static uint8_t +kwboot_img_csum8(void *_data, size_t size) +{ + uint8_t *data = _data, csum; + + for (csum = 0; size-- > 0; data++) + csum += *data; + + return csum; +} + +static int +kwboot_img_patch_hdr(void *img, size_t size) +{ + int rc; + struct main_hdr_v1 *hdr; + uint8_t csum; + size_t hdrsz = sizeof(*hdr); + int image_ver; + + rc = -1; + hdr = img; + + if (size < hdrsz) { + errno = EINVAL; + goto out; + } + + image_ver = image_version(img); + if (image_ver < 0) { + fprintf(stderr, "Invalid image header version\n"); + errno = EINVAL; + goto out; + } + + if (image_ver == 0) + hdrsz = sizeof(*hdr); + else + hdrsz = KWBHEADER_V1_SIZE(hdr); + + csum = kwboot_img_csum8(hdr, hdrsz) - hdr->checksum; + if (csum != hdr->checksum) { + errno = EINVAL; + goto out; + } + + if (hdr->blockid == IBR_HDR_UART_ID) { + rc = 0; + goto out; + } + + hdr->blockid = IBR_HDR_UART_ID; + + if (image_ver == 0) { + struct main_hdr_v0 *hdr_v0 = img; + + hdr_v0->nandeccmode = IBR_HDR_ECC_DISABLED; + hdr_v0->nandpagesize = 0; + + hdr_v0->srcaddr = hdr_v0->ext + ? sizeof(struct kwb_header) + : sizeof(*hdr_v0); + } + + hdr->checksum = kwboot_img_csum8(hdr, hdrsz) - csum; + + rc = 0; +out: + return rc; +} + +static void +kwboot_usage(FILE *stream, char *progname) +{ + fprintf(stream, + "Usage: %s [OPTIONS] [-b <image> | -D <image> ] [-B <baud> ] <TTY>\n", + progname); + fprintf(stream, "\n"); + fprintf(stream, + " -b <image>: boot <image> with preamble (Kirkwood, Armada 370/XP)\n"); + fprintf(stream, " -p: patch <image> to type 0x69 (uart boot)\n"); + fprintf(stream, + " -D <image>: boot <image> without preamble (Dove)\n"); + fprintf(stream, " -d: enter debug mode\n"); + fprintf(stream, " -a: use timings for Armada XP\n"); + fprintf(stream, " -q <req-delay>: use specific request-delay\n"); + fprintf(stream, " -s <resp-timeo>: use specific response-timeout\n"); + fprintf(stream, + " -o <block-timeo>: use specific xmodem block timeout\n"); + fprintf(stream, "\n"); + fprintf(stream, " -t: mini terminal\n"); + fprintf(stream, "\n"); + fprintf(stream, " -B <baud>: set baud rate\n"); + fprintf(stream, "\n"); +} + +int +main(int argc, char **argv) +{ + const char *ttypath, *imgpath; + int rv, rc, tty, term, prot, patch; + void *bootmsg; + void *debugmsg; + void *img; + size_t size; + speed_t speed; + + rv = 1; + tty = -1; + bootmsg = NULL; + debugmsg = NULL; + imgpath = NULL; + img = NULL; + term = 0; + patch = 0; + size = 0; + speed = B115200; + + kwboot_verbose = isatty(STDOUT_FILENO); + + do { + int c = getopt(argc, argv, "hb:ptaB:dD:q:s:o:"); + if (c < 0) + break; + + switch (c) { + case 'b': + bootmsg = kwboot_msg_boot; + imgpath = optarg; + break; + + case 'D': + bootmsg = NULL; + imgpath = optarg; + break; + + case 'd': + debugmsg = kwboot_msg_debug; + break; + + case 'p': + patch = 1; + break; + + case 't': + term = 1; + break; + + case 'a': + msg_req_delay = KWBOOT_MSG_REQ_DELAY_AXP; + msg_rsp_timeo = KWBOOT_MSG_RSP_TIMEO_AXP; + break; + + case 'q': + msg_req_delay = atoi(optarg); + break; + + case 's': + msg_rsp_timeo = atoi(optarg); + break; + + case 'o': + blk_rsp_timeo = atoi(optarg); + break; + + case 'B': + speed = kwboot_tty_speed(atoi(optarg)); + if (speed == -1) + goto usage; + break; + + case 'h': + rv = 0; + default: + goto usage; + } + } while (1); + + if (!bootmsg && !term && !debugmsg) + goto usage; + + if (patch && !imgpath) + goto usage; + + if (argc - optind < 1) + goto usage; + + ttypath = argv[optind++]; + + tty = kwboot_open_tty(ttypath, speed); + if (tty < 0) { + perror(ttypath); + goto out; + } + + if (imgpath) { + prot = PROT_READ | (patch ? PROT_WRITE : 0); + + img = kwboot_mmap_image(imgpath, &size, prot); + if (!img) { + perror(imgpath); + goto out; + } + } + + if (patch) { + rc = kwboot_img_patch_hdr(img, size); + if (rc) { + fprintf(stderr, "%s: Invalid image.\n", imgpath); + goto out; + } + } + + if (debugmsg) { + rc = kwboot_debugmsg(tty, debugmsg); + if (rc) { + perror("debugmsg"); + goto out; + } + } else if (bootmsg) { + rc = kwboot_bootmsg(tty, bootmsg); + if (rc) { + perror("bootmsg"); + goto out; + } + } + + if (img) { + rc = kwboot_xmodem(tty, img, size); + if (rc) { + perror("xmodem"); + goto out; + } + } + + if (term) { + rc = kwboot_terminal(tty); + if (rc && !(errno == EINTR)) { + perror("terminal"); + goto out; + } + } + + rv = 0; +out: + if (tty >= 0) + close(tty); + + if (img) + munmap(img, size); + + return rv; + +usage: + kwboot_usage(rv ? stderr : stdout, basename(argv[0])); + goto out; +} diff --git a/tools/u-boot-tools/lib/.crc16.o.cmd b/tools/u-boot-tools/lib/.crc16.o.cmd new file mode 100644 index 0000000..889fc89 --- /dev/null +++ b/tools/u-boot-tools/lib/.crc16.o.cmd @@ -0,0 +1,105 @@ +cmd_tools/lib/crc16.o := cc -Wp,-MD,tools/lib/.crc16.o.d -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -std=gnu11 -include ./include/compiler.h -idirafterinclude -idirafter./arch/arm/include -I./scripts/dtc/libfdt -I./tools -DUSE_HOSTCC -D__KERNEL_STRICT_NAMES -D_GNU_SOURCE -c -o tools/lib/crc16.o tools/lib/crc16.c + +source_tools/lib/crc16.o := tools/lib/crc16.c + +deps_tools/lib/crc16.o := \ + /usr/include/stdc-predef.h \ + include/compiler.h \ + /usr/lib/gcc/x86_64-linux-gnu/8/include/stddef.h \ + /usr/lib/gcc/x86_64-linux-gnu/8/include/stdint.h \ + /usr/include/stdint.h \ + /usr/include/x86_64-linux-gnu/bits/libc-header-start.h \ + /usr/include/features.h \ + /usr/include/x86_64-linux-gnu/sys/cdefs.h \ + /usr/include/x86_64-linux-gnu/bits/wordsize.h \ + /usr/include/x86_64-linux-gnu/bits/long-double.h \ + /usr/include/x86_64-linux-gnu/gnu/stubs.h \ + /usr/include/x86_64-linux-gnu/gnu/stubs-64.h \ + /usr/include/x86_64-linux-gnu/bits/types.h \ + /usr/include/x86_64-linux-gnu/bits/typesizes.h \ + /usr/include/x86_64-linux-gnu/bits/wchar.h \ + /usr/include/x86_64-linux-gnu/bits/stdint-intn.h \ + /usr/include/x86_64-linux-gnu/bits/stdint-uintn.h \ + /usr/include/errno.h \ + /usr/include/x86_64-linux-gnu/bits/errno.h \ + /usr/include/linux/errno.h \ + /usr/include/x86_64-linux-gnu/asm/errno.h \ + /usr/include/asm-generic/errno.h \ + /usr/include/asm-generic/errno-base.h \ + /usr/include/x86_64-linux-gnu/bits/types/error_t.h \ + /usr/include/stdlib.h \ + /usr/include/x86_64-linux-gnu/bits/waitflags.h \ + /usr/include/x86_64-linux-gnu/bits/waitstatus.h \ + /usr/include/x86_64-linux-gnu/bits/floatn.h \ + /usr/include/x86_64-linux-gnu/bits/floatn-common.h \ + /usr/include/x86_64-linux-gnu/bits/types/locale_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/__locale_t.h \ + /usr/include/x86_64-linux-gnu/sys/types.h \ + /usr/include/x86_64-linux-gnu/bits/types/clock_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/clockid_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/time_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/timer_t.h \ + /usr/include/endian.h \ + /usr/include/x86_64-linux-gnu/bits/endian.h \ + /usr/include/x86_64-linux-gnu/bits/byteswap.h \ + /usr/include/x86_64-linux-gnu/bits/uintn-identity.h \ + /usr/include/x86_64-linux-gnu/sys/select.h \ + /usr/include/x86_64-linux-gnu/bits/select.h \ + /usr/include/x86_64-linux-gnu/bits/types/sigset_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/__sigset_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/struct_timeval.h \ + /usr/include/x86_64-linux-gnu/bits/types/struct_timespec.h \ + /usr/include/x86_64-linux-gnu/bits/pthreadtypes.h \ + /usr/include/x86_64-linux-gnu/bits/thread-shared-types.h \ + /usr/include/x86_64-linux-gnu/bits/pthreadtypes-arch.h \ + /usr/include/alloca.h \ + /usr/include/x86_64-linux-gnu/bits/stdlib-bsearch.h \ + /usr/include/x86_64-linux-gnu/bits/stdlib-float.h \ + /usr/include/stdio.h \ + /usr/lib/gcc/x86_64-linux-gnu/8/include/stdarg.h \ + /usr/include/x86_64-linux-gnu/bits/types/__fpos_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/__mbstate_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/__fpos64_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/__FILE.h \ + /usr/include/x86_64-linux-gnu/bits/types/FILE.h \ + /usr/include/x86_64-linux-gnu/bits/types/struct_FILE.h \ + /usr/include/x86_64-linux-gnu/bits/types/cookie_io_functions_t.h \ + /usr/include/x86_64-linux-gnu/bits/stdio_lim.h \ + /usr/include/x86_64-linux-gnu/bits/sys_errlist.h \ + /usr/include/x86_64-linux-gnu/bits/stdio.h \ + /usr/include/string.h \ + /usr/include/strings.h \ + /usr/include/x86_64-linux-gnu/sys/mman.h \ + /usr/include/x86_64-linux-gnu/bits/mman.h \ + /usr/include/x86_64-linux-gnu/bits/mman-linux.h \ + /usr/include/x86_64-linux-gnu/bits/mman-shared.h \ + /usr/include/fcntl.h \ + /usr/include/x86_64-linux-gnu/bits/fcntl.h \ + /usr/include/x86_64-linux-gnu/bits/fcntl-linux.h \ + /usr/include/x86_64-linux-gnu/bits/types/struct_iovec.h \ + /usr/include/linux/falloc.h \ + /usr/include/x86_64-linux-gnu/bits/stat.h \ + /usr/include/byteswap.h \ + /usr/include/time.h \ + /usr/include/x86_64-linux-gnu/bits/time.h \ + /usr/include/x86_64-linux-gnu/bits/timex.h \ + /usr/include/x86_64-linux-gnu/bits/types/struct_tm.h \ + /usr/include/x86_64-linux-gnu/bits/types/struct_itimerspec.h \ + tools/../lib/crc16.c \ + /usr/include/arpa/inet.h \ + /usr/include/netinet/in.h \ + /usr/include/x86_64-linux-gnu/sys/socket.h \ + /usr/include/x86_64-linux-gnu/bits/socket.h \ + /usr/include/x86_64-linux-gnu/bits/socket_type.h \ + /usr/include/x86_64-linux-gnu/bits/sockaddr.h \ + /usr/include/x86_64-linux-gnu/asm/socket.h \ + /usr/include/asm-generic/socket.h \ + /usr/include/x86_64-linux-gnu/asm/sockios.h \ + /usr/include/asm-generic/sockios.h \ + /usr/include/x86_64-linux-gnu/bits/types/struct_osockaddr.h \ + /usr/include/x86_64-linux-gnu/bits/in.h \ + include/u-boot/crc.h \ + +tools/lib/crc16.o: $(deps_tools/lib/crc16.o) + +$(deps_tools/lib/crc16.o): diff --git a/tools/u-boot-tools/lib/.crc32.o.cmd b/tools/u-boot-tools/lib/.crc32.o.cmd new file mode 100644 index 0000000..4352529 --- /dev/null +++ b/tools/u-boot-tools/lib/.crc32.o.cmd @@ -0,0 +1,110 @@ +cmd_tools/lib/crc32.o := cc -Wp,-MD,tools/lib/.crc32.o.d -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -std=gnu11 -include ./include/compiler.h -idirafterinclude -idirafter./arch/arm/include -I./scripts/dtc/libfdt -I./tools -DUSE_HOSTCC -D__KERNEL_STRICT_NAMES -D_GNU_SOURCE -pedantic -c -o tools/lib/crc32.o tools/lib/crc32.c + +source_tools/lib/crc32.o := tools/lib/crc32.c + +deps_tools/lib/crc32.o := \ + /usr/include/stdc-predef.h \ + include/compiler.h \ + /usr/lib/gcc/x86_64-linux-gnu/8/include/stddef.h \ + /usr/lib/gcc/x86_64-linux-gnu/8/include/stdint.h \ + /usr/include/stdint.h \ + /usr/include/x86_64-linux-gnu/bits/libc-header-start.h \ + /usr/include/features.h \ + /usr/include/x86_64-linux-gnu/sys/cdefs.h \ + /usr/include/x86_64-linux-gnu/bits/wordsize.h \ + /usr/include/x86_64-linux-gnu/bits/long-double.h \ + /usr/include/x86_64-linux-gnu/gnu/stubs.h \ + /usr/include/x86_64-linux-gnu/gnu/stubs-64.h \ + /usr/include/x86_64-linux-gnu/bits/types.h \ + /usr/include/x86_64-linux-gnu/bits/typesizes.h \ + /usr/include/x86_64-linux-gnu/bits/wchar.h \ + /usr/include/x86_64-linux-gnu/bits/stdint-intn.h \ + /usr/include/x86_64-linux-gnu/bits/stdint-uintn.h \ + /usr/include/errno.h \ + /usr/include/x86_64-linux-gnu/bits/errno.h \ + /usr/include/linux/errno.h \ + /usr/include/x86_64-linux-gnu/asm/errno.h \ + /usr/include/asm-generic/errno.h \ + /usr/include/asm-generic/errno-base.h \ + /usr/include/x86_64-linux-gnu/bits/types/error_t.h \ + /usr/include/stdlib.h \ + /usr/include/x86_64-linux-gnu/bits/waitflags.h \ + /usr/include/x86_64-linux-gnu/bits/waitstatus.h \ + /usr/include/x86_64-linux-gnu/bits/floatn.h \ + /usr/include/x86_64-linux-gnu/bits/floatn-common.h \ + /usr/include/x86_64-linux-gnu/bits/types/locale_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/__locale_t.h \ + /usr/include/x86_64-linux-gnu/sys/types.h \ + /usr/include/x86_64-linux-gnu/bits/types/clock_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/clockid_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/time_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/timer_t.h \ + /usr/include/endian.h \ + /usr/include/x86_64-linux-gnu/bits/endian.h \ + /usr/include/x86_64-linux-gnu/bits/byteswap.h \ + /usr/include/x86_64-linux-gnu/bits/uintn-identity.h \ + /usr/include/x86_64-linux-gnu/sys/select.h \ + /usr/include/x86_64-linux-gnu/bits/select.h \ + /usr/include/x86_64-linux-gnu/bits/types/sigset_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/__sigset_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/struct_timeval.h \ + /usr/include/x86_64-linux-gnu/bits/types/struct_timespec.h \ + /usr/include/x86_64-linux-gnu/bits/pthreadtypes.h \ + /usr/include/x86_64-linux-gnu/bits/thread-shared-types.h \ + /usr/include/x86_64-linux-gnu/bits/pthreadtypes-arch.h \ + /usr/include/alloca.h \ + /usr/include/x86_64-linux-gnu/bits/stdlib-bsearch.h \ + /usr/include/x86_64-linux-gnu/bits/stdlib-float.h \ + /usr/include/stdio.h \ + /usr/lib/gcc/x86_64-linux-gnu/8/include/stdarg.h \ + /usr/include/x86_64-linux-gnu/bits/types/__fpos_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/__mbstate_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/__fpos64_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/__FILE.h \ + /usr/include/x86_64-linux-gnu/bits/types/FILE.h \ + /usr/include/x86_64-linux-gnu/bits/types/struct_FILE.h \ + /usr/include/x86_64-linux-gnu/bits/types/cookie_io_functions_t.h \ + /usr/include/x86_64-linux-gnu/bits/stdio_lim.h \ + /usr/include/x86_64-linux-gnu/bits/sys_errlist.h \ + /usr/include/x86_64-linux-gnu/bits/stdio.h \ + /usr/include/string.h \ + /usr/include/strings.h \ + /usr/include/x86_64-linux-gnu/sys/mman.h \ + /usr/include/x86_64-linux-gnu/bits/mman.h \ + /usr/include/x86_64-linux-gnu/bits/mman-linux.h \ + /usr/include/x86_64-linux-gnu/bits/mman-shared.h \ + /usr/include/fcntl.h \ + /usr/include/x86_64-linux-gnu/bits/fcntl.h \ + /usr/include/x86_64-linux-gnu/bits/fcntl-linux.h \ + /usr/include/x86_64-linux-gnu/bits/types/struct_iovec.h \ + /usr/include/linux/falloc.h \ + /usr/include/x86_64-linux-gnu/bits/stat.h \ + /usr/include/byteswap.h \ + /usr/include/time.h \ + /usr/include/x86_64-linux-gnu/bits/time.h \ + /usr/include/x86_64-linux-gnu/bits/timex.h \ + /usr/include/x86_64-linux-gnu/bits/types/struct_tm.h \ + /usr/include/x86_64-linux-gnu/bits/types/struct_itimerspec.h \ + tools/../lib/crc32.c \ + $(wildcard include/config/hw/watchdog.h) \ + $(wildcard include/config/watchdog.h) \ + $(wildcard include/config/dynamic/crc/table.h) \ + /usr/include/arpa/inet.h \ + /usr/include/netinet/in.h \ + /usr/include/x86_64-linux-gnu/sys/socket.h \ + /usr/include/x86_64-linux-gnu/bits/socket.h \ + /usr/include/x86_64-linux-gnu/bits/socket_type.h \ + /usr/include/x86_64-linux-gnu/bits/sockaddr.h \ + /usr/include/x86_64-linux-gnu/asm/socket.h \ + /usr/include/asm-generic/socket.h \ + /usr/include/x86_64-linux-gnu/asm/sockios.h \ + /usr/include/asm-generic/sockios.h \ + /usr/include/x86_64-linux-gnu/bits/types/struct_osockaddr.h \ + /usr/include/x86_64-linux-gnu/bits/in.h \ + include/compiler.h \ + include/u-boot/crc.h \ + include/u-boot/zlib.h \ + +tools/lib/crc32.o: $(deps_tools/lib/crc32.o) + +$(deps_tools/lib/crc32.o): diff --git a/tools/u-boot-tools/lib/.crc8.o.cmd b/tools/u-boot-tools/lib/.crc8.o.cmd new file mode 100644 index 0000000..8f1cfea --- /dev/null +++ b/tools/u-boot-tools/lib/.crc8.o.cmd @@ -0,0 +1,93 @@ +cmd_tools/lib/crc8.o := cc -Wp,-MD,tools/lib/.crc8.o.d -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -std=gnu11 -include ./include/compiler.h -idirafterinclude -idirafter./arch/arm/include -I./scripts/dtc/libfdt -I./tools -DUSE_HOSTCC -D__KERNEL_STRICT_NAMES -D_GNU_SOURCE -pedantic -c -o tools/lib/crc8.o tools/lib/crc8.c + +source_tools/lib/crc8.o := tools/lib/crc8.c + +deps_tools/lib/crc8.o := \ + /usr/include/stdc-predef.h \ + include/compiler.h \ + /usr/lib/gcc/x86_64-linux-gnu/8/include/stddef.h \ + /usr/lib/gcc/x86_64-linux-gnu/8/include/stdint.h \ + /usr/include/stdint.h \ + /usr/include/x86_64-linux-gnu/bits/libc-header-start.h \ + /usr/include/features.h \ + /usr/include/x86_64-linux-gnu/sys/cdefs.h \ + /usr/include/x86_64-linux-gnu/bits/wordsize.h \ + /usr/include/x86_64-linux-gnu/bits/long-double.h \ + /usr/include/x86_64-linux-gnu/gnu/stubs.h \ + /usr/include/x86_64-linux-gnu/gnu/stubs-64.h \ + /usr/include/x86_64-linux-gnu/bits/types.h \ + /usr/include/x86_64-linux-gnu/bits/typesizes.h \ + /usr/include/x86_64-linux-gnu/bits/wchar.h \ + /usr/include/x86_64-linux-gnu/bits/stdint-intn.h \ + /usr/include/x86_64-linux-gnu/bits/stdint-uintn.h \ + /usr/include/errno.h \ + /usr/include/x86_64-linux-gnu/bits/errno.h \ + /usr/include/linux/errno.h \ + /usr/include/x86_64-linux-gnu/asm/errno.h \ + /usr/include/asm-generic/errno.h \ + /usr/include/asm-generic/errno-base.h \ + /usr/include/x86_64-linux-gnu/bits/types/error_t.h \ + /usr/include/stdlib.h \ + /usr/include/x86_64-linux-gnu/bits/waitflags.h \ + /usr/include/x86_64-linux-gnu/bits/waitstatus.h \ + /usr/include/x86_64-linux-gnu/bits/floatn.h \ + /usr/include/x86_64-linux-gnu/bits/floatn-common.h \ + /usr/include/x86_64-linux-gnu/bits/types/locale_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/__locale_t.h \ + /usr/include/x86_64-linux-gnu/sys/types.h \ + /usr/include/x86_64-linux-gnu/bits/types/clock_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/clockid_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/time_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/timer_t.h \ + /usr/include/endian.h \ + /usr/include/x86_64-linux-gnu/bits/endian.h \ + /usr/include/x86_64-linux-gnu/bits/byteswap.h \ + /usr/include/x86_64-linux-gnu/bits/uintn-identity.h \ + /usr/include/x86_64-linux-gnu/sys/select.h \ + /usr/include/x86_64-linux-gnu/bits/select.h \ + /usr/include/x86_64-linux-gnu/bits/types/sigset_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/__sigset_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/struct_timeval.h \ + /usr/include/x86_64-linux-gnu/bits/types/struct_timespec.h \ + /usr/include/x86_64-linux-gnu/bits/pthreadtypes.h \ + /usr/include/x86_64-linux-gnu/bits/thread-shared-types.h \ + /usr/include/x86_64-linux-gnu/bits/pthreadtypes-arch.h \ + /usr/include/alloca.h \ + /usr/include/x86_64-linux-gnu/bits/stdlib-bsearch.h \ + /usr/include/x86_64-linux-gnu/bits/stdlib-float.h \ + /usr/include/stdio.h \ + /usr/lib/gcc/x86_64-linux-gnu/8/include/stdarg.h \ + /usr/include/x86_64-linux-gnu/bits/types/__fpos_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/__mbstate_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/__fpos64_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/__FILE.h \ + /usr/include/x86_64-linux-gnu/bits/types/FILE.h \ + /usr/include/x86_64-linux-gnu/bits/types/struct_FILE.h \ + /usr/include/x86_64-linux-gnu/bits/types/cookie_io_functions_t.h \ + /usr/include/x86_64-linux-gnu/bits/stdio_lim.h \ + /usr/include/x86_64-linux-gnu/bits/sys_errlist.h \ + /usr/include/x86_64-linux-gnu/bits/stdio.h \ + /usr/include/string.h \ + /usr/include/strings.h \ + /usr/include/x86_64-linux-gnu/sys/mman.h \ + /usr/include/x86_64-linux-gnu/bits/mman.h \ + /usr/include/x86_64-linux-gnu/bits/mman-linux.h \ + /usr/include/x86_64-linux-gnu/bits/mman-shared.h \ + /usr/include/fcntl.h \ + /usr/include/x86_64-linux-gnu/bits/fcntl.h \ + /usr/include/x86_64-linux-gnu/bits/fcntl-linux.h \ + /usr/include/x86_64-linux-gnu/bits/types/struct_iovec.h \ + /usr/include/linux/falloc.h \ + /usr/include/x86_64-linux-gnu/bits/stat.h \ + /usr/include/byteswap.h \ + /usr/include/time.h \ + /usr/include/x86_64-linux-gnu/bits/time.h \ + /usr/include/x86_64-linux-gnu/bits/timex.h \ + /usr/include/x86_64-linux-gnu/bits/types/struct_tm.h \ + /usr/include/x86_64-linux-gnu/bits/types/struct_itimerspec.h \ + tools/../lib/crc8.c \ + include/linux/crc8.h \ + +tools/lib/crc8.o: $(deps_tools/lib/crc8.o) + +$(deps_tools/lib/crc8.o): diff --git a/tools/u-boot-tools/lib/.fdtdec.o.cmd b/tools/u-boot-tools/lib/.fdtdec.o.cmd new file mode 100644 index 0000000..e9bffc3 --- /dev/null +++ b/tools/u-boot-tools/lib/.fdtdec.o.cmd @@ -0,0 +1,113 @@ +cmd_tools/lib/fdtdec.o := cc -Wp,-MD,tools/lib/.fdtdec.o.d -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -std=gnu11 -include ./include/compiler.h -idirafterinclude -idirafter./arch/arm/include -I./scripts/dtc/libfdt -I./tools -DUSE_HOSTCC -D__KERNEL_STRICT_NAMES -D_GNU_SOURCE -c -o tools/lib/fdtdec.o tools/lib/fdtdec.c + +source_tools/lib/fdtdec.o := tools/lib/fdtdec.c + +deps_tools/lib/fdtdec.o := \ + /usr/include/stdc-predef.h \ + include/compiler.h \ + /usr/lib/gcc/x86_64-linux-gnu/8/include/stddef.h \ + /usr/lib/gcc/x86_64-linux-gnu/8/include/stdint.h \ + /usr/include/stdint.h \ + /usr/include/x86_64-linux-gnu/bits/libc-header-start.h \ + /usr/include/features.h \ + /usr/include/x86_64-linux-gnu/sys/cdefs.h \ + /usr/include/x86_64-linux-gnu/bits/wordsize.h \ + /usr/include/x86_64-linux-gnu/bits/long-double.h \ + /usr/include/x86_64-linux-gnu/gnu/stubs.h \ + /usr/include/x86_64-linux-gnu/gnu/stubs-64.h \ + /usr/include/x86_64-linux-gnu/bits/types.h \ + /usr/include/x86_64-linux-gnu/bits/typesizes.h \ + /usr/include/x86_64-linux-gnu/bits/wchar.h \ + /usr/include/x86_64-linux-gnu/bits/stdint-intn.h \ + /usr/include/x86_64-linux-gnu/bits/stdint-uintn.h \ + /usr/include/errno.h \ + /usr/include/x86_64-linux-gnu/bits/errno.h \ + /usr/include/linux/errno.h \ + /usr/include/x86_64-linux-gnu/asm/errno.h \ + /usr/include/asm-generic/errno.h \ + /usr/include/asm-generic/errno-base.h \ + /usr/include/x86_64-linux-gnu/bits/types/error_t.h \ + /usr/include/stdlib.h \ + /usr/include/x86_64-linux-gnu/bits/waitflags.h \ + /usr/include/x86_64-linux-gnu/bits/waitstatus.h \ + /usr/include/x86_64-linux-gnu/bits/floatn.h \ + /usr/include/x86_64-linux-gnu/bits/floatn-common.h \ + /usr/include/x86_64-linux-gnu/bits/types/locale_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/__locale_t.h \ + /usr/include/x86_64-linux-gnu/sys/types.h \ + /usr/include/x86_64-linux-gnu/bits/types/clock_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/clockid_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/time_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/timer_t.h \ + /usr/include/endian.h \ + /usr/include/x86_64-linux-gnu/bits/endian.h \ + /usr/include/x86_64-linux-gnu/bits/byteswap.h \ + /usr/include/x86_64-linux-gnu/bits/uintn-identity.h \ + /usr/include/x86_64-linux-gnu/sys/select.h \ + /usr/include/x86_64-linux-gnu/bits/select.h \ + /usr/include/x86_64-linux-gnu/bits/types/sigset_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/__sigset_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/struct_timeval.h \ + /usr/include/x86_64-linux-gnu/bits/types/struct_timespec.h \ + /usr/include/x86_64-linux-gnu/bits/pthreadtypes.h \ + /usr/include/x86_64-linux-gnu/bits/thread-shared-types.h \ + /usr/include/x86_64-linux-gnu/bits/pthreadtypes-arch.h \ + /usr/include/alloca.h \ + /usr/include/x86_64-linux-gnu/bits/stdlib-bsearch.h \ + /usr/include/x86_64-linux-gnu/bits/stdlib-float.h \ + /usr/include/stdio.h \ + /usr/lib/gcc/x86_64-linux-gnu/8/include/stdarg.h \ + /usr/include/x86_64-linux-gnu/bits/types/__fpos_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/__mbstate_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/__fpos64_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/__FILE.h \ + /usr/include/x86_64-linux-gnu/bits/types/FILE.h \ + /usr/include/x86_64-linux-gnu/bits/types/struct_FILE.h \ + /usr/include/x86_64-linux-gnu/bits/types/cookie_io_functions_t.h \ + /usr/include/x86_64-linux-gnu/bits/stdio_lim.h \ + /usr/include/x86_64-linux-gnu/bits/sys_errlist.h \ + /usr/include/x86_64-linux-gnu/bits/stdio.h \ + /usr/include/string.h \ + /usr/include/strings.h \ + /usr/include/x86_64-linux-gnu/sys/mman.h \ + /usr/include/x86_64-linux-gnu/bits/mman.h \ + /usr/include/x86_64-linux-gnu/bits/mman-linux.h \ + /usr/include/x86_64-linux-gnu/bits/mman-shared.h \ + /usr/include/fcntl.h \ + /usr/include/x86_64-linux-gnu/bits/fcntl.h \ + /usr/include/x86_64-linux-gnu/bits/fcntl-linux.h \ + /usr/include/x86_64-linux-gnu/bits/types/struct_iovec.h \ + /usr/include/linux/falloc.h \ + /usr/include/x86_64-linux-gnu/bits/stat.h \ + /usr/include/byteswap.h \ + /usr/include/time.h \ + /usr/include/x86_64-linux-gnu/bits/time.h \ + /usr/include/x86_64-linux-gnu/bits/timex.h \ + /usr/include/x86_64-linux-gnu/bits/types/struct_tm.h \ + /usr/include/x86_64-linux-gnu/bits/types/struct_itimerspec.h \ + tools/../lib/fdtdec.c \ + $(wildcard include/config/of/translate.h) \ + $(wildcard include/config/pci.h) \ + $(wildcard include/config/dm/pci.h) \ + $(wildcard include/config/spl/build.h) \ + $(wildcard include/config/of/embed.h) \ + $(wildcard include/config/nr/dram/banks.h) \ + $(wildcard include/config/multi/dtb/fit.h) \ + $(wildcard include/config/multi/dtb/fit/gzip.h) \ + $(wildcard include/config/multi/dtb/fit/lzo.h) \ + $(wildcard include/config/spl/multi/dtb/fit/uncompress/sz.h) \ + $(wildcard include/config/gzip.h) \ + $(wildcard include/config/lzo.h) \ + $(wildcard include/config/multi/dtb/fit/dyn/alloc.h) \ + $(wildcard include/config/multi/dtb/fit/user/defined/area.h) \ + $(wildcard include/config/multi/dtb/fit/user/def/addr.h) \ + $(wildcard include/config/of/board.h) \ + $(wildcard include/config/of/separate.h) \ + $(wildcard include/config/spl/separate/bss.h) \ + $(wildcard include/config/of/control.h) \ + $(wildcard include/config/of/hostfile.h) \ + $(wildcard include/config/of/prior/stage.h) \ + +tools/lib/fdtdec.o: $(deps_tools/lib/fdtdec.o) + +$(deps_tools/lib/fdtdec.o): diff --git a/tools/u-boot-tools/lib/.fdtdec_common.o.cmd b/tools/u-boot-tools/lib/.fdtdec_common.o.cmd new file mode 100644 index 0000000..77ba37b --- /dev/null +++ b/tools/u-boot-tools/lib/.fdtdec_common.o.cmd @@ -0,0 +1,108 @@ +cmd_tools/lib/fdtdec_common.o := cc -Wp,-MD,tools/lib/.fdtdec_common.o.d -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -std=gnu11 -include ./include/compiler.h -idirafterinclude -idirafter./arch/arm/include -I./scripts/dtc/libfdt -I./tools -DUSE_HOSTCC -D__KERNEL_STRICT_NAMES -D_GNU_SOURCE -c -o tools/lib/fdtdec_common.o tools/lib/fdtdec_common.c + +source_tools/lib/fdtdec_common.o := tools/lib/fdtdec_common.c + +deps_tools/lib/fdtdec_common.o := \ + /usr/include/stdc-predef.h \ + include/compiler.h \ + /usr/lib/gcc/x86_64-linux-gnu/8/include/stddef.h \ + /usr/lib/gcc/x86_64-linux-gnu/8/include/stdint.h \ + /usr/include/stdint.h \ + /usr/include/x86_64-linux-gnu/bits/libc-header-start.h \ + /usr/include/features.h \ + /usr/include/x86_64-linux-gnu/sys/cdefs.h \ + /usr/include/x86_64-linux-gnu/bits/wordsize.h \ + /usr/include/x86_64-linux-gnu/bits/long-double.h \ + /usr/include/x86_64-linux-gnu/gnu/stubs.h \ + /usr/include/x86_64-linux-gnu/gnu/stubs-64.h \ + /usr/include/x86_64-linux-gnu/bits/types.h \ + /usr/include/x86_64-linux-gnu/bits/typesizes.h \ + /usr/include/x86_64-linux-gnu/bits/wchar.h \ + /usr/include/x86_64-linux-gnu/bits/stdint-intn.h \ + /usr/include/x86_64-linux-gnu/bits/stdint-uintn.h \ + /usr/include/errno.h \ + /usr/include/x86_64-linux-gnu/bits/errno.h \ + /usr/include/linux/errno.h \ + /usr/include/x86_64-linux-gnu/asm/errno.h \ + /usr/include/asm-generic/errno.h \ + /usr/include/asm-generic/errno-base.h \ + /usr/include/x86_64-linux-gnu/bits/types/error_t.h \ + /usr/include/stdlib.h \ + /usr/include/x86_64-linux-gnu/bits/waitflags.h \ + /usr/include/x86_64-linux-gnu/bits/waitstatus.h \ + /usr/include/x86_64-linux-gnu/bits/floatn.h \ + /usr/include/x86_64-linux-gnu/bits/floatn-common.h \ + /usr/include/x86_64-linux-gnu/bits/types/locale_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/__locale_t.h \ + /usr/include/x86_64-linux-gnu/sys/types.h \ + /usr/include/x86_64-linux-gnu/bits/types/clock_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/clockid_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/time_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/timer_t.h \ + /usr/include/endian.h \ + /usr/include/x86_64-linux-gnu/bits/endian.h \ + /usr/include/x86_64-linux-gnu/bits/byteswap.h \ + /usr/include/x86_64-linux-gnu/bits/uintn-identity.h \ + /usr/include/x86_64-linux-gnu/sys/select.h \ + /usr/include/x86_64-linux-gnu/bits/select.h \ + /usr/include/x86_64-linux-gnu/bits/types/sigset_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/__sigset_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/struct_timeval.h \ + /usr/include/x86_64-linux-gnu/bits/types/struct_timespec.h \ + /usr/include/x86_64-linux-gnu/bits/pthreadtypes.h \ + /usr/include/x86_64-linux-gnu/bits/thread-shared-types.h \ + /usr/include/x86_64-linux-gnu/bits/pthreadtypes-arch.h \ + /usr/include/alloca.h \ + /usr/include/x86_64-linux-gnu/bits/stdlib-bsearch.h \ + /usr/include/x86_64-linux-gnu/bits/stdlib-float.h \ + /usr/include/stdio.h \ + /usr/lib/gcc/x86_64-linux-gnu/8/include/stdarg.h \ + /usr/include/x86_64-linux-gnu/bits/types/__fpos_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/__mbstate_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/__fpos64_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/__FILE.h \ + /usr/include/x86_64-linux-gnu/bits/types/FILE.h \ + /usr/include/x86_64-linux-gnu/bits/types/struct_FILE.h \ + /usr/include/x86_64-linux-gnu/bits/types/cookie_io_functions_t.h \ + /usr/include/x86_64-linux-gnu/bits/stdio_lim.h \ + /usr/include/x86_64-linux-gnu/bits/sys_errlist.h \ + /usr/include/x86_64-linux-gnu/bits/stdio.h \ + /usr/include/string.h \ + /usr/include/strings.h \ + /usr/include/x86_64-linux-gnu/sys/mman.h \ + /usr/include/x86_64-linux-gnu/bits/mman.h \ + /usr/include/x86_64-linux-gnu/bits/mman-linux.h \ + /usr/include/x86_64-linux-gnu/bits/mman-shared.h \ + /usr/include/fcntl.h \ + /usr/include/x86_64-linux-gnu/bits/fcntl.h \ + /usr/include/x86_64-linux-gnu/bits/fcntl-linux.h \ + /usr/include/x86_64-linux-gnu/bits/types/struct_iovec.h \ + /usr/include/linux/falloc.h \ + /usr/include/x86_64-linux-gnu/bits/stat.h \ + /usr/include/byteswap.h \ + /usr/include/time.h \ + /usr/include/x86_64-linux-gnu/bits/time.h \ + /usr/include/x86_64-linux-gnu/bits/timex.h \ + /usr/include/x86_64-linux-gnu/bits/types/struct_tm.h \ + /usr/include/x86_64-linux-gnu/bits/types/struct_itimerspec.h \ + tools/../lib/fdtdec_common.c \ + scripts/dtc/libfdt/libfdt.h \ + scripts/dtc/libfdt/libfdt_env.h \ + scripts/dtc/libfdt/fdt.h \ + include/fdt_support.h \ + $(wildcard include/config/of/libfdt.h) \ + $(wildcard include/config/arch/fixup/fdt/memory.h) \ + $(wildcard include/config/usb/ehci/fsl.h) \ + $(wildcard include/config/usb/xhci/fsl.h) \ + $(wildcard include/config/sys/fsl/sec/compat.h) \ + $(wildcard include/config/pci.h) \ + $(wildcard include/config/sys/fdt/pad.h) \ + $(wildcard include/config/of/board/setup.h) \ + $(wildcard include/config/of/system/setup.h) \ + $(wildcard include/config/fdt/fixup/partitions.h) \ + $(wildcard include/config/fman/enet.h) \ + $(wildcard include/config/fsl/mc/enet.h) \ + +tools/lib/fdtdec_common.o: $(deps_tools/lib/fdtdec_common.o) + +$(deps_tools/lib/fdtdec_common.o): diff --git a/tools/u-boot-tools/lib/.md5.o.cmd b/tools/u-boot-tools/lib/.md5.o.cmd new file mode 100644 index 0000000..5bcde5e --- /dev/null +++ b/tools/u-boot-tools/lib/.md5.o.cmd @@ -0,0 +1,96 @@ +cmd_tools/lib/md5.o := cc -Wp,-MD,tools/lib/.md5.o.d -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -std=gnu11 -include ./include/compiler.h -idirafterinclude -idirafter./arch/arm/include -I./scripts/dtc/libfdt -I./tools -DUSE_HOSTCC -D__KERNEL_STRICT_NAMES -D_GNU_SOURCE -pedantic -c -o tools/lib/md5.o tools/lib/md5.c + +source_tools/lib/md5.o := tools/lib/md5.c + +deps_tools/lib/md5.o := \ + /usr/include/stdc-predef.h \ + include/compiler.h \ + /usr/lib/gcc/x86_64-linux-gnu/8/include/stddef.h \ + /usr/lib/gcc/x86_64-linux-gnu/8/include/stdint.h \ + /usr/include/stdint.h \ + /usr/include/x86_64-linux-gnu/bits/libc-header-start.h \ + /usr/include/features.h \ + /usr/include/x86_64-linux-gnu/sys/cdefs.h \ + /usr/include/x86_64-linux-gnu/bits/wordsize.h \ + /usr/include/x86_64-linux-gnu/bits/long-double.h \ + /usr/include/x86_64-linux-gnu/gnu/stubs.h \ + /usr/include/x86_64-linux-gnu/gnu/stubs-64.h \ + /usr/include/x86_64-linux-gnu/bits/types.h \ + /usr/include/x86_64-linux-gnu/bits/typesizes.h \ + /usr/include/x86_64-linux-gnu/bits/wchar.h \ + /usr/include/x86_64-linux-gnu/bits/stdint-intn.h \ + /usr/include/x86_64-linux-gnu/bits/stdint-uintn.h \ + /usr/include/errno.h \ + /usr/include/x86_64-linux-gnu/bits/errno.h \ + /usr/include/linux/errno.h \ + /usr/include/x86_64-linux-gnu/asm/errno.h \ + /usr/include/asm-generic/errno.h \ + /usr/include/asm-generic/errno-base.h \ + /usr/include/x86_64-linux-gnu/bits/types/error_t.h \ + /usr/include/stdlib.h \ + /usr/include/x86_64-linux-gnu/bits/waitflags.h \ + /usr/include/x86_64-linux-gnu/bits/waitstatus.h \ + /usr/include/x86_64-linux-gnu/bits/floatn.h \ + /usr/include/x86_64-linux-gnu/bits/floatn-common.h \ + /usr/include/x86_64-linux-gnu/bits/types/locale_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/__locale_t.h \ + /usr/include/x86_64-linux-gnu/sys/types.h \ + /usr/include/x86_64-linux-gnu/bits/types/clock_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/clockid_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/time_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/timer_t.h \ + /usr/include/endian.h \ + /usr/include/x86_64-linux-gnu/bits/endian.h \ + /usr/include/x86_64-linux-gnu/bits/byteswap.h \ + /usr/include/x86_64-linux-gnu/bits/uintn-identity.h \ + /usr/include/x86_64-linux-gnu/sys/select.h \ + /usr/include/x86_64-linux-gnu/bits/select.h \ + /usr/include/x86_64-linux-gnu/bits/types/sigset_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/__sigset_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/struct_timeval.h \ + /usr/include/x86_64-linux-gnu/bits/types/struct_timespec.h \ + /usr/include/x86_64-linux-gnu/bits/pthreadtypes.h \ + /usr/include/x86_64-linux-gnu/bits/thread-shared-types.h \ + /usr/include/x86_64-linux-gnu/bits/pthreadtypes-arch.h \ + /usr/include/alloca.h \ + /usr/include/x86_64-linux-gnu/bits/stdlib-bsearch.h \ + /usr/include/x86_64-linux-gnu/bits/stdlib-float.h \ + /usr/include/stdio.h \ + /usr/lib/gcc/x86_64-linux-gnu/8/include/stdarg.h \ + /usr/include/x86_64-linux-gnu/bits/types/__fpos_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/__mbstate_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/__fpos64_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/__FILE.h \ + /usr/include/x86_64-linux-gnu/bits/types/FILE.h \ + /usr/include/x86_64-linux-gnu/bits/types/struct_FILE.h \ + /usr/include/x86_64-linux-gnu/bits/types/cookie_io_functions_t.h \ + /usr/include/x86_64-linux-gnu/bits/stdio_lim.h \ + /usr/include/x86_64-linux-gnu/bits/sys_errlist.h \ + /usr/include/x86_64-linux-gnu/bits/stdio.h \ + /usr/include/string.h \ + /usr/include/strings.h \ + /usr/include/x86_64-linux-gnu/sys/mman.h \ + /usr/include/x86_64-linux-gnu/bits/mman.h \ + /usr/include/x86_64-linux-gnu/bits/mman-linux.h \ + /usr/include/x86_64-linux-gnu/bits/mman-shared.h \ + /usr/include/fcntl.h \ + /usr/include/x86_64-linux-gnu/bits/fcntl.h \ + /usr/include/x86_64-linux-gnu/bits/fcntl-linux.h \ + /usr/include/x86_64-linux-gnu/bits/types/struct_iovec.h \ + /usr/include/linux/falloc.h \ + /usr/include/x86_64-linux-gnu/bits/stat.h \ + /usr/include/byteswap.h \ + /usr/include/time.h \ + /usr/include/x86_64-linux-gnu/bits/time.h \ + /usr/include/x86_64-linux-gnu/bits/timex.h \ + /usr/include/x86_64-linux-gnu/bits/types/struct_tm.h \ + /usr/include/x86_64-linux-gnu/bits/types/struct_itimerspec.h \ + tools/../lib/md5.c \ + $(wildcard include/config/hw/watchdog.h) \ + $(wildcard include/config/watchdog.h) \ + include/compiler.h \ + include/u-boot/md5.h \ + +tools/lib/md5.o: $(deps_tools/lib/md5.o) + +$(deps_tools/lib/md5.o): diff --git a/tools/u-boot-tools/lib/.rc4.o.cmd b/tools/u-boot-tools/lib/.rc4.o.cmd new file mode 100644 index 0000000..09cf31a --- /dev/null +++ b/tools/u-boot-tools/lib/.rc4.o.cmd @@ -0,0 +1,93 @@ +cmd_tools/lib/rc4.o := cc -Wp,-MD,tools/lib/.rc4.o.d -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -std=gnu11 -include ./include/compiler.h -idirafterinclude -idirafter./arch/arm/include -I./scripts/dtc/libfdt -I./tools -DUSE_HOSTCC -D__KERNEL_STRICT_NAMES -D_GNU_SOURCE -c -o tools/lib/rc4.o tools/lib/rc4.c + +source_tools/lib/rc4.o := tools/lib/rc4.c + +deps_tools/lib/rc4.o := \ + /usr/include/stdc-predef.h \ + include/compiler.h \ + /usr/lib/gcc/x86_64-linux-gnu/8/include/stddef.h \ + /usr/lib/gcc/x86_64-linux-gnu/8/include/stdint.h \ + /usr/include/stdint.h \ + /usr/include/x86_64-linux-gnu/bits/libc-header-start.h \ + /usr/include/features.h \ + /usr/include/x86_64-linux-gnu/sys/cdefs.h \ + /usr/include/x86_64-linux-gnu/bits/wordsize.h \ + /usr/include/x86_64-linux-gnu/bits/long-double.h \ + /usr/include/x86_64-linux-gnu/gnu/stubs.h \ + /usr/include/x86_64-linux-gnu/gnu/stubs-64.h \ + /usr/include/x86_64-linux-gnu/bits/types.h \ + /usr/include/x86_64-linux-gnu/bits/typesizes.h \ + /usr/include/x86_64-linux-gnu/bits/wchar.h \ + /usr/include/x86_64-linux-gnu/bits/stdint-intn.h \ + /usr/include/x86_64-linux-gnu/bits/stdint-uintn.h \ + /usr/include/errno.h \ + /usr/include/x86_64-linux-gnu/bits/errno.h \ + /usr/include/linux/errno.h \ + /usr/include/x86_64-linux-gnu/asm/errno.h \ + /usr/include/asm-generic/errno.h \ + /usr/include/asm-generic/errno-base.h \ + /usr/include/x86_64-linux-gnu/bits/types/error_t.h \ + /usr/include/stdlib.h \ + /usr/include/x86_64-linux-gnu/bits/waitflags.h \ + /usr/include/x86_64-linux-gnu/bits/waitstatus.h \ + /usr/include/x86_64-linux-gnu/bits/floatn.h \ + /usr/include/x86_64-linux-gnu/bits/floatn-common.h \ + /usr/include/x86_64-linux-gnu/bits/types/locale_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/__locale_t.h \ + /usr/include/x86_64-linux-gnu/sys/types.h \ + /usr/include/x86_64-linux-gnu/bits/types/clock_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/clockid_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/time_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/timer_t.h \ + /usr/include/endian.h \ + /usr/include/x86_64-linux-gnu/bits/endian.h \ + /usr/include/x86_64-linux-gnu/bits/byteswap.h \ + /usr/include/x86_64-linux-gnu/bits/uintn-identity.h \ + /usr/include/x86_64-linux-gnu/sys/select.h \ + /usr/include/x86_64-linux-gnu/bits/select.h \ + /usr/include/x86_64-linux-gnu/bits/types/sigset_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/__sigset_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/struct_timeval.h \ + /usr/include/x86_64-linux-gnu/bits/types/struct_timespec.h \ + /usr/include/x86_64-linux-gnu/bits/pthreadtypes.h \ + /usr/include/x86_64-linux-gnu/bits/thread-shared-types.h \ + /usr/include/x86_64-linux-gnu/bits/pthreadtypes-arch.h \ + /usr/include/alloca.h \ + /usr/include/x86_64-linux-gnu/bits/stdlib-bsearch.h \ + /usr/include/x86_64-linux-gnu/bits/stdlib-float.h \ + /usr/include/stdio.h \ + /usr/lib/gcc/x86_64-linux-gnu/8/include/stdarg.h \ + /usr/include/x86_64-linux-gnu/bits/types/__fpos_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/__mbstate_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/__fpos64_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/__FILE.h \ + /usr/include/x86_64-linux-gnu/bits/types/FILE.h \ + /usr/include/x86_64-linux-gnu/bits/types/struct_FILE.h \ + /usr/include/x86_64-linux-gnu/bits/types/cookie_io_functions_t.h \ + /usr/include/x86_64-linux-gnu/bits/stdio_lim.h \ + /usr/include/x86_64-linux-gnu/bits/sys_errlist.h \ + /usr/include/x86_64-linux-gnu/bits/stdio.h \ + /usr/include/string.h \ + /usr/include/strings.h \ + /usr/include/x86_64-linux-gnu/sys/mman.h \ + /usr/include/x86_64-linux-gnu/bits/mman.h \ + /usr/include/x86_64-linux-gnu/bits/mman-linux.h \ + /usr/include/x86_64-linux-gnu/bits/mman-shared.h \ + /usr/include/fcntl.h \ + /usr/include/x86_64-linux-gnu/bits/fcntl.h \ + /usr/include/x86_64-linux-gnu/bits/fcntl-linux.h \ + /usr/include/x86_64-linux-gnu/bits/types/struct_iovec.h \ + /usr/include/linux/falloc.h \ + /usr/include/x86_64-linux-gnu/bits/stat.h \ + /usr/include/byteswap.h \ + /usr/include/time.h \ + /usr/include/x86_64-linux-gnu/bits/time.h \ + /usr/include/x86_64-linux-gnu/bits/timex.h \ + /usr/include/x86_64-linux-gnu/bits/types/struct_tm.h \ + /usr/include/x86_64-linux-gnu/bits/types/struct_itimerspec.h \ + tools/../lib/rc4.c \ + include/rc4.h \ + +tools/lib/rc4.o: $(deps_tools/lib/rc4.o) + +$(deps_tools/lib/rc4.o): diff --git a/tools/u-boot-tools/lib/.sha1.o.cmd b/tools/u-boot-tools/lib/.sha1.o.cmd new file mode 100644 index 0000000..137f340 --- /dev/null +++ b/tools/u-boot-tools/lib/.sha1.o.cmd @@ -0,0 +1,97 @@ +cmd_tools/lib/sha1.o := cc -Wp,-MD,tools/lib/.sha1.o.d -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -std=gnu11 -include ./include/compiler.h -idirafterinclude -idirafter./arch/arm/include -I./scripts/dtc/libfdt -I./tools -DUSE_HOSTCC -D__KERNEL_STRICT_NAMES -D_GNU_SOURCE -pedantic -c -o tools/lib/sha1.o tools/lib/sha1.c + +source_tools/lib/sha1.o := tools/lib/sha1.c + +deps_tools/lib/sha1.o := \ + /usr/include/stdc-predef.h \ + include/compiler.h \ + /usr/lib/gcc/x86_64-linux-gnu/8/include/stddef.h \ + /usr/lib/gcc/x86_64-linux-gnu/8/include/stdint.h \ + /usr/include/stdint.h \ + /usr/include/x86_64-linux-gnu/bits/libc-header-start.h \ + /usr/include/features.h \ + /usr/include/x86_64-linux-gnu/sys/cdefs.h \ + /usr/include/x86_64-linux-gnu/bits/wordsize.h \ + /usr/include/x86_64-linux-gnu/bits/long-double.h \ + /usr/include/x86_64-linux-gnu/gnu/stubs.h \ + /usr/include/x86_64-linux-gnu/gnu/stubs-64.h \ + /usr/include/x86_64-linux-gnu/bits/types.h \ + /usr/include/x86_64-linux-gnu/bits/typesizes.h \ + /usr/include/x86_64-linux-gnu/bits/wchar.h \ + /usr/include/x86_64-linux-gnu/bits/stdint-intn.h \ + /usr/include/x86_64-linux-gnu/bits/stdint-uintn.h \ + /usr/include/errno.h \ + /usr/include/x86_64-linux-gnu/bits/errno.h \ + /usr/include/linux/errno.h \ + /usr/include/x86_64-linux-gnu/asm/errno.h \ + /usr/include/asm-generic/errno.h \ + /usr/include/asm-generic/errno-base.h \ + /usr/include/x86_64-linux-gnu/bits/types/error_t.h \ + /usr/include/stdlib.h \ + /usr/include/x86_64-linux-gnu/bits/waitflags.h \ + /usr/include/x86_64-linux-gnu/bits/waitstatus.h \ + /usr/include/x86_64-linux-gnu/bits/floatn.h \ + /usr/include/x86_64-linux-gnu/bits/floatn-common.h \ + /usr/include/x86_64-linux-gnu/bits/types/locale_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/__locale_t.h \ + /usr/include/x86_64-linux-gnu/sys/types.h \ + /usr/include/x86_64-linux-gnu/bits/types/clock_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/clockid_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/time_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/timer_t.h \ + /usr/include/endian.h \ + /usr/include/x86_64-linux-gnu/bits/endian.h \ + /usr/include/x86_64-linux-gnu/bits/byteswap.h \ + /usr/include/x86_64-linux-gnu/bits/uintn-identity.h \ + /usr/include/x86_64-linux-gnu/sys/select.h \ + /usr/include/x86_64-linux-gnu/bits/select.h \ + /usr/include/x86_64-linux-gnu/bits/types/sigset_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/__sigset_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/struct_timeval.h \ + /usr/include/x86_64-linux-gnu/bits/types/struct_timespec.h \ + /usr/include/x86_64-linux-gnu/bits/pthreadtypes.h \ + /usr/include/x86_64-linux-gnu/bits/thread-shared-types.h \ + /usr/include/x86_64-linux-gnu/bits/pthreadtypes-arch.h \ + /usr/include/alloca.h \ + /usr/include/x86_64-linux-gnu/bits/stdlib-bsearch.h \ + /usr/include/x86_64-linux-gnu/bits/stdlib-float.h \ + /usr/include/stdio.h \ + /usr/lib/gcc/x86_64-linux-gnu/8/include/stdarg.h \ + /usr/include/x86_64-linux-gnu/bits/types/__fpos_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/__mbstate_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/__fpos64_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/__FILE.h \ + /usr/include/x86_64-linux-gnu/bits/types/FILE.h \ + /usr/include/x86_64-linux-gnu/bits/types/struct_FILE.h \ + /usr/include/x86_64-linux-gnu/bits/types/cookie_io_functions_t.h \ + /usr/include/x86_64-linux-gnu/bits/stdio_lim.h \ + /usr/include/x86_64-linux-gnu/bits/sys_errlist.h \ + /usr/include/x86_64-linux-gnu/bits/stdio.h \ + /usr/include/string.h \ + /usr/include/strings.h \ + /usr/include/x86_64-linux-gnu/sys/mman.h \ + /usr/include/x86_64-linux-gnu/bits/mman.h \ + /usr/include/x86_64-linux-gnu/bits/mman-linux.h \ + /usr/include/x86_64-linux-gnu/bits/mman-shared.h \ + /usr/include/fcntl.h \ + /usr/include/x86_64-linux-gnu/bits/fcntl.h \ + /usr/include/x86_64-linux-gnu/bits/fcntl-linux.h \ + /usr/include/x86_64-linux-gnu/bits/types/struct_iovec.h \ + /usr/include/linux/falloc.h \ + /usr/include/x86_64-linux-gnu/bits/stat.h \ + /usr/include/byteswap.h \ + /usr/include/time.h \ + /usr/include/x86_64-linux-gnu/bits/time.h \ + /usr/include/x86_64-linux-gnu/bits/timex.h \ + /usr/include/x86_64-linux-gnu/bits/types/struct_tm.h \ + /usr/include/x86_64-linux-gnu/bits/types/struct_itimerspec.h \ + tools/../lib/sha1.c \ + $(wildcard include/config/hw/watchdog.h) \ + $(wildcard include/config/watchdog.h) \ + include/watchdog.h \ + $(wildcard include/config/mpc85xx.h) \ + include/u-boot/sha1.h \ + +tools/lib/sha1.o: $(deps_tools/lib/sha1.o) + +$(deps_tools/lib/sha1.o): diff --git a/tools/u-boot-tools/lib/.sha256.o.cmd b/tools/u-boot-tools/lib/.sha256.o.cmd new file mode 100644 index 0000000..880821a --- /dev/null +++ b/tools/u-boot-tools/lib/.sha256.o.cmd @@ -0,0 +1,97 @@ +cmd_tools/lib/sha256.o := cc -Wp,-MD,tools/lib/.sha256.o.d -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -std=gnu11 -include ./include/compiler.h -idirafterinclude -idirafter./arch/arm/include -I./scripts/dtc/libfdt -I./tools -DUSE_HOSTCC -D__KERNEL_STRICT_NAMES -D_GNU_SOURCE -pedantic -c -o tools/lib/sha256.o tools/lib/sha256.c + +source_tools/lib/sha256.o := tools/lib/sha256.c + +deps_tools/lib/sha256.o := \ + /usr/include/stdc-predef.h \ + include/compiler.h \ + /usr/lib/gcc/x86_64-linux-gnu/8/include/stddef.h \ + /usr/lib/gcc/x86_64-linux-gnu/8/include/stdint.h \ + /usr/include/stdint.h \ + /usr/include/x86_64-linux-gnu/bits/libc-header-start.h \ + /usr/include/features.h \ + /usr/include/x86_64-linux-gnu/sys/cdefs.h \ + /usr/include/x86_64-linux-gnu/bits/wordsize.h \ + /usr/include/x86_64-linux-gnu/bits/long-double.h \ + /usr/include/x86_64-linux-gnu/gnu/stubs.h \ + /usr/include/x86_64-linux-gnu/gnu/stubs-64.h \ + /usr/include/x86_64-linux-gnu/bits/types.h \ + /usr/include/x86_64-linux-gnu/bits/typesizes.h \ + /usr/include/x86_64-linux-gnu/bits/wchar.h \ + /usr/include/x86_64-linux-gnu/bits/stdint-intn.h \ + /usr/include/x86_64-linux-gnu/bits/stdint-uintn.h \ + /usr/include/errno.h \ + /usr/include/x86_64-linux-gnu/bits/errno.h \ + /usr/include/linux/errno.h \ + /usr/include/x86_64-linux-gnu/asm/errno.h \ + /usr/include/asm-generic/errno.h \ + /usr/include/asm-generic/errno-base.h \ + /usr/include/x86_64-linux-gnu/bits/types/error_t.h \ + /usr/include/stdlib.h \ + /usr/include/x86_64-linux-gnu/bits/waitflags.h \ + /usr/include/x86_64-linux-gnu/bits/waitstatus.h \ + /usr/include/x86_64-linux-gnu/bits/floatn.h \ + /usr/include/x86_64-linux-gnu/bits/floatn-common.h \ + /usr/include/x86_64-linux-gnu/bits/types/locale_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/__locale_t.h \ + /usr/include/x86_64-linux-gnu/sys/types.h \ + /usr/include/x86_64-linux-gnu/bits/types/clock_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/clockid_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/time_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/timer_t.h \ + /usr/include/endian.h \ + /usr/include/x86_64-linux-gnu/bits/endian.h \ + /usr/include/x86_64-linux-gnu/bits/byteswap.h \ + /usr/include/x86_64-linux-gnu/bits/uintn-identity.h \ + /usr/include/x86_64-linux-gnu/sys/select.h \ + /usr/include/x86_64-linux-gnu/bits/select.h \ + /usr/include/x86_64-linux-gnu/bits/types/sigset_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/__sigset_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/struct_timeval.h \ + /usr/include/x86_64-linux-gnu/bits/types/struct_timespec.h \ + /usr/include/x86_64-linux-gnu/bits/pthreadtypes.h \ + /usr/include/x86_64-linux-gnu/bits/thread-shared-types.h \ + /usr/include/x86_64-linux-gnu/bits/pthreadtypes-arch.h \ + /usr/include/alloca.h \ + /usr/include/x86_64-linux-gnu/bits/stdlib-bsearch.h \ + /usr/include/x86_64-linux-gnu/bits/stdlib-float.h \ + /usr/include/stdio.h \ + /usr/lib/gcc/x86_64-linux-gnu/8/include/stdarg.h \ + /usr/include/x86_64-linux-gnu/bits/types/__fpos_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/__mbstate_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/__fpos64_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/__FILE.h \ + /usr/include/x86_64-linux-gnu/bits/types/FILE.h \ + /usr/include/x86_64-linux-gnu/bits/types/struct_FILE.h \ + /usr/include/x86_64-linux-gnu/bits/types/cookie_io_functions_t.h \ + /usr/include/x86_64-linux-gnu/bits/stdio_lim.h \ + /usr/include/x86_64-linux-gnu/bits/sys_errlist.h \ + /usr/include/x86_64-linux-gnu/bits/stdio.h \ + /usr/include/string.h \ + /usr/include/strings.h \ + /usr/include/x86_64-linux-gnu/sys/mman.h \ + /usr/include/x86_64-linux-gnu/bits/mman.h \ + /usr/include/x86_64-linux-gnu/bits/mman-linux.h \ + /usr/include/x86_64-linux-gnu/bits/mman-shared.h \ + /usr/include/fcntl.h \ + /usr/include/x86_64-linux-gnu/bits/fcntl.h \ + /usr/include/x86_64-linux-gnu/bits/fcntl-linux.h \ + /usr/include/x86_64-linux-gnu/bits/types/struct_iovec.h \ + /usr/include/linux/falloc.h \ + /usr/include/x86_64-linux-gnu/bits/stat.h \ + /usr/include/byteswap.h \ + /usr/include/time.h \ + /usr/include/x86_64-linux-gnu/bits/time.h \ + /usr/include/x86_64-linux-gnu/bits/timex.h \ + /usr/include/x86_64-linux-gnu/bits/types/struct_tm.h \ + /usr/include/x86_64-linux-gnu/bits/types/struct_itimerspec.h \ + tools/../lib/sha256.c \ + $(wildcard include/config/hw/watchdog.h) \ + $(wildcard include/config/watchdog.h) \ + include/watchdog.h \ + $(wildcard include/config/mpc85xx.h) \ + include/u-boot/sha256.h \ + +tools/lib/sha256.o: $(deps_tools/lib/sha256.o) + +$(deps_tools/lib/sha256.o): diff --git a/tools/u-boot-tools/lib/crc16.c b/tools/u-boot-tools/lib/crc16.c new file mode 100644 index 0000000..2e9e81a --- /dev/null +++ b/tools/u-boot-tools/lib/crc16.c @@ -0,0 +1 @@ +#include <../lib/crc16.c> diff --git a/tools/u-boot-tools/lib/crc16.o b/tools/u-boot-tools/lib/crc16.o new file mode 100644 index 0000000000000000000000000000000000000000..2b6d0759e83eba679dd2d4325fd40ad243ff6a2a GIT binary patch literal 2208 zcmb<-^>JfjWMqH=Mg}_u1P><4z>vU-U^{@B4h*~uJPfUu>P&i_{(JQDf&_hfUDy~q zfAGsQcr+hl<llDX-~$e$u>btqE~Oc>1o~JWD}8=AjbGk{fuZxqVKCkLt<I*`?Z4qk zk6u%#3G9qu{l^*kw;etBki*FRKmWD^X~sNmK9<KzpQjx>$dT5`b(mki0i@pWB!~lI z;lhdnP728eEe=ZqjwL)V;AqsG=(R9=W6#007tcL>|ItuLfT`FhNNH=(6sPY+*ODwJ zOEgz+j#|9;@|?r}Ki_+tq~dI%z``i<EaiB~5|`GHtfk&tHCj2RzI%H9>zbpzSA)%p z)C9#0*shm-OPd<D#VvY!)pE({7VY=H|9d|7`kv!QJf23hWh}37;7F8U(onWIbMo!Y zzL)ENdUR&a<XWk{^J~-<ukBUSvI?aFxQwlYwEo_{b9(>pd9N#HM|4~7mRh|mr@iKh z&(Rnq9cG&Z9!HsVZ~OngIeYf5)>`hp8NHr!r`K)EyBhb!PuR{#FHo+4Z~p&%@9*CK zb6$GC)%wW!75yC(S2mv7_<AAFMcspbAM+nJ2rOhcWbjylB|z1|tsp&NXY-87zZc)y zY<pPda{c4D&l{O02z^$%WMt|j5>%cPR`hD?>7^^CcDCky^?#~!mG|f)lfx<tL>gJr zO59UaLRejHFaNcDX8Vrm@z3kN%O1D6ep&64*+jMtV&Q3JWuk5-VLmr=PxAfLe|ci& z%g(Nyt5>>2q<XMbsAn9Ic*xOYu~?(_bj)r0*K)sqcHfx2d-bf{rCuRX=2>D@AGt0_ zZPuD(rT3TrZO&cav#a)YuYEE1WUpEcdrnG>tIr~vW}OEz2YKY*+x?F{Uvq!<`d#~P z_WzhKo@Z7U>Q@psS#OiwMZOPm4DQa(Rtg#}sY#iMc?uSK#(D<2W||NND8-0?2nGfQ z#;PC&#tH#OX&!ct35*O30t^fcGEg~dC=C)bfU@17G%gC1hgcXs{>LWIj5UWbFfcPP zV^hw+%)o*n&Ixie15&1BLMUNI7Dos(Ff*_t@eoXA1`Y%ViN(yohQxy~g&7zagpk}1 z3mbW;IHYV~V1S2Da#6COnO-sjn2s+=OoFnKlQT<7klFF&De+0AX%K4|xS%x1TOe2b z`40g;IK&l@#6fzXTm};;10)8*F!wR&6<6khT%%W9QUswhV639loJ75n)QS=Yy_Cd~ zL<YU2;$jB9qI?J^IX^cyHLrw0FE76&RnOfoRJXV!F*%z-53DUUBR;JtF*g-zEyWbb zU!b%E3Ik-rVE$-B7G!{=TVx?=1_p5IfvJbd&44B;Sb7G9H^?4XN|1mmR0F3oP}+nF zK&fL;8l3`}fh?xOzyMBN=qa06{f1EcL2-v{?gJPHO#&2NFewmi3DpntCrAv0A3*iV zf;dPR=6)FM1l5nE1kC&k)eq(%5ior)p>U{v4CT-OL5~+$_`~!E;fTKzSmF<)7lc82 zVetoY4~E?g3=9rX`xQV67#JA185qEM4qZQ}oPjC@84Je=Q2UMGA`k}DI0%P<fkBV~ SGwpyRe?avof;dPRT|WRIUygeK literal 0 HcmV?d00001 diff --git a/tools/u-boot-tools/lib/crc32.c b/tools/u-boot-tools/lib/crc32.c new file mode 100644 index 0000000..5fa5f67 --- /dev/null +++ b/tools/u-boot-tools/lib/crc32.c @@ -0,0 +1 @@ +#include <../lib/crc32.c> diff --git a/tools/u-boot-tools/lib/crc32.o b/tools/u-boot-tools/lib/crc32.o new file mode 100644 index 0000000000000000000000000000000000000000..475c7445713fd50607b48d83137d897c701d9769 GIT binary patch literal 3088 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 OnE|yZ5yU~l==uS`(fUpR literal 0 HcmV?d00001 diff --git a/tools/u-boot-tools/lib/crc8.c b/tools/u-boot-tools/lib/crc8.c new file mode 100644 index 0000000..f50097a --- /dev/null +++ b/tools/u-boot-tools/lib/crc8.c @@ -0,0 +1 @@ +#include <../lib/crc8.c> diff --git a/tools/u-boot-tools/lib/crc8.o b/tools/u-boot-tools/lib/crc8.o new file mode 100644 index 0000000000000000000000000000000000000000..237f1efb10d9d3d99cf45911a231cfb6cf117954 GIT binary patch literal 1256 zcmb<-^>JfjWMqH=Mg}_u1P><4z@Wi|U^{@B4h-B3TnwE*S})aEHNRr?=&ccBOq;;J zP0aAZE)E6;hJz0{_~jWo52iJKY-mnvJ;3jAknuqCE5_0{9?i!X`L`YMu>4+n;V^@{ zv$K_ghD&NvW@4U#g`TmVfv%Y*gu%eTAOg0Ffq}6qh=H*}fKi%<onrzcL|g_c=Ln_o zQ6N9DFns)vO${?vA2Tp8Gaz{yNh31@GZGKMWM*JRaFAHc3~Wd|1e2M81;K%`Kp`iD z<Q}-+l8cfp^pY7s6iBH8h+tp<yAZ{ddc~EwC5cH4dc`G05IO_KDoL#<VbDuSEJ<Y0 zODZmA&`ZwG%}vcKVbIIVFG<yN_Y2i6E=f$zX3#52%}LZt&4^DcO3Y1#+C&*8&A<Tm z6?#ZO(h>s$+@DZu1fVpGQiSS95BWxz0yG*F-^fDh3=9mQuq9W&4%B`FWJ4Jk7-06x zqKU)ohtZ}`{b<S<=AjEf`7nJ@E`t|TKT2FPFfc5DDnNG^$gfZ-Fy+d?08VYN@CFMq zFfi<ZS|kA#0O<q85r_r}!Nftz(Cr4vUjPvd3=B7*G$%Bi(e;DG(Ze33Wd+D!1_lOR i5C;iEh2bKC450i7=fW8mpbA6aA`k{t7{Xy-U;qFq+*Ex4 literal 0 HcmV?d00001 diff --git a/tools/u-boot-tools/lib/fdtdec.c b/tools/u-boot-tools/lib/fdtdec.c new file mode 100644 index 0000000..9568094 --- /dev/null +++ b/tools/u-boot-tools/lib/fdtdec.c @@ -0,0 +1 @@ +#include <../lib/fdtdec.c> diff --git a/tools/u-boot-tools/lib/fdtdec.o b/tools/u-boot-tools/lib/fdtdec.o new file mode 100644 index 0000000000000000000000000000000000000000..4ff70e2a444864c8af648efa41dda3fbca12bd32 GIT binary patch literal 920 zcmb<-^>JfjWMqH=Mg}_u1P><4zz~5X=l~XWVBlonU|?`}cD7Q`a7j(dOw3cT&@<LE z&^6P9Fu=-i5Fq1N7(V{TCe4i11q=+#3{2RRGcYqS;}B=XA<lv+o|aOQlA5fS%)p>m zT$x*vn8cu0Tv7y~GhnQe)QS=Yy_Cd~L<YU2;$jB9<ow*+)VvY~y}bOAR6TdUP~GB^ z#N=#D<4F~fW?%q^0vnn}NZ6wZLe(ORDniB3Lxx!W>I@8^*u@B0V)g4l?KePn6axbT zOusCeI4C`UgkZEOR6j@z8OI`Xkk~MNFd+~hTWU#wDp&!f*%=rZK$ru{mw?io450J| m<seZUAcGkg7`TwcKuoAGhzZ7mIP?oa6}o~|Aqc22TmS$c@+j8; literal 0 HcmV?d00001 diff --git a/tools/u-boot-tools/lib/fdtdec_common.c b/tools/u-boot-tools/lib/fdtdec_common.c new file mode 100644 index 0000000..04843b7 --- /dev/null +++ b/tools/u-boot-tools/lib/fdtdec_common.c @@ -0,0 +1 @@ +#include <../lib/fdtdec_common.c> diff --git a/tools/u-boot-tools/lib/fdtdec_common.o b/tools/u-boot-tools/lib/fdtdec_common.o new file mode 100644 index 0000000000000000000000000000000000000000..aafea987d198015f45b2d143224a2ba164104933 GIT binary patch literal 1696 zcmb<-^>JfjWMqH=Mg}_u1P><4z_5TB!FB*M9T<2RxEX>wPkS`K5%B2sQQ>(368C65 zP{Q6^qr$^n&h62BM4<CV^kIH^hHeHh=WyqtgRdkGK9=CT%)jl*!516{9|@>3{AXa` zyv%tRq?Fp`F}OQBTPbL`q$Xu1<|$a{8S5G7nrT89Am@sJ2nGfQ#;PC&#tH#OX&!ct z35*O3Dhvz^GElhy5bek((9Gn<rx3-(C*aJ-;ll04Cy>a;0aE7zl4oFGz^4wX1cL&F zH4DSX{}>Vs3=GUzV*(_O6d6bwnHgA+cnBsl0~>;a#A0S(N8%xv%nYmu4wNOxz`!7c z<Q}kJ85kH87#J7?plXmQGaTYhP;+4VVKhTpN=Zs;a(r@rZf<^_UNVfIo>~&0nODLP z@9yL8<meOc@8%Zl8WJDk=;Y%X4^kALo?22+lwSa1gA4#^C<W<&xP})(fkTFifq~)A ze+Y=hArA5nNQ#MpfdPa;Y!LPU5l}IZINaTO#g(}wiAfB4#U(`$Is?WkO3g{sD@m;= zVbDuSEJ<Y0ODZmA&;$7}HLrw0FE76&RnOfoRJXV!F*%z-53D>jBR;JtF*g-z9mN#L zKcF;*o&q3wn1Nvdnhof2Db2tD4m+57nDPgpKww~CFn|hhg7Odp0|N(?F9D_1pyKE$ z_5oBLodUTPSxg6NCb{|zq4vYv2{RjJzbs4uO2h1j(N<9XP-!^z7tTUu!1TdH{h|7y z!37g$fMul&gb0`g3qLT2fq}sbNB9ds0|e$TQ22v#GfY1${6X%)u$zH_Ap;}<%D+$= zls7<nK^SB=Oh3q6s8t|gIPQQN8Uz=CFrdakI1CI7pmGD7eg~*vBwQtgfvz6_9$m1W literal 0 HcmV?d00001 diff --git a/tools/u-boot-tools/lib/libfdt/.fdt_region.o.cmd b/tools/u-boot-tools/lib/libfdt/.fdt_region.o.cmd new file mode 100644 index 0000000..b4aecbc --- /dev/null +++ b/tools/u-boot-tools/lib/libfdt/.fdt_region.o.cmd @@ -0,0 +1,112 @@ +cmd_tools/lib/libfdt/fdt_region.o := cc -Wp,-MD,tools/lib/libfdt/.fdt_region.o.d -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -std=gnu11 -include ./include/compiler.h -idirafterinclude -idirafter./arch/arm/include -I./scripts/dtc/libfdt -I./tools -DUSE_HOSTCC -D__KERNEL_STRICT_NAMES -D_GNU_SOURCE -c -o tools/lib/libfdt/fdt_region.o tools/lib/libfdt/fdt_region.c + +source_tools/lib/libfdt/fdt_region.o := tools/lib/libfdt/fdt_region.c + +deps_tools/lib/libfdt/fdt_region.o := \ + /usr/include/stdc-predef.h \ + include/compiler.h \ + /usr/lib/gcc/x86_64-linux-gnu/8/include/stddef.h \ + /usr/lib/gcc/x86_64-linux-gnu/8/include/stdint.h \ + /usr/include/stdint.h \ + /usr/include/x86_64-linux-gnu/bits/libc-header-start.h \ + /usr/include/features.h \ + /usr/include/x86_64-linux-gnu/sys/cdefs.h \ + /usr/include/x86_64-linux-gnu/bits/wordsize.h \ + /usr/include/x86_64-linux-gnu/bits/long-double.h \ + /usr/include/x86_64-linux-gnu/gnu/stubs.h \ + /usr/include/x86_64-linux-gnu/gnu/stubs-64.h \ + /usr/include/x86_64-linux-gnu/bits/types.h \ + /usr/include/x86_64-linux-gnu/bits/typesizes.h \ + /usr/include/x86_64-linux-gnu/bits/wchar.h \ + /usr/include/x86_64-linux-gnu/bits/stdint-intn.h \ + /usr/include/x86_64-linux-gnu/bits/stdint-uintn.h \ + /usr/include/errno.h \ + /usr/include/x86_64-linux-gnu/bits/errno.h \ + /usr/include/linux/errno.h \ + /usr/include/x86_64-linux-gnu/asm/errno.h \ + /usr/include/asm-generic/errno.h \ + /usr/include/asm-generic/errno-base.h \ + /usr/include/x86_64-linux-gnu/bits/types/error_t.h \ + /usr/include/stdlib.h \ + /usr/include/x86_64-linux-gnu/bits/waitflags.h \ + /usr/include/x86_64-linux-gnu/bits/waitstatus.h \ + /usr/include/x86_64-linux-gnu/bits/floatn.h \ + /usr/include/x86_64-linux-gnu/bits/floatn-common.h \ + /usr/include/x86_64-linux-gnu/bits/types/locale_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/__locale_t.h \ + /usr/include/x86_64-linux-gnu/sys/types.h \ + /usr/include/x86_64-linux-gnu/bits/types/clock_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/clockid_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/time_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/timer_t.h \ + /usr/include/endian.h \ + /usr/include/x86_64-linux-gnu/bits/endian.h \ + /usr/include/x86_64-linux-gnu/bits/byteswap.h \ + /usr/include/x86_64-linux-gnu/bits/uintn-identity.h \ + /usr/include/x86_64-linux-gnu/sys/select.h \ + /usr/include/x86_64-linux-gnu/bits/select.h \ + /usr/include/x86_64-linux-gnu/bits/types/sigset_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/__sigset_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/struct_timeval.h \ + /usr/include/x86_64-linux-gnu/bits/types/struct_timespec.h \ + /usr/include/x86_64-linux-gnu/bits/pthreadtypes.h \ + /usr/include/x86_64-linux-gnu/bits/thread-shared-types.h \ + /usr/include/x86_64-linux-gnu/bits/pthreadtypes-arch.h \ + /usr/include/alloca.h \ + /usr/include/x86_64-linux-gnu/bits/stdlib-bsearch.h \ + /usr/include/x86_64-linux-gnu/bits/stdlib-float.h \ + /usr/include/stdio.h \ + /usr/lib/gcc/x86_64-linux-gnu/8/include/stdarg.h \ + /usr/include/x86_64-linux-gnu/bits/types/__fpos_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/__mbstate_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/__fpos64_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/__FILE.h \ + /usr/include/x86_64-linux-gnu/bits/types/FILE.h \ + /usr/include/x86_64-linux-gnu/bits/types/struct_FILE.h \ + /usr/include/x86_64-linux-gnu/bits/types/cookie_io_functions_t.h \ + /usr/include/x86_64-linux-gnu/bits/stdio_lim.h \ + /usr/include/x86_64-linux-gnu/bits/sys_errlist.h \ + /usr/include/x86_64-linux-gnu/bits/stdio.h \ + /usr/include/string.h \ + /usr/include/strings.h \ + /usr/include/x86_64-linux-gnu/sys/mman.h \ + /usr/include/x86_64-linux-gnu/bits/mman.h \ + /usr/include/x86_64-linux-gnu/bits/mman-linux.h \ + /usr/include/x86_64-linux-gnu/bits/mman-shared.h \ + /usr/include/fcntl.h \ + /usr/include/x86_64-linux-gnu/bits/fcntl.h \ + /usr/include/x86_64-linux-gnu/bits/fcntl-linux.h \ + /usr/include/x86_64-linux-gnu/bits/types/struct_iovec.h \ + /usr/include/linux/falloc.h \ + /usr/include/x86_64-linux-gnu/bits/stat.h \ + /usr/include/byteswap.h \ + /usr/include/time.h \ + /usr/include/x86_64-linux-gnu/bits/time.h \ + /usr/include/x86_64-linux-gnu/bits/timex.h \ + /usr/include/x86_64-linux-gnu/bits/types/struct_tm.h \ + /usr/include/x86_64-linux-gnu/bits/types/struct_itimerspec.h \ + tools/../lib/libfdt/fdt_region.c \ + include/linux/libfdt_env.h \ + tools/../scripts/dtc/libfdt/libfdt_env.h \ + tools/fdt_host.h \ + tools/../include/linux/libfdt.h \ + tools/../include/linux/../../scripts/dtc/libfdt/libfdt.h \ + tools/../include/linux/../../scripts/dtc/libfdt/libfdt_env.h \ + tools/../include/linux/../../scripts/dtc/libfdt/fdt.h \ + tools/../include/fdt_support.h \ + $(wildcard include/config/of/libfdt.h) \ + $(wildcard include/config/arch/fixup/fdt/memory.h) \ + $(wildcard include/config/usb/ehci/fsl.h) \ + $(wildcard include/config/usb/xhci/fsl.h) \ + $(wildcard include/config/sys/fsl/sec/compat.h) \ + $(wildcard include/config/pci.h) \ + $(wildcard include/config/sys/fdt/pad.h) \ + $(wildcard include/config/of/board/setup.h) \ + $(wildcard include/config/of/system/setup.h) \ + $(wildcard include/config/fdt/fixup/partitions.h) \ + $(wildcard include/config/fman/enet.h) \ + $(wildcard include/config/fsl/mc/enet.h) \ + +tools/lib/libfdt/fdt_region.o: $(deps_tools/lib/libfdt/fdt_region.o) + +$(deps_tools/lib/libfdt/fdt_region.o): diff --git a/tools/u-boot-tools/lib/libfdt/.fdt_ro.o.cmd b/tools/u-boot-tools/lib/libfdt/.fdt_ro.o.cmd new file mode 100644 index 0000000..1458c90 --- /dev/null +++ b/tools/u-boot-tools/lib/libfdt/.fdt_ro.o.cmd @@ -0,0 +1,115 @@ +cmd_tools/lib/libfdt/fdt_ro.o := cc -Wp,-MD,tools/lib/libfdt/.fdt_ro.o.d -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -std=gnu11 -include ./include/compiler.h -idirafterinclude -idirafter./arch/arm/include -I./scripts/dtc/libfdt -I./tools -DUSE_HOSTCC -D__KERNEL_STRICT_NAMES -D_GNU_SOURCE -c -o tools/lib/libfdt/fdt_ro.o tools/lib/libfdt/fdt_ro.c + +source_tools/lib/libfdt/fdt_ro.o := tools/lib/libfdt/fdt_ro.c + +deps_tools/lib/libfdt/fdt_ro.o := \ + /usr/include/stdc-predef.h \ + include/compiler.h \ + /usr/lib/gcc/x86_64-linux-gnu/8/include/stddef.h \ + /usr/lib/gcc/x86_64-linux-gnu/8/include/stdint.h \ + /usr/include/stdint.h \ + /usr/include/x86_64-linux-gnu/bits/libc-header-start.h \ + /usr/include/features.h \ + /usr/include/x86_64-linux-gnu/sys/cdefs.h \ + /usr/include/x86_64-linux-gnu/bits/wordsize.h \ + /usr/include/x86_64-linux-gnu/bits/long-double.h \ + /usr/include/x86_64-linux-gnu/gnu/stubs.h \ + /usr/include/x86_64-linux-gnu/gnu/stubs-64.h \ + /usr/include/x86_64-linux-gnu/bits/types.h \ + /usr/include/x86_64-linux-gnu/bits/typesizes.h \ + /usr/include/x86_64-linux-gnu/bits/wchar.h \ + /usr/include/x86_64-linux-gnu/bits/stdint-intn.h \ + /usr/include/x86_64-linux-gnu/bits/stdint-uintn.h \ + /usr/include/errno.h \ + /usr/include/x86_64-linux-gnu/bits/errno.h \ + /usr/include/linux/errno.h \ + /usr/include/x86_64-linux-gnu/asm/errno.h \ + /usr/include/asm-generic/errno.h \ + /usr/include/asm-generic/errno-base.h \ + /usr/include/x86_64-linux-gnu/bits/types/error_t.h \ + /usr/include/stdlib.h \ + /usr/include/x86_64-linux-gnu/bits/waitflags.h \ + /usr/include/x86_64-linux-gnu/bits/waitstatus.h \ + /usr/include/x86_64-linux-gnu/bits/floatn.h \ + /usr/include/x86_64-linux-gnu/bits/floatn-common.h \ + /usr/include/x86_64-linux-gnu/bits/types/locale_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/__locale_t.h \ + /usr/include/x86_64-linux-gnu/sys/types.h \ + /usr/include/x86_64-linux-gnu/bits/types/clock_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/clockid_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/time_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/timer_t.h \ + /usr/include/endian.h \ + /usr/include/x86_64-linux-gnu/bits/endian.h \ + /usr/include/x86_64-linux-gnu/bits/byteswap.h \ + /usr/include/x86_64-linux-gnu/bits/uintn-identity.h \ + /usr/include/x86_64-linux-gnu/sys/select.h \ + /usr/include/x86_64-linux-gnu/bits/select.h \ + /usr/include/x86_64-linux-gnu/bits/types/sigset_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/__sigset_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/struct_timeval.h \ + /usr/include/x86_64-linux-gnu/bits/types/struct_timespec.h \ + /usr/include/x86_64-linux-gnu/bits/pthreadtypes.h \ + /usr/include/x86_64-linux-gnu/bits/thread-shared-types.h \ + /usr/include/x86_64-linux-gnu/bits/pthreadtypes-arch.h \ + /usr/include/alloca.h \ + /usr/include/x86_64-linux-gnu/bits/stdlib-bsearch.h \ + /usr/include/x86_64-linux-gnu/bits/stdlib-float.h \ + /usr/include/stdio.h \ + /usr/lib/gcc/x86_64-linux-gnu/8/include/stdarg.h \ + /usr/include/x86_64-linux-gnu/bits/types/__fpos_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/__mbstate_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/__fpos64_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/__FILE.h \ + /usr/include/x86_64-linux-gnu/bits/types/FILE.h \ + /usr/include/x86_64-linux-gnu/bits/types/struct_FILE.h \ + /usr/include/x86_64-linux-gnu/bits/types/cookie_io_functions_t.h \ + /usr/include/x86_64-linux-gnu/bits/stdio_lim.h \ + /usr/include/x86_64-linux-gnu/bits/sys_errlist.h \ + /usr/include/x86_64-linux-gnu/bits/stdio.h \ + /usr/include/string.h \ + /usr/include/strings.h \ + /usr/include/x86_64-linux-gnu/sys/mman.h \ + /usr/include/x86_64-linux-gnu/bits/mman.h \ + /usr/include/x86_64-linux-gnu/bits/mman-linux.h \ + /usr/include/x86_64-linux-gnu/bits/mman-shared.h \ + /usr/include/fcntl.h \ + /usr/include/x86_64-linux-gnu/bits/fcntl.h \ + /usr/include/x86_64-linux-gnu/bits/fcntl-linux.h \ + /usr/include/x86_64-linux-gnu/bits/types/struct_iovec.h \ + /usr/include/linux/falloc.h \ + /usr/include/x86_64-linux-gnu/bits/stat.h \ + /usr/include/byteswap.h \ + /usr/include/time.h \ + /usr/include/x86_64-linux-gnu/bits/time.h \ + /usr/include/x86_64-linux-gnu/bits/timex.h \ + /usr/include/x86_64-linux-gnu/bits/types/struct_tm.h \ + /usr/include/x86_64-linux-gnu/bits/types/struct_itimerspec.h \ + tools/../lib/libfdt/fdt_ro.c \ + include/linux/libfdt_env.h \ + tools/../scripts/dtc/libfdt/libfdt_env.h \ + tools/fdt_host.h \ + tools/../include/linux/libfdt.h \ + tools/../include/linux/../../scripts/dtc/libfdt/libfdt.h \ + tools/../include/linux/../../scripts/dtc/libfdt/libfdt_env.h \ + tools/../include/linux/../../scripts/dtc/libfdt/fdt.h \ + tools/../include/fdt_support.h \ + $(wildcard include/config/of/libfdt.h) \ + $(wildcard include/config/arch/fixup/fdt/memory.h) \ + $(wildcard include/config/usb/ehci/fsl.h) \ + $(wildcard include/config/usb/xhci/fsl.h) \ + $(wildcard include/config/sys/fsl/sec/compat.h) \ + $(wildcard include/config/pci.h) \ + $(wildcard include/config/sys/fdt/pad.h) \ + $(wildcard include/config/of/board/setup.h) \ + $(wildcard include/config/of/system/setup.h) \ + $(wildcard include/config/fdt/fixup/partitions.h) \ + $(wildcard include/config/fman/enet.h) \ + $(wildcard include/config/fsl/mc/enet.h) \ + tools/../lib/libfdt/libfdt_internal.h \ + tools/../lib/libfdt/../../scripts/dtc/libfdt/libfdt_internal.h \ + scripts/dtc/libfdt/fdt.h \ + +tools/lib/libfdt/fdt_ro.o: $(deps_tools/lib/libfdt/fdt_ro.o) + +$(deps_tools/lib/libfdt/fdt_ro.o): diff --git a/tools/u-boot-tools/lib/libfdt/fdt_region.c b/tools/u-boot-tools/lib/libfdt/fdt_region.c new file mode 100644 index 0000000..d85be27 --- /dev/null +++ b/tools/u-boot-tools/lib/libfdt/fdt_region.c @@ -0,0 +1 @@ +#include <../lib/libfdt/fdt_region.c> diff --git a/tools/u-boot-tools/lib/libfdt/fdt_region.o b/tools/u-boot-tools/lib/libfdt/fdt_region.o new file mode 100644 index 0000000000000000000000000000000000000000..d185c6b1edda497ec92735552c09bca808194ffa GIT binary patch literal 7808 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 literal 0 HcmV?d00001 diff --git a/tools/u-boot-tools/lib/libfdt/fdt_ro.c b/tools/u-boot-tools/lib/libfdt/fdt_ro.c new file mode 100644 index 0000000..fd0a499 --- /dev/null +++ b/tools/u-boot-tools/lib/libfdt/fdt_ro.c @@ -0,0 +1 @@ +#include <../lib/libfdt/fdt_ro.c> diff --git a/tools/u-boot-tools/lib/libfdt/fdt_ro.o b/tools/u-boot-tools/lib/libfdt/fdt_ro.o new file mode 100644 index 0000000000000000000000000000000000000000..5a925b0a25f3e535fb5d29b26910ba86fe3b1d35 GIT binary patch literal 12560 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 K$h|N&hz0=1uktPc literal 0 HcmV?d00001 diff --git a/tools/u-boot-tools/lib/md5.c b/tools/u-boot-tools/lib/md5.c new file mode 100644 index 0000000..d12053d --- /dev/null +++ b/tools/u-boot-tools/lib/md5.c @@ -0,0 +1 @@ +#include <../lib/md5.c> diff --git a/tools/u-boot-tools/lib/md5.o b/tools/u-boot-tools/lib/md5.o new file mode 100644 index 0000000000000000000000000000000000000000..9d765b8c8a2025e353c3873badb9adaa1d36fe0d GIT binary patch literal 4304 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 literal 0 HcmV?d00001 diff --git a/tools/u-boot-tools/lib/rc4.c b/tools/u-boot-tools/lib/rc4.c new file mode 100644 index 0000000..79dd7de --- /dev/null +++ b/tools/u-boot-tools/lib/rc4.c @@ -0,0 +1 @@ +#include <../lib/rc4.c> diff --git a/tools/u-boot-tools/lib/rc4.o b/tools/u-boot-tools/lib/rc4.o new file mode 100644 index 0000000000000000000000000000000000000000..0efe03e515935fbf572960ab55f0fca8b82fd2b0 GIT binary patch literal 1440 zcmb<-^>JfjWMqH=Mg}_u1P><4!0><x!FB*M9T>P7xEMSd-%Ma+U@$!CYIwk-*F~kn zr?*F?gMop8U%mmP%(3|ae+LhTEC2Qjj?D)c-T1eO2)TBMaC$T!Wb|nK39_s7wom6t zkIq9ry)!^uSHqM1@(eEg+nD&bF?uu~VgxC5VLaf%c!Gc10T=#lAuJt^od;byg8sX7 zxUh71T3#-_*!ry`sMqbk;Yq`9KD{CAjA{JxEg<U|dczp`w_W1j<|5M3d5!TP|F)AZ z{M!z?@o)2C>2T@r5$SLOnZe{D(!sy&0RJ`@mJS1UkLKfyo|eB#4|_Bo0eS8)gS)e{ zm4b##YEoumo`Qv*v7UjhnI?n*wuON~1WGej1u-yI2rx?XuycU)$}li6fG|k<DTr|A zo59R<myZL)#)Cm|z{2qHKQ<-IpkTrx!OVaZNl5a{49rM81e2M86~RGbF*C3s@eoXA z1{MSd%3^0=U=Tub57>_&xuRqfy<~=>WRv*RyyX0pRFD`01A_vT2Duc3p<D(Az2eH; zlEfqiz2cH02%Q0Am84dbFzBTumLxLhB^4Jl=q2ap=BDPAFzDswm!#^s`-SQjmn0@< zGw2ni<|OK+X2hozCFa6xgxf$CLz;mB>^t-jf#d-O2DpErrVBu67^Mi+j~)Z6pa_O3 zLZZ=y)fpHVKxu_s{W?(l4Up^rF=6)0f;dPRW<QKJh3ZFA0%khF6o7dM0$tb(svjk; z85kHmpb9MzTEQ%kU%?D0;mW`OPIa*Gh6*zjKn;|D3V`&1;s`{8gka(zYoJO&!f@OG zl3-w9xB(U6goZP^evmkN*n_l0Kn)cFagZ?7Sh$EF0|VCF*8$ZZ0#^xPpz8+!C&r7b literal 0 HcmV?d00001 diff --git a/tools/u-boot-tools/lib/sha1.c b/tools/u-boot-tools/lib/sha1.c new file mode 100644 index 0000000..c868d26 --- /dev/null +++ b/tools/u-boot-tools/lib/sha1.c @@ -0,0 +1 @@ +#include <../lib/sha1.c> diff --git a/tools/u-boot-tools/lib/sha1.o b/tools/u-boot-tools/lib/sha1.o new file mode 100644 index 0000000000000000000000000000000000000000..23e859dfa76d2ad7dd76704a3aa47a3f11d1730f GIT binary patch literal 13024 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 literal 0 HcmV?d00001 diff --git a/tools/u-boot-tools/lib/sha256.c b/tools/u-boot-tools/lib/sha256.c new file mode 100644 index 0000000..85b3f7f --- /dev/null +++ b/tools/u-boot-tools/lib/sha256.c @@ -0,0 +1 @@ +#include <../lib/sha256.c> diff --git a/tools/u-boot-tools/lib/sha256.o b/tools/u-boot-tools/lib/sha256.o new file mode 100644 index 0000000000000000000000000000000000000000..839b461cf71702b5109478bb0f0f6cee3e79be79 GIT binary patch literal 14000 zcmb<-^>JfjWMqH=Mg}_u1P><4z))a}U^{@B4h*~uJPeNEo}IrO!yH2$LqdZ+n%{7A z+jYC~xOA6^bmz(NpXWc(S);P1yH3Ti^GLUwiA%Sef+PPq{_~wRDm~qG8vJLw{dBs+ z3|u-(R66)ixOA6ExOCUCbh@Z4apXVI?PlT9?Z?rXqq3#*uS<8HfJ>)~$_|kJv-~GJ zb5v&VpW;6O(z?Ns|GXprNk{&(ojxjaIzv<zbh@ZaaO{ra=ytR5>AdFJ9mM0(nWC}> zWPi7xj7O)5ifiXJm+mkD$IinpojEEi9QjYXbjGMmaqK+p(s|USGeqTqOLv$ENbCiO z_r#?;OrpC?r87t6hfC*mm(EKE&vPF<#nxG(@~87hca6##!wVq6L+pkZdRy3L=yy4F zp6k5nVtC9&@uQ34H4vlQMdb<OvCbR4B6gkk4xZq3>AZgM9FvRTeMiO%h95fb9X!Sb zqFgw?DVL}`;5^58tn-J9;sM5Mj=fX#C%Tvyx)|PY?L6#w@H89aA(u`lo7<)HCRp?^ zyWv5X&KoY>F)9xXPjr4{ywLgS;7O)~r+6KEb(kGHZyTQK{OHnoALOSu2M=;7em6YT zdDEryGv~+77?lq$iXUA&uW|ka`>sUgi;LlL#*2&xJAWv?a522#(jB7m!nt>k;Q8MN zwmNpcay)p7jq$ik=LHCx+okgon01KV@DNz^l;gouOyDqa>^#Nz!|~uLUdF>No!1>Z zPZ?f!Ja~x9#qhdg=O;(bQ(ÀHNwDBg16yyV(>i18QWAxFbkE}hrid#9Kzy}k9N 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 X1gP9Zk9&|f2Xuc~B8Y>8(e(oW6=p$D literal 0 HcmV?d00001 diff --git a/tools/u-boot-tools/libfdt/.fdt.o.cmd b/tools/u-boot-tools/libfdt/.fdt.o.cmd new file mode 100644 index 0000000..3c7470d --- /dev/null +++ b/tools/u-boot-tools/libfdt/.fdt.o.cmd @@ -0,0 +1,114 @@ +cmd_tools/libfdt/fdt.o := cc -Wp,-MD,tools/libfdt/.fdt.o.d -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -std=gnu11 -include ./include/compiler.h -idirafterinclude -idirafter./arch/arm/include -I./scripts/dtc/libfdt -I./tools -DUSE_HOSTCC -D__KERNEL_STRICT_NAMES -D_GNU_SOURCE -c -o tools/libfdt/fdt.o tools/libfdt/fdt.c + +source_tools/libfdt/fdt.o := tools/libfdt/fdt.c + +deps_tools/libfdt/fdt.o := \ + /usr/include/stdc-predef.h \ + include/compiler.h \ + /usr/lib/gcc/x86_64-linux-gnu/8/include/stddef.h \ + /usr/lib/gcc/x86_64-linux-gnu/8/include/stdint.h \ + /usr/include/stdint.h \ + /usr/include/x86_64-linux-gnu/bits/libc-header-start.h \ + /usr/include/features.h \ + /usr/include/x86_64-linux-gnu/sys/cdefs.h \ + /usr/include/x86_64-linux-gnu/bits/wordsize.h \ + /usr/include/x86_64-linux-gnu/bits/long-double.h \ + /usr/include/x86_64-linux-gnu/gnu/stubs.h \ + /usr/include/x86_64-linux-gnu/gnu/stubs-64.h \ + /usr/include/x86_64-linux-gnu/bits/types.h \ + /usr/include/x86_64-linux-gnu/bits/typesizes.h \ + /usr/include/x86_64-linux-gnu/bits/wchar.h \ + /usr/include/x86_64-linux-gnu/bits/stdint-intn.h \ + /usr/include/x86_64-linux-gnu/bits/stdint-uintn.h \ + /usr/include/errno.h \ + /usr/include/x86_64-linux-gnu/bits/errno.h \ + /usr/include/linux/errno.h \ + /usr/include/x86_64-linux-gnu/asm/errno.h \ + /usr/include/asm-generic/errno.h \ + /usr/include/asm-generic/errno-base.h \ + /usr/include/x86_64-linux-gnu/bits/types/error_t.h \ + /usr/include/stdlib.h \ + /usr/include/x86_64-linux-gnu/bits/waitflags.h \ + /usr/include/x86_64-linux-gnu/bits/waitstatus.h \ + /usr/include/x86_64-linux-gnu/bits/floatn.h \ + /usr/include/x86_64-linux-gnu/bits/floatn-common.h \ + /usr/include/x86_64-linux-gnu/bits/types/locale_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/__locale_t.h \ + /usr/include/x86_64-linux-gnu/sys/types.h \ + /usr/include/x86_64-linux-gnu/bits/types/clock_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/clockid_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/time_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/timer_t.h \ + /usr/include/endian.h \ + /usr/include/x86_64-linux-gnu/bits/endian.h \ + /usr/include/x86_64-linux-gnu/bits/byteswap.h \ + /usr/include/x86_64-linux-gnu/bits/uintn-identity.h \ + /usr/include/x86_64-linux-gnu/sys/select.h \ + /usr/include/x86_64-linux-gnu/bits/select.h \ + /usr/include/x86_64-linux-gnu/bits/types/sigset_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/__sigset_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/struct_timeval.h \ + /usr/include/x86_64-linux-gnu/bits/types/struct_timespec.h \ + /usr/include/x86_64-linux-gnu/bits/pthreadtypes.h \ + /usr/include/x86_64-linux-gnu/bits/thread-shared-types.h \ + /usr/include/x86_64-linux-gnu/bits/pthreadtypes-arch.h \ + /usr/include/alloca.h \ + /usr/include/x86_64-linux-gnu/bits/stdlib-bsearch.h \ + /usr/include/x86_64-linux-gnu/bits/stdlib-float.h \ + /usr/include/stdio.h \ + /usr/lib/gcc/x86_64-linux-gnu/8/include/stdarg.h \ + /usr/include/x86_64-linux-gnu/bits/types/__fpos_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/__mbstate_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/__fpos64_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/__FILE.h \ + /usr/include/x86_64-linux-gnu/bits/types/FILE.h \ + /usr/include/x86_64-linux-gnu/bits/types/struct_FILE.h \ + /usr/include/x86_64-linux-gnu/bits/types/cookie_io_functions_t.h \ + /usr/include/x86_64-linux-gnu/bits/stdio_lim.h \ + /usr/include/x86_64-linux-gnu/bits/sys_errlist.h \ + /usr/include/x86_64-linux-gnu/bits/stdio.h \ + /usr/include/string.h \ + /usr/include/strings.h \ + /usr/include/x86_64-linux-gnu/sys/mman.h \ + /usr/include/x86_64-linux-gnu/bits/mman.h \ + /usr/include/x86_64-linux-gnu/bits/mman-linux.h \ + /usr/include/x86_64-linux-gnu/bits/mman-shared.h \ + /usr/include/fcntl.h \ + /usr/include/x86_64-linux-gnu/bits/fcntl.h \ + /usr/include/x86_64-linux-gnu/bits/fcntl-linux.h \ + /usr/include/x86_64-linux-gnu/bits/types/struct_iovec.h \ + /usr/include/linux/falloc.h \ + /usr/include/x86_64-linux-gnu/bits/stat.h \ + /usr/include/byteswap.h \ + /usr/include/time.h \ + /usr/include/x86_64-linux-gnu/bits/time.h \ + /usr/include/x86_64-linux-gnu/bits/timex.h \ + /usr/include/x86_64-linux-gnu/bits/types/struct_tm.h \ + /usr/include/x86_64-linux-gnu/bits/types/struct_itimerspec.h \ + tools/fdt_host.h \ + tools/../include/linux/libfdt.h \ + tools/../include/linux/../../scripts/dtc/libfdt/libfdt.h \ + tools/../include/linux/../../scripts/dtc/libfdt/libfdt_env.h \ + tools/../include/linux/../../scripts/dtc/libfdt/fdt.h \ + tools/../include/fdt_support.h \ + $(wildcard include/config/of/libfdt.h) \ + $(wildcard include/config/arch/fixup/fdt/memory.h) \ + $(wildcard include/config/usb/ehci/fsl.h) \ + $(wildcard include/config/usb/xhci/fsl.h) \ + $(wildcard include/config/sys/fsl/sec/compat.h) \ + $(wildcard include/config/pci.h) \ + $(wildcard include/config/sys/fdt/pad.h) \ + $(wildcard include/config/of/board/setup.h) \ + $(wildcard include/config/of/system/setup.h) \ + $(wildcard include/config/fdt/fixup/partitions.h) \ + $(wildcard include/config/fman/enet.h) \ + $(wildcard include/config/fsl/mc/enet.h) \ + tools/../scripts/dtc/libfdt/fdt.c \ + tools/../scripts/dtc/libfdt/libfdt_env.h \ + scripts/dtc/libfdt/fdt.h \ + scripts/dtc/libfdt/libfdt.h \ + tools/../scripts/dtc/libfdt/libfdt_internal.h \ + +tools/libfdt/fdt.o: $(deps_tools/libfdt/fdt.o) + +$(deps_tools/libfdt/fdt.o): diff --git a/tools/u-boot-tools/libfdt/.fdt_addresses.o.cmd b/tools/u-boot-tools/libfdt/.fdt_addresses.o.cmd new file mode 100644 index 0000000..ebcbf3e --- /dev/null +++ b/tools/u-boot-tools/libfdt/.fdt_addresses.o.cmd @@ -0,0 +1,114 @@ +cmd_tools/libfdt/fdt_addresses.o := cc -Wp,-MD,tools/libfdt/.fdt_addresses.o.d -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -std=gnu11 -include ./include/compiler.h -idirafterinclude -idirafter./arch/arm/include -I./scripts/dtc/libfdt -I./tools -DUSE_HOSTCC -D__KERNEL_STRICT_NAMES -D_GNU_SOURCE -c -o tools/libfdt/fdt_addresses.o tools/libfdt/fdt_addresses.c + +source_tools/libfdt/fdt_addresses.o := tools/libfdt/fdt_addresses.c + +deps_tools/libfdt/fdt_addresses.o := \ + /usr/include/stdc-predef.h \ + include/compiler.h \ + /usr/lib/gcc/x86_64-linux-gnu/8/include/stddef.h \ + /usr/lib/gcc/x86_64-linux-gnu/8/include/stdint.h \ + /usr/include/stdint.h \ + /usr/include/x86_64-linux-gnu/bits/libc-header-start.h \ + /usr/include/features.h \ + /usr/include/x86_64-linux-gnu/sys/cdefs.h \ + /usr/include/x86_64-linux-gnu/bits/wordsize.h \ + /usr/include/x86_64-linux-gnu/bits/long-double.h \ + /usr/include/x86_64-linux-gnu/gnu/stubs.h \ + /usr/include/x86_64-linux-gnu/gnu/stubs-64.h \ + /usr/include/x86_64-linux-gnu/bits/types.h \ + /usr/include/x86_64-linux-gnu/bits/typesizes.h \ + /usr/include/x86_64-linux-gnu/bits/wchar.h \ + /usr/include/x86_64-linux-gnu/bits/stdint-intn.h \ + /usr/include/x86_64-linux-gnu/bits/stdint-uintn.h \ + /usr/include/errno.h \ + /usr/include/x86_64-linux-gnu/bits/errno.h \ + /usr/include/linux/errno.h \ + /usr/include/x86_64-linux-gnu/asm/errno.h \ + /usr/include/asm-generic/errno.h \ + /usr/include/asm-generic/errno-base.h \ + /usr/include/x86_64-linux-gnu/bits/types/error_t.h \ + /usr/include/stdlib.h \ + /usr/include/x86_64-linux-gnu/bits/waitflags.h \ + /usr/include/x86_64-linux-gnu/bits/waitstatus.h \ + /usr/include/x86_64-linux-gnu/bits/floatn.h \ + /usr/include/x86_64-linux-gnu/bits/floatn-common.h \ + /usr/include/x86_64-linux-gnu/bits/types/locale_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/__locale_t.h \ + /usr/include/x86_64-linux-gnu/sys/types.h \ + /usr/include/x86_64-linux-gnu/bits/types/clock_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/clockid_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/time_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/timer_t.h \ + /usr/include/endian.h \ + /usr/include/x86_64-linux-gnu/bits/endian.h \ + /usr/include/x86_64-linux-gnu/bits/byteswap.h \ + /usr/include/x86_64-linux-gnu/bits/uintn-identity.h \ + /usr/include/x86_64-linux-gnu/sys/select.h \ + /usr/include/x86_64-linux-gnu/bits/select.h \ + /usr/include/x86_64-linux-gnu/bits/types/sigset_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/__sigset_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/struct_timeval.h \ + /usr/include/x86_64-linux-gnu/bits/types/struct_timespec.h \ + /usr/include/x86_64-linux-gnu/bits/pthreadtypes.h \ + /usr/include/x86_64-linux-gnu/bits/thread-shared-types.h \ + /usr/include/x86_64-linux-gnu/bits/pthreadtypes-arch.h \ + /usr/include/alloca.h \ + /usr/include/x86_64-linux-gnu/bits/stdlib-bsearch.h \ + /usr/include/x86_64-linux-gnu/bits/stdlib-float.h \ + /usr/include/stdio.h \ + /usr/lib/gcc/x86_64-linux-gnu/8/include/stdarg.h \ + /usr/include/x86_64-linux-gnu/bits/types/__fpos_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/__mbstate_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/__fpos64_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/__FILE.h \ + /usr/include/x86_64-linux-gnu/bits/types/FILE.h \ + /usr/include/x86_64-linux-gnu/bits/types/struct_FILE.h \ + /usr/include/x86_64-linux-gnu/bits/types/cookie_io_functions_t.h \ + /usr/include/x86_64-linux-gnu/bits/stdio_lim.h \ + /usr/include/x86_64-linux-gnu/bits/sys_errlist.h \ + /usr/include/x86_64-linux-gnu/bits/stdio.h \ + /usr/include/string.h \ + /usr/include/strings.h \ + /usr/include/x86_64-linux-gnu/sys/mman.h \ + /usr/include/x86_64-linux-gnu/bits/mman.h \ + /usr/include/x86_64-linux-gnu/bits/mman-linux.h \ + /usr/include/x86_64-linux-gnu/bits/mman-shared.h \ + /usr/include/fcntl.h \ + /usr/include/x86_64-linux-gnu/bits/fcntl.h \ + /usr/include/x86_64-linux-gnu/bits/fcntl-linux.h \ + /usr/include/x86_64-linux-gnu/bits/types/struct_iovec.h \ + /usr/include/linux/falloc.h \ + /usr/include/x86_64-linux-gnu/bits/stat.h \ + /usr/include/byteswap.h \ + /usr/include/time.h \ + /usr/include/x86_64-linux-gnu/bits/time.h \ + /usr/include/x86_64-linux-gnu/bits/timex.h \ + /usr/include/x86_64-linux-gnu/bits/types/struct_tm.h \ + /usr/include/x86_64-linux-gnu/bits/types/struct_itimerspec.h \ + tools/fdt_host.h \ + tools/../include/linux/libfdt.h \ + tools/../include/linux/../../scripts/dtc/libfdt/libfdt.h \ + tools/../include/linux/../../scripts/dtc/libfdt/libfdt_env.h \ + tools/../include/linux/../../scripts/dtc/libfdt/fdt.h \ + tools/../include/fdt_support.h \ + $(wildcard include/config/of/libfdt.h) \ + $(wildcard include/config/arch/fixup/fdt/memory.h) \ + $(wildcard include/config/usb/ehci/fsl.h) \ + $(wildcard include/config/usb/xhci/fsl.h) \ + $(wildcard include/config/sys/fsl/sec/compat.h) \ + $(wildcard include/config/pci.h) \ + $(wildcard include/config/sys/fdt/pad.h) \ + $(wildcard include/config/of/board/setup.h) \ + $(wildcard include/config/of/system/setup.h) \ + $(wildcard include/config/fdt/fixup/partitions.h) \ + $(wildcard include/config/fman/enet.h) \ + $(wildcard include/config/fsl/mc/enet.h) \ + tools/../scripts/dtc/libfdt/fdt_addresses.c \ + tools/../scripts/dtc/libfdt/libfdt_env.h \ + scripts/dtc/libfdt/fdt.h \ + scripts/dtc/libfdt/libfdt.h \ + tools/../scripts/dtc/libfdt/libfdt_internal.h \ + +tools/libfdt/fdt_addresses.o: $(deps_tools/libfdt/fdt_addresses.o) + +$(deps_tools/libfdt/fdt_addresses.o): diff --git a/tools/u-boot-tools/libfdt/.fdt_empty_tree.o.cmd b/tools/u-boot-tools/libfdt/.fdt_empty_tree.o.cmd new file mode 100644 index 0000000..5f1dea7 --- /dev/null +++ b/tools/u-boot-tools/libfdt/.fdt_empty_tree.o.cmd @@ -0,0 +1,114 @@ +cmd_tools/libfdt/fdt_empty_tree.o := cc -Wp,-MD,tools/libfdt/.fdt_empty_tree.o.d -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -std=gnu11 -include ./include/compiler.h -idirafterinclude -idirafter./arch/arm/include -I./scripts/dtc/libfdt -I./tools -DUSE_HOSTCC -D__KERNEL_STRICT_NAMES -D_GNU_SOURCE -c -o tools/libfdt/fdt_empty_tree.o tools/libfdt/fdt_empty_tree.c + +source_tools/libfdt/fdt_empty_tree.o := tools/libfdt/fdt_empty_tree.c + +deps_tools/libfdt/fdt_empty_tree.o := \ + /usr/include/stdc-predef.h \ + include/compiler.h \ + /usr/lib/gcc/x86_64-linux-gnu/8/include/stddef.h \ + /usr/lib/gcc/x86_64-linux-gnu/8/include/stdint.h \ + /usr/include/stdint.h \ + /usr/include/x86_64-linux-gnu/bits/libc-header-start.h \ + /usr/include/features.h \ + /usr/include/x86_64-linux-gnu/sys/cdefs.h \ + /usr/include/x86_64-linux-gnu/bits/wordsize.h \ + /usr/include/x86_64-linux-gnu/bits/long-double.h \ + /usr/include/x86_64-linux-gnu/gnu/stubs.h \ + /usr/include/x86_64-linux-gnu/gnu/stubs-64.h \ + /usr/include/x86_64-linux-gnu/bits/types.h \ + /usr/include/x86_64-linux-gnu/bits/typesizes.h \ + /usr/include/x86_64-linux-gnu/bits/wchar.h \ + /usr/include/x86_64-linux-gnu/bits/stdint-intn.h \ + /usr/include/x86_64-linux-gnu/bits/stdint-uintn.h \ + /usr/include/errno.h \ + /usr/include/x86_64-linux-gnu/bits/errno.h \ + /usr/include/linux/errno.h \ + /usr/include/x86_64-linux-gnu/asm/errno.h \ + /usr/include/asm-generic/errno.h \ + /usr/include/asm-generic/errno-base.h \ + /usr/include/x86_64-linux-gnu/bits/types/error_t.h \ + /usr/include/stdlib.h \ + /usr/include/x86_64-linux-gnu/bits/waitflags.h \ + /usr/include/x86_64-linux-gnu/bits/waitstatus.h \ + /usr/include/x86_64-linux-gnu/bits/floatn.h \ + /usr/include/x86_64-linux-gnu/bits/floatn-common.h \ + /usr/include/x86_64-linux-gnu/bits/types/locale_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/__locale_t.h \ + /usr/include/x86_64-linux-gnu/sys/types.h \ + /usr/include/x86_64-linux-gnu/bits/types/clock_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/clockid_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/time_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/timer_t.h \ + /usr/include/endian.h \ + /usr/include/x86_64-linux-gnu/bits/endian.h \ + /usr/include/x86_64-linux-gnu/bits/byteswap.h \ + /usr/include/x86_64-linux-gnu/bits/uintn-identity.h \ + /usr/include/x86_64-linux-gnu/sys/select.h \ + /usr/include/x86_64-linux-gnu/bits/select.h \ + /usr/include/x86_64-linux-gnu/bits/types/sigset_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/__sigset_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/struct_timeval.h \ + /usr/include/x86_64-linux-gnu/bits/types/struct_timespec.h \ + /usr/include/x86_64-linux-gnu/bits/pthreadtypes.h \ + /usr/include/x86_64-linux-gnu/bits/thread-shared-types.h \ + /usr/include/x86_64-linux-gnu/bits/pthreadtypes-arch.h \ + /usr/include/alloca.h \ + /usr/include/x86_64-linux-gnu/bits/stdlib-bsearch.h \ + /usr/include/x86_64-linux-gnu/bits/stdlib-float.h \ + /usr/include/stdio.h \ + /usr/lib/gcc/x86_64-linux-gnu/8/include/stdarg.h \ + /usr/include/x86_64-linux-gnu/bits/types/__fpos_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/__mbstate_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/__fpos64_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/__FILE.h \ + /usr/include/x86_64-linux-gnu/bits/types/FILE.h \ + /usr/include/x86_64-linux-gnu/bits/types/struct_FILE.h \ + /usr/include/x86_64-linux-gnu/bits/types/cookie_io_functions_t.h \ + /usr/include/x86_64-linux-gnu/bits/stdio_lim.h \ + /usr/include/x86_64-linux-gnu/bits/sys_errlist.h \ + /usr/include/x86_64-linux-gnu/bits/stdio.h \ + /usr/include/string.h \ + /usr/include/strings.h \ + /usr/include/x86_64-linux-gnu/sys/mman.h \ + /usr/include/x86_64-linux-gnu/bits/mman.h \ + /usr/include/x86_64-linux-gnu/bits/mman-linux.h \ + /usr/include/x86_64-linux-gnu/bits/mman-shared.h \ + /usr/include/fcntl.h \ + /usr/include/x86_64-linux-gnu/bits/fcntl.h \ + /usr/include/x86_64-linux-gnu/bits/fcntl-linux.h \ + /usr/include/x86_64-linux-gnu/bits/types/struct_iovec.h \ + /usr/include/linux/falloc.h \ + /usr/include/x86_64-linux-gnu/bits/stat.h \ + /usr/include/byteswap.h \ + /usr/include/time.h \ + /usr/include/x86_64-linux-gnu/bits/time.h \ + /usr/include/x86_64-linux-gnu/bits/timex.h \ + /usr/include/x86_64-linux-gnu/bits/types/struct_tm.h \ + /usr/include/x86_64-linux-gnu/bits/types/struct_itimerspec.h \ + tools/fdt_host.h \ + tools/../include/linux/libfdt.h \ + tools/../include/linux/../../scripts/dtc/libfdt/libfdt.h \ + tools/../include/linux/../../scripts/dtc/libfdt/libfdt_env.h \ + tools/../include/linux/../../scripts/dtc/libfdt/fdt.h \ + tools/../include/fdt_support.h \ + $(wildcard include/config/of/libfdt.h) \ + $(wildcard include/config/arch/fixup/fdt/memory.h) \ + $(wildcard include/config/usb/ehci/fsl.h) \ + $(wildcard include/config/usb/xhci/fsl.h) \ + $(wildcard include/config/sys/fsl/sec/compat.h) \ + $(wildcard include/config/pci.h) \ + $(wildcard include/config/sys/fdt/pad.h) \ + $(wildcard include/config/of/board/setup.h) \ + $(wildcard include/config/of/system/setup.h) \ + $(wildcard include/config/fdt/fixup/partitions.h) \ + $(wildcard include/config/fman/enet.h) \ + $(wildcard include/config/fsl/mc/enet.h) \ + tools/../scripts/dtc/libfdt/fdt_empty_tree.c \ + tools/../scripts/dtc/libfdt/libfdt_env.h \ + scripts/dtc/libfdt/fdt.h \ + scripts/dtc/libfdt/libfdt.h \ + tools/../scripts/dtc/libfdt/libfdt_internal.h \ + +tools/libfdt/fdt_empty_tree.o: $(deps_tools/libfdt/fdt_empty_tree.o) + +$(deps_tools/libfdt/fdt_empty_tree.o): diff --git a/tools/u-boot-tools/libfdt/.fdt_overlay.o.cmd b/tools/u-boot-tools/libfdt/.fdt_overlay.o.cmd new file mode 100644 index 0000000..70e8e2c --- /dev/null +++ b/tools/u-boot-tools/libfdt/.fdt_overlay.o.cmd @@ -0,0 +1,114 @@ +cmd_tools/libfdt/fdt_overlay.o := cc -Wp,-MD,tools/libfdt/.fdt_overlay.o.d -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -std=gnu11 -include ./include/compiler.h -idirafterinclude -idirafter./arch/arm/include -I./scripts/dtc/libfdt -I./tools -DUSE_HOSTCC -D__KERNEL_STRICT_NAMES -D_GNU_SOURCE -c -o tools/libfdt/fdt_overlay.o tools/libfdt/fdt_overlay.c + +source_tools/libfdt/fdt_overlay.o := tools/libfdt/fdt_overlay.c + +deps_tools/libfdt/fdt_overlay.o := \ + /usr/include/stdc-predef.h \ + include/compiler.h \ + /usr/lib/gcc/x86_64-linux-gnu/8/include/stddef.h \ + /usr/lib/gcc/x86_64-linux-gnu/8/include/stdint.h \ + /usr/include/stdint.h \ + /usr/include/x86_64-linux-gnu/bits/libc-header-start.h \ + /usr/include/features.h \ + /usr/include/x86_64-linux-gnu/sys/cdefs.h \ + /usr/include/x86_64-linux-gnu/bits/wordsize.h \ + /usr/include/x86_64-linux-gnu/bits/long-double.h \ + /usr/include/x86_64-linux-gnu/gnu/stubs.h \ + /usr/include/x86_64-linux-gnu/gnu/stubs-64.h \ + /usr/include/x86_64-linux-gnu/bits/types.h \ + /usr/include/x86_64-linux-gnu/bits/typesizes.h \ + /usr/include/x86_64-linux-gnu/bits/wchar.h \ + /usr/include/x86_64-linux-gnu/bits/stdint-intn.h \ + /usr/include/x86_64-linux-gnu/bits/stdint-uintn.h \ + /usr/include/errno.h \ + /usr/include/x86_64-linux-gnu/bits/errno.h \ + /usr/include/linux/errno.h \ + /usr/include/x86_64-linux-gnu/asm/errno.h \ + /usr/include/asm-generic/errno.h \ + /usr/include/asm-generic/errno-base.h \ + /usr/include/x86_64-linux-gnu/bits/types/error_t.h \ + /usr/include/stdlib.h \ + /usr/include/x86_64-linux-gnu/bits/waitflags.h \ + /usr/include/x86_64-linux-gnu/bits/waitstatus.h \ + /usr/include/x86_64-linux-gnu/bits/floatn.h \ + /usr/include/x86_64-linux-gnu/bits/floatn-common.h \ + /usr/include/x86_64-linux-gnu/bits/types/locale_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/__locale_t.h \ + /usr/include/x86_64-linux-gnu/sys/types.h \ + /usr/include/x86_64-linux-gnu/bits/types/clock_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/clockid_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/time_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/timer_t.h \ + /usr/include/endian.h \ + /usr/include/x86_64-linux-gnu/bits/endian.h \ + /usr/include/x86_64-linux-gnu/bits/byteswap.h \ + /usr/include/x86_64-linux-gnu/bits/uintn-identity.h \ + /usr/include/x86_64-linux-gnu/sys/select.h \ + /usr/include/x86_64-linux-gnu/bits/select.h \ + /usr/include/x86_64-linux-gnu/bits/types/sigset_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/__sigset_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/struct_timeval.h \ + /usr/include/x86_64-linux-gnu/bits/types/struct_timespec.h \ + /usr/include/x86_64-linux-gnu/bits/pthreadtypes.h \ + /usr/include/x86_64-linux-gnu/bits/thread-shared-types.h \ + /usr/include/x86_64-linux-gnu/bits/pthreadtypes-arch.h \ + /usr/include/alloca.h \ + /usr/include/x86_64-linux-gnu/bits/stdlib-bsearch.h \ + /usr/include/x86_64-linux-gnu/bits/stdlib-float.h \ + /usr/include/stdio.h \ + /usr/lib/gcc/x86_64-linux-gnu/8/include/stdarg.h \ + /usr/include/x86_64-linux-gnu/bits/types/__fpos_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/__mbstate_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/__fpos64_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/__FILE.h \ + /usr/include/x86_64-linux-gnu/bits/types/FILE.h \ + /usr/include/x86_64-linux-gnu/bits/types/struct_FILE.h \ + /usr/include/x86_64-linux-gnu/bits/types/cookie_io_functions_t.h \ + /usr/include/x86_64-linux-gnu/bits/stdio_lim.h \ + /usr/include/x86_64-linux-gnu/bits/sys_errlist.h \ + /usr/include/x86_64-linux-gnu/bits/stdio.h \ + /usr/include/string.h \ + /usr/include/strings.h \ + /usr/include/x86_64-linux-gnu/sys/mman.h \ + /usr/include/x86_64-linux-gnu/bits/mman.h \ + /usr/include/x86_64-linux-gnu/bits/mman-linux.h \ + /usr/include/x86_64-linux-gnu/bits/mman-shared.h \ + /usr/include/fcntl.h \ + /usr/include/x86_64-linux-gnu/bits/fcntl.h \ + /usr/include/x86_64-linux-gnu/bits/fcntl-linux.h \ + /usr/include/x86_64-linux-gnu/bits/types/struct_iovec.h \ + /usr/include/linux/falloc.h \ + /usr/include/x86_64-linux-gnu/bits/stat.h \ + /usr/include/byteswap.h \ + /usr/include/time.h \ + /usr/include/x86_64-linux-gnu/bits/time.h \ + /usr/include/x86_64-linux-gnu/bits/timex.h \ + /usr/include/x86_64-linux-gnu/bits/types/struct_tm.h \ + /usr/include/x86_64-linux-gnu/bits/types/struct_itimerspec.h \ + tools/fdt_host.h \ + tools/../include/linux/libfdt.h \ + tools/../include/linux/../../scripts/dtc/libfdt/libfdt.h \ + tools/../include/linux/../../scripts/dtc/libfdt/libfdt_env.h \ + tools/../include/linux/../../scripts/dtc/libfdt/fdt.h \ + tools/../include/fdt_support.h \ + $(wildcard include/config/of/libfdt.h) \ + $(wildcard include/config/arch/fixup/fdt/memory.h) \ + $(wildcard include/config/usb/ehci/fsl.h) \ + $(wildcard include/config/usb/xhci/fsl.h) \ + $(wildcard include/config/sys/fsl/sec/compat.h) \ + $(wildcard include/config/pci.h) \ + $(wildcard include/config/sys/fdt/pad.h) \ + $(wildcard include/config/of/board/setup.h) \ + $(wildcard include/config/of/system/setup.h) \ + $(wildcard include/config/fdt/fixup/partitions.h) \ + $(wildcard include/config/fman/enet.h) \ + $(wildcard include/config/fsl/mc/enet.h) \ + tools/../scripts/dtc/libfdt/fdt_overlay.c \ + tools/../scripts/dtc/libfdt/libfdt_env.h \ + scripts/dtc/libfdt/fdt.h \ + scripts/dtc/libfdt/libfdt.h \ + tools/../scripts/dtc/libfdt/libfdt_internal.h \ + +tools/libfdt/fdt_overlay.o: $(deps_tools/libfdt/fdt_overlay.o) + +$(deps_tools/libfdt/fdt_overlay.o): diff --git a/tools/u-boot-tools/libfdt/.fdt_rw.o.cmd b/tools/u-boot-tools/libfdt/.fdt_rw.o.cmd new file mode 100644 index 0000000..61f53f4 --- /dev/null +++ b/tools/u-boot-tools/libfdt/.fdt_rw.o.cmd @@ -0,0 +1,114 @@ +cmd_tools/libfdt/fdt_rw.o := cc -Wp,-MD,tools/libfdt/.fdt_rw.o.d -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -std=gnu11 -include ./include/compiler.h -idirafterinclude -idirafter./arch/arm/include -I./scripts/dtc/libfdt -I./tools -DUSE_HOSTCC -D__KERNEL_STRICT_NAMES -D_GNU_SOURCE -c -o tools/libfdt/fdt_rw.o tools/libfdt/fdt_rw.c + +source_tools/libfdt/fdt_rw.o := tools/libfdt/fdt_rw.c + +deps_tools/libfdt/fdt_rw.o := \ + /usr/include/stdc-predef.h \ + include/compiler.h \ + /usr/lib/gcc/x86_64-linux-gnu/8/include/stddef.h \ + /usr/lib/gcc/x86_64-linux-gnu/8/include/stdint.h \ + /usr/include/stdint.h \ + /usr/include/x86_64-linux-gnu/bits/libc-header-start.h \ + /usr/include/features.h \ + /usr/include/x86_64-linux-gnu/sys/cdefs.h \ + /usr/include/x86_64-linux-gnu/bits/wordsize.h \ + /usr/include/x86_64-linux-gnu/bits/long-double.h \ + /usr/include/x86_64-linux-gnu/gnu/stubs.h \ + /usr/include/x86_64-linux-gnu/gnu/stubs-64.h \ + /usr/include/x86_64-linux-gnu/bits/types.h \ + /usr/include/x86_64-linux-gnu/bits/typesizes.h \ + /usr/include/x86_64-linux-gnu/bits/wchar.h \ + /usr/include/x86_64-linux-gnu/bits/stdint-intn.h \ + /usr/include/x86_64-linux-gnu/bits/stdint-uintn.h \ + /usr/include/errno.h \ + /usr/include/x86_64-linux-gnu/bits/errno.h \ + /usr/include/linux/errno.h \ + /usr/include/x86_64-linux-gnu/asm/errno.h \ + /usr/include/asm-generic/errno.h \ + /usr/include/asm-generic/errno-base.h \ + /usr/include/x86_64-linux-gnu/bits/types/error_t.h \ + /usr/include/stdlib.h \ + /usr/include/x86_64-linux-gnu/bits/waitflags.h \ + /usr/include/x86_64-linux-gnu/bits/waitstatus.h \ + /usr/include/x86_64-linux-gnu/bits/floatn.h \ + /usr/include/x86_64-linux-gnu/bits/floatn-common.h \ + /usr/include/x86_64-linux-gnu/bits/types/locale_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/__locale_t.h \ + /usr/include/x86_64-linux-gnu/sys/types.h \ + /usr/include/x86_64-linux-gnu/bits/types/clock_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/clockid_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/time_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/timer_t.h \ + /usr/include/endian.h \ + /usr/include/x86_64-linux-gnu/bits/endian.h \ + /usr/include/x86_64-linux-gnu/bits/byteswap.h \ + /usr/include/x86_64-linux-gnu/bits/uintn-identity.h \ + /usr/include/x86_64-linux-gnu/sys/select.h \ + /usr/include/x86_64-linux-gnu/bits/select.h \ + /usr/include/x86_64-linux-gnu/bits/types/sigset_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/__sigset_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/struct_timeval.h \ + /usr/include/x86_64-linux-gnu/bits/types/struct_timespec.h \ + /usr/include/x86_64-linux-gnu/bits/pthreadtypes.h \ + /usr/include/x86_64-linux-gnu/bits/thread-shared-types.h \ + /usr/include/x86_64-linux-gnu/bits/pthreadtypes-arch.h \ + /usr/include/alloca.h \ + /usr/include/x86_64-linux-gnu/bits/stdlib-bsearch.h \ + /usr/include/x86_64-linux-gnu/bits/stdlib-float.h \ + /usr/include/stdio.h \ + /usr/lib/gcc/x86_64-linux-gnu/8/include/stdarg.h \ + /usr/include/x86_64-linux-gnu/bits/types/__fpos_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/__mbstate_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/__fpos64_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/__FILE.h \ + /usr/include/x86_64-linux-gnu/bits/types/FILE.h \ + /usr/include/x86_64-linux-gnu/bits/types/struct_FILE.h \ + /usr/include/x86_64-linux-gnu/bits/types/cookie_io_functions_t.h \ + /usr/include/x86_64-linux-gnu/bits/stdio_lim.h \ + /usr/include/x86_64-linux-gnu/bits/sys_errlist.h \ + /usr/include/x86_64-linux-gnu/bits/stdio.h \ + /usr/include/string.h \ + /usr/include/strings.h \ + /usr/include/x86_64-linux-gnu/sys/mman.h \ + /usr/include/x86_64-linux-gnu/bits/mman.h \ + /usr/include/x86_64-linux-gnu/bits/mman-linux.h \ + /usr/include/x86_64-linux-gnu/bits/mman-shared.h \ + /usr/include/fcntl.h \ + /usr/include/x86_64-linux-gnu/bits/fcntl.h \ + /usr/include/x86_64-linux-gnu/bits/fcntl-linux.h \ + /usr/include/x86_64-linux-gnu/bits/types/struct_iovec.h \ + /usr/include/linux/falloc.h \ + /usr/include/x86_64-linux-gnu/bits/stat.h \ + /usr/include/byteswap.h \ + /usr/include/time.h \ + /usr/include/x86_64-linux-gnu/bits/time.h \ + /usr/include/x86_64-linux-gnu/bits/timex.h \ + /usr/include/x86_64-linux-gnu/bits/types/struct_tm.h \ + /usr/include/x86_64-linux-gnu/bits/types/struct_itimerspec.h \ + tools/fdt_host.h \ + tools/../include/linux/libfdt.h \ + tools/../include/linux/../../scripts/dtc/libfdt/libfdt.h \ + tools/../include/linux/../../scripts/dtc/libfdt/libfdt_env.h \ + tools/../include/linux/../../scripts/dtc/libfdt/fdt.h \ + tools/../include/fdt_support.h \ + $(wildcard include/config/of/libfdt.h) \ + $(wildcard include/config/arch/fixup/fdt/memory.h) \ + $(wildcard include/config/usb/ehci/fsl.h) \ + $(wildcard include/config/usb/xhci/fsl.h) \ + $(wildcard include/config/sys/fsl/sec/compat.h) \ + $(wildcard include/config/pci.h) \ + $(wildcard include/config/sys/fdt/pad.h) \ + $(wildcard include/config/of/board/setup.h) \ + $(wildcard include/config/of/system/setup.h) \ + $(wildcard include/config/fdt/fixup/partitions.h) \ + $(wildcard include/config/fman/enet.h) \ + $(wildcard include/config/fsl/mc/enet.h) \ + tools/libfdt/../../scripts/dtc/libfdt/fdt_rw.c \ + tools/libfdt/../../scripts/dtc/libfdt/libfdt_env.h \ + scripts/dtc/libfdt/fdt.h \ + scripts/dtc/libfdt/libfdt.h \ + tools/libfdt/../../scripts/dtc/libfdt/libfdt_internal.h \ + +tools/libfdt/fdt_rw.o: $(deps_tools/libfdt/fdt_rw.o) + +$(deps_tools/libfdt/fdt_rw.o): diff --git a/tools/u-boot-tools/libfdt/.fdt_strerror.o.cmd b/tools/u-boot-tools/libfdt/.fdt_strerror.o.cmd new file mode 100644 index 0000000..1338e0e --- /dev/null +++ b/tools/u-boot-tools/libfdt/.fdt_strerror.o.cmd @@ -0,0 +1,114 @@ +cmd_tools/libfdt/fdt_strerror.o := cc -Wp,-MD,tools/libfdt/.fdt_strerror.o.d -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -std=gnu11 -include ./include/compiler.h -idirafterinclude -idirafter./arch/arm/include -I./scripts/dtc/libfdt -I./tools -DUSE_HOSTCC -D__KERNEL_STRICT_NAMES -D_GNU_SOURCE -c -o tools/libfdt/fdt_strerror.o tools/libfdt/fdt_strerror.c + +source_tools/libfdt/fdt_strerror.o := tools/libfdt/fdt_strerror.c + +deps_tools/libfdt/fdt_strerror.o := \ + /usr/include/stdc-predef.h \ + include/compiler.h \ + /usr/lib/gcc/x86_64-linux-gnu/8/include/stddef.h \ + /usr/lib/gcc/x86_64-linux-gnu/8/include/stdint.h \ + /usr/include/stdint.h \ + /usr/include/x86_64-linux-gnu/bits/libc-header-start.h \ + /usr/include/features.h \ + /usr/include/x86_64-linux-gnu/sys/cdefs.h \ + /usr/include/x86_64-linux-gnu/bits/wordsize.h \ + /usr/include/x86_64-linux-gnu/bits/long-double.h \ + /usr/include/x86_64-linux-gnu/gnu/stubs.h \ + /usr/include/x86_64-linux-gnu/gnu/stubs-64.h \ + /usr/include/x86_64-linux-gnu/bits/types.h \ + /usr/include/x86_64-linux-gnu/bits/typesizes.h \ + /usr/include/x86_64-linux-gnu/bits/wchar.h \ + /usr/include/x86_64-linux-gnu/bits/stdint-intn.h \ + /usr/include/x86_64-linux-gnu/bits/stdint-uintn.h \ + /usr/include/errno.h \ + /usr/include/x86_64-linux-gnu/bits/errno.h \ + /usr/include/linux/errno.h \ + /usr/include/x86_64-linux-gnu/asm/errno.h \ + /usr/include/asm-generic/errno.h \ + /usr/include/asm-generic/errno-base.h \ + /usr/include/x86_64-linux-gnu/bits/types/error_t.h \ + /usr/include/stdlib.h \ + /usr/include/x86_64-linux-gnu/bits/waitflags.h \ + /usr/include/x86_64-linux-gnu/bits/waitstatus.h \ + /usr/include/x86_64-linux-gnu/bits/floatn.h \ + /usr/include/x86_64-linux-gnu/bits/floatn-common.h \ + /usr/include/x86_64-linux-gnu/bits/types/locale_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/__locale_t.h \ + /usr/include/x86_64-linux-gnu/sys/types.h \ + /usr/include/x86_64-linux-gnu/bits/types/clock_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/clockid_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/time_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/timer_t.h \ + /usr/include/endian.h \ + /usr/include/x86_64-linux-gnu/bits/endian.h \ + /usr/include/x86_64-linux-gnu/bits/byteswap.h \ + /usr/include/x86_64-linux-gnu/bits/uintn-identity.h \ + /usr/include/x86_64-linux-gnu/sys/select.h \ + /usr/include/x86_64-linux-gnu/bits/select.h \ + /usr/include/x86_64-linux-gnu/bits/types/sigset_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/__sigset_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/struct_timeval.h \ + /usr/include/x86_64-linux-gnu/bits/types/struct_timespec.h \ + /usr/include/x86_64-linux-gnu/bits/pthreadtypes.h \ + /usr/include/x86_64-linux-gnu/bits/thread-shared-types.h \ + /usr/include/x86_64-linux-gnu/bits/pthreadtypes-arch.h \ + /usr/include/alloca.h \ + /usr/include/x86_64-linux-gnu/bits/stdlib-bsearch.h \ + /usr/include/x86_64-linux-gnu/bits/stdlib-float.h \ + /usr/include/stdio.h \ + /usr/lib/gcc/x86_64-linux-gnu/8/include/stdarg.h \ + /usr/include/x86_64-linux-gnu/bits/types/__fpos_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/__mbstate_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/__fpos64_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/__FILE.h \ + /usr/include/x86_64-linux-gnu/bits/types/FILE.h \ + /usr/include/x86_64-linux-gnu/bits/types/struct_FILE.h \ + /usr/include/x86_64-linux-gnu/bits/types/cookie_io_functions_t.h \ + /usr/include/x86_64-linux-gnu/bits/stdio_lim.h \ + /usr/include/x86_64-linux-gnu/bits/sys_errlist.h \ + /usr/include/x86_64-linux-gnu/bits/stdio.h \ + /usr/include/string.h \ + /usr/include/strings.h \ + /usr/include/x86_64-linux-gnu/sys/mman.h \ + /usr/include/x86_64-linux-gnu/bits/mman.h \ + /usr/include/x86_64-linux-gnu/bits/mman-linux.h \ + /usr/include/x86_64-linux-gnu/bits/mman-shared.h \ + /usr/include/fcntl.h \ + /usr/include/x86_64-linux-gnu/bits/fcntl.h \ + /usr/include/x86_64-linux-gnu/bits/fcntl-linux.h \ + /usr/include/x86_64-linux-gnu/bits/types/struct_iovec.h \ + /usr/include/linux/falloc.h \ + /usr/include/x86_64-linux-gnu/bits/stat.h \ + /usr/include/byteswap.h \ + /usr/include/time.h \ + /usr/include/x86_64-linux-gnu/bits/time.h \ + /usr/include/x86_64-linux-gnu/bits/timex.h \ + /usr/include/x86_64-linux-gnu/bits/types/struct_tm.h \ + /usr/include/x86_64-linux-gnu/bits/types/struct_itimerspec.h \ + tools/fdt_host.h \ + tools/../include/linux/libfdt.h \ + tools/../include/linux/../../scripts/dtc/libfdt/libfdt.h \ + tools/../include/linux/../../scripts/dtc/libfdt/libfdt_env.h \ + tools/../include/linux/../../scripts/dtc/libfdt/fdt.h \ + tools/../include/fdt_support.h \ + $(wildcard include/config/of/libfdt.h) \ + $(wildcard include/config/arch/fixup/fdt/memory.h) \ + $(wildcard include/config/usb/ehci/fsl.h) \ + $(wildcard include/config/usb/xhci/fsl.h) \ + $(wildcard include/config/sys/fsl/sec/compat.h) \ + $(wildcard include/config/pci.h) \ + $(wildcard include/config/sys/fdt/pad.h) \ + $(wildcard include/config/of/board/setup.h) \ + $(wildcard include/config/of/system/setup.h) \ + $(wildcard include/config/fdt/fixup/partitions.h) \ + $(wildcard include/config/fman/enet.h) \ + $(wildcard include/config/fsl/mc/enet.h) \ + tools/../scripts/dtc/libfdt/fdt_strerror.c \ + tools/../scripts/dtc/libfdt/libfdt_env.h \ + scripts/dtc/libfdt/fdt.h \ + scripts/dtc/libfdt/libfdt.h \ + tools/../scripts/dtc/libfdt/libfdt_internal.h \ + +tools/libfdt/fdt_strerror.o: $(deps_tools/libfdt/fdt_strerror.o) + +$(deps_tools/libfdt/fdt_strerror.o): diff --git a/tools/u-boot-tools/libfdt/.fdt_sw.o.cmd b/tools/u-boot-tools/libfdt/.fdt_sw.o.cmd new file mode 100644 index 0000000..aaa3464 --- /dev/null +++ b/tools/u-boot-tools/libfdt/.fdt_sw.o.cmd @@ -0,0 +1,114 @@ +cmd_tools/libfdt/fdt_sw.o := cc -Wp,-MD,tools/libfdt/.fdt_sw.o.d -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -std=gnu11 -include ./include/compiler.h -idirafterinclude -idirafter./arch/arm/include -I./scripts/dtc/libfdt -I./tools -DUSE_HOSTCC -D__KERNEL_STRICT_NAMES -D_GNU_SOURCE -c -o tools/libfdt/fdt_sw.o tools/libfdt/fdt_sw.c + +source_tools/libfdt/fdt_sw.o := tools/libfdt/fdt_sw.c + +deps_tools/libfdt/fdt_sw.o := \ + /usr/include/stdc-predef.h \ + include/compiler.h \ + /usr/lib/gcc/x86_64-linux-gnu/8/include/stddef.h \ + /usr/lib/gcc/x86_64-linux-gnu/8/include/stdint.h \ + /usr/include/stdint.h \ + /usr/include/x86_64-linux-gnu/bits/libc-header-start.h \ + /usr/include/features.h \ + /usr/include/x86_64-linux-gnu/sys/cdefs.h \ + /usr/include/x86_64-linux-gnu/bits/wordsize.h \ + /usr/include/x86_64-linux-gnu/bits/long-double.h \ + /usr/include/x86_64-linux-gnu/gnu/stubs.h \ + /usr/include/x86_64-linux-gnu/gnu/stubs-64.h \ + /usr/include/x86_64-linux-gnu/bits/types.h \ + /usr/include/x86_64-linux-gnu/bits/typesizes.h \ + /usr/include/x86_64-linux-gnu/bits/wchar.h \ + /usr/include/x86_64-linux-gnu/bits/stdint-intn.h \ + /usr/include/x86_64-linux-gnu/bits/stdint-uintn.h \ + /usr/include/errno.h \ + /usr/include/x86_64-linux-gnu/bits/errno.h \ + /usr/include/linux/errno.h \ + /usr/include/x86_64-linux-gnu/asm/errno.h \ + /usr/include/asm-generic/errno.h \ + /usr/include/asm-generic/errno-base.h \ + /usr/include/x86_64-linux-gnu/bits/types/error_t.h \ + /usr/include/stdlib.h \ + /usr/include/x86_64-linux-gnu/bits/waitflags.h \ + /usr/include/x86_64-linux-gnu/bits/waitstatus.h \ + /usr/include/x86_64-linux-gnu/bits/floatn.h \ + /usr/include/x86_64-linux-gnu/bits/floatn-common.h \ + /usr/include/x86_64-linux-gnu/bits/types/locale_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/__locale_t.h \ + /usr/include/x86_64-linux-gnu/sys/types.h \ + /usr/include/x86_64-linux-gnu/bits/types/clock_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/clockid_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/time_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/timer_t.h \ + /usr/include/endian.h \ + /usr/include/x86_64-linux-gnu/bits/endian.h \ + /usr/include/x86_64-linux-gnu/bits/byteswap.h \ + /usr/include/x86_64-linux-gnu/bits/uintn-identity.h \ + /usr/include/x86_64-linux-gnu/sys/select.h \ + /usr/include/x86_64-linux-gnu/bits/select.h \ + /usr/include/x86_64-linux-gnu/bits/types/sigset_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/__sigset_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/struct_timeval.h \ + /usr/include/x86_64-linux-gnu/bits/types/struct_timespec.h \ + /usr/include/x86_64-linux-gnu/bits/pthreadtypes.h \ + /usr/include/x86_64-linux-gnu/bits/thread-shared-types.h \ + /usr/include/x86_64-linux-gnu/bits/pthreadtypes-arch.h \ + /usr/include/alloca.h \ + /usr/include/x86_64-linux-gnu/bits/stdlib-bsearch.h \ + /usr/include/x86_64-linux-gnu/bits/stdlib-float.h \ + /usr/include/stdio.h \ + /usr/lib/gcc/x86_64-linux-gnu/8/include/stdarg.h \ + /usr/include/x86_64-linux-gnu/bits/types/__fpos_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/__mbstate_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/__fpos64_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/__FILE.h \ + /usr/include/x86_64-linux-gnu/bits/types/FILE.h \ + /usr/include/x86_64-linux-gnu/bits/types/struct_FILE.h \ + /usr/include/x86_64-linux-gnu/bits/types/cookie_io_functions_t.h \ + /usr/include/x86_64-linux-gnu/bits/stdio_lim.h \ + /usr/include/x86_64-linux-gnu/bits/sys_errlist.h \ + /usr/include/x86_64-linux-gnu/bits/stdio.h \ + /usr/include/string.h \ + /usr/include/strings.h \ + /usr/include/x86_64-linux-gnu/sys/mman.h \ + /usr/include/x86_64-linux-gnu/bits/mman.h \ + /usr/include/x86_64-linux-gnu/bits/mman-linux.h \ + /usr/include/x86_64-linux-gnu/bits/mman-shared.h \ + /usr/include/fcntl.h \ + /usr/include/x86_64-linux-gnu/bits/fcntl.h \ + /usr/include/x86_64-linux-gnu/bits/fcntl-linux.h \ + /usr/include/x86_64-linux-gnu/bits/types/struct_iovec.h \ + /usr/include/linux/falloc.h \ + /usr/include/x86_64-linux-gnu/bits/stat.h \ + /usr/include/byteswap.h \ + /usr/include/time.h \ + /usr/include/x86_64-linux-gnu/bits/time.h \ + /usr/include/x86_64-linux-gnu/bits/timex.h \ + /usr/include/x86_64-linux-gnu/bits/types/struct_tm.h \ + /usr/include/x86_64-linux-gnu/bits/types/struct_itimerspec.h \ + tools/fdt_host.h \ + tools/../include/linux/libfdt.h \ + tools/../include/linux/../../scripts/dtc/libfdt/libfdt.h \ + tools/../include/linux/../../scripts/dtc/libfdt/libfdt_env.h \ + tools/../include/linux/../../scripts/dtc/libfdt/fdt.h \ + tools/../include/fdt_support.h \ + $(wildcard include/config/of/libfdt.h) \ + $(wildcard include/config/arch/fixup/fdt/memory.h) \ + $(wildcard include/config/usb/ehci/fsl.h) \ + $(wildcard include/config/usb/xhci/fsl.h) \ + $(wildcard include/config/sys/fsl/sec/compat.h) \ + $(wildcard include/config/pci.h) \ + $(wildcard include/config/sys/fdt/pad.h) \ + $(wildcard include/config/of/board/setup.h) \ + $(wildcard include/config/of/system/setup.h) \ + $(wildcard include/config/fdt/fixup/partitions.h) \ + $(wildcard include/config/fman/enet.h) \ + $(wildcard include/config/fsl/mc/enet.h) \ + tools/../scripts/dtc/libfdt/fdt_sw.c \ + tools/../scripts/dtc/libfdt/libfdt_env.h \ + scripts/dtc/libfdt/fdt.h \ + scripts/dtc/libfdt/libfdt.h \ + tools/../scripts/dtc/libfdt/libfdt_internal.h \ + +tools/libfdt/fdt_sw.o: $(deps_tools/libfdt/fdt_sw.o) + +$(deps_tools/libfdt/fdt_sw.o): diff --git a/tools/u-boot-tools/libfdt/.fdt_wip.o.cmd b/tools/u-boot-tools/libfdt/.fdt_wip.o.cmd new file mode 100644 index 0000000..4793e48 --- /dev/null +++ b/tools/u-boot-tools/libfdt/.fdt_wip.o.cmd @@ -0,0 +1,114 @@ +cmd_tools/libfdt/fdt_wip.o := cc -Wp,-MD,tools/libfdt/.fdt_wip.o.d -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -std=gnu11 -include ./include/compiler.h -idirafterinclude -idirafter./arch/arm/include -I./scripts/dtc/libfdt -I./tools -DUSE_HOSTCC -D__KERNEL_STRICT_NAMES -D_GNU_SOURCE -c -o tools/libfdt/fdt_wip.o tools/libfdt/fdt_wip.c + +source_tools/libfdt/fdt_wip.o := tools/libfdt/fdt_wip.c + +deps_tools/libfdt/fdt_wip.o := \ + /usr/include/stdc-predef.h \ + include/compiler.h \ + /usr/lib/gcc/x86_64-linux-gnu/8/include/stddef.h \ + /usr/lib/gcc/x86_64-linux-gnu/8/include/stdint.h \ + /usr/include/stdint.h \ + /usr/include/x86_64-linux-gnu/bits/libc-header-start.h \ + /usr/include/features.h \ + /usr/include/x86_64-linux-gnu/sys/cdefs.h \ + /usr/include/x86_64-linux-gnu/bits/wordsize.h \ + /usr/include/x86_64-linux-gnu/bits/long-double.h \ + /usr/include/x86_64-linux-gnu/gnu/stubs.h \ + /usr/include/x86_64-linux-gnu/gnu/stubs-64.h \ + /usr/include/x86_64-linux-gnu/bits/types.h \ + /usr/include/x86_64-linux-gnu/bits/typesizes.h \ + /usr/include/x86_64-linux-gnu/bits/wchar.h \ + /usr/include/x86_64-linux-gnu/bits/stdint-intn.h \ + /usr/include/x86_64-linux-gnu/bits/stdint-uintn.h \ + /usr/include/errno.h \ + /usr/include/x86_64-linux-gnu/bits/errno.h \ + /usr/include/linux/errno.h \ + /usr/include/x86_64-linux-gnu/asm/errno.h \ + /usr/include/asm-generic/errno.h \ + /usr/include/asm-generic/errno-base.h \ + /usr/include/x86_64-linux-gnu/bits/types/error_t.h \ + /usr/include/stdlib.h \ + /usr/include/x86_64-linux-gnu/bits/waitflags.h \ + /usr/include/x86_64-linux-gnu/bits/waitstatus.h \ + /usr/include/x86_64-linux-gnu/bits/floatn.h \ + /usr/include/x86_64-linux-gnu/bits/floatn-common.h \ + /usr/include/x86_64-linux-gnu/bits/types/locale_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/__locale_t.h \ + /usr/include/x86_64-linux-gnu/sys/types.h \ + /usr/include/x86_64-linux-gnu/bits/types/clock_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/clockid_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/time_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/timer_t.h \ + /usr/include/endian.h \ + /usr/include/x86_64-linux-gnu/bits/endian.h \ + /usr/include/x86_64-linux-gnu/bits/byteswap.h \ + /usr/include/x86_64-linux-gnu/bits/uintn-identity.h \ + /usr/include/x86_64-linux-gnu/sys/select.h \ + /usr/include/x86_64-linux-gnu/bits/select.h \ + /usr/include/x86_64-linux-gnu/bits/types/sigset_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/__sigset_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/struct_timeval.h \ + /usr/include/x86_64-linux-gnu/bits/types/struct_timespec.h \ + /usr/include/x86_64-linux-gnu/bits/pthreadtypes.h \ + /usr/include/x86_64-linux-gnu/bits/thread-shared-types.h \ + /usr/include/x86_64-linux-gnu/bits/pthreadtypes-arch.h \ + /usr/include/alloca.h \ + /usr/include/x86_64-linux-gnu/bits/stdlib-bsearch.h \ + /usr/include/x86_64-linux-gnu/bits/stdlib-float.h \ + /usr/include/stdio.h \ + /usr/lib/gcc/x86_64-linux-gnu/8/include/stdarg.h \ + /usr/include/x86_64-linux-gnu/bits/types/__fpos_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/__mbstate_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/__fpos64_t.h \ + /usr/include/x86_64-linux-gnu/bits/types/__FILE.h \ + /usr/include/x86_64-linux-gnu/bits/types/FILE.h \ + /usr/include/x86_64-linux-gnu/bits/types/struct_FILE.h \ + /usr/include/x86_64-linux-gnu/bits/types/cookie_io_functions_t.h \ + /usr/include/x86_64-linux-gnu/bits/stdio_lim.h \ + /usr/include/x86_64-linux-gnu/bits/sys_errlist.h \ + /usr/include/x86_64-linux-gnu/bits/stdio.h \ + /usr/include/string.h \ + /usr/include/strings.h \ + /usr/include/x86_64-linux-gnu/sys/mman.h \ + /usr/include/x86_64-linux-gnu/bits/mman.h \ + /usr/include/x86_64-linux-gnu/bits/mman-linux.h \ + /usr/include/x86_64-linux-gnu/bits/mman-shared.h \ + /usr/include/fcntl.h \ + /usr/include/x86_64-linux-gnu/bits/fcntl.h \ + /usr/include/x86_64-linux-gnu/bits/fcntl-linux.h \ + /usr/include/x86_64-linux-gnu/bits/types/struct_iovec.h \ + /usr/include/linux/falloc.h \ + /usr/include/x86_64-linux-gnu/bits/stat.h \ + /usr/include/byteswap.h \ + /usr/include/time.h \ + /usr/include/x86_64-linux-gnu/bits/time.h \ + /usr/include/x86_64-linux-gnu/bits/timex.h \ + /usr/include/x86_64-linux-gnu/bits/types/struct_tm.h \ + /usr/include/x86_64-linux-gnu/bits/types/struct_itimerspec.h \ + tools/fdt_host.h \ + tools/../include/linux/libfdt.h \ + tools/../include/linux/../../scripts/dtc/libfdt/libfdt.h \ + tools/../include/linux/../../scripts/dtc/libfdt/libfdt_env.h \ + tools/../include/linux/../../scripts/dtc/libfdt/fdt.h \ + tools/../include/fdt_support.h \ + $(wildcard include/config/of/libfdt.h) \ + $(wildcard include/config/arch/fixup/fdt/memory.h) \ + $(wildcard include/config/usb/ehci/fsl.h) \ + $(wildcard include/config/usb/xhci/fsl.h) \ + $(wildcard include/config/sys/fsl/sec/compat.h) \ + $(wildcard include/config/pci.h) \ + $(wildcard include/config/sys/fdt/pad.h) \ + $(wildcard include/config/of/board/setup.h) \ + $(wildcard include/config/of/system/setup.h) \ + $(wildcard include/config/fdt/fixup/partitions.h) \ + $(wildcard include/config/fman/enet.h) \ + $(wildcard include/config/fsl/mc/enet.h) \ + tools/../scripts/dtc/libfdt/fdt_wip.c \ + tools/../scripts/dtc/libfdt/libfdt_env.h \ + scripts/dtc/libfdt/fdt.h \ + scripts/dtc/libfdt/libfdt.h \ + tools/../scripts/dtc/libfdt/libfdt_internal.h \ + +tools/libfdt/fdt_wip.o: $(deps_tools/libfdt/fdt_wip.o) + +$(deps_tools/libfdt/fdt_wip.o): diff --git a/tools/u-boot-tools/libfdt/fdt.c b/tools/u-boot-tools/libfdt/fdt.c new file mode 100644 index 0000000..8ba8091 --- /dev/null +++ b/tools/u-boot-tools/libfdt/fdt.c @@ -0,0 +1,2 @@ +#include "fdt_host.h" +#include "../scripts/dtc/libfdt/fdt.c" diff --git a/tools/u-boot-tools/libfdt/fdt.o b/tools/u-boot-tools/libfdt/fdt.o new file mode 100644 index 0000000000000000000000000000000000000000..4072bffc3b0f4bf11861258cd708e9cc89ea557e GIT binary patch literal 4728 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 AtpET3 literal 0 HcmV?d00001 diff --git a/tools/u-boot-tools/libfdt/fdt_addresses.c b/tools/u-boot-tools/libfdt/fdt_addresses.c new file mode 100644 index 0000000..242a2c0 --- /dev/null +++ b/tools/u-boot-tools/libfdt/fdt_addresses.c @@ -0,0 +1,2 @@ +#include "fdt_host.h" +#include "../scripts/dtc/libfdt/fdt_addresses.c" diff --git a/tools/u-boot-tools/libfdt/fdt_addresses.o b/tools/u-boot-tools/libfdt/fdt_addresses.o new file mode 100644 index 0000000000000000000000000000000000000000..182d78a2b083e3dd8e3f48ef140c90b2200020fd GIT binary patch literal 1976 zcmb<-^>JfjWMqH=Mg}_u1P><4z)-=0U^{@B4h*~uJPaPqZzMc=ML{ASy*?^DFTkwU z10{CNH7YzTrS9DVofkSU9(*Zr@PP#93I1*84!+_z_)wtn5yO852F??l7kUH!H~(VU z_38ir|NPE}JerS49Ojp2*uliWzyRi?@yoj~Fzf)yzkWD@a{ItGHUD6RSq3#9t9|#B z6H`)(Qj3dqlT&kYiW!uPGpkaeYzB8{XDbB_m(--p#5@HHJ!3rsT{BGxgMmQ=6b=jw z42)Gl42%^5jM6;p91|EB7!(*77-XPw`5@YbPk||bi;u&F+mnlr!=0M}q6QMi3=D8J zzF=9n8jx0O7!-Fb3?KhvlVipjvkVN(49wV+GcYqCMLLoSW(HOy9)iitz>eS`v6vY+ zka!TLAeh25M+_>?52jHGW(GD?0R{#JX$A%cAtZl+!;67|L6?DnK>$NF1A{XTaet^e z1yDUOa~RT6O5&l3AhlR8nL*FT*#JZsBFn@>l1IF|kH3?nPrSdITd-?Le2Alyk83<w zU3zLsK~a7Im<>upPz?<55CbJYP$>QR4*`(yVF1SfF9QPuOuQWGK9G7&B=JV5I4H)D z)q~O-ND8DLgh6Z&b^#F#3=AMK5Qh1iL9e(nw<Ix%L9e)^2tsGTSVgHhiFzfe6(tOM zDTyVC40=h$#SD5y`5+ERxuKpRgI;oeZf<H`34>l<eo3mHyI-hoaY<rwHiI5mcWOp_ zT2W$dD%5uJDUiQFX&pU9!1B#~G^5ee0w~>p!V0Dyrn~`~6%wEVT%bJ6zyMFd5>Rn9 zs5n$PRFr{19^@!E55Yhe)`6Nyu6{$P{YD5Yz$}>kApd~{pajf*7!C3_h;0Cshf!7_ zK?Vi}S(pHnh6=-TnkUqLs5G1khbe@MKp5!4aZvrBv<FcEAtRs)Vc`!EMT9?$AISj8 z8z6tc#6a`|Xn?}}1&TjVz5-#8JS_e|)}Y%BlHUQ6U|?X#fYPAy7=+RFgYq8KDv&lf kR)7kE{0A3=`W2ZE%HL3>$ifT)phO9>A4QY_9_}z<0NR)Awg3PC literal 0 HcmV?d00001 diff --git a/tools/u-boot-tools/libfdt/fdt_empty_tree.c b/tools/u-boot-tools/libfdt/fdt_empty_tree.c new file mode 100644 index 0000000..9ccbb1f --- /dev/null +++ b/tools/u-boot-tools/libfdt/fdt_empty_tree.c @@ -0,0 +1,2 @@ +#include "fdt_host.h" +#include "../scripts/dtc/libfdt/fdt_empty_tree.c" diff --git a/tools/u-boot-tools/libfdt/fdt_empty_tree.o b/tools/u-boot-tools/libfdt/fdt_empty_tree.o new file mode 100644 index 0000000000000000000000000000000000000000..281d85aa8cb78815ab1a5ed3cf34b44c4382a391 GIT binary patch literal 2000 zcmb<-^>JfjWMqH=Mg}_u1P><4z%YRY!FB*M9T<2Sco;%EzXp4B{`P2o!|?(n+<Ksd z$D{cOM|A9Aet8!L29M7BQ1Q|i9=)a@B}hV7k=Q4Z*n2@nbiVTFyyww*KRWg$NE-u# zyR);Ef`&_KQf6YFf`y*3o`J5JCWOHt0#U-iSQW&;SRueD&BM+yfsuj1gn@xU1}c{Y zq8<4J+L)aAB$}Ds`4oJ)_#_<p1RVJ|+_<axBs{@PkSd%Q6gDgjAOB-hz>GEI7#Nrt zn6W8mU}iuHR3sJ546H~z1e2M89l=3jF*9%=@gPhgFonw;Hn1!*A<n?SAcW+8a9A-g zFz7NcFbE(kgtDD+i2LCXPr)Hxj6=Kuhj=#*afY;%lK9lzf|APklA_d9y<`SGA7=xw zXmU|%Vo55JBtyKrkH3?nPrSdITd-?Le2Alyk83=VS}-#$GcU6^BfcoLIJKxOH8-&U zESi*>o|zY)m!Faf=BDPQAUF^mVDbEd)V%o2ypnuqa53;e8K4*kvH$#s02L&0UIqpR zn7BSvoQZ(}9J?@abEr7T96luVp!fhufyDWd#Qi~n3=H6u28u_RIcZ4Zf=KEWpyD8V zK^V$qV9+bB%q>YwV$drtDT2@$Fji4&PNH5(YDEcyUP@v~B7<I1aWR8lQ9g(RQf{bc z$e@>;pPQSSSHhr|mtT^q=k6D(TU?TuoXwyI)}5LWpH`HZn+ms%Tn5PfpgfA6!XafA z0|P8w!^{VT35*S*rJ-R3Qx6gY;S6XNUIF4TFfed2Fo1IlEayo;#nqtVP~}ij28LB| z7AgbeR%8(!Xnd2a-w<lQ5wf8S2>a3PgYjWB$loBg0h<2RP`)gbhEX83AU2HlgxU`h zL&hhNIY?}nKA2D(R6k1EV_;x_=?{P@gwn9^hjJMram1ehC}S`%Fu>vo6n`MSAPmwA z69>5m-EOcvNP>ZZVFQ%rhNb~@{hSP-@*boE6`z0_IulhGoQ9BvKzR{8?m_YzQ1@qm LI7k@k9JmMoPkG@# literal 0 HcmV?d00001 diff --git a/tools/u-boot-tools/libfdt/fdt_overlay.c b/tools/u-boot-tools/libfdt/fdt_overlay.c new file mode 100644 index 0000000..801ec37 --- /dev/null +++ b/tools/u-boot-tools/libfdt/fdt_overlay.c @@ -0,0 +1,2 @@ +#include "fdt_host.h" +#include "../scripts/dtc/libfdt/fdt_overlay.c" diff --git a/tools/u-boot-tools/libfdt/fdt_overlay.o b/tools/u-boot-tools/libfdt/fdt_overlay.o new file mode 100644 index 0000000000000000000000000000000000000000..81e3226f31a89a89318b4a0f4708d959a5c45b8a GIT binary patch literal 9128 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 C5G4Wt literal 0 HcmV?d00001 diff --git a/tools/u-boot-tools/libfdt/fdt_rw.c b/tools/u-boot-tools/libfdt/fdt_rw.c new file mode 100644 index 0000000..68fc7c8 --- /dev/null +++ b/tools/u-boot-tools/libfdt/fdt_rw.c @@ -0,0 +1,35 @@ +/* SPDX-License-Identifier: GPL-2.0+ BSD-2-Clause */ +#include "fdt_host.h" +#include "../../scripts/dtc/libfdt/fdt_rw.c" + +int fdt_remove_unused_strings(const void *old, void *new) +{ + const struct fdt_property *old_prop; + struct fdt_property *new_prop; + int size = fdt_totalsize(old); + int next_offset, offset; + const char *str; + int ret; + int tag = FDT_PROP; + + /* Make a copy and remove the strings */ + memcpy(new, old, size); + fdt_set_size_dt_strings(new, 0); + + /* Add every property name back into the new string table */ + for (offset = 0; tag != FDT_END; offset = next_offset) { + tag = fdt_next_tag(old, offset, &next_offset); + if (tag != FDT_PROP) + continue; + old_prop = fdt_get_property_by_offset(old, offset, NULL); + new_prop = (struct fdt_property *)(unsigned long) + fdt_get_property_by_offset(new, offset, NULL); + str = fdt_string(old, fdt32_to_cpu(old_prop->nameoff)); + ret = fdt_find_add_string_(new, str); + if (ret < 0) + return ret; + new_prop->nameoff = cpu_to_fdt32(ret); + } + + return 0; +} diff --git a/tools/u-boot-tools/libfdt/fdt_rw.o b/tools/u-boot-tools/libfdt/fdt_rw.o new file mode 100644 index 0000000000000000000000000000000000000000..2b20ccfc8b3fe7f2d64449e6b6aafa0e85f12157 GIT binary patch literal 10144 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 E0C{bdmH+?% literal 0 HcmV?d00001 diff --git a/tools/u-boot-tools/libfdt/fdt_strerror.c b/tools/u-boot-tools/libfdt/fdt_strerror.c new file mode 100644 index 0000000..d7ed70b --- /dev/null +++ b/tools/u-boot-tools/libfdt/fdt_strerror.c @@ -0,0 +1,2 @@ +#include "fdt_host.h" +#include "../scripts/dtc/libfdt/fdt_strerror.c" diff --git a/tools/u-boot-tools/libfdt/fdt_strerror.o b/tools/u-boot-tools/libfdt/fdt_strerror.o new file mode 100644 index 0000000000000000000000000000000000000000..1aceaa2500a05a25b361b0926de837064303eeee GIT binary patch literal 2768 zcmb<-^>JfjWMqH=Mg}_u1P><4z#zbmU^{@B4h;MZd<-7FtRP<N|9WF6vqTHZX#W4M zOz!)AD9@u;6r|82`M*au%MXv%10MV?hYs76=4I#Qm***@78T_e*)iDU<-?g}i8+}m z3i)Yi#i=FwIjMQ+B^h=MZZ0A5u0cWZe*PhD{-J&@Z~@l{&)|??IM>fVIKa`_70!2Z zbn$m{3w8}b771_+@j&5wIQqHxplAvXaSTBSgan29IXi~9BCK_Cbn$g`_jE=!Cd@S` z*wfz+StvLpDAYM5GzejolcS4|W2Ap5!a<&XA+AAwjy}lx{G45Vd=S2Ja&!rE^a({V z#Xrn7$j31fuFKCq0OE~cP<RhI?C$JrrJ&)Gnv|KCr(mIHtY@HWrU_wy+$sVh7#J8B ztAZF9D+Cy&dDuB7FfuTJ#ATpz_D~uh1xmv#3?KhvQ^Slkg)=ZPGcaRQ&cMvTf<qjf z_EF>nKyGJXgNsdovq0t}#F2y<m>D>cxo|c!0~eePW5|LTtPIj%8bK(5nXC*T`@u{o zp$?`o-3bzAX5au(C|HMqfk6l<eBfc7mQoU5Tv7zir+Uc@AZ}_=QAuJ_PAY?*kFybo zFa!|>U^$plnBlOHWny3e`|8hs2;hS%0Eu%mFfhQx#gW8$ki=Dy#6d+8Og+fmASsYJ z$m(G}1f>U1C>VkiF)+Zx2`26Y6_<e0F!4YrUja(P#1o->4JZv0FNX3BpfpUp8OpbS z(lGI9P(GwgWdMgf$eqif;vP^MrhX@s9{{Cc;-{ee2q+B`zYFCjKxvrxCn!GyO2foi zKnw;3h5|HkQK)zYnz%Ytya7$z0xI5tChiRthlM97zCakNjDbO~xH7jSF^NI1xCE5C z81#xWV639loJ75n)QS=Yy_Cd~L<YU2;$jB9qI?hsq}))?kU<Zu2rN>Rnxj{gua}db zoS4I)mz<xQo0?a`pqH0llB(zK7phxal9-$g)sUJIpH`HZn+kUZxeSoMKwd=8>9F{P z=O?HI==l|tuR)~^Og&T?Lk38Yfq?-Q{-87tvImyFVd+*4T>gN{8kkN9T@Rr!NRS!G zf}nf^G82|xVRFRk2jwr2nMN>;P#UH`4kQT`15q$8jCRH0{s&M6$mJ<WKPbFGGz^2n z4HQ%$8YTv!AAkfI7#L<i%>jiUD2_mW1<Aw050rmEN?`dHBnQF;&_oI;e?cM;3{neX z!Dvvq0Aqv1VYnBnAI68#=xWQL{zq3n0V;s*E|~ja<`pt9fNLvs|5t!K%fP?@^C!sv zpxOndA0`fR57a7{kzo1&R4@aifPsO5mw|x+RCc552i0WgX$NG01~mLFK^!CuH4ZK! W$bgx4L6Q@o?uV5#ATba|*AD<H1`M|V literal 0 HcmV?d00001 diff --git a/tools/u-boot-tools/libfdt/fdt_sw.c b/tools/u-boot-tools/libfdt/fdt_sw.c new file mode 100644 index 0000000..ed6b327 --- /dev/null +++ b/tools/u-boot-tools/libfdt/fdt_sw.c @@ -0,0 +1,2 @@ +#include "fdt_host.h" +#include "../scripts/dtc/libfdt/fdt_sw.c" diff --git a/tools/u-boot-tools/libfdt/fdt_sw.o b/tools/u-boot-tools/libfdt/fdt_sw.o new file mode 100644 index 0000000000000000000000000000000000000000..7eca52ee1ed2dd33a13b7023ad2605d14f134d9e GIT binary patch literal 5344 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 DFZ`u9 literal 0 HcmV?d00001 diff --git a/tools/u-boot-tools/libfdt/fdt_wip.c b/tools/u-boot-tools/libfdt/fdt_wip.c new file mode 100644 index 0000000..bad73ed --- /dev/null +++ b/tools/u-boot-tools/libfdt/fdt_wip.c @@ -0,0 +1,2 @@ +#include "fdt_host.h" +#include "../scripts/dtc/libfdt/fdt_wip.c" diff --git a/tools/u-boot-tools/libfdt/fdt_wip.o b/tools/u-boot-tools/libfdt/fdt_wip.o new file mode 100644 index 0000000000000000000000000000000000000000..23c66d0bcb025fb283ad3d340896685799bbc629 GIT binary patch literal 2800 zcmb<-^>JfjWMqH=Mg}_u1P><4!0>?`!FB*M9T<2RxEUNnd^^vCx^x~5_Go@1;M41( z!qXk2V(<c_#G~~<iF&VywsnXKPq|*_dynMHKAq1z7>|Qx4G(xU9}$R-b&NU8FVE2J zqQZmB-|_eV|NpP=PH+tK>^$cf>e_kKF~qU+OQ=WZ-(Zi<%OJ}<dVN%Qz(#>AvU9P_ zQQ;}|_vpM25o><K;n8`^xAUW8Xy-v!!vijz&pbNcgXN+kVOBZDI>sI5mv=z1A1c@Y z@&m*j7t;9UTR>a}kLEWLs21yX`>62r2J(0$U-Do)<Y9TCScqSq;W$V&i%0VT77xn{ zMIXVQl{lO>0qReQ!~F7K^Fup7fI?2<xQhx8NK5Ot3RREJ-yXd=Dm)-}d33(&JO=Yq zcZdp4>!k{o)&rH#J(`b5M8_U>4Dsyz6WaMT80@ju0~Pw3FAOhrzVqpP-gy`-=8=3E zpS!>jz~JueY^9*#lA4s6n5ST&XRK$SYo-ZdfLz7EAOfKn7^{L97%K!ArFqynCNMHE zfYO2tR4yJwJMjthFuCwav@yHzDKxXV@EH_w@hLd+NjUNeIPr0~aC@L~85kH6pctgR z9Yi?s3G^|!@=5eEyYeaYu(<GPw6VJI88oxG^Eo8(d3f*z1oC+}@;SJ0@fkSsX*lvJ zIPpn1fz5XXnU5~$!OZ}2;}MYA3=9mOAlijbp^}S_!<pNIkAs1ML4|>V;R#5b0pceH zM?QfzCTBj0W@Z;YgHo{b9r-vIbQl;Ic%T`?4Wu67UuTeiE5RzAz-EG^abb`VEDRt2 zW0Pmbngbaam>HO{DQ942V8J2IhC>`FGb5>BW?)6)L73bO3=Bd@?g7U!0|SFT0|SEq zL<tJ%ibFgShd3ziLFR*KkU`-1XJB9`!J!_MX|UM~(g!l<1IQ3q95XO5%)nvJN~pga zKoJ3pa|Q;6eK^#gfvRtSst3nC0|UcN9O@a;QcB{>GYj;R!K~ucl7gcAg80n5f}F(U z)cCx_+|-=Zy!e8|qLR$S9ENyzAAcuDpLl;aw_w+h_z*`YAJ=%W&UC0ws1AnQ)ZFBP zO0X1~p<pqna)#oPqMXz`uuxupK|ILv)S?oIzVy@*RDrzwl+^gtyp;I-w6x;X5{Sil zsTC#hAQ`wJa1K;iK}ivV1T?%sDUgeSf#J`82rxqu2c;pHcq5WHFOvGHNaB1*;v12~ zLHQJB&Mzc!P<nugXF}r#<Q^d;bGnek1(C$(A&G;^IG8yKQ1h7>7#Khp%4JA_GC*P= z3`@5s(8OWs5L^U9%>b!~r8fbny&y3VhNUY8z2eH;lEfqiz2cH02%Q0A6{Y4R>XoEc zlrZR}B$gyH=p_{wGw3Df=jNv7l`!b#<(H)Dx%-9c7MCO@XEW%5m8WLJrxhjUrb3-Z zF$D@2kkipi7EnC^GW-}w62yRFP~1S7U`m>S0bE+a)Pn^<`44K~4X6Mo0|U6!0mV5; z2$s&&pyKEyAsZ+VK?>0@nsdPAH^@wK^&3L%H$bxoX1^?&ILv++Z3Wejri?+ENca0g z^`k4-fC^Z^EP`49@+*`Hro3>3zXY^Or~s>CU;u?bNC>7M7XBdPp;kdvLZ}32fc^j} zU|?VXl?5QZAPiCg(+@HisuUy)#}Uw~2bN!85>Vq{Tn13y#ioA($gvCz44`@jt{56t G$b0~)`nW6r literal 0 HcmV?d00001 diff --git a/tools/u-boot-tools/logos/atmel.bmp b/tools/u-boot-tools/logos/atmel.bmp new file mode 100644 index 0000000000000000000000000000000000000000..5c659ce87abd93be1090d8f54291edec5271544b GIT binary patch literal 15478 zcmZ?rEwf<&12Yx|1`P%Vh7Sx33?Ly!1`e<|h&aN>zyQJwj0_AQe6W(?|G_H8|A(rW z{vWDl`G2T}_5a}-w*QA~IsPB6<NSZ5j_d!CdY=DB8hHO7ZRG!dv{B&y(I&zF$C`xx zA8Qu*f2>9H|M6Dw|HoS;{vU6X`hTKb`u~Y`ng1s`WdEP+l>dLSOaA|fPKEy`yA=PQ z>Q?@Lsz>?%$sU#er+QTXpX$^2f4Wce|LF<Z|IbX&{(pLc&i^wL^!}fjXz>5cB!mBF zCmH@fJH`0_*~upV&rUJ<e{PED|8vvK|DT&?@&EjE%m3$R*#5sT)As*`Sq}d%&UXBN zagNjfi*sE5Uz+Ro|I$47|Ci=@{J%Wk^Z(@qUjHvI@cDmbk>CF-ivs>%T@v*F>XOj^ zSC@tTzqTy=|FvZi|F5rz`hR^z%>V1FV*lS*75D$f>V*F{)+GMFxi<O#&9y22Z>~%I ze`{UZ|6A)b{@>b=`TzEY?Ekkn=KR0CDfj=K&H4ZDY%chJXG`J#yIYI@-`!gB|L(TZ z|M#|+|G&4r;{UxJmH+SWtona{XU+fnyK4VG*j@ks{_ckV5B4<vf4H~#|HFOF{~zpY z`Tr1v548V(bfEqJ!vh`vA06oY|M*b%|3`;<{yzrc!xR2LJ~H9|6A(T&`Tx^nlm9<C zHs$|Q5I!;e|I-sQ{yziZQ?vd*J3Z(B^V4(xKR>hJ|BJH=|Gzl5=>LoJOa8w+zx4mh z3(Nk$ytv~3tBWiDzq+*Q|EtSu{=dGw?*Hp6>;AvKy5axpYa9N*xw`THn`;~Yzq!8U z|C<|I|G&Ml?f=`GJO01Dwe$bGTf6?hyS?ZCyE}XSzrVBZ|NFZK{=dI>;QxnvhyH(f zaP<Gj2gm+@e0bvj$44jre|miC|EDKs|9^gZ?*Hd!7yo~Ge)<2G7a)A)|JPU7|9^dT z^Z&QkxBq{81HyOze|vZT|93Ec`2WX;$NztPc=G?pr)U3vg7J(0Kfk>C|Le=^|G&Px z`Ty(NyZ^txz5oCF`-lI(e|-A?=jWIIe|~-Y|L50_|9^k~`2Y9Mum6Al{`vn8g#Z2h z&%nSiiqXPg*<vVIvSAl-U>i)!YMkmfub4M??%cWawt-aaSOK+R@pcdw1+RxmtwRw8 zakjv$Sq9>v;dRriBCXU#`S|!Gwe4dXW^P22$KdW<zZmSKxoKMJP~eYgCx$kVz(RAV zhA0pZ1Ml22A=*+`0R&W*gJd_lK-KH40C7=p6;w*S1VtFcnPmo*a{%#>@y=zrhGNX1 z!W)WNrOXRg?nG9C%HF=Z%F9?C<RS%y6`%?LqR)RbsxlnB*}@Rz3{F@SY@Zh^1(jxC zU|kGSv0eu%0O5j!V0dR1REi-UCIqJ^i$LX6;6f0_j9^4<1y#bpz`zqU6NisCbvbe( z)a_aTcOO?FIQ>CP#ZBV#@1`sTCWP4#4^r2^8%v~-Y(Ut>z$BB0EmiMa;3j~iiecBT zNeB<u;Pxd#29JNI7^1j^2L4@ag<=D;PZ*d?=OJ7PWo<4JLQ%u8YuENVHkgpy6dY*+ zsvAt<^zZgA1(-n)n)?2oV+v|!K}^R&>Q4u|5J7B=;XzZjYuB#rsqplzh8aQ#%}^Fj z|JL!NnN4;7E>K6a0i7#06Y59^wcZz90Ru>4y)QhGSmUl_Kq^5PhktiYl0!F~+WuW( ziyXV`{OWeTzBURxEJ*g6fQv|wGk5LUwI!Y%NeaRQOKpVb3T8KKnI5bNM&R)8Vo-}8 zVkV5FvVV65BY9WYsR2^GZJ(EA$O^NOfzfFP%$>V-Au=jl9vr{BcFog;iLfOTDx|Ud zcY6?$V>kr0Ev>DsZNZ6!YUOi3FU*k)>{in^!Tq&!MUfoba@JnBICAP|;uSNr1_7w- z3^ABl+@+Y!AgCe;h26ish*F=^rePICDVU^|f7e*ToXEgcjPNbkj(H|<IrBAOkzKpC z1;cYGgH*^A)W*e*T39QBLAW1(A&u3)o19@G%qU)q+EAmGe_P<0Q2;Ya=9_YIK!Df; zsDBs0V}QkK=2oa6oZ3+U&jbpng&bT7l!4X1v$<datTGiXxTxgc?dC8W81k@Wxka_r zP_O{%&KS6Q@3l}NWNK3|%)?9u*qZ2YwOIWd4>O#%56!!~sN~;S@aQty0(T1@#%gV- zlNik)1sl5Bb&gO43=GcL0tBWWn}4?$K=m?*VT3(({JS$9)<fWFfw=@P-3JRF0ayY@ zSH9Q)mIv8FQFAwnN^Jg}2Wyi`%|=lR;!wxG>usSS%AmgzWGX?N2P>@{ahAU`;4KE0 zBAkkLVe@YnAJjFrnEI&W-^K8@Y5+m6f)s2EfVzmG8N|hkcXq)W*TS{v*$}P+n|~`| zWoHJs~K|4xV5z<}9RhFgWi*aAxrys+{MNfO4~Rst_(#ip)D0~?_&7i|75gk>%) zfk_?zHo&3_Pkh0=0irh<Lp3O1FRegocJ10011qE%c!kk`2DH@1=HDDx&hLVlgG^G# zzv)otGcbs$5CFcQ#>ggps0I~${@u0C8J5kV$`O<>v?)F?{tZPqgb>RT)CC~Yzq?kM z;xcYv{p*3tS(xG^`FGb$MNAXGA_MDRcd#3XB1rS^&WZ4@1FGpm$G;@xUr<3)3NNHl zJUp=e^+RzBUXCrOy-95T1q};i!s|L@b7|q<444H3$72&<Dhbo>pr!)}{@u074V$54 z`xiErz=Tor?|^qRVn7KC1y6!?P$A>bRoI+_T`U@84k`ZKwbBk&pE3yMmP0`;sF>fi z3u}84U;Vqu0_Ln9kVBC1Hh3xIf-DSXH^WBN^}*cf@Ilnz6<7hh*kP;%X&}kJyB5G( z(oD&q+yF8GhDoh|H#@`pn+!7oMsI?TH>#s|P<AH4blHMcFP4Y#!No6F429@|4cjq- zyDX&nch_{dhuPCX*#XIDQvJIl9+p_N(Yu+GVKNN7tw=V2n46Sf{)1%gP4G|?K#Wj< z6v8k(4~l}_OQL_bH^9p*bn{8|@2*<d@HST!*eNg<?n1;o1EUM55`oL?+SLpXigt)- z9;~Iy=mil#C6~c#QApMx(Z9QP<-%(vDbx-#$^J!T4q24p{2e*4fM8%?U@Aoki<$85 zpB%Iqi|`O*KdM(jyv>2Ib}$Ddd`R~1#sHWJ4B9A7Zj$}G-42#C8LXjw4v-UfPJs75 zLGluvNL}Z3h>0k8#Cw5MfwBI4kbV^04I9m1(1ut+vVV7NwT5|^(Gon73$=h`|L*F8 zB}E1X^$D=?o^|PPCxSh}z>^Eh-rGB1RTu*U7re(c6P{ujw9sm{?UNC5`4DH4?%!SW z^dJslU|{iuxe`l#v}x0(P3f@Q)&%0gF~sU!>ky;E3}QYrHg4Ind0mA$yn=z*!eQF5 zdh?den<n|gs$K>L219tky(J75{tOIy-AE4FwY32;-YyOu3?{?ByXM0~jWr%_EqeXC zQJI&Q7hbq=@gf08OJUcpU5L~MT0|wRZ=wb-9AKV>&|K=qdPtsSVBqb8hS{!NyH>+% zF9rq{*X9+F48L<lvn{MDVqoB^fT}0Qzq{(-cJtOiOvjAhjqpH$I2nl)gWA1oiytih zkz|QsG6x~TdDpHs<Q%}O;SgU~Qxxy0j+ka)U~pa!wUaFWZm)!goB(`W87==3<KJDY z3~_sy1(8fKg1`uxIw79eUJh$>VJL(OsKduD$@1^6ZSX;OMm2D=Wfw+!k|_V~oB^+2 zpe}<`>|q|T!7sQNl)<Py1L9E_xg{P)K7eXao&;05i#-4CTIUHhih%((>p)KY?%Fk7 z4SNi;hi%#DgHd!rEY(J-u0dYeQj9)d4^asrRgi**O#kj$p$Bs(vm0dQjXeMEm~9Nt z8xYrlNr9}5yLPRQ;RbV1307-ZiURo*inrIRz}$nXnt_4E5_yFVx&GZXOBH4?Q#iQm zK%Reh?OGEDFa6*q8bNB;Z7qlj7bYhbkJiG3`go}qa`^;P&A=cQ5ADA})RFDqofF`t zA~$%!61o1}wPmWa5OP*!S4@Yk65F{rT@GHSF)%PNi@49mn8Jj3WY?xCHp1vd9E+Iu zY~&hw7rFU&*REYV%HRRY2O3UC^Y12WJv}|x^bv!C9ulyDx)4t7oRMq_bt{{kODTBz zA11lH)KLzS;+cf3;^!eXE@3L*^!8ct76OQ5%_wA*j*-*Jj^ACowj@H_!N9<vFl8rt zdt>M7l`B`myL{CvkpPN+Ag<l9X3^x@+?<l;*~>7J>&9g>n~QStYNjk$htIblgSM|* z(4FpQYi@3C>zCELU>&@10Fne_3jDikvnxCqYA;6jFW7W^;~QWFbSEhB@2(YAuyTRX zW+hsB2K!`~`FGa>6_|%ve5S#QS)}!%!_B`tC&0&dSY2S{^)UADF2tY=10$?57}oyX znu*bL9oGKcwF$mJ0G5@9w|{r7wu9G75FZaI|8Acb;oy>mr9}X)U$7DjG$H<kkwePA zGtEn8Op5d*$-^_@g*4d9L(0D{wXO4})P@#fjY%AW+Z*96X^@A9l7H8mtw@S@oZ9P& z!)w@ucNQTR(nHC=>nzr!6vk9c@WAddT%wzTkjku~<lmikec1uQ6H+mz>~Q%MD!v(M s{mxMG@2-CHj>YpzY@w4gP~Q+iEr1WgBk#8w{O#v${q}|?J_~jM0AIf&fdBvi literal 0 HcmV?d00001 diff --git a/tools/u-boot-tools/logos/compulab.bmp b/tools/u-boot-tools/logos/compulab.bmp new file mode 100644 index 0000000000000000000000000000000000000000..df5435cb596ee93496557b78fbbb0c2b97299b4b GIT binary patch literal 31810 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 V1V%$(Gz3ONU^E0qLx8dn002W{owxu1 literal 0 HcmV?d00001 diff --git a/tools/u-boot-tools/logos/denx-comp.bmp b/tools/u-boot-tools/logos/denx-comp.bmp new file mode 100644 index 0000000000000000000000000000000000000000..89d0f471c85cf734b5f0e2bb2508d39f5b9e72b8 GIT binary patch literal 4148 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 DEvVwD literal 0 HcmV?d00001 diff --git a/tools/u-boot-tools/logos/denx.bmp b/tools/u-boot-tools/logos/denx.bmp new file mode 100644 index 0000000000000000000000000000000000000000..c4cde09d59d60aac35a91fd15ff81d0d1c7c024d GIT binary patch literal 15538 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 literal 0 HcmV?d00001 diff --git a/tools/u-boot-tools/logos/engicam.bmp b/tools/u-boot-tools/logos/engicam.bmp new file mode 100755 index 0000000000000000000000000000000000000000..f6c60fb810d29830941e18d6725ef95785d95967 GIT binary patch literal 60214 zcmZ?rHG9nf24*Y_3>pj!3<69H3<V4f42%pMU~z`$5DJrQU|?uqU|?uyfKUt#AST0s z0|yum+&RGT;nXq)1_cKO289F$289L&289I-3<?Jr7!)2bFgP$UFgPeMFgQ3cFgPSI zFgP?YFgPq=U~o9Vz~JzJfgyo`fgwSGfg!<xfgvG*fgz!Rfgxc514F_A28M(OP<s^^ z7#bWH7#b45ZeVCwz`)ROfPtam0RzJV1_p)&3JeSj92giDBrq^6XkcJiuz-PK!2t$_ z1rHb)4lpn<98h3jIN-p*a3Fz!;Xnfe!+`}13<nM{FdTTm!0>>9f#HDy1H%Ic28IU- z3=9t%7#JQbU|@J~fPvw`1Bmk(6c`-9FoD5=p@G4HVF7~!!vO{dh6fO@DJU>FC^#@U zC?qgAC^RrQC@f%bP&mNgpzr|VZ3hJg2L}fR2Zsa(2Zsg*2Zsd=4h{zx92_1%f+RtK z!6Ctc!66}m!6Bi6!69J*gG0gr28V<Pkl<=iU~p(~U~p(iU~p(?U~p(yz~InufWe{R z0VF6FC@?rIaA0s)kig)upn<_*!2$+{1qT=$7Ce9i^8p10hXW1_4hIq#91b)vI2>5O z;BepogTsLb;3#l-puphpz=6TxK>~xrg9ZkN2MZV+9vonBc<=xk-wI$~H!viCVxFOa zVF5z}!vTf{h6fA{pr}z$U}#WqU}#WCU}#WiU}#WSz|f#@fT2O*0VLWS6c`#D92goL z5*QjB8W<WJ7BDn89AId0cmRpg1O<kM1P6wOgan3$ga(F&gar%@2?rP&5*~olSwn*Y zLqmfDLqkIXLjx!cHY{LhXgI*o(C`3~6c#8jG%RpnXjqWI(6FF^p<%%ShK2<P7#bEl zfFzRx3JeVg92go7Brr4_XkchKuz;cAzyXGa0}sGSsNsPEL&F0HhK2_T3=Iz&7#bcd zU}$)7fT7{R14!~=PyomF0fq#yuMaRRU^oDV4<JcPL4o0bf&;?=g#?BJ3JnYg6c#WX zP&mMFK;Z!-**PdM9B^=8IN*@LaKNE~;ef*eh64@<7!Ei*fF#KT1%?9&4h#np5*Q97 zG%y@USio=~;Q+&dga?r1+MvL2puvIRKtlq<frbW#0}Trp4m2EKIMDC_l9U%HFdSIm zz;Iwe0>gm?4GaesEMPdW-~hvc1rH#}{D1<(fddW<2M#1K95~RxaNxiKh7Z60Gb{mx zQv)bt|7T$M&%nU&|38Fc_zz+-{0H&>kAl$<7!85Z5Eu=C(GVC7fzc2c4S~@R7!85Z z5Eu=C(GVCcA@FUmIAqiv0~i9|INv^ba{E#z3kY;xy8YzITh0M=&ZyM`83Ny)TmmHn z41DSBXkjss@iHKGvp<0p3K*#YEHLTGXfZJ$(K0ZWa^A+57{DolrSr*XDKRjyLvLHZ z-G;<CLGt!!ilBGg(BHmqPY9+1gn}pZci*UubPWN#%?3n5fU!E?($#gNmeDr^-V$y% zz)}K??qsL06Gv^MUkG6DU&3M@7k!(44ji?Kjv?@k^uhs>L@tfCE9n?b^s<VR+~mN* z(m6UBL9bY%n>}wKd7Vshv{^~Fh@y{0#Ev=ON-v{5Px{0YoopemC5fCkMq8D1iYj_o zLwRywVHr&j^oS|C*g?vWByyR6${wvU=n`49w}i5$BqC9aj!e-$uIOL`J2XaTtsw+v zK~>_S2I)A&H%`vCZ=XDQ`<9cFHZJ_e&i?kv+qdlOv@xCzX#mLz$S~Bb65;^fB+-c_ zJifgp3LwfB6b(2AHc*U2XhrfBjLG?A5^Cz5bo&XmXhX3T#dt6WhdciN|Nj<kZNMc| z<JrO5VFXTN(5#1e2~`m0BOElUMOY#NhdvBpaCogDE;W>tl&nDwUR2&C4AcLALsfx` z*9q6jiL3-64441^|39omgQ~>od7SGwI&Y(|@j|f`YAzCm#bY1`y~W-T?|cG@(EtDc zqc+X3Wf+h-|Iw_6w9Qck2@LU|x&&eX*k}?7@W2DnsR5)ADVQ*rSlX1R4#m&`6F_7g zlyOpcz#@AKHIP}bSPD{2U>yhA8ZQ)EVU9x3*wP#)9<O!6R*-?S8{8aR@r0IZpvq9( zgd<6Ud<w#7jX;nPIT)PPh%X&L=>cyEfrwU^YtS4D4;nNEGzk9x{|`=~Fj<5!%)I~q z|D(mjBqS*W6D`re3`C)kiU|~3Q51nWSaK&J3m_}VP%}9T*7W@!y%dG|8r24DNfO~* zv=oKKy$H2<SfJ!P5fsZLV2~bgAYmaeQ!$!DvFHH{A!-KH5X9zolzhX&!h$WN(9#Up z4r~NWFRHB=Dlz><j0K>0MU7O1H&Hx>7Dym{81*87C`NS;)C3euh~R)TFKNX?Ny!$B z&_WWxEFe%Filh{kiO4x9(Tyb(&;pMITZKWSr7SFP^HFR?bsC5VOE<7UB~YG#w6L&% ziv!f`gQIeTs)tgr*hFy?W|D-t1x$mZ5~`ntg#|1|G6A-bgrWhW6k#hS3$yS>b10@3 zhzKkU(Ar=KVYufo48+WI#KjiKVt6PM(1I3^sJR|w5LOJ*g;w$)(mmWm|Ns9-)eZAB zsx9bA5?Mb+l><-bsH*VuAof8bnFJEm@n}3uhoC9I#f3W+H2~qla0bNH$i|=-N|-8; zRim<Dp^R!Psu~s+7SwbOZgyd)!4v?eR8-f1h2VaHF;KO_(gUiSK#2>c9t(|HB*Kiq zq7b(bxOnI!AtgYRqLzTDUSYxXFRDY)lq0x^gamdLOadVc^9QDRXd#7Bp}@?-M#IfV zu@zG#EZu-zf$0QH4PX&)N<~$T=2)<KILe3rC~krmWe_dsBwDeFE=>d<ns6bJOah7G zO0c()33vd*T!7|KWVOg_M2d#G3nGaShM5NuM<&tIAc}rub=cVOP$r-YIT}&(EjCkO zVqn*!rB84gfq4c>qXsEh7f1%xO(;ncB#nw;c0y>3Xh2nrg$Hvc2`K@h6k;JR5-oBt zDo0%U5KSP|(iq+4*dhZph*1L<W?$!R(1`N4Z{OHa<zb=Bc?k+&8X$BhR1_XZ=v`un zG8Pt=OHZD>eR3PM@Pq^gYL0-XKqQ}`ItgwUs$Y?lB$8ej6SWw^W(!Oub{dxWwm@Q; zXcBM#f(5C`4zm`msdgI&Ktw7^=%6Qb)WC(r21-a_Q^10}oeb4ID0)FoMwN!dC`cTR zVHpWxH-tpmdGHN+*b?F?)Tl!D(f|Lb6$8@R0hEwHZNtEw17o218<x&t(gbOkBPS7^ z5+Dlk&S9`%SqVY1Lt!Zu9MtFu9W}bZN>CduSQMacB}2`1=;osjheKi%IW$rAgI$1@ zsJ_AXu|TpqS_*oC0Z`%{DLtTs1lEBTB!_`r0w=JfIwZB2OoSUr8-O5GJfKDn*j~6B z!3@F<MGbJYgpL|?U=tx7eXv!S!<Q_W0~Dx6pqY<5v%)MuOW#;508Mm3EP)m;m|CC? z$4WsG5y(13B!Lv7r8<xTWQ>{-Fw!})QY>sFCqnW)kz@zRWq2`0jw0Eikg$YWf|h=u zqNo&dNTFy3DMOY72Lx)a!;nUGE;L5L8X*L#c5EdgSRF(eG%#R=F~~gxF(N&nRwPJH zxeaaBT)Ko9&_Ie&kQEr|9PBEb1d^{iNoYb~gf_@(RE*>ui0!BiL_)<L`m`WwLPsq- zU=|~V6l#{j7&-vC2-Q6p)iSCyc#sjK3yzUO4q`u!kO$AGK?4J_tN@~%Ac^E-NTP!2 zKv;x`2Bb+-m@){BmXF}h$H9PTf|84gEF2a?)#Ig*A_r<Ws&c{(MU8YsD4-`5gt>@Z zf~pr=`xd=MKrtWWVnkqrEP%!+NCJY<^E!qCh%(R&L?^uVM9aJ&gK=V{^njYW5k(o2 zUW97Ygo+S{y9W;gEfcVGE+(`*YRzKQa6)nt8WUPZB5Wtwp(r+?q#snH;1(l73Ozkw zrcSgWam@J-R68&XKu-^dw21IAl=ba4ym&!16z*_r4CI(Z>AxbJf~*bhceFUgNas*T zVWmiL9<tp;vEdGc*-WxSQHn|A;6bfW5w1am6l#uQVL{Oibr5QrLFmOQ4UbW%Eb5Fr zj`>xX+W+uKA)&@Vb}~vxu)uN}swd$Y1`!@07F-za7(5J=90<})MT}7+qs1addcklf zYJ|bV6l2;3;b>S$q0bE=b>5I{LG?XCFH{MtG<to28bw6UB@$m_Ag2e^n1_WNYWTog zWT=@O*%+wDa8rno#9(#Ydi|LYcy$Y3KLf*BREJ`w7Yq$3F@luPaRw?P($M?^3qlkt zP@@z>392-D`GG16Zy}&qfQ^G12x!hk=AuL-O6>%53R*D0gh?)k;m$-&cF2xldHs<n z@aZ**E))*j8W@9QhoXczQuv^VBijiJ67<F*Vui#1Z-`+a)Tlw|g}N6l`=K=ua6|@F zFK&wH8Ux9{=xrTnQHJIOcp-?AjgY*K+fh&%G#?{E_Tnd^QUgfQr;7+}s4S?fp>}~N zREJ_@NRSc~jGA5%6$VOfL9rBWCz=u@yHM04bKrVmZa{S|qU-_7qlX<>kQf3jS9Br( zvSZMiJg6Z7^$kY0fEJ;s=^Q0p;ddZ<0)siIllbKD@#804bp~37!k7ZW$dH(hMQK+< zLjmvH7(CL@!U`^qwlovwV-SrnAL<Y^GoYd{3Zw-IBT_3&0WKOfx{y*Ks(q;Oh?2gc zA%|)Z$Ssf{M@w<UyA0I<AOj)zGbrN|jXy&)VUW<&iWDqpc?u~gkQ5;^Q5*;lAT(o; zErx~^#&UeP-v9q`Wdh7509v@B`3EJ2Sm2F*War~xqm(;H%_vltAvqZ|@{R0EXlDmA zJwS)0P%OagN#k%9daVSv@e$EU0iy60Tqg<xBfp{7>R1$@ITf{-ge7#;a6|PxB!^&> zA@Fp9A%o;(xL%kmP>n_^V?YW}(khk-U62~=7&Sd3LIKrZsObSUBp`awQY6SH5KX8F z1u4a0cMn_?RUgPaFea^d`1lb=0f82NAX`x_?3{!ZAm>A@6$Yv)mr%HHOVI)gsVqWK z5B4yWfP^y0oluMx<xo*5g&M0^M>wD=F(_0Qz>+yy$pwj8xLy?hK$16Va)o#T><2Wb z!7YFrhJ%4(2gE!ONlNPhR6=|LX~M+ts6p6{=1@#MAQ6aXA(~KP1eVZI5(&!_lwuvO z8AT^b_C?KxARD0=t`}nWfAk$w$O#p*oPlW~hDM1=c*LVR3@!{Y5LFVKyirVn`T<gJ zp~`^M2*ecpB&s%;iJhQ0CJAG2QKESW>=f)a!fb}pV9$b;qwP_FCUmGWD8-IC&IK#< zQBnzNYaOZ)lR~l>Vm+#J(GoaXZo<$EF#;=znlE7K4AseK!G~I5fSrQr0Tvb(a1#Qx z0D|QstWH4|MD;Gr_=_Z^1(0IwT~2JyK+Cl-D^Y3W^o$bd5DC=4Wch|VbPNtDkSEbR z2NHr})X+k*8?G0s5N+orVlf|B9yQ03&}_q)kb|akyy*dTLmH?kLro!YZ-CMfMi7A= zOprh=c;P03qL>tnqrLz+4BZK+UWVI<$^dy5rV%C3Arh!*suR600n>@940%`vrz0Td z!wmROT7`k38Oa<>CQ53Bh7@WnLIvSkQ9=S#Eg&nOgp|fXxdz2}qLV60=0n5@sqF{i z+mq-$41}F1ETp0rH4}g(kh~6EhJxaISm}$Z6Q#X}Vi__AY(6{&FqSl;MG>k)kW(D8 z5!l(NX%gaQR53Km(5lYcsM+Asf26$4j^1-5HmRa{2o^G=_AhXZS%cCkMqr@jT9}on zG_p>VGz*qMiG6S^p!fq`=%eUF86e<<oyCAoW26T(=c0NCIfH_hR-?)i;GviduJqBG zujuIkqryNgFmC@x9tZzMc=;z**P*xx5hWxIPk#LP2{nS?=0FlXn#E{g2v>~4Ku*ud zz6K8&q2+r}6rvgmvk^7B!AJLD(nQf{mZ5nGcZwszaHI?kU4D#e8Af_Q2?-W<l*Y{y zl;UC%dN~ZvM}*ykY6BulNE?vCF&qUQL_>E4j>Lno9K?ck68@vlZF8dd8>AF$K8m66 zNJA0Dz5^Vr8y|rZYbcct#-JgIDGoIegM3UNJ)rL2x{Z<^Kz>Hb-Kdf*;CzJ7mryyh z90Uqp2qvY*z)@SEE_a4nh|#i!ScgF(r)QK*_yoObgybUZ6$VlOfoy<RBnZb4W}%q@ z?Fqp2p`|!bq{5^K(<nxRibO)`0VO0lQMy%Nn~_4{Eoy25H9rYE2)&j81sW7TBDPKW z30LWWD?g$+6lxzD1qvOQ`%wz9PV|F7ASnmcP?%2CfeBm*6s{U812vAotraA7C~hM< z8^R1gDSJSv6(tlPDIKO3M5Efqj+{Io=@cn>qMbwz2_}#s_%NDhk)6}|`V-+K@#)iR z>_bstlW^FK<Y-VMj1wzB(u?Xyw9zM61fduTDm=jkpavkZr6yP_oInnB)L|`<5)^we z_jrJm;Krz_m=kSL7iM}ut1yt06T}iE13OVlA7Z@@jyIgd#as9R_F*WPamaZb;#>$x zvO`f~<Oyo>fGrY3F%%J!C|XI4E^N+)*$0us7@#1sT!!gJadju*^ng?$fJP!=A&n@Q zkTL*TN(EOaFne**XyJvhfC?;70f^0Fv<L<D53ngih#`5Bg{2dP2T%8?Eg`sGqznW0 z5xnGpt02fgb1u3Gs8IxW4?$xg3Q)rT32OdFq-uy-%=RnTOJD+?_fS$8SPDuIS7G4P z4mE`m3O(fDzCum8xRMQAIod!Baw!Szr-78B7>ckO-Gc;EAEpA7I79DYpg9h+LIJrA zfg#mA5+5aiQTLo<Ne`$A21zeMEvjn~O5iC5VL28STH;4&M`b<v%0U=>eT=FJi**no z)Hou-p}72ryk`={P(<*cHz^6-+HwijAb~g)M4}c*=oJP^uMZRe_%h-pjFDp$!#hzD zHIgSl&H!Vy(g<W97$Y(j@~{+G3PzwM^lxZ<u<_`_7BIc2^d-V+0ipsWePi<u#8R|G zh}~fjr4SM&&`{k5*Nj>-Ao3w<MFKtD3t}u5@)DK|j^<oM*#p&&o?3Ayh}-BV4q@GQ z4D%qi0K^p%NOod)7-}AplK-6GbOkvJ5$X#lg_5nHI?<>%kjN&A1lt4Aj}dNA8~^`D z9%_e9Kp^C>vLGg6BN56`@)nZi7(s|6htLgSfuk0YmLP(t<HQ&h2FgG&*olbD3N-*q zfx{ZzbhOk4HUL6kOAojz3}ioHco<?FQaSvDrSl0p=i4Wpn1vy()C4gDMBXMUA;1(N zVg=pf&_DrE;D7;{4aP_g1xaGV;CKQnMkA1>)KLsY4oegXkbcy2AQ1LJi(2Hc1xum@ zFowraizZMX8bb-l97tY4&ZZzyv_(-M_dzkTAHXU>1jG=i3Iv7Hd%&RvUX<R3*IEb` z$ZRl1L?FU+WL76Erir9sda?QjY$N&*G>&m?s9G?E79%h_keZX|JwN0a!3ZCuV_d+- zLkL)Hhv9OxEQ}GOSfUgoO~IFSBc))7wP?i;!fzPv0IA23f8lWrQiE3h!c2q}TW?v| zzkv_RVSn=N2}TDQBd}oFp)@fqN#vvg%Z-?!26ifhK+W$kheG7Al2FHBP{`qqlCqH{ zu!Q&}6nT)9Nas)^rzMaSntsd}#b{Hqupk}C05$?~$t{NI&|(S0Z&=d<dixlv3)N8U zCc?^LmM8z&LCN47XujnVOcd-&&}MX~iMS}_xF&}E6>1Qw3t_fFX|()~<0z@yh)Bbg zYLRV$r9`j;Py-V+{ecz0&l(3i31#dTBPpXf7uiy<ZuB`7umzKlPS1xGJs=4T)3FpU zAa$4x6|h~{Ljo*?Ah4GrkV@q(3urc+^Z)-#ph<R4P|84P#M{6HyNZ}JfT9Mh3zYOw zY(eFqmB0`!m?R?YqQwYIDMA6pL?~*cVGl*nS#a!cZ(l+wS&;ew?9k&^P=`ENupKss zk{IF6yZz+t+b3vuW1zSh5-d1e2a!Z0u|_q9nOL0zDTi-g0!<Nf{(lRpr#sQpCq`=s z<{L153ni|J;efSa^wQCMg(bs6^r4c7Y7tc&#zQRyP@)x4wt%h18bB~(&}n#DLYF|} z!`0y%Iz-chvMvYgwEvhr6tEst&q0-9A0fb0iY<U4sb&&*W!0okXqLSMNhNUO(dP@n zPC*c4rUwj5kn967apZS|y$}|{80=XHHEpBBIeHKgX(QNPxUtx61*-uOC}|FB#s#TD z$8gg@0Rh2S9f$4%h*FeUZkW-CaDpnro@~Gs2B<(_XSx04b|<L5_>Y+$@D~uo6bUG5 zz-~m&IdCT;F{o5wfISE|7S&L6Cm`41U^n6>AQ_2p;|ev#h_wK2I^5y#&IgK<P)&y` z#+L4|Br$BU3Qjh+!Q}%dsFw%Q21*`qvmp$;xd0`uiQs_SlOT&AX5%8Um1iiS0pSp+ zFwjg#spm1>fKq-y%*R2(t;Hb^6GlsOL|Onh9pQXzJpvr*0b36PrJ#bEilxi}r^F{L zkoLp>Z=hiaPRt=Gj42tIZ^1OF{Y}s$2H0Mxvv5#YTnn}ijR2=;uo2i2ExJ7@@qwjV z$c~a=(5yn{!qXBqTagr^#{+Ri1l)A6#~=ijT#qX~U~v;_ISDZlOD7oIf&?W4c9zab zo#352x6#88q!}d~k-Q6Hk~kg(_7{3Ef$TuTsAW6IBCHtUTCl-rY7nUfVmhj!XqgwR z1fNe4h9kzSu-S@GhowtMj0IRD8Z(x$D#P3p0&))4^nf;O0#XMKuqP~>-@q47f-6L< zafQ+{2WdpYq>oo4A`u)1*a?y=3@mQM@C%Be*s|gO|A>JhBtN1s5vc)V{{@OV5C=2O zAr2`9$z#DN#v_FdhW~J-2P_o^rZYeWW2uBd!v80A!qU%MXeR?Kj6`5-e89az@_ZJS z`Vq-4D3cl$28Kl_xf4r;0k#`u><R8m7z1tO5;NPv6oP52X%2pDHCPEMfu;Edb`<(} zJ7^XJDuF>^x(P!GL;y2d!3p8pZI(;y-~NAlI|)Z}z;i|h$TuX;cVpyQh~v;m)Uq99 z4OWaOYp@jEXhwpX%*cjd&o!Wtt0yQ036h>m=t&HVtw_qTR2X14q2JF6(=rKP*q}!< z&hh~xnh}8nwjDuWWIAw#@&7;c?0xh>b%aK&y;_(rSXhWz55xWz;vuYA2FWHE6ZI}x z^o>$*K1})l|8OA~1Dx0q@*oz9p*Yh6SS9+nA<LyFNF&T3C14D*3<nKXgN^uxo+hvx zkE|8V3M}4(szy`z7Pm^2@Ob+cq4FE&Tm1J{<H|9JL_r)2ngXFNALJC|Qt!8K*=cmC zH`sjI8Bfd5#nSj7E&-sb!c}S0(&?jS(;@^gY9-W&CXNRl0;PrPM$M#q2oPvG5tSg& zn`3nM#HfYT3jyLr*YKnZc)yl<ZW=Xd07HPnHpC^o4f+A}*QnLxh5#kKj3L#tAUA+U znm&{fK0H+erWCQ`4kIl@hq!++hO{ta9;*oMF_9r2AS2!RXa@&ijeHEjNuwQ$;hSy< zPXVH(2V7Hj!#9XVn9oRRMU19~;h#FdmqwzM0ywy%?a1Mu96)ozkl9Kci2yE)YcJ%8 z2&0kZNt7Gv;RyhlaS7W5@yH6K5$0Ryng--V0A+U~?WP-Hfikka%YJ(j`qIA6OSeb+ zmm@k=eB)$)`{efR+fUxI<6l-YqC;ua^P?d!8UmvsFd71*Aut*OqaiRF0;3@?8Umvs ZFd71*Aut*OqaiRF0;3@?8Un<G006rg%HjY3 literal 0 HcmV?d00001 diff --git a/tools/u-boot-tools/logos/esd.bmp b/tools/u-boot-tools/logos/esd.bmp new file mode 100644 index 0000000000000000000000000000000000000000..a6b403024fbd92952c6fac576d215b49afdb6626 GIT binary patch literal 35078 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 Is)25T05H?ci2wiq literal 0 HcmV?d00001 diff --git a/tools/u-boot-tools/logos/freescale.bmp b/tools/u-boot-tools/logos/freescale.bmp new file mode 100644 index 0000000000000000000000000000000000000000..1589e8073d133b61d5dedb99ff393ef9aa68a07f GIT binary patch literal 46738 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 literal 0 HcmV?d00001 diff --git a/tools/u-boot-tools/logos/gateworks.bmp b/tools/u-boot-tools/logos/gateworks.bmp new file mode 100644 index 0000000000000000000000000000000000000000..01edb7cedf46c3441619213b6baf33410055601f GIT binary patch literal 56202 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 literal 0 HcmV?d00001 diff --git a/tools/u-boot-tools/logos/intercontrol.bmp b/tools/u-boot-tools/logos/intercontrol.bmp new file mode 100644 index 0000000000000000000000000000000000000000..cf2a884b2df9e6fd63d89f929b64724d0fa10426 GIT binary patch literal 4998 zcmZ?rZ4+hy12!fG1`P%Vh8_k61_cHN21W)Buy}(Y1A~Gd1eY){Fn};v0wmAD!N9>G zz`!8@!V(Mu0ul@Y5+JO=AR(c^AfceZpa8}i3<??=3>q2+3>pRo3<d@k3<ee!3>IMQ zz+mCvz~BJJ9t;j19t<8}9KawTae=`jAb=qNj3XE%6fQ7GDBNHOh=^cNP`JSm5fQ-< z5s|>4pmBpCA|ZhxAt8Z5!{7!(LP7>ZLPiFIfyE7mjEoEh3ou>4VBv6sA)}yxp`f6E z!NKDOLqS0Wg9nJNs9^92xWQ0SQNd7A(ZEpA(7+H7@qnSBp@AU+Om{FebaXIufbj%| zjEn~i9TO%nOqei%A*0{{LqWj<h6xj9Fie;+gQ2400mF<LGZ<#fSimr2!2*T_V7!8% zqvHj`f)y(mR)Fyah7}t&Fl^Ycfnmms7YrLV>|ofj1B4GStXT1aVaI_33@bLgU^sB# z0K*0lec}Yei4!LncI^1TaN@)Th64vaFr2t>f#Cue-(Wa#;se8t8#fp(TmaDz7;Zdx zz;NTn2Zjd^9xyz3@q*#OgC7hpK=ca`{e$7fhYt)N!1xElj~_o6ibnz75P+n2tR!3? zL>9_L;((YSQ4kFdXgH%7#40X^Q4j`L36#me0Of)7!!Sq_1T%392??<<K{y~1ri5-Z z$Wjm%vdRM68EeH2;({=kkMIZBCm?YshR8rY50(T61H``|HBc#tI*<S)FrX}mI;b5G z3L?YpUtFB!Z)IiWpH*CF#R3t8D4-(=wpJ*sI93Vb0v4sL;#f$~KwJS*0Ahh@5CQfO zhye};xUXSqz|vqTkQz`>fM}2`SP-Hf%*7x;3Wc(Ytw1ycbNfSi7;0%Pz`(%35(~8) z;uZ!bXb?fH1_?q~@c4%@KvHlFQUm6I7~miP^B9l<0wjRM1uH}*7#J9=piU98va%8a zsfS`@WprUPFmM-VL6VApaWT|Y5XV652eCmcFbyW4!NLIZJcNd+L8KR`I#g-=;Q|X5 z2Bp~I*Z~d~|6)kCK|~hF_n-^_@+!n15D}<HAslEz02eGE5s3FeERYa{29wApfC3F9 z2r(I*WMIh3f|a%K5CSO&nTW2K#(V~b;#i2)xWWY*8gO@jRfBnO7Kni>T)-+Jk_aY* zg-#OX7IaNC;xjPB!s18?t(>8ejkt|vV1TDx|H3S!;un@)K;aL`z92&&JZQ9mQX(Yu z!NL$#AVClpq?$y`z@P*xRG7GhAO#J`D0n^sbHFyjTmZ5SEP!Bu6;Z)0+=bAxjsaE? zK}-hQ1kI3OtD$U2fPvM3{0(M62(T)UHV_viM<NE-0sbJ(3=FZwPzHnxF$v5D+k(d} z5T#&Ml(_|7H*!NOMQGUvu>i>}U?w#3z-*8TI0GJqU=fg$K+>dOkVe!t1B-v55=azk z8iWZZVFbv0FdNQ*^1u{?07;RG!8%deQnAHZkXi|16j%Wy^ubK5;R2R{$03LZ(M%c% z(#ODn+{P?MZDWEJ5ONFDPKa8Fo1j9ZP+;v03=B&Cg%EB2kU)WYA7U;vw=zI!WS2qM z5Up@F$WW*dDHKQ>1am8a05si#gdoO%SWq{>s%D6tU<sHEn1d`1O>Gb}h$O*=U?(6Z kfaRdJpoJ1h3MvC)qPUCJZh_@mT=t`gLwqty4wDc701p1^UjP6A literal 0 HcmV?d00001 diff --git a/tools/u-boot-tools/logos/linux_logo_ttcontrol.bmp b/tools/u-boot-tools/logos/linux_logo_ttcontrol.bmp new file mode 100644 index 0000000000000000000000000000000000000000..031d3a41ecd3c2d1a732af0a3c53b430545f61e8 GIT binary patch literal 11830 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 literal 0 HcmV?d00001 diff --git a/tools/u-boot-tools/logos/linux_logo_ttcontrol_palfin.bmp b/tools/u-boot-tools/logos/linux_logo_ttcontrol_palfin.bmp new file mode 100644 index 0000000000000000000000000000000000000000..e3e38d1bf6423c7373b6e41530c24ec4d8f6da0c GIT binary patch literal 20534 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 OAut*OqaiRtLI40060foV literal 0 HcmV?d00001 diff --git a/tools/u-boot-tools/logos/microchip.bmp b/tools/u-boot-tools/logos/microchip.bmp new file mode 100644 index 0000000000000000000000000000000000000000..bcecbe972f4e5f088f458df835396fc44293687f GIT binary patch literal 12726 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 literal 0 HcmV?d00001 diff --git a/tools/u-boot-tools/logos/ronetix.bmp b/tools/u-boot-tools/logos/ronetix.bmp new file mode 100644 index 0000000000000000000000000000000000000000..f956813915c7822ba11a3ab81ad9f48fa2849a5c GIT binary patch literal 5638 zcmZ?rWfNln12!fG1`P%Vh6M}^3<?Yk42%pMU~vW!1_lK^2rgk@U;tr=1P22H2L}TO zhX4bI00>Jk2na|p2uOgi0)vEv0)vEt0)qk=YcMEiXfSAK7%*rU7%&(ZSTGn^STI<C zu>*sJg9C#D7<(`{cz7^)fN=nWfW!p`kAMJ%05FbVkWje5Afa%BAs`}xK|$dLLqtRb zLqtRZgM!8lhKPg&hJ=I!1`UH73<(Jt3<((-3<efA7&0<47%aea0fU9Z4Tg+@0)~Qu z0tN?<8w>>n6$~CAx}t)?Bj5%@MMVWeMMVQcMMDEaK*R%vhK2@)2r%8j(9qGr&;iC1 z7&0;*Fmz0qz%XIL1cr=)2Mh%T4;Us)n87e%#tepviU$lcX3SuiF=GM4j0FoA7J%^z zhK`OG3=3AQU|0dh8yHq>*ub!1!v=;KGhQ%k*sz0P#|{uaz_4P)3x*vB4lu0P@PgsM zfddR1K=g?d3@1*UVA!$a1H*|E7Z?s4_`q=D!Ucv4V0?q&#EB0KH*VZuxNrePKVZ1= z-~q#p8y^@RJb1wH;Kd7u2M>NQya3TJK=coW7au+_d;sGg3_pJSV6Z73+<?<)aJ!F( z#b8i7ke8hqJp^-;QehyeFw5V`5fwOj#TFMAC)znW+65qa&D##5%r3Mz*U28jhLZM< zSzv{pAYrhkEU*fD*Hkd==xCP&RsbbJ{Gq@rshF3OlSLmY4xy5TIe8#}o6W$BkBu1} zuyN`Z7JD!<Gc(FSbbv^Em?{R{;#77R2TC(a<`x$h7xFPNGcz+v7Z(@DvVdjSQ>_@l z3`Sv)0+2by#l;3}P{1Uc4ssu=15<@q*r9+~Bc7Ly6BDpAdl!4NadNULLrp9$4rOPD zs$(%KPUV4dpkkcNnjrTHgA8L+0=bV1q=KC%y;zAEtOgNAg&Hg%IZjS?X6?dau=_Y! zbPHiNrHZhCHE?orvS=5(LUhAKU^I&|n){OYKvsjL5$<D$1_K)#I~%iEad9E0`%;Uu z#UMJ_JRmN}Qh?ge&SnP^;RPGYYz1N$7Z+y;L!~)6nRSbcO(5=rh9`&uD`yi$bzh+b zSQonh4<{$weOv;_fG+^M`-)?^zz$^N0Vlz9F^G+vY@D88CqdlDq6X#^r;33s0y%=s zthl(?2+U<S4}yU}0Z`zx38T7C7ov>MfCJ$^HsRbtBv4R*!+pj6>>MEbSjBUTi(_HI z!^Rr~aS_ygp!88(oDFk5Cp!zsc~JMULDhnEfTIJ=eV$<FaWWg(F(bK81f(1pV~yY9 z;$kyMkTF{n$MQo=;$#s3l@TD#$nML6L=z_`Cp7Y5?(+w!1mi+cP;8*OFBF{EIoWtq z^;yVqUm-NfahdRg4CmzJWR^??+g1$CADk?p@XM8kcm(7;dx$(Fe%bK4FG~PoDvL+4 z0vnS1km46&7_RUuPKUaS6_j>B8B-Zi+91L&R|ercXl5cN{0e0tal)cqTr7*^K29!v zDBu%F20IX^`--C=c?6UUIXT%`P2i;oQur0h!kov(4s#wc?z4bsV-d+ME|x}iA15fg zgD@K>IBVj{A0W58LYxL7VMz-ljEG;|LS;xWaB^}8AhHAD`1NE1=RP*x_+oVTfoua| zb}oYMD|UoNEhih7PjPW{3e<J5@MF`~hXeyBCzrTWNKjB%GB}L!yN^}1IF=J^FgrWg zPpIJsavKN};l5CQurZvRY`mexMJ`f`kuV4HfK1?I7vKWZAc6-JF`Qfiy1B6O4_E$R z(ar@|37niPwqWyM?gM3Lc7f0^2ndY;`xPaA!QNu$fo4-LSo&iYNh;2>mXeoKhlL<C z^Ff^tavub<voUECbf1MiB-ELq6*1I(MWAwqO%&{8Bmyaar1F6BJj{KLY*6c&C9{e% z4W$$n6{YlYAUdPKDmmF%5CE!}oii4eKfsX(QIA|cvsj6Ptl?x6hP0nx`J=c{mW313 zeZ}CwW&!)f3zTdj?khBaIgdp%w>Uuu<UB=1Da#^=g#jR0c0p}8Py<(Cpkl}a>OOWQ za~Lq=1GyerK65IAQwKX2tgwfMUvY7|02{l2AG8$*qrAb1j7`kn-zODZX@K3As|wAS z>})2*#c}cw=PAldxkKDn>;x(lw4odjl?rtrvr{p&@IYz<f@7ae5?udsft|<6<_*#X z!f^K$$MUdo!kaHJ23RjACmS1!a8|Jf3n$2Zsgh9Vv9Y@r7Z<0g%PB&Dyi_pA$l~H+ zU1muA0p%8hs{v3g<5cVq)eJQmMlqX!-3Q?!wZ9SWEA|IP3q%1Hl3BSpSA>n7CpSp| zY6BY=q>@TdfjLl4F%IIQLIoCZ{RZI`BfGCqnTZWl#G+s}X1=6iPiCkXizFm>f^<u= zu(LBm`!kO4W-e4A3dL+uoW#e*>k6&eI9d3^Kn4^S7l+HqLjq7yPAd~4mL<pts(&FI z6!#V9+DHiSp#pvp?WE#jZ)higOAuN{fOIQx@$zz-gILAI#hRSlxB(Zte{m2yJ101Q zadNT<L#jZKR$nQY1Eq}fK?23aNnENB280AvqmcLo@sS#FP$pP33j)#+Re5o7ZdO)y zHY_*`lkkDW)Z$_fXiJ`xSpixAfJ`oSfMyXzMJZ>nKyk6329yD!Qo;F*latvA#G@q! zm6M#D>?|5EKY=aFH-$M+Udk6NQd}IQtN{bc(CkYG_kqeuPIfj}=K|`M;!G`A9+6jv ziD@&!3TRj>jFSb@1AwWZ1)VOy1}Rri4Too9g!>9LAXOEjFkpu^i%_*vnHS5+g;Y7g z4Toi7c`dk5E~0A$aw9t%Qn?0KM14j;C{{C}nHb4^#i@dzste>c5at$jLrJt)O`)Nn eGdPdP=^<H>8ifEcsRQb|;^HDxITaNZIXwVe+ys*V literal 0 HcmV?d00001 diff --git a/tools/u-boot-tools/logos/siemens.bmp b/tools/u-boot-tools/logos/siemens.bmp new file mode 100644 index 0000000000000000000000000000000000000000..bff2b190e0c88ece91484ca82f0705ff28ed9b2c GIT binary patch literal 25766 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 aCj_c<B0a6lbyQVVwau+O{ZmR%hYJBIk%Ptn literal 0 HcmV?d00001 diff --git a/tools/u-boot-tools/logos/solidrun.bmp b/tools/u-boot-tools/logos/solidrun.bmp new file mode 100644 index 0000000000000000000000000000000000000000..93db1f8f1649b1c2d81751b4403a9534b68d4167 GIT binary patch literal 5558 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 literal 0 HcmV?d00001 diff --git a/tools/u-boot-tools/logos/syteco.bmp b/tools/u-boot-tools/logos/syteco.bmp new file mode 100644 index 0000000000000000000000000000000000000000..14031f2c8c5eaee2e961a89b684ac3c986329673 GIT binary patch literal 11414 zcmZ?rou<P824*Y_3>pj!3>^#%3_c7D42%pMU~v$U$j!h2!VHXH36Ri#28M?J3=IGO zH#9W-KXBl{e-L0`VE8|ZN5fz=4Gixzz&cm}BawiW6<r~feSmH}E<QaR!bA)JI|M}` z^+N>3cwC%OhoFbdXow(%2sBkPGBP3wGBH7gsYRh1k75^y1GflWC0v3!40Pi`b|Ya{ z7APRqHmEibMVwZ=4gom>)CdG)RuB(lFcBC<Da8%}nFe+Om>|&|U_DTRD3{<25maX| zP~Z**N*sdb3<f5U01<HlQo_o@0u2fX#Z10KK(;~*fs#y2ARCCo=<Z-3J47&?!9bZi z$aDy%GbnWjSq{PC42s=BMjFB942s=BdK$s%3@W&T#Haxk6wo|^Op%mMSy_>_!r3Hc zUshJs%nvdJsm@`-Y>!~%XjUBVASpz!xC7Z4ph_RqtVR(7c@(K=0dYYX!$TlJ!WdG- zW4Z&yN+w(m!Qu{#kR|K}unMq4Fx-Le3>*%@<_>g+fNddy067HR9hlAlIRw<hLXiRG zGMH14Y(kA1BnkXXMn*<7cc54X=dfa=Rvhj?4H5j7Bg+!$4p0jf6nJolp;X7n2BK8G z$PxtD#DxcD)Zh#c<d`8~H<~oEP6mXJ(O76vGhpsOiyFGQ11-=nxUkH^!omWRg9w2N zWGIG75JzK=7eaa=mV!x`QZNVEG?)Z&G_p=a`alkNhy^%Fm^CPQ1r#JO3F7Eccc6yT zXn0ULJU~SY$jE_Cr{Geb6-xsX<T&Cn#pwgwRvH}c07uQh#|tDxsOb*)z!(YTDWqlw zB?M3*iiANz#AB570Ux15aR<aEWD=u+22QL<ZU8ZfcLPWziaS_g4k6$UZ2cOLVH99g zcfcG%$Q{&o2%0;f4k73c><&S$lt~O5bay};Lf9Qx9fFc)NOK5=JHQSh!W~!~f;vP+ zsXIUpA;ujT4nZs1DR2k0kIu+QlsnKIg60gUS;V>qC0=0TI7GSw#Ubd<pv)aG-DISE zltjt^^Aefi0n<)`JK#z|g(wmx)&&rC=)M3O1PWug@dOy~qy*N9L_o|Zn#AT165WAh zITDj-7eEzaaR@2yV8Wsw<Qk~yBv6<Jk>n00v@8d51T2}6;0o+bIs%CTNt%g?31Tw} zNty#-s?kG)wD7><4sd$}W;zKpnnNgc2dK?Ng6&93Q5`~|J3!8$N{CSA4u~_T;Sh@4 z0doeq4nZ0xr^FoyXOQm@M1n!em;{(;`4-t3)NlyGNmx1wsFuNaXoHGiVPs<{XQPhd af-S&Cj5-88R7OK&G(^xt0G*FLjQ{}OKa@iN literal 0 HcmV?d00001 diff --git a/tools/u-boot-tools/logos/toradex.bmp b/tools/u-boot-tools/logos/toradex.bmp new file mode 100644 index 0000000000000000000000000000000000000000..3e2dcf23358dd46fc7b1bb0dae70d3ba985606ee GIT binary patch literal 24982 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 literal 0 HcmV?d00001 diff --git a/tools/u-boot-tools/logos/u-boot_logo.bmp b/tools/u-boot-tools/logos/u-boot_logo.bmp new file mode 100644 index 0000000000000000000000000000000000000000..40245dd40db38e0f68ef9943e9598f1a47b84ac9 GIT binary patch literal 25738 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 F902cmBYpq? literal 0 HcmV?d00001 diff --git a/tools/u-boot-tools/logos/u-boot_logo.svg b/tools/u-boot-tools/logos/u-boot_logo.svg new file mode 100644 index 0000000..e45ef2e --- /dev/null +++ b/tools/u-boot-tools/logos/u-boot_logo.svg @@ -0,0 +1,248 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- SPDX-License-Identifier: CC-BY-SA-4.0 --> + +<!-- Copyright (c) 2018, Heinrich Schuchardt <xypron.glpk@gmx.de> --> + +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + width="186" + height="186" + viewBox="0 0 186 186" + id="svg2" + version="1.1" + inkscape:version="0.92.3 (2405546, 2018-03-11)" + sodipodi:docname="u-boot_logo.svg" + inkscape:export-filename="tools/logos/u-boot_logo.png" + inkscape:export-xdpi="41.290001" + inkscape:export-ydpi="41.290001"> + <title + id="title30">U-Boot Logo</title> + <metadata + id="metadata31"> + <rdf:RDF> + <cc:Work + rdf:about=""> + <dc:format>image/svg+xml</dc:format> + <dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> + <dc:title>U-Boot Logo</dc:title> + <cc:license + rdf:resource="http://creativecommons.org/licenses/by-sa/4.0/" /> + <dc:creator> + <cc:Agent> + <dc:title>Heinrich Schuchardt <xypron.glpk@gmx.de></dc:title> + </cc:Agent> + </dc:creator> + <dc:date>May 21st, 2018</dc:date> + </cc:Work> + <cc:License + rdf:about="http://creativecommons.org/licenses/by-sa/4.0/"> + <cc:permits + rdf:resource="http://creativecommons.org/ns#Reproduction" /> + <cc:permits + rdf:resource="http://creativecommons.org/ns#Distribution" /> + <cc:requires + rdf:resource="http://creativecommons.org/ns#Notice" /> + <cc:requires + rdf:resource="http://creativecommons.org/ns#Attribution" /> + <cc:permits + rdf:resource="http://creativecommons.org/ns#DerivativeWorks" /> + <cc:requires + rdf:resource="http://creativecommons.org/ns#ShareAlike" /> + </cc:License> + </rdf:RDF> + </metadata> + <defs + id="defs29" /> + <sodipodi:namedview + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1" + objecttolerance="10" + gridtolerance="10" + guidetolerance="10" + inkscape:pageopacity="0" + inkscape:pageshadow="2" + inkscape:window-width="1440" + inkscape:window-height="871" + id="namedview27" + showgrid="false" + inkscape:zoom="3" + inkscape:cx="93" + inkscape:cy="93" + inkscape:window-x="0" + inkscape:window-y="0" + inkscape:window-maximized="1" + inkscape:current-layer="layer1" /> + <g + inkscape:label="Layer 1" + inkscape:groupmode="layer" + id="layer1" + transform="translate(0,0)"> + <rect + style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:0" + id="rect31" + width="186" + height="186" + x="0" + y="0" /> + <circle + style="fill:#004466;fill-opacity:1;stroke-width:0;stroke:none" + id="path835" + cx="93" + cy="93" + r="93" /> + <path + inkscape:connector-curvature="0" + style="fill:#ffcc88;fill-opacity:1;stroke:none;stroke-width:0;stroke:none" + d="m 116,76 a 20,20 0 0 1 -20,-20 20,20 0 0 1 20,-20 v 11 a 9,9 0 0 0 -9,9 9,9 0 0 0 9,9 z" + id="path4136-6-6-1-6-3-5" /> + <path + inkscape:connector-curvature="0" + style="fill:#ffcc88;fill-opacity:1;stroke:none;stroke-width:0" + d="m 116,66 a 10,10 0 0 1 -10,-10 10,10 0 0 1 10,-10 v 1 a 9,9 0 0 0 -9,9 9,9 0 0 0 9,9 z" + id="path4136-6-6-1-6-3-5-1-9" /> + <ellipse + style="fill:#dd9955;fill-opacity:1;stroke:none;stroke-width:0" + id="path4136-6-6-1-6-3-5-1-2" + cx="116" + cy="41.5" + rx="4" + ry="5.5" /> + <circle + style="fill:#dd9955;fill-opacity:1;stroke:none;stroke-width:0" + id="path4352" + cx="86" + cy="66" + r="10" /> + <circle + style="fill:#dd9955;fill-opacity:1;stroke:none;stroke-width:0" + id="path4352-1" + cx="126" + cy="66" + r="10" /> + <rect + style="fill:#dd9955;fill-opacity:1;stroke:none;stroke-width:0" + id="rect4399" + width="39" + height="20" + x="86.5" + y="56" /> + <rect + style="fill:#dd9955;fill-opacity:1;stroke:none;stroke-width:0" + id="rect4660" + width="60" + height="9.5" + x="76" + y="66.5" /> + <circle + style="fill:#dd9955;fill-opacity:1;stroke:none;stroke-width:0" + id="path4549-5" + cx="36" + cy="81" + r="15" /> + <circle + style="fill:#dd9955;fill-opacity:1;stroke:none;stroke-width:0" + id="path4549-5-5" + cx="36" + cy="121" + r="15" /> + <ellipse + style="fill:#ffcc88;fill-opacity:1;stroke:#000000;stroke-width:0" + id="path4136-6-6" + cx="15" + cy="91" + rx="4" + ry="10" /> + <ellipse + style="fill:#ffcc88;fill-opacity:1;stroke:#000000;stroke-width:0" + id="path4136-6-6-1" + cx="15" + cy="111" + rx="4" + ry="10" /> + <rect + style="fill:#dd9955;fill-opacity:1;stroke:#000000;stroke-width:0" + id="rect4213" + width="65" + height="4" + x="11" + y="99" /> + <ellipse + style="fill:#ffcc88;fill-opacity:1;stroke:#000000;stroke-width:0" + id="path4136" + cx="100.5" + cy="100.5" + rx="74.5" + ry="34.5" /> + <ellipse + style="fill:#dd9955;fill-opacity:1;stroke:none;stroke-width:0" + id="path4416" + cx="70" + cy="95.5" + rx="15" + ry="12.5" /> + <ellipse + style="fill:#ffcc88;fill-opacity:1;stroke:none;stroke-width:0" + id="path4416-9" + cx="70" + cy="95.5" + rx="14" + ry="11.5" /> + <ellipse + style="fill:#dd9955;fill-opacity:1;stroke:none;stroke-width:0" + id="path4416-0" + cx="70" + cy="95.5" + rx="11" + ry="8.5" /> + <ellipse + style="fill:#dd9955;fill-opacity:1;stroke:none;stroke-width:0" + id="path4416-94" + cx="110" + cy="95.5" + rx="15" + ry="12.5" /> + <ellipse + style="fill:#ffcc88;fill-opacity:1;stroke:none;stroke-width:0" + id="path4416-9-1" + cx="110" + cy="95.5" + rx="14" + ry="11.5" /> + <ellipse + style="fill:#dd9955;fill-opacity:1;stroke:none;stroke-width:0" + id="path4416-0-1" + cx="110" + cy="95.5" + rx="11" + ry="8.5" /> + <ellipse + style="fill:#dd9955;fill-opacity:1;stroke:none;stroke-width:0" + id="path4416-94-2" + cx="150" + cy="95.5" + rx="15" + ry="12.5" /> + <ellipse + style="fill:#ffcc88;fill-opacity:1;stroke:none;stroke-width:0" + id="path4416-9-1-2" + cx="150" + cy="95.5" + rx="14" + ry="11.5" /> + <ellipse + style="fill:#dd9955;fill-opacity:1;stroke:none;stroke-width:0" + id="path4416-0-1-9" + cx="150" + cy="95.5" + rx="11" + ry="8.5" /> + </g> +</svg> diff --git a/tools/u-boot-tools/logos/wandboard.bmp b/tools/u-boot-tools/logos/wandboard.bmp new file mode 100644 index 0000000000000000000000000000000000000000..7f288a8e8eee052c96b9c2c2699e2e3d3efe1758 GIT binary patch literal 22390 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 bouWciO-`C)2D54wDSgl;puy}!y0{Gh<ycD{ literal 0 HcmV?d00001 diff --git a/tools/u-boot-tools/lpc32xximage.c b/tools/u-boot-tools/lpc32xximage.c new file mode 100644 index 0000000..37931f9 --- /dev/null +++ b/tools/u-boot-tools/lpc32xximage.c @@ -0,0 +1,177 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Image manipulator for LPC32XX SoCs + * + * (C) Copyright 2015 DENX Software Engineering GmbH + * Written-by: Albert ARIBAUD <albert.aribaud@3adev.fr> + * + * Derived from omapimage.c: + * + * (C) Copyright 2010 + * Linaro LTD, www.linaro.org + * Author: John Rigby <john.rigby@linaro.org> + * Based on TI's signGP.c + * + * (C) Copyright 2009 + * Stefano Babic, DENX Software Engineering, sbabic@denx.de. + * + * (C) Copyright 2008 + * Marvell Semiconductor <www.marvell.com> + * Written-by: Prafulla Wadaskar <prafulla@marvell.com> + */ + +#include "imagetool.h" +#include <compiler.h> +#include <image.h> + +/* + * NAND page 0 boot header + */ + +struct nand_page_0_boot_header { + uint32_t data[129]; + uint32_t pad[383]; +}; + +/* + * Default ICC (interface configuration data [sic]) if none specified + * in board config + */ + +#ifndef LPC32XX_BOOT_ICR +#define LPC32XX_BOOT_ICR 0x00000096 +#endif + +/* + * Default boot NAND page size if none specified in board config + */ + +#ifndef LPC32XX_BOOT_NAND_PAGESIZE +#define LPC32XX_BOOT_NAND_PAGESIZE 2048 +#endif + +/* + * Default boot NAND pages per sector if none specified in board config + */ + +#ifndef LPC32XX_BOOT_NAND_PAGES_PER_SECTOR +#define LPC32XX_BOOT_NAND_PAGES_PER_SECTOR 64 +#endif + +/* + * Maximum size for boot code is 56K unless defined in board config + */ + +#ifndef LPC32XX_BOOT_CODESIZE +#define LPC32XX_BOOT_CODESIZE (56*1024) +#endif + +/* signature byte for a readable block */ + +#define LPC32XX_BOOT_BLOCK_OK 0xaa + +static struct nand_page_0_boot_header lpc32xximage_header; + +static int lpc32xximage_check_image_types(uint8_t type) +{ + if (type == IH_TYPE_LPC32XXIMAGE) + return EXIT_SUCCESS; + return EXIT_FAILURE; +} + +static int lpc32xximage_verify_header(unsigned char *ptr, int image_size, + struct image_tool_params *params) +{ + struct nand_page_0_boot_header *hdr = + (struct nand_page_0_boot_header *)ptr; + + /* turn image size from bytes to NAND pages, page 0 included */ + int image_size_in_pages = ((image_size - 1) + / LPC32XX_BOOT_NAND_PAGESIZE); + + if (hdr->data[0] != (0xff & LPC32XX_BOOT_ICR)) + return -1; + if (hdr->data[1] != (0xff & ~LPC32XX_BOOT_ICR)) + return -1; + if (hdr->data[2] != (0xff & LPC32XX_BOOT_ICR)) + return -1; + if (hdr->data[3] != (0xff & ~LPC32XX_BOOT_ICR)) + return -1; + if (hdr->data[4] != (0xff & image_size_in_pages)) + return -1; + if (hdr->data[5] != (0xff & ~image_size_in_pages)) + return -1; + if (hdr->data[6] != (0xff & image_size_in_pages)) + return -1; + if (hdr->data[7] != (0xff & ~image_size_in_pages)) + return -1; + if (hdr->data[8] != (0xff & image_size_in_pages)) + return -1; + if (hdr->data[9] != (0xff & ~image_size_in_pages)) + return -1; + if (hdr->data[10] != (0xff & image_size_in_pages)) + return -1; + if (hdr->data[11] != (0xff & ~image_size_in_pages)) + return -1; + if (hdr->data[12] != LPC32XX_BOOT_BLOCK_OK) + return -1; + if (hdr->data[128] != LPC32XX_BOOT_BLOCK_OK) + return -1; + return 0; +} + +static void print_hdr_byte(struct nand_page_0_boot_header *hdr, int ofs) +{ + printf("header[%d] = %02x\n", ofs, hdr->data[ofs]); +} + +static void lpc32xximage_print_header(const void *ptr) +{ + struct nand_page_0_boot_header *hdr = + (struct nand_page_0_boot_header *)ptr; + int ofs; + + for (ofs = 0; ofs <= 12; ofs++) + print_hdr_byte(hdr, ofs); + print_hdr_byte(hdr, 128); +} + +static void lpc32xximage_set_header(void *ptr, struct stat *sbuf, int ifd, + struct image_tool_params *params) +{ + struct nand_page_0_boot_header *hdr = + (struct nand_page_0_boot_header *)ptr; + + /* turn image size from bytes to NAND pages, page 0 included */ + int image_size_in_pages = ((sbuf->st_size + + LPC32XX_BOOT_NAND_PAGESIZE - 1) + / LPC32XX_BOOT_NAND_PAGESIZE); + + /* fill header -- default byte value is 0x00, not 0xFF */ + memset((void *)hdr, 0, sizeof(*hdr)); + hdr->data[0] = (hdr->data[2] = 0xff & LPC32XX_BOOT_ICR); + hdr->data[1] = (hdr->data[3] = 0xff & ~LPC32XX_BOOT_ICR); + hdr->data[4] = (hdr->data[6] = (hdr->data[8] + = (hdr->data[10] = 0xff & image_size_in_pages))); + hdr->data[5] = (hdr->data[7] = (hdr->data[9] + = (hdr->data[11] = 0xff & ~image_size_in_pages))); + hdr->data[12] = (hdr->data[128] = LPC32XX_BOOT_BLOCK_OK); +} + +/* + * lpc32xximage parameters + */ +U_BOOT_IMAGE_TYPE( + lpc32xximage, + "LPC32XX Boot Image", + sizeof(lpc32xximage_header), + (void *)&lpc32xximage_header, + NULL, + lpc32xximage_verify_header, + lpc32xximage_print_header, + lpc32xximage_set_header, + NULL, + lpc32xximage_check_image_types, + NULL, + NULL +); diff --git a/tools/u-boot-tools/lpc32xximage.o b/tools/u-boot-tools/lpc32xximage.o new file mode 100644 index 0000000000000000000000000000000000000000..21f547ecd5b9490351692b05d8df50645074d2c2 GIT binary patch literal 3088 zcmb<-^>JfjWMqH=Mg}_u1P><4z;J;b!FB*M9T)@|1Q-ktI5hkh<)3=sa2mh73j;%M z+dp;&hURyS{GP`S{@`x3pT@wzP@36X&yrc1+*r>6=0-Kw^JJEW@Nc_d87@%j^Zf$< zwgZ;#BBf5&;S!~`*6uQ;7S`bkrN-9oDy4eX;Toly*6uo`Dvk99t3akn8y;xf&%ngM z0OIjaJ@EYo$nqAjt9JbV|NsBtG(CQK2;ZaI&A_Acmq%|shsW{u4^UrtbYAr6-SCf} zfx(0IKRW}12fybBkLKq;3=eo{{%kzRz`?-aaqu7a!I#XRJysug0$b~`1LXEh1_lO? zP9~2|Ck_YxZO6V}>vYoSbW-SalIV020Eu)u>2x}&bUMj&I*A-_g1PcA%sq}FKE0|S zmxg+D{tY&~-5oNQq4S<k=X1jY9?gdtUx1`Mnt$__KI@(e(j2|7fq{X+@Ib6buPsQ< zG3F(Rg5!+T#FW&cXw{Th1zQDG1EUHq2A=?DW21-&1*iP{5(UrP#Pn3SYCH@MJaXhn zxjQ>sDQLK)CS@k(DOl(k>lx^pX+jtv<3vCN0|NtNRS*MXg#e>84?D*MMg|4}1_lNh zs2mrR28kI!*=<l7B<2ETcR*<k1_p)*DBB%GJMjthFnRDvv@yHzDKxVr^GSH{33&41 z^C~FqvoL)857Lf~nX%>!1_ovZW^|=sJ~IOgm_{Z*?q)(V2j)k81_lO@I7l9(9_CLk zsQ3bqAOiyf%&#CWGXpD#LcvK;^#&l1FfcHH!vU&-nSl+;LQplJI6xAIh0P4GIE;q~ zSCCF-Q2qchknjeudL;iMv@$R=a3iTiFqs*65F9A$2$;t77f6_y0Tx;y9t59cfTS4+ z4?*6?A<mFfkZf#JQIQGC7kbGk-1y{-)a2}VuvC0WWkG5&s&rXuQD$0YJS5AZN)@M; zU==SY%FKhyAPkN#C@G3Zb_oNL9EvzH2WEqwkFx<oyt|LTlcP_(znfdIYe;;Eqmz$o zJOkLiGzJC+Q)v7%F)%O)GB7aw0hNpY|Njqwii2Vblm}trIZ$zQ^~F$eboH>121)b6 zI7l=|9Fzovp<=Lf4H5^L1r=scfHFYhAiH7eKzSJ?1rvv*XIMD^6NlvsNS(w0&RHOH zKzczKW<E#^gh65;Yyu*{c?Fd35NXH*O&n%^44OF1Jq&uqmANH}Nep_$B}EWA1I8*! z%}LZNNv$Yh&`U`yNo3GVDlTTwE6N9PK*|mE3>ox5iV%?x;e(ZfWb|_KlM{0o^pf*) zb5rw581(Y;OH%dR{X%t%OA?c_p{i3e;?s%}bBQYEq29nrfx-(Ew&*1VEPTsBl1RE? z@dXo-W?%r9dNB1c@f*-Aod6Z!1D6*J@RCFVDy{|<M=w#gquC2p4vuda8$|0cK+Htf ze*mT*lqf+8p%~;}C=*N>LhUyK^AH3~KgfLuQAm0N@nOchLiMBD{{pH2&3d^1L1ut3 zDBM8#14M)PFl+&h5Lh^Y!Ve?_GY=*nio<?TTLNSjtbPQsK^Wct0XX6>0Av{h1H%N6 zCI$uuko_RLK^P<ivmcZQL0*OBQ;-}8M?f>5EQo`IL25x<7@Y@oKS&H2mqPU;i-FiM zeIPanH$(NKE3beGfLc=^bubKbKa5|8!~YMU{zp!~pxO(jALf6Md!Saqj0Dp^pn^X@ z3K$p|_!$@&KxHGkeoh8ZdjhNlh4=tU6buXu&rn1W=?^9hD(lhH9!Sms+VbrMagZ?7 HIdBmG^X+1W literal 0 HcmV?d00001 diff --git a/tools/u-boot-tools/microcode-tool b/tools/u-boot-tools/microcode-tool new file mode 120000 index 0000000..8be8507 --- /dev/null +++ b/tools/u-boot-tools/microcode-tool @@ -0,0 +1 @@ +microcode-tool.py \ No newline at end of file diff --git a/tools/u-boot-tools/microcode-tool.py b/tools/u-boot-tools/microcode-tool.py new file mode 100755 index 0000000..249a33b --- /dev/null +++ b/tools/u-boot-tools/microcode-tool.py @@ -0,0 +1,316 @@ +#!/usr/bin/env python2 +# SPDX-License-Identifier: GPL-2.0+ +# +# Copyright (c) 2014 Google, Inc +# +# Intel microcode update tool + +from optparse import OptionParser +import os +import re +import struct +import sys + +MICROCODE_DIR = 'arch/x86/dts/microcode' + +class Microcode: + """Holds information about the microcode for a particular model of CPU. + + Attributes: + name: Name of the CPU this microcode is for, including any version + information (e.g. 'm12206a7_00000029') + model: Model code string (this is cpuid(1).eax, e.g. '206a7') + words: List of hex words containing the microcode. The first 16 words + are the public header. + """ + def __init__(self, name, data): + self.name = name + # Convert data into a list of hex words + self.words = [] + for value in ''.join(data).split(','): + hexval = value.strip() + if hexval: + self.words.append(int(hexval, 0)) + + # The model is in the 4rd hex word + self.model = '%x' % self.words[3] + +def ParseFile(fname): + """Parse a micrcode.dat file and return the component parts + + Args: + fname: Filename to parse + Returns: + 3-Tuple: + date: String containing date from the file's header + license_text: List of text lines for the license file + microcodes: List of Microcode objects from the file + """ + re_date = re.compile('/\* *(.* [0-9]{4}) *\*/$') + re_license = re.compile('/[^-*+] *(.*)$') + re_name = re.compile('/\* *(.*)\.inc *\*/', re.IGNORECASE) + microcodes = {} + license_text = [] + date = '' + data = [] + name = None + with open(fname) as fd: + for line in fd: + line = line.rstrip() + m_date = re_date.match(line) + m_license = re_license.match(line) + m_name = re_name.match(line) + if m_name: + if name: + microcodes[name] = Microcode(name, data) + name = m_name.group(1).lower() + data = [] + elif m_license: + license_text.append(m_license.group(1)) + elif m_date: + date = m_date.group(1) + else: + data.append(line) + if name: + microcodes[name] = Microcode(name, data) + return date, license_text, microcodes + +def ParseHeaderFiles(fname_list): + """Parse a list of header files and return the component parts + + Args: + fname_list: List of files to parse + Returns: + date: String containing date from the file's header + license_text: List of text lines for the license file + microcodes: List of Microcode objects from the file + """ + microcodes = {} + license_text = [] + date = '' + name = None + for fname in fname_list: + name = os.path.basename(fname).lower() + name = os.path.splitext(name)[0] + data = [] + with open(fname) as fd: + license_start = False + license_end = False + for line in fd: + line = line.rstrip() + + if len(line) >= 2: + if line[0] == '/' and line[1] == '*': + license_start = True + continue + if line[0] == '*' and line[1] == '/': + license_end = True + continue + if license_start and not license_end: + # Ignore blank line + if len(line) > 0: + license_text.append(line) + continue + # Omit anything after the last comma + words = line.split(',')[:-1] + data += [word + ',' for word in words] + microcodes[name] = Microcode(name, data) + return date, license_text, microcodes + + +def List(date, microcodes, model): + """List the available microcode chunks + + Args: + date: Date of the microcode file + microcodes: Dict of Microcode objects indexed by name + model: Model string to search for, or None + """ + print 'Date: %s' % date + if model: + mcode_list, tried = FindMicrocode(microcodes, model.lower()) + print 'Matching models %s:' % (', '.join(tried)) + else: + print 'All models:' + mcode_list = [microcodes[m] for m in microcodes.keys()] + for mcode in mcode_list: + print '%-20s: model %s' % (mcode.name, mcode.model) + +def FindMicrocode(microcodes, model): + """Find all the microcode chunks which match the given model. + + This model is something like 306a9 (the value returned in eax from + cpuid(1) when running on Intel CPUs). But we allow a partial match, + omitting the last 1 or two characters to allow many families to have the + same microcode. + + If the model name is ambiguous we return a list of matches. + + Args: + microcodes: Dict of Microcode objects indexed by name + model: String containing model name to find + Returns: + Tuple: + List of matching Microcode objects + List of abbreviations we tried + """ + # Allow a full name to be used + mcode = microcodes.get(model) + if mcode: + return [mcode], [] + + tried = [] + found = [] + for i in range(3): + abbrev = model[:-i] if i else model + tried.append(abbrev) + for mcode in microcodes.values(): + if mcode.model.startswith(abbrev): + found.append(mcode) + if found: + break + return found, tried + +def CreateFile(date, license_text, mcodes, outfile): + """Create a microcode file in U-Boot's .dtsi format + + Args: + date: String containing date of original microcode file + license: List of text lines for the license file + mcodes: Microcode objects to write (normally only 1) + outfile: Filename to write to ('-' for stdout) + """ + out = '''/*%s + * --- + * This is a device tree fragment. Use #include to add these properties to a + * node. + * + * Date: %s + */ + +compatible = "intel,microcode"; +intel,header-version = <%d>; +intel,update-revision = <%#x>; +intel,date-code = <%#x>; +intel,processor-signature = <%#x>; +intel,checksum = <%#x>; +intel,loader-revision = <%d>; +intel,processor-flags = <%#x>; + +/* The first 48-bytes are the public header which repeats the above data */ +data = <%s +\t>;''' + words = '' + add_comments = len(mcodes) > 1 + for mcode in mcodes: + if add_comments: + words += '\n/* %s */' % mcode.name + for i in range(len(mcode.words)): + if not (i & 3): + words += '\n' + val = mcode.words[i] + # Change each word so it will be little-endian in the FDT + # This data is needed before RAM is available on some platforms so + # we cannot do an endianness swap on boot. + val = struct.unpack("<I", struct.pack(">I", val))[0] + words += '\t%#010x' % val + + # Use the first microcode for the headers + mcode = mcodes[0] + + # Take care to avoid adding a space before a tab + text = '' + for line in license_text: + if line[0] == '\t': + text += '\n *' + line + else: + text += '\n * ' + line + args = [text, date] + args += [mcode.words[i] for i in range(7)] + args.append(words) + if outfile == '-': + print out % tuple(args) + else: + if not outfile: + if not os.path.exists(MICROCODE_DIR): + print >> sys.stderr, "Creating directory '%s'" % MICROCODE_DIR + os.makedirs(MICROCODE_DIR) + outfile = os.path.join(MICROCODE_DIR, mcode.name + '.dtsi') + print >> sys.stderr, "Writing microcode for '%s' to '%s'" % ( + ', '.join([mcode.name for mcode in mcodes]), outfile) + with open(outfile, 'w') as fd: + print >> fd, out % tuple(args) + +def MicrocodeTool(): + """Run the microcode tool""" + commands = 'create,license,list'.split(',') + parser = OptionParser() + parser.add_option('-d', '--mcfile', type='string', action='store', + help='Name of microcode.dat file') + parser.add_option('-H', '--headerfile', type='string', action='append', + help='Name of .h file containing microcode') + parser.add_option('-m', '--model', type='string', action='store', + help="Model name to extract ('all' for all)") + parser.add_option('-M', '--multiple', type='string', action='store', + help="Allow output of multiple models") + parser.add_option('-o', '--outfile', type='string', action='store', + help='Filename to use for output (- for stdout), default is' + ' %s/<name>.dtsi' % MICROCODE_DIR) + parser.usage += """ command + + Process an Intel microcode file (use -h for help). Commands: + + create Create microcode .dtsi file for a model + list List available models in microcode file + license Print the license + + Typical usage: + + ./tools/microcode-tool -d microcode.dat -m 306a create + + This will find the appropriate file and write it to %s.""" % MICROCODE_DIR + + (options, args) = parser.parse_args() + if not args: + parser.error('Please specify a command') + cmd = args[0] + if cmd not in commands: + parser.error("Unknown command '%s'" % cmd) + + if (not not options.mcfile) != (not not options.mcfile): + parser.error("You must specify either header files or a microcode file, not both") + if options.headerfile: + date, license_text, microcodes = ParseHeaderFiles(options.headerfile) + elif options.mcfile: + date, license_text, microcodes = ParseFile(options.mcfile) + else: + parser.error('You must specify a microcode file (or header files)') + + if cmd == 'list': + List(date, microcodes, options.model) + elif cmd == 'license': + print '\n'.join(license_text) + elif cmd == 'create': + if not options.model: + parser.error('You must specify a model to create') + model = options.model.lower() + if options.model == 'all': + options.multiple = True + mcode_list = microcodes.values() + tried = [] + else: + mcode_list, tried = FindMicrocode(microcodes, model) + if not mcode_list: + parser.error("Unknown model '%s' (%s) - try 'list' to list" % + (model, ', '.join(tried))) + if not options.multiple and len(mcode_list) > 1: + parser.error("Ambiguous model '%s' (%s) matched %s - try 'list' " + "to list or specify a particular file" % + (model, ', '.join(tried), + ', '.join([m.name for m in mcode_list]))) + CreateFile(date, license_text, mcode_list, options.outfile) + else: + parser.error("Unknown command '%s'" % cmd) + +if __name__ == "__main__": + MicrocodeTool() diff --git a/tools/u-boot-tools/mingw_support.c b/tools/u-boot-tools/mingw_support.c new file mode 100644 index 0000000..2b17bf7 --- /dev/null +++ b/tools/u-boot-tools/mingw_support.c @@ -0,0 +1,113 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2008 Extreme Engineering Solutions, Inc. + * + * mmap/munmap implementation derived from: + * Clamav Native Windows Port : mmap win32 compatibility layer + * Copyright (c) 2005-2006 Gianluigi Tiesi <sherpya@netfarm.it> + * Parts by Kees Zeelenberg <kzlg@users.sourceforge.net> (LibGW32C) + */ + +#include "mingw_support.h" +#include <stdio.h> +#include <stdint.h> +#include <string.h> +#include <errno.h> +#include <assert.h> +#include <io.h> + +int fsync(int fd) +{ + return _commit(fd); +} + +void *mmap(void *addr, size_t len, int prot, int flags, int fd, int offset) +{ + void *map = NULL; + HANDLE handle = INVALID_HANDLE_VALUE; + DWORD cfm_flags = 0, mvf_flags = 0; + + switch (prot) { + case PROT_READ | PROT_WRITE: + cfm_flags = PAGE_READWRITE; + mvf_flags = FILE_MAP_ALL_ACCESS; + break; + case PROT_WRITE: + cfm_flags = PAGE_READWRITE; + mvf_flags = FILE_MAP_WRITE; + break; + case PROT_READ: + cfm_flags = PAGE_READONLY; + mvf_flags = FILE_MAP_READ; + break; + default: + return MAP_FAILED; + } + + handle = CreateFileMappingA((HANDLE) _get_osfhandle(fd), NULL, + cfm_flags, HIDWORD(len), LODWORD(len), NULL); + if (!handle) + return MAP_FAILED; + + map = MapViewOfFile(handle, mvf_flags, HIDWORD(offset), + LODWORD(offset), len); + CloseHandle(handle); + + if (!map) + return MAP_FAILED; + + return map; +} + +int munmap(void *addr, size_t len) +{ + if (!UnmapViewOfFile(addr)) + return -1; + + return 0; +} + +/* Reentrant string tokenizer. Generic version. + Copyright (C) 1991,1996-1999,2001,2004,2007 Free Software Foundation, Inc. + This file is part of the GNU C Library. + */ + +/* Parse S into tokens separated by characters in DELIM. + If S is NULL, the saved pointer in SAVE_PTR is used as + the next starting point. For example: + char s[] = "-abc-=-def"; + char *sp; + x = strtok_r(s, "-", &sp); // x = "abc", sp = "=-def" + x = strtok_r(NULL, "-=", &sp); // x = "def", sp = NULL + x = strtok_r(NULL, "=", &sp); // x = NULL + // s = "abc\0-def\0" +*/ +char *strtok_r(char *s, const char *delim, char **save_ptr) +{ + char *token; + + if (s == NULL) + s = *save_ptr; + + /* Scan leading delimiters. */ + s += strspn(s, delim); + if (*s == '\0') { + *save_ptr = s; + return NULL; + } + + /* Find the end of the token. */ + token = s; + s = strpbrk (token, delim); + if (s == NULL) { + /* This token finishes the string. */ + *save_ptr = memchr(token, '\0', strlen(token)); + } else { + /* Terminate the token and make *SAVE_PTR point past it. */ + *s = '\0'; + *save_ptr = s + 1; + } + return token; +} + +#include "getline.c" diff --git a/tools/u-boot-tools/mingw_support.h b/tools/u-boot-tools/mingw_support.h new file mode 100644 index 0000000..e0b8ac3 --- /dev/null +++ b/tools/u-boot-tools/mingw_support.h @@ -0,0 +1,45 @@ +/* SPDX-License-Identifier: LGPL-2.0+ */ +/* + * Copyright 2008 Extreme Engineering Solutions, Inc. + */ + +#ifndef __MINGW_SUPPORT_H_ +#define __WINGW_SUPPORT_H_ 1 + +/* Defining __INSIDE_MSYS__ helps to prevent u-boot/mingw overlap */ +#define __INSIDE_MSYS__ 1 + +#include <windows.h> + +/* mmap protections */ +#define PROT_READ 0x1 /* Page can be read */ +#define PROT_WRITE 0x2 /* Page can be written */ +#define PROT_EXEC 0x4 /* Page can be executed */ +#define PROT_NONE 0x0 /* Page can not be accessed */ + +/* Sharing types (must choose one and only one of these) */ +#define MAP_SHARED 0x01 /* Share changes */ +#define MAP_PRIVATE 0x02 /* Changes are private */ + +/* File perms */ +#ifndef S_IRGRP +# define S_IRGRP 0 +#endif +#ifndef S_IWGRP +# define S_IWGRP 0 +#endif + +/* Windows 64-bit access macros */ +#define LODWORD(x) ((DWORD)((DWORDLONG)(x))) +#define HIDWORD(x) ((DWORD)(((DWORDLONG)(x) >> 32) & 0xffffffff)) + +typedef UINT uint; +typedef ULONG ulong; + +int fsync(int fd); +void *mmap(void *, size_t, int, int, int, int); +int munmap(void *, size_t); +char *strtok_r(char *s, const char *delim, char **save_ptr); +#include "getline.h" + +#endif /* __MINGW_SUPPORT_H_ */ diff --git a/tools/u-boot-tools/mips-relocs.c b/tools/u-boot-tools/mips-relocs.c new file mode 100644 index 0000000..6252580 --- /dev/null +++ b/tools/u-boot-tools/mips-relocs.c @@ -0,0 +1,418 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * MIPS Relocation Data Generator + * + * Copyright (c) 2017 Imagination Technologies Ltd. + */ + +#include <assert.h> +#include <elf.h> +#include <errno.h> +#include <fcntl.h> +#include <limits.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <sys/mman.h> +#include <sys/stat.h> +#include <unistd.h> + +#include <asm/relocs.h> + +#define hdr_field(pfx, idx, field) ({ \ + uint64_t _val; \ + unsigned int _size; \ + \ + if (is_64) { \ + _val = pfx##hdr64[idx].field; \ + _size = sizeof(pfx##hdr64[0].field); \ + } else { \ + _val = pfx##hdr32[idx].field; \ + _size = sizeof(pfx##hdr32[0].field); \ + } \ + \ + switch (_size) { \ + case 1: \ + break; \ + case 2: \ + _val = is_be ? be16toh(_val) : le16toh(_val); \ + break; \ + case 4: \ + _val = is_be ? be32toh(_val) : le32toh(_val); \ + break; \ + case 8: \ + _val = is_be ? be64toh(_val) : le64toh(_val); \ + break; \ + } \ + \ + _val; \ +}) + +#define set_hdr_field(pfx, idx, field, val) ({ \ + uint64_t _val; \ + unsigned int _size; \ + \ + if (is_64) \ + _size = sizeof(pfx##hdr64[0].field); \ + else \ + _size = sizeof(pfx##hdr32[0].field); \ + \ + switch (_size) { \ + case 1: \ + _val = val; \ + break; \ + case 2: \ + _val = is_be ? htobe16(val) : htole16(val); \ + break; \ + case 4: \ + _val = is_be ? htobe32(val) : htole32(val); \ + break; \ + case 8: \ + _val = is_be ? htobe64(val) : htole64(val); \ + break; \ + default: \ + /* We should never reach here */ \ + _val = 0; \ + assert(0); \ + break; \ + } \ + \ + if (is_64) \ + pfx##hdr64[idx].field = _val; \ + else \ + pfx##hdr32[idx].field = _val; \ +}) + +#define ehdr_field(field) \ + hdr_field(e, 0, field) +#define phdr_field(idx, field) \ + hdr_field(p, idx, field) +#define shdr_field(idx, field) \ + hdr_field(s, idx, field) + +#define set_phdr_field(idx, field, val) \ + set_hdr_field(p, idx, field, val) +#define set_shdr_field(idx, field, val) \ + set_hdr_field(s, idx, field, val) + +#define shstr(idx) (&shstrtab[idx]) + +bool is_64, is_be; +uint64_t text_base; + +struct mips_reloc { + uint8_t type; + uint64_t offset; +} *relocs; +size_t relocs_sz, relocs_idx; + +static int add_reloc(unsigned int type, uint64_t off) +{ + struct mips_reloc *new; + size_t new_sz; + + switch (type) { + case R_MIPS_NONE: + case R_MIPS_LO16: + case R_MIPS_PC16: + case R_MIPS_HIGHER: + case R_MIPS_HIGHEST: + case R_MIPS_PC21_S2: + case R_MIPS_PC26_S2: + /* Skip these relocs */ + return 0; + + default: + break; + } + + if (relocs_idx == relocs_sz) { + new_sz = relocs_sz ? relocs_sz * 2 : 128; + new = realloc(relocs, new_sz * sizeof(*relocs)); + if (!new) { + fprintf(stderr, "Out of memory\n"); + return -ENOMEM; + } + + relocs = new; + relocs_sz = new_sz; + } + + relocs[relocs_idx++] = (struct mips_reloc){ + .type = type, + .offset = off, + }; + + return 0; +} + +static int parse_mips32_rel(const void *_rel) +{ + const Elf32_Rel *rel = _rel; + uint32_t off, type; + + off = is_be ? be32toh(rel->r_offset) : le32toh(rel->r_offset); + off -= text_base; + + type = is_be ? be32toh(rel->r_info) : le32toh(rel->r_info); + type = ELF32_R_TYPE(type); + + return add_reloc(type, off); +} + +static int parse_mips64_rela(const void *_rel) +{ + const Elf64_Rela *rel = _rel; + uint64_t off, type; + + off = is_be ? be64toh(rel->r_offset) : le64toh(rel->r_offset); + off -= text_base; + + type = rel->r_info >> (64 - 8); + + return add_reloc(type, off); +} + +static void output_uint(uint8_t **buf, uint64_t val) +{ + uint64_t tmp; + + do { + tmp = val & 0x7f; + val >>= 7; + tmp |= !!val << 7; + *(*buf)++ = tmp; + } while (val); +} + +static int compare_relocs(const void *a, const void *b) +{ + const struct mips_reloc *ra = a, *rb = b; + + return ra->offset - rb->offset; +} + +int main(int argc, char *argv[]) +{ + unsigned int i, j, i_rel_shdr, sh_type, sh_entsize, sh_entries; + size_t rel_size, rel_actual_size; + const char *shstrtab, *sh_name, *rel_pfx; + int (*parse_fn)(const void *rel); + uint8_t *buf_start, *buf; + const Elf32_Ehdr *ehdr32; + const Elf64_Ehdr *ehdr64; + uintptr_t sh_offset; + Elf32_Shdr *shdr32; + Elf64_Shdr *shdr64; + struct stat st; + int err, fd; + void *elf; + bool skip; + + fd = open(argv[1], O_RDWR); + if (fd == -1) { + fprintf(stderr, "Unable to open input file %s\n", argv[1]); + err = errno; + goto out_ret; + } + + err = fstat(fd, &st); + if (err) { + fprintf(stderr, "Unable to fstat() input file\n"); + goto out_close_fd; + } + + elf = mmap(NULL, st.st_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + if (elf == MAP_FAILED) { + fprintf(stderr, "Unable to mmap() input file\n"); + err = errno; + goto out_close_fd; + } + + ehdr32 = elf; + ehdr64 = elf; + + if (memcmp(&ehdr32->e_ident[EI_MAG0], ELFMAG, SELFMAG)) { + fprintf(stderr, "Input file is not an ELF\n"); + err = -EINVAL; + goto out_free_relocs; + } + + if (ehdr32->e_ident[EI_VERSION] != EV_CURRENT) { + fprintf(stderr, "Unrecognised ELF version\n"); + err = -EINVAL; + goto out_free_relocs; + } + + switch (ehdr32->e_ident[EI_CLASS]) { + case ELFCLASS32: + is_64 = false; + break; + case ELFCLASS64: + is_64 = true; + break; + default: + fprintf(stderr, "Unrecognised ELF class\n"); + err = -EINVAL; + goto out_free_relocs; + } + + switch (ehdr32->e_ident[EI_DATA]) { + case ELFDATA2LSB: + is_be = false; + break; + case ELFDATA2MSB: + is_be = true; + break; + default: + fprintf(stderr, "Unrecognised ELF data encoding\n"); + err = -EINVAL; + goto out_free_relocs; + } + + if (ehdr_field(e_type) != ET_EXEC) { + fprintf(stderr, "Input ELF is not an executable\n"); + printf("type 0x%lx\n", ehdr_field(e_type)); + err = -EINVAL; + goto out_free_relocs; + } + + if (ehdr_field(e_machine) != EM_MIPS) { + fprintf(stderr, "Input ELF does not target MIPS\n"); + err = -EINVAL; + goto out_free_relocs; + } + + shdr32 = elf + ehdr_field(e_shoff); + shdr64 = elf + ehdr_field(e_shoff); + shstrtab = elf + shdr_field(ehdr_field(e_shstrndx), sh_offset); + + i_rel_shdr = UINT_MAX; + for (i = 0; i < ehdr_field(e_shnum); i++) { + sh_name = shstr(shdr_field(i, sh_name)); + + if (!strcmp(sh_name, ".data.reloc")) { + i_rel_shdr = i; + continue; + } + + if (!strcmp(sh_name, ".text")) { + text_base = shdr_field(i, sh_addr); + continue; + } + } + if (i_rel_shdr == UINT_MAX) { + fprintf(stderr, "Unable to find .rel section\n"); + err = -EINVAL; + goto out_free_relocs; + } + if (!text_base) { + fprintf(stderr, "Unable to find .text base address\n"); + err = -EINVAL; + goto out_free_relocs; + } + + rel_pfx = is_64 ? ".rela." : ".rel."; + + for (i = 0; i < ehdr_field(e_shnum); i++) { + sh_type = shdr_field(i, sh_type); + if ((sh_type != SHT_REL) && (sh_type != SHT_RELA)) + continue; + + sh_name = shstr(shdr_field(i, sh_name)); + if (strncmp(sh_name, rel_pfx, strlen(rel_pfx))) { + if (strcmp(sh_name, ".rel") && strcmp(sh_name, ".rel.dyn")) + fprintf(stderr, "WARNING: Unexpected reloc section name '%s'\n", sh_name); + continue; + } + + /* + * Skip reloc sections which either don't correspond to another + * section in the ELF, or whose corresponding section isn't + * loaded as part of the U-Boot binary (ie. doesn't have the + * alloc flags set). + */ + skip = true; + for (j = 0; j < ehdr_field(e_shnum); j++) { + if (strcmp(&sh_name[strlen(rel_pfx) - 1], shstr(shdr_field(j, sh_name)))) + continue; + + skip = !(shdr_field(j, sh_flags) & SHF_ALLOC); + break; + } + if (skip) + continue; + + sh_offset = shdr_field(i, sh_offset); + sh_entsize = shdr_field(i, sh_entsize); + sh_entries = shdr_field(i, sh_size) / sh_entsize; + + if (sh_type == SHT_REL) { + if (is_64) { + fprintf(stderr, "REL-style reloc in MIPS64 ELF?\n"); + err = -EINVAL; + goto out_free_relocs; + } else { + parse_fn = parse_mips32_rel; + } + } else { + if (is_64) { + parse_fn = parse_mips64_rela; + } else { + fprintf(stderr, "RELA-style reloc in MIPS32 ELF?\n"); + err = -EINVAL; + goto out_free_relocs; + } + } + + for (j = 0; j < sh_entries; j++) { + err = parse_fn(elf + sh_offset + (j * sh_entsize)); + if (err) + goto out_free_relocs; + } + } + + /* Sort relocs in ascending order of offset */ + qsort(relocs, relocs_idx, sizeof(*relocs), compare_relocs); + + /* Make reloc offsets relative to their predecessor */ + for (i = relocs_idx - 1; i > 0; i--) + relocs[i].offset -= relocs[i - 1].offset; + + /* Write the relocations to the .rel section */ + buf = buf_start = elf + shdr_field(i_rel_shdr, sh_offset); + for (i = 0; i < relocs_idx; i++) { + output_uint(&buf, relocs[i].type); + output_uint(&buf, relocs[i].offset >> 2); + } + + /* Write a terminating R_MIPS_NONE (0) */ + output_uint(&buf, R_MIPS_NONE); + + /* Ensure the relocs didn't overflow the .rel section */ + rel_size = shdr_field(i_rel_shdr, sh_size); + rel_actual_size = buf - buf_start; + if (rel_actual_size > rel_size) { + fprintf(stderr, "Relocations overflow available space of 0x%lx (required 0x%lx)!\n", + rel_size, rel_actual_size); + fprintf(stderr, "Please adjust CONFIG_MIPS_RELOCATION_TABLE_SIZE to at least 0x%lx\n", + (rel_actual_size + 0x100) & ~0xFF); + err = -ENOMEM; + goto out_free_relocs; + } + + /* Make sure data is written back to the file */ + err = msync(elf, st.st_size, MS_SYNC); + if (err) { + fprintf(stderr, "Failed to msync: %d\n", errno); + goto out_free_relocs; + } + +out_free_relocs: + free(relocs); + munmap(elf, st.st_size); +out_close_fd: + close(fd); +out_ret: + return err; +} diff --git a/tools/u-boot-tools/mkenvimage b/tools/u-boot-tools/mkenvimage new file mode 100755 index 0000000000000000000000000000000000000000..86bbcc097200eea97c69326c0ac58e93a01b0564 GIT binary patch literal 18120 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 literal 0 HcmV?d00001 diff --git a/tools/u-boot-tools/mkenvimage.c b/tools/u-boot-tools/mkenvimage.c new file mode 100644 index 0000000..75967d0 --- /dev/null +++ b/tools/u-boot-tools/mkenvimage.c @@ -0,0 +1,314 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2011 Free Electrons + * David Wagner <david.wagner@free-electrons.com> + * + * Inspired from envcrc.c: + * (C) Copyright 2001 + * Paolo Scaffardi, AIRVENT SAM s.p.a - RIMINI(ITALY), arsenio@tin.it + */ + +#include <errno.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <stdint.h> +#include <string.h> +#include <unistd.h> +#include <libgen.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/mman.h> + +#include "compiler.h" +#include <u-boot/crc.h> +#include <version.h> + +#define CRC_SIZE sizeof(uint32_t) + +static void usage(const char *exec_name) +{ + fprintf(stderr, "%s [-h] [-r] [-b] [-p <byte>] -s <environment partition size> -o <output> <input file>\n" + "\n" + "This tool takes a key=value input file (same as would a `printenv' show) and generates the corresponding environment image, ready to be flashed.\n" + "\n" + "\tThe input file is in format:\n" + "\t\tkey1=value1\n" + "\t\tkey2=value2\n" + "\t\t...\n" + "\tEmpty lines are skipped, and lines with a # in the first\n" + "\tcolumn are treated as comments (also skipped).\n" + "\t-r : the environment has multiple copies in flash\n" + "\t-b : the target is big endian (default is little endian)\n" + "\t-p <byte> : fill the image with <byte> bytes instead of 0xff bytes\n" + "\t-V : print version information and exit\n" + "\n" + "If the input file is \"-\", data is read from standard input\n", + exec_name); +} + +long int xstrtol(const char *s) +{ + long int tmp; + + errno = 0; + tmp = strtol(s, NULL, 0); + if (!errno) + return tmp; + + if (errno == ERANGE) + fprintf(stderr, "Bad integer format: %s\n", s); + else + fprintf(stderr, "Error while parsing %s: %s\n", s, + strerror(errno)); + + exit(EXIT_FAILURE); +} + +int main(int argc, char **argv) +{ + uint32_t crc, targetendian_crc; + const char *txt_filename = NULL, *bin_filename = NULL; + int txt_fd, bin_fd; + unsigned char *dataptr, *envptr; + unsigned char *filebuf = NULL; + unsigned int filesize = 0, envsize = 0, datasize = 0; + int bigendian = 0; + int redundant = 0; + unsigned char padbyte = 0xff; + + int option; + int ret = EXIT_SUCCESS; + + struct stat txt_file_stat; + + int fp, ep; + const char *prg; + + prg = basename(argv[0]); + + /* Turn off getopt()'s internal error message */ + opterr = 0; + + /* Parse the cmdline */ + while ((option = getopt(argc, argv, ":s:o:rbp:hV")) != -1) { + switch (option) { + case 's': + datasize = xstrtol(optarg); + break; + case 'o': + bin_filename = strdup(optarg); + if (!bin_filename) { + fprintf(stderr, "Can't strdup() the output filename\n"); + return EXIT_FAILURE; + } + break; + case 'r': + redundant = 1; + break; + case 'b': + bigendian = 1; + break; + case 'p': + padbyte = xstrtol(optarg); + break; + case 'h': + usage(prg); + return EXIT_SUCCESS; + case 'V': + printf("%s version %s\n", prg, PLAIN_VERSION); + return EXIT_SUCCESS; + case ':': + fprintf(stderr, "Missing argument for option -%c\n", + optopt); + usage(prg); + return EXIT_FAILURE; + default: + fprintf(stderr, "Wrong option -%c\n", optopt); + usage(prg); + return EXIT_FAILURE; + } + } + + /* Check datasize and allocate the data */ + if (datasize == 0) { + fprintf(stderr, "Please specify the size of the environment partition.\n"); + usage(prg); + return EXIT_FAILURE; + } + + dataptr = malloc(datasize * sizeof(*dataptr)); + if (!dataptr) { + fprintf(stderr, "Can't alloc %d bytes for dataptr.\n", + datasize); + return EXIT_FAILURE; + } + + /* + * envptr points to the beginning of the actual environment (after the + * crc and possible `redundant' byte + */ + envsize = datasize - (CRC_SIZE + redundant); + envptr = dataptr + CRC_SIZE + redundant; + + /* Pad the environment with the padding byte */ + memset(envptr, padbyte, envsize); + + /* Open the input file ... */ + if (optind >= argc || strcmp(argv[optind], "-") == 0) { + int readbytes = 0; + int readlen = sizeof(*envptr) * 4096; + txt_fd = STDIN_FILENO; + + do { + filebuf = realloc(filebuf, filesize + readlen); + if (!filebuf) { + fprintf(stderr, "Can't realloc memory for the input file buffer\n"); + return EXIT_FAILURE; + } + readbytes = read(txt_fd, filebuf + filesize, readlen); + if (readbytes < 0) { + fprintf(stderr, "Error while reading stdin: %s\n", + strerror(errno)); + return EXIT_FAILURE; + } + filesize += readbytes; + } while (readbytes == readlen); + + } else { + txt_filename = argv[optind]; + txt_fd = open(txt_filename, O_RDONLY); + if (txt_fd == -1) { + fprintf(stderr, "Can't open \"%s\": %s\n", + txt_filename, strerror(errno)); + return EXIT_FAILURE; + } + /* ... and check it */ + ret = fstat(txt_fd, &txt_file_stat); + if (ret == -1) { + fprintf(stderr, "Can't stat() on \"%s\": %s\n", + txt_filename, strerror(errno)); + return EXIT_FAILURE; + } + + filesize = txt_file_stat.st_size; + + filebuf = mmap(NULL, sizeof(*envptr) * filesize, PROT_READ, + MAP_PRIVATE, txt_fd, 0); + if (filebuf == MAP_FAILED) { + fprintf(stderr, "mmap (%zu bytes) failed: %s\n", + sizeof(*envptr) * filesize, + strerror(errno)); + fprintf(stderr, "Falling back to read()\n"); + + filebuf = malloc(sizeof(*envptr) * filesize); + ret = read(txt_fd, filebuf, sizeof(*envptr) * filesize); + if (ret != sizeof(*envptr) * filesize) { + fprintf(stderr, "Can't read the whole input file (%zu bytes): %s\n", + sizeof(*envptr) * filesize, + strerror(errno)); + + return EXIT_FAILURE; + } + } + ret = close(txt_fd); + } + + /* Parse a byte at time until reaching the file OR until the environment fills + * up. Check ep against envsize - 1 to allow for extra trailing '\0'. */ + for (fp = 0, ep = 0 ; fp < filesize && ep < envsize - 1; fp++) { + if (filebuf[fp] == '\n') { + if (fp == 0 || filebuf[fp-1] == '\n') { + /* + * Skip empty lines. + */ + continue; + } else if (filebuf[fp-1] == '\\') { + /* + * Embedded newline in a variable. + * + * The backslash was added to the envptr; rewind + * and replace it with a newline + */ + ep--; + envptr[ep++] = '\n'; + } else { + /* End of a variable */ + envptr[ep++] = '\0'; + } + } else if ((fp == 0 || filebuf[fp-1] == '\n') && filebuf[fp] == '#') { + /* Comment, skip the line. */ + while (++fp < filesize && filebuf[fp] != '\n') + continue; + } else { + envptr[ep++] = filebuf[fp]; + } + } + /* If there are more bytes in the file still, it means the env filled up + * before parsing the whole file. Eat comments & whitespace here to see if + * there was anything meaning full left in the file, and if so, throw a error + * and exit. */ + for( ; fp < filesize; fp++ ) + { + if (filebuf[fp] == '\n') { + if (fp == 0 || filebuf[fp-1] == '\n') { + /* Ignore blank lines */ + continue; + } + } else if ((fp == 0 || filebuf[fp-1] == '\n') && filebuf[fp] == '#') { + while (++fp < filesize && filebuf[fp] != '\n') + continue; + } else { + fprintf(stderr, "The environment file is too large for the target environment storage\n"); + return EXIT_FAILURE; + } + } + /* + * Make sure there is a final '\0' + * And do it again on the next byte to mark the end of the environment. + */ + if (envptr[ep-1] != '\0') { + envptr[ep++] = '\0'; + /* + * The text file doesn't have an ending newline. We need to + * check the env size again to make sure we have room for two \0 + */ + if (ep >= envsize) { + fprintf(stderr, "The environment file is too large for the target environment storage\n"); + return EXIT_FAILURE; + } + envptr[ep] = '\0'; + } else { + envptr[ep] = '\0'; + } + + /* Computes the CRC and put it at the beginning of the data */ + crc = crc32(0, envptr, envsize); + targetendian_crc = bigendian ? cpu_to_be32(crc) : cpu_to_le32(crc); + + memcpy(dataptr, &targetendian_crc, sizeof(targetendian_crc)); + if (redundant) + dataptr[sizeof(targetendian_crc)] = 1; + + if (!bin_filename || strcmp(bin_filename, "-") == 0) { + bin_fd = STDOUT_FILENO; + } else { + bin_fd = creat(bin_filename, S_IRUSR | S_IWUSR | S_IRGRP | + S_IWGRP); + if (bin_fd == -1) { + fprintf(stderr, "Can't open output file \"%s\": %s\n", + bin_filename, strerror(errno)); + return EXIT_FAILURE; + } + } + + if (write(bin_fd, dataptr, sizeof(*dataptr) * datasize) != + sizeof(*dataptr) * datasize) { + fprintf(stderr, "write() failed: %s\n", strerror(errno)); + return EXIT_FAILURE; + } + + ret = close(bin_fd); + + return ret; +} diff --git a/tools/u-boot-tools/mkenvimage.o b/tools/u-boot-tools/mkenvimage.o new file mode 100644 index 0000000000000000000000000000000000000000..762c00f0575b2b809e547e76cc12fed51bf4ecef GIT binary patch literal 10800 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 literal 0 HcmV?d00001 diff --git a/tools/u-boot-tools/mkexynosspl.c b/tools/u-boot-tools/mkexynosspl.c new file mode 100644 index 0000000..53122b8 --- /dev/null +++ b/tools/u-boot-tools/mkexynosspl.c @@ -0,0 +1,186 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2012 Samsung Electronics + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <fcntl.h> +#include <errno.h> +#include <string.h> +#include <sys/stat.h> +#include <compiler.h> + +#define CHECKSUM_OFFSET (14*1024-4) +#define FILE_PERM (S_IRUSR | S_IWUSR | S_IRGRP \ + | S_IWGRP | S_IROTH | S_IWOTH) +/* + * Requirement for the fixed size SPL header: + * IROM code reads first (CHECKSUM_OFFSET + 4) bytes from boot device. It then + * calculates the checksum of CHECKSUM_OFFSET bytes and compares with data at + * CHECKSUM_OFFSET location. + * + * Requirement for the variable size SPL header: + + * IROM code reads the below header to find out the size of the blob (total + * size, header size included) and its checksum. Then it reads the rest of the + * blob [i.e size - sizeof(struct var_size_header) bytes], calculates the + * checksum and compares it with value read from the header. + */ +struct var_size_header { + uint32_t spl_size; + uint32_t spl_checksum; + uint32_t reserved[2]; +}; + +static const char *prog_name; + +static void write_to_file(int ofd, void *buffer, int size) +{ + if (write(ofd, buffer, size) == size) + return; + + fprintf(stderr, "%s: Failed to write to output file: %s\n", + prog_name, strerror(errno)); + exit(EXIT_FAILURE); +} + +/* + * The argv is expected to include one optional parameter and two filenames: + * [--vs] IN OUT + * + * --vs - turns on the variable size SPL mode + * IN - the u-boot SPL binary, usually u-boot-spl.bin + * OUT - the prepared SPL blob, usually ${BOARD}-spl.bin + * + * This utility first reads the "u-boot-spl.bin" into a buffer. In case of + * fixed size SPL the buffer size is exactly CHECKSUM_OFFSET (such that + * smaller u-boot-spl.bin gets padded with 0xff bytes, the larger than limit + * u-boot-spl.bin causes an error). For variable size SPL the buffer size is + * eqaul to size of the IN file. + * + * Then it calculates checksum of the buffer by just summing up all bytes. + * Then + * + * - for fixed size SPL the buffer is written into the output file and the + * checksum is appended to the file in little endian format, which results + * in checksum added exactly at CHECKSUM_OFFSET. + * + * - for variable size SPL the checksum and file size are stored in the + * var_size_header structure (again, in little endian format) and the + * structure is written into the output file. Then the buffer is written + * into the output file. + */ +int main(int argc, char **argv) +{ + unsigned char *buffer; + int i, ifd, ofd; + uint32_t checksum = 0; + off_t len; + int var_size_flag, read_size, count; + struct stat stat; + const int if_index = argc - 2; /* Input file name index in argv. */ + const int of_index = argc - 1; /* Output file name index in argv. */ + + /* Strip path off the program name. */ + prog_name = strrchr(argv[0], '/'); + if (prog_name) + prog_name++; + else + prog_name = argv[0]; + + if ((argc < 3) || + (argc > 4) || + ((argc == 4) && strcmp(argv[1], "--vs"))) { + fprintf(stderr, "Usage: %s [--vs] <infile> <outfile>\n", + prog_name); + exit(EXIT_FAILURE); + } + + /* four args mean variable size SPL wrapper is required */ + var_size_flag = (argc == 4); + + ifd = open(argv[if_index], O_RDONLY); + if (ifd < 0) { + fprintf(stderr, "%s: Can't open %s: %s\n", + prog_name, argv[if_index], strerror(errno)); + exit(EXIT_FAILURE); + } + + ofd = open(argv[of_index], O_WRONLY | O_CREAT | O_TRUNC, FILE_PERM); + if (ofd < 0) { + fprintf(stderr, "%s: Can't open %s: %s\n", + prog_name, argv[of_index], strerror(errno)); + exit(EXIT_FAILURE); + } + + if (fstat(ifd, &stat)) { + fprintf(stderr, "%s: Unable to get size of %s: %s\n", + prog_name, argv[if_index], strerror(errno)); + exit(EXIT_FAILURE); + } + + len = stat.st_size; + + if (var_size_flag) { + read_size = len; + count = len; + } else { + if (len > CHECKSUM_OFFSET) { + fprintf(stderr, + "%s: %s is too big (exceeds %d bytes)\n", + prog_name, argv[if_index], CHECKSUM_OFFSET); + exit(EXIT_FAILURE); + } + count = CHECKSUM_OFFSET; + read_size = len; + } + + buffer = malloc(count); + if (!buffer) { + fprintf(stderr, + "%s: Failed to allocate %d bytes to store %s\n", + prog_name, count, argv[if_index]); + exit(EXIT_FAILURE); + } + + if (read(ifd, buffer, read_size) != read_size) { + fprintf(stderr, "%s: Can't read %s: %s\n", + prog_name, argv[if_index], strerror(errno)); + exit(EXIT_FAILURE); + } + + /* Pad if needed with 0xff to make flashing faster. */ + if (read_size < count) + memset((char *)buffer + read_size, 0xff, count - read_size); + + for (i = 0, checksum = 0; i < count; i++) + checksum += buffer[i]; + checksum = cpu_to_le32(checksum); + + if (var_size_flag) { + /* Prepare and write out the variable size SPL header. */ + struct var_size_header vsh; + uint32_t spl_size; + + memset(&vsh, 0, sizeof(vsh)); + memcpy(&vsh.spl_checksum, &checksum, sizeof(checksum)); + + spl_size = cpu_to_le32(count + sizeof(struct var_size_header)); + memcpy(&vsh.spl_size, &spl_size, sizeof(spl_size)); + write_to_file(ofd, &vsh, sizeof(vsh)); + } + + write_to_file(ofd, buffer, count); + + /* For fixed size SPL checksum is appended in the end. */ + if (!var_size_flag) + write_to_file(ofd, &checksum, sizeof(checksum)); + + close(ifd); + close(ofd); + free(buffer); + + return EXIT_SUCCESS; +} diff --git a/tools/u-boot-tools/mkimage b/tools/u-boot-tools/mkimage new file mode 100755 index 0000000000000000000000000000000000000000..86dc186443460707274d2f5de515c083f57bfe97 GIT binary patch literal 242816 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 literal 0 HcmV?d00001 diff --git a/tools/u-boot-tools/mkimage.c b/tools/u-boot-tools/mkimage.c new file mode 100644 index 0000000..ea5ed54 --- /dev/null +++ b/tools/u-boot-tools/mkimage.c @@ -0,0 +1,740 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2008 Semihalf + * + * (C) Copyright 2000-2009 + * DENX Software Engineering + * Wolfgang Denk, wd@denx.de + */ + +#include "mkimage.h" +#include "imximage.h" +#include <image.h> +#include <version.h> + +static void copy_file(int, const char *, int); + +/* parameters initialized by core will be used by the image type code */ +static struct image_tool_params params = { + .os = IH_OS_LINUX, + .arch = IH_ARCH_PPC, + .type = IH_TYPE_KERNEL, + .comp = IH_COMP_GZIP, + .dtc = MKIMAGE_DEFAULT_DTC_OPTIONS, + .imagename = "", + .imagename2 = "", +}; + +static enum ih_category cur_category; + +static int h_compare_category_name(const void *vtype1, const void *vtype2) +{ + const int *type1 = vtype1; + const int *type2 = vtype2; + const char *name1 = genimg_get_cat_short_name(cur_category, *type1); + const char *name2 = genimg_get_cat_short_name(cur_category, *type2); + + return strcmp(name1, name2); +} + +static int show_valid_options(enum ih_category category) +{ + int *order; + int count; + int item; + int i; + + count = genimg_get_cat_count(category); + order = calloc(count, sizeof(*order)); + if (!order) + return -ENOMEM; + + /* Sort the names in order of short name for easier reading */ + for (item = 0; item < count; item++) + order[item] = item; + cur_category = category; + qsort(order, count, sizeof(int), h_compare_category_name); + + fprintf(stderr, "\nInvalid %s, supported are:\n", + genimg_get_cat_desc(category)); + for (i = 0; i < count; i++) { + item = order[i]; + fprintf(stderr, "\t%-15s %s\n", + genimg_get_cat_short_name(category, item), + genimg_get_cat_name(category, item)); + } + fprintf(stderr, "\n"); + free(order); + + return 0; +} + +static void usage(const char *msg) +{ + fprintf(stderr, "Error: %s\n", msg); + fprintf(stderr, "Usage: %s -l image\n" + " -l ==> list image header information\n", + params.cmdname); + fprintf(stderr, + " %s [-x] -A arch -O os -T type -C comp -a addr -e ep -n name -d data_file[:data_file...] image\n" + " -A ==> set architecture to 'arch'\n" + " -O ==> set operating system to 'os'\n" + " -T ==> set image type to 'type'\n" + " -C ==> set compression type 'comp'\n" + " -a ==> set load address to 'addr' (hex)\n" + " -e ==> set entry point to 'ep' (hex)\n" + " -n ==> set image name to 'name'\n" + " -d ==> use image data from 'datafile'\n" + " -x ==> set XIP (execute in place)\n", + params.cmdname); + fprintf(stderr, + " %s [-D dtc_options] [-f fit-image.its|-f auto|-F] [-b <dtb> [-b <dtb>]] [-i <ramdisk.cpio.gz>] fit-image\n" + " <dtb> file is used with -f auto, it may occur multiple times.\n", + params.cmdname); + fprintf(stderr, + " -D => set all options for device tree compiler\n" + " -f => input filename for FIT source\n" + " -i => input filename for ramdisk file\n"); +#ifdef CONFIG_FIT_SIGNATURE + fprintf(stderr, + "Signing / verified boot options: [-E] [-k keydir] [-K dtb] [ -c <comment>] [-p addr] [-r] [-N engine]\n" + " -E => place data outside of the FIT structure\n" + " -k => set directory containing private keys\n" + " -K => write public keys to this .dtb file\n" + " -c => add comment in signature node\n" + " -F => re-sign existing FIT image\n" + " -p => place external data at a static position\n" + " -r => mark keys used as 'required' in dtb\n" + " -N => engine to use for signing (pkcs11)\n"); +#else + fprintf(stderr, + "Signing / verified boot not supported (CONFIG_FIT_SIGNATURE undefined)\n"); +#endif + fprintf(stderr, " %s -V ==> print version information and exit\n", + params.cmdname); + fprintf(stderr, "Use '-T list' to see a list of available image types\n"); + + exit(EXIT_FAILURE); +} + +static int add_content(int type, const char *fname) +{ + struct content_info *cont; + + cont = calloc(1, sizeof(*cont)); + if (!cont) + return -1; + cont->type = type; + cont->fname = fname; + if (params.content_tail) + params.content_tail->next = cont; + else + params.content_head = cont; + params.content_tail = cont; + + return 0; +} + +static void process_args(int argc, char **argv) +{ + char *ptr; + int type = IH_TYPE_INVALID; + char *datafile = NULL; + int opt; + + while ((opt = getopt(argc, argv, + "a:A:b:c:C:d:D:e:Ef:Fk:i:K:ln:N:p:O:rR:qsT:vVx")) != -1) { + switch (opt) { + case 'a': + params.addr = strtoull(optarg, &ptr, 16); + if (*ptr) { + fprintf(stderr, "%s: invalid load address %s\n", + params.cmdname, optarg); + exit(EXIT_FAILURE); + } + break; + case 'A': + params.arch = genimg_get_arch_id(optarg); + if (params.arch < 0) { + show_valid_options(IH_ARCH); + usage("Invalid architecture"); + } + break; + case 'b': + if (add_content(IH_TYPE_FLATDT, optarg)) { + fprintf(stderr, + "%s: Out of memory adding content '%s'", + params.cmdname, optarg); + exit(EXIT_FAILURE); + } + break; + case 'c': + params.comment = optarg; + break; + case 'C': + params.comp = genimg_get_comp_id(optarg); + if (params.comp < 0) { + show_valid_options(IH_COMP); + usage("Invalid compression type"); + } + break; + case 'd': + params.datafile = optarg; + params.dflag = 1; + break; + case 'D': + params.dtc = optarg; + break; + case 'e': + params.ep = strtoull(optarg, &ptr, 16); + if (*ptr) { + fprintf(stderr, "%s: invalid entry point %s\n", + params.cmdname, optarg); + exit(EXIT_FAILURE); + } + params.eflag = 1; + break; + case 'E': + params.external_data = true; + break; + case 'f': + datafile = optarg; + params.auto_its = !strcmp(datafile, "auto"); + /* no break */ + case 'F': + /* + * The flattened image tree (FIT) format + * requires a flattened device tree image type + */ + params.type = IH_TYPE_FLATDT; + params.fflag = 1; + break; + case 'i': + params.fit_ramdisk = optarg; + break; + case 'k': + params.keydir = optarg; + break; + case 'K': + params.keydest = optarg; + break; + case 'l': + params.lflag = 1; + break; + case 'n': + params.imagename = optarg; + break; + case 'N': + params.engine_id = optarg; + break; + case 'O': + params.os = genimg_get_os_id(optarg); + if (params.os < 0) { + show_valid_options(IH_OS); + usage("Invalid operating system"); + } + break; + case 'p': + params.external_offset = strtoull(optarg, &ptr, 16); + if (*ptr) { + fprintf(stderr, "%s: invalid offset size %s\n", + params.cmdname, optarg); + exit(EXIT_FAILURE); + } + break; + case 'q': + params.quiet = 1; + break; + case 'r': + params.require_keys = 1; + break; + case 'R': + /* + * This entry is for the second configuration + * file, if only one is not enough. + */ + params.imagename2 = optarg; + break; + case 's': + params.skipcpy = 1; + break; + case 'T': + if (strcmp(optarg, "list") == 0) { + show_valid_options(IH_TYPE); + exit(EXIT_SUCCESS); + } + type = genimg_get_type_id(optarg); + if (type < 0) { + show_valid_options(IH_TYPE); + usage("Invalid image type"); + } + break; + case 'v': + params.vflag++; + break; + case 'V': + printf("mkimage version %s\n", PLAIN_VERSION); + exit(EXIT_SUCCESS); + case 'x': + params.xflag++; + break; + default: + usage("Invalid option"); + } + } + + /* The last parameter is expected to be the imagefile */ + if (optind < argc) + params.imagefile = argv[optind]; + + /* + * For auto-generated FIT images we need to know the image type to put + * in the FIT, which is separate from the file's image type (which + * will always be IH_TYPE_FLATDT in this case). + */ + if (params.type == IH_TYPE_FLATDT) { + params.fit_image_type = type ? type : IH_TYPE_KERNEL; + /* For auto_its, datafile is always 'auto' */ + if (!params.auto_its) + params.datafile = datafile; + else if (!params.datafile) + usage("Missing data file for auto-FIT (use -d)"); + } else if (type != IH_TYPE_INVALID) { + if (type == IH_TYPE_SCRIPT && !params.datafile) + usage("Missing data file for script (use -d)"); + params.type = type; + } + + if (!params.imagefile) + usage("Missing output filename"); +} + +int main(int argc, char **argv) +{ + int ifd = -1; + struct stat sbuf; + char *ptr; + int retval = 0; + struct image_type_params *tparams = NULL; + int pad_len = 0; + int dfd; + size_t map_len; + + params.cmdname = *argv; + params.addr = 0; + params.ep = 0; + + process_args(argc, argv); + + /* set tparams as per input type_id */ + tparams = imagetool_get_type(params.type); + if (tparams == NULL) { + fprintf (stderr, "%s: unsupported type %s\n", + params.cmdname, genimg_get_type_name(params.type)); + exit (EXIT_FAILURE); + } + + /* + * check the passed arguments parameters meets the requirements + * as per image type to be generated/listed + */ + if (tparams->check_params) + if (tparams->check_params (¶ms)) + usage("Bad parameters for image type"); + + if (!params.eflag) { + params.ep = params.addr; + /* If XIP, entry point must be after the U-Boot header */ + if (params.xflag) + params.ep += tparams->header_size; + } + + if (params.fflag){ + if (tparams->fflag_handle) + /* + * in some cases, some additional processing needs + * to be done if fflag is defined + * + * For ex. fit_handle_file for Fit file support + */ + retval = tparams->fflag_handle(¶ms); + + if (retval != EXIT_SUCCESS) + exit (retval); + } + + if (params.lflag || params.fflag) { + ifd = open (params.imagefile, O_RDONLY|O_BINARY); + } else { + ifd = open (params.imagefile, + O_RDWR|O_CREAT|O_TRUNC|O_BINARY, 0666); + } + + if (ifd < 0) { + fprintf (stderr, "%s: Can't open %s: %s\n", + params.cmdname, params.imagefile, + strerror(errno)); + exit (EXIT_FAILURE); + } + + if (params.lflag || params.fflag) { + /* + * list header information of existing image + */ + if (fstat(ifd, &sbuf) < 0) { + fprintf (stderr, "%s: Can't stat %s: %s\n", + params.cmdname, params.imagefile, + strerror(errno)); + exit (EXIT_FAILURE); + } + + if ((unsigned)sbuf.st_size < tparams->header_size) { + fprintf (stderr, + "%s: Bad size: \"%s\" is not valid image\n", + params.cmdname, params.imagefile); + exit (EXIT_FAILURE); + } + + ptr = mmap(0, sbuf.st_size, PROT_READ, MAP_SHARED, ifd, 0); + if (ptr == MAP_FAILED) { + fprintf (stderr, "%s: Can't read %s: %s\n", + params.cmdname, params.imagefile, + strerror(errno)); + exit (EXIT_FAILURE); + } + + /* + * scan through mkimage registry for all supported image types + * and verify the input image file header for match + * Print the image information for matched image type + * Returns the error code if not matched + */ + retval = imagetool_verify_print_header(ptr, &sbuf, + tparams, ¶ms); + + (void) munmap((void *)ptr, sbuf.st_size); + (void) close (ifd); + + exit (retval); + } + + if ((params.type != IH_TYPE_MULTI) && (params.type != IH_TYPE_SCRIPT)) { + dfd = open(params.datafile, O_RDONLY | O_BINARY); + if (dfd < 0) { + fprintf(stderr, "%s: Can't open %s: %s\n", + params.cmdname, params.datafile, + strerror(errno)); + exit(EXIT_FAILURE); + } + + if (fstat(dfd, &sbuf) < 0) { + fprintf(stderr, "%s: Can't stat %s: %s\n", + params.cmdname, params.datafile, + strerror(errno)); + exit(EXIT_FAILURE); + } + + params.file_size = sbuf.st_size + tparams->header_size; + close(dfd); + } + + /* + * In case there an header with a variable + * length will be added, the corresponding + * function is called. This is responsible to + * allocate memory for the header itself. + */ + if (tparams->vrec_header) + pad_len = tparams->vrec_header(¶ms, tparams); + else + memset(tparams->hdr, 0, tparams->header_size); + + if (write(ifd, tparams->hdr, tparams->header_size) + != tparams->header_size) { + fprintf (stderr, "%s: Write error on %s: %s\n", + params.cmdname, params.imagefile, strerror(errno)); + exit (EXIT_FAILURE); + } + + if (!params.skipcpy) { + if (params.type == IH_TYPE_MULTI || + params.type == IH_TYPE_SCRIPT) { + char *file = params.datafile; + uint32_t size; + + for (;;) { + char *sep = NULL; + + if (file) { + if ((sep = strchr(file, ':')) != NULL) { + *sep = '\0'; + } + + if (stat (file, &sbuf) < 0) { + fprintf (stderr, "%s: Can't stat %s: %s\n", + params.cmdname, file, strerror(errno)); + exit (EXIT_FAILURE); + } + size = cpu_to_uimage (sbuf.st_size); + } else { + size = 0; + } + + if (write(ifd, (char *)&size, sizeof(size)) != sizeof(size)) { + fprintf (stderr, "%s: Write error on %s: %s\n", + params.cmdname, params.imagefile, + strerror(errno)); + exit (EXIT_FAILURE); + } + + if (!file) { + break; + } + + if (sep) { + *sep = ':'; + file = sep + 1; + } else { + file = NULL; + } + } + + file = params.datafile; + + for (;;) { + char *sep = strchr(file, ':'); + if (sep) { + *sep = '\0'; + copy_file (ifd, file, 1); + *sep++ = ':'; + file = sep; + } else { + copy_file (ifd, file, 0); + break; + } + } + } else if (params.type == IH_TYPE_PBLIMAGE) { + /* PBL has special Image format, implements its' own */ + pbl_load_uboot(ifd, ¶ms); + } else if (params.type == IH_TYPE_ZYNQMPBIF) { + /* Image file is meta, walk through actual targets */ + int ret; + + ret = zynqmpbif_copy_image(ifd, ¶ms); + if (ret) + return ret; + } else if (params.type == IH_TYPE_IMX8IMAGE) { + /* i.MX8/8X has special Image format */ + int ret; + + ret = imx8image_copy_image(ifd, ¶ms); + if (ret) + return ret; + } else if (params.type == IH_TYPE_IMX8MIMAGE) { + /* i.MX8M has special Image format */ + int ret; + + ret = imx8mimage_copy_image(ifd, ¶ms); + if (ret) + return ret; + } else { + copy_file(ifd, params.datafile, pad_len); + } + if (params.type == IH_TYPE_FIRMWARE_IVT) { + /* Add alignment and IVT */ + uint32_t aligned_filesize = (params.file_size + 0x1000 + - 1) & ~(0x1000 - 1); + flash_header_v2_t ivt_header = { { 0xd1, 0x2000, 0x40 }, + params.addr, 0, 0, 0, params.addr + + aligned_filesize + - tparams->header_size, + params.addr + aligned_filesize + - tparams->header_size + + 0x20, 0 }; + int i = params.file_size; + for (; i < aligned_filesize; i++) { + if (write(ifd, (char *) &i, 1) != 1) { + fprintf(stderr, + "%s: Write error on %s: %s\n", + params.cmdname, + params.imagefile, + strerror(errno)); + exit(EXIT_FAILURE); + } + } + if (write(ifd, &ivt_header, sizeof(flash_header_v2_t)) + != sizeof(flash_header_v2_t)) { + fprintf(stderr, "%s: Write error on %s: %s\n", + params.cmdname, + params.imagefile, + strerror(errno)); + exit(EXIT_FAILURE); + } + } + } + + /* We're a bit of paranoid */ +#if defined(_POSIX_SYNCHRONIZED_IO) && \ + !defined(__sun__) && \ + !defined(__FreeBSD__) && \ + !defined(__OpenBSD__) && \ + !defined(__APPLE__) + (void) fdatasync (ifd); +#else + (void) fsync (ifd); +#endif + + if (fstat(ifd, &sbuf) < 0) { + fprintf (stderr, "%s: Can't stat %s: %s\n", + params.cmdname, params.imagefile, strerror(errno)); + exit (EXIT_FAILURE); + } + params.file_size = sbuf.st_size; + + map_len = sbuf.st_size; + ptr = mmap(0, map_len, PROT_READ | PROT_WRITE, MAP_SHARED, ifd, 0); + if (ptr == MAP_FAILED) { + fprintf (stderr, "%s: Can't map %s: %s\n", + params.cmdname, params.imagefile, strerror(errno)); + exit (EXIT_FAILURE); + } + + /* Setup the image header as per input image type*/ + if (tparams->set_header) + tparams->set_header (ptr, &sbuf, ifd, ¶ms); + else { + fprintf (stderr, "%s: Can't set header for %s: %s\n", + params.cmdname, tparams->name, strerror(errno)); + exit (EXIT_FAILURE); + } + + /* Print the image information by processing image header */ + if (tparams->print_header) + tparams->print_header (ptr); + else { + fprintf (stderr, "%s: Can't print header for %s\n", + params.cmdname, tparams->name); + } + + (void)munmap((void *)ptr, map_len); + + /* We're a bit of paranoid */ +#if defined(_POSIX_SYNCHRONIZED_IO) && \ + !defined(__sun__) && \ + !defined(__FreeBSD__) && \ + !defined(__OpenBSD__) && \ + !defined(__APPLE__) + (void) fdatasync (ifd); +#else + (void) fsync (ifd); +#endif + + if (close(ifd)) { + fprintf (stderr, "%s: Write error on %s: %s\n", + params.cmdname, params.imagefile, strerror(errno)); + exit (EXIT_FAILURE); + } + + exit (EXIT_SUCCESS); +} + +static void +copy_file (int ifd, const char *datafile, int pad) +{ + int dfd; + struct stat sbuf; + unsigned char *ptr; + int tail; + int zero = 0; + uint8_t zeros[4096]; + int offset = 0; + int size; + struct image_type_params *tparams = imagetool_get_type(params.type); + + memset(zeros, 0, sizeof(zeros)); + + if (params.vflag) { + fprintf (stderr, "Adding Image %s\n", datafile); + } + + if ((dfd = open(datafile, O_RDONLY|O_BINARY)) < 0) { + fprintf (stderr, "%s: Can't open %s: %s\n", + params.cmdname, datafile, strerror(errno)); + exit (EXIT_FAILURE); + } + + if (fstat(dfd, &sbuf) < 0) { + fprintf (stderr, "%s: Can't stat %s: %s\n", + params.cmdname, datafile, strerror(errno)); + exit (EXIT_FAILURE); + } + + ptr = mmap(0, sbuf.st_size, PROT_READ, MAP_SHARED, dfd, 0); + if (ptr == MAP_FAILED) { + fprintf (stderr, "%s: Can't read %s: %s\n", + params.cmdname, datafile, strerror(errno)); + exit (EXIT_FAILURE); + } + + if (params.xflag) { + unsigned char *p = NULL; + /* + * XIP: do not append the image_header_t at the + * beginning of the file, but consume the space + * reserved for it. + */ + + if ((unsigned)sbuf.st_size < tparams->header_size) { + fprintf (stderr, + "%s: Bad size: \"%s\" is too small for XIP\n", + params.cmdname, datafile); + exit (EXIT_FAILURE); + } + + for (p = ptr; p < ptr + tparams->header_size; p++) { + if ( *p != 0xff ) { + fprintf (stderr, + "%s: Bad file: \"%s\" has invalid buffer for XIP\n", + params.cmdname, datafile); + exit (EXIT_FAILURE); + } + } + + offset = tparams->header_size; + } + + size = sbuf.st_size - offset; + if (write(ifd, ptr + offset, size) != size) { + fprintf (stderr, "%s: Write error on %s: %s\n", + params.cmdname, params.imagefile, strerror(errno)); + exit (EXIT_FAILURE); + } + + tail = size % 4; + if ((pad == 1) && (tail != 0)) { + + if (write(ifd, (char *)&zero, 4-tail) != 4-tail) { + fprintf (stderr, "%s: Write error on %s: %s\n", + params.cmdname, params.imagefile, + strerror(errno)); + exit (EXIT_FAILURE); + } + } else if (pad > 1) { + while (pad > 0) { + int todo = sizeof(zeros); + + if (todo > pad) + todo = pad; + if (write(ifd, (char *)&zeros, todo) != todo) { + fprintf(stderr, "%s: Write error on %s: %s\n", + params.cmdname, params.imagefile, + strerror(errno)); + exit(EXIT_FAILURE); + } + pad -= todo; + } + } + + (void) munmap((void *)ptr, sbuf.st_size); + (void) close (dfd); +} diff --git a/tools/u-boot-tools/mkimage.h b/tools/u-boot-tools/mkimage.h new file mode 100644 index 0000000..0254af5 --- /dev/null +++ b/tools/u-boot-tools/mkimage.h @@ -0,0 +1,47 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * (C) Copyright 2000-2004 + * DENX Software Engineering + * Wolfgang Denk, wd@denx.de + */ + +#ifndef _MKIIMAGE_H_ +#define _MKIIMAGE_H_ + +#include "os_support.h" +#include <errno.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/stat.h> +#include <time.h> +#include <unistd.h> +#include <u-boot/sha1.h> +#include "fdt_host.h" +#include "imagetool.h" + +#undef MKIMAGE_DEBUG + +#ifdef MKIMAGE_DEBUG +#define debug(fmt,args...) printf (fmt ,##args) +#else +#define debug(fmt,args...) +#endif /* MKIMAGE_DEBUG */ + +static inline void *map_sysmem(ulong paddr, unsigned long len) +{ + return (void *)(uintptr_t)paddr; +} + +static inline ulong map_to_sysmem(void *ptr) +{ + return (ulong)(uintptr_t)ptr; +} + +#define MKIMAGE_TMPFILE_SUFFIX ".tmp" +#define MKIMAGE_MAX_TMPFILE_LEN 256 +#define MKIMAGE_DEFAULT_DTC_OPTIONS "-I dts -O dtb -p 500" +#define MKIMAGE_MAX_DTC_CMDLINE_LEN 512 + +#endif /* _MKIIMAGE_H_ */ diff --git a/tools/u-boot-tools/mkimage.o b/tools/u-boot-tools/mkimage.o new file mode 100644 index 0000000000000000000000000000000000000000..65df7a86254df5be833db9acf07345e54ab0b7f8 GIT binary patch literal 21984 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 Y+=ZqDEM)*~sKLg~!Gh4Rg0Vp~0AR)4g#Z8m literal 0 HcmV?d00001 diff --git a/tools/u-boot-tools/mksunxiboot.c b/tools/u-boot-tools/mksunxiboot.c new file mode 100644 index 0000000..1c8701e --- /dev/null +++ b/tools/u-boot-tools/mksunxiboot.c @@ -0,0 +1,177 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2007-2011 + * Allwinner Technology Co., Ltd. <www.allwinnertech.com> + * Tom Cubie <tangliang@allwinnertech.com> + * + * a simple tool to generate bootable image for sunxi platform. + */ +#include <fcntl.h> +#include <stdio.h> +#include <unistd.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/stat.h> +#include "../arch/arm/include/asm/arch-sunxi/spl.h" + +#define STAMP_VALUE 0x5F0A6C39 + +/* check sum functon from sun4i boot code */ +int gen_check_sum(struct boot_file_head *head_p) +{ + uint32_t length; + uint32_t *buf; + uint32_t loop; + uint32_t i; + uint32_t sum; + + length = le32_to_cpu(head_p->length); + if ((length & 0x3) != 0) /* must 4-byte-aligned */ + return -1; + buf = (uint32_t *)head_p; + head_p->check_sum = cpu_to_le32(STAMP_VALUE); /* fill stamp */ + loop = length >> 2; + + /* calculate the sum */ + for (i = 0, sum = 0; i < loop; i++) + sum += le32_to_cpu(buf[i]); + + /* write back check sum */ + head_p->check_sum = cpu_to_le32(sum); + + return 0; +} + +#define ALIGN(x, a) __ALIGN_MASK((x), (typeof(x))(a)-1) +#define __ALIGN_MASK(x, mask) (((x)+(mask))&~(mask)) + +#define SUNXI_SRAM_SIZE 0x8000 /* SoC with smaller size are limited before */ +#define SRAM_LOAD_MAX_SIZE (SUNXI_SRAM_SIZE - sizeof(struct boot_file_head)) + +/* + * BROM (at least on A10 and A20) requires NAND-images to be explicitly aligned + * to a multiple of 8K, and rejects the image otherwise. MMC-images are fine + * with 512B blocks. To cater for both, align to the largest of the two. + */ +#define BLOCK_SIZE 0x2000 + +struct boot_img { + struct boot_file_head header; + char code[SRAM_LOAD_MAX_SIZE]; + char pad[BLOCK_SIZE]; +}; + +int main(int argc, char *argv[]) +{ + int fd_in, fd_out; + struct boot_img img; + unsigned file_size; + int count; + char *tool_name = argv[0]; + char *default_dt = NULL; + + /* a sanity check */ + if ((sizeof(img.header) % 32) != 0) { + fprintf(stderr, "ERROR: the SPL header must be a multiple "); + fprintf(stderr, "of 32 bytes.\n"); + return EXIT_FAILURE; + } + + /* process optional command line switches */ + while (argc >= 2 && argv[1][0] == '-') { + if (strcmp(argv[1], "--default-dt") == 0) { + if (argc >= 3) { + default_dt = argv[2]; + argv += 2; + argc -= 2; + continue; + } + fprintf(stderr, "ERROR: no --default-dt arg\n"); + return EXIT_FAILURE; + } else { + fprintf(stderr, "ERROR: bad option '%s'\n", argv[1]); + return EXIT_FAILURE; + } + } + + if (argc < 3) { + printf("This program converts an input binary file to a sunxi bootable image.\n"); + printf("\nUsage: %s [options] input_file output_file\n", + tool_name); + printf("Where [options] may be:\n"); + printf(" --default-dt arg - 'arg' is the default device tree name\n"); + printf(" (CONFIG_DEFAULT_DEVICE_TREE).\n"); + return EXIT_FAILURE; + } + + fd_in = open(argv[1], O_RDONLY); + if (fd_in < 0) { + perror("Open input file"); + return EXIT_FAILURE; + } + + memset(&img, 0, sizeof(img)); + + /* get input file size */ + file_size = lseek(fd_in, 0, SEEK_END); + + if (file_size > SRAM_LOAD_MAX_SIZE) { + fprintf(stderr, "ERROR: File too large!\n"); + return EXIT_FAILURE; + } + + fd_out = open(argv[2], O_WRONLY | O_CREAT, 0666); + if (fd_out < 0) { + perror("Open output file"); + return EXIT_FAILURE; + } + + /* read file to buffer to calculate checksum */ + lseek(fd_in, 0, SEEK_SET); + count = read(fd_in, img.code, file_size); + if (count != file_size) { + perror("Reading input image"); + return EXIT_FAILURE; + } + + /* fill the header */ + img.header.b_instruction = /* b instruction */ + 0xEA000000 | /* jump to the first instr after the header */ + ((sizeof(struct boot_file_head) / sizeof(int) - 2) + & 0x00FFFFFF); + memcpy(img.header.magic, BOOT0_MAGIC, 8); /* no '0' termination */ + img.header.length = + ALIGN(file_size + sizeof(struct boot_file_head), BLOCK_SIZE); + img.header.b_instruction = cpu_to_le32(img.header.b_instruction); + img.header.length = cpu_to_le32(img.header.length); + + memcpy(img.header.spl_signature, SPL_SIGNATURE, 3); /* "sunxi" marker */ + img.header.spl_signature[3] = SPL_HEADER_VERSION; + + if (default_dt) { + if (strlen(default_dt) + 1 <= sizeof(img.header.string_pool)) { + strcpy((char *)img.header.string_pool, default_dt); + img.header.dt_name_offset = + cpu_to_le32(offsetof(struct boot_file_head, + string_pool)); + } else { + printf("WARNING: The SPL header is too small\n"); + printf(" and has no space to store the dt name.\n"); + } + } + + gen_check_sum(&img.header); + + count = write(fd_out, &img, le32_to_cpu(img.header.length)); + if (count != le32_to_cpu(img.header.length)) { + perror("Writing output"); + return EXIT_FAILURE; + } + + close(fd_in); + close(fd_out); + + return EXIT_SUCCESS; +} diff --git a/tools/u-boot-tools/moveconfig.py b/tools/u-boot-tools/moveconfig.py new file mode 100755 index 0000000..caa81ac --- /dev/null +++ b/tools/u-boot-tools/moveconfig.py @@ -0,0 +1,1885 @@ +#!/usr/bin/env python2 +# SPDX-License-Identifier: GPL-2.0+ +# +# Author: Masahiro Yamada <yamada.masahiro@socionext.com> +# + +""" +Move config options from headers to defconfig files. + +Since Kconfig was introduced to U-Boot, we have worked on moving +config options from headers to Kconfig (defconfig). + +This tool intends to help this tremendous work. + + +Usage +----- + +First, you must edit the Kconfig to add the menu entries for the configs +you are moving. + +And then run this tool giving CONFIG names you want to move. +For example, if you want to move CONFIG_CMD_USB and CONFIG_SYS_TEXT_BASE, +simply type as follows: + + $ tools/moveconfig.py CONFIG_CMD_USB CONFIG_SYS_TEXT_BASE + +The tool walks through all the defconfig files and move the given CONFIGs. + +The log is also displayed on the terminal. + +The log is printed for each defconfig as follows: + +<defconfig_name> + <action1> + <action2> + <action3> + ... + +<defconfig_name> is the name of the defconfig. + +<action*> shows what the tool did for that defconfig. +It looks like one of the following: + + - Move 'CONFIG_... ' + This config option was moved to the defconfig + + - CONFIG_... is not defined in Kconfig. Do nothing. + The entry for this CONFIG was not found in Kconfig. The option is not + defined in the config header, either. So, this case can be just skipped. + + - CONFIG_... is not defined in Kconfig (suspicious). Do nothing. + This option is defined in the config header, but its entry was not found + in Kconfig. + There are two common cases: + - You forgot to create an entry for the CONFIG before running + this tool, or made a typo in a CONFIG passed to this tool. + - The entry was hidden due to unmet 'depends on'. + The tool does not know if the result is reasonable, so please check it + manually. + + - 'CONFIG_...' is the same as the define in Kconfig. Do nothing. + The define in the config header matched the one in Kconfig. + We do not need to touch it. + + - Compiler is missing. Do nothing. + The compiler specified for this architecture was not found + in your PATH environment. + (If -e option is passed, the tool exits immediately.) + + - Failed to process. + An error occurred during processing this defconfig. Skipped. + (If -e option is passed, the tool exits immediately on error.) + +Finally, you will be asked, Clean up headers? [y/n]: + +If you say 'y' here, the unnecessary config defines are removed +from the config headers (include/configs/*.h). +It just uses the regex method, so you should not rely on it. +Just in case, please do 'git diff' to see what happened. + + +How does it work? +----------------- + +This tool runs configuration and builds include/autoconf.mk for every +defconfig. The config options defined in Kconfig appear in the .config +file (unless they are hidden because of unmet dependency.) +On the other hand, the config options defined by board headers are seen +in include/autoconf.mk. The tool looks for the specified options in both +of them to decide the appropriate action for the options. If the given +config option is found in the .config, but its value does not match the +one from the board header, the config option in the .config is replaced +with the define in the board header. Then, the .config is synced by +"make savedefconfig" and the defconfig is updated with it. + +For faster processing, this tool handles multi-threading. It creates +separate build directories where the out-of-tree build is run. The +temporary build directories are automatically created and deleted as +needed. The number of threads are chosen based on the number of the CPU +cores of your system although you can change it via -j (--jobs) option. + + +Toolchains +---------- + +Appropriate toolchain are necessary to generate include/autoconf.mk +for all the architectures supported by U-Boot. Most of them are available +at the kernel.org site, some are not provided by kernel.org. This tool uses +the same tools as buildman, so see that tool for setup (e.g. --fetch-arch). + + +Tips and trips +-------------- + +To sync only X86 defconfigs: + + ./tools/moveconfig.py -s -d <(grep -l X86 configs/*) + +or: + + grep -l X86 configs/* | ./tools/moveconfig.py -s -d - + +To process CONFIG_CMD_FPGAD only for a subset of configs based on path match: + + ls configs/{hrcon*,iocon*,strider*} | \ + ./tools/moveconfig.py -Cy CONFIG_CMD_FPGAD -d - + + +Finding implied CONFIGs +----------------------- + +Some CONFIG options can be implied by others and this can help to reduce +the size of the defconfig files. For example, CONFIG_X86 implies +CONFIG_CMD_IRQ, so we can put 'imply CMD_IRQ' under 'config X86' and +all x86 boards will have that option, avoiding adding CONFIG_CMD_IRQ to +each of the x86 defconfig files. + +This tool can help find such configs. To use it, first build a database: + + ./tools/moveconfig.py -b + +Then try to query it: + + ./tools/moveconfig.py -i CONFIG_CMD_IRQ + CONFIG_CMD_IRQ found in 311/2384 defconfigs + 44 : CONFIG_SYS_FSL_ERRATUM_IFC_A002769 + 41 : CONFIG_SYS_FSL_ERRATUM_A007075 + 31 : CONFIG_SYS_FSL_DDR_VER_44 + 28 : CONFIG_ARCH_P1010 + 28 : CONFIG_SYS_FSL_ERRATUM_P1010_A003549 + 28 : CONFIG_SYS_FSL_ERRATUM_SEC_A003571 + 28 : CONFIG_SYS_FSL_ERRATUM_IFC_A003399 + 25 : CONFIG_SYS_FSL_ERRATUM_A008044 + 22 : CONFIG_ARCH_P1020 + 21 : CONFIG_SYS_FSL_DDR_VER_46 + 20 : CONFIG_MAX_PIRQ_LINKS + 20 : CONFIG_HPET_ADDRESS + 20 : CONFIG_X86 + 20 : CONFIG_PCIE_ECAM_SIZE + 20 : CONFIG_IRQ_SLOT_COUNT + 20 : CONFIG_I8259_PIC + 20 : CONFIG_CPU_ADDR_BITS + 20 : CONFIG_RAMBASE + 20 : CONFIG_SYS_FSL_ERRATUM_A005871 + 20 : CONFIG_PCIE_ECAM_BASE + 20 : CONFIG_X86_TSC_TIMER + 20 : CONFIG_I8254_TIMER + 20 : CONFIG_CMD_GETTIME + 19 : CONFIG_SYS_FSL_ERRATUM_A005812 + 18 : CONFIG_X86_RUN_32BIT + 17 : CONFIG_CMD_CHIP_CONFIG + ... + +This shows a list of config options which might imply CONFIG_CMD_EEPROM along +with how many defconfigs they cover. From this you can see that CONFIG_X86 +implies CONFIG_CMD_EEPROM. Therefore, instead of adding CONFIG_CMD_EEPROM to +the defconfig of every x86 board, you could add a single imply line to the +Kconfig file: + + config X86 + bool "x86 architecture" + ... + imply CMD_EEPROM + +That will cover 20 defconfigs. Many of the options listed are not suitable as +they are not related. E.g. it would be odd for CONFIG_CMD_GETTIME to imply +CMD_EEPROM. + +Using this search you can reduce the size of moveconfig patches. + +You can automatically add 'imply' statements in the Kconfig with the -a +option: + + ./tools/moveconfig.py -s -i CONFIG_SCSI \ + -a CONFIG_ARCH_LS1021A,CONFIG_ARCH_LS1043A + +This will add 'imply SCSI' to the two CONFIG options mentioned, assuming that +the database indicates that they do actually imply CONFIG_SCSI and do not +already have an 'imply SCSI'. + +The output shows where the imply is added: + + 18 : CONFIG_ARCH_LS1021A arch/arm/cpu/armv7/ls102xa/Kconfig:1 + 13 : CONFIG_ARCH_LS1043A arch/arm/cpu/armv8/fsl-layerscape/Kconfig:11 + 12 : CONFIG_ARCH_LS1046A arch/arm/cpu/armv8/fsl-layerscape/Kconfig:31 + +The first number is the number of boards which can avoid having a special +CONFIG_SCSI option in their defconfig file if this 'imply' is added. +The location at the right is the Kconfig file and line number where the config +appears. For example, adding 'imply CONFIG_SCSI' to the 'config ARCH_LS1021A' +in arch/arm/cpu/armv7/ls102xa/Kconfig at line 1 will help 18 boards to reduce +the size of their defconfig files. + +If you want to add an 'imply' to every imply config in the list, you can use + + ./tools/moveconfig.py -s -i CONFIG_SCSI -a all + +To control which ones are displayed, use -I <list> where list is a list of +options (use '-I help' to see possible options and their meaning). + +To skip showing you options that already have an 'imply' attached, use -A. + +When you have finished adding 'imply' options you can regenerate the +defconfig files for affected boards with something like: + + git show --stat | ./tools/moveconfig.py -s -d - + +This will regenerate only those defconfigs changed in the current commit. +If you start with (say) 100 defconfigs being changed in the commit, and add +a few 'imply' options as above, then regenerate, hopefully you can reduce the +number of defconfigs changed in the commit. + + +Available options +----------------- + + -c, --color + Surround each portion of the log with escape sequences to display it + in color on the terminal. + + -C, --commit + Create a git commit with the changes when the operation is complete. A + standard commit message is used which may need to be edited. + + -d, --defconfigs + Specify a file containing a list of defconfigs to move. The defconfig + files can be given with shell-style wildcards. Use '-' to read from stdin. + + -n, --dry-run + Perform a trial run that does not make any changes. It is useful to + see what is going to happen before one actually runs it. + + -e, --exit-on-error + Exit immediately if Make exits with a non-zero status while processing + a defconfig file. + + -s, --force-sync + Do "make savedefconfig" forcibly for all the defconfig files. + If not specified, "make savedefconfig" only occurs for cases + where at least one CONFIG was moved. + + -S, --spl + Look for moved config options in spl/include/autoconf.mk instead of + include/autoconf.mk. This is useful for moving options for SPL build + because SPL related options (mostly prefixed with CONFIG_SPL_) are + sometimes blocked by CONFIG_SPL_BUILD ifdef conditionals. + + -H, --headers-only + Only cleanup the headers; skip the defconfig processing + + -j, --jobs + Specify the number of threads to run simultaneously. If not specified, + the number of threads is the same as the number of CPU cores. + + -r, --git-ref + Specify the git ref to clone for building the autoconf.mk. If unspecified + use the CWD. This is useful for when changes to the Kconfig affect the + default values and you want to capture the state of the defconfig from + before that change was in effect. If in doubt, specify a ref pre-Kconfig + changes (use HEAD if Kconfig changes are not committed). Worst case it will + take a bit longer to run, but will always do the right thing. + + -v, --verbose + Show any build errors as boards are built + + -y, --yes + Instead of prompting, automatically go ahead with all operations. This + includes cleaning up headers, CONFIG_SYS_EXTRA_OPTIONS, the config whitelist + and the README. + +To see the complete list of supported options, run + + $ tools/moveconfig.py -h + +""" + +import collections +import copy +import difflib +import filecmp +import fnmatch +import glob +import multiprocessing +import optparse +import os +import Queue +import re +import shutil +import subprocess +import sys +import tempfile +import threading +import time + +sys.path.append(os.path.join(os.path.dirname(__file__), 'buildman')) +sys.path.append(os.path.join(os.path.dirname(__file__), 'patman')) +import bsettings +import kconfiglib +import toolchain + +SHOW_GNU_MAKE = 'scripts/show-gnu-make' +SLEEP_TIME=0.03 + +STATE_IDLE = 0 +STATE_DEFCONFIG = 1 +STATE_AUTOCONF = 2 +STATE_SAVEDEFCONFIG = 3 + +ACTION_MOVE = 0 +ACTION_NO_ENTRY = 1 +ACTION_NO_ENTRY_WARN = 2 +ACTION_NO_CHANGE = 3 + +COLOR_BLACK = '0;30' +COLOR_RED = '0;31' +COLOR_GREEN = '0;32' +COLOR_BROWN = '0;33' +COLOR_BLUE = '0;34' +COLOR_PURPLE = '0;35' +COLOR_CYAN = '0;36' +COLOR_LIGHT_GRAY = '0;37' +COLOR_DARK_GRAY = '1;30' +COLOR_LIGHT_RED = '1;31' +COLOR_LIGHT_GREEN = '1;32' +COLOR_YELLOW = '1;33' +COLOR_LIGHT_BLUE = '1;34' +COLOR_LIGHT_PURPLE = '1;35' +COLOR_LIGHT_CYAN = '1;36' +COLOR_WHITE = '1;37' + +AUTO_CONF_PATH = 'include/config/auto.conf' +CONFIG_DATABASE = 'moveconfig.db' + +CONFIG_LEN = len('CONFIG_') + +### helper functions ### +def get_devnull(): + """Get the file object of '/dev/null' device.""" + try: + devnull = subprocess.DEVNULL # py3k + except AttributeError: + devnull = open(os.devnull, 'wb') + return devnull + +def check_top_directory(): + """Exit if we are not at the top of source directory.""" + for f in ('README', 'Licenses'): + if not os.path.exists(f): + sys.exit('Please run at the top of source directory.') + +def check_clean_directory(): + """Exit if the source tree is not clean.""" + for f in ('.config', 'include/config'): + if os.path.exists(f): + sys.exit("source tree is not clean, please run 'make mrproper'") + +def get_make_cmd(): + """Get the command name of GNU Make. + + U-Boot needs GNU Make for building, but the command name is not + necessarily "make". (for example, "gmake" on FreeBSD). + Returns the most appropriate command name on your system. + """ + process = subprocess.Popen([SHOW_GNU_MAKE], stdout=subprocess.PIPE) + ret = process.communicate() + if process.returncode: + sys.exit('GNU Make not found') + return ret[0].rstrip() + +def get_matched_defconfig(line): + """Get the defconfig files that match a pattern + + Args: + line: Path or filename to match, e.g. 'configs/snow_defconfig' or + 'k2*_defconfig'. If no directory is provided, 'configs/' is + prepended + + Returns: + a list of matching defconfig files + """ + dirname = os.path.dirname(line) + if dirname: + pattern = line + else: + pattern = os.path.join('configs', line) + return glob.glob(pattern) + glob.glob(pattern + '_defconfig') + +def get_matched_defconfigs(defconfigs_file): + """Get all the defconfig files that match the patterns in a file. + + Args: + defconfigs_file: File containing a list of defconfigs to process, or + '-' to read the list from stdin + + Returns: + A list of paths to defconfig files, with no duplicates + """ + defconfigs = [] + if defconfigs_file == '-': + fd = sys.stdin + defconfigs_file = 'stdin' + else: + fd = open(defconfigs_file) + for i, line in enumerate(fd): + line = line.strip() + if not line: + continue # skip blank lines silently + if ' ' in line: + line = line.split(' ')[0] # handle 'git log' input + matched = get_matched_defconfig(line) + if not matched: + print >> sys.stderr, "warning: %s:%d: no defconfig matched '%s'" % \ + (defconfigs_file, i + 1, line) + + defconfigs += matched + + # use set() to drop multiple matching + return [ defconfig[len('configs') + 1:] for defconfig in set(defconfigs) ] + +def get_all_defconfigs(): + """Get all the defconfig files under the configs/ directory.""" + defconfigs = [] + for (dirpath, dirnames, filenames) in os.walk('configs'): + dirpath = dirpath[len('configs') + 1:] + for filename in fnmatch.filter(filenames, '*_defconfig'): + defconfigs.append(os.path.join(dirpath, filename)) + + return defconfigs + +def color_text(color_enabled, color, string): + """Return colored string.""" + if color_enabled: + # LF should not be surrounded by the escape sequence. + # Otherwise, additional whitespace or line-feed might be printed. + return '\n'.join([ '\033[' + color + 'm' + s + '\033[0m' if s else '' + for s in string.split('\n') ]) + else: + return string + +def show_diff(a, b, file_path, color_enabled): + """Show unidified diff. + + Arguments: + a: A list of lines (before) + b: A list of lines (after) + file_path: Path to the file + color_enabled: Display the diff in color + """ + + diff = difflib.unified_diff(a, b, + fromfile=os.path.join('a', file_path), + tofile=os.path.join('b', file_path)) + + for line in diff: + if line[0] == '-' and line[1] != '-': + print color_text(color_enabled, COLOR_RED, line), + elif line[0] == '+' and line[1] != '+': + print color_text(color_enabled, COLOR_GREEN, line), + else: + print line, + +def extend_matched_lines(lines, matched, pre_patterns, post_patterns, extend_pre, + extend_post): + """Extend matched lines if desired patterns are found before/after already + matched lines. + + Arguments: + lines: A list of lines handled. + matched: A list of line numbers that have been already matched. + (will be updated by this function) + pre_patterns: A list of regular expression that should be matched as + preamble. + post_patterns: A list of regular expression that should be matched as + postamble. + extend_pre: Add the line number of matched preamble to the matched list. + extend_post: Add the line number of matched postamble to the matched list. + """ + extended_matched = [] + + j = matched[0] + + for i in matched: + if i == 0 or i < j: + continue + j = i + while j in matched: + j += 1 + if j >= len(lines): + break + + for p in pre_patterns: + if p.search(lines[i - 1]): + break + else: + # not matched + continue + + for p in post_patterns: + if p.search(lines[j]): + break + else: + # not matched + continue + + if extend_pre: + extended_matched.append(i - 1) + if extend_post: + extended_matched.append(j) + + matched += extended_matched + matched.sort() + +def confirm(options, prompt): + if not options.yes: + while True: + choice = raw_input('{} [y/n]: '.format(prompt)) + choice = choice.lower() + print choice + if choice == 'y' or choice == 'n': + break + + if choice == 'n': + return False + + return True + +def cleanup_one_header(header_path, patterns, options): + """Clean regex-matched lines away from a file. + + Arguments: + header_path: path to the cleaned file. + patterns: list of regex patterns. Any lines matching to these + patterns are deleted. + options: option flags. + """ + with open(header_path) as f: + lines = f.readlines() + + matched = [] + for i, line in enumerate(lines): + if i - 1 in matched and lines[i - 1][-2:] == '\\\n': + matched.append(i) + continue + for pattern in patterns: + if pattern.search(line): + matched.append(i) + break + + if not matched: + return + + # remove empty #ifdef ... #endif, successive blank lines + pattern_if = re.compile(r'#\s*if(def|ndef)?\W') # #if, #ifdef, #ifndef + pattern_elif = re.compile(r'#\s*el(if|se)\W') # #elif, #else + pattern_endif = re.compile(r'#\s*endif\W') # #endif + pattern_blank = re.compile(r'^\s*$') # empty line + + while True: + old_matched = copy.copy(matched) + extend_matched_lines(lines, matched, [pattern_if], + [pattern_endif], True, True) + extend_matched_lines(lines, matched, [pattern_elif], + [pattern_elif, pattern_endif], True, False) + extend_matched_lines(lines, matched, [pattern_if, pattern_elif], + [pattern_blank], False, True) + extend_matched_lines(lines, matched, [pattern_blank], + [pattern_elif, pattern_endif], True, False) + extend_matched_lines(lines, matched, [pattern_blank], + [pattern_blank], True, False) + if matched == old_matched: + break + + tolines = copy.copy(lines) + + for i in reversed(matched): + tolines.pop(i) + + show_diff(lines, tolines, header_path, options.color) + + if options.dry_run: + return + + with open(header_path, 'w') as f: + for line in tolines: + f.write(line) + +def cleanup_headers(configs, options): + """Delete config defines from board headers. + + Arguments: + configs: A list of CONFIGs to remove. + options: option flags. + """ + if not confirm(options, 'Clean up headers?'): + return + + patterns = [] + for config in configs: + patterns.append(re.compile(r'#\s*define\s+%s\W' % config)) + patterns.append(re.compile(r'#\s*undef\s+%s\W' % config)) + + for dir in 'include', 'arch', 'board': + for (dirpath, dirnames, filenames) in os.walk(dir): + if dirpath == os.path.join('include', 'generated'): + continue + for filename in filenames: + if not fnmatch.fnmatch(filename, '*~'): + cleanup_one_header(os.path.join(dirpath, filename), + patterns, options) + +def cleanup_one_extra_option(defconfig_path, configs, options): + """Delete config defines in CONFIG_SYS_EXTRA_OPTIONS in one defconfig file. + + Arguments: + defconfig_path: path to the cleaned defconfig file. + configs: A list of CONFIGs to remove. + options: option flags. + """ + + start = 'CONFIG_SYS_EXTRA_OPTIONS="' + end = '"\n' + + with open(defconfig_path) as f: + lines = f.readlines() + + for i, line in enumerate(lines): + if line.startswith(start) and line.endswith(end): + break + else: + # CONFIG_SYS_EXTRA_OPTIONS was not found in this defconfig + return + + old_tokens = line[len(start):-len(end)].split(',') + new_tokens = [] + + for token in old_tokens: + pos = token.find('=') + if not (token[:pos] if pos >= 0 else token) in configs: + new_tokens.append(token) + + if new_tokens == old_tokens: + return + + tolines = copy.copy(lines) + + if new_tokens: + tolines[i] = start + ','.join(new_tokens) + end + else: + tolines.pop(i) + + show_diff(lines, tolines, defconfig_path, options.color) + + if options.dry_run: + return + + with open(defconfig_path, 'w') as f: + for line in tolines: + f.write(line) + +def cleanup_extra_options(configs, options): + """Delete config defines in CONFIG_SYS_EXTRA_OPTIONS in defconfig files. + + Arguments: + configs: A list of CONFIGs to remove. + options: option flags. + """ + if not confirm(options, 'Clean up CONFIG_SYS_EXTRA_OPTIONS?'): + return + + configs = [ config[len('CONFIG_'):] for config in configs ] + + defconfigs = get_all_defconfigs() + + for defconfig in defconfigs: + cleanup_one_extra_option(os.path.join('configs', defconfig), configs, + options) + +def cleanup_whitelist(configs, options): + """Delete config whitelist entries + + Arguments: + configs: A list of CONFIGs to remove. + options: option flags. + """ + if not confirm(options, 'Clean up whitelist entries?'): + return + + with open(os.path.join('scripts', 'config_whitelist.txt')) as f: + lines = f.readlines() + + lines = [x for x in lines if x.strip() not in configs] + + with open(os.path.join('scripts', 'config_whitelist.txt'), 'w') as f: + f.write(''.join(lines)) + +def find_matching(patterns, line): + for pat in patterns: + if pat.search(line): + return True + return False + +def cleanup_readme(configs, options): + """Delete config description in README + + Arguments: + configs: A list of CONFIGs to remove. + options: option flags. + """ + if not confirm(options, 'Clean up README?'): + return + + patterns = [] + for config in configs: + patterns.append(re.compile(r'^\s+%s' % config)) + + with open('README') as f: + lines = f.readlines() + + found = False + newlines = [] + for line in lines: + if not found: + found = find_matching(patterns, line) + if found: + continue + + if found and re.search(r'^\s+CONFIG', line): + found = False + + if not found: + newlines.append(line) + + with open('README', 'w') as f: + f.write(''.join(newlines)) + + +### classes ### +class Progress: + + """Progress Indicator""" + + def __init__(self, total): + """Create a new progress indicator. + + Arguments: + total: A number of defconfig files to process. + """ + self.current = 0 + self.total = total + + def inc(self): + """Increment the number of processed defconfig files.""" + + self.current += 1 + + def show(self): + """Display the progress.""" + print ' %d defconfigs out of %d\r' % (self.current, self.total), + sys.stdout.flush() + + +class KconfigScanner: + """Kconfig scanner.""" + + def __init__(self): + """Scan all the Kconfig files and create a Config object.""" + # Define environment variables referenced from Kconfig + os.environ['srctree'] = os.getcwd() + os.environ['UBOOTVERSION'] = 'dummy' + os.environ['KCONFIG_OBJDIR'] = '' + self.conf = kconfiglib.Config() + + +class KconfigParser: + + """A parser of .config and include/autoconf.mk.""" + + re_arch = re.compile(r'CONFIG_SYS_ARCH="(.*)"') + re_cpu = re.compile(r'CONFIG_SYS_CPU="(.*)"') + + def __init__(self, configs, options, build_dir): + """Create a new parser. + + Arguments: + configs: A list of CONFIGs to move. + options: option flags. + build_dir: Build directory. + """ + self.configs = configs + self.options = options + self.dotconfig = os.path.join(build_dir, '.config') + self.autoconf = os.path.join(build_dir, 'include', 'autoconf.mk') + self.spl_autoconf = os.path.join(build_dir, 'spl', 'include', + 'autoconf.mk') + self.config_autoconf = os.path.join(build_dir, AUTO_CONF_PATH) + self.defconfig = os.path.join(build_dir, 'defconfig') + + def get_arch(self): + """Parse .config file and return the architecture. + + Returns: + Architecture name (e.g. 'arm'). + """ + arch = '' + cpu = '' + for line in open(self.dotconfig): + m = self.re_arch.match(line) + if m: + arch = m.group(1) + continue + m = self.re_cpu.match(line) + if m: + cpu = m.group(1) + + if not arch: + return None + + # fix-up for aarch64 + if arch == 'arm' and cpu == 'armv8': + arch = 'aarch64' + + return arch + + def parse_one_config(self, config, dotconfig_lines, autoconf_lines): + """Parse .config, defconfig, include/autoconf.mk for one config. + + This function looks for the config options in the lines from + defconfig, .config, and include/autoconf.mk in order to decide + which action should be taken for this defconfig. + + Arguments: + config: CONFIG name to parse. + dotconfig_lines: lines from the .config file. + autoconf_lines: lines from the include/autoconf.mk file. + + Returns: + A tupple of the action for this defconfig and the line + matched for the config. + """ + not_set = '# %s is not set' % config + + for line in autoconf_lines: + line = line.rstrip() + if line.startswith(config + '='): + new_val = line + break + else: + new_val = not_set + + for line in dotconfig_lines: + line = line.rstrip() + if line.startswith(config + '=') or line == not_set: + old_val = line + break + else: + if new_val == not_set: + return (ACTION_NO_ENTRY, config) + else: + return (ACTION_NO_ENTRY_WARN, config) + + # If this CONFIG is neither bool nor trisate + if old_val[-2:] != '=y' and old_val[-2:] != '=m' and old_val != not_set: + # tools/scripts/define2mk.sed changes '1' to 'y'. + # This is a problem if the CONFIG is int type. + # Check the type in Kconfig and handle it correctly. + if new_val[-2:] == '=y': + new_val = new_val[:-1] + '1' + + return (ACTION_NO_CHANGE if old_val == new_val else ACTION_MOVE, + new_val) + + def update_dotconfig(self): + """Parse files for the config options and update the .config. + + This function parses the generated .config and include/autoconf.mk + searching the target options. + Move the config option(s) to the .config as needed. + + Arguments: + defconfig: defconfig name. + + Returns: + Return a tuple of (updated flag, log string). + The "updated flag" is True if the .config was updated, False + otherwise. The "log string" shows what happend to the .config. + """ + + results = [] + updated = False + suspicious = False + rm_files = [self.config_autoconf, self.autoconf] + + if self.options.spl: + if os.path.exists(self.spl_autoconf): + autoconf_path = self.spl_autoconf + rm_files.append(self.spl_autoconf) + else: + for f in rm_files: + os.remove(f) + return (updated, suspicious, + color_text(self.options.color, COLOR_BROWN, + "SPL is not enabled. Skipped.") + '\n') + else: + autoconf_path = self.autoconf + + with open(self.dotconfig) as f: + dotconfig_lines = f.readlines() + + with open(autoconf_path) as f: + autoconf_lines = f.readlines() + + for config in self.configs: + result = self.parse_one_config(config, dotconfig_lines, + autoconf_lines) + results.append(result) + + log = '' + + for (action, value) in results: + if action == ACTION_MOVE: + actlog = "Move '%s'" % value + log_color = COLOR_LIGHT_GREEN + elif action == ACTION_NO_ENTRY: + actlog = "%s is not defined in Kconfig. Do nothing." % value + log_color = COLOR_LIGHT_BLUE + elif action == ACTION_NO_ENTRY_WARN: + actlog = "%s is not defined in Kconfig (suspicious). Do nothing." % value + log_color = COLOR_YELLOW + suspicious = True + elif action == ACTION_NO_CHANGE: + actlog = "'%s' is the same as the define in Kconfig. Do nothing." \ + % value + log_color = COLOR_LIGHT_PURPLE + elif action == ACTION_SPL_NOT_EXIST: + actlog = "SPL is not enabled for this defconfig. Skip." + log_color = COLOR_PURPLE + else: + sys.exit("Internal Error. This should not happen.") + + log += color_text(self.options.color, log_color, actlog) + '\n' + + with open(self.dotconfig, 'a') as f: + for (action, value) in results: + if action == ACTION_MOVE: + f.write(value + '\n') + updated = True + + self.results = results + for f in rm_files: + os.remove(f) + + return (updated, suspicious, log) + + def check_defconfig(self): + """Check the defconfig after savedefconfig + + Returns: + Return additional log if moved CONFIGs were removed again by + 'make savedefconfig'. + """ + + log = '' + + with open(self.defconfig) as f: + defconfig_lines = f.readlines() + + for (action, value) in self.results: + if action != ACTION_MOVE: + continue + if not value + '\n' in defconfig_lines: + log += color_text(self.options.color, COLOR_YELLOW, + "'%s' was removed by savedefconfig.\n" % + value) + + return log + + +class DatabaseThread(threading.Thread): + """This thread processes results from Slot threads. + + It collects the data in the master config directary. There is only one + result thread, and this helps to serialise the build output. + """ + def __init__(self, config_db, db_queue): + """Set up a new result thread + + Args: + builder: Builder which will be sent each result + """ + threading.Thread.__init__(self) + self.config_db = config_db + self.db_queue= db_queue + + def run(self): + """Called to start up the result thread. + + We collect the next result job and pass it on to the build. + """ + while True: + defconfig, configs = self.db_queue.get() + self.config_db[defconfig] = configs + self.db_queue.task_done() + + +class Slot: + + """A slot to store a subprocess. + + Each instance of this class handles one subprocess. + This class is useful to control multiple threads + for faster processing. + """ + + def __init__(self, toolchains, configs, options, progress, devnull, + make_cmd, reference_src_dir, db_queue): + """Create a new process slot. + + Arguments: + toolchains: Toolchains object containing toolchains. + configs: A list of CONFIGs to move. + options: option flags. + progress: A progress indicator. + devnull: A file object of '/dev/null'. + make_cmd: command name of GNU Make. + reference_src_dir: Determine the true starting config state from this + source tree. + db_queue: output queue to write config info for the database + """ + self.toolchains = toolchains + self.options = options + self.progress = progress + self.build_dir = tempfile.mkdtemp() + self.devnull = devnull + self.make_cmd = (make_cmd, 'O=' + self.build_dir) + self.reference_src_dir = reference_src_dir + self.db_queue = db_queue + self.parser = KconfigParser(configs, options, self.build_dir) + self.state = STATE_IDLE + self.failed_boards = set() + self.suspicious_boards = set() + + def __del__(self): + """Delete the working directory + + This function makes sure the temporary directory is cleaned away + even if Python suddenly dies due to error. It should be done in here + because it is guaranteed the destructor is always invoked when the + instance of the class gets unreferenced. + + If the subprocess is still running, wait until it finishes. + """ + if self.state != STATE_IDLE: + while self.ps.poll() == None: + pass + shutil.rmtree(self.build_dir) + + def add(self, defconfig): + """Assign a new subprocess for defconfig and add it to the slot. + + If the slot is vacant, create a new subprocess for processing the + given defconfig and add it to the slot. Just returns False if + the slot is occupied (i.e. the current subprocess is still running). + + Arguments: + defconfig: defconfig name. + + Returns: + Return True on success or False on failure + """ + if self.state != STATE_IDLE: + return False + + self.defconfig = defconfig + self.log = '' + self.current_src_dir = self.reference_src_dir + self.do_defconfig() + return True + + def poll(self): + """Check the status of the subprocess and handle it as needed. + + Returns True if the slot is vacant (i.e. in idle state). + If the configuration is successfully finished, assign a new + subprocess to build include/autoconf.mk. + If include/autoconf.mk is generated, invoke the parser to + parse the .config and the include/autoconf.mk, moving + config options to the .config as needed. + If the .config was updated, run "make savedefconfig" to sync + it, update the original defconfig, and then set the slot back + to the idle state. + + Returns: + Return True if the subprocess is terminated, False otherwise + """ + if self.state == STATE_IDLE: + return True + + if self.ps.poll() == None: + return False + + if self.ps.poll() != 0: + self.handle_error() + elif self.state == STATE_DEFCONFIG: + if self.reference_src_dir and not self.current_src_dir: + self.do_savedefconfig() + else: + self.do_autoconf() + elif self.state == STATE_AUTOCONF: + if self.current_src_dir: + self.current_src_dir = None + self.do_defconfig() + elif self.options.build_db: + self.do_build_db() + else: + self.do_savedefconfig() + elif self.state == STATE_SAVEDEFCONFIG: + self.update_defconfig() + else: + sys.exit("Internal Error. This should not happen.") + + return True if self.state == STATE_IDLE else False + + def handle_error(self): + """Handle error cases.""" + + self.log += color_text(self.options.color, COLOR_LIGHT_RED, + "Failed to process.\n") + if self.options.verbose: + self.log += color_text(self.options.color, COLOR_LIGHT_CYAN, + self.ps.stderr.read()) + self.finish(False) + + def do_defconfig(self): + """Run 'make <board>_defconfig' to create the .config file.""" + + cmd = list(self.make_cmd) + cmd.append(self.defconfig) + self.ps = subprocess.Popen(cmd, stdout=self.devnull, + stderr=subprocess.PIPE, + cwd=self.current_src_dir) + self.state = STATE_DEFCONFIG + + def do_autoconf(self): + """Run 'make AUTO_CONF_PATH'.""" + + arch = self.parser.get_arch() + try: + toolchain = self.toolchains.Select(arch) + except ValueError: + self.log += color_text(self.options.color, COLOR_YELLOW, + "Tool chain for '%s' is missing. Do nothing.\n" % arch) + self.finish(False) + return + env = toolchain.MakeEnvironment(False) + + cmd = list(self.make_cmd) + cmd.append('KCONFIG_IGNORE_DUPLICATES=1') + cmd.append(AUTO_CONF_PATH) + self.ps = subprocess.Popen(cmd, stdout=self.devnull, env=env, + stderr=subprocess.PIPE, + cwd=self.current_src_dir) + self.state = STATE_AUTOCONF + + def do_build_db(self): + """Add the board to the database""" + configs = {} + with open(os.path.join(self.build_dir, AUTO_CONF_PATH)) as fd: + for line in fd.readlines(): + if line.startswith('CONFIG'): + config, value = line.split('=', 1) + configs[config] = value.rstrip() + self.db_queue.put([self.defconfig, configs]) + self.finish(True) + + def do_savedefconfig(self): + """Update the .config and run 'make savedefconfig'.""" + + (updated, suspicious, log) = self.parser.update_dotconfig() + if suspicious: + self.suspicious_boards.add(self.defconfig) + self.log += log + + if not self.options.force_sync and not updated: + self.finish(True) + return + if updated: + self.log += color_text(self.options.color, COLOR_LIGHT_GREEN, + "Syncing by savedefconfig...\n") + else: + self.log += "Syncing by savedefconfig (forced by option)...\n" + + cmd = list(self.make_cmd) + cmd.append('savedefconfig') + self.ps = subprocess.Popen(cmd, stdout=self.devnull, + stderr=subprocess.PIPE) + self.state = STATE_SAVEDEFCONFIG + + def update_defconfig(self): + """Update the input defconfig and go back to the idle state.""" + + log = self.parser.check_defconfig() + if log: + self.suspicious_boards.add(self.defconfig) + self.log += log + orig_defconfig = os.path.join('configs', self.defconfig) + new_defconfig = os.path.join(self.build_dir, 'defconfig') + updated = not filecmp.cmp(orig_defconfig, new_defconfig) + + if updated: + self.log += color_text(self.options.color, COLOR_LIGHT_BLUE, + "defconfig was updated.\n") + + if not self.options.dry_run and updated: + shutil.move(new_defconfig, orig_defconfig) + self.finish(True) + + def finish(self, success): + """Display log along with progress and go to the idle state. + + Arguments: + success: Should be True when the defconfig was processed + successfully, or False when it fails. + """ + # output at least 30 characters to hide the "* defconfigs out of *". + log = self.defconfig.ljust(30) + '\n' + + log += '\n'.join([ ' ' + s for s in self.log.split('\n') ]) + # Some threads are running in parallel. + # Print log atomically to not mix up logs from different threads. + print >> (sys.stdout if success else sys.stderr), log + + if not success: + if self.options.exit_on_error: + sys.exit("Exit on error.") + # If --exit-on-error flag is not set, skip this board and continue. + # Record the failed board. + self.failed_boards.add(self.defconfig) + + self.progress.inc() + self.progress.show() + self.state = STATE_IDLE + + def get_failed_boards(self): + """Returns a set of failed boards (defconfigs) in this slot. + """ + return self.failed_boards + + def get_suspicious_boards(self): + """Returns a set of boards (defconfigs) with possible misconversion. + """ + return self.suspicious_boards - self.failed_boards + +class Slots: + + """Controller of the array of subprocess slots.""" + + def __init__(self, toolchains, configs, options, progress, + reference_src_dir, db_queue): + """Create a new slots controller. + + Arguments: + toolchains: Toolchains object containing toolchains. + configs: A list of CONFIGs to move. + options: option flags. + progress: A progress indicator. + reference_src_dir: Determine the true starting config state from this + source tree. + db_queue: output queue to write config info for the database + """ + self.options = options + self.slots = [] + devnull = get_devnull() + make_cmd = get_make_cmd() + for i in range(options.jobs): + self.slots.append(Slot(toolchains, configs, options, progress, + devnull, make_cmd, reference_src_dir, + db_queue)) + + def add(self, defconfig): + """Add a new subprocess if a vacant slot is found. + + Arguments: + defconfig: defconfig name to be put into. + + Returns: + Return True on success or False on failure + """ + for slot in self.slots: + if slot.add(defconfig): + return True + return False + + def available(self): + """Check if there is a vacant slot. + + Returns: + Return True if at lease one vacant slot is found, False otherwise. + """ + for slot in self.slots: + if slot.poll(): + return True + return False + + def empty(self): + """Check if all slots are vacant. + + Returns: + Return True if all the slots are vacant, False otherwise. + """ + ret = True + for slot in self.slots: + if not slot.poll(): + ret = False + return ret + + def show_failed_boards(self): + """Display all of the failed boards (defconfigs).""" + boards = set() + output_file = 'moveconfig.failed' + + for slot in self.slots: + boards |= slot.get_failed_boards() + + if boards: + boards = '\n'.join(boards) + '\n' + msg = "The following boards were not processed due to error:\n" + msg += boards + msg += "(the list has been saved in %s)\n" % output_file + print >> sys.stderr, color_text(self.options.color, COLOR_LIGHT_RED, + msg) + + with open(output_file, 'w') as f: + f.write(boards) + + def show_suspicious_boards(self): + """Display all boards (defconfigs) with possible misconversion.""" + boards = set() + output_file = 'moveconfig.suspicious' + + for slot in self.slots: + boards |= slot.get_suspicious_boards() + + if boards: + boards = '\n'.join(boards) + '\n' + msg = "The following boards might have been converted incorrectly.\n" + msg += "It is highly recommended to check them manually:\n" + msg += boards + msg += "(the list has been saved in %s)\n" % output_file + print >> sys.stderr, color_text(self.options.color, COLOR_YELLOW, + msg) + + with open(output_file, 'w') as f: + f.write(boards) + +class ReferenceSource: + + """Reference source against which original configs should be parsed.""" + + def __init__(self, commit): + """Create a reference source directory based on a specified commit. + + Arguments: + commit: commit to git-clone + """ + self.src_dir = tempfile.mkdtemp() + print "Cloning git repo to a separate work directory..." + subprocess.check_output(['git', 'clone', os.getcwd(), '.'], + cwd=self.src_dir) + print "Checkout '%s' to build the original autoconf.mk." % \ + subprocess.check_output(['git', 'rev-parse', '--short', commit]).strip() + subprocess.check_output(['git', 'checkout', commit], + stderr=subprocess.STDOUT, cwd=self.src_dir) + + def __del__(self): + """Delete the reference source directory + + This function makes sure the temporary directory is cleaned away + even if Python suddenly dies due to error. It should be done in here + because it is guaranteed the destructor is always invoked when the + instance of the class gets unreferenced. + """ + shutil.rmtree(self.src_dir) + + def get_dir(self): + """Return the absolute path to the reference source directory.""" + + return self.src_dir + +def move_config(toolchains, configs, options, db_queue): + """Move config options to defconfig files. + + Arguments: + configs: A list of CONFIGs to move. + options: option flags + """ + if len(configs) == 0: + if options.force_sync: + print 'No CONFIG is specified. You are probably syncing defconfigs.', + elif options.build_db: + print 'Building %s database' % CONFIG_DATABASE + else: + print 'Neither CONFIG nor --force-sync is specified. Nothing will happen.', + else: + print 'Move ' + ', '.join(configs), + print '(jobs: %d)\n' % options.jobs + + if options.git_ref: + reference_src = ReferenceSource(options.git_ref) + reference_src_dir = reference_src.get_dir() + else: + reference_src_dir = None + + if options.defconfigs: + defconfigs = get_matched_defconfigs(options.defconfigs) + else: + defconfigs = get_all_defconfigs() + + progress = Progress(len(defconfigs)) + slots = Slots(toolchains, configs, options, progress, reference_src_dir, + db_queue) + + # Main loop to process defconfig files: + # Add a new subprocess into a vacant slot. + # Sleep if there is no available slot. + for defconfig in defconfigs: + while not slots.add(defconfig): + while not slots.available(): + # No available slot: sleep for a while + time.sleep(SLEEP_TIME) + + # wait until all the subprocesses finish + while not slots.empty(): + time.sleep(SLEEP_TIME) + + print '' + slots.show_failed_boards() + slots.show_suspicious_boards() + +def find_kconfig_rules(kconf, config, imply_config): + """Check whether a config has a 'select' or 'imply' keyword + + Args: + kconf: Kconfig.Config object + config: Name of config to check (without CONFIG_ prefix) + imply_config: Implying config (without CONFIG_ prefix) which may or + may not have an 'imply' for 'config') + + Returns: + Symbol object for 'config' if found, else None + """ + sym = kconf.get_symbol(imply_config) + if sym: + for sel in sym.get_selected_symbols() | sym.get_implied_symbols(): + if sel.get_name() == config: + return sym + return None + +def check_imply_rule(kconf, config, imply_config): + """Check if we can add an 'imply' option + + This finds imply_config in the Kconfig and looks to see if it is possible + to add an 'imply' for 'config' to that part of the Kconfig. + + Args: + kconf: Kconfig.Config object + config: Name of config to check (without CONFIG_ prefix) + imply_config: Implying config (without CONFIG_ prefix) which may or + may not have an 'imply' for 'config') + + Returns: + tuple: + filename of Kconfig file containing imply_config, or None if none + line number within the Kconfig file, or 0 if none + message indicating the result + """ + sym = kconf.get_symbol(imply_config) + if not sym: + return 'cannot find sym' + locs = sym.get_def_locations() + if len(locs) != 1: + return '%d locations' % len(locs) + fname, linenum = locs[0] + cwd = os.getcwd() + if cwd and fname.startswith(cwd): + fname = fname[len(cwd) + 1:] + file_line = ' at %s:%d' % (fname, linenum) + with open(fname) as fd: + data = fd.read().splitlines() + if data[linenum - 1] != 'config %s' % imply_config: + return None, 0, 'bad sym format %s%s' % (data[linenum], file_line) + return fname, linenum, 'adding%s' % file_line + +def add_imply_rule(config, fname, linenum): + """Add a new 'imply' option to a Kconfig + + Args: + config: config option to add an imply for (without CONFIG_ prefix) + fname: Kconfig filename to update + linenum: Line number to place the 'imply' before + + Returns: + Message indicating the result + """ + file_line = ' at %s:%d' % (fname, linenum) + data = open(fname).read().splitlines() + linenum -= 1 + + for offset, line in enumerate(data[linenum:]): + if line.strip().startswith('help') or not line: + data.insert(linenum + offset, '\timply %s' % config) + with open(fname, 'w') as fd: + fd.write('\n'.join(data) + '\n') + return 'added%s' % file_line + + return 'could not insert%s' + +(IMPLY_MIN_2, IMPLY_TARGET, IMPLY_CMD, IMPLY_NON_ARCH_BOARD) = ( + 1, 2, 4, 8) + +IMPLY_FLAGS = { + 'min2': [IMPLY_MIN_2, 'Show options which imply >2 boards (normally >5)'], + 'target': [IMPLY_TARGET, 'Allow CONFIG_TARGET_... options to imply'], + 'cmd': [IMPLY_CMD, 'Allow CONFIG_CMD_... to imply'], + 'non-arch-board': [ + IMPLY_NON_ARCH_BOARD, + 'Allow Kconfig options outside arch/ and /board/ to imply'], +}; + +def do_imply_config(config_list, add_imply, imply_flags, skip_added, + check_kconfig=True, find_superset=False): + """Find CONFIG options which imply those in the list + + Some CONFIG options can be implied by others and this can help to reduce + the size of the defconfig files. For example, CONFIG_X86 implies + CONFIG_CMD_IRQ, so we can put 'imply CMD_IRQ' under 'config X86' and + all x86 boards will have that option, avoiding adding CONFIG_CMD_IRQ to + each of the x86 defconfig files. + + This function uses the moveconfig database to find such options. It + displays a list of things that could possibly imply those in the list. + The algorithm ignores any that start with CONFIG_TARGET since these + typically refer to only a few defconfigs (often one). It also does not + display a config with less than 5 defconfigs. + + The algorithm works using sets. For each target config in config_list: + - Get the set 'defconfigs' which use that target config + - For each config (from a list of all configs): + - Get the set 'imply_defconfig' of defconfigs which use that config + - + - If imply_defconfigs contains anything not in defconfigs then + this config does not imply the target config + + Params: + config_list: List of CONFIG options to check (each a string) + add_imply: Automatically add an 'imply' for each config. + imply_flags: Flags which control which implying configs are allowed + (IMPLY_...) + skip_added: Don't show options which already have an imply added. + check_kconfig: Check if implied symbols already have an 'imply' or + 'select' for the target config, and show this information if so. + find_superset: True to look for configs which are a superset of those + already found. So for example if CONFIG_EXYNOS5 implies an option, + but CONFIG_EXYNOS covers a larger set of defconfigs and also + implies that option, this will drop the former in favour of the + latter. In practice this option has not proved very used. + + Note the terminoloy: + config - a CONFIG_XXX options (a string, e.g. 'CONFIG_CMD_EEPROM') + defconfig - a defconfig file (a string, e.g. 'configs/snow_defconfig') + """ + kconf = KconfigScanner().conf if check_kconfig else None + if add_imply and add_imply != 'all': + add_imply = add_imply.split() + + # key is defconfig name, value is dict of (CONFIG_xxx, value) + config_db = {} + + # Holds a dict containing the set of defconfigs that contain each config + # key is config, value is set of defconfigs using that config + defconfig_db = collections.defaultdict(set) + + # Set of all config options we have seen + all_configs = set() + + # Set of all defconfigs we have seen + all_defconfigs = set() + + # Read in the database + configs = {} + with open(CONFIG_DATABASE) as fd: + for line in fd.readlines(): + line = line.rstrip() + if not line: # Separator between defconfigs + config_db[defconfig] = configs + all_defconfigs.add(defconfig) + configs = {} + elif line[0] == ' ': # CONFIG line + config, value = line.strip().split('=', 1) + configs[config] = value + defconfig_db[config].add(defconfig) + all_configs.add(config) + else: # New defconfig + defconfig = line + + # Work through each target config option in tern, independently + for config in config_list: + defconfigs = defconfig_db.get(config) + if not defconfigs: + print '%s not found in any defconfig' % config + continue + + # Get the set of defconfigs without this one (since a config cannot + # imply itself) + non_defconfigs = all_defconfigs - defconfigs + num_defconfigs = len(defconfigs) + print '%s found in %d/%d defconfigs' % (config, num_defconfigs, + len(all_configs)) + + # This will hold the results: key=config, value=defconfigs containing it + imply_configs = {} + rest_configs = all_configs - set([config]) + + # Look at every possible config, except the target one + for imply_config in rest_configs: + if 'ERRATUM' in imply_config: + continue + if not (imply_flags & IMPLY_CMD): + if 'CONFIG_CMD' in imply_config: + continue + if not (imply_flags & IMPLY_TARGET): + if 'CONFIG_TARGET' in imply_config: + continue + + # Find set of defconfigs that have this config + imply_defconfig = defconfig_db[imply_config] + + # Get the intersection of this with defconfigs containing the + # target config + common_defconfigs = imply_defconfig & defconfigs + + # Get the set of defconfigs containing this config which DO NOT + # also contain the taret config. If this set is non-empty it means + # that this config affects other defconfigs as well as (possibly) + # the ones affected by the target config. This means it implies + # things we don't want to imply. + not_common_defconfigs = imply_defconfig & non_defconfigs + if not_common_defconfigs: + continue + + # If there are common defconfigs, imply_config may be useful + if common_defconfigs: + skip = False + if find_superset: + for prev in imply_configs.keys(): + prev_count = len(imply_configs[prev]) + count = len(common_defconfigs) + if (prev_count > count and + (imply_configs[prev] & common_defconfigs == + common_defconfigs)): + # skip imply_config because prev is a superset + skip = True + break + elif count > prev_count: + # delete prev because imply_config is a superset + del imply_configs[prev] + if not skip: + imply_configs[imply_config] = common_defconfigs + + # Now we have a dict imply_configs of configs which imply each config + # The value of each dict item is the set of defconfigs containing that + # config. Rank them so that we print the configs that imply the largest + # number of defconfigs first. + ranked_iconfigs = sorted(imply_configs, + key=lambda k: len(imply_configs[k]), reverse=True) + kconfig_info = '' + cwd = os.getcwd() + add_list = collections.defaultdict(list) + for iconfig in ranked_iconfigs: + num_common = len(imply_configs[iconfig]) + + # Don't bother if there are less than 5 defconfigs affected. + if num_common < (2 if imply_flags & IMPLY_MIN_2 else 5): + continue + missing = defconfigs - imply_configs[iconfig] + missing_str = ', '.join(missing) if missing else 'all' + missing_str = '' + show = True + if kconf: + sym = find_kconfig_rules(kconf, config[CONFIG_LEN:], + iconfig[CONFIG_LEN:]) + kconfig_info = '' + if sym: + locs = sym.get_def_locations() + if len(locs) == 1: + fname, linenum = locs[0] + if cwd and fname.startswith(cwd): + fname = fname[len(cwd) + 1:] + kconfig_info = '%s:%d' % (fname, linenum) + if skip_added: + show = False + else: + sym = kconf.get_symbol(iconfig[CONFIG_LEN:]) + fname = '' + if sym: + locs = sym.get_def_locations() + if len(locs) == 1: + fname, linenum = locs[0] + if cwd and fname.startswith(cwd): + fname = fname[len(cwd) + 1:] + in_arch_board = not sym or (fname.startswith('arch') or + fname.startswith('board')) + if (not in_arch_board and + not (imply_flags & IMPLY_NON_ARCH_BOARD)): + continue + + if add_imply and (add_imply == 'all' or + iconfig in add_imply): + fname, linenum, kconfig_info = (check_imply_rule(kconf, + config[CONFIG_LEN:], iconfig[CONFIG_LEN:])) + if fname: + add_list[fname].append(linenum) + + if show and kconfig_info != 'skip': + print '%5d : %-30s%-25s %s' % (num_common, iconfig.ljust(30), + kconfig_info, missing_str) + + # Having collected a list of things to add, now we add them. We process + # each file from the largest line number to the smallest so that + # earlier additions do not affect our line numbers. E.g. if we added an + # imply at line 20 it would change the position of each line after + # that. + for fname, linenums in add_list.iteritems(): + for linenum in sorted(linenums, reverse=True): + add_imply_rule(config[CONFIG_LEN:], fname, linenum) + + +def main(): + try: + cpu_count = multiprocessing.cpu_count() + except NotImplementedError: + cpu_count = 1 + + parser = optparse.OptionParser() + # Add options here + parser.add_option('-a', '--add-imply', type='string', default='', + help='comma-separated list of CONFIG options to add ' + "an 'imply' statement to for the CONFIG in -i") + parser.add_option('-A', '--skip-added', action='store_true', default=False, + help="don't show options which are already marked as " + 'implying others') + parser.add_option('-b', '--build-db', action='store_true', default=False, + help='build a CONFIG database') + parser.add_option('-c', '--color', action='store_true', default=False, + help='display the log in color') + parser.add_option('-C', '--commit', action='store_true', default=False, + help='Create a git commit for the operation') + parser.add_option('-d', '--defconfigs', type='string', + help='a file containing a list of defconfigs to move, ' + "one per line (for example 'snow_defconfig') " + "or '-' to read from stdin") + parser.add_option('-i', '--imply', action='store_true', default=False, + help='find options which imply others') + parser.add_option('-I', '--imply-flags', type='string', default='', + help="control the -i option ('help' for help") + parser.add_option('-n', '--dry-run', action='store_true', default=False, + help='perform a trial run (show log with no changes)') + parser.add_option('-e', '--exit-on-error', action='store_true', + default=False, + help='exit immediately on any error') + parser.add_option('-s', '--force-sync', action='store_true', default=False, + help='force sync by savedefconfig') + parser.add_option('-S', '--spl', action='store_true', default=False, + help='parse config options defined for SPL build') + parser.add_option('-H', '--headers-only', dest='cleanup_headers_only', + action='store_true', default=False, + help='only cleanup the headers') + parser.add_option('-j', '--jobs', type='int', default=cpu_count, + help='the number of jobs to run simultaneously') + parser.add_option('-r', '--git-ref', type='string', + help='the git ref to clone for building the autoconf.mk') + parser.add_option('-y', '--yes', action='store_true', default=False, + help="respond 'yes' to any prompts") + parser.add_option('-v', '--verbose', action='store_true', default=False, + help='show any build errors as boards are built') + parser.usage += ' CONFIG ...' + + (options, configs) = parser.parse_args() + + if len(configs) == 0 and not any((options.force_sync, options.build_db, + options.imply)): + parser.print_usage() + sys.exit(1) + + # prefix the option name with CONFIG_ if missing + configs = [ config if config.startswith('CONFIG_') else 'CONFIG_' + config + for config in configs ] + + check_top_directory() + + if options.imply: + imply_flags = 0 + if options.imply_flags == 'all': + imply_flags = -1 + + elif options.imply_flags: + for flag in options.imply_flags.split(','): + bad = flag not in IMPLY_FLAGS + if bad: + print "Invalid flag '%s'" % flag + if flag == 'help' or bad: + print "Imply flags: (separate with ',')" + for name, info in IMPLY_FLAGS.iteritems(): + print ' %-15s: %s' % (name, info[1]) + parser.print_usage() + sys.exit(1) + imply_flags |= IMPLY_FLAGS[flag][0] + + do_imply_config(configs, options.add_imply, imply_flags, + options.skip_added) + return + + config_db = {} + db_queue = Queue.Queue() + t = DatabaseThread(config_db, db_queue) + t.setDaemon(True) + t.start() + + if not options.cleanup_headers_only: + check_clean_directory() + bsettings.Setup('') + toolchains = toolchain.Toolchains() + toolchains.GetSettings() + toolchains.Scan(verbose=False) + move_config(toolchains, configs, options, db_queue) + db_queue.join() + + if configs: + cleanup_headers(configs, options) + cleanup_extra_options(configs, options) + cleanup_whitelist(configs, options) + cleanup_readme(configs, options) + + if options.commit: + subprocess.call(['git', 'add', '-u']) + if configs: + msg = 'Convert %s %sto Kconfig' % (configs[0], + 'et al ' if len(configs) > 1 else '') + msg += ('\n\nThis converts the following to Kconfig:\n %s\n' % + '\n '.join(configs)) + else: + msg = 'configs: Resync with savedefconfig' + msg += '\n\nRsync all defconfig files using moveconfig.py' + subprocess.call(['git', 'commit', '-s', '-m', msg]) + + if options.build_db: + with open(CONFIG_DATABASE, 'w') as fd: + for defconfig, configs in config_db.iteritems(): + fd.write('%s\n' % defconfig) + for config in sorted(configs.keys()): + fd.write(' %s=%s\n' % (config, configs[config])) + fd.write('\n') + +if __name__ == '__main__': + main() diff --git a/tools/u-boot-tools/mrvl_uart.sh b/tools/u-boot-tools/mrvl_uart.sh new file mode 100755 index 0000000..a46411f --- /dev/null +++ b/tools/u-boot-tools/mrvl_uart.sh @@ -0,0 +1,119 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0 +# +###################################################### +# Copyright (C) 2016 Marvell International Ltd. +# +# https://spdx.org/licenses +# +# Author: Konstantin Porotchkin kostap@marvell.com +# +# Version 0.3 +# +# UART recovery downloader for Armada SoCs +# +###################################################### + +port=$1 +file=$2 +speed=$3 + +pattern_repeat=1500 +default_baudrate=115200 +tmpfile=/tmp/xmodem.pattern +tools=( dd stty sx minicom ) + +case "$3" in + 2) + fast_baudrate=230400 + prefix="\xF2" + ;; + 4) + fast_baudrate=460800 + prefix="\xF4" + ;; + 8) + fast_baudrate=921600 + prefix="\xF8" + ;; + *) + fast_baudrate=$default_baudrate + prefix="\xBB" +esac + +if [[ -z "$port" || -z "$file" ]] +then + echo -e "\nMarvell recovery image downloader for Armada SoC family." + echo -e "Command syntax:" + echo -e "\t$(basename $0) <port> <file> [2|4|8]" + echo -e "\tport - serial port the target board is connected to" + echo -e "\tfile - recovery boot image for target download" + echo -e "\t2|4|8 - times to increase the default serial port speed by" + echo -e "For example - load the image over ttyUSB0 @ 460800 baud:" + echo -e "$(basename $0) /dev/ttyUSB0 /tmp/flash-image.bin 4\n" + echo -e "=====WARNING=====" + echo -e "- The speed-up option is not available in SoC families prior to A8K+" + echo -e "- This utility is not compatible with Armada 37xx SoC family\n" +fi + +# Sanity checks +if [ -c "$port" ] +then + echo -e "Using device connected on serial port \"$port\"" +else + echo "Wrong serial port name!" + exit 1 +fi + +if [ -f "$file" ] +then + echo -e "Loading flash image file \"$file\"" +else + echo "File $file does not exist!" + exit 1 +fi + +# Verify required tools installation +for tool in ${tools[@]} +do + toolname=`which $tool` + if [ -z "$toolname" ] + then + echo -e "Missing installation of \"$tool\" --> Exiting" + exit 1 + fi +done + + +echo -e "Recovery will run at $fast_baudrate baud" +echo -e "========================================" + +if [ -f "$tmpfile" ] +then + rm -f $tmpfile +fi + +# Send the escape sequence to target board using default debug port speed +stty -F $port raw ignbrk time 5 $default_baudrate +counter=0 +while [ $counter -lt $pattern_repeat ]; do + echo -n -e "$prefix\x11\x22\x33\x44\x55\x66\x77" >> $tmpfile + let counter=counter+1 +done + +echo -en "Press the \"Reset\" button on the target board and " +echo -en "the \"Enter\" key on the host keyboard simultaneously" +read +dd if=$tmpfile of=$port &>/dev/null + +# Speed up the binary image transfer +stty -F $port raw ignbrk time 5 $fast_baudrate +sx -vv $file > $port < $port +#sx-at91 $port $file + +# Return the port to the default speed +stty -F $port raw ignbrk time 5 $default_baudrate + +# Optional - fire up Minicom +minicom -D $port -b $default_baudrate + diff --git a/tools/u-boot-tools/mtk_image.c b/tools/u-boot-tools/mtk_image.c new file mode 100644 index 0000000..2ca5194 --- /dev/null +++ b/tools/u-boot-tools/mtk_image.c @@ -0,0 +1,749 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Generate MediaTek BootROM header for SPL/U-Boot images + * + * Copyright (C) 2018 MediaTek Inc. + * Author: Weijie Gao <weijie.gao@mediatek.com> + */ + +#include <image.h> +#include <u-boot/sha256.h> +#include "imagetool.h" +#include "mtk_image.h" + +/* NAND header for SPI-NAND with 2KB page + 64B spare */ +static const union nand_boot_header snand_hdr_2k_64_data = { + .data = { + 0x42, 0x4F, 0x4F, 0x54, 0x4C, 0x4F, 0x41, 0x44, + 0x45, 0x52, 0x21, 0x00, 0x56, 0x30, 0x30, 0x36, + 0x4E, 0x46, 0x49, 0x49, 0x4E, 0x46, 0x4F, 0x00, + 0x00, 0x00, 0x00, 0x08, 0x03, 0x00, 0x40, 0x00, + 0x40, 0x00, 0x00, 0x08, 0x10, 0x00, 0x16, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x7B, 0xC4, 0x17, 0x9D, + 0xCA, 0x42, 0x90, 0xD0, 0x98, 0xD0, 0xE0, 0xF7, + 0xDB, 0xCD, 0x16, 0xF6, 0x03, 0x73, 0xD2, 0xB8, + 0x93, 0xB2, 0x56, 0x5A, 0x84, 0x6E, 0x00, 0x00 + } +}; + +/* NAND header for SPI-NAND with 2KB page + 120B/128B spare */ +static const union nand_boot_header snand_hdr_2k_128_data = { + .data = { + 0x42, 0x4F, 0x4F, 0x54, 0x4C, 0x4F, 0x41, 0x44, + 0x45, 0x52, 0x21, 0x00, 0x56, 0x30, 0x30, 0x36, + 0x4E, 0x46, 0x49, 0x49, 0x4E, 0x46, 0x4F, 0x00, + 0x00, 0x00, 0x00, 0x08, 0x05, 0x00, 0x70, 0x00, + 0x40, 0x00, 0x00, 0x08, 0x10, 0x00, 0x16, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x90, 0x28, 0xED, 0x13, + 0x7F, 0x12, 0x22, 0xCD, 0x3D, 0x06, 0xF1, 0xB3, + 0x6F, 0x2E, 0xD9, 0xA0, 0x9D, 0x7A, 0xBD, 0xD7, + 0xB3, 0x28, 0x3C, 0x13, 0xDB, 0x4E, 0x00, 0x00 + } +}; + +/* NAND header for SPI-NAND with 4KB page + 256B spare */ +static const union nand_boot_header snand_hdr_4k_256_data = { + .data = { + 0x42, 0x4F, 0x4F, 0x54, 0x4C, 0x4F, 0x41, 0x44, + 0x45, 0x52, 0x21, 0x00, 0x56, 0x30, 0x30, 0x36, + 0x4E, 0x46, 0x49, 0x49, 0x4E, 0x46, 0x4F, 0x00, + 0x00, 0x00, 0x00, 0x10, 0x05, 0x00, 0xE0, 0x00, + 0x40, 0x00, 0x00, 0x08, 0x10, 0x00, 0x16, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x47, 0xED, 0x0E, 0xC3, + 0x83, 0xBF, 0x41, 0xD2, 0x85, 0x21, 0x97, 0x57, + 0xC4, 0x2E, 0x6B, 0x7A, 0x40, 0xE0, 0xCF, 0x8F, + 0x37, 0xBD, 0x17, 0xB6, 0xC7, 0xFE, 0x00, 0x00 + } +}; + +/* NAND header for Parallel NAND 1Gb with 2KB page + 64B spare */ +static const union nand_boot_header nand_hdr_1gb_2k_64_data = { + .data = { + 0x42, 0x4F, 0x4F, 0x54, 0x4C, 0x4F, 0x41, 0x44, + 0x45, 0x52, 0x21, 0x00, 0x56, 0x30, 0x30, 0x36, + 0x4E, 0x46, 0x49, 0x49, 0x4E, 0x46, 0x4F, 0x00, + 0x00, 0x00, 0x00, 0x08, 0x05, 0x00, 0x40, 0x00, + 0x40, 0x00, 0x00, 0x04, 0x0B, 0x00, 0x11, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x12, 0x28, 0x1C, 0x12, + 0x8F, 0xFD, 0xF8, 0x32, 0x6F, 0x6D, 0xCF, 0x6C, + 0xDA, 0x21, 0x70, 0x8C, 0xDA, 0x0A, 0x22, 0x82, + 0xAA, 0x59, 0xFA, 0x7C, 0x42, 0x2D, 0x00, 0x00 + } +}; + +/* NAND header for Parallel NAND 2Gb with 2KB page + 64B spare */ +static const union nand_boot_header nand_hdr_2gb_2k_64_data = { + .data = { + 0x42, 0x4F, 0x4F, 0x54, 0x4C, 0x4F, 0x41, 0x44, + 0x45, 0x52, 0x21, 0x00, 0x56, 0x30, 0x30, 0x36, + 0x4E, 0x46, 0x49, 0x49, 0x4E, 0x46, 0x4F, 0x00, + 0x00, 0x00, 0x00, 0x08, 0x05, 0x00, 0x40, 0x00, + 0x40, 0x00, 0x00, 0x08, 0x0B, 0x00, 0x11, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x20, 0x9C, 0x3D, 0x2D, + 0x7B, 0x68, 0x63, 0x52, 0x2E, 0x04, 0x63, 0xF1, + 0x35, 0x4E, 0x44, 0x3E, 0xF8, 0xAC, 0x9B, 0x95, + 0xAB, 0xFE, 0xE4, 0xE1, 0xD5, 0xF9, 0x00, 0x00 + } +}; + +/* NAND header for Parallel NAND 4Gb with 2KB page + 64B spare */ +static const union nand_boot_header nand_hdr_4gb_2k_64_data = { + .data = { + 0x42, 0x4F, 0x4F, 0x54, 0x4C, 0x4F, 0x41, 0x44, + 0x45, 0x52, 0x21, 0x00, 0x56, 0x30, 0x30, 0x36, + 0x4E, 0x46, 0x49, 0x49, 0x4E, 0x46, 0x4F, 0x00, + 0x00, 0x00, 0x00, 0x08, 0x05, 0x00, 0x40, 0x00, + 0x40, 0x00, 0x00, 0x10, 0x0B, 0x00, 0x11, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xE3, 0x0F, 0x86, 0x32, + 0x68, 0x05, 0xD9, 0xC8, 0x13, 0xDF, 0xC5, 0x0B, + 0x35, 0x3A, 0x68, 0xA5, 0x3C, 0x0C, 0x73, 0x87, + 0x63, 0xB0, 0xBE, 0xCC, 0x84, 0x47, 0x00, 0x00 + } +}; + +/* NAND header for Parallel NAND 2Gb with 2KB page + 128B spare */ +static const union nand_boot_header nand_hdr_2gb_2k_128_data = { + .data = { + 0x42, 0x4F, 0x4F, 0x54, 0x4C, 0x4F, 0x41, 0x44, + 0x45, 0x52, 0x21, 0x00, 0x56, 0x30, 0x30, 0x36, + 0x4E, 0x46, 0x49, 0x49, 0x4E, 0x46, 0x4F, 0x00, + 0x00, 0x00, 0x00, 0x08, 0x05, 0x00, 0x70, 0x00, + 0x40, 0x00, 0x00, 0x08, 0x0B, 0x00, 0x11, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0xA5, 0xE9, 0x5A, + 0xDF, 0x58, 0x62, 0x41, 0xD6, 0x26, 0x77, 0xBC, + 0x76, 0x1F, 0x27, 0x4E, 0x4F, 0x6C, 0xC3, 0xF0, + 0x36, 0xDE, 0xD9, 0xB3, 0xFF, 0x93, 0x00, 0x00 + } +}; + +/* NAND header for Parallel NAND 4Gb with 2KB page + 128B spare */ +static const union nand_boot_header nand_hdr_4gb_2k_128_data = { + .data = { + 0x42, 0x4F, 0x4F, 0x54, 0x4C, 0x4F, 0x41, 0x44, + 0x45, 0x52, 0x21, 0x00, 0x56, 0x30, 0x30, 0x36, + 0x4E, 0x46, 0x49, 0x49, 0x4E, 0x46, 0x4F, 0x00, + 0x00, 0x00, 0x00, 0x08, 0x05, 0x00, 0x70, 0x00, + 0x40, 0x00, 0x00, 0x10, 0x0B, 0x00, 0x11, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xC2, 0x36, 0x52, 0x45, + 0xCC, 0x35, 0xD8, 0xDB, 0xEB, 0xFD, 0xD1, 0x46, + 0x76, 0x6B, 0x0B, 0xD5, 0x8B, 0xCC, 0x2B, 0xE2, + 0xFE, 0x90, 0x83, 0x9E, 0xAE, 0x2D, 0x00, 0x00 + } +}; + +static const struct nand_header_type { + const char *name; + const union nand_boot_header *data; +} nand_headers[] = { + { + .name = "2k+64", + .data = &snand_hdr_2k_64_data + }, { + .name = "2k+120", + .data = &snand_hdr_2k_128_data + }, { + .name = "2k+128", + .data = &snand_hdr_2k_128_data + }, { + .name = "4k+256", + .data = &snand_hdr_4k_256_data + }, { + .name = "1g:2k+64", + .data = &nand_hdr_1gb_2k_64_data + }, { + .name = "2g:2k+64", + .data = &nand_hdr_2gb_2k_64_data + }, { + .name = "4g:2k+64", + .data = &nand_hdr_4gb_2k_64_data + }, { + .name = "2g:2k+128", + .data = &nand_hdr_2gb_2k_128_data + }, { + .name = "4g:2k+128", + .data = &nand_hdr_4gb_2k_128_data + } +}; + +static const struct brom_img_type { + const char *name; + enum brlyt_img_type type; +} brom_images[] = { + { + .name = "nand", + .type = BRLYT_TYPE_NAND + }, { + .name = "emmc", + .type = BRLYT_TYPE_EMMC + }, { + .name = "nor", + .type = BRLYT_TYPE_NOR + }, { + .name = "sdmmc", + .type = BRLYT_TYPE_SDMMC + }, { + .name = "snand", + .type = BRLYT_TYPE_SNAND + } +}; + +/* Image type selected by user */ +static enum brlyt_img_type hdr_media; +static int use_lk_hdr; + +/* LK image name */ +static char lk_name[32] = "U-Boot"; + +/* NAND header selected by user */ +static const union nand_boot_header *hdr_nand; + +/* GFH header + 2 * 4KB pages of NAND */ +static char hdr_tmp[sizeof(struct gfh_header) + 0x2000]; + +static int mtk_image_check_image_types(uint8_t type) +{ + if (type == IH_TYPE_MTKIMAGE) + return EXIT_SUCCESS; + else + return EXIT_FAILURE; +} + +static int mtk_brom_parse_imagename(const char *imagename) +{ +#define is_blank_char(c) \ + ((c) == '\t' || (c) == '\n' || (c) == '\r' || (c) == ' ') + + char *buf = strdup(imagename), *key, *val, *end, *next; + int i; + + /* User passed arguments from image name */ + static const char *media = ""; + static const char *nandinfo = ""; + static const char *lk = ""; + + key = buf; + while (key) { + next = strchr(key, ';'); + if (next) + *next = 0; + + val = strchr(key, '='); + if (val) { + *val++ = 0; + + /* Trim key */ + while (is_blank_char(*key)) + key++; + + end = key + strlen(key) - 1; + while ((end >= key) && is_blank_char(*end)) + end--; + end++; + + if (is_blank_char(*end)) + *end = 0; + + /* Trim value */ + while (is_blank_char(*val)) + val++; + + end = val + strlen(val) - 1; + while ((end >= val) && is_blank_char(*end)) + end--; + end++; + + if (is_blank_char(*end)) + *end = 0; + + /* record user passed arguments */ + if (!strcmp(key, "media")) + media = val; + + if (!strcmp(key, "nandinfo")) + nandinfo = val; + + if (!strcmp(key, "lk")) + lk = val; + + if (!strcmp(key, "lkname")) + snprintf(lk_name, sizeof(lk_name), "%s", val); + } + + if (next) + key = next + 1; + else + break; + } + + /* if user specified LK image header, skip following checks */ + if (lk && lk[0] == '1') { + use_lk_hdr = 1; + free(buf); + return 0; + } + + /* parse media type */ + for (i = 0; i < ARRAY_SIZE(brom_images); i++) { + if (!strcmp(brom_images[i].name, media)) { + hdr_media = brom_images[i].type; + break; + } + } + + /* parse nand header type */ + for (i = 0; i < ARRAY_SIZE(nand_headers); i++) { + if (!strcmp(nand_headers[i].name, nandinfo)) { + hdr_nand = nand_headers[i].data; + break; + } + } + + free(buf); + + if (hdr_media == BRLYT_TYPE_INVALID) { + fprintf(stderr, "Error: media type is invalid or missing.\n"); + fprintf(stderr, " Please specify -n \"media=<type>\"\n"); + return -EINVAL; + } + + if ((hdr_media == BRLYT_TYPE_NAND || hdr_media == BRLYT_TYPE_SNAND) && + !hdr_nand) { + fprintf(stderr, "Error: nand info is invalid or missing.\n"); + fprintf(stderr, " Please specify -n \"media=%s;" + "nandinfo=<info>\"\n", media); + return -EINVAL; + } + + return 0; +} + +static int mtk_image_check_params(struct image_tool_params *params) +{ + if (!params->addr) { + fprintf(stderr, "Error: Load Address must be set.\n"); + return -EINVAL; + } + + if (!params->imagename) { + fprintf(stderr, "Error: Image Name must be set.\n"); + return -EINVAL; + } + + return mtk_brom_parse_imagename(params->imagename); +} + +static int mtk_image_vrec_header(struct image_tool_params *params, + struct image_type_params *tparams) +{ + if (use_lk_hdr) { + tparams->header_size = sizeof(union lk_hdr); + tparams->hdr = &hdr_tmp; + memset(&hdr_tmp, 0xff, tparams->header_size); + return 0; + } + + if (hdr_media == BRLYT_TYPE_NAND || hdr_media == BRLYT_TYPE_SNAND) + tparams->header_size = 2 * le16_to_cpu(hdr_nand->pagesize); + else + tparams->header_size = sizeof(struct gen_device_header); + + tparams->header_size += sizeof(struct gfh_header); + tparams->hdr = &hdr_tmp; + + memset(&hdr_tmp, 0xff, tparams->header_size); + + return SHA256_SUM_LEN; +} + +static int mtk_image_verify_gen_header(const uint8_t *ptr, int print) +{ + union gen_boot_header *gbh = (union gen_boot_header *)ptr; + struct brom_layout_header *bh; + struct gfh_header *gfh; + const char *bootmedia; + + if (!strcmp(gbh->name, SF_BOOT_NAME)) + bootmedia = "Serial NOR"; + else if (!strcmp(gbh->name, EMMC_BOOT_NAME)) + bootmedia = "eMMC"; + else if (!strcmp(gbh->name, SDMMC_BOOT_NAME)) + bootmedia = "SD/MMC"; + else + return -1; + + if (print) + printf("Boot Media: %s\n", bootmedia); + + if (le32_to_cpu(gbh->version) != 1 || + le32_to_cpu(gbh->size) != sizeof(union gen_boot_header)) + return -1; + + bh = (struct brom_layout_header *)(ptr + le32_to_cpu(gbh->size)); + + if (strcmp(bh->name, BRLYT_NAME)) + return -1; + + if (le32_to_cpu(bh->magic) != BRLYT_MAGIC || + (le32_to_cpu(bh->type) != BRLYT_TYPE_NOR && + le32_to_cpu(bh->type) != BRLYT_TYPE_EMMC && + le32_to_cpu(bh->type) != BRLYT_TYPE_SDMMC)) + return -1; + + gfh = (struct gfh_header *)(ptr + le32_to_cpu(bh->header_size)); + + if (strcmp(gfh->file_info.name, GFH_FILE_INFO_NAME)) + return -1; + + if (le32_to_cpu(gfh->file_info.flash_type) != GFH_FLASH_TYPE_GEN) + return -1; + + if (print) + printf("Load Address: %08x\n", + le32_to_cpu(gfh->file_info.load_addr) + + le32_to_cpu(gfh->file_info.jump_offset)); + + return 0; +} + +static int mtk_image_verify_nand_header(const uint8_t *ptr, int print) +{ + union nand_boot_header *nh = (union nand_boot_header *)ptr; + struct brom_layout_header *bh; + struct gfh_header *gfh; + const char *bootmedia; + + if (strncmp(nh->version, NAND_BOOT_VERSION, sizeof(nh->version)) || + strcmp(nh->id, NAND_BOOT_ID)) + return -1; + + bh = (struct brom_layout_header *)(ptr + le16_to_cpu(nh->pagesize)); + + if (strcmp(bh->name, BRLYT_NAME)) + return -1; + + if (le32_to_cpu(bh->magic) != BRLYT_MAGIC) { + return -1; + } else { + if (le32_to_cpu(bh->type) == BRLYT_TYPE_NAND) + bootmedia = "Parallel NAND"; + else if (le32_to_cpu(bh->type) == BRLYT_TYPE_SNAND) + bootmedia = "Serial NAND"; + else + return -1; + } + + if (print) { + printf("Boot Media: %s\n", bootmedia); + + if (le32_to_cpu(bh->type) == BRLYT_TYPE_NAND) { + uint64_t capacity = + (uint64_t)le16_to_cpu(nh->numblocks) * + (uint64_t)le16_to_cpu(nh->pages_of_block) * + (uint64_t)le16_to_cpu(nh->pagesize) * 8; + printf("Capacity: %dGb\n", + (uint32_t)(capacity >> 30)); + } + + if (le16_to_cpu(nh->pagesize) >= 1024) + printf("Page Size: %dKB\n", + le16_to_cpu(nh->pagesize) >> 10); + else + printf("Page Size: %dB\n", + le16_to_cpu(nh->pagesize)); + + printf("Spare Size: %dB\n", le16_to_cpu(nh->oobsize)); + } + + gfh = (struct gfh_header *)(ptr + 2 * le16_to_cpu(nh->pagesize)); + + if (strcmp(gfh->file_info.name, GFH_FILE_INFO_NAME)) + return -1; + + if (le32_to_cpu(gfh->file_info.flash_type) != GFH_FLASH_TYPE_NAND) + return -1; + + if (print) + printf("Load Address: %08x\n", + le32_to_cpu(gfh->file_info.load_addr) + + le32_to_cpu(gfh->file_info.jump_offset)); + + return 0; +} + +static int mtk_image_verify_header(unsigned char *ptr, int image_size, + struct image_tool_params *params) +{ + union lk_hdr *lk = (union lk_hdr *)ptr; + + /* nothing to verify for LK image header */ + if (le32_to_cpu(lk->magic) == LK_PART_MAGIC) + return 0; + + if (!strcmp((char *)ptr, NAND_BOOT_NAME)) + return mtk_image_verify_nand_header(ptr, 0); + else + return mtk_image_verify_gen_header(ptr, 0); + + return -1; +} + +static void mtk_image_print_header(const void *ptr) +{ + union lk_hdr *lk = (union lk_hdr *)ptr; + + if (le32_to_cpu(lk->magic) == LK_PART_MAGIC) { + printf("Image Type: MediaTek LK Image\n"); + printf("Load Address: %08x\n", le32_to_cpu(lk->loadaddr)); + return; + } + + printf("Image Type: MediaTek BootROM Loadable Image\n"); + + if (!strcmp((char *)ptr, NAND_BOOT_NAME)) + mtk_image_verify_nand_header(ptr, 1); + else + mtk_image_verify_gen_header(ptr, 1); +} + +static void put_brom_layout_header(struct brom_layout_header *hdr, int type) +{ + strncpy(hdr->name, BRLYT_NAME, sizeof(hdr->name)); + hdr->version = cpu_to_le32(1); + hdr->magic = cpu_to_le32(BRLYT_MAGIC); + hdr->type = cpu_to_le32(type); +} + +static void put_ghf_common_header(struct gfh_common_header *gfh, int size, + int type, int ver) +{ + memcpy(gfh->magic, GFH_HEADER_MAGIC, sizeof(gfh->magic)); + gfh->version = ver; + gfh->size = cpu_to_le16(size); + gfh->type = cpu_to_le16(type); +} + +static void put_ghf_header(struct gfh_header *gfh, int file_size, + int dev_hdr_size, int load_addr, int flash_type) +{ + memset(gfh, 0, sizeof(struct gfh_header)); + + /* GFH_FILE_INFO header */ + put_ghf_common_header(&gfh->file_info.gfh, sizeof(gfh->file_info), + GFH_TYPE_FILE_INFO, 1); + strncpy(gfh->file_info.name, GFH_FILE_INFO_NAME, + sizeof(gfh->file_info.name)); + gfh->file_info.unused = cpu_to_le32(1); + gfh->file_info.file_type = cpu_to_le16(1); + gfh->file_info.flash_type = flash_type; + gfh->file_info.sig_type = GFH_SIG_TYPE_SHA256; + gfh->file_info.load_addr = cpu_to_le32(load_addr - sizeof(*gfh)); + gfh->file_info.total_size = cpu_to_le32(file_size - dev_hdr_size); + gfh->file_info.max_size = cpu_to_le32(file_size); + gfh->file_info.hdr_size = sizeof(*gfh); + gfh->file_info.sig_size = SHA256_SUM_LEN; + gfh->file_info.jump_offset = sizeof(*gfh); + gfh->file_info.processed = cpu_to_le32(1); + + /* GFH_BL_INFO header */ + put_ghf_common_header(&gfh->bl_info.gfh, sizeof(gfh->bl_info), + GFH_TYPE_BL_INFO, 1); + gfh->bl_info.attr = cpu_to_le32(1); + + /* GFH_BROM_CFG header */ + put_ghf_common_header(&gfh->brom_cfg.gfh, sizeof(gfh->brom_cfg), + GFH_TYPE_BROM_CFG, 3); + gfh->brom_cfg.cfg_bits = cpu_to_le32( + GFH_BROM_CFG_USBDL_AUTO_DETECT_DIS | + GFH_BROM_CFG_USBDL_BY_KCOL0_TIMEOUT_EN | + GFH_BROM_CFG_USBDL_BY_FLAG_TIMEOUT_EN); + gfh->brom_cfg.usbdl_by_kcol0_timeout_ms = cpu_to_le32(5000); + + /* GFH_BL_SEC_KEY header */ + put_ghf_common_header(&gfh->bl_sec_key.gfh, sizeof(gfh->bl_sec_key), + GFH_TYPE_BL_SEC_KEY, 1); + + /* GFH_ANTI_CLONE header */ + put_ghf_common_header(&gfh->anti_clone.gfh, sizeof(gfh->anti_clone), + GFH_TYPE_ANTI_CLONE, 1); + gfh->anti_clone.ac_offset = cpu_to_le32(0x10); + gfh->anti_clone.ac_len = cpu_to_le32(0x80); + + /* GFH_BROM_SEC_CFG header */ + put_ghf_common_header(&gfh->brom_sec_cfg.gfh, + sizeof(gfh->brom_sec_cfg), + GFH_TYPE_BROM_SEC_CFG, 1); + gfh->brom_sec_cfg.cfg_bits = + cpu_to_le32(BROM_SEC_CFG_JTAG_EN | BROM_SEC_CFG_UART_EN); +} + +static void put_hash(uint8_t *buff, int size) +{ + sha256_context ctx; + + sha256_starts(&ctx); + sha256_update(&ctx, buff, size); + sha256_finish(&ctx, buff + size); +} + +static void mtk_image_set_gen_header(void *ptr, off_t filesize, + uint32_t loadaddr) +{ + struct gen_device_header *hdr = (struct gen_device_header *)ptr; + struct gfh_header *gfh; + const char *bootname = NULL; + + if (hdr_media == BRLYT_TYPE_NOR) + bootname = SF_BOOT_NAME; + else if (hdr_media == BRLYT_TYPE_EMMC) + bootname = EMMC_BOOT_NAME; + else if (hdr_media == BRLYT_TYPE_SDMMC) + bootname = SDMMC_BOOT_NAME; + + /* Generic device header */ + snprintf(hdr->boot.name, sizeof(hdr->boot.name), "%s", bootname); + hdr->boot.version = cpu_to_le32(1); + hdr->boot.size = cpu_to_le32(sizeof(hdr->boot)); + + /* BRLYT header */ + put_brom_layout_header(&hdr->brlyt, hdr_media); + hdr->brlyt.header_size = cpu_to_le32(sizeof(struct gen_device_header)); + hdr->brlyt.total_size = cpu_to_le32(filesize); + hdr->brlyt.header_size_2 = hdr->brlyt.header_size; + hdr->brlyt.total_size_2 = hdr->brlyt.total_size; + + /* GFH header */ + gfh = (struct gfh_header *)(ptr + sizeof(struct gen_device_header)); + put_ghf_header(gfh, filesize, sizeof(struct gen_device_header), + loadaddr, GFH_FLASH_TYPE_GEN); + + /* Generate SHA256 hash */ + put_hash((uint8_t *)gfh, + filesize - sizeof(struct gen_device_header) - SHA256_SUM_LEN); +} + +static void mtk_image_set_nand_header(void *ptr, off_t filesize, + uint32_t loadaddr) +{ + union nand_boot_header *nh = (union nand_boot_header *)ptr; + struct brom_layout_header *brlyt; + struct gfh_header *gfh; + uint32_t payload_pages; + int i; + + /* NAND device header, repeat 4 times */ + for (i = 0; i < 4; i++) + memcpy(nh + i, hdr_nand, sizeof(union nand_boot_header)); + + /* BRLYT header */ + payload_pages = (filesize + le16_to_cpu(hdr_nand->pagesize) - 1) / + le16_to_cpu(hdr_nand->pagesize); + brlyt = (struct brom_layout_header *) + (ptr + le16_to_cpu(hdr_nand->pagesize)); + put_brom_layout_header(brlyt, hdr_media); + brlyt->header_size = cpu_to_le32(2); + brlyt->total_size = cpu_to_le32(payload_pages); + brlyt->header_size_2 = brlyt->header_size; + brlyt->total_size_2 = brlyt->total_size; + brlyt->unused = cpu_to_le32(1); + + /* GFH header */ + gfh = (struct gfh_header *)(ptr + 2 * le16_to_cpu(hdr_nand->pagesize)); + put_ghf_header(gfh, filesize, 2 * le16_to_cpu(hdr_nand->pagesize), + loadaddr, GFH_FLASH_TYPE_NAND); + + /* Generate SHA256 hash */ + put_hash((uint8_t *)gfh, + filesize - 2 * le16_to_cpu(hdr_nand->pagesize) - SHA256_SUM_LEN); +} + +static void mtk_image_set_header(void *ptr, struct stat *sbuf, int ifd, + struct image_tool_params *params) +{ + union lk_hdr *lk = (union lk_hdr *)ptr; + + if (use_lk_hdr) { + lk->magic = cpu_to_le32(LK_PART_MAGIC); + lk->size = cpu_to_le32(sbuf->st_size - sizeof(union lk_hdr)); + lk->loadaddr = cpu_to_le32(params->addr); + lk->mode = 0xffffffff; /* must be non-zero */ + memset(lk->name, 0, sizeof(lk->name)); + strncpy(lk->name, lk_name, sizeof(lk->name)); + return; + } + + if (hdr_media == BRLYT_TYPE_NAND || hdr_media == BRLYT_TYPE_SNAND) + mtk_image_set_nand_header(ptr, sbuf->st_size, params->addr); + else + mtk_image_set_gen_header(ptr, sbuf->st_size, params->addr); +} + +U_BOOT_IMAGE_TYPE( + mtk_image, + "MediaTek BootROM Loadable Image support", + 0, + NULL, + mtk_image_check_params, + mtk_image_verify_header, + mtk_image_print_header, + mtk_image_set_header, + NULL, + mtk_image_check_image_types, + NULL, + mtk_image_vrec_header +); diff --git a/tools/u-boot-tools/mtk_image.h b/tools/u-boot-tools/mtk_image.h new file mode 100644 index 0000000..0a9eab3 --- /dev/null +++ b/tools/u-boot-tools/mtk_image.h @@ -0,0 +1,199 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * MediaTek BootROM header definitions + * + * Copyright (C) 2018 MediaTek Inc. + * Author: Weijie Gao <weijie.gao@mediatek.com> + */ + +#ifndef _MTK_IMAGE_H +#define _MTK_IMAGE_H + +/* Device header definitions */ + +/* Header for NOR/SD/eMMC */ +union gen_boot_header { + struct { + char name[12]; + __le32 version; + __le32 size; + }; + + uint8_t pad[0x200]; +}; + +#define EMMC_BOOT_NAME "EMMC_BOOT" +#define SF_BOOT_NAME "SF_BOOT" +#define SDMMC_BOOT_NAME "SDMMC_BOOT" + +/* Header for NAND */ +union nand_boot_header { + struct { + char name[12]; + char version[4]; + char id[8]; + __le16 ioif; + __le16 pagesize; + __le16 addrcycles; + __le16 oobsize; + __le16 pages_of_block; + __le16 numblocks; + __le16 writesize_shift; + __le16 erasesize_shift; + uint8_t dummy[60]; + uint8_t ecc_parity[28]; + }; + + uint8_t data[0x80]; +}; + +#define NAND_BOOT_NAME "BOOTLOADER!" +#define NAND_BOOT_VERSION "V006" +#define NAND_BOOT_ID "NFIINFO" + +/* BootROM layout header */ +struct brom_layout_header { + char name[8]; + __le32 version; + __le32 header_size; + __le32 total_size; + __le32 magic; + __le32 type; + __le32 header_size_2; + __le32 total_size_2; + __le32 unused; +}; + +#define BRLYT_NAME "BRLYT" +#define BRLYT_MAGIC 0x42424242 + +enum brlyt_img_type { + BRLYT_TYPE_INVALID = 0, + BRLYT_TYPE_NAND = 0x10002, + BRLYT_TYPE_EMMC = 0x10005, + BRLYT_TYPE_NOR = 0x10007, + BRLYT_TYPE_SDMMC = 0x10008, + BRLYT_TYPE_SNAND = 0x10009 +}; + +/* Combined device header for NOR/SD/eMMC */ +struct gen_device_header { + union gen_boot_header boot; + + union { + struct brom_layout_header brlyt; + uint8_t brlyt_pad[0x400]; + }; +}; + +/* BootROM header definitions */ +struct gfh_common_header { + uint8_t magic[3]; + uint8_t version; + __le16 size; + __le16 type; +}; + +#define GFH_HEADER_MAGIC "MMM" + +#define GFH_TYPE_FILE_INFO 0 +#define GFH_TYPE_BL_INFO 1 +#define GFH_TYPE_BROM_CFG 7 +#define GFH_TYPE_BL_SEC_KEY 3 +#define GFH_TYPE_ANTI_CLONE 2 +#define GFH_TYPE_BROM_SEC_CFG 8 + +struct gfh_file_info { + struct gfh_common_header gfh; + char name[12]; + __le32 unused; + __le16 file_type; + uint8_t flash_type; + uint8_t sig_type; + __le32 load_addr; + __le32 total_size; + __le32 max_size; + __le32 hdr_size; + __le32 sig_size; + __le32 jump_offset; + __le32 processed; +}; + +#define GFH_FILE_INFO_NAME "FILE_INFO" + +#define GFH_FLASH_TYPE_GEN 5 +#define GFH_FLASH_TYPE_NAND 2 + +#define GFH_SIG_TYPE_NONE 0 +#define GFH_SIG_TYPE_SHA256 1 + +struct gfh_bl_info { + struct gfh_common_header gfh; + __le32 attr; +}; + +struct gfh_brom_cfg { + struct gfh_common_header gfh; + __le32 cfg_bits; + __le32 usbdl_by_auto_detect_timeout_ms; + uint8_t unused[0x48]; + __le32 usbdl_by_kcol0_timeout_ms; + __le32 usbdl_by_flag_timeout_ms; + uint32_t pad; +}; + +#define GFH_BROM_CFG_USBDL_BY_AUTO_DETECT_TIMEOUT_EN 0x02 +#define GFH_BROM_CFG_USBDL_AUTO_DETECT_DIS 0x10 +#define GFH_BROM_CFG_USBDL_BY_KCOL0_TIMEOUT_EN 0x80 +#define GFH_BROM_CFG_USBDL_BY_FLAG_TIMEOUT_EN 0x100 + +struct gfh_bl_sec_key { + struct gfh_common_header gfh; + uint8_t pad[0x20c]; +}; + +struct gfh_anti_clone { + struct gfh_common_header gfh; + uint8_t ac_b2k; + uint8_t ac_b2c; + uint16_t pad; + __le32 ac_offset; + __le32 ac_len; +}; + +struct gfh_brom_sec_cfg { + struct gfh_common_header gfh; + __le32 cfg_bits; + char customer_name[0x20]; + __le32 pad; +}; + +#define BROM_SEC_CFG_JTAG_EN 1 +#define BROM_SEC_CFG_UART_EN 2 + +struct gfh_header { + struct gfh_file_info file_info; + struct gfh_bl_info bl_info; + struct gfh_brom_cfg brom_cfg; + struct gfh_bl_sec_key bl_sec_key; + struct gfh_anti_clone anti_clone; + struct gfh_brom_sec_cfg brom_sec_cfg; +}; + +/* LK image header */ + +union lk_hdr { + struct { + __le32 magic; + __le32 size; + char name[32]; + __le32 loadaddr; + __le32 mode; + }; + + uint8_t data[512]; +}; + +#define LK_PART_MAGIC 0x58881688 + +#endif /* _MTK_IMAGE_H */ diff --git a/tools/u-boot-tools/mtk_image.o b/tools/u-boot-tools/mtk_image.o new file mode 100644 index 0000000000000000000000000000000000000000..8fa227658411f02bbb42a3cbe2fdbcf9a6fb17a5 GIT binary patch literal 14752 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 literal 0 HcmV?d00001 diff --git a/tools/u-boot-tools/mxsboot.c b/tools/u-boot-tools/mxsboot.c new file mode 100644 index 0000000..04d86f8 --- /dev/null +++ b/tools/u-boot-tools/mxsboot.c @@ -0,0 +1,675 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Freescale i.MX28 image generator + * + * Copyright (C) 2011 Marek Vasut <marek.vasut@gmail.com> + * on behalf of DENX Software Engineering GmbH + */ + +#include <fcntl.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <unistd.h> + +#include "compiler.h" + +/* Taken from <linux/kernel.h> */ +#define __round_mask(x, y) ((__typeof__(x))((y)-1)) +#define round_down(x, y) ((x) & ~__round_mask(x, y)) + +/* + * Default BCB layout. + * + * TWEAK this if you have blown any OCOTP fuses. + */ +#define STRIDE_PAGES 64 +#define STRIDE_COUNT 4 + +/* + * Layout for 256Mb big NAND with 2048b page size, 64b OOB size and + * 128kb erase size. + * + * TWEAK this if you have different kind of NAND chip. + */ +static uint32_t nand_writesize = 2048; +static uint32_t nand_oobsize = 64; +static uint32_t nand_erasesize = 128 * 1024; + +/* + * Sector on which the SigmaTel boot partition (0x53) starts. + */ +static uint32_t sd_sector = 2048; + +/* + * Each of the U-Boot bootstreams is at maximum 1MB big. + * + * TWEAK this if, for some wild reason, you need to boot bigger image. + */ +#define MAX_BOOTSTREAM_SIZE (1 * 1024 * 1024) + +/* i.MX28 NAND controller-specific constants. DO NOT TWEAK! */ +#define MXS_NAND_DMA_DESCRIPTOR_COUNT 4 +#define MXS_NAND_CHUNK_DATA_CHUNK_SIZE 512 +#define MXS_NAND_METADATA_SIZE 10 +#define MXS_NAND_BITS_PER_ECC_LEVEL 13 +#define MXS_NAND_COMMAND_BUFFER_SIZE 32 + +struct mx28_nand_fcb { + uint32_t checksum; + uint32_t fingerprint; + uint32_t version; + struct { + uint8_t data_setup; + uint8_t data_hold; + uint8_t address_setup; + uint8_t dsample_time; + uint8_t nand_timing_state; + uint8_t rea; + uint8_t rloh; + uint8_t rhoh; + } timing; + uint32_t page_data_size; + uint32_t total_page_size; + uint32_t sectors_per_block; + uint32_t number_of_nands; /* Ignored */ + uint32_t total_internal_die; /* Ignored */ + uint32_t cell_type; /* Ignored */ + uint32_t ecc_block_n_ecc_type; + uint32_t ecc_block_0_size; + uint32_t ecc_block_n_size; + uint32_t ecc_block_0_ecc_type; + uint32_t metadata_bytes; + uint32_t num_ecc_blocks_per_page; + uint32_t ecc_block_n_ecc_level_sdk; /* Ignored */ + uint32_t ecc_block_0_size_sdk; /* Ignored */ + uint32_t ecc_block_n_size_sdk; /* Ignored */ + uint32_t ecc_block_0_ecc_level_sdk; /* Ignored */ + uint32_t num_ecc_blocks_per_page_sdk; /* Ignored */ + uint32_t metadata_bytes_sdk; /* Ignored */ + uint32_t erase_threshold; + uint32_t boot_patch; + uint32_t patch_sectors; + uint32_t firmware1_starting_sector; + uint32_t firmware2_starting_sector; + uint32_t sectors_in_firmware1; + uint32_t sectors_in_firmware2; + uint32_t dbbt_search_area_start_address; + uint32_t badblock_marker_byte; + uint32_t badblock_marker_start_bit; + uint32_t bb_marker_physical_offset; +}; + +struct mx28_nand_dbbt { + uint32_t checksum; + uint32_t fingerprint; + uint32_t version; + uint32_t number_bb; + uint32_t number_2k_pages_bb; +}; + +struct mx28_nand_bbt { + uint32_t nand; + uint32_t number_bb; + uint32_t badblock[510]; +}; + +struct mx28_sd_drive_info { + uint32_t chip_num; + uint32_t drive_type; + uint32_t tag; + uint32_t first_sector_number; + uint32_t sector_count; +}; + +struct mx28_sd_config_block { + uint32_t signature; + uint32_t primary_boot_tag; + uint32_t secondary_boot_tag; + uint32_t num_copies; + struct mx28_sd_drive_info drv_info[1]; +}; + +static inline uint32_t mx28_nand_ecc_chunk_cnt(uint32_t page_data_size) +{ + return page_data_size / MXS_NAND_CHUNK_DATA_CHUNK_SIZE; +} + +static inline uint32_t mx28_nand_ecc_size_in_bits(uint32_t ecc_strength) +{ + return ecc_strength * MXS_NAND_BITS_PER_ECC_LEVEL; +} + +static inline uint32_t mx28_nand_get_ecc_strength(uint32_t page_data_size, + uint32_t page_oob_size) +{ + int ecc_strength; + + /* + * Determine the ECC layout with the formula: + * ECC bits per chunk = (total page spare data bits) / + * (bits per ECC level) / (chunks per page) + * where: + * total page spare data bits = + * (page oob size - meta data size) * (bits per byte) + */ + ecc_strength = ((page_oob_size - MXS_NAND_METADATA_SIZE) * 8) + / (MXS_NAND_BITS_PER_ECC_LEVEL * + mx28_nand_ecc_chunk_cnt(page_data_size)); + + return round_down(ecc_strength, 2); +} + +static inline uint32_t mx28_nand_get_mark_offset(uint32_t page_data_size, + uint32_t ecc_strength) +{ + uint32_t chunk_data_size_in_bits; + uint32_t chunk_ecc_size_in_bits; + uint32_t chunk_total_size_in_bits; + uint32_t block_mark_chunk_number; + uint32_t block_mark_chunk_bit_offset; + uint32_t block_mark_bit_offset; + + chunk_data_size_in_bits = MXS_NAND_CHUNK_DATA_CHUNK_SIZE * 8; + chunk_ecc_size_in_bits = mx28_nand_ecc_size_in_bits(ecc_strength); + + chunk_total_size_in_bits = + chunk_data_size_in_bits + chunk_ecc_size_in_bits; + + /* Compute the bit offset of the block mark within the physical page. */ + block_mark_bit_offset = page_data_size * 8; + + /* Subtract the metadata bits. */ + block_mark_bit_offset -= MXS_NAND_METADATA_SIZE * 8; + + /* + * Compute the chunk number (starting at zero) in which the block mark + * appears. + */ + block_mark_chunk_number = + block_mark_bit_offset / chunk_total_size_in_bits; + + /* + * Compute the bit offset of the block mark within its chunk, and + * validate it. + */ + block_mark_chunk_bit_offset = block_mark_bit_offset - + (block_mark_chunk_number * chunk_total_size_in_bits); + + if (block_mark_chunk_bit_offset > chunk_data_size_in_bits) + return 1; + + /* + * Now that we know the chunk number in which the block mark appears, + * we can subtract all the ECC bits that appear before it. + */ + block_mark_bit_offset -= + block_mark_chunk_number * chunk_ecc_size_in_bits; + + return block_mark_bit_offset; +} + +static inline uint32_t mx28_nand_mark_byte_offset(void) +{ + uint32_t ecc_strength; + ecc_strength = mx28_nand_get_ecc_strength(nand_writesize, nand_oobsize); + return mx28_nand_get_mark_offset(nand_writesize, ecc_strength) >> 3; +} + +static inline uint32_t mx28_nand_mark_bit_offset(void) +{ + uint32_t ecc_strength; + ecc_strength = mx28_nand_get_ecc_strength(nand_writesize, nand_oobsize); + return mx28_nand_get_mark_offset(nand_writesize, ecc_strength) & 0x7; +} + +static uint32_t mx28_nand_block_csum(uint8_t *block, uint32_t size) +{ + uint32_t csum = 0; + int i; + + for (i = 0; i < size; i++) + csum += block[i]; + + return csum ^ 0xffffffff; +} + +static struct mx28_nand_fcb *mx28_nand_get_fcb(uint32_t size) +{ + struct mx28_nand_fcb *fcb; + uint32_t bcb_size_bytes; + uint32_t stride_size_bytes; + uint32_t bootstream_size_pages; + uint32_t fw1_start_page; + uint32_t fw2_start_page; + + fcb = malloc(nand_writesize); + if (!fcb) { + printf("MX28 NAND: Unable to allocate FCB\n"); + return NULL; + } + + memset(fcb, 0, nand_writesize); + + fcb->fingerprint = 0x20424346; + fcb->version = 0x01000000; + + /* + * FIXME: These here are default values as found in kobs-ng. We should + * probably retrieve the data from NAND or something. + */ + fcb->timing.data_setup = 80; + fcb->timing.data_hold = 60; + fcb->timing.address_setup = 25; + fcb->timing.dsample_time = 6; + + fcb->page_data_size = nand_writesize; + fcb->total_page_size = nand_writesize + nand_oobsize; + fcb->sectors_per_block = nand_erasesize / nand_writesize; + + fcb->num_ecc_blocks_per_page = (nand_writesize / 512) - 1; + fcb->ecc_block_0_size = 512; + fcb->ecc_block_n_size = 512; + fcb->metadata_bytes = 10; + fcb->ecc_block_n_ecc_type = mx28_nand_get_ecc_strength( + nand_writesize, nand_oobsize) >> 1; + fcb->ecc_block_0_ecc_type = mx28_nand_get_ecc_strength( + nand_writesize, nand_oobsize) >> 1; + if (fcb->ecc_block_n_ecc_type == 0) { + printf("MX28 NAND: Unsupported NAND geometry\n"); + goto err; + } + + fcb->boot_patch = 0; + fcb->patch_sectors = 0; + + fcb->badblock_marker_byte = mx28_nand_mark_byte_offset(); + fcb->badblock_marker_start_bit = mx28_nand_mark_bit_offset(); + fcb->bb_marker_physical_offset = nand_writesize; + + stride_size_bytes = STRIDE_PAGES * nand_writesize; + bcb_size_bytes = stride_size_bytes * STRIDE_COUNT; + + bootstream_size_pages = (size + (nand_writesize - 1)) / + nand_writesize; + + fw1_start_page = 2 * bcb_size_bytes / nand_writesize; + fw2_start_page = (2 * bcb_size_bytes + MAX_BOOTSTREAM_SIZE) / + nand_writesize; + + fcb->firmware1_starting_sector = fw1_start_page; + fcb->firmware2_starting_sector = fw2_start_page; + fcb->sectors_in_firmware1 = bootstream_size_pages; + fcb->sectors_in_firmware2 = bootstream_size_pages; + + fcb->dbbt_search_area_start_address = STRIDE_PAGES * STRIDE_COUNT; + + return fcb; + +err: + free(fcb); + return NULL; +} + +static struct mx28_nand_dbbt *mx28_nand_get_dbbt(void) +{ + struct mx28_nand_dbbt *dbbt; + + dbbt = malloc(nand_writesize); + if (!dbbt) { + printf("MX28 NAND: Unable to allocate DBBT\n"); + return NULL; + } + + memset(dbbt, 0, nand_writesize); + + dbbt->fingerprint = 0x54424244; + dbbt->version = 0x1; + + return dbbt; +} + +static inline uint8_t mx28_nand_parity_13_8(const uint8_t b) +{ + uint32_t parity = 0, tmp; + + tmp = ((b >> 6) ^ (b >> 5) ^ (b >> 3) ^ (b >> 2)) & 1; + parity |= tmp << 0; + + tmp = ((b >> 7) ^ (b >> 5) ^ (b >> 4) ^ (b >> 2) ^ (b >> 1)) & 1; + parity |= tmp << 1; + + tmp = ((b >> 7) ^ (b >> 6) ^ (b >> 5) ^ (b >> 1) ^ (b >> 0)) & 1; + parity |= tmp << 2; + + tmp = ((b >> 7) ^ (b >> 4) ^ (b >> 3) ^ (b >> 0)) & 1; + parity |= tmp << 3; + + tmp = ((b >> 6) ^ (b >> 4) ^ (b >> 3) ^ + (b >> 2) ^ (b >> 1) ^ (b >> 0)) & 1; + parity |= tmp << 4; + + return parity; +} + +static uint8_t *mx28_nand_fcb_block(struct mx28_nand_fcb *fcb) +{ + uint8_t *block; + uint8_t *ecc; + int i; + + block = malloc(nand_writesize + nand_oobsize); + if (!block) { + printf("MX28 NAND: Unable to allocate FCB block\n"); + return NULL; + } + + memset(block, 0, nand_writesize + nand_oobsize); + + /* Update the FCB checksum */ + fcb->checksum = mx28_nand_block_csum(((uint8_t *)fcb) + 4, 508); + + /* Figure 12-11. in iMX28RM, rev. 1, says FCB is at offset 12 */ + memcpy(block + 12, fcb, sizeof(struct mx28_nand_fcb)); + + /* ECC is at offset 12 + 512 */ + ecc = block + 12 + 512; + + /* Compute the ECC parity */ + for (i = 0; i < sizeof(struct mx28_nand_fcb); i++) + ecc[i] = mx28_nand_parity_13_8(block[i + 12]); + + return block; +} + +static int mx28_nand_write_fcb(struct mx28_nand_fcb *fcb, uint8_t *buf) +{ + uint32_t offset; + uint8_t *fcbblock; + int ret = 0; + int i; + + fcbblock = mx28_nand_fcb_block(fcb); + if (!fcbblock) + return -1; + + for (i = 0; i < STRIDE_PAGES * STRIDE_COUNT; i += STRIDE_PAGES) { + offset = i * nand_writesize; + memcpy(buf + offset, fcbblock, nand_writesize + nand_oobsize); + /* Mark the NAND page is OK. */ + buf[offset + nand_writesize] = 0xff; + } + + free(fcbblock); + return ret; +} + +static int mx28_nand_write_dbbt(struct mx28_nand_dbbt *dbbt, uint8_t *buf) +{ + uint32_t offset; + int i = STRIDE_PAGES * STRIDE_COUNT; + + for (; i < 2 * STRIDE_PAGES * STRIDE_COUNT; i += STRIDE_PAGES) { + offset = i * nand_writesize; + memcpy(buf + offset, dbbt, sizeof(struct mx28_nand_dbbt)); + } + + return 0; +} + +static int mx28_nand_write_firmware(struct mx28_nand_fcb *fcb, int infd, + uint8_t *buf) +{ + int ret; + off_t size; + uint32_t offset1, offset2; + + size = lseek(infd, 0, SEEK_END); + lseek(infd, 0, SEEK_SET); + + offset1 = fcb->firmware1_starting_sector * nand_writesize; + offset2 = fcb->firmware2_starting_sector * nand_writesize; + + ret = read(infd, buf + offset1, size); + if (ret != size) + return -1; + + memcpy(buf + offset2, buf + offset1, size); + + return 0; +} + +static void usage(void) +{ + printf( + "Usage: mxsboot [ops] <type> <infile> <outfile>\n" + "Augment BootStream file with a proper header for i.MX28 boot\n" + "\n" + " <type> type of image:\n" + " \"nand\" for NAND image\n" + " \"sd\" for SD image\n" + " <infile> input file, the u-boot.sb bootstream\n" + " <outfile> output file, the bootable image\n" + "\n"); + printf( + "For NAND boot, these options are accepted:\n" + " -w <size> NAND page size\n" + " -o <size> NAND OOB size\n" + " -e <size> NAND erase size\n" + "\n" + "For SD boot, these options are accepted:\n" + " -p <sector> Sector where the SGTL partition starts\n" + ); +} + +static int mx28_create_nand_image(int infd, int outfd) +{ + struct mx28_nand_fcb *fcb; + struct mx28_nand_dbbt *dbbt; + int ret = -1; + uint8_t *buf; + int size; + ssize_t wr_size; + + size = nand_writesize * 512 + 2 * MAX_BOOTSTREAM_SIZE; + + buf = malloc(size); + if (!buf) { + printf("Can not allocate output buffer of %d bytes\n", size); + goto err0; + } + + memset(buf, 0, size); + + fcb = mx28_nand_get_fcb(MAX_BOOTSTREAM_SIZE); + if (!fcb) { + printf("Unable to compile FCB\n"); + goto err1; + } + + dbbt = mx28_nand_get_dbbt(); + if (!dbbt) { + printf("Unable to compile DBBT\n"); + goto err2; + } + + ret = mx28_nand_write_fcb(fcb, buf); + if (ret) { + printf("Unable to write FCB to buffer\n"); + goto err3; + } + + ret = mx28_nand_write_dbbt(dbbt, buf); + if (ret) { + printf("Unable to write DBBT to buffer\n"); + goto err3; + } + + ret = mx28_nand_write_firmware(fcb, infd, buf); + if (ret) { + printf("Unable to write firmware to buffer\n"); + goto err3; + } + + wr_size = write(outfd, buf, size); + if (wr_size != size) { + ret = -1; + goto err3; + } + + ret = 0; + +err3: + free(dbbt); +err2: + free(fcb); +err1: + free(buf); +err0: + return ret; +} + +static int mx28_create_sd_image(int infd, int outfd) +{ + int ret = -1; + uint32_t *buf; + int size; + off_t fsize; + ssize_t wr_size; + struct mx28_sd_config_block *cb; + + fsize = lseek(infd, 0, SEEK_END); + lseek(infd, 0, SEEK_SET); + size = fsize + 4 * 512; + + buf = malloc(size); + if (!buf) { + printf("Can not allocate output buffer of %d bytes\n", size); + goto err0; + } + + ret = read(infd, (uint8_t *)buf + 4 * 512, fsize); + if (ret != fsize) { + ret = -1; + goto err1; + } + + cb = (struct mx28_sd_config_block *)buf; + + cb->signature = cpu_to_le32(0x00112233); + cb->primary_boot_tag = cpu_to_le32(0x1); + cb->secondary_boot_tag = cpu_to_le32(0x1); + cb->num_copies = cpu_to_le32(1); + cb->drv_info[0].chip_num = cpu_to_le32(0x0); + cb->drv_info[0].drive_type = cpu_to_le32(0x0); + cb->drv_info[0].tag = cpu_to_le32(0x1); + cb->drv_info[0].first_sector_number = cpu_to_le32(sd_sector + 4); + cb->drv_info[0].sector_count = cpu_to_le32((size - 4) / 512); + + wr_size = write(outfd, buf, size); + if (wr_size != size) { + ret = -1; + goto err1; + } + + ret = 0; + +err1: + free(buf); +err0: + return ret; +} + +static int parse_ops(int argc, char **argv) +{ + int i; + int tmp; + char *end; + enum param { + PARAM_WRITE, + PARAM_OOB, + PARAM_ERASE, + PARAM_PART, + PARAM_SD, + PARAM_NAND + }; + int type; + + if (argc < 4) + return -1; + + for (i = 1; i < argc; i++) { + if (!strncmp(argv[i], "-w", 2)) + type = PARAM_WRITE; + else if (!strncmp(argv[i], "-o", 2)) + type = PARAM_OOB; + else if (!strncmp(argv[i], "-e", 2)) + type = PARAM_ERASE; + else if (!strncmp(argv[i], "-p", 2)) + type = PARAM_PART; + else /* SD/MMC */ + break; + + tmp = strtol(argv[++i], &end, 10); + if (tmp % 2) + return -1; + if (tmp <= 0) + return -1; + + if (type == PARAM_WRITE) + nand_writesize = tmp; + if (type == PARAM_OOB) + nand_oobsize = tmp; + if (type == PARAM_ERASE) + nand_erasesize = tmp; + if (type == PARAM_PART) + sd_sector = tmp; + } + + if (strcmp(argv[i], "sd") && strcmp(argv[i], "nand")) + return -1; + + if (i + 3 != argc) + return -1; + + return i; +} + +int main(int argc, char **argv) +{ + int infd, outfd; + int ret = 0; + int offset; + + offset = parse_ops(argc, argv); + if (offset < 0) { + usage(); + ret = 1; + goto err1; + } + + infd = open(argv[offset + 1], O_RDONLY); + if (infd < 0) { + printf("Input BootStream file can not be opened\n"); + ret = 2; + goto err1; + } + + outfd = open(argv[offset + 2], O_CREAT | O_TRUNC | O_WRONLY, + S_IRUSR | S_IWUSR); + if (outfd < 0) { + printf("Output file can not be created\n"); + ret = 3; + goto err2; + } + + if (!strcmp(argv[offset], "sd")) + ret = mx28_create_sd_image(infd, outfd); + else if (!strcmp(argv[offset], "nand")) + ret = mx28_create_nand_image(infd, outfd); + + close(outfd); +err2: + close(infd); +err1: + return ret; +} diff --git a/tools/u-boot-tools/mxsimage.c b/tools/u-boot-tools/mxsimage.c new file mode 100644 index 0000000..0bb5c6a --- /dev/null +++ b/tools/u-boot-tools/mxsimage.c @@ -0,0 +1,2365 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Freescale i.MX23/i.MX28 SB image generator + * + * Copyright (C) 2012-2013 Marek Vasut <marex@denx.de> + */ + +#ifdef CONFIG_MXS + +#include <errno.h> +#include <fcntl.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include <limits.h> + +#include <openssl/evp.h> + +#include "imagetool.h" +#include "mxsimage.h" +#include "pbl_crc32.h" +#include <image.h> + +/* + * OpenSSL 1.1.0 and newer compatibility functions: + * https://wiki.openssl.org/index.php/1.1_API_Changes + */ +#if OPENSSL_VERSION_NUMBER < 0x10100000L || \ + (defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x2070000fL) +static void *OPENSSL_zalloc(size_t num) +{ + void *ret = OPENSSL_malloc(num); + + if (ret != NULL) + memset(ret, 0, num); + return ret; +} + +EVP_MD_CTX *EVP_MD_CTX_new(void) +{ + return OPENSSL_zalloc(sizeof(EVP_MD_CTX)); +} + +void EVP_MD_CTX_free(EVP_MD_CTX *ctx) +{ + EVP_MD_CTX_cleanup(ctx); + OPENSSL_free(ctx); +} + +int EVP_CIPHER_CTX_reset(EVP_CIPHER_CTX *ctx) +{ + return EVP_CIPHER_CTX_cleanup(ctx); +} +#endif + +/* + * DCD block + * |-Write to address command block + * | 0xf00 == 0xf33d + * | 0xba2 == 0xb33f + * |-ORR address with mask command block + * | 0xf00 |= 0x1337 + * |-Write to address command block + * | 0xba2 == 0xd00d + * : + */ +#define SB_HAB_DCD_WRITE 0xccUL +#define SB_HAB_DCD_CHECK 0xcfUL +#define SB_HAB_DCD_NOOP 0xc0UL +#define SB_HAB_DCD_MASK_BIT (1 << 3) +#define SB_HAB_DCD_SET_BIT (1 << 4) + +/* Addr.n = Value.n */ +#define SB_DCD_WRITE \ + (SB_HAB_DCD_WRITE << 24) +/* Addr.n &= ~Value.n */ +#define SB_DCD_ANDC \ + ((SB_HAB_DCD_WRITE << 24) | SB_HAB_DCD_SET_BIT) +/* Addr.n |= Value.n */ +#define SB_DCD_ORR \ + ((SB_HAB_DCD_WRITE << 24) | SB_HAB_DCD_SET_BIT | SB_HAB_DCD_MASK_BIT) +/* (Addr.n & Value.n) == 0 */ +#define SB_DCD_CHK_EQZ \ + (SB_HAB_DCD_CHECK << 24) +/* (Addr.n & Value.n) == Value.n */ +#define SB_DCD_CHK_EQ \ + ((SB_HAB_DCD_CHECK << 24) | SB_HAB_DCD_SET_BIT) +/* (Addr.n & Value.n) != Value.n */ +#define SB_DCD_CHK_NEQ \ + ((SB_HAB_DCD_CHECK << 24) | SB_HAB_DCD_MASK_BIT) +/* (Addr.n & Value.n) != 0 */ +#define SB_DCD_CHK_NEZ \ + ((SB_HAB_DCD_CHECK << 24) | SB_HAB_DCD_SET_BIT | SB_HAB_DCD_MASK_BIT) +/* NOP */ +#define SB_DCD_NOOP \ + (SB_HAB_DCD_NOOP << 24) + +struct sb_dcd_ctx { + struct sb_dcd_ctx *dcd; + + uint32_t id; + + /* The DCD block. */ + uint32_t *payload; + /* Size of the whole DCD block. */ + uint32_t size; + + /* Pointer to previous DCD command block. */ + uint32_t *prev_dcd_head; +}; + +/* + * IMAGE + * |-SECTION + * | |-CMD + * | |-CMD + * | `-CMD + * |-SECTION + * | |-CMD + * : : + */ +struct sb_cmd_list { + char *cmd; + size_t len; + unsigned int lineno; +}; + +struct sb_cmd_ctx { + uint32_t size; + + struct sb_cmd_ctx *cmd; + + uint8_t *data; + uint32_t length; + + struct sb_command payload; + struct sb_command c_payload; +}; + +struct sb_section_ctx { + uint32_t size; + + /* Section flags */ + unsigned int boot:1; + + struct sb_section_ctx *sect; + + struct sb_cmd_ctx *cmd_head; + struct sb_cmd_ctx *cmd_tail; + + struct sb_sections_header payload; +}; + +struct sb_image_ctx { + unsigned int in_section:1; + unsigned int in_dcd:1; + /* Image configuration */ + unsigned int display_progress:1; + unsigned int silent_dump:1; + char *input_filename; + char *output_filename; + char *cfg_filename; + uint8_t image_key[16]; + + /* Number of section in the image */ + unsigned int sect_count; + /* Bootable section */ + unsigned int sect_boot; + unsigned int sect_boot_found:1; + + struct sb_section_ctx *sect_head; + struct sb_section_ctx *sect_tail; + + struct sb_dcd_ctx *dcd_head; + struct sb_dcd_ctx *dcd_tail; + + EVP_CIPHER_CTX *cipher_ctx; + EVP_MD_CTX *md_ctx; + uint8_t digest[32]; + struct sb_key_dictionary_key sb_dict_key; + + struct sb_boot_image_header payload; +}; + +/* + * Instruction semantics: + * NOOP + * TAG [LAST] + * LOAD address file + * LOAD IVT address IVT_entry_point + * FILL address pattern length + * JUMP [HAB] address [r0_arg] + * CALL [HAB] address [r0_arg] + * MODE mode + * For i.MX23, mode = USB/I2C/SPI1_FLASH/SPI2_FLASH/NAND_BCH + * JTAG/SPI3_EEPROM/SD_SSP0/SD_SSP1 + * For i.MX28, mode = USB/I2C/SPI2_FLASH/SPI3_FLASH/NAND_BCH + * JTAG/SPI2_EEPROM/SD_SSP0/SD_SSP1 + */ + +/* + * AES libcrypto + */ +static int sb_aes_init(struct sb_image_ctx *ictx, uint8_t *iv, int enc) +{ + EVP_CIPHER_CTX *ctx; + int ret; + + /* If there is no init vector, init vector is all zeroes. */ + if (!iv) + iv = ictx->image_key; + + ctx = EVP_CIPHER_CTX_new(); + ret = EVP_CipherInit(ctx, EVP_aes_128_cbc(), ictx->image_key, iv, enc); + if (ret == 1) { + EVP_CIPHER_CTX_set_padding(ctx, 0); + ictx->cipher_ctx = ctx; + } + return ret; +} + +static int sb_aes_crypt(struct sb_image_ctx *ictx, uint8_t *in_data, + uint8_t *out_data, int in_len) +{ + EVP_CIPHER_CTX *ctx = ictx->cipher_ctx; + int ret, outlen; + uint8_t *outbuf; + + outbuf = malloc(in_len); + if (!outbuf) + return -ENOMEM; + memset(outbuf, 0, sizeof(in_len)); + + ret = EVP_CipherUpdate(ctx, outbuf, &outlen, in_data, in_len); + if (!ret) { + ret = -EINVAL; + goto err; + } + + if (out_data) + memcpy(out_data, outbuf, outlen); + +err: + free(outbuf); + return ret; +} + +static int sb_aes_deinit(EVP_CIPHER_CTX *ctx) +{ + return EVP_CIPHER_CTX_reset(ctx); +} + +static int sb_aes_reinit(struct sb_image_ctx *ictx, int enc) +{ + int ret; + EVP_CIPHER_CTX *ctx = ictx->cipher_ctx; + struct sb_boot_image_header *sb_header = &ictx->payload; + uint8_t *iv = sb_header->iv; + + ret = sb_aes_deinit(ctx); + if (!ret) + return ret; + return sb_aes_init(ictx, iv, enc); +} + +/* + * Debug + */ +static void soprintf(struct sb_image_ctx *ictx, const char *fmt, ...) +{ + va_list ap; + + if (ictx->silent_dump) + return; + + va_start(ap, fmt); + vfprintf(stdout, fmt, ap); + va_end(ap); +} + +/* + * Code + */ +static time_t sb_get_timestamp(void) +{ + struct tm time_2000 = { + .tm_yday = 1, /* Jan. 1st */ + .tm_year = 100, /* 2000 */ + }; + time_t seconds_to_2000 = mktime(&time_2000); + time_t seconds_to_now = time(NULL); + + return seconds_to_now - seconds_to_2000; +} + +static int sb_get_time(time_t time, struct tm *tm) +{ + struct tm time_2000 = { + .tm_yday = 1, /* Jan. 1st */ + .tm_year = 0, /* 1900 */ + }; + const time_t seconds_to_2000 = mktime(&time_2000); + const time_t seconds_to_now = seconds_to_2000 + time; + struct tm *ret; + ret = gmtime_r(&seconds_to_now, tm); + return ret ? 0 : -EINVAL; +} + +static void sb_encrypt_sb_header(struct sb_image_ctx *ictx) +{ + EVP_MD_CTX *md_ctx = ictx->md_ctx; + struct sb_boot_image_header *sb_header = &ictx->payload; + uint8_t *sb_header_ptr = (uint8_t *)sb_header; + + /* Encrypt the header, compute the digest. */ + sb_aes_crypt(ictx, sb_header_ptr, NULL, sizeof(*sb_header)); + EVP_DigestUpdate(md_ctx, sb_header_ptr, sizeof(*sb_header)); +} + +static void sb_encrypt_sb_sections_header(struct sb_image_ctx *ictx) +{ + EVP_MD_CTX *md_ctx = ictx->md_ctx; + struct sb_section_ctx *sctx = ictx->sect_head; + struct sb_sections_header *shdr; + uint8_t *sb_sections_header_ptr; + const int size = sizeof(*shdr); + + while (sctx) { + shdr = &sctx->payload; + sb_sections_header_ptr = (uint8_t *)shdr; + + sb_aes_crypt(ictx, sb_sections_header_ptr, + ictx->sb_dict_key.cbc_mac, size); + EVP_DigestUpdate(md_ctx, sb_sections_header_ptr, size); + + sctx = sctx->sect; + }; +} + +static void sb_encrypt_key_dictionary_key(struct sb_image_ctx *ictx) +{ + EVP_MD_CTX *md_ctx = ictx->md_ctx; + + sb_aes_crypt(ictx, ictx->image_key, ictx->sb_dict_key.key, + sizeof(ictx->sb_dict_key.key)); + EVP_DigestUpdate(md_ctx, &ictx->sb_dict_key, sizeof(ictx->sb_dict_key)); +} + +static void sb_decrypt_key_dictionary_key(struct sb_image_ctx *ictx) +{ + EVP_MD_CTX *md_ctx = ictx->md_ctx; + + EVP_DigestUpdate(md_ctx, &ictx->sb_dict_key, sizeof(ictx->sb_dict_key)); + sb_aes_crypt(ictx, ictx->sb_dict_key.key, ictx->image_key, + sizeof(ictx->sb_dict_key.key)); +} + +static void sb_encrypt_tag(struct sb_image_ctx *ictx, + struct sb_cmd_ctx *cctx) +{ + EVP_MD_CTX *md_ctx = ictx->md_ctx; + struct sb_command *cmd = &cctx->payload; + + sb_aes_crypt(ictx, (uint8_t *)cmd, + (uint8_t *)&cctx->c_payload, sizeof(*cmd)); + EVP_DigestUpdate(md_ctx, &cctx->c_payload, sizeof(*cmd)); +} + +static int sb_encrypt_image(struct sb_image_ctx *ictx) +{ + /* Start image-wide crypto. */ + ictx->md_ctx = EVP_MD_CTX_new(); + EVP_DigestInit(ictx->md_ctx, EVP_sha1()); + + /* + * SB image header. + */ + sb_aes_init(ictx, NULL, 1); + sb_encrypt_sb_header(ictx); + + /* + * SB sections header. + */ + sb_encrypt_sb_sections_header(ictx); + + /* + * Key dictionary. + */ + sb_aes_reinit(ictx, 1); + sb_encrypt_key_dictionary_key(ictx); + + /* + * Section tags. + */ + struct sb_cmd_ctx *cctx; + struct sb_command *ccmd; + struct sb_section_ctx *sctx = ictx->sect_head; + + while (sctx) { + cctx = sctx->cmd_head; + + sb_aes_reinit(ictx, 1); + + while (cctx) { + ccmd = &cctx->payload; + + sb_encrypt_tag(ictx, cctx); + + if (ccmd->header.tag == ROM_TAG_CMD) { + sb_aes_reinit(ictx, 1); + } else if (ccmd->header.tag == ROM_LOAD_CMD) { + sb_aes_crypt(ictx, cctx->data, cctx->data, + cctx->length); + EVP_DigestUpdate(ictx->md_ctx, cctx->data, + cctx->length); + } + + cctx = cctx->cmd; + } + + sctx = sctx->sect; + }; + + /* + * Dump the SHA1 of the whole image. + */ + sb_aes_reinit(ictx, 1); + + EVP_DigestFinal(ictx->md_ctx, ictx->digest, NULL); + EVP_MD_CTX_free(ictx->md_ctx); + sb_aes_crypt(ictx, ictx->digest, ictx->digest, sizeof(ictx->digest)); + + /* Stop the encryption session. */ + sb_aes_deinit(ictx->cipher_ctx); + + return 0; +} + +static int sb_load_file(struct sb_cmd_ctx *cctx, char *filename) +{ + long real_size, roundup_size; + uint8_t *data; + long ret; + unsigned long size; + FILE *fp; + + if (!filename) { + fprintf(stderr, "ERR: Missing filename!\n"); + return -EINVAL; + } + + fp = fopen(filename, "r"); + if (!fp) + goto err_open; + + ret = fseek(fp, 0, SEEK_END); + if (ret < 0) + goto err_file; + + real_size = ftell(fp); + if (real_size < 0) + goto err_file; + + ret = fseek(fp, 0, SEEK_SET); + if (ret < 0) + goto err_file; + + roundup_size = roundup(real_size, SB_BLOCK_SIZE); + data = calloc(1, roundup_size); + if (!data) + goto err_file; + + size = fread(data, 1, real_size, fp); + if (size != (unsigned long)real_size) + goto err_alloc; + + cctx->data = data; + cctx->length = roundup_size; + + fclose(fp); + return 0; + +err_alloc: + free(data); +err_file: + fclose(fp); +err_open: + fprintf(stderr, "ERR: Failed to load file \"%s\"\n", filename); + return -EINVAL; +} + +static uint8_t sb_command_checksum(struct sb_command *inst) +{ + uint8_t *inst_ptr = (uint8_t *)inst; + uint8_t csum = 0; + unsigned int i; + + for (i = 0; i < sizeof(struct sb_command); i++) + csum += inst_ptr[i]; + + return csum; +} + +static int sb_token_to_long(char *tok, uint32_t *rid) +{ + char *endptr; + unsigned long id; + + if (tok[0] != '0' || tok[1] != 'x') { + fprintf(stderr, "ERR: Invalid hexadecimal number!\n"); + return -EINVAL; + } + + tok += 2; + + errno = 0; + id = strtoul(tok, &endptr, 16); + if ((errno == ERANGE && id == ULONG_MAX) || (errno != 0 && id == 0)) { + fprintf(stderr, "ERR: Value can't be decoded!\n"); + return -EINVAL; + } + + /* Check for 32-bit overflow. */ + if (id > 0xffffffff) { + fprintf(stderr, "ERR: Value too big!\n"); + return -EINVAL; + } + + if (endptr == tok) { + fprintf(stderr, "ERR: Deformed value!\n"); + return -EINVAL; + } + + *rid = (uint32_t)id; + return 0; +} + +static int sb_grow_dcd(struct sb_dcd_ctx *dctx, unsigned int inc_size) +{ + uint32_t *tmp; + + if (!inc_size) + return 0; + + dctx->size += inc_size; + tmp = realloc(dctx->payload, dctx->size); + if (!tmp) + return -ENOMEM; + + dctx->payload = tmp; + + /* Assemble and update the HAB DCD header. */ + dctx->payload[0] = htonl((SB_HAB_DCD_TAG << 24) | + (dctx->size << 8) | + SB_HAB_VERSION); + + return 0; +} + +static int sb_build_dcd(struct sb_image_ctx *ictx, struct sb_cmd_list *cmd) +{ + struct sb_dcd_ctx *dctx; + + char *tok; + uint32_t id; + int ret; + + dctx = calloc(1, sizeof(*dctx)); + if (!dctx) + return -ENOMEM; + + ret = sb_grow_dcd(dctx, 4); + if (ret) + goto err_dcd; + + /* Read DCD block number. */ + tok = strtok(cmd->cmd, " "); + if (!tok) { + fprintf(stderr, "#%i ERR: DCD block without number!\n", + cmd->lineno); + ret = -EINVAL; + goto err_dcd; + } + + /* Parse the DCD block number. */ + ret = sb_token_to_long(tok, &id); + if (ret) { + fprintf(stderr, "#%i ERR: Malformed DCD block number!\n", + cmd->lineno); + goto err_dcd; + } + + dctx->id = id; + + /* + * The DCD block is now constructed. Append it to the list. + * WARNING: The DCD size is still not computed and will be + * updated while parsing it's commands. + */ + if (!ictx->dcd_head) { + ictx->dcd_head = dctx; + ictx->dcd_tail = dctx; + } else { + ictx->dcd_tail->dcd = dctx; + ictx->dcd_tail = dctx; + } + + return 0; + +err_dcd: + free(dctx->payload); + free(dctx); + return ret; +} + +static int sb_build_dcd_block(struct sb_image_ctx *ictx, + struct sb_cmd_list *cmd, + uint32_t type) +{ + char *tok; + uint32_t address, value, length; + int ret; + + struct sb_dcd_ctx *dctx = ictx->dcd_tail; + uint32_t *dcd; + + if (dctx->prev_dcd_head && (type != SB_DCD_NOOP) && + ((dctx->prev_dcd_head[0] & 0xff0000ff) == type)) { + /* Same instruction as before, just append it. */ + ret = sb_grow_dcd(dctx, 8); + if (ret) + return ret; + } else if (type == SB_DCD_NOOP) { + ret = sb_grow_dcd(dctx, 4); + if (ret) + return ret; + + /* Update DCD command block pointer. */ + dctx->prev_dcd_head = dctx->payload + + dctx->size / sizeof(*dctx->payload) - 1; + + /* NOOP has only 4 bytes and no payload. */ + goto noop; + } else { + /* + * Either a different instruction block started now + * or this is the first instruction block. + */ + ret = sb_grow_dcd(dctx, 12); + if (ret) + return ret; + + /* Update DCD command block pointer. */ + dctx->prev_dcd_head = dctx->payload + + dctx->size / sizeof(*dctx->payload) - 3; + } + + dcd = dctx->payload + dctx->size / sizeof(*dctx->payload) - 2; + + /* + * Prepare the command. + */ + tok = strtok(cmd->cmd, " "); + if (!tok) { + fprintf(stderr, "#%i ERR: Missing DCD address!\n", + cmd->lineno); + ret = -EINVAL; + goto err; + } + + /* Read DCD destination address. */ + ret = sb_token_to_long(tok, &address); + if (ret) { + fprintf(stderr, "#%i ERR: Incorrect DCD address!\n", + cmd->lineno); + goto err; + } + + tok = strtok(NULL, " "); + if (!tok) { + fprintf(stderr, "#%i ERR: Missing DCD value!\n", + cmd->lineno); + ret = -EINVAL; + goto err; + } + + /* Read DCD operation value. */ + ret = sb_token_to_long(tok, &value); + if (ret) { + fprintf(stderr, "#%i ERR: Incorrect DCD value!\n", + cmd->lineno); + goto err; + } + + /* Fill in the new DCD entry. */ + dcd[0] = htonl(address); + dcd[1] = htonl(value); + +noop: + /* Update the DCD command block. */ + length = dctx->size - + ((dctx->prev_dcd_head - dctx->payload) * + sizeof(*dctx->payload)); + dctx->prev_dcd_head[0] = htonl(type | (length << 8)); + +err: + return ret; +} + +static int sb_build_section(struct sb_image_ctx *ictx, struct sb_cmd_list *cmd) +{ + struct sb_section_ctx *sctx; + struct sb_sections_header *shdr; + char *tok; + uint32_t bootable = 0; + uint32_t id; + int ret; + + sctx = calloc(1, sizeof(*sctx)); + if (!sctx) + return -ENOMEM; + + /* Read section number. */ + tok = strtok(cmd->cmd, " "); + if (!tok) { + fprintf(stderr, "#%i ERR: Section without number!\n", + cmd->lineno); + ret = -EINVAL; + goto err_sect; + } + + /* Parse the section number. */ + ret = sb_token_to_long(tok, &id); + if (ret) { + fprintf(stderr, "#%i ERR: Malformed section number!\n", + cmd->lineno); + goto err_sect; + } + + /* Read section's BOOTABLE flag. */ + tok = strtok(NULL, " "); + if (tok && (strlen(tok) == 8) && !strncmp(tok, "BOOTABLE", 8)) + bootable = SB_SECTION_FLAG_BOOTABLE; + + sctx->boot = bootable; + + shdr = &sctx->payload; + shdr->section_number = id; + shdr->section_flags = bootable; + + /* + * The section is now constructed. Append it to the list. + * WARNING: The section size is still not computed and will + * be updated while parsing it's commands. + */ + ictx->sect_count++; + + /* Mark that this section is bootable one. */ + if (bootable) { + if (ictx->sect_boot_found) { + fprintf(stderr, + "#%i WARN: Multiple bootable section!\n", + cmd->lineno); + } else { + ictx->sect_boot = id; + ictx->sect_boot_found = 1; + } + } + + if (!ictx->sect_head) { + ictx->sect_head = sctx; + ictx->sect_tail = sctx; + } else { + ictx->sect_tail->sect = sctx; + ictx->sect_tail = sctx; + } + + return 0; + +err_sect: + free(sctx); + return ret; +} + +static int sb_build_command_nop(struct sb_image_ctx *ictx) +{ + struct sb_section_ctx *sctx = ictx->sect_tail; + struct sb_cmd_ctx *cctx; + struct sb_command *ccmd; + + cctx = calloc(1, sizeof(*cctx)); + if (!cctx) + return -ENOMEM; + + ccmd = &cctx->payload; + + /* + * Construct the command. + */ + ccmd->header.checksum = 0x5a; + ccmd->header.tag = ROM_NOP_CMD; + + cctx->size = sizeof(*ccmd); + + /* + * Append the command to the last section. + */ + if (!sctx->cmd_head) { + sctx->cmd_head = cctx; + sctx->cmd_tail = cctx; + } else { + sctx->cmd_tail->cmd = cctx; + sctx->cmd_tail = cctx; + } + + return 0; +} + +static int sb_build_command_tag(struct sb_image_ctx *ictx, + struct sb_cmd_list *cmd) +{ + struct sb_section_ctx *sctx = ictx->sect_tail; + struct sb_cmd_ctx *cctx; + struct sb_command *ccmd; + char *tok; + + cctx = calloc(1, sizeof(*cctx)); + if (!cctx) + return -ENOMEM; + + ccmd = &cctx->payload; + + /* + * Prepare the command. + */ + /* Check for the LAST keyword. */ + tok = strtok(cmd->cmd, " "); + if (tok && !strcmp(tok, "LAST")) + ccmd->header.flags = ROM_TAG_CMD_FLAG_ROM_LAST_TAG; + + /* + * Construct the command. + */ + ccmd->header.checksum = 0x5a; + ccmd->header.tag = ROM_TAG_CMD; + + cctx->size = sizeof(*ccmd); + + /* + * Append the command to the last section. + */ + if (!sctx->cmd_head) { + sctx->cmd_head = cctx; + sctx->cmd_tail = cctx; + } else { + sctx->cmd_tail->cmd = cctx; + sctx->cmd_tail = cctx; + } + + return 0; +} + +static int sb_build_command_load(struct sb_image_ctx *ictx, + struct sb_cmd_list *cmd) +{ + struct sb_section_ctx *sctx = ictx->sect_tail; + struct sb_cmd_ctx *cctx; + struct sb_command *ccmd; + char *tok; + int ret, is_ivt = 0, is_dcd = 0; + uint32_t dest, dcd = 0; + + cctx = calloc(1, sizeof(*cctx)); + if (!cctx) + return -ENOMEM; + + ccmd = &cctx->payload; + + /* + * Prepare the command. + */ + tok = strtok(cmd->cmd, " "); + if (!tok) { + fprintf(stderr, "#%i ERR: Missing LOAD address or 'IVT'!\n", + cmd->lineno); + ret = -EINVAL; + goto err; + } + + /* Check for "IVT" flag. */ + if (!strcmp(tok, "IVT")) + is_ivt = 1; + if (!strcmp(tok, "DCD")) + is_dcd = 1; + if (is_ivt || is_dcd) { + tok = strtok(NULL, " "); + if (!tok) { + fprintf(stderr, "#%i ERR: Missing LOAD address!\n", + cmd->lineno); + ret = -EINVAL; + goto err; + } + } + + /* Read load destination address. */ + ret = sb_token_to_long(tok, &dest); + if (ret) { + fprintf(stderr, "#%i ERR: Incorrect LOAD address!\n", + cmd->lineno); + goto err; + } + + /* Read filename or IVT entrypoint or DCD block ID. */ + tok = strtok(NULL, " "); + if (!tok) { + fprintf(stderr, + "#%i ERR: Missing LOAD filename or IVT ep or DCD block ID!\n", + cmd->lineno); + ret = -EINVAL; + goto err; + } + + if (is_ivt) { + /* Handle IVT. */ + struct sb_ivt_header *ivt; + uint32_t ivtep; + ret = sb_token_to_long(tok, &ivtep); + + if (ret) { + fprintf(stderr, + "#%i ERR: Incorrect IVT entry point!\n", + cmd->lineno); + goto err; + } + + ivt = calloc(1, sizeof(*ivt)); + if (!ivt) { + ret = -ENOMEM; + goto err; + } + + ivt->header = sb_hab_ivt_header(); + ivt->entry = ivtep; + ivt->self = dest; + + cctx->data = (uint8_t *)ivt; + cctx->length = sizeof(*ivt); + } else if (is_dcd) { + struct sb_dcd_ctx *dctx = ictx->dcd_head; + uint32_t dcdid; + uint8_t *payload; + uint32_t asize; + ret = sb_token_to_long(tok, &dcdid); + + if (ret) { + fprintf(stderr, + "#%i ERR: Incorrect DCD block ID!\n", + cmd->lineno); + goto err; + } + + while (dctx) { + if (dctx->id == dcdid) + break; + dctx = dctx->dcd; + } + + if (!dctx) { + fprintf(stderr, "#%i ERR: DCD block %08x not found!\n", + cmd->lineno, dcdid); + goto err; + } + + asize = roundup(dctx->size, SB_BLOCK_SIZE); + payload = calloc(1, asize); + if (!payload) { + ret = -ENOMEM; + goto err; + } + + memcpy(payload, dctx->payload, dctx->size); + + cctx->data = payload; + cctx->length = asize; + + /* Set the Load DCD flag. */ + dcd = ROM_LOAD_CMD_FLAG_DCD_LOAD; + } else { + /* Regular LOAD of a file. */ + ret = sb_load_file(cctx, tok); + if (ret) { + fprintf(stderr, "#%i ERR: Cannot load '%s'!\n", + cmd->lineno, tok); + goto err; + } + } + + if (cctx->length & (SB_BLOCK_SIZE - 1)) { + fprintf(stderr, "#%i ERR: Unaligned payload!\n", + cmd->lineno); + } + + /* + * Construct the command. + */ + ccmd->header.checksum = 0x5a; + ccmd->header.tag = ROM_LOAD_CMD; + ccmd->header.flags = dcd; + + ccmd->load.address = dest; + ccmd->load.count = cctx->length; + ccmd->load.crc32 = pbl_crc32(0, + (const char *)cctx->data, + cctx->length); + + cctx->size = sizeof(*ccmd) + cctx->length; + + /* + * Append the command to the last section. + */ + if (!sctx->cmd_head) { + sctx->cmd_head = cctx; + sctx->cmd_tail = cctx; + } else { + sctx->cmd_tail->cmd = cctx; + sctx->cmd_tail = cctx; + } + + return 0; + +err: + free(cctx); + return ret; +} + +static int sb_build_command_fill(struct sb_image_ctx *ictx, + struct sb_cmd_list *cmd) +{ + struct sb_section_ctx *sctx = ictx->sect_tail; + struct sb_cmd_ctx *cctx; + struct sb_command *ccmd; + char *tok; + uint32_t address, pattern, length; + int ret; + + cctx = calloc(1, sizeof(*cctx)); + if (!cctx) + return -ENOMEM; + + ccmd = &cctx->payload; + + /* + * Prepare the command. + */ + tok = strtok(cmd->cmd, " "); + if (!tok) { + fprintf(stderr, "#%i ERR: Missing FILL address!\n", + cmd->lineno); + ret = -EINVAL; + goto err; + } + + /* Read fill destination address. */ + ret = sb_token_to_long(tok, &address); + if (ret) { + fprintf(stderr, "#%i ERR: Incorrect FILL address!\n", + cmd->lineno); + goto err; + } + + tok = strtok(NULL, " "); + if (!tok) { + fprintf(stderr, "#%i ERR: Missing FILL pattern!\n", + cmd->lineno); + ret = -EINVAL; + goto err; + } + + /* Read fill pattern address. */ + ret = sb_token_to_long(tok, &pattern); + if (ret) { + fprintf(stderr, "#%i ERR: Incorrect FILL pattern!\n", + cmd->lineno); + goto err; + } + + tok = strtok(NULL, " "); + if (!tok) { + fprintf(stderr, "#%i ERR: Missing FILL length!\n", + cmd->lineno); + ret = -EINVAL; + goto err; + } + + /* Read fill pattern address. */ + ret = sb_token_to_long(tok, &length); + if (ret) { + fprintf(stderr, "#%i ERR: Incorrect FILL length!\n", + cmd->lineno); + goto err; + } + + /* + * Construct the command. + */ + ccmd->header.checksum = 0x5a; + ccmd->header.tag = ROM_FILL_CMD; + + ccmd->fill.address = address; + ccmd->fill.count = length; + ccmd->fill.pattern = pattern; + + cctx->size = sizeof(*ccmd); + + /* + * Append the command to the last section. + */ + if (!sctx->cmd_head) { + sctx->cmd_head = cctx; + sctx->cmd_tail = cctx; + } else { + sctx->cmd_tail->cmd = cctx; + sctx->cmd_tail = cctx; + } + + return 0; + +err: + free(cctx); + return ret; +} + +static int sb_build_command_jump_call(struct sb_image_ctx *ictx, + struct sb_cmd_list *cmd, + unsigned int is_call) +{ + struct sb_section_ctx *sctx = ictx->sect_tail; + struct sb_cmd_ctx *cctx; + struct sb_command *ccmd; + char *tok; + uint32_t dest, arg = 0x0; + uint32_t hab = 0; + int ret; + const char *cmdname = is_call ? "CALL" : "JUMP"; + + cctx = calloc(1, sizeof(*cctx)); + if (!cctx) + return -ENOMEM; + + ccmd = &cctx->payload; + + /* + * Prepare the command. + */ + tok = strtok(cmd->cmd, " "); + if (!tok) { + fprintf(stderr, + "#%i ERR: Missing %s address or 'HAB'!\n", + cmd->lineno, cmdname); + ret = -EINVAL; + goto err; + } + + /* Check for "HAB" flag. */ + if (!strcmp(tok, "HAB")) { + hab = is_call ? ROM_CALL_CMD_FLAG_HAB : ROM_JUMP_CMD_FLAG_HAB; + tok = strtok(NULL, " "); + if (!tok) { + fprintf(stderr, "#%i ERR: Missing %s address!\n", + cmd->lineno, cmdname); + ret = -EINVAL; + goto err; + } + } + /* Read load destination address. */ + ret = sb_token_to_long(tok, &dest); + if (ret) { + fprintf(stderr, "#%i ERR: Incorrect %s address!\n", + cmd->lineno, cmdname); + goto err; + } + + tok = strtok(NULL, " "); + if (tok) { + ret = sb_token_to_long(tok, &arg); + if (ret) { + fprintf(stderr, + "#%i ERR: Incorrect %s argument!\n", + cmd->lineno, cmdname); + goto err; + } + } + + /* + * Construct the command. + */ + ccmd->header.checksum = 0x5a; + ccmd->header.tag = is_call ? ROM_CALL_CMD : ROM_JUMP_CMD; + ccmd->header.flags = hab; + + ccmd->call.address = dest; + ccmd->call.argument = arg; + + cctx->size = sizeof(*ccmd); + + /* + * Append the command to the last section. + */ + if (!sctx->cmd_head) { + sctx->cmd_head = cctx; + sctx->cmd_tail = cctx; + } else { + sctx->cmd_tail->cmd = cctx; + sctx->cmd_tail = cctx; + } + + return 0; + +err: + free(cctx); + return ret; +} + +static int sb_build_command_jump(struct sb_image_ctx *ictx, + struct sb_cmd_list *cmd) +{ + return sb_build_command_jump_call(ictx, cmd, 0); +} + +static int sb_build_command_call(struct sb_image_ctx *ictx, + struct sb_cmd_list *cmd) +{ + return sb_build_command_jump_call(ictx, cmd, 1); +} + +static int sb_build_command_mode(struct sb_image_ctx *ictx, + struct sb_cmd_list *cmd) +{ + struct sb_section_ctx *sctx = ictx->sect_tail; + struct sb_cmd_ctx *cctx; + struct sb_command *ccmd; + char *tok; + int ret; + unsigned int i; + uint32_t mode = 0xffffffff; + + cctx = calloc(1, sizeof(*cctx)); + if (!cctx) + return -ENOMEM; + + ccmd = &cctx->payload; + + /* + * Prepare the command. + */ + tok = strtok(cmd->cmd, " "); + if (!tok) { + fprintf(stderr, "#%i ERR: Missing MODE boot mode argument!\n", + cmd->lineno); + ret = -EINVAL; + goto err; + } + + for (i = 0; i < ARRAY_SIZE(modetable); i++) { + if (!strcmp(tok, modetable[i].name)) { + mode = modetable[i].mode; + break; + } + + if (!modetable[i].altname) + continue; + + if (!strcmp(tok, modetable[i].altname)) { + mode = modetable[i].mode; + break; + } + } + + if (mode == 0xffffffff) { + fprintf(stderr, "#%i ERR: Invalid MODE boot mode argument!\n", + cmd->lineno); + ret = -EINVAL; + goto err; + } + + /* + * Construct the command. + */ + ccmd->header.checksum = 0x5a; + ccmd->header.tag = ROM_MODE_CMD; + + ccmd->mode.mode = mode; + + cctx->size = sizeof(*ccmd); + + /* + * Append the command to the last section. + */ + if (!sctx->cmd_head) { + sctx->cmd_head = cctx; + sctx->cmd_tail = cctx; + } else { + sctx->cmd_tail->cmd = cctx; + sctx->cmd_tail = cctx; + } + + return 0; + +err: + free(cctx); + return ret; +} + +static int sb_prefill_image_header(struct sb_image_ctx *ictx) +{ + struct sb_boot_image_header *hdr = &ictx->payload; + + /* Fill signatures */ + memcpy(hdr->signature1, "STMP", 4); + memcpy(hdr->signature2, "sgtl", 4); + + /* SB Image version 1.1 */ + hdr->major_version = SB_VERSION_MAJOR; + hdr->minor_version = SB_VERSION_MINOR; + + /* Boot image major version */ + hdr->product_version.major = htons(0x999); + hdr->product_version.minor = htons(0x999); + hdr->product_version.revision = htons(0x999); + /* Boot image major version */ + hdr->component_version.major = htons(0x999); + hdr->component_version.minor = htons(0x999); + hdr->component_version.revision = htons(0x999); + + /* Drive tag must be 0x0 for i.MX23 */ + hdr->drive_tag = 0; + + hdr->header_blocks = + sizeof(struct sb_boot_image_header) / SB_BLOCK_SIZE; + hdr->section_header_size = + sizeof(struct sb_sections_header) / SB_BLOCK_SIZE; + hdr->timestamp_us = sb_get_timestamp() * 1000000; + + hdr->flags = ictx->display_progress ? + SB_IMAGE_FLAG_DISPLAY_PROGRESS : 0; + + /* FIXME -- We support only default key */ + hdr->key_count = 1; + + return 0; +} + +static int sb_postfill_image_header(struct sb_image_ctx *ictx) +{ + struct sb_boot_image_header *hdr = &ictx->payload; + struct sb_section_ctx *sctx = ictx->sect_head; + uint32_t kd_size, sections_blocks; + EVP_MD_CTX *md_ctx; + + /* The main SB header size in blocks. */ + hdr->image_blocks = hdr->header_blocks; + + /* Size of the key dictionary, which has single zero entry. */ + kd_size = hdr->key_count * sizeof(struct sb_key_dictionary_key); + hdr->image_blocks += kd_size / SB_BLOCK_SIZE; + + /* Now count the payloads. */ + hdr->section_count = ictx->sect_count; + while (sctx) { + hdr->image_blocks += sctx->size / SB_BLOCK_SIZE; + sctx = sctx->sect; + } + + if (!ictx->sect_boot_found) { + fprintf(stderr, "ERR: No bootable section selected!\n"); + return -EINVAL; + } + hdr->first_boot_section_id = ictx->sect_boot; + + /* The n * SB section size in blocks. */ + sections_blocks = hdr->section_count * hdr->section_header_size; + hdr->image_blocks += sections_blocks; + + /* Key dictionary offset. */ + hdr->key_dictionary_block = hdr->header_blocks + sections_blocks; + + /* Digest of the whole image. */ + hdr->image_blocks += 2; + + /* Pointer past the dictionary. */ + hdr->first_boot_tag_block = + hdr->key_dictionary_block + kd_size / SB_BLOCK_SIZE; + + /* Compute header digest. */ + md_ctx = EVP_MD_CTX_new(); + + EVP_DigestInit(md_ctx, EVP_sha1()); + EVP_DigestUpdate(md_ctx, hdr->signature1, + sizeof(struct sb_boot_image_header) - + sizeof(hdr->digest)); + EVP_DigestFinal(md_ctx, hdr->digest, NULL); + EVP_MD_CTX_free(md_ctx); + + return 0; +} + +static int sb_fixup_sections_and_tags(struct sb_image_ctx *ictx) +{ + /* Fixup the placement of sections. */ + struct sb_boot_image_header *ihdr = &ictx->payload; + struct sb_section_ctx *sctx = ictx->sect_head; + struct sb_sections_header *shdr; + struct sb_cmd_ctx *cctx; + struct sb_command *ccmd; + uint32_t offset = ihdr->first_boot_tag_block; + + while (sctx) { + shdr = &sctx->payload; + + /* Fill in the section TAG offset. */ + shdr->section_offset = offset + 1; + offset += shdr->section_size; + + /* Section length is measured from the TAG block. */ + shdr->section_size--; + + /* Fixup the TAG command. */ + cctx = sctx->cmd_head; + while (cctx) { + ccmd = &cctx->payload; + if (ccmd->header.tag == ROM_TAG_CMD) { + ccmd->tag.section_number = shdr->section_number; + ccmd->tag.section_length = shdr->section_size; + ccmd->tag.section_flags = shdr->section_flags; + } + + /* Update the command checksum. */ + ccmd->header.checksum = sb_command_checksum(ccmd); + + cctx = cctx->cmd; + } + + sctx = sctx->sect; + } + + return 0; +} + +static int sb_parse_line(struct sb_image_ctx *ictx, struct sb_cmd_list *cmd) +{ + char *tok; + char *line = cmd->cmd; + char *rptr = NULL; + int ret; + + /* Analyze the identifier on this line first. */ + tok = strtok_r(line, " ", &rptr); + if (!tok || (strlen(tok) == 0)) { + fprintf(stderr, "#%i ERR: Invalid line!\n", cmd->lineno); + return -EINVAL; + } + + cmd->cmd = rptr; + + /* set DISPLAY_PROGRESS flag */ + if (!strcmp(tok, "DISPLAYPROGRESS")) { + ictx->display_progress = 1; + return 0; + } + + /* DCD */ + if (!strcmp(tok, "DCD")) { + ictx->in_section = 0; + ictx->in_dcd = 1; + sb_build_dcd(ictx, cmd); + return 0; + } + + /* Section */ + if (!strcmp(tok, "SECTION")) { + ictx->in_section = 1; + ictx->in_dcd = 0; + sb_build_section(ictx, cmd); + return 0; + } + + if (!ictx->in_section && !ictx->in_dcd) { + fprintf(stderr, "#%i ERR: Data outside of a section!\n", + cmd->lineno); + return -EINVAL; + } + + if (ictx->in_section) { + /* Section commands */ + if (!strcmp(tok, "NOP")) { + ret = sb_build_command_nop(ictx); + } else if (!strcmp(tok, "TAG")) { + ret = sb_build_command_tag(ictx, cmd); + } else if (!strcmp(tok, "LOAD")) { + ret = sb_build_command_load(ictx, cmd); + } else if (!strcmp(tok, "FILL")) { + ret = sb_build_command_fill(ictx, cmd); + } else if (!strcmp(tok, "JUMP")) { + ret = sb_build_command_jump(ictx, cmd); + } else if (!strcmp(tok, "CALL")) { + ret = sb_build_command_call(ictx, cmd); + } else if (!strcmp(tok, "MODE")) { + ret = sb_build_command_mode(ictx, cmd); + } else { + fprintf(stderr, + "#%i ERR: Unsupported instruction '%s'!\n", + cmd->lineno, tok); + return -ENOTSUP; + } + } else if (ictx->in_dcd) { + char *lptr; + uint32_t ilen = '1'; + + tok = strtok_r(tok, ".", &lptr); + if (!tok || (strlen(tok) == 0) || (lptr && strlen(lptr) != 1)) { + fprintf(stderr, "#%i ERR: Invalid line!\n", + cmd->lineno); + return -EINVAL; + } + + if (lptr && + (lptr[0] != '1' && lptr[0] != '2' && lptr[0] != '4')) { + fprintf(stderr, "#%i ERR: Invalid instruction width!\n", + cmd->lineno); + return -EINVAL; + } + + if (lptr) + ilen = lptr[0] - '1'; + + /* DCD commands */ + if (!strcmp(tok, "WRITE")) { + ret = sb_build_dcd_block(ictx, cmd, + SB_DCD_WRITE | ilen); + } else if (!strcmp(tok, "ANDC")) { + ret = sb_build_dcd_block(ictx, cmd, + SB_DCD_ANDC | ilen); + } else if (!strcmp(tok, "ORR")) { + ret = sb_build_dcd_block(ictx, cmd, + SB_DCD_ORR | ilen); + } else if (!strcmp(tok, "EQZ")) { + ret = sb_build_dcd_block(ictx, cmd, + SB_DCD_CHK_EQZ | ilen); + } else if (!strcmp(tok, "EQ")) { + ret = sb_build_dcd_block(ictx, cmd, + SB_DCD_CHK_EQ | ilen); + } else if (!strcmp(tok, "NEQ")) { + ret = sb_build_dcd_block(ictx, cmd, + SB_DCD_CHK_NEQ | ilen); + } else if (!strcmp(tok, "NEZ")) { + ret = sb_build_dcd_block(ictx, cmd, + SB_DCD_CHK_NEZ | ilen); + } else if (!strcmp(tok, "NOOP")) { + ret = sb_build_dcd_block(ictx, cmd, SB_DCD_NOOP); + } else { + fprintf(stderr, + "#%i ERR: Unsupported instruction '%s'!\n", + cmd->lineno, tok); + return -ENOTSUP; + } + } else { + fprintf(stderr, "#%i ERR: Unsupported instruction '%s'!\n", + cmd->lineno, tok); + return -ENOTSUP; + } + + /* + * Here we have at least one section with one command, otherwise we + * would have failed already higher above. + * + * FIXME -- should the updating happen here ? + */ + if (ictx->in_section && !ret) { + ictx->sect_tail->size += ictx->sect_tail->cmd_tail->size; + ictx->sect_tail->payload.section_size = + ictx->sect_tail->size / SB_BLOCK_SIZE; + } + + return ret; +} + +static int sb_load_cmdfile(struct sb_image_ctx *ictx) +{ + struct sb_cmd_list cmd; + int lineno = 1; + FILE *fp; + char *line = NULL; + ssize_t rlen; + size_t len; + + fp = fopen(ictx->cfg_filename, "r"); + if (!fp) + goto err_file; + + while ((rlen = getline(&line, &len, fp)) > 0) { + memset(&cmd, 0, sizeof(cmd)); + + /* Strip the trailing newline. */ + line[rlen - 1] = '\0'; + + cmd.cmd = line; + cmd.len = rlen; + cmd.lineno = lineno++; + + sb_parse_line(ictx, &cmd); + } + + free(line); + + fclose(fp); + + return 0; + +err_file: + fclose(fp); + fprintf(stderr, "ERR: Failed to load file \"%s\"\n", + ictx->cfg_filename); + return -EINVAL; +} + +static int sb_build_tree_from_cfg(struct sb_image_ctx *ictx) +{ + int ret; + + ret = sb_load_cmdfile(ictx); + if (ret) + return ret; + + ret = sb_prefill_image_header(ictx); + if (ret) + return ret; + + ret = sb_postfill_image_header(ictx); + if (ret) + return ret; + + ret = sb_fixup_sections_and_tags(ictx); + if (ret) + return ret; + + return 0; +} + +static int sb_verify_image_header(struct sb_image_ctx *ictx, + FILE *fp, long fsize) +{ + /* Verify static fields in the image header. */ + struct sb_boot_image_header *hdr = &ictx->payload; + const char *stat[2] = { "[PASS]", "[FAIL]" }; + struct tm tm; + int sz, ret = 0; + unsigned char digest[20]; + EVP_MD_CTX *md_ctx; + unsigned long size; + + /* Start image-wide crypto. */ + ictx->md_ctx = EVP_MD_CTX_new(); + EVP_DigestInit(ictx->md_ctx, EVP_sha1()); + + soprintf(ictx, "---------- Verifying SB Image Header ----------\n"); + + size = fread(&ictx->payload, 1, sizeof(ictx->payload), fp); + if (size != sizeof(ictx->payload)) { + fprintf(stderr, "ERR: SB image header too short!\n"); + return -EINVAL; + } + + /* Compute header digest. */ + md_ctx = EVP_MD_CTX_new(); + EVP_DigestInit(md_ctx, EVP_sha1()); + EVP_DigestUpdate(md_ctx, hdr->signature1, + sizeof(struct sb_boot_image_header) - + sizeof(hdr->digest)); + EVP_DigestFinal(md_ctx, digest, NULL); + EVP_MD_CTX_free(md_ctx); + + sb_aes_init(ictx, NULL, 1); + sb_encrypt_sb_header(ictx); + + if (memcmp(digest, hdr->digest, 20)) + ret = -EINVAL; + soprintf(ictx, "%s Image header checksum: %s\n", stat[!!ret], + ret ? "BAD" : "OK"); + if (ret) + return ret; + + if (memcmp(hdr->signature1, "STMP", 4) || + memcmp(hdr->signature2, "sgtl", 4)) + ret = -EINVAL; + soprintf(ictx, "%s Signatures: '%.4s' '%.4s'\n", + stat[!!ret], hdr->signature1, hdr->signature2); + if (ret) + return ret; + + if ((hdr->major_version != SB_VERSION_MAJOR) || + ((hdr->minor_version != 1) && (hdr->minor_version != 2))) + ret = -EINVAL; + soprintf(ictx, "%s Image version: v%i.%i\n", stat[!!ret], + hdr->major_version, hdr->minor_version); + if (ret) + return ret; + + ret = sb_get_time(hdr->timestamp_us / 1000000, &tm); + soprintf(ictx, + "%s Creation time: %02i:%02i:%02i %02i/%02i/%04i\n", + stat[!!ret], tm.tm_hour, tm.tm_min, tm.tm_sec, + tm.tm_mday, tm.tm_mon, tm.tm_year + 2000); + if (ret) + return ret; + + soprintf(ictx, "%s Product version: %x.%x.%x\n", stat[0], + ntohs(hdr->product_version.major), + ntohs(hdr->product_version.minor), + ntohs(hdr->product_version.revision)); + soprintf(ictx, "%s Component version: %x.%x.%x\n", stat[0], + ntohs(hdr->component_version.major), + ntohs(hdr->component_version.minor), + ntohs(hdr->component_version.revision)); + + if (hdr->flags & ~SB_IMAGE_FLAGS_MASK) + ret = -EINVAL; + soprintf(ictx, "%s Image flags: %s\n", stat[!!ret], + hdr->flags & SB_IMAGE_FLAG_DISPLAY_PROGRESS ? + "Display_progress" : ""); + if (ret) + return ret; + + if (hdr->drive_tag != 0) + ret = -EINVAL; + soprintf(ictx, "%s Drive tag: %i\n", stat[!!ret], + hdr->drive_tag); + if (ret) + return ret; + + sz = sizeof(struct sb_boot_image_header) / SB_BLOCK_SIZE; + if (hdr->header_blocks != sz) + ret = -EINVAL; + soprintf(ictx, "%s Image header size (blocks): %i\n", stat[!!ret], + hdr->header_blocks); + if (ret) + return ret; + + sz = sizeof(struct sb_sections_header) / SB_BLOCK_SIZE; + if (hdr->section_header_size != sz) + ret = -EINVAL; + soprintf(ictx, "%s Section header size (blocks): %i\n", stat[!!ret], + hdr->section_header_size); + if (ret) + return ret; + + soprintf(ictx, "%s Sections count: %i\n", stat[!!ret], + hdr->section_count); + soprintf(ictx, "%s First bootable section %i\n", stat[!!ret], + hdr->first_boot_section_id); + + if (hdr->image_blocks != fsize / SB_BLOCK_SIZE) + ret = -EINVAL; + soprintf(ictx, "%s Image size (blocks): %i\n", stat[!!ret], + hdr->image_blocks); + if (ret) + return ret; + + sz = hdr->header_blocks + hdr->section_header_size * hdr->section_count; + if (hdr->key_dictionary_block != sz) + ret = -EINVAL; + soprintf(ictx, "%s Key dict offset (blocks): %i\n", stat[!!ret], + hdr->key_dictionary_block); + if (ret) + return ret; + + if (hdr->key_count != 1) + ret = -EINVAL; + soprintf(ictx, "%s Number of encryption keys: %i\n", stat[!!ret], + hdr->key_count); + if (ret) + return ret; + + sz = hdr->header_blocks + hdr->section_header_size * hdr->section_count; + sz += hdr->key_count * + sizeof(struct sb_key_dictionary_key) / SB_BLOCK_SIZE; + if (hdr->first_boot_tag_block != (unsigned)sz) + ret = -EINVAL; + soprintf(ictx, "%s First TAG block (blocks): %i\n", stat[!!ret], + hdr->first_boot_tag_block); + if (ret) + return ret; + + return 0; +} + +static void sb_decrypt_tag(struct sb_image_ctx *ictx, + struct sb_cmd_ctx *cctx) +{ + EVP_MD_CTX *md_ctx = ictx->md_ctx; + struct sb_command *cmd = &cctx->payload; + + sb_aes_crypt(ictx, (uint8_t *)&cctx->c_payload, + (uint8_t *)&cctx->payload, sizeof(*cmd)); + EVP_DigestUpdate(md_ctx, &cctx->c_payload, sizeof(*cmd)); +} + +static int sb_verify_command(struct sb_image_ctx *ictx, + struct sb_cmd_ctx *cctx, FILE *fp, + unsigned long *tsize) +{ + struct sb_command *ccmd = &cctx->payload; + unsigned long size, asize; + char *csum, *flag = ""; + int ret; + unsigned int i; + uint8_t csn, csc = ccmd->header.checksum; + ccmd->header.checksum = 0x5a; + csn = sb_command_checksum(ccmd); + ccmd->header.checksum = csc; + + if (csc == csn) + ret = 0; + else + ret = -EINVAL; + csum = ret ? "checksum BAD" : "checksum OK"; + + switch (ccmd->header.tag) { + case ROM_NOP_CMD: + soprintf(ictx, " NOOP # %s\n", csum); + return ret; + case ROM_TAG_CMD: + if (ccmd->header.flags & ROM_TAG_CMD_FLAG_ROM_LAST_TAG) + flag = "LAST"; + soprintf(ictx, " TAG %s # %s\n", flag, csum); + sb_aes_reinit(ictx, 0); + return ret; + case ROM_LOAD_CMD: + soprintf(ictx, " LOAD addr=0x%08x length=0x%08x # %s\n", + ccmd->load.address, ccmd->load.count, csum); + + cctx->length = ccmd->load.count; + asize = roundup(cctx->length, SB_BLOCK_SIZE); + cctx->data = malloc(asize); + if (!cctx->data) + return -ENOMEM; + + size = fread(cctx->data, 1, asize, fp); + if (size != asize) { + fprintf(stderr, + "ERR: SB LOAD command payload too short!\n"); + return -EINVAL; + } + + *tsize += size; + + EVP_DigestUpdate(ictx->md_ctx, cctx->data, asize); + sb_aes_crypt(ictx, cctx->data, cctx->data, asize); + + if (ccmd->load.crc32 != pbl_crc32(0, + (const char *)cctx->data, + asize)) { + fprintf(stderr, + "ERR: SB LOAD command payload CRC32 invalid!\n"); + return -EINVAL; + } + return 0; + case ROM_FILL_CMD: + soprintf(ictx, + " FILL addr=0x%08x length=0x%08x pattern=0x%08x # %s\n", + ccmd->fill.address, ccmd->fill.count, + ccmd->fill.pattern, csum); + return 0; + case ROM_JUMP_CMD: + if (ccmd->header.flags & ROM_JUMP_CMD_FLAG_HAB) + flag = " HAB"; + soprintf(ictx, + " JUMP%s addr=0x%08x r0_arg=0x%08x # %s\n", + flag, ccmd->fill.address, ccmd->jump.argument, csum); + return 0; + case ROM_CALL_CMD: + if (ccmd->header.flags & ROM_CALL_CMD_FLAG_HAB) + flag = " HAB"; + soprintf(ictx, + " CALL%s addr=0x%08x r0_arg=0x%08x # %s\n", + flag, ccmd->fill.address, ccmd->jump.argument, csum); + return 0; + case ROM_MODE_CMD: + for (i = 0; i < ARRAY_SIZE(modetable); i++) { + if (ccmd->mode.mode == modetable[i].mode) { + soprintf(ictx, " MODE %s # %s\n", + modetable[i].name, csum); + break; + } + } + fprintf(stderr, " MODE !INVALID! # %s\n", csum); + return 0; + } + + return ret; +} + +static int sb_verify_commands(struct sb_image_ctx *ictx, + struct sb_section_ctx *sctx, FILE *fp) +{ + unsigned long size, tsize = 0; + struct sb_cmd_ctx *cctx; + int ret; + + sb_aes_reinit(ictx, 0); + + while (tsize < sctx->size) { + cctx = calloc(1, sizeof(*cctx)); + if (!cctx) + return -ENOMEM; + if (!sctx->cmd_head) { + sctx->cmd_head = cctx; + sctx->cmd_tail = cctx; + } else { + sctx->cmd_tail->cmd = cctx; + sctx->cmd_tail = cctx; + } + + size = fread(&cctx->c_payload, 1, sizeof(cctx->c_payload), fp); + if (size != sizeof(cctx->c_payload)) { + fprintf(stderr, "ERR: SB command header too short!\n"); + return -EINVAL; + } + + tsize += size; + + sb_decrypt_tag(ictx, cctx); + + ret = sb_verify_command(ictx, cctx, fp, &tsize); + if (ret) + return -EINVAL; + } + + return 0; +} + +static int sb_verify_sections_cmds(struct sb_image_ctx *ictx, FILE *fp) +{ + struct sb_boot_image_header *hdr = &ictx->payload; + struct sb_sections_header *shdr; + unsigned int i; + int ret; + struct sb_section_ctx *sctx; + unsigned long size; + char *bootable = ""; + + soprintf(ictx, "----- Verifying SB Sections and Commands -----\n"); + + for (i = 0; i < hdr->section_count; i++) { + sctx = calloc(1, sizeof(*sctx)); + if (!sctx) + return -ENOMEM; + if (!ictx->sect_head) { + ictx->sect_head = sctx; + ictx->sect_tail = sctx; + } else { + ictx->sect_tail->sect = sctx; + ictx->sect_tail = sctx; + } + + size = fread(&sctx->payload, 1, sizeof(sctx->payload), fp); + if (size != sizeof(sctx->payload)) { + fprintf(stderr, "ERR: SB section header too short!\n"); + return -EINVAL; + } + } + + size = fread(&ictx->sb_dict_key, 1, sizeof(ictx->sb_dict_key), fp); + if (size != sizeof(ictx->sb_dict_key)) { + fprintf(stderr, "ERR: SB key dictionary too short!\n"); + return -EINVAL; + } + + sb_encrypt_sb_sections_header(ictx); + sb_aes_reinit(ictx, 0); + sb_decrypt_key_dictionary_key(ictx); + + sb_aes_reinit(ictx, 0); + + sctx = ictx->sect_head; + while (sctx) { + shdr = &sctx->payload; + + if (shdr->section_flags & SB_SECTION_FLAG_BOOTABLE) { + sctx->boot = 1; + bootable = " BOOTABLE"; + } + + sctx->size = (shdr->section_size * SB_BLOCK_SIZE) + + sizeof(struct sb_command); + soprintf(ictx, "SECTION 0x%x%s # size = %i bytes\n", + shdr->section_number, bootable, sctx->size); + + if (shdr->section_flags & ~SB_SECTION_FLAG_BOOTABLE) + fprintf(stderr, " WARN: Unknown section flag(s) %08x\n", + shdr->section_flags); + + if ((shdr->section_flags & SB_SECTION_FLAG_BOOTABLE) && + (hdr->first_boot_section_id != shdr->section_number)) { + fprintf(stderr, + " WARN: Bootable section does ID not match image header ID!\n"); + } + + ret = sb_verify_commands(ictx, sctx, fp); + if (ret) + return ret; + + sctx = sctx->sect; + } + + /* + * FIXME IDEA: + * check if the first TAG command is at sctx->section_offset + */ + return 0; +} + +static int sb_verify_image_end(struct sb_image_ctx *ictx, + FILE *fp, off_t filesz) +{ + uint8_t digest[32]; + unsigned long size; + off_t pos; + int ret; + + soprintf(ictx, "------------- Verifying image end -------------\n"); + + size = fread(digest, 1, sizeof(digest), fp); + if (size != sizeof(digest)) { + fprintf(stderr, "ERR: SB key dictionary too short!\n"); + return -EINVAL; + } + + pos = ftell(fp); + if (pos != filesz) { + fprintf(stderr, "ERR: Trailing data past the image!\n"); + return -EINVAL; + } + + /* Check the image digest. */ + EVP_DigestFinal(ictx->md_ctx, ictx->digest, NULL); + EVP_MD_CTX_free(ictx->md_ctx); + + /* Decrypt the image digest from the input image. */ + sb_aes_reinit(ictx, 0); + sb_aes_crypt(ictx, digest, digest, sizeof(digest)); + + /* Check all of 20 bytes of the SHA1 hash. */ + ret = memcmp(digest, ictx->digest, 20) ? -EINVAL : 0; + + if (ret) + soprintf(ictx, "[FAIL] Full-image checksum: BAD\n"); + else + soprintf(ictx, "[PASS] Full-image checksum: OK\n"); + + return ret; +} + + +static int sb_build_tree_from_img(struct sb_image_ctx *ictx) +{ + long filesize; + int ret; + FILE *fp; + + if (!ictx->input_filename) { + fprintf(stderr, "ERR: Missing filename!\n"); + return -EINVAL; + } + + fp = fopen(ictx->input_filename, "r"); + if (!fp) + goto err_open; + + ret = fseek(fp, 0, SEEK_END); + if (ret < 0) + goto err_file; + + filesize = ftell(fp); + if (filesize < 0) + goto err_file; + + ret = fseek(fp, 0, SEEK_SET); + if (ret < 0) + goto err_file; + + if (filesize < (signed)sizeof(ictx->payload)) { + fprintf(stderr, "ERR: File too short!\n"); + goto err_file; + } + + if (filesize & (SB_BLOCK_SIZE - 1)) { + fprintf(stderr, "ERR: The file is not aligned!\n"); + goto err_file; + } + + /* Load and verify image header */ + ret = sb_verify_image_header(ictx, fp, filesize); + if (ret) + goto err_verify; + + /* Load and verify sections and commands */ + ret = sb_verify_sections_cmds(ictx, fp); + if (ret) + goto err_verify; + + ret = sb_verify_image_end(ictx, fp, filesize); + if (ret) + goto err_verify; + + ret = 0; + +err_verify: + soprintf(ictx, "-------------------- Result -------------------\n"); + soprintf(ictx, "Verification %s\n", ret ? "FAILED" : "PASSED"); + + /* Stop the encryption session. */ + sb_aes_deinit(ictx->cipher_ctx); + + fclose(fp); + return ret; + +err_file: + fclose(fp); +err_open: + fprintf(stderr, "ERR: Failed to load file \"%s\"\n", + ictx->input_filename); + return -EINVAL; +} + +static void sb_free_image(struct sb_image_ctx *ictx) +{ + struct sb_section_ctx *sctx = ictx->sect_head, *s_head; + struct sb_dcd_ctx *dctx = ictx->dcd_head, *d_head; + struct sb_cmd_ctx *cctx, *c_head; + + while (sctx) { + s_head = sctx; + c_head = sctx->cmd_head; + + while (c_head) { + cctx = c_head; + c_head = c_head->cmd; + if (cctx->data) + free(cctx->data); + free(cctx); + } + + sctx = sctx->sect; + free(s_head); + } + + while (dctx) { + d_head = dctx; + dctx = dctx->dcd; + free(d_head->payload); + free(d_head); + } +} + +/* + * MXSSB-MKIMAGE glue code. + */ +static int mxsimage_check_image_types(uint8_t type) +{ + if (type == IH_TYPE_MXSIMAGE) + return EXIT_SUCCESS; + else + return EXIT_FAILURE; +} + +static void mxsimage_set_header(void *ptr, struct stat *sbuf, int ifd, + struct image_tool_params *params) +{ +} + +int mxsimage_check_params(struct image_tool_params *params) +{ + if (!params) + return -1; + if (!strlen(params->imagename)) { + fprintf(stderr, + "Error: %s - Configuration file not specified, it is needed for mxsimage generation\n", + params->cmdname); + return -1; + } + + /* + * Check parameters: + * XIP is not allowed and verify that incompatible + * parameters are not sent at the same time + * For example, if list is required a data image must not be provided + */ + return (params->dflag && (params->fflag || params->lflag)) || + (params->fflag && (params->dflag || params->lflag)) || + (params->lflag && (params->dflag || params->fflag)) || + (params->xflag) || !(strlen(params->imagename)); +} + +static int mxsimage_verify_print_header(char *file, int silent) +{ + int ret; + struct sb_image_ctx ctx; + + memset(&ctx, 0, sizeof(ctx)); + + ctx.input_filename = file; + ctx.silent_dump = silent; + + ret = sb_build_tree_from_img(&ctx); + sb_free_image(&ctx); + + return ret; +} + +char *imagefile; +static int mxsimage_verify_header(unsigned char *ptr, int image_size, + struct image_tool_params *params) +{ + struct sb_boot_image_header *hdr; + + if (!ptr) + return -EINVAL; + + hdr = (struct sb_boot_image_header *)ptr; + + /* + * Check if the header contains the MXS image signatures, + * if so, do a full-image verification. + */ + if (memcmp(hdr->signature1, "STMP", 4) || + memcmp(hdr->signature2, "sgtl", 4)) + return -EINVAL; + + imagefile = params->imagefile; + + return mxsimage_verify_print_header(params->imagefile, 1); +} + +static void mxsimage_print_header(const void *hdr) +{ + if (imagefile) + mxsimage_verify_print_header(imagefile, 0); +} + +static int sb_build_image(struct sb_image_ctx *ictx, + struct image_type_params *tparams) +{ + struct sb_boot_image_header *sb_header = &ictx->payload; + struct sb_section_ctx *sctx; + struct sb_cmd_ctx *cctx; + struct sb_command *ccmd; + struct sb_key_dictionary_key *sb_dict_key = &ictx->sb_dict_key; + + uint8_t *image, *iptr; + + /* Calculate image size. */ + uint32_t size = sizeof(*sb_header) + + ictx->sect_count * sizeof(struct sb_sections_header) + + sizeof(*sb_dict_key) + sizeof(ictx->digest); + + sctx = ictx->sect_head; + while (sctx) { + size += sctx->size; + sctx = sctx->sect; + }; + + image = malloc(size); + if (!image) + return -ENOMEM; + iptr = image; + + memcpy(iptr, sb_header, sizeof(*sb_header)); + iptr += sizeof(*sb_header); + + sctx = ictx->sect_head; + while (sctx) { + memcpy(iptr, &sctx->payload, sizeof(struct sb_sections_header)); + iptr += sizeof(struct sb_sections_header); + sctx = sctx->sect; + }; + + memcpy(iptr, sb_dict_key, sizeof(*sb_dict_key)); + iptr += sizeof(*sb_dict_key); + + sctx = ictx->sect_head; + while (sctx) { + cctx = sctx->cmd_head; + while (cctx) { + ccmd = &cctx->payload; + + memcpy(iptr, &cctx->c_payload, sizeof(cctx->payload)); + iptr += sizeof(cctx->payload); + + if (ccmd->header.tag == ROM_LOAD_CMD) { + memcpy(iptr, cctx->data, cctx->length); + iptr += cctx->length; + } + + cctx = cctx->cmd; + } + + sctx = sctx->sect; + }; + + memcpy(iptr, ictx->digest, sizeof(ictx->digest)); + iptr += sizeof(ictx->digest); + + /* Configure the mkimage */ + tparams->hdr = image; + tparams->header_size = size; + + return 0; +} + +static int mxsimage_generate(struct image_tool_params *params, + struct image_type_params *tparams) +{ + int ret; + struct sb_image_ctx ctx; + + /* Do not copy the U-Boot image! */ + params->skipcpy = 1; + + memset(&ctx, 0, sizeof(ctx)); + + ctx.cfg_filename = params->imagename; + ctx.output_filename = params->imagefile; + + ret = sb_build_tree_from_cfg(&ctx); + if (ret) + goto fail; + + ret = sb_encrypt_image(&ctx); + if (!ret) + ret = sb_build_image(&ctx, tparams); + +fail: + sb_free_image(&ctx); + + return ret; +} + +/* + * mxsimage parameters + */ +U_BOOT_IMAGE_TYPE( + mxsimage, + "Freescale MXS Boot Image support", + 0, + NULL, + mxsimage_check_params, + mxsimage_verify_header, + mxsimage_print_header, + mxsimage_set_header, + NULL, + mxsimage_check_image_types, + NULL, + mxsimage_generate +); +#endif diff --git a/tools/u-boot-tools/mxsimage.h b/tools/u-boot-tools/mxsimage.h new file mode 100644 index 0000000..a6a45a4 --- /dev/null +++ b/tools/u-boot-tools/mxsimage.h @@ -0,0 +1,230 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Freescale i.MX28 SB image generator + * + * Copyright (C) 2012 Marek Vasut <marex@denx.de> + */ + +#ifndef __MXSSB_H__ +#define __MXSSB_H__ + +#include <stdint.h> +#include <arpa/inet.h> + +#define SB_BLOCK_SIZE 16 + +#define roundup(x, y) ((((x) + ((y) - 1)) / (y)) * (y)) +#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) + +struct sb_boot_image_version { + uint16_t major; + uint16_t pad0; + uint16_t minor; + uint16_t pad1; + uint16_t revision; + uint16_t pad2; +}; + +struct sb_boot_image_header { + union { + /* SHA1 of the header. */ + uint8_t digest[20]; + struct { + /* CBC-MAC initialization vector. */ + uint8_t iv[16]; + uint8_t extra[4]; + }; + }; + /* 'STMP' */ + uint8_t signature1[4]; + /* Major version of the image format. */ + uint8_t major_version; + /* Minor version of the image format. */ + uint8_t minor_version; + /* Flags associated with the image. */ + uint16_t flags; + /* Size of the image in 16b blocks. */ + uint32_t image_blocks; + /* Offset of the first tag in 16b blocks. */ + uint32_t first_boot_tag_block; + /* ID of the section to boot from. */ + uint32_t first_boot_section_id; + /* Amount of crypto keys. */ + uint16_t key_count; + /* Offset to the key dictionary in 16b blocks. */ + uint16_t key_dictionary_block; + /* Size of this header in 16b blocks. */ + uint16_t header_blocks; + /* Amount of section headers. */ + uint16_t section_count; + /* Section header size in 16b blocks. */ + uint16_t section_header_size; + /* Padding to align timestamp to uint64_t. */ + uint8_t padding0[2]; + /* 'sgtl' (since v1.1) */ + uint8_t signature2[4]; + /* Image generation date, in microseconds since 1.1.2000 . */ + uint64_t timestamp_us; + /* Product version. */ + struct sb_boot_image_version + product_version; + /* Component version. */ + struct sb_boot_image_version + component_version; + /* Drive tag for the system drive. (since v1.1) */ + uint16_t drive_tag; + /* Padding. */ + uint8_t padding1[6]; +}; + +#define SB_VERSION_MAJOR 1 +#define SB_VERSION_MINOR 1 + +/* Enable to HTLLC boot report. */ +#define SB_IMAGE_FLAG_DISPLAY_PROGRESS (1 << 0) +#define SB_IMAGE_FLAGS_MASK SB_IMAGE_FLAG_DISPLAY_PROGRESS + +struct sb_key_dictionary_key { + /* The CBC-MAC of image and sections header. */ + uint8_t cbc_mac[SB_BLOCK_SIZE]; + /* The AES key encrypted by image key (zero). */ + uint8_t key[SB_BLOCK_SIZE]; +}; + +struct sb_ivt_header { + uint32_t header; + uint32_t entry; + uint32_t reserved1; + uint32_t dcd; + uint32_t boot_data; + uint32_t self; + uint32_t csf; + uint32_t reserved2; +}; + +#define SB_HAB_IVT_TAG 0xd1UL +#define SB_HAB_DCD_TAG 0xd2UL + +#define SB_HAB_VERSION 0x40UL + +/* + * The "size" field in the IVT header is not naturally aligned, + * use this macro to fill first 4 bytes of the IVT header without + * causing issues on some systems (esp. M68k, PPC, MIPS-BE, ARM-BE). + */ +static inline uint32_t sb_hab_ivt_header(void) +{ + uint32_t ret = 0; + ret |= SB_HAB_IVT_TAG << 24; + ret |= sizeof(struct sb_ivt_header) << 16; + ret |= SB_HAB_VERSION; + return htonl(ret); +} + +struct sb_sections_header { + /* Section number. */ + uint32_t section_number; + /* Offset of this sections first instruction after "TAG". */ + uint32_t section_offset; + /* Size of the section in 16b blocks. */ + uint32_t section_size; + /* Section flags. */ + uint32_t section_flags; +}; + +#define SB_SECTION_FLAG_BOOTABLE (1 << 0) + +struct sb_command { + struct { + uint8_t checksum; + uint8_t tag; + uint16_t flags; +#define ROM_TAG_CMD_FLAG_ROM_LAST_TAG 0x1 +#define ROM_LOAD_CMD_FLAG_DCD_LOAD 0x1 /* MX28 only */ +#define ROM_JUMP_CMD_FLAG_HAB 0x1 /* MX28 only */ +#define ROM_CALL_CMD_FLAG_HAB 0x1 /* MX28 only */ + } header; + + union { + struct { + uint32_t reserved[3]; + } nop; + struct { + uint32_t section_number; + uint32_t section_length; + uint32_t section_flags; + } tag; + struct { + uint32_t address; + uint32_t count; + uint32_t crc32; + } load; + struct { + uint32_t address; + uint32_t count; + uint32_t pattern; + } fill; + struct { + uint32_t address; + uint32_t reserved; + /* Passed in register r0 before JUMP */ + uint32_t argument; + } jump; + struct { + uint32_t address; + uint32_t reserved; + /* Passed in register r0 before CALL */ + uint32_t argument; + } call; + struct { + uint32_t reserved1; + uint32_t reserved2; + uint32_t mode; + } mode; + + }; +}; + +/* + * Most of the mode names are same or at least similar + * on i.MX23 and i.MX28, but some of the mode names + * differ. The "name" field represents the mode name + * on i.MX28 as seen in Table 12-2 of the datasheet. + * The "altname" field represents the differently named + * fields on i.MX23 as seen in Table 35-3 of the + * datasheet. + */ +static const struct { + const char *name; + const char *altname; + const uint8_t mode; +} modetable[] = { + { "USB", NULL, 0x00 }, + { "I2C", NULL, 0x01 }, + { "SPI2_FLASH", "SPI1_FLASH", 0x02 }, + { "SPI3_FLASH", "SPI2_FLASH", 0x03 }, + { "NAND_BCH", NULL, 0x04 }, + { "JTAG", NULL, 0x06 }, + { "SPI3_EEPROM", "SPI2_EEPROM", 0x08 }, + { "SD_SSP0", NULL, 0x09 }, + { "SD_SSP1", NULL, 0x0A } +}; + +enum sb_tag { + ROM_NOP_CMD = 0x00, + ROM_TAG_CMD = 0x01, + ROM_LOAD_CMD = 0x02, + ROM_FILL_CMD = 0x03, + ROM_JUMP_CMD = 0x04, + ROM_CALL_CMD = 0x05, + ROM_MODE_CMD = 0x06 +}; + +struct sb_source_entry { + uint8_t tag; + uint32_t address; + uint32_t flags; + char *filename; +}; + +#endif /* __MXSSB_H__ */ diff --git a/tools/u-boot-tools/mxsimage.o b/tools/u-boot-tools/mxsimage.o new file mode 100644 index 0000000000000000000000000000000000000000..398e95efc88ebdc2654e5cca81165bc685222ae5 GIT binary patch literal 928 zcmb<-^>JfjWMqH=Mg}_u1P><4z>t6>=l~XWVBlonU|?`}cD7Q`a7j(dOw3cT&@<LE z&^6P9Fu=-i5Fq1N7(V{TCe4i11q=+#3{2RRGcYqS;}B=XA<lv+o?B6znVXoNs+Y{b zpjTX(TauW>pjTW{1fervtdi7<5(d4L#F9h?y`<t|2EF9`+}zZ>5(d4z{E}2XcfU~G z;*!MVY)lKt6Om?M0EY$}ns!JWpb0|NB8w_Q#n3~DSpDh@44_!X2w`IN>p<-{Kz0-Z z0|QLIESfkdU4VpOv?)|SNDLXrB6E<~Fnur~5FcBrNq{O?0j1d)7#Kj91Im|x(wq#S obO+@iQ5+zH85kINki<Yts4$2L#)3HXi$E2+f>j|1s4!dr0G(_p)Bpeg literal 0 HcmV?d00001 diff --git a/tools/u-boot-tools/ncb.c b/tools/u-boot-tools/ncb.c new file mode 100644 index 0000000..ec8d8a7 --- /dev/null +++ b/tools/u-boot-tools/ncb.c @@ -0,0 +1,38 @@ +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <sys/socket.h> +#include <netinet/in.h> + +int main (int argc, char *argv[]) +{ + int s, len, o, port = 6666; + char buf[512]; + struct sockaddr_in addr; + socklen_t addr_len = sizeof addr; + + if (argc > 1) + port = atoi (argv[1]); + + s = socket (PF_INET, SOCK_DGRAM, IPPROTO_UDP); + + o = 1; + len = 4; + setsockopt (3, SOL_SOCKET, SO_REUSEADDR, &o, len); + + addr.sin_family = AF_INET; + addr.sin_port = htons (port); + addr.sin_addr.s_addr = INADDR_ANY; /* receive broadcasts */ + + bind (s, (struct sockaddr *) &addr, sizeof addr); + + for (;;) { + len = recvfrom (s, buf, sizeof buf, 0, (struct sockaddr *) &addr, &addr_len); + if (len < 0) + break; + if (write (1, buf, len) != len) + fprintf(stderr, "WARNING: serial characters dropped\n"); + } + + return 0; +} diff --git a/tools/u-boot-tools/netconsole b/tools/u-boot-tools/netconsole new file mode 100755 index 0000000..1a0ef22 --- /dev/null +++ b/tools/u-boot-tools/netconsole @@ -0,0 +1,63 @@ +#!/bin/sh + +usage() { + ( + echo "Usage: $0 <board-IP> [board-port [board-in-port]]" + echo "" + echo "If port is not specified, '6666' will be used" + [ -z "$*" ] && exit 0 + echo "" + echo "ERROR: $*" + exit 1 + ) 1>&2 + exit $? +} + +while [ -n "$1" ] ; do + case $1 in + -h|--help) usage;; + --) break;; + -*) usage "Invalid option $1";; + *) break;; + esac + shift +done + +ip=$1 +board_out_port=${2:-6666} +board_in_port=${3:-${board_out_port}} + +echo Board out port: ${board_out_port} +echo Board in port: ${board_in_port} + +if [ -z "${ip}" ] || [ -n "$4" ] ; then + usage "Invalid number of arguments" +fi + +for nc in netcat nc ; do + type ${nc} >/dev/null 2>&1 && break +done + +trap "stty icanon echo intr ^C" 0 2 3 5 10 13 15 +echo "NOTE: the interrupt signal (normally ^C) has been remapped to ^T" + +stty -icanon -echo intr ^T +( +if type ncb 2>/dev/null ; then + # see if ncb is in $PATH + exec ncb ${board_out_port} + +elif [ -x ${0%/*}/ncb ] ; then + # maybe it's in the same dir as the netconsole script + exec ${0%/*}/ncb ${board_out_port} + +else + # blah, just use regular netcat + while ${nc} -u -l -p ${board_out_port} < /dev/null ; do + : + done +fi +) & +pid=$! +${nc} -u ${ip} ${board_in_port} +kill ${pid} 2>/dev/null diff --git a/tools/u-boot-tools/omap/clocks_get_m_n.c b/tools/u-boot-tools/omap/clocks_get_m_n.c new file mode 100644 index 0000000..e21b0e2 --- /dev/null +++ b/tools/u-boot-tools/omap/clocks_get_m_n.c @@ -0,0 +1,185 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Program for finding M & N values for DPLLs + * To be run on Host PC + * + * (C) Copyright 2010 + * Texas Instruments, <www.ti.com> + * + * Aneesh V <aneesh@ti.com> + */ +#include <stdlib.h> +#include <stdio.h> +typedef unsigned int u32; +#define MAX_N 127 + +/* + * get_m_n_optimized() - Finds optimal DPLL multiplier(M) and divider(N) + * values based on the reference frequency, required output frequency, + * maximum tolerance for output frequency etc. + * + * target_freq_khz - output frequency required in KHz + * ref_freq_khz - reference(input) frequency in KHz + * m - pointer to computed M value + * n - pointer to computed N value + * tolerance_khz - tolerance for the output frequency. When the algorithm + * succeeds in finding vialble M and N values the corresponding output + * frequency will be in the range: + * [target_freq_khz - tolerance_khz, target_freq_khz] + * + * Formula: + * Fdpll = (2 * M * Fref) / (N + 1) + * + * Considerations for lock-time: + * - Smaller the N, better lock-time, especially lock-time will be + * - For acceptable lock-times: + * Fref / (M + 1) >= 1 MHz + * + * Considerations for power: + * - The difference in power for different N values giving the same + * output is negligible. So, we optimize for lock-time + * + * Hard-constraints: + * - N can not be greater than 127(7 bit field for representing N) + * + * Usage: + * $ gcc clocks_get_m_n.c + * $ ./a.out + */ +int get_m_n_optimized(u32 target_freq_khz, u32 ref_freq_khz, u32 *M, u32 *N) +{ + u32 freq = target_freq_khz; + u32 m_optimal, n_optimal, freq_optimal = 0, freq_old; + u32 m, n; + n = 1; + while (1) { + m = target_freq_khz / ref_freq_khz / 2 * n; + freq_old = 0; + while (1) { + freq = ref_freq_khz * 2 * m / n; + if (freq > target_freq_khz) { + freq = freq_old; + m--; + break; + } + m++; + freq_old = freq; + } + if (freq > freq_optimal) { + freq_optimal = freq; + m_optimal = m; + n_optimal = n; + } + n++; + if ((freq_optimal == target_freq_khz) || + ((ref_freq_khz / n) < 1000)) { + break; + } + } + n--; + *M = m_optimal; + *N = n_optimal - 1; + printf("ref %d m %d n %d target %d locked %d\n", ref_freq_khz, + m_optimal, n_optimal - 1, target_freq_khz, freq_optimal); + return 0; +} + +void main(void) +{ + u32 m, n; + printf("\nMPU - 2000000\n"); + get_m_n_optimized(2000000, 12000, &m, &n); + get_m_n_optimized(2000000, 13000, &m, &n); + get_m_n_optimized(2000000, 16800, &m, &n); + get_m_n_optimized(2000000, 19200, &m, &n); + get_m_n_optimized(2000000, 26000, &m, &n); + get_m_n_optimized(2000000, 27000, &m, &n); + get_m_n_optimized(2000000, 38400, &m, &n); + + printf("\nMPU - 1200000\n"); + get_m_n_optimized(1200000, 12000, &m, &n); + get_m_n_optimized(1200000, 13000, &m, &n); + get_m_n_optimized(1200000, 16800, &m, &n); + get_m_n_optimized(1200000, 19200, &m, &n); + get_m_n_optimized(1200000, 26000, &m, &n); + get_m_n_optimized(1200000, 27000, &m, &n); + get_m_n_optimized(1200000, 38400, &m, &n); + + printf("\nMPU - 1584000\n"); + get_m_n_optimized(1584000, 12000, &m, &n); + get_m_n_optimized(1584000, 13000, &m, &n); + get_m_n_optimized(1584000, 16800, &m, &n); + get_m_n_optimized(1584000, 19200, &m, &n); + get_m_n_optimized(1584000, 26000, &m, &n); + get_m_n_optimized(1584000, 27000, &m, &n); + get_m_n_optimized(1584000, 38400, &m, &n); + + printf("\nCore 1600000\n"); + get_m_n_optimized(1600000, 12000, &m, &n); + get_m_n_optimized(1600000, 13000, &m, &n); + get_m_n_optimized(1600000, 16800, &m, &n); + get_m_n_optimized(1600000, 19200, &m, &n); + get_m_n_optimized(1600000, 26000, &m, &n); + get_m_n_optimized(1600000, 27000, &m, &n); + get_m_n_optimized(1600000, 38400, &m, &n); + + printf("\nPER 1536000\n"); + get_m_n_optimized(1536000, 12000, &m, &n); + get_m_n_optimized(1536000, 13000, &m, &n); + get_m_n_optimized(1536000, 16800, &m, &n); + get_m_n_optimized(1536000, 19200, &m, &n); + get_m_n_optimized(1536000, 26000, &m, &n); + get_m_n_optimized(1536000, 27000, &m, &n); + get_m_n_optimized(1536000, 38400, &m, &n); + + printf("\nIVA 1862000\n"); + get_m_n_optimized(1862000, 12000, &m, &n); + get_m_n_optimized(1862000, 13000, &m, &n); + get_m_n_optimized(1862000, 16800, &m, &n); + get_m_n_optimized(1862000, 19200, &m, &n); + get_m_n_optimized(1862000, 26000, &m, &n); + get_m_n_optimized(1862000, 27000, &m, &n); + get_m_n_optimized(1862000, 38400, &m, &n); + + printf("\nIVA Nitro - 1290000\n"); + get_m_n_optimized(1290000, 12000, &m, &n); + get_m_n_optimized(1290000, 13000, &m, &n); + get_m_n_optimized(1290000, 16800, &m, &n); + get_m_n_optimized(1290000, 19200, &m, &n); + get_m_n_optimized(1290000, 26000, &m, &n); + get_m_n_optimized(1290000, 27000, &m, &n); + get_m_n_optimized(1290000, 38400, &m, &n); + + printf("\nABE 196608 sys clk\n"); + get_m_n_optimized(196608, 12000, &m, &n); + get_m_n_optimized(196608, 13000, &m, &n); + get_m_n_optimized(196608, 16800, &m, &n); + get_m_n_optimized(196608, 19200, &m, &n); + get_m_n_optimized(196608, 26000, &m, &n); + get_m_n_optimized(196608, 27000, &m, &n); + get_m_n_optimized(196608, 38400, &m, &n); + + printf("\nABE 196608 32K\n"); + get_m_n_optimized(196608000/4, 32768, &m, &n); + + printf("\nUSB 1920000\n"); + get_m_n_optimized(1920000, 12000, &m, &n); + get_m_n_optimized(1920000, 13000, &m, &n); + get_m_n_optimized(1920000, 16800, &m, &n); + get_m_n_optimized(1920000, 19200, &m, &n); + get_m_n_optimized(1920000, 26000, &m, &n); + get_m_n_optimized(1920000, 27000, &m, &n); + get_m_n_optimized(1920000, 38400, &m, &n); + + printf("\nCore ES1 1523712\n"); + get_m_n_optimized(1524000, 12000, &m, &n); + get_m_n_optimized(1524000, 13000, &m, &n); + get_m_n_optimized(1524000, 16800, &m, &n); + get_m_n_optimized(1524000, 19200, &m, &n); + get_m_n_optimized(1524000, 26000, &m, &n); + get_m_n_optimized(1524000, 27000, &m, &n); + + /* exact recommendation for SDPs */ + get_m_n_optimized(1523712, 38400, &m, &n); + +} diff --git a/tools/u-boot-tools/omapimage.c b/tools/u-boot-tools/omapimage.c new file mode 100644 index 0000000..c59cdcc --- /dev/null +++ b/tools/u-boot-tools/omapimage.c @@ -0,0 +1,181 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2010 + * Linaro LTD, www.linaro.org + * Author: John Rigby <john.rigby@linaro.org> + * Based on TI's signGP.c + * + * (C) Copyright 2009 + * Stefano Babic, DENX Software Engineering, sbabic@denx.de. + * + * (C) Copyright 2008 + * Marvell Semiconductor <www.marvell.com> + * Written-by: Prafulla Wadaskar <prafulla@marvell.com> + */ + +#include "imagetool.h" +#include <compiler.h> +#include <image.h> +#include "gpheader.h" +#include "omapimage.h" + +#define DIV_ROUND_UP(n, d) (((n) + (d) - 1) / (d)) + +/* Header size is CH header rounded up to 512 bytes plus GP header */ +#define OMAP_CH_HDR_SIZE 512 +#define OMAP_FILE_HDR_SIZE (OMAP_CH_HDR_SIZE + GPIMAGE_HDR_SIZE) + +static int do_swap32 = 0; + +static uint8_t omapimage_header[OMAP_FILE_HDR_SIZE]; + +static int omapimage_check_image_types(uint8_t type) +{ + if (type == IH_TYPE_OMAPIMAGE) + return EXIT_SUCCESS; + return EXIT_FAILURE; +} + +static int omapimage_verify_header(unsigned char *ptr, int image_size, + struct image_tool_params *params) +{ + struct ch_toc *toc = (struct ch_toc *)ptr; + struct gp_header *gph = (struct gp_header *)(ptr+OMAP_CH_HDR_SIZE); + uint32_t offset, size; + + while (toc->section_offset != 0xffffffff + && toc->section_size != 0xffffffff) { + if (do_swap32) { + offset = cpu_to_be32(toc->section_offset); + size = cpu_to_be32(toc->section_size); + } else { + offset = toc->section_offset; + size = toc->section_size; + } + if (!offset || !size) + return -1; + if (offset >= OMAP_CH_HDR_SIZE || + offset+size >= OMAP_CH_HDR_SIZE) + return -1; + toc++; + } + + return gph_verify_header(gph, do_swap32); +} + +static void omapimage_print_section(struct ch_settings *chs) +{ + const char *section_name; + + if (chs->section_key) + section_name = "CHSETTINGS"; + else + section_name = "UNKNOWNKEY"; + + printf("%s (%x) " + "valid:%x " + "version:%x " + "reserved:%x " + "flags:%x\n", + section_name, + chs->section_key, + chs->valid, + chs->version, + chs->reserved, + chs->flags); +} + +static void omapimage_print_header(const void *ptr) +{ + const struct ch_toc *toc = (struct ch_toc *)ptr; + const struct gp_header *gph = + (struct gp_header *)(ptr+OMAP_CH_HDR_SIZE); + uint32_t offset, size; + + while (toc->section_offset != 0xffffffff + && toc->section_size != 0xffffffff) { + if (do_swap32) { + offset = cpu_to_be32(toc->section_offset); + size = cpu_to_be32(toc->section_size); + } else { + offset = toc->section_offset; + size = toc->section_size; + } + + if (offset >= OMAP_CH_HDR_SIZE || + offset+size >= OMAP_CH_HDR_SIZE) + exit(EXIT_FAILURE); + + printf("Section %s offset %x length %x\n", + toc->section_name, + toc->section_offset, + toc->section_size); + + omapimage_print_section((struct ch_settings *)(ptr+offset)); + toc++; + } + + gph_print_header(gph, do_swap32); +} + +static int toc_offset(void *hdr, void *member) +{ + return member - hdr; +} + +static void omapimage_set_header(void *ptr, struct stat *sbuf, int ifd, + struct image_tool_params *params) +{ + struct ch_toc *toc = (struct ch_toc *)ptr; + struct ch_settings *chs = (struct ch_settings *) + (ptr + 2 * sizeof(*toc)); + struct gp_header *gph = (struct gp_header *)(ptr + OMAP_CH_HDR_SIZE); + + toc->section_offset = toc_offset(ptr, chs); + toc->section_size = sizeof(struct ch_settings); + strcpy((char *)toc->section_name, "CHSETTINGS"); + + chs->section_key = KEY_CHSETTINGS; + chs->valid = 0; + chs->version = 1; + chs->reserved = 0; + chs->flags = 0; + + toc++; + memset(toc, 0xff, sizeof(*toc)); + + gph_set_header(gph, sbuf->st_size - OMAP_CH_HDR_SIZE, + params->addr, 0); + + if (strncmp(params->imagename, "byteswap", 8) == 0) { + do_swap32 = 1; + int swapped = 0; + uint32_t *data = (uint32_t *)ptr; + const off_t size_in_words = + DIV_ROUND_UP(sbuf->st_size, sizeof(uint32_t)); + + while (swapped < size_in_words) { + *data = cpu_to_be32(*data); + swapped++; + data++; + } + } +} + +/* + * omapimage parameters + */ +U_BOOT_IMAGE_TYPE( + omapimage, + "TI OMAP CH/GP Boot Image support", + OMAP_FILE_HDR_SIZE, + (void *)&omapimage_header, + gpimage_check_params, + omapimage_verify_header, + omapimage_print_header, + omapimage_set_header, + NULL, + omapimage_check_image_types, + NULL, + NULL +); diff --git a/tools/u-boot-tools/omapimage.h b/tools/u-boot-tools/omapimage.h new file mode 100644 index 0000000..175fac2 --- /dev/null +++ b/tools/u-boot-tools/omapimage.h @@ -0,0 +1,28 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * (C) Copyright 2010 + * Linaro LTD, www.linaro.org + * Author John Rigby <john.rigby@linaro.org> + * Based on TI's signGP.c + */ + +#ifndef _OMAPIMAGE_H_ +#define _OMAPIMAGE_H_ + +struct ch_toc { + uint32_t section_offset; + uint32_t section_size; + uint8_t unused[12]; + uint8_t section_name[12]; +}; + +struct ch_settings { + uint32_t section_key; + uint8_t valid; + uint8_t version; + uint16_t reserved; + uint32_t flags; +}; + +#define KEY_CHSETTINGS 0xC0C0C0C1 +#endif /* _OMAPIMAGE_H_ */ diff --git a/tools/u-boot-tools/omapimage.o b/tools/u-boot-tools/omapimage.o new file mode 100644 index 0000000000000000000000000000000000000000..758d3398e9a9e521706993cdd503fd58a4ec281b GIT binary patch literal 4248 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 Kd>Dmh90LF_l4jWe literal 0 HcmV?d00001 diff --git a/tools/u-boot-tools/os_support.c b/tools/u-boot-tools/os_support.c new file mode 100644 index 0000000..21e43c8 --- /dev/null +++ b/tools/u-boot-tools/os_support.c @@ -0,0 +1,15 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2009 Extreme Engineering Solutions, Inc. + */ + +/* + * Include additional files required for supporting different operating systems + */ +#include "compiler.h" +#ifdef __MINGW32__ +#include "mingw_support.c" +#endif +#if defined(__APPLE__) && __DARWIN_C_LEVEL < 200809L +#include "getline.c" +#endif diff --git a/tools/u-boot-tools/os_support.h b/tools/u-boot-tools/os_support.h new file mode 100644 index 0000000..3a2106e --- /dev/null +++ b/tools/u-boot-tools/os_support.h @@ -0,0 +1,22 @@ +/* SPDX-License-Identifier: LGPL-2.0+ */ +/* + * Copyright 2009 Extreme Engineering Solutions, Inc. + */ + +#ifndef __OS_SUPPORT_H_ +#define __OS_SUPPORT_H_ + +#include "compiler.h" + +/* + * Include additional files required for supporting different operating systems + */ +#ifdef __MINGW32__ +#include "mingw_support.h" +#endif + +#if defined(__APPLE__) && __DARWIN_C_LEVEL < 200809L +#include "getline.h" +#endif + +#endif /* __OS_SUPPORT_H_ */ diff --git a/tools/u-boot-tools/os_support.o b/tools/u-boot-tools/os_support.o new file mode 100644 index 0000000000000000000000000000000000000000..e561a43ccc9c64a6749b20cc7d4000b7d56eec44 GIT binary patch literal 928 zcmb<-^>JfjWMqH=Mg}_u1P><4z>t6>=l~XWVBlonU|?`}cD7Q`a7j(dOw3cT&@<LE z&^6P9Fu=-i5Fq1N7(V{TCe4i11q=+#3{2RRGcYqS;}B=XA<lv+o?jeaTv||&UsR%( z%)p>mT$x*vn8cu0Tv7y~GhnQe)QS=Yy_Cd~L<YU2;$jB9<ow*+)VvY~y}bOAR6TdU zP~GB^#N=#DE65R%W?%q^1{<1YNF1OELe(ORDniB3Lx@=Y>I@8^SjGrpV)g4l?KePn z6axbTOusCeI4E6!gkZEOR6j@z8OI`Xkk~MNFd+~hTdGNbDp&!f*%=rZK$ru{mw?io q44`xe<seZUAcGkg82FIHKuoAGhzZ7mIP{A_6}o~|Aqc22TmS$Dgeuzr literal 0 HcmV?d00001 diff --git a/tools/u-boot-tools/patman/.gitignore b/tools/u-boot-tools/patman/.gitignore new file mode 100644 index 0000000..0d20b64 --- /dev/null +++ b/tools/u-boot-tools/patman/.gitignore @@ -0,0 +1 @@ +*.pyc diff --git a/tools/u-boot-tools/patman/README b/tools/u-boot-tools/patman/README new file mode 100644 index 0000000..7917fc8 --- /dev/null +++ b/tools/u-boot-tools/patman/README @@ -0,0 +1,497 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (c) 2011 The Chromium OS Authors. + +What is this? +============= + +This tool is a Python script which: +- Creates patch directly from your branch +- Cleans them up by removing unwanted tags +- Inserts a cover letter with change lists +- Runs the patches through checkpatch.pl and its own checks +- Optionally emails them out to selected people + +It is intended to automate patch creation and make it a less +error-prone process. It is useful for U-Boot and Linux work so far, +since it uses the checkpatch.pl script. + +It is configured almost entirely by tags it finds in your commits. +This means that you can work on a number of different branches at +once, and keep the settings with each branch rather than having to +git format-patch, git send-email, etc. with the correct parameters +each time. So for example if you put: + +Series-to: fred.blogs@napier.co.nz + +in one of your commits, the series will be sent there. + +In Linux and U-Boot this will also call get_maintainer.pl on each of your +patches automatically (unless you use -m to disable this). + + +How to use this tool +==================== + +This tool requires a certain way of working: + +- Maintain a number of branches, one for each patch series you are +working on +- Add tags into the commits within each branch to indicate where the +series should be sent, cover letter, version, etc. Most of these are +normally in the top commit so it is easy to change them with 'git +commit --amend' +- Each branch tracks the upstream branch, so that this script can +automatically determine the number of commits in it (optional) +- Check out a branch, and run this script to create and send out your +patches. Weeks later, change the patches and repeat, knowing that you +will get a consistent result each time. + + +How to configure it +=================== + +For most cases of using patman for U-Boot development, patman can use the +file 'doc/git-mailrc' in your U-Boot directory to supply the email aliases +you need. To make this work, tell git where to find the file by typing +this once: + + git config sendemail.aliasesfile doc/git-mailrc + +For both Linux and U-Boot the 'scripts/get_maintainer.pl' handles figuring +out where to send patches pretty well. + +During the first run patman creates a config file for you by taking the default +user name and email address from the global .gitconfig file. + +To add your own, create a file ~/.patman like this: + +>>>> +# patman alias file + +[alias] +me: Simon Glass <sjg@chromium.org> + +u-boot: U-Boot Mailing List <u-boot@lists.denx.de> +wolfgang: Wolfgang Denk <wd@denx.de> +others: Mike Frysinger <vapier@gentoo.org>, Fred Bloggs <f.bloggs@napier.net> + +<<<< + +Aliases are recursive. + +The checkpatch.pl in the U-Boot tools/ subdirectory will be located and +used. Failing that you can put it into your path or ~/bin/checkpatch.pl + +If you want to avoid sending patches to email addresses that are picked up +by patman but are known to bounce you can add a [bounces] section to your +.patman file. Unlike the [alias] section these are simple key: value pairs +that are not recursive. + +>>> + +[bounces] +gonefishing: Fred Bloggs <f.bloggs@napier.net> + +<<< + + +If you want to change the defaults for patman's command-line arguments, +you can add a [settings] section to your .patman file. This can be used +for any command line option by referring to the "dest" for the option in +patman.py. For reference, the useful ones (at the moment) shown below +(all with the non-default setting): + +>>> + +[settings] +ignore_errors: True +process_tags: False +verbose: True +smtp_server: /path/to/sendmail + +<<< + + +If you want to adjust settings (or aliases) that affect just a single +project you can add a section that looks like [project_settings] or +[project_alias]. If you want to use tags for your linux work, you could +do: + +>>> + +[linux_settings] +process_tags: True + +<<< + + +How to run it +============= + +First do a dry run: + +$ ./tools/patman/patman -n + +If it can't detect the upstream branch, try telling it how many patches +there are in your series: + +$ ./tools/patman/patman -n -c5 + +This will create patch files in your current directory and tell you who +it is thinking of sending them to. Take a look at the patch files. + +$ ./tools/patman/patman -n -c5 -s1 + +Similar to the above, but skip the first commit and take the next 5. This +is useful if your top commit is for setting up testing. + + +How to install it +================= + +The most up to date version of patman can be found in the U-Boot sources. +However to use it on other projects it may be more convenient to install it as +a standalone application. A distutils installer is included, this can be used +to install patman: + +$ cd tools/patman && python setup.py install + + +How to add tags +=============== + +To make this script useful you must add tags like the following into any +commit. Most can only appear once in the whole series. + +Series-to: email / alias + Email address / alias to send patch series to (you can add this + multiple times) + +Series-cc: email / alias, ... + Email address / alias to Cc patch series to (you can add this + multiple times) + +Series-version: n + Sets the version number of this patch series + +Series-prefix: prefix + Sets the subject prefix. Normally empty but it can be RFC for + RFC patches, or RESEND if you are being ignored. The patch subject + is like [RFC PATCH] or [RESEND PATCH]. + In the meantime, git format.subjectprefix option will be added as + well. If your format.subjectprefix is set to InternalProject, then + the patch shows like: [InternalProject][RFC/RESEND PATCH] + +Series-name: name + Sets the name of the series. You don't need to have a name, and + patman does not yet use it, but it is convenient to put the branch + name here to help you keep track of multiple upstreaming efforts. + +Cover-letter: +This is the patch set title +blah blah +more blah blah +END + Sets the cover letter contents for the series. The first line + will become the subject of the cover letter + +Cover-letter-cc: email / alias + Additional email addresses / aliases to send cover letter to (you + can add this multiple times) + +Series-notes: +blah blah +blah blah +more blah blah +END + Sets some notes for the patch series, which you don't want in + the commit messages, but do want to send, The notes are joined + together and put after the cover letter. Can appear multiple + times. + +Commit-notes: +blah blah +blah blah +more blah blah +END + Similar, but for a single commit (patch). These notes will appear + immediately below the --- cut in the patch file. + + Signed-off-by: Their Name <email> + A sign-off is added automatically to your patches (this is + probably a bug). If you put this tag in your patches, it will + override the default signoff that patman automatically adds. + Multiple duplicate signoffs will be removed. + + Tested-by: Their Name <email> + Reviewed-by: Their Name <email> + Acked-by: Their Name <email> + These indicate that someone has tested/reviewed/acked your patch. + When you get this reply on the mailing list, you can add this + tag to the relevant commit and the script will include it when + you send out the next version. If 'Tested-by:' is set to + yourself, it will be removed. No one will believe you. + +Series-changes: n +- Guinea pig moved into its cage +- Other changes ending with a blank line +<blank line> + This can appear in any commit. It lists the changes for a + particular version n of that commit. The change list is + created based on this information. Each commit gets its own + change list and also the whole thing is repeated in the cover + letter (where duplicate change lines are merged). + + By adding your change lists into your commits it is easier to + keep track of what happened. When you amend a commit, remember + to update the log there and then, knowing that the script will + do the rest. + +Patch-cc: Their Name <email> + This copies a single patch to another email address. Note that the + Cc: used by git send-email is ignored by patman, but will be + interpreted by git send-email if you use it. + +Series-process-log: sort, uniq + This tells patman to sort and/or uniq the change logs. It is + assumed that each change log entry is only a single line long. + Use 'sort' to sort the entries, and 'uniq' to include only + unique entries. If omitted, no change log processing is done. + Separate each tag with a comma. + +Various other tags are silently removed, like these Chrome OS and +Gerrit tags: + +BUG=... +TEST=... +Change-Id: +Review URL: +Reviewed-on: +Commit-xxxx: (except Commit-notes) + +Exercise for the reader: Try adding some tags to one of your current +patch series and see how the patches turn out. + + +Where Patches Are Sent +====================== + +Once the patches are created, patman sends them using git send-email. The +whole series is sent to the recipients in Series-to: and Series-cc. +You can Cc individual patches to other people with the Patch-cc: tag. Tags +in the subject are also picked up to Cc patches. For example, a commit like +this: + +>>>> +commit 10212537b85ff9b6e09c82045127522c0f0db981 +Author: Mike Frysinger <vapier@gentoo.org> +Date: Mon Nov 7 23:18:44 2011 -0500 + + x86: arm: add a git mailrc file for maintainers + + This should make sending out e-mails to the right people easier. + + Patch-cc: sandbox, mikef, ag + Patch-cc: afleming +<<<< + +will create a patch which is copied to x86, arm, sandbox, mikef, ag and +afleming. + +If you have a cover letter it will get sent to the union of the Patch-cc +lists of all of the other patches. If you want to sent it to additional +people you can add a tag: + +Cover-letter-cc: <list of addresses> + +These people will get the cover letter even if they are not on the To/Cc +list for any of the patches. + + +Example Work Flow +================= + +The basic workflow is to create your commits, add some tags to the top +commit, and type 'patman' to check and send them. + +Here is an example workflow for a series of 4 patches. Let's say you have +these rather contrived patches in the following order in branch us-cmd in +your tree where 'us' means your upstreaming activity (newest to oldest as +output by git log --oneline): + + 7c7909c wip + 89234f5 Don't include standard parser if hush is used + 8d640a7 mmc: sparc: Stop using builtin_run_command() + 0c859a9 Rename run_command2() to run_command() + a74443f sandbox: Rename run_command() to builtin_run_command() + +The first patch is some test things that enable your code to be compiled, +but that you don't want to submit because there is an existing patch for it +on the list. So you can tell patman to create and check some patches +(skipping the first patch) with: + + patman -s1 -n + +If you want to do all of them including the work-in-progress one, then +(if you are tracking an upstream branch): + + patman -n + +Let's say that patman reports an error in the second patch. Then: + + git rebase -i HEAD~6 + <change 'pick' to 'edit' in 89234f5> + <use editor to make code changes> + git add -u + git rebase --continue + +Now you have an updated patch series. To check it: + + patman -s1 -n + +Let's say it is now clean and you want to send it. Now you need to set up +the destination. So amend the top commit with: + + git commit --amend + +Use your editor to add some tags, so that the whole commit message is: + + The current run_command() is really only one of the options, with + hush providing the other. It really shouldn't be called directly + in case the hush parser is bring used, so rename this function to + better explain its purpose. + + Series-to: u-boot + Series-cc: bfin, marex + Series-prefix: RFC + Cover-letter: + Unified command execution in one place + + At present two parsers have similar code to execute commands. Also + cmd_usage() is called all over the place. This series adds a single + function which processes commands called cmd_process(). + END + + Change-Id: Ica71a14c1f0ecb5650f771a32fecb8d2eb9d8a17 + + +You want this to be an RFC and Cc the whole series to the bfin alias and +to Marek. Two of the patches have tags (those are the bits at the front of +the subject that say mmc: sparc: and sandbox:), so 8d640a7 will be Cc'd to +mmc and sparc, and the last one to sandbox. + +Now to send the patches, take off the -n flag: + + patman -s1 + +The patches will be created, shown in your editor, and then sent along with +the cover letter. Note that patman's tags are automatically removed so that +people on the list don't see your secret info. + +Of course patches often attract comments and you need to make some updates. +Let's say one person sent comments and you get an Acked-by: on one patch. +Also, the patch on the list that you were waiting for has been merged, +so you can drop your wip commit. So you resync with upstream: + + git fetch origin (or whatever upstream is called) + git rebase origin/master + +and use git rebase -i to edit the commits, dropping the wip one. You add +the ack tag to one commit: + + Acked-by: Heiko Schocher <hs@denx.de> + +update the Series-cc: in the top commit: + + Series-cc: bfin, marex, Heiko Schocher <hs@denx.de> + +and remove the Series-prefix: tag since it it isn't an RFC any more. The +series is now version two, so the series info in the top commit looks like +this: + + Series-to: u-boot + Series-cc: bfin, marex, Heiko Schocher <hs@denx.de> + Series-version: 2 + Cover-letter: + ... + +Finally, you need to add a change log to the two commits you changed. You +add change logs to each individual commit where the changes happened, like +this: + + Series-changes: 2 + - Updated the command decoder to reduce code size + - Wound the torque propounder up a little more + +(note the blank line at the end of the list) + +When you run patman it will collect all the change logs from the different +commits and combine them into the cover letter, if you have one. So finally +you have a new series of commits: + + faeb973 Don't include standard parser if hush is used + 1b2f2fe mmc: sparc: Stop using builtin_run_command() + cfbe330 Rename run_command2() to run_command() + 0682677 sandbox: Rename run_command() to builtin_run_command() + +so to send them: + + patman + +and it will create and send the version 2 series. + +General points: + +1. When you change back to the us-cmd branch days or weeks later all your +information is still there, safely stored in the commits. You don't need +to remember what version you are up to, who you sent the last lot of patches +to, or anything about the change logs. + +2. If you put tags in the subject, patman will Cc the maintainers +automatically in many cases. + +3. If you want to keep the commits from each series you sent so that you can +compare change and see what you did, you can either create a new branch for +each version, or just tag the branch before you start changing it: + + git tag sent/us-cmd-rfc + ...later... + git tag sent/us-cmd-v2 + +4. If you want to modify the patches a little before sending, you can do +this in your editor, but be careful! + +5. If you want to run git send-email yourself, use the -n flag which will +print out the command line patman would have used. + +6. It is a good idea to add the change log info as you change the commit, +not later when you can't remember which patch you changed. You can always +go back and change or remove logs from commits. + + +Other thoughts +============== + +This script has been split into sensible files but still needs work. +Most of these are indicated by a TODO in the code. + +It would be nice if this could handle the In-reply-to side of things. + +The tests are incomplete, as is customary. Use the --test flag to run them, +and make sure you are in the tools/patman directory first: + + $ cd /path/to/u-boot + $ cd tools/patman + $ ./patman --test + +Error handling doesn't always produce friendly error messages - e.g. +putting an incorrect tag in a commit may provide a confusing message. + +There might be a few other features not mentioned in this README. They +might be bugs. In particular, tags are case sensitive which is probably +a bad thing. + + +Simon Glass <sjg@chromium.org> +v1, v2, 19-Oct-11 +revised v3 24-Nov-11 diff --git a/tools/u-boot-tools/patman/__init__.py b/tools/u-boot-tools/patman/__init__.py new file mode 100644 index 0000000..7cbe5fa --- /dev/null +++ b/tools/u-boot-tools/patman/__init__.py @@ -0,0 +1,3 @@ +__all__ = ['checkpatch', 'command', 'commit', 'cros_subprocess', + 'get_maintainer', 'gitutil', 'patchstream', 'project', + 'series', 'settings', 'terminal', 'test'] diff --git a/tools/u-boot-tools/patman/checkpatch.py b/tools/u-boot-tools/patman/checkpatch.py new file mode 100644 index 0000000..d47ea43 --- /dev/null +++ b/tools/u-boot-tools/patman/checkpatch.py @@ -0,0 +1,174 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (c) 2011 The Chromium OS Authors. +# + +import collections +import command +import gitutil +import os +import re +import sys +import terminal + +def FindCheckPatch(): + top_level = gitutil.GetTopLevel() + try_list = [ + os.getcwd(), + os.path.join(os.getcwd(), '..', '..'), + os.path.join(top_level, 'tools'), + os.path.join(top_level, 'scripts'), + '%s/bin' % os.getenv('HOME'), + ] + # Look in current dir + for path in try_list: + fname = os.path.join(path, 'checkpatch.pl') + if os.path.isfile(fname): + return fname + + # Look upwwards for a Chrome OS tree + while not os.path.ismount(path): + fname = os.path.join(path, 'src', 'third_party', 'kernel', 'files', + 'scripts', 'checkpatch.pl') + if os.path.isfile(fname): + return fname + path = os.path.dirname(path) + + sys.exit('Cannot find checkpatch.pl - please put it in your ' + + '~/bin directory or use --no-check') + +def CheckPatch(fname, verbose=False): + """Run checkpatch.pl on a file. + + Returns: + namedtuple containing: + ok: False=failure, True=ok + problems: List of problems, each a dict: + 'type'; error or warning + 'msg': text message + 'file' : filename + 'line': line number + errors: Number of errors + warnings: Number of warnings + checks: Number of checks + lines: Number of lines + stdout: Full output of checkpatch + """ + fields = ['ok', 'problems', 'errors', 'warnings', 'checks', 'lines', + 'stdout'] + result = collections.namedtuple('CheckPatchResult', fields) + result.ok = False + result.errors, result.warning, result.checks = 0, 0, 0 + result.lines = 0 + result.problems = [] + chk = FindCheckPatch() + item = {} + result.stdout = command.Output(chk, '--no-tree', fname, + raise_on_error=False) + #pipe = subprocess.Popen(cmd, stdout=subprocess.PIPE) + #stdout, stderr = pipe.communicate() + + # total: 0 errors, 0 warnings, 159 lines checked + # or: + # total: 0 errors, 2 warnings, 7 checks, 473 lines checked + re_stats = re.compile('total: (\\d+) errors, (\d+) warnings, (\d+)') + re_stats_full = re.compile('total: (\\d+) errors, (\d+) warnings, (\d+)' + ' checks, (\d+)') + re_ok = re.compile('.*has no obvious style problems') + re_bad = re.compile('.*has style problems, please review') + re_error = re.compile('ERROR: (.*)') + re_warning = re.compile('WARNING: (.*)') + re_check = re.compile('CHECK: (.*)') + re_file = re.compile('#\d+: FILE: ([^:]*):(\d+):') + + for line in result.stdout.splitlines(): + if verbose: + print(line) + + # A blank line indicates the end of a message + if not line and item: + result.problems.append(item) + item = {} + match = re_stats_full.match(line) + if not match: + match = re_stats.match(line) + if match: + result.errors = int(match.group(1)) + result.warnings = int(match.group(2)) + if len(match.groups()) == 4: + result.checks = int(match.group(3)) + result.lines = int(match.group(4)) + else: + result.lines = int(match.group(3)) + elif re_ok.match(line): + result.ok = True + elif re_bad.match(line): + result.ok = False + err_match = re_error.match(line) + warn_match = re_warning.match(line) + file_match = re_file.match(line) + check_match = re_check.match(line) + if err_match: + item['msg'] = err_match.group(1) + item['type'] = 'error' + elif warn_match: + item['msg'] = warn_match.group(1) + item['type'] = 'warning' + elif check_match: + item['msg'] = check_match.group(1) + item['type'] = 'check' + elif file_match: + item['file'] = file_match.group(1) + item['line'] = int(file_match.group(2)) + + return result + +def GetWarningMsg(col, msg_type, fname, line, msg): + '''Create a message for a given file/line + + Args: + msg_type: Message type ('error' or 'warning') + fname: Filename which reports the problem + line: Line number where it was noticed + msg: Message to report + ''' + if msg_type == 'warning': + msg_type = col.Color(col.YELLOW, msg_type) + elif msg_type == 'error': + msg_type = col.Color(col.RED, msg_type) + elif msg_type == 'check': + msg_type = col.Color(col.MAGENTA, msg_type) + return '%s:%d: %s: %s\n' % (fname, line, msg_type, msg) + +def CheckPatches(verbose, args): + '''Run the checkpatch.pl script on each patch''' + error_count, warning_count, check_count = 0, 0, 0 + col = terminal.Color() + + for fname in args: + result = CheckPatch(fname, verbose) + if not result.ok: + error_count += result.errors + warning_count += result.warnings + check_count += result.checks + print('%d errors, %d warnings, %d checks for %s:' % (result.errors, + result.warnings, result.checks, col.Color(col.BLUE, fname))) + if (len(result.problems) != result.errors + result.warnings + + result.checks): + print("Internal error: some problems lost") + for item in result.problems: + sys.stderr.write( + GetWarningMsg(col, item.get('type', '<unknown>'), + item.get('file', '<unknown>'), + item.get('line', 0), item.get('msg', 'message'))) + print + #print(stdout) + if error_count or warning_count or check_count: + str = 'checkpatch.pl found %d error(s), %d warning(s), %d checks(s)' + color = col.GREEN + if warning_count: + color = col.YELLOW + if error_count: + color = col.RED + print(col.Color(color, str % (error_count, warning_count, check_count))) + return False + return True diff --git a/tools/u-boot-tools/patman/command.py b/tools/u-boot-tools/patman/command.py new file mode 100644 index 0000000..14edcda --- /dev/null +++ b/tools/u-boot-tools/patman/command.py @@ -0,0 +1,127 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (c) 2011 The Chromium OS Authors. +# + +import os +import cros_subprocess + +"""Shell command ease-ups for Python.""" + +class CommandResult: + """A class which captures the result of executing a command. + + Members: + stdout: stdout obtained from command, as a string + stderr: stderr obtained from command, as a string + return_code: Return code from command + exception: Exception received, or None if all ok + """ + def __init__(self): + self.stdout = None + self.stderr = None + self.combined = None + self.return_code = None + self.exception = None + + def __init__(self, stdout='', stderr='', combined='', return_code=0, + exception=None): + self.stdout = stdout + self.stderr = stderr + self.combined = combined + self.return_code = return_code + self.exception = exception + + +# This permits interception of RunPipe for test purposes. If it is set to +# a function, then that function is called with the pipe list being +# executed. Otherwise, it is assumed to be a CommandResult object, and is +# returned as the result for every RunPipe() call. +# When this value is None, commands are executed as normal. +test_result = None + +def RunPipe(pipe_list, infile=None, outfile=None, + capture=False, capture_stderr=False, oneline=False, + raise_on_error=True, cwd=None, **kwargs): + """ + Perform a command pipeline, with optional input/output filenames. + + Args: + pipe_list: List of command lines to execute. Each command line is + piped into the next, and is itself a list of strings. For + example [ ['ls', '.git'] ['wc'] ] will pipe the output of + 'ls .git' into 'wc'. + infile: File to provide stdin to the pipeline + outfile: File to store stdout + capture: True to capture output + capture_stderr: True to capture stderr + oneline: True to strip newline chars from output + kwargs: Additional keyword arguments to cros_subprocess.Popen() + Returns: + CommandResult object + """ + if test_result: + if hasattr(test_result, '__call__'): + result = test_result(pipe_list=pipe_list) + if result: + return result + else: + return test_result + # No result: fall through to normal processing + result = CommandResult() + last_pipe = None + pipeline = list(pipe_list) + user_pipestr = '|'.join([' '.join(pipe) for pipe in pipe_list]) + kwargs['stdout'] = None + kwargs['stderr'] = None + while pipeline: + cmd = pipeline.pop(0) + if last_pipe is not None: + kwargs['stdin'] = last_pipe.stdout + elif infile: + kwargs['stdin'] = open(infile, 'rb') + if pipeline or capture: + kwargs['stdout'] = cros_subprocess.PIPE + elif outfile: + kwargs['stdout'] = open(outfile, 'wb') + if capture_stderr: + kwargs['stderr'] = cros_subprocess.PIPE + + try: + last_pipe = cros_subprocess.Popen(cmd, cwd=cwd, **kwargs) + except Exception as err: + result.exception = err + if raise_on_error: + raise Exception("Error running '%s': %s" % (user_pipestr, str)) + result.return_code = 255 + return result + + if capture: + result.stdout, result.stderr, result.combined = ( + last_pipe.CommunicateFilter(None)) + if result.stdout and oneline: + result.output = result.stdout.rstrip('\r\n') + result.return_code = last_pipe.wait() + else: + result.return_code = os.waitpid(last_pipe.pid, 0)[1] + if raise_on_error and result.return_code: + raise Exception("Error running '%s'" % user_pipestr) + return result + +def Output(*cmd, **kwargs): + raise_on_error = kwargs.get('raise_on_error', True) + return RunPipe([cmd], capture=True, raise_on_error=raise_on_error).stdout + +def OutputOneLine(*cmd, **kwargs): + raise_on_error = kwargs.pop('raise_on_error', True) + return (RunPipe([cmd], capture=True, oneline=True, + raise_on_error=raise_on_error, + **kwargs).stdout.strip()) + +def Run(*cmd, **kwargs): + return RunPipe([cmd], **kwargs).stdout + +def RunList(cmd): + return RunPipe([cmd], capture=True).stdout + +def StopAll(): + cros_subprocess.stay_alive = False diff --git a/tools/u-boot-tools/patman/commit.py b/tools/u-boot-tools/patman/commit.py new file mode 100644 index 0000000..2bf3a0b --- /dev/null +++ b/tools/u-boot-tools/patman/commit.py @@ -0,0 +1,87 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (c) 2011 The Chromium OS Authors. +# + +import re + +# Separates a tag: at the beginning of the subject from the rest of it +re_subject_tag = re.compile('([^:\s]*):\s*(.*)') + +class Commit: + """Holds information about a single commit/patch in the series. + + Args: + hash: Commit hash (as a string) + + Variables: + hash: Commit hash + subject: Subject line + tags: List of maintainer tag strings + changes: Dict containing a list of changes (single line strings). + The dict is indexed by change version (an integer) + cc_list: List of people to aliases/emails to cc on this commit + notes: List of lines in the commit (not series) notes + """ + def __init__(self, hash): + self.hash = hash + self.subject = None + self.tags = [] + self.changes = {} + self.cc_list = [] + self.signoff_set = set() + self.notes = [] + + def AddChange(self, version, info): + """Add a new change line to the change list for a version. + + Args: + version: Patch set version (integer: 1, 2, 3) + info: Description of change in this version + """ + if not self.changes.get(version): + self.changes[version] = [] + self.changes[version].append(info) + + def CheckTags(self): + """Create a list of subject tags in the commit + + Subject tags look like this: + + propounder: fort: Change the widget to propound correctly + + Here the tags are propounder and fort. Multiple tags are supported. + The list is updated in self.tag. + + Returns: + None if ok, else the name of a tag with no email alias + """ + str = self.subject + m = True + while m: + m = re_subject_tag.match(str) + if m: + tag = m.group(1) + self.tags.append(tag) + str = m.group(2) + return None + + def AddCc(self, cc_list): + """Add a list of people to Cc when we send this patch. + + Args: + cc_list: List of aliases or email addresses + """ + self.cc_list += cc_list + + def CheckDuplicateSignoff(self, signoff): + """Check a list of signoffs we have send for this patch + + Args: + signoff: Signoff line + Returns: + True if this signoff is new, False if we have already seen it. + """ + if signoff in self.signoff_set: + return False + self.signoff_set.add(signoff) + return True diff --git a/tools/u-boot-tools/patman/cros_subprocess.py b/tools/u-boot-tools/patman/cros_subprocess.py new file mode 100644 index 0000000..ebd4300 --- /dev/null +++ b/tools/u-boot-tools/patman/cros_subprocess.py @@ -0,0 +1,397 @@ +# Copyright (c) 2012 The Chromium OS Authors. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. +# +# Copyright (c) 2003-2005 by Peter Astrand <astrand@lysator.liu.se> +# Licensed to PSF under a Contributor Agreement. +# See http://www.python.org/2.4/license for licensing details. + +"""Subprocress execution + +This module holds a subclass of subprocess.Popen with our own required +features, mainly that we get access to the subprocess output while it +is running rather than just at the end. This makes it easiler to show +progress information and filter output in real time. +""" + +import errno +import os +import pty +import select +import subprocess +import sys +import unittest + + +# Import these here so the caller does not need to import subprocess also. +PIPE = subprocess.PIPE +STDOUT = subprocess.STDOUT +PIPE_PTY = -3 # Pipe output through a pty +stay_alive = True + + +class Popen(subprocess.Popen): + """Like subprocess.Popen with ptys and incremental output + + This class deals with running a child process and filtering its output on + both stdout and stderr while it is running. We do this so we can monitor + progress, and possibly relay the output to the user if requested. + + The class is similar to subprocess.Popen, the equivalent is something like: + + Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + + But this class has many fewer features, and two enhancement: + + 1. Rather than getting the output data only at the end, this class sends it + to a provided operation as it arrives. + 2. We use pseudo terminals so that the child will hopefully flush its output + to us as soon as it is produced, rather than waiting for the end of a + line. + + Use CommunicateFilter() to handle output from the subprocess. + + """ + + def __init__(self, args, stdin=None, stdout=PIPE_PTY, stderr=PIPE_PTY, + shell=False, cwd=None, env=None, **kwargs): + """Cut-down constructor + + Args: + args: Program and arguments for subprocess to execute. + stdin: See subprocess.Popen() + stdout: See subprocess.Popen(), except that we support the sentinel + value of cros_subprocess.PIPE_PTY. + stderr: See subprocess.Popen(), except that we support the sentinel + value of cros_subprocess.PIPE_PTY. + shell: See subprocess.Popen() + cwd: Working directory to change to for subprocess, or None if none. + env: Environment to use for this subprocess, or None to inherit parent. + kwargs: No other arguments are supported at the moment. Passing other + arguments will cause a ValueError to be raised. + """ + stdout_pty = None + stderr_pty = None + + if stdout == PIPE_PTY: + stdout_pty = pty.openpty() + stdout = os.fdopen(stdout_pty[1]) + if stderr == PIPE_PTY: + stderr_pty = pty.openpty() + stderr = os.fdopen(stderr_pty[1]) + + super(Popen, self).__init__(args, stdin=stdin, + stdout=stdout, stderr=stderr, shell=shell, cwd=cwd, env=env, + **kwargs) + + # If we're on a PTY, we passed the slave half of the PTY to the subprocess. + # We want to use the master half on our end from now on. Setting this here + # does make some assumptions about the implementation of subprocess, but + # those assumptions are pretty minor. + + # Note that if stderr is STDOUT, then self.stderr will be set to None by + # this constructor. + if stdout_pty is not None: + self.stdout = os.fdopen(stdout_pty[0]) + if stderr_pty is not None: + self.stderr = os.fdopen(stderr_pty[0]) + + # Insist that unit tests exist for other arguments we don't support. + if kwargs: + raise ValueError("Unit tests do not test extra args - please add tests") + + def CommunicateFilter(self, output): + """Interact with process: Read data from stdout and stderr. + + This method runs until end-of-file is reached, then waits for the + subprocess to terminate. + + The output function is sent all output from the subprocess and must be + defined like this: + + def Output([self,] stream, data) + Args: + stream: the stream the output was received on, which will be + sys.stdout or sys.stderr. + data: a string containing the data + + Note: The data read is buffered in memory, so do not use this + method if the data size is large or unlimited. + + Args: + output: Function to call with each fragment of output. + + Returns: + A tuple (stdout, stderr, combined) which is the data received on + stdout, stderr and the combined data (interleaved stdout and stderr). + + Note that the interleaved output will only be sensible if you have + set both stdout and stderr to PIPE or PIPE_PTY. Even then it depends on + the timing of the output in the subprocess. If a subprocess flips + between stdout and stderr quickly in succession, by the time we come to + read the output from each we may see several lines in each, and will read + all the stdout lines, then all the stderr lines. So the interleaving + may not be correct. In this case you might want to pass + stderr=cros_subprocess.STDOUT to the constructor. + + This feature is still useful for subprocesses where stderr is + rarely used and indicates an error. + + Note also that if you set stderr to STDOUT, then stderr will be empty + and the combined output will just be the same as stdout. + """ + + read_set = [] + write_set = [] + stdout = None # Return + stderr = None # Return + + if self.stdin: + # Flush stdio buffer. This might block, if the user has + # been writing to .stdin in an uncontrolled fashion. + self.stdin.flush() + if input: + write_set.append(self.stdin) + else: + self.stdin.close() + if self.stdout: + read_set.append(self.stdout) + stdout = [] + if self.stderr and self.stderr != self.stdout: + read_set.append(self.stderr) + stderr = [] + combined = [] + + input_offset = 0 + while read_set or write_set: + try: + rlist, wlist, _ = select.select(read_set, write_set, [], 0.2) + except select.error as e: + if e.args[0] == errno.EINTR: + continue + raise + + if not stay_alive: + self.terminate() + + if self.stdin in wlist: + # When select has indicated that the file is writable, + # we can write up to PIPE_BUF bytes without risk + # blocking. POSIX defines PIPE_BUF >= 512 + chunk = input[input_offset : input_offset + 512] + bytes_written = os.write(self.stdin.fileno(), chunk) + input_offset += bytes_written + if input_offset >= len(input): + self.stdin.close() + write_set.remove(self.stdin) + + if self.stdout in rlist: + data = "" + # We will get an error on read if the pty is closed + try: + data = os.read(self.stdout.fileno(), 1024) + except OSError: + pass + if data == "": + self.stdout.close() + read_set.remove(self.stdout) + else: + stdout.append(data) + combined.append(data) + if output: + output(sys.stdout, data) + if self.stderr in rlist: + data = "" + # We will get an error on read if the pty is closed + try: + data = os.read(self.stderr.fileno(), 1024) + except OSError: + pass + if data == "": + self.stderr.close() + read_set.remove(self.stderr) + else: + stderr.append(data) + combined.append(data) + if output: + output(sys.stderr, data) + + # All data exchanged. Translate lists into strings. + if stdout is not None: + stdout = ''.join(stdout) + else: + stdout = '' + if stderr is not None: + stderr = ''.join(stderr) + else: + stderr = '' + combined = ''.join(combined) + + # Translate newlines, if requested. We cannot let the file + # object do the translation: It is based on stdio, which is + # impossible to combine with select (unless forcing no + # buffering). + if self.universal_newlines and hasattr(file, 'newlines'): + if stdout: + stdout = self._translate_newlines(stdout) + if stderr: + stderr = self._translate_newlines(stderr) + + self.wait() + return (stdout, stderr, combined) + + +# Just being a unittest.TestCase gives us 14 public methods. Unless we +# disable this, we can only have 6 tests in a TestCase. That's not enough. +# +# pylint: disable=R0904 + +class TestSubprocess(unittest.TestCase): + """Our simple unit test for this module""" + + class MyOperation: + """Provides a operation that we can pass to Popen""" + def __init__(self, input_to_send=None): + """Constructor to set up the operation and possible input. + + Args: + input_to_send: a text string to send when we first get input. We will + add \r\n to the string. + """ + self.stdout_data = '' + self.stderr_data = '' + self.combined_data = '' + self.stdin_pipe = None + self._input_to_send = input_to_send + if input_to_send: + pipe = os.pipe() + self.stdin_read_pipe = pipe[0] + self._stdin_write_pipe = os.fdopen(pipe[1], 'w') + + def Output(self, stream, data): + """Output handler for Popen. Stores the data for later comparison""" + if stream == sys.stdout: + self.stdout_data += data + if stream == sys.stderr: + self.stderr_data += data + self.combined_data += data + + # Output the input string if we have one. + if self._input_to_send: + self._stdin_write_pipe.write(self._input_to_send + '\r\n') + self._stdin_write_pipe.flush() + + def _BasicCheck(self, plist, oper): + """Basic checks that the output looks sane.""" + self.assertEqual(plist[0], oper.stdout_data) + self.assertEqual(plist[1], oper.stderr_data) + self.assertEqual(plist[2], oper.combined_data) + + # The total length of stdout and stderr should equal the combined length + self.assertEqual(len(plist[0]) + len(plist[1]), len(plist[2])) + + def test_simple(self): + """Simple redirection: Get process list""" + oper = TestSubprocess.MyOperation() + plist = Popen(['ps']).CommunicateFilter(oper.Output) + self._BasicCheck(plist, oper) + + def test_stderr(self): + """Check stdout and stderr""" + oper = TestSubprocess.MyOperation() + cmd = 'echo fred >/dev/stderr && false || echo bad' + plist = Popen([cmd], shell=True).CommunicateFilter(oper.Output) + self._BasicCheck(plist, oper) + self.assertEqual(plist [0], 'bad\r\n') + self.assertEqual(plist [1], 'fred\r\n') + + def test_shell(self): + """Check with and without shell works""" + oper = TestSubprocess.MyOperation() + cmd = 'echo test >/dev/stderr' + self.assertRaises(OSError, Popen, [cmd], shell=False) + plist = Popen([cmd], shell=True).CommunicateFilter(oper.Output) + self._BasicCheck(plist, oper) + self.assertEqual(len(plist [0]), 0) + self.assertEqual(plist [1], 'test\r\n') + + def test_list_args(self): + """Check with and without shell works using list arguments""" + oper = TestSubprocess.MyOperation() + cmd = ['echo', 'test', '>/dev/stderr'] + plist = Popen(cmd, shell=False).CommunicateFilter(oper.Output) + self._BasicCheck(plist, oper) + self.assertEqual(plist [0], ' '.join(cmd[1:]) + '\r\n') + self.assertEqual(len(plist [1]), 0) + + oper = TestSubprocess.MyOperation() + + # this should be interpreted as 'echo' with the other args dropped + cmd = ['echo', 'test', '>/dev/stderr'] + plist = Popen(cmd, shell=True).CommunicateFilter(oper.Output) + self._BasicCheck(plist, oper) + self.assertEqual(plist [0], '\r\n') + + def test_cwd(self): + """Check we can change directory""" + for shell in (False, True): + oper = TestSubprocess.MyOperation() + plist = Popen('pwd', shell=shell, cwd='/tmp').CommunicateFilter(oper.Output) + self._BasicCheck(plist, oper) + self.assertEqual(plist [0], '/tmp\r\n') + + def test_env(self): + """Check we can change environment""" + for add in (False, True): + oper = TestSubprocess.MyOperation() + env = os.environ + if add: + env ['FRED'] = 'fred' + cmd = 'echo $FRED' + plist = Popen(cmd, shell=True, env=env).CommunicateFilter(oper.Output) + self._BasicCheck(plist, oper) + self.assertEqual(plist [0], add and 'fred\r\n' or '\r\n') + + def test_extra_args(self): + """Check we can't add extra arguments""" + self.assertRaises(ValueError, Popen, 'true', close_fds=False) + + def test_basic_input(self): + """Check that incremental input works + + We set up a subprocess which will prompt for name. When we see this prompt + we send the name as input to the process. It should then print the name + properly to stdout. + """ + oper = TestSubprocess.MyOperation('Flash') + prompt = 'What is your name?: ' + cmd = 'echo -n "%s"; read name; echo Hello $name' % prompt + plist = Popen([cmd], stdin=oper.stdin_read_pipe, + shell=True).CommunicateFilter(oper.Output) + self._BasicCheck(plist, oper) + self.assertEqual(len(plist [1]), 0) + self.assertEqual(plist [0], prompt + 'Hello Flash\r\r\n') + + def test_isatty(self): + """Check that ptys appear as terminals to the subprocess""" + oper = TestSubprocess.MyOperation() + cmd = ('if [ -t %d ]; then echo "terminal %d" >&%d; ' + 'else echo "not %d" >&%d; fi;') + both_cmds = '' + for fd in (1, 2): + both_cmds += cmd % (fd, fd, fd, fd, fd) + plist = Popen(both_cmds, shell=True).CommunicateFilter(oper.Output) + self._BasicCheck(plist, oper) + self.assertEqual(plist [0], 'terminal 1\r\n') + self.assertEqual(plist [1], 'terminal 2\r\n') + + # Now try with PIPE and make sure it is not a terminal + oper = TestSubprocess.MyOperation() + plist = Popen(both_cmds, stdout=subprocess.PIPE, stderr=subprocess.PIPE, + shell=True).CommunicateFilter(oper.Output) + self._BasicCheck(plist, oper) + self.assertEqual(plist [0], 'not 1\n') + self.assertEqual(plist [1], 'not 2\n') + +if __name__ == '__main__': + unittest.main() diff --git a/tools/u-boot-tools/patman/func_test.py b/tools/u-boot-tools/patman/func_test.py new file mode 100644 index 0000000..d79e716 --- /dev/null +++ b/tools/u-boot-tools/patman/func_test.py @@ -0,0 +1,242 @@ +# -*- coding: utf-8 -*- +# SPDX-License-Identifier: GPL-2.0+ +# +# Copyright 2017 Google, Inc +# + +import contextlib +import os +import re +import shutil +import sys +import tempfile +import unittest + +import gitutil +import patchstream +import settings + + +@contextlib.contextmanager +def capture(): + import sys + from cStringIO import StringIO + oldout,olderr = sys.stdout, sys.stderr + try: + out=[StringIO(), StringIO()] + sys.stdout,sys.stderr = out + yield out + finally: + sys.stdout,sys.stderr = oldout, olderr + out[0] = out[0].getvalue() + out[1] = out[1].getvalue() + + +class TestFunctional(unittest.TestCase): + def setUp(self): + self.tmpdir = tempfile.mkdtemp(prefix='patman.') + + def tearDown(self): + shutil.rmtree(self.tmpdir) + + @staticmethod + def GetPath(fname): + return os.path.join(os.path.dirname(os.path.realpath(sys.argv[0])), + 'test', fname) + + @classmethod + def GetText(self, fname): + return open(self.GetPath(fname)).read() + + @classmethod + def GetPatchName(self, subject): + fname = re.sub('[ :]', '-', subject) + return fname.replace('--', '-') + + def CreatePatchesForTest(self, series): + cover_fname = None + fname_list = [] + for i, commit in enumerate(series.commits): + clean_subject = self.GetPatchName(commit.subject) + src_fname = '%04d-%s.patch' % (i + 1, clean_subject[:52]) + fname = os.path.join(self.tmpdir, src_fname) + shutil.copy(self.GetPath(src_fname), fname) + fname_list.append(fname) + if series.get('cover'): + src_fname = '0000-cover-letter.patch' + cover_fname = os.path.join(self.tmpdir, src_fname) + fname = os.path.join(self.tmpdir, src_fname) + shutil.copy(self.GetPath(src_fname), fname) + + return cover_fname, fname_list + + def testBasic(self): + """Tests the basic flow of patman + + This creates a series from some hard-coded patches build from a simple + tree with the following metadata in the top commit: + + Series-to: u-boot + Series-prefix: RFC + Series-cc: Stefan Brüns <stefan.bruens@rwth-aachen.de> + Cover-letter-cc: Lord Mëlchett <clergy@palace.gov> + Series-version: 2 + Series-changes: 4 + - Some changes + + Cover-letter: + test: A test patch series + This is a test of how the cover + leter + works + END + + and this in the first commit: + + Series-notes: + some notes + about some things + from the first commit + END + + Commit-notes: + Some notes about + the first commit + END + + with the following commands: + + git log -n2 --reverse >/path/to/tools/patman/test/test01.txt + git format-patch --subject-prefix RFC --cover-letter HEAD~2 + mv 00* /path/to/tools/patman/test + + It checks these aspects: + - git log can be processed by patchstream + - emailing patches uses the correct command + - CC file has information on each commit + - cover letter has the expected text and subject + - each patch has the correct subject + - dry-run information prints out correctly + - unicode is handled correctly + - Series-to, Series-cc, Series-prefix, Cover-letter + - Cover-letter-cc, Series-version, Series-changes, Series-notes + - Commit-notes + """ + process_tags = True + ignore_bad_tags = True + stefan = u'Stefan Brüns <stefan.bruens@rwth-aachen.de>' + rick = 'Richard III <richard@palace.gov>' + mel = u'Lord Mëlchett <clergy@palace.gov>' + ed = u'Lond Edmund Blackaddër <weasel@blackadder.org' + fred = 'Fred Bloggs <f.bloggs@napier.net>' + add_maintainers = [stefan, rick] + dry_run = True + in_reply_to = mel + count = 2 + settings.alias = { + 'fdt': ['simon'], + 'u-boot': ['u-boot@lists.denx.de'], + 'simon': [ed], + 'fred': [fred], + } + + text = self.GetText('test01.txt') + series = patchstream.GetMetaDataForTest(text) + cover_fname, args = self.CreatePatchesForTest(series) + with capture() as out: + patchstream.FixPatches(series, args) + if cover_fname and series.get('cover'): + patchstream.InsertCoverLetter(cover_fname, series, count) + series.DoChecks() + cc_file = series.MakeCcFile(process_tags, cover_fname, + not ignore_bad_tags, add_maintainers, + None) + cmd = gitutil.EmailPatches(series, cover_fname, args, + dry_run, not ignore_bad_tags, cc_file, + in_reply_to=in_reply_to, thread=None) + series.ShowActions(args, cmd, process_tags) + cc_lines = open(cc_file).read().splitlines() + os.remove(cc_file) + + lines = out[0].splitlines() + #print '\n'.join(lines) + self.assertEqual('Cleaned %s patches' % len(series.commits), lines[0]) + self.assertEqual('Change log missing for v2', lines[1]) + self.assertEqual('Change log missing for v3', lines[2]) + self.assertEqual('Change log for unknown version v4', lines[3]) + self.assertEqual("Alias 'pci' not found", lines[4]) + self.assertIn('Dry run', lines[5]) + self.assertIn('Send a total of %d patches' % count, lines[7]) + line = 8 + for i, commit in enumerate(series.commits): + self.assertEqual(' %s' % args[i], lines[line + 0]) + line += 1 + while 'Cc:' in lines[line]: + line += 1 + self.assertEqual('To: u-boot@lists.denx.de', lines[line]) + self.assertEqual('Cc: %s' % stefan.encode('utf-8'), lines[line + 1]) + self.assertEqual('Version: 3', lines[line + 2]) + self.assertEqual('Prefix:\t RFC', lines[line + 3]) + self.assertEqual('Cover: 4 lines', lines[line + 4]) + line += 5 + self.assertEqual(' Cc: %s' % mel.encode('utf-8'), lines[line + 0]) + self.assertEqual(' Cc: %s' % rick, lines[line + 1]) + self.assertEqual(' Cc: %s' % fred, lines[line + 2]) + self.assertEqual(' Cc: %s' % ed.encode('utf-8'), lines[line + 3]) + expected = ('Git command: git send-email --annotate ' + '--in-reply-to="%s" --to "u-boot@lists.denx.de" ' + '--cc "%s" --cc-cmd "%s --cc-cmd %s" %s %s' + % (in_reply_to, stefan, sys.argv[0], cc_file, cover_fname, + ' '.join(args))).encode('utf-8') + line += 4 + self.assertEqual(expected, lines[line]) + + self.assertEqual(('%s %s, %s' % (args[0], rick, stefan)) + .encode('utf-8'), cc_lines[0]) + self.assertEqual(('%s %s, %s, %s, %s' % (args[1], fred, rick, stefan, + ed)).encode('utf-8'), cc_lines[1]) + + expected = ''' +This is a test of how the cover +leter +works + +some notes +about some things +from the first commit + +Changes in v4: +- Some changes + +Simon Glass (2): + pci: Correct cast for sandbox + fdt: Correct cast for sandbox in fdtdec_setup_mem_size_base() + + cmd/pci.c | 3 ++- + fs/fat/fat.c | 1 + + lib/efi_loader/efi_memory.c | 1 + + lib/fdtdec.c | 3 ++- + 4 files changed, 6 insertions(+), 2 deletions(-) + +--\x20 +2.7.4 + +''' + lines = open(cover_fname).read().splitlines() + #print '\n'.join(lines) + self.assertEqual( + 'Subject: [RFC PATCH v3 0/2] test: A test patch series', + lines[3]) + self.assertEqual(expected.splitlines(), lines[7:]) + + for i, fname in enumerate(args): + lines = open(fname).read().splitlines() + #print '\n'.join(lines) + subject = [line for line in lines if line.startswith('Subject')] + self.assertEqual('Subject: [RFC %d/%d]' % (i + 1, count), + subject[0][:18]) + if i == 0: + # Check that we got our commit notes + self.assertEqual('---', lines[17]) + self.assertEqual('Some notes about', lines[18]) + self.assertEqual('the first commit', lines[19]) diff --git a/tools/u-boot-tools/patman/get_maintainer.py b/tools/u-boot-tools/patman/get_maintainer.py new file mode 100644 index 0000000..0ffb55a --- /dev/null +++ b/tools/u-boot-tools/patman/get_maintainer.py @@ -0,0 +1,47 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (c) 2012 The Chromium OS Authors. +# + +import command +import gitutil +import os + +def FindGetMaintainer(): + """Look for the get_maintainer.pl script. + + Returns: + If the script is found we'll return a path to it; else None. + """ + try_list = [ + os.path.join(gitutil.GetTopLevel(), 'scripts'), + ] + # Look in the list + for path in try_list: + fname = os.path.join(path, 'get_maintainer.pl') + if os.path.isfile(fname): + return fname + + return None + +def GetMaintainer(fname, verbose=False): + """Run get_maintainer.pl on a file if we find it. + + We look for get_maintainer.pl in the 'scripts' directory at the top of + git. If we find it we'll run it. If we don't find get_maintainer.pl + then we fail silently. + + Args: + fname: Path to the patch file to run get_maintainer.pl on. + + Returns: + A list of email addresses to CC to. + """ + get_maintainer = FindGetMaintainer() + if not get_maintainer: + if verbose: + print("WARNING: Couldn't find get_maintainer.pl") + return [] + + stdout = command.Output(get_maintainer, '--norolestats', fname) + lines = stdout.splitlines() + return [ x.replace('"', '') for x in lines ] diff --git a/tools/u-boot-tools/patman/gitutil.py b/tools/u-boot-tools/patman/gitutil.py new file mode 100644 index 0000000..9905bb0 --- /dev/null +++ b/tools/u-boot-tools/patman/gitutil.py @@ -0,0 +1,597 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (c) 2011 The Chromium OS Authors. +# + +import command +import re +import os +import series +import subprocess +import sys +import terminal + +import checkpatch +import settings + +# True to use --no-decorate - we check this in Setup() +use_no_decorate = True + +def LogCmd(commit_range, git_dir=None, oneline=False, reverse=False, + count=None): + """Create a command to perform a 'git log' + + Args: + commit_range: Range expression to use for log, None for none + git_dir: Path to git repositiory (None to use default) + oneline: True to use --oneline, else False + reverse: True to reverse the log (--reverse) + count: Number of commits to list, or None for no limit + Return: + List containing command and arguments to run + """ + cmd = ['git'] + if git_dir: + cmd += ['--git-dir', git_dir] + cmd += ['--no-pager', 'log', '--no-color'] + if oneline: + cmd.append('--oneline') + if use_no_decorate: + cmd.append('--no-decorate') + if reverse: + cmd.append('--reverse') + if count is not None: + cmd.append('-n%d' % count) + if commit_range: + cmd.append(commit_range) + + # Add this in case we have a branch with the same name as a directory. + # This avoids messages like this, for example: + # fatal: ambiguous argument 'test': both revision and filename + cmd.append('--') + return cmd + +def CountCommitsToBranch(): + """Returns number of commits between HEAD and the tracking branch. + + This looks back to the tracking branch and works out the number of commits + since then. + + Return: + Number of patches that exist on top of the branch + """ + pipe = [LogCmd('@{upstream}..', oneline=True), + ['wc', '-l']] + stdout = command.RunPipe(pipe, capture=True, oneline=True).stdout + patch_count = int(stdout) + return patch_count + +def NameRevision(commit_hash): + """Gets the revision name for a commit + + Args: + commit_hash: Commit hash to look up + + Return: + Name of revision, if any, else None + """ + pipe = ['git', 'name-rev', commit_hash] + stdout = command.RunPipe([pipe], capture=True, oneline=True).stdout + + # We expect a commit, a space, then a revision name + name = stdout.split(' ')[1].strip() + return name + +def GuessUpstream(git_dir, branch): + """Tries to guess the upstream for a branch + + This lists out top commits on a branch and tries to find a suitable + upstream. It does this by looking for the first commit where + 'git name-rev' returns a plain branch name, with no ! or ^ modifiers. + + Args: + git_dir: Git directory containing repo + branch: Name of branch + + Returns: + Tuple: + Name of upstream branch (e.g. 'upstream/master') or None if none + Warning/error message, or None if none + """ + pipe = [LogCmd(branch, git_dir=git_dir, oneline=True, count=100)] + result = command.RunPipe(pipe, capture=True, capture_stderr=True, + raise_on_error=False) + if result.return_code: + return None, "Branch '%s' not found" % branch + for line in result.stdout.splitlines()[1:]: + commit_hash = line.split(' ')[0] + name = NameRevision(commit_hash) + if '~' not in name and '^' not in name: + if name.startswith('remotes/'): + name = name[8:] + return name, "Guessing upstream as '%s'" % name + return None, "Cannot find a suitable upstream for branch '%s'" % branch + +def GetUpstream(git_dir, branch): + """Returns the name of the upstream for a branch + + Args: + git_dir: Git directory containing repo + branch: Name of branch + + Returns: + Tuple: + Name of upstream branch (e.g. 'upstream/master') or None if none + Warning/error message, or None if none + """ + try: + remote = command.OutputOneLine('git', '--git-dir', git_dir, 'config', + 'branch.%s.remote' % branch) + merge = command.OutputOneLine('git', '--git-dir', git_dir, 'config', + 'branch.%s.merge' % branch) + except: + upstream, msg = GuessUpstream(git_dir, branch) + return upstream, msg + + if remote == '.': + return merge, None + elif remote and merge: + leaf = merge.split('/')[-1] + return '%s/%s' % (remote, leaf), None + else: + raise ValueError("Cannot determine upstream branch for branch " + "'%s' remote='%s', merge='%s'" % (branch, remote, merge)) + + +def GetRangeInBranch(git_dir, branch, include_upstream=False): + """Returns an expression for the commits in the given branch. + + Args: + git_dir: Directory containing git repo + branch: Name of branch + Return: + Expression in the form 'upstream..branch' which can be used to + access the commits. If the branch does not exist, returns None. + """ + upstream, msg = GetUpstream(git_dir, branch) + if not upstream: + return None, msg + rstr = '%s%s..%s' % (upstream, '~' if include_upstream else '', branch) + return rstr, msg + +def CountCommitsInRange(git_dir, range_expr): + """Returns the number of commits in the given range. + + Args: + git_dir: Directory containing git repo + range_expr: Range to check + Return: + Number of patches that exist in the supplied rangem or None if none + were found + """ + pipe = [LogCmd(range_expr, git_dir=git_dir, oneline=True)] + result = command.RunPipe(pipe, capture=True, capture_stderr=True, + raise_on_error=False) + if result.return_code: + return None, "Range '%s' not found or is invalid" % range_expr + patch_count = len(result.stdout.splitlines()) + return patch_count, None + +def CountCommitsInBranch(git_dir, branch, include_upstream=False): + """Returns the number of commits in the given branch. + + Args: + git_dir: Directory containing git repo + branch: Name of branch + Return: + Number of patches that exist on top of the branch, or None if the + branch does not exist. + """ + range_expr, msg = GetRangeInBranch(git_dir, branch, include_upstream) + if not range_expr: + return None, msg + return CountCommitsInRange(git_dir, range_expr) + +def CountCommits(commit_range): + """Returns the number of commits in the given range. + + Args: + commit_range: Range of commits to count (e.g. 'HEAD..base') + Return: + Number of patches that exist on top of the branch + """ + pipe = [LogCmd(commit_range, oneline=True), + ['wc', '-l']] + stdout = command.RunPipe(pipe, capture=True, oneline=True).stdout + patch_count = int(stdout) + return patch_count + +def Checkout(commit_hash, git_dir=None, work_tree=None, force=False): + """Checkout the selected commit for this build + + Args: + commit_hash: Commit hash to check out + """ + pipe = ['git'] + if git_dir: + pipe.extend(['--git-dir', git_dir]) + if work_tree: + pipe.extend(['--work-tree', work_tree]) + pipe.append('checkout') + if force: + pipe.append('-f') + pipe.append(commit_hash) + result = command.RunPipe([pipe], capture=True, raise_on_error=False, + capture_stderr=True) + if result.return_code != 0: + raise OSError('git checkout (%s): %s' % (pipe, result.stderr)) + +def Clone(git_dir, output_dir): + """Checkout the selected commit for this build + + Args: + commit_hash: Commit hash to check out + """ + pipe = ['git', 'clone', git_dir, '.'] + result = command.RunPipe([pipe], capture=True, cwd=output_dir, + capture_stderr=True) + if result.return_code != 0: + raise OSError('git clone: %s' % result.stderr) + +def Fetch(git_dir=None, work_tree=None): + """Fetch from the origin repo + + Args: + commit_hash: Commit hash to check out + """ + pipe = ['git'] + if git_dir: + pipe.extend(['--git-dir', git_dir]) + if work_tree: + pipe.extend(['--work-tree', work_tree]) + pipe.append('fetch') + result = command.RunPipe([pipe], capture=True, capture_stderr=True) + if result.return_code != 0: + raise OSError('git fetch: %s' % result.stderr) + +def CreatePatches(start, count, series): + """Create a series of patches from the top of the current branch. + + The patch files are written to the current directory using + git format-patch. + + Args: + start: Commit to start from: 0=HEAD, 1=next one, etc. + count: number of commits to include + Return: + Filename of cover letter + List of filenames of patch files + """ + if series.get('version'): + version = '%s ' % series['version'] + cmd = ['git', 'format-patch', '-M', '--signoff'] + if series.get('cover'): + cmd.append('--cover-letter') + prefix = series.GetPatchPrefix() + if prefix: + cmd += ['--subject-prefix=%s' % prefix] + cmd += ['HEAD~%d..HEAD~%d' % (start + count, start)] + + stdout = command.RunList(cmd) + files = stdout.splitlines() + + # We have an extra file if there is a cover letter + if series.get('cover'): + return files[0], files[1:] + else: + return None, files + +def BuildEmailList(in_list, tag=None, alias=None, raise_on_error=True): + """Build a list of email addresses based on an input list. + + Takes a list of email addresses and aliases, and turns this into a list + of only email address, by resolving any aliases that are present. + + If the tag is given, then each email address is prepended with this + tag and a space. If the tag starts with a minus sign (indicating a + command line parameter) then the email address is quoted. + + Args: + in_list: List of aliases/email addresses + tag: Text to put before each address + alias: Alias dictionary + raise_on_error: True to raise an error when an alias fails to match, + False to just print a message. + + Returns: + List of email addresses + + >>> alias = {} + >>> alias['fred'] = ['f.bloggs@napier.co.nz'] + >>> alias['john'] = ['j.bloggs@napier.co.nz'] + >>> alias['mary'] = ['Mary Poppins <m.poppins@cloud.net>'] + >>> alias['boys'] = ['fred', ' john'] + >>> alias['all'] = ['fred ', 'john', ' mary '] + >>> BuildEmailList(['john', 'mary'], None, alias) + ['j.bloggs@napier.co.nz', 'Mary Poppins <m.poppins@cloud.net>'] + >>> BuildEmailList(['john', 'mary'], '--to', alias) + ['--to "j.bloggs@napier.co.nz"', \ +'--to "Mary Poppins <m.poppins@cloud.net>"'] + >>> BuildEmailList(['john', 'mary'], 'Cc', alias) + ['Cc j.bloggs@napier.co.nz', 'Cc Mary Poppins <m.poppins@cloud.net>'] + """ + quote = '"' if tag and tag[0] == '-' else '' + raw = [] + for item in in_list: + raw += LookupEmail(item, alias, raise_on_error=raise_on_error) + result = [] + for item in raw: + if not item in result: + result.append(item) + if tag: + return ['%s %s%s%s' % (tag, quote, email, quote) for email in result] + return result + +def EmailPatches(series, cover_fname, args, dry_run, raise_on_error, cc_fname, + self_only=False, alias=None, in_reply_to=None, thread=False, + smtp_server=None): + """Email a patch series. + + Args: + series: Series object containing destination info + cover_fname: filename of cover letter + args: list of filenames of patch files + dry_run: Just return the command that would be run + raise_on_error: True to raise an error when an alias fails to match, + False to just print a message. + cc_fname: Filename of Cc file for per-commit Cc + self_only: True to just email to yourself as a test + in_reply_to: If set we'll pass this to git as --in-reply-to. + Should be a message ID that this is in reply to. + thread: True to add --thread to git send-email (make + all patches reply to cover-letter or first patch in series) + smtp_server: SMTP server to use to send patches + + Returns: + Git command that was/would be run + + # For the duration of this doctest pretend that we ran patman with ./patman + >>> _old_argv0 = sys.argv[0] + >>> sys.argv[0] = './patman' + + >>> alias = {} + >>> alias['fred'] = ['f.bloggs@napier.co.nz'] + >>> alias['john'] = ['j.bloggs@napier.co.nz'] + >>> alias['mary'] = ['m.poppins@cloud.net'] + >>> alias['boys'] = ['fred', ' john'] + >>> alias['all'] = ['fred ', 'john', ' mary '] + >>> alias[os.getenv('USER')] = ['this-is-me@me.com'] + >>> series = series.Series() + >>> series.to = ['fred'] + >>> series.cc = ['mary'] + >>> EmailPatches(series, 'cover', ['p1', 'p2'], True, True, 'cc-fname', \ + False, alias) + 'git send-email --annotate --to "f.bloggs@napier.co.nz" --cc \ +"m.poppins@cloud.net" --cc-cmd "./patman --cc-cmd cc-fname" cover p1 p2' + >>> EmailPatches(series, None, ['p1'], True, True, 'cc-fname', False, \ + alias) + 'git send-email --annotate --to "f.bloggs@napier.co.nz" --cc \ +"m.poppins@cloud.net" --cc-cmd "./patman --cc-cmd cc-fname" p1' + >>> series.cc = ['all'] + >>> EmailPatches(series, 'cover', ['p1', 'p2'], True, True, 'cc-fname', \ + True, alias) + 'git send-email --annotate --to "this-is-me@me.com" --cc-cmd "./patman \ +--cc-cmd cc-fname" cover p1 p2' + >>> EmailPatches(series, 'cover', ['p1', 'p2'], True, True, 'cc-fname', \ + False, alias) + 'git send-email --annotate --to "f.bloggs@napier.co.nz" --cc \ +"f.bloggs@napier.co.nz" --cc "j.bloggs@napier.co.nz" --cc \ +"m.poppins@cloud.net" --cc-cmd "./patman --cc-cmd cc-fname" cover p1 p2' + + # Restore argv[0] since we clobbered it. + >>> sys.argv[0] = _old_argv0 + """ + to = BuildEmailList(series.get('to'), '--to', alias, raise_on_error) + if not to: + git_config_to = command.Output('git', 'config', 'sendemail.to', + raise_on_error=False) + if not git_config_to: + print ("No recipient.\n" + "Please add something like this to a commit\n" + "Series-to: Fred Bloggs <f.blogs@napier.co.nz>\n" + "Or do something like this\n" + "git config sendemail.to u-boot@lists.denx.de") + return + cc = BuildEmailList(list(set(series.get('cc')) - set(series.get('to'))), + '--cc', alias, raise_on_error) + if self_only: + to = BuildEmailList([os.getenv('USER')], '--to', alias, raise_on_error) + cc = [] + cmd = ['git', 'send-email', '--annotate'] + if smtp_server: + cmd.append('--smtp-server=%s' % smtp_server) + if in_reply_to: + if type(in_reply_to) != str: + in_reply_to = in_reply_to.encode('utf-8') + cmd.append('--in-reply-to="%s"' % in_reply_to) + if thread: + cmd.append('--thread') + + cmd += to + cmd += cc + cmd += ['--cc-cmd', '"%s --cc-cmd %s"' % (sys.argv[0], cc_fname)] + if cover_fname: + cmd.append(cover_fname) + cmd += args + cmdstr = ' '.join(cmd) + if not dry_run: + os.system(cmdstr) + return cmdstr + + +def LookupEmail(lookup_name, alias=None, raise_on_error=True, level=0): + """If an email address is an alias, look it up and return the full name + + TODO: Why not just use git's own alias feature? + + Args: + lookup_name: Alias or email address to look up + alias: Dictionary containing aliases (None to use settings default) + raise_on_error: True to raise an error when an alias fails to match, + False to just print a message. + + Returns: + tuple: + list containing a list of email addresses + + Raises: + OSError if a recursive alias reference was found + ValueError if an alias was not found + + >>> alias = {} + >>> alias['fred'] = ['f.bloggs@napier.co.nz'] + >>> alias['john'] = ['j.bloggs@napier.co.nz'] + >>> alias['mary'] = ['m.poppins@cloud.net'] + >>> alias['boys'] = ['fred', ' john', 'f.bloggs@napier.co.nz'] + >>> alias['all'] = ['fred ', 'john', ' mary '] + >>> alias['loop'] = ['other', 'john', ' mary '] + >>> alias['other'] = ['loop', 'john', ' mary '] + >>> LookupEmail('mary', alias) + ['m.poppins@cloud.net'] + >>> LookupEmail('arthur.wellesley@howe.ro.uk', alias) + ['arthur.wellesley@howe.ro.uk'] + >>> LookupEmail('boys', alias) + ['f.bloggs@napier.co.nz', 'j.bloggs@napier.co.nz'] + >>> LookupEmail('all', alias) + ['f.bloggs@napier.co.nz', 'j.bloggs@napier.co.nz', 'm.poppins@cloud.net'] + >>> LookupEmail('odd', alias) + Traceback (most recent call last): + ... + ValueError: Alias 'odd' not found + >>> LookupEmail('loop', alias) + Traceback (most recent call last): + ... + OSError: Recursive email alias at 'other' + >>> LookupEmail('odd', alias, raise_on_error=False) + Alias 'odd' not found + [] + >>> # In this case the loop part will effectively be ignored. + >>> LookupEmail('loop', alias, raise_on_error=False) + Recursive email alias at 'other' + Recursive email alias at 'john' + Recursive email alias at 'mary' + ['j.bloggs@napier.co.nz', 'm.poppins@cloud.net'] + """ + if not alias: + alias = settings.alias + lookup_name = lookup_name.strip() + if '@' in lookup_name: # Perhaps a real email address + return [lookup_name] + + lookup_name = lookup_name.lower() + col = terminal.Color() + + out_list = [] + if level > 10: + msg = "Recursive email alias at '%s'" % lookup_name + if raise_on_error: + raise OSError(msg) + else: + print(col.Color(col.RED, msg)) + return out_list + + if lookup_name: + if not lookup_name in alias: + msg = "Alias '%s' not found" % lookup_name + if raise_on_error: + raise ValueError(msg) + else: + print(col.Color(col.RED, msg)) + return out_list + for item in alias[lookup_name]: + todo = LookupEmail(item, alias, raise_on_error, level + 1) + for new_item in todo: + if not new_item in out_list: + out_list.append(new_item) + + #print("No match for alias '%s'" % lookup_name) + return out_list + +def GetTopLevel(): + """Return name of top-level directory for this git repo. + + Returns: + Full path to git top-level directory + + This test makes sure that we are running tests in the right subdir + + >>> os.path.realpath(os.path.dirname(__file__)) == \ + os.path.join(GetTopLevel(), 'tools', 'patman') + True + """ + return command.OutputOneLine('git', 'rev-parse', '--show-toplevel') + +def GetAliasFile(): + """Gets the name of the git alias file. + + Returns: + Filename of git alias file, or None if none + """ + fname = command.OutputOneLine('git', 'config', 'sendemail.aliasesfile', + raise_on_error=False) + if fname: + fname = os.path.join(GetTopLevel(), fname.strip()) + return fname + +def GetDefaultUserName(): + """Gets the user.name from .gitconfig file. + + Returns: + User name found in .gitconfig file, or None if none + """ + uname = command.OutputOneLine('git', 'config', '--global', 'user.name') + return uname + +def GetDefaultUserEmail(): + """Gets the user.email from the global .gitconfig file. + + Returns: + User's email found in .gitconfig file, or None if none + """ + uemail = command.OutputOneLine('git', 'config', '--global', 'user.email') + return uemail + +def GetDefaultSubjectPrefix(): + """Gets the format.subjectprefix from local .git/config file. + + Returns: + Subject prefix found in local .git/config file, or None if none + """ + sub_prefix = command.OutputOneLine('git', 'config', 'format.subjectprefix', + raise_on_error=False) + + return sub_prefix + +def Setup(): + """Set up git utils, by reading the alias files.""" + # Check for a git alias file also + global use_no_decorate + + alias_fname = GetAliasFile() + if alias_fname: + settings.ReadGitAliases(alias_fname) + cmd = LogCmd(None, count=0) + use_no_decorate = (command.RunPipe([cmd], raise_on_error=False) + .return_code == 0) + +def GetHead(): + """Get the hash of the current HEAD + + Returns: + Hash of HEAD + """ + return command.OutputOneLine('git', 'show', '-s', '--pretty=format:%H') + +if __name__ == "__main__": + import doctest + + doctest.testmod() diff --git a/tools/u-boot-tools/patman/patchstream.py b/tools/u-boot-tools/patman/patchstream.py new file mode 100644 index 0000000..b6455b0 --- /dev/null +++ b/tools/u-boot-tools/patman/patchstream.py @@ -0,0 +1,526 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (c) 2011 The Chromium OS Authors. +# + +import math +import os +import re +import shutil +import tempfile + +import command +import commit +import gitutil +from series import Series + +# Tags that we detect and remove +re_remove = re.compile('^BUG=|^TEST=|^BRANCH=|^Change-Id:|^Review URL:' + '|Reviewed-on:|Commit-\w*:') + +# Lines which are allowed after a TEST= line +re_allowed_after_test = re.compile('^Signed-off-by:') + +# Signoffs +re_signoff = re.compile('^Signed-off-by: *(.*)') + +# The start of the cover letter +re_cover = re.compile('^Cover-letter:') + +# A cover letter Cc +re_cover_cc = re.compile('^Cover-letter-cc: *(.*)') + +# Patch series tag +re_series_tag = re.compile('^Series-([a-z-]*): *(.*)') + +# Commit series tag +re_commit_tag = re.compile('^Commit-([a-z-]*): *(.*)') + +# Commit tags that we want to collect and keep +re_tag = re.compile('^(Tested-by|Acked-by|Reviewed-by|Patch-cc): (.*)') + +# The start of a new commit in the git log +re_commit = re.compile('^commit ([0-9a-f]*)$') + +# We detect these since checkpatch doesn't always do it +re_space_before_tab = re.compile('^[+].* \t') + +# States we can be in - can we use range() and still have comments? +STATE_MSG_HEADER = 0 # Still in the message header +STATE_PATCH_SUBJECT = 1 # In patch subject (first line of log for a commit) +STATE_PATCH_HEADER = 2 # In patch header (after the subject) +STATE_DIFFS = 3 # In the diff part (past --- line) + +class PatchStream: + """Class for detecting/injecting tags in a patch or series of patches + + We support processing the output of 'git log' to read out the tags we + are interested in. We can also process a patch file in order to remove + unwanted tags or inject additional ones. These correspond to the two + phases of processing. + """ + def __init__(self, series, name=None, is_log=False): + self.skip_blank = False # True to skip a single blank line + self.found_test = False # Found a TEST= line + self.lines_after_test = 0 # MNumber of lines found after TEST= + self.warn = [] # List of warnings we have collected + self.linenum = 1 # Output line number we are up to + self.in_section = None # Name of start...END section we are in + self.notes = [] # Series notes + self.section = [] # The current section...END section + self.series = series # Info about the patch series + self.is_log = is_log # True if indent like git log + self.in_change = 0 # Non-zero if we are in a change list + self.blank_count = 0 # Number of blank lines stored up + self.state = STATE_MSG_HEADER # What state are we in? + self.signoff = [] # Contents of signoff line + self.commit = None # Current commit + + def AddToSeries(self, line, name, value): + """Add a new Series-xxx tag. + + When a Series-xxx tag is detected, we come here to record it, if we + are scanning a 'git log'. + + Args: + line: Source line containing tag (useful for debug/error messages) + name: Tag name (part after 'Series-') + value: Tag value (part after 'Series-xxx: ') + """ + if name == 'notes': + self.in_section = name + self.skip_blank = False + if self.is_log: + self.series.AddTag(self.commit, line, name, value) + + def AddToCommit(self, line, name, value): + """Add a new Commit-xxx tag. + + When a Commit-xxx tag is detected, we come here to record it. + + Args: + line: Source line containing tag (useful for debug/error messages) + name: Tag name (part after 'Commit-') + value: Tag value (part after 'Commit-xxx: ') + """ + if name == 'notes': + self.in_section = 'commit-' + name + self.skip_blank = False + + def CloseCommit(self): + """Save the current commit into our commit list, and reset our state""" + if self.commit and self.is_log: + self.series.AddCommit(self.commit) + self.commit = None + # If 'END' is missing in a 'Cover-letter' section, and that section + # happens to show up at the very end of the commit message, this is + # the chance for us to fix it up. + if self.in_section == 'cover' and self.is_log: + self.series.cover = self.section + self.in_section = None + self.skip_blank = True + self.section = [] + + def ProcessLine(self, line): + """Process a single line of a patch file or commit log + + This process a line and returns a list of lines to output. The list + may be empty or may contain multiple output lines. + + This is where all the complicated logic is located. The class's + state is used to move between different states and detect things + properly. + + We can be in one of two modes: + self.is_log == True: This is 'git log' mode, where most output is + indented by 4 characters and we are scanning for tags + + self.is_log == False: This is 'patch' mode, where we already have + all the tags, and are processing patches to remove junk we + don't want, and add things we think are required. + + Args: + line: text line to process + + Returns: + list of output lines, or [] if nothing should be output + """ + # Initially we have no output. Prepare the input line string + out = [] + line = line.rstrip('\n') + + commit_match = re_commit.match(line) if self.is_log else None + + if self.is_log: + if line[:4] == ' ': + line = line[4:] + + # Handle state transition and skipping blank lines + series_tag_match = re_series_tag.match(line) + commit_tag_match = re_commit_tag.match(line) + cover_match = re_cover.match(line) + cover_cc_match = re_cover_cc.match(line) + signoff_match = re_signoff.match(line) + tag_match = None + if self.state == STATE_PATCH_HEADER: + tag_match = re_tag.match(line) + is_blank = not line.strip() + if is_blank: + if (self.state == STATE_MSG_HEADER + or self.state == STATE_PATCH_SUBJECT): + self.state += 1 + + # We don't have a subject in the text stream of patch files + # It has its own line with a Subject: tag + if not self.is_log and self.state == STATE_PATCH_SUBJECT: + self.state += 1 + elif commit_match: + self.state = STATE_MSG_HEADER + + # If a tag is detected, or a new commit starts + if series_tag_match or commit_tag_match or \ + cover_match or cover_cc_match or signoff_match or \ + self.state == STATE_MSG_HEADER: + # but we are already in a section, this means 'END' is missing + # for that section, fix it up. + if self.in_section: + self.warn.append("Missing 'END' in section '%s'" % self.in_section) + if self.in_section == 'cover': + self.series.cover = self.section + elif self.in_section == 'notes': + if self.is_log: + self.series.notes += self.section + elif self.in_section == 'commit-notes': + if self.is_log: + self.commit.notes += self.section + else: + self.warn.append("Unknown section '%s'" % self.in_section) + self.in_section = None + self.skip_blank = True + self.section = [] + # but we are already in a change list, that means a blank line + # is missing, fix it up. + if self.in_change: + self.warn.append("Missing 'blank line' in section 'Series-changes'") + self.in_change = 0 + + # If we are in a section, keep collecting lines until we see END + if self.in_section: + if line == 'END': + if self.in_section == 'cover': + self.series.cover = self.section + elif self.in_section == 'notes': + if self.is_log: + self.series.notes += self.section + elif self.in_section == 'commit-notes': + if self.is_log: + self.commit.notes += self.section + else: + self.warn.append("Unknown section '%s'" % self.in_section) + self.in_section = None + self.skip_blank = True + self.section = [] + else: + self.section.append(line) + + # Detect the commit subject + elif not is_blank and self.state == STATE_PATCH_SUBJECT: + self.commit.subject = line + + # Detect the tags we want to remove, and skip blank lines + elif re_remove.match(line) and not commit_tag_match: + self.skip_blank = True + + # TEST= should be the last thing in the commit, so remove + # everything after it + if line.startswith('TEST='): + self.found_test = True + elif self.skip_blank and is_blank: + self.skip_blank = False + + # Detect the start of a cover letter section + elif cover_match: + self.in_section = 'cover' + self.skip_blank = False + + elif cover_cc_match: + value = cover_cc_match.group(1) + self.AddToSeries(line, 'cover-cc', value) + + # If we are in a change list, key collected lines until a blank one + elif self.in_change: + if is_blank: + # Blank line ends this change list + self.in_change = 0 + elif line == '---': + self.in_change = 0 + out = self.ProcessLine(line) + else: + if self.is_log: + self.series.AddChange(self.in_change, self.commit, line) + self.skip_blank = False + + # Detect Series-xxx tags + elif series_tag_match: + name = series_tag_match.group(1) + value = series_tag_match.group(2) + if name == 'changes': + # value is the version number: e.g. 1, or 2 + try: + value = int(value) + except ValueError as str: + raise ValueError("%s: Cannot decode version info '%s'" % + (self.commit.hash, line)) + self.in_change = int(value) + else: + self.AddToSeries(line, name, value) + self.skip_blank = True + + # Detect Commit-xxx tags + elif commit_tag_match: + name = commit_tag_match.group(1) + value = commit_tag_match.group(2) + if name == 'notes': + self.AddToCommit(line, name, value) + self.skip_blank = True + + # Detect the start of a new commit + elif commit_match: + self.CloseCommit() + self.commit = commit.Commit(commit_match.group(1)) + + # Detect tags in the commit message + elif tag_match: + # Remove Tested-by self, since few will take much notice + if (tag_match.group(1) == 'Tested-by' and + tag_match.group(2).find(os.getenv('USER') + '@') != -1): + self.warn.append("Ignoring %s" % line) + elif tag_match.group(1) == 'Patch-cc': + self.commit.AddCc(tag_match.group(2).split(',')) + else: + out = [line] + + # Suppress duplicate signoffs + elif signoff_match: + if (self.is_log or not self.commit or + self.commit.CheckDuplicateSignoff(signoff_match.group(1))): + out = [line] + + # Well that means this is an ordinary line + else: + # Look for space before tab + m = re_space_before_tab.match(line) + if m: + self.warn.append('Line %d/%d has space before tab' % + (self.linenum, m.start())) + + # OK, we have a valid non-blank line + out = [line] + self.linenum += 1 + self.skip_blank = False + if self.state == STATE_DIFFS: + pass + + # If this is the start of the diffs section, emit our tags and + # change log + elif line == '---': + self.state = STATE_DIFFS + + # Output the tags (signeoff first), then change list + out = [] + log = self.series.MakeChangeLog(self.commit) + out += [line] + if self.commit: + out += self.commit.notes + out += [''] + log + elif self.found_test: + if not re_allowed_after_test.match(line): + self.lines_after_test += 1 + + return out + + def Finalize(self): + """Close out processing of this patch stream""" + self.CloseCommit() + if self.lines_after_test: + self.warn.append('Found %d lines after TEST=' % + self.lines_after_test) + + def ProcessStream(self, infd, outfd): + """Copy a stream from infd to outfd, filtering out unwanting things. + + This is used to process patch files one at a time. + + Args: + infd: Input stream file object + outfd: Output stream file object + """ + # Extract the filename from each diff, for nice warnings + fname = None + last_fname = None + re_fname = re.compile('diff --git a/(.*) b/.*') + while True: + line = infd.readline() + if not line: + break + out = self.ProcessLine(line) + + # Try to detect blank lines at EOF + for line in out: + match = re_fname.match(line) + if match: + last_fname = fname + fname = match.group(1) + if line == '+': + self.blank_count += 1 + else: + if self.blank_count and (line == '-- ' or match): + self.warn.append("Found possible blank line(s) at " + "end of file '%s'" % last_fname) + outfd.write('+\n' * self.blank_count) + outfd.write(line + '\n') + self.blank_count = 0 + self.Finalize() + + +def GetMetaDataForList(commit_range, git_dir=None, count=None, + series = None, allow_overwrite=False): + """Reads out patch series metadata from the commits + + This does a 'git log' on the relevant commits and pulls out the tags we + are interested in. + + Args: + commit_range: Range of commits to count (e.g. 'HEAD..base') + git_dir: Path to git repositiory (None to use default) + count: Number of commits to list, or None for no limit + series: Series object to add information into. By default a new series + is started. + allow_overwrite: Allow tags to overwrite an existing tag + Returns: + A Series object containing information about the commits. + """ + if not series: + series = Series() + series.allow_overwrite = allow_overwrite + params = gitutil.LogCmd(commit_range, reverse=True, count=count, + git_dir=git_dir) + stdout = command.RunPipe([params], capture=True).stdout + ps = PatchStream(series, is_log=True) + for line in stdout.splitlines(): + ps.ProcessLine(line) + ps.Finalize() + return series + +def GetMetaData(start, count): + """Reads out patch series metadata from the commits + + This does a 'git log' on the relevant commits and pulls out the tags we + are interested in. + + Args: + start: Commit to start from: 0=HEAD, 1=next one, etc. + count: Number of commits to list + """ + return GetMetaDataForList('HEAD~%d' % start, None, count) + +def GetMetaDataForTest(text): + """Process metadata from a file containing a git log. Used for tests + + Args: + text: + """ + series = Series() + ps = PatchStream(series, is_log=True) + for line in text.splitlines(): + ps.ProcessLine(line) + ps.Finalize() + return series + +def FixPatch(backup_dir, fname, series, commit): + """Fix up a patch file, by adding/removing as required. + + We remove our tags from the patch file, insert changes lists, etc. + The patch file is processed in place, and overwritten. + + A backup file is put into backup_dir (if not None). + + Args: + fname: Filename to patch file to process + series: Series information about this patch set + commit: Commit object for this patch file + Return: + A list of errors, or [] if all ok. + """ + handle, tmpname = tempfile.mkstemp() + outfd = os.fdopen(handle, 'w') + infd = open(fname, 'r') + ps = PatchStream(series) + ps.commit = commit + ps.ProcessStream(infd, outfd) + infd.close() + outfd.close() + + # Create a backup file if required + if backup_dir: + shutil.copy(fname, os.path.join(backup_dir, os.path.basename(fname))) + shutil.move(tmpname, fname) + return ps.warn + +def FixPatches(series, fnames): + """Fix up a list of patches identified by filenames + + The patch files are processed in place, and overwritten. + + Args: + series: The series object + fnames: List of patch files to process + """ + # Current workflow creates patches, so we shouldn't need a backup + backup_dir = None #tempfile.mkdtemp('clean-patch') + count = 0 + for fname in fnames: + commit = series.commits[count] + commit.patch = fname + result = FixPatch(backup_dir, fname, series, commit) + if result: + print('%d warnings for %s:' % (len(result), fname)) + for warn in result: + print('\t', warn) + print + count += 1 + print('Cleaned %d patches' % count) + +def InsertCoverLetter(fname, series, count): + """Inserts a cover letter with the required info into patch 0 + + Args: + fname: Input / output filename of the cover letter file + series: Series object + count: Number of patches in the series + """ + fd = open(fname, 'r') + lines = fd.readlines() + fd.close() + + fd = open(fname, 'w') + text = series.cover + prefix = series.GetPatchPrefix() + for line in lines: + if line.startswith('Subject:'): + # if more than 10 or 100 patches, it should say 00/xx, 000/xxx, etc + zero_repeat = int(math.log10(count)) + 1 + zero = '0' * zero_repeat + line = 'Subject: [%s %s/%d] %s\n' % (prefix, zero, count, text[0]) + + # Insert our cover letter + elif line.startswith('*** BLURB HERE ***'): + # First the blurb test + line = '\n'.join(text[1:]) + '\n' + if series.get('notes'): + line += '\n'.join(series.notes) + '\n' + + # Now the change list + out = series.MakeChangeLog(None) + line += '\n' + '\n'.join(out) + fd.write(line) + fd.close() diff --git a/tools/u-boot-tools/patman/patman b/tools/u-boot-tools/patman/patman new file mode 120000 index 0000000..6cc3d7a --- /dev/null +++ b/tools/u-boot-tools/patman/patman @@ -0,0 +1 @@ +patman.py \ No newline at end of file diff --git a/tools/u-boot-tools/patman/patman.py b/tools/u-boot-tools/patman/patman.py new file mode 100755 index 0000000..27a2feb --- /dev/null +++ b/tools/u-boot-tools/patman/patman.py @@ -0,0 +1,183 @@ +#!/usr/bin/env python2 +# SPDX-License-Identifier: GPL-2.0+ +# +# Copyright (c) 2011 The Chromium OS Authors. +# + +"""See README for more information""" + +from optparse import OptionParser +import os +import re +import sys +import unittest + +# Our modules +try: + from patman import checkpatch, command, gitutil, patchstream, \ + project, settings, terminal, test +except ImportError: + import checkpatch + import command + import gitutil + import patchstream + import project + import settings + import terminal + import test + + +parser = OptionParser() +parser.add_option('-H', '--full-help', action='store_true', dest='full_help', + default=False, help='Display the README file') +parser.add_option('-c', '--count', dest='count', type='int', + default=-1, help='Automatically create patches from top n commits') +parser.add_option('-i', '--ignore-errors', action='store_true', + dest='ignore_errors', default=False, + help='Send patches email even if patch errors are found') +parser.add_option('-m', '--no-maintainers', action='store_false', + dest='add_maintainers', default=True, + help="Don't cc the file maintainers automatically") +parser.add_option('-l', '--limit-cc', dest='limit', type='int', + default=None, help='Limit the cc list to LIMIT entries [default: %default]') +parser.add_option('-n', '--dry-run', action='store_true', dest='dry_run', + default=False, help="Do a dry run (create but don't email patches)") +parser.add_option('-p', '--project', default=project.DetectProject(), + help="Project name; affects default option values and " + "aliases [default: %default]") +parser.add_option('-r', '--in-reply-to', type='string', action='store', + help="Message ID that this series is in reply to") +parser.add_option('-s', '--start', dest='start', type='int', + default=0, help='Commit to start creating patches from (0 = HEAD)') +parser.add_option('-t', '--ignore-bad-tags', action='store_true', + default=False, help='Ignore bad tags / aliases') +parser.add_option('--test', action='store_true', dest='test', + default=False, help='run tests') +parser.add_option('-v', '--verbose', action='store_true', dest='verbose', + default=False, help='Verbose output of errors and warnings') +parser.add_option('--cc-cmd', dest='cc_cmd', type='string', action='store', + default=None, help='Output cc list for patch file (used by git)') +parser.add_option('--no-check', action='store_false', dest='check_patch', + default=True, + help="Don't check for patch compliance") +parser.add_option('--no-tags', action='store_false', dest='process_tags', + default=True, help="Don't process subject tags as aliaes") +parser.add_option('--smtp-server', type='str', + help="Specify the SMTP server to 'git send-email'") +parser.add_option('-T', '--thread', action='store_true', dest='thread', + default=False, help='Create patches as a single thread') + +parser.usage += """ + +Create patches from commits in a branch, check them and email them as +specified by tags you place in the commits. Use -n to do a dry run first.""" + + +# Parse options twice: first to get the project and second to handle +# defaults properly (which depends on project). +(options, args) = parser.parse_args() +settings.Setup(parser, options.project, '') +(options, args) = parser.parse_args() + +if __name__ != "__main__": + pass + +# Run our meagre tests +elif options.test: + import doctest + import func_test + + sys.argv = [sys.argv[0]] + result = unittest.TestResult() + for module in (test.TestPatch, func_test.TestFunctional): + suite = unittest.TestLoader().loadTestsFromTestCase(module) + suite.run(result) + + for module in ['gitutil', 'settings']: + suite = doctest.DocTestSuite(module) + suite.run(result) + + # TODO: Surely we can just 'print' result? + print(result) + for test, err in result.errors: + print(err) + for test, err in result.failures: + print(err) + +# Called from git with a patch filename as argument +# Printout a list of additional CC recipients for this patch +elif options.cc_cmd: + fd = open(options.cc_cmd, 'r') + re_line = re.compile('(\S*) (.*)') + for line in fd.readlines(): + match = re_line.match(line) + if match and match.group(1) == args[0]: + for cc in match.group(2).split(', '): + cc = cc.strip() + if cc: + print(cc) + fd.close() + +elif options.full_help: + pager = os.getenv('PAGER') + if not pager: + pager = 'more' + fname = os.path.join(os.path.dirname(os.path.realpath(sys.argv[0])), + 'README') + command.Run(pager, fname) + +# Process commits, produce patches files, check them, email them +else: + gitutil.Setup() + + if options.count == -1: + # Work out how many patches to send if we can + options.count = gitutil.CountCommitsToBranch() - options.start + + col = terminal.Color() + if not options.count: + str = 'No commits found to process - please use -c flag' + sys.exit(col.Color(col.RED, str)) + + # Read the metadata from the commits + if options.count: + series = patchstream.GetMetaData(options.start, options.count) + cover_fname, args = gitutil.CreatePatches(options.start, options.count, + series) + + # Fix up the patch files to our liking, and insert the cover letter + patchstream.FixPatches(series, args) + if cover_fname and series.get('cover'): + patchstream.InsertCoverLetter(cover_fname, series, options.count) + + # Do a few checks on the series + series.DoChecks() + + # Check the patches, and run them through 'git am' just to be sure + if options.check_patch: + ok = checkpatch.CheckPatches(options.verbose, args) + else: + ok = True + + cc_file = series.MakeCcFile(options.process_tags, cover_fname, + not options.ignore_bad_tags, + options.add_maintainers, options.limit) + + # Email the patches out (giving the user time to check / cancel) + cmd = '' + its_a_go = ok or options.ignore_errors + if its_a_go: + cmd = gitutil.EmailPatches(series, cover_fname, args, + options.dry_run, not options.ignore_bad_tags, cc_file, + in_reply_to=options.in_reply_to, thread=options.thread, + smtp_server=options.smtp_server) + else: + print(col.Color(col.RED, "Not sending emails due to errors/warnings")) + + # For a dry run, just show our actions as a sanity check + if options.dry_run: + series.ShowActions(args, cmd, options.process_tags) + if not its_a_go: + print(col.Color(col.RED, "Email would not be sent")) + + os.remove(cc_file) diff --git a/tools/u-boot-tools/patman/project.py b/tools/u-boot-tools/patman/project.py new file mode 100644 index 0000000..1d9cfc0 --- /dev/null +++ b/tools/u-boot-tools/patman/project.py @@ -0,0 +1,26 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (c) 2012 The Chromium OS Authors. +# + +import os.path + +import gitutil + +def DetectProject(): + """Autodetect the name of the current project. + + This looks for signature files/directories that are unlikely to exist except + in the given project. + + Returns: + The name of the project, like "linux" or "u-boot". Returns "unknown" + if we can't detect the project. + """ + top_level = gitutil.GetTopLevel() + + if os.path.exists(os.path.join(top_level, "include", "u-boot")): + return "u-boot" + elif os.path.exists(os.path.join(top_level, "kernel")): + return "linux" + + return "unknown" diff --git a/tools/u-boot-tools/patman/series.py b/tools/u-boot-tools/patman/series.py new file mode 100644 index 0000000..2735afa --- /dev/null +++ b/tools/u-boot-tools/patman/series.py @@ -0,0 +1,292 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (c) 2011 The Chromium OS Authors. +# + +from __future__ import print_function + +import itertools +import os + +import get_maintainer +import gitutil +import settings +import terminal + +# Series-xxx tags that we understand +valid_series = ['to', 'cc', 'version', 'changes', 'prefix', 'notes', 'name', + 'cover_cc', 'process_log'] + +class Series(dict): + """Holds information about a patch series, including all tags. + + Vars: + cc: List of aliases/emails to Cc all patches to + commits: List of Commit objects, one for each patch + cover: List of lines in the cover letter + notes: List of lines in the notes + changes: (dict) List of changes for each version, The key is + the integer version number + allow_overwrite: Allow tags to overwrite an existing tag + """ + def __init__(self): + self.cc = [] + self.to = [] + self.cover_cc = [] + self.commits = [] + self.cover = None + self.notes = [] + self.changes = {} + self.allow_overwrite = False + + # Written in MakeCcFile() + # key: name of patch file + # value: list of email addresses + self._generated_cc = {} + + # These make us more like a dictionary + def __setattr__(self, name, value): + self[name] = value + + def __getattr__(self, name): + return self[name] + + def AddTag(self, commit, line, name, value): + """Add a new Series-xxx tag along with its value. + + Args: + line: Source line containing tag (useful for debug/error messages) + name: Tag name (part after 'Series-') + value: Tag value (part after 'Series-xxx: ') + """ + # If we already have it, then add to our list + name = name.replace('-', '_') + if name in self and not self.allow_overwrite: + values = value.split(',') + values = [str.strip() for str in values] + if type(self[name]) != type([]): + raise ValueError("In %s: line '%s': Cannot add another value " + "'%s' to series '%s'" % + (commit.hash, line, values, self[name])) + self[name] += values + + # Otherwise just set the value + elif name in valid_series: + if name=="notes": + self[name] = [value] + else: + self[name] = value + else: + raise ValueError("In %s: line '%s': Unknown 'Series-%s': valid " + "options are %s" % (commit.hash, line, name, + ', '.join(valid_series))) + + def AddCommit(self, commit): + """Add a commit into our list of commits + + We create a list of tags in the commit subject also. + + Args: + commit: Commit object to add + """ + commit.CheckTags() + self.commits.append(commit) + + def ShowActions(self, args, cmd, process_tags): + """Show what actions we will/would perform + + Args: + args: List of patch files we created + cmd: The git command we would have run + process_tags: Process tags as if they were aliases + """ + to_set = set(gitutil.BuildEmailList(self.to)); + cc_set = set(gitutil.BuildEmailList(self.cc)); + + col = terminal.Color() + print('Dry run, so not doing much. But I would do this:') + print() + print('Send a total of %d patch%s with %scover letter.' % ( + len(args), '' if len(args) == 1 else 'es', + self.get('cover') and 'a ' or 'no ')) + + # TODO: Colour the patches according to whether they passed checks + for upto in range(len(args)): + commit = self.commits[upto] + print(col.Color(col.GREEN, ' %s' % args[upto])) + cc_list = list(self._generated_cc[commit.patch]) + for email in set(cc_list) - to_set - cc_set: + if email == None: + email = col.Color(col.YELLOW, "<alias '%s' not found>" + % tag) + if email: + print(' Cc: ', email) + print + for item in to_set: + print('To:\t ', item) + for item in cc_set - to_set: + print('Cc:\t ', item) + print('Version: ', self.get('version')) + print('Prefix:\t ', self.get('prefix')) + if self.cover: + print('Cover: %d lines' % len(self.cover)) + cover_cc = gitutil.BuildEmailList(self.get('cover_cc', '')) + all_ccs = itertools.chain(cover_cc, *self._generated_cc.values()) + for email in set(all_ccs) - to_set - cc_set: + print(' Cc: ', email) + if cmd: + print('Git command: %s' % cmd) + + def MakeChangeLog(self, commit): + """Create a list of changes for each version. + + Return: + The change log as a list of strings, one per line + + Changes in v4: + - Jog the dial back closer to the widget + + Changes in v3: None + Changes in v2: + - Fix the widget + - Jog the dial + + etc. + """ + final = [] + process_it = self.get('process_log', '').split(',') + process_it = [item.strip() for item in process_it] + need_blank = False + for change in sorted(self.changes, reverse=True): + out = [] + for this_commit, text in self.changes[change]: + if commit and this_commit != commit: + continue + if 'uniq' not in process_it or text not in out: + out.append(text) + line = 'Changes in v%d:' % change + have_changes = len(out) > 0 + if 'sort' in process_it: + out = sorted(out) + if have_changes: + out.insert(0, line) + else: + out = [line + ' None'] + if need_blank: + out.insert(0, '') + final += out + need_blank = have_changes + if self.changes: + final.append('') + return final + + def DoChecks(self): + """Check that each version has a change log + + Print an error if something is wrong. + """ + col = terminal.Color() + if self.get('version'): + changes_copy = dict(self.changes) + for version in range(1, int(self.version) + 1): + if self.changes.get(version): + del changes_copy[version] + else: + if version > 1: + str = 'Change log missing for v%d' % version + print(col.Color(col.RED, str)) + for version in changes_copy: + str = 'Change log for unknown version v%d' % version + print(col.Color(col.RED, str)) + elif self.changes: + str = 'Change log exists, but no version is set' + print(col.Color(col.RED, str)) + + def MakeCcFile(self, process_tags, cover_fname, raise_on_error, + add_maintainers, limit): + """Make a cc file for us to use for per-commit Cc automation + + Also stores in self._generated_cc to make ShowActions() faster. + + Args: + process_tags: Process tags as if they were aliases + cover_fname: If non-None the name of the cover letter. + raise_on_error: True to raise an error when an alias fails to match, + False to just print a message. + add_maintainers: Either: + True/False to call the get_maintainers to CC maintainers + List of maintainers to include (for testing) + limit: Limit the length of the Cc list + Return: + Filename of temp file created + """ + col = terminal.Color() + # Look for commit tags (of the form 'xxx:' at the start of the subject) + fname = '/tmp/patman.%d' % os.getpid() + fd = open(fname, 'w') + all_ccs = [] + for commit in self.commits: + cc = [] + if process_tags: + cc += gitutil.BuildEmailList(commit.tags, + raise_on_error=raise_on_error) + cc += gitutil.BuildEmailList(commit.cc_list, + raise_on_error=raise_on_error) + if type(add_maintainers) == type(cc): + cc += add_maintainers + elif add_maintainers: + cc += get_maintainer.GetMaintainer(commit.patch) + for x in set(cc) & set(settings.bounces): + print(col.Color(col.YELLOW, 'Skipping "%s"' % x)) + cc = set(cc) - set(settings.bounces) + cc = [m.encode('utf-8') if type(m) != str else m for m in cc] + if limit is not None: + cc = cc[:limit] + all_ccs += cc + print(commit.patch, ', '.join(set(cc)), file=fd) + self._generated_cc[commit.patch] = cc + + if cover_fname: + cover_cc = gitutil.BuildEmailList(self.get('cover_cc', '')) + cover_cc = [m.encode('utf-8') if type(m) != str else m + for m in cover_cc] + cc_list = ', '.join([x.decode('utf-8') + for x in set(cover_cc + all_ccs)]) + print(cover_fname, cc_list.encode('utf-8'), file=fd) + + fd.close() + return fname + + def AddChange(self, version, commit, info): + """Add a new change line to a version. + + This will later appear in the change log. + + Args: + version: version number to add change list to + info: change line for this version + """ + if not self.changes.get(version): + self.changes[version] = [] + self.changes[version].append([commit, info]) + + def GetPatchPrefix(self): + """Get the patch version string + + Return: + Patch string, like 'RFC PATCH v5' or just 'PATCH' + """ + git_prefix = gitutil.GetDefaultSubjectPrefix() + if git_prefix: + git_prefix = '%s][' % git_prefix + else: + git_prefix = '' + + version = '' + if self.get('version'): + version = ' v%s' % self['version'] + + # Get patch name prefix + prefix = '' + if self.get('prefix'): + prefix = '%s ' % self['prefix'] + return '%s%sPATCH%s' % (git_prefix, prefix, version) diff --git a/tools/u-boot-tools/patman/settings.py b/tools/u-boot-tools/patman/settings.py new file mode 100644 index 0000000..ea2bc74 --- /dev/null +++ b/tools/u-boot-tools/patman/settings.py @@ -0,0 +1,356 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (c) 2011 The Chromium OS Authors. +# + +from __future__ import print_function + +try: + import configparser as ConfigParser +except: + import ConfigParser + +import os +import re + +import command +import gitutil + +"""Default settings per-project. + +These are used by _ProjectConfigParser. Settings names should match +the "dest" of the option parser from patman.py. +""" +_default_settings = { + "u-boot": {}, + "linux": { + "process_tags": "False", + } +} + +class _ProjectConfigParser(ConfigParser.SafeConfigParser): + """ConfigParser that handles projects. + + There are two main goals of this class: + - Load project-specific default settings. + - Merge general default settings/aliases with project-specific ones. + + # Sample config used for tests below... + >>> try: + ... from StringIO import StringIO + ... except ImportError: + ... from io import StringIO + >>> sample_config = ''' + ... [alias] + ... me: Peter P. <likesspiders@example.com> + ... enemies: Evil <evil@example.com> + ... + ... [sm_alias] + ... enemies: Green G. <ugly@example.com> + ... + ... [sm2_alias] + ... enemies: Doc O. <pus@example.com> + ... + ... [settings] + ... am_hero: True + ... ''' + + # Check to make sure that bogus project gets general alias. + >>> config = _ProjectConfigParser("zzz") + >>> config.readfp(StringIO(sample_config)) + >>> config.get("alias", "enemies") + u'Evil <evil@example.com>' + + # Check to make sure that alias gets overridden by project. + >>> config = _ProjectConfigParser("sm") + >>> config.readfp(StringIO(sample_config)) + >>> config.get("alias", "enemies") + u'Green G. <ugly@example.com>' + + # Check to make sure that settings get merged with project. + >>> config = _ProjectConfigParser("linux") + >>> config.readfp(StringIO(sample_config)) + >>> sorted(config.items("settings")) + [(u'am_hero', u'True'), (u'process_tags', u'False')] + + # Check to make sure that settings works with unknown project. + >>> config = _ProjectConfigParser("unknown") + >>> config.readfp(StringIO(sample_config)) + >>> sorted(config.items("settings")) + [(u'am_hero', u'True')] + """ + def __init__(self, project_name): + """Construct _ProjectConfigParser. + + In addition to standard SafeConfigParser initialization, this also loads + project defaults. + + Args: + project_name: The name of the project. + """ + self._project_name = project_name + ConfigParser.SafeConfigParser.__init__(self) + + # Update the project settings in the config based on + # the _default_settings global. + project_settings = "%s_settings" % project_name + if not self.has_section(project_settings): + self.add_section(project_settings) + project_defaults = _default_settings.get(project_name, {}) + for setting_name, setting_value in project_defaults.items(): + self.set(project_settings, setting_name, setting_value) + + def _to_unicode(self, val): + """Make sure a value is of type 'unicode' + + Args: + val: string or unicode object + + Returns: + unicode version of val + """ + return val if isinstance(val, unicode) else val.decode('utf-8') + + def get(self, section, option, *args, **kwargs): + """Extend SafeConfigParser to try project_section before section. + + Args: + See SafeConfigParser. + Returns: + See SafeConfigParser. + """ + try: + val = ConfigParser.SafeConfigParser.get( + self, "%s_%s" % (self._project_name, section), option, + *args, **kwargs + ) + except (ConfigParser.NoSectionError, ConfigParser.NoOptionError): + val = ConfigParser.SafeConfigParser.get( + self, section, option, *args, **kwargs + ) + return self._to_unicode(val) + + def items(self, section, *args, **kwargs): + """Extend SafeConfigParser to add project_section to section. + + Args: + See SafeConfigParser. + Returns: + See SafeConfigParser. + """ + project_items = [] + has_project_section = False + top_items = [] + + # Get items from the project section + try: + project_items = ConfigParser.SafeConfigParser.items( + self, "%s_%s" % (self._project_name, section), *args, **kwargs + ) + has_project_section = True + except ConfigParser.NoSectionError: + pass + + # Get top-level items + try: + top_items = ConfigParser.SafeConfigParser.items( + self, section, *args, **kwargs + ) + except ConfigParser.NoSectionError: + # If neither section exists raise the error on... + if not has_project_section: + raise + + item_dict = dict(top_items) + item_dict.update(project_items) + return {(self._to_unicode(item), self._to_unicode(val)) + for item, val in item_dict.iteritems()} + +def ReadGitAliases(fname): + """Read a git alias file. This is in the form used by git: + + alias uboot u-boot@lists.denx.de + alias wd Wolfgang Denk <wd@denx.de> + + Args: + fname: Filename to read + """ + try: + fd = open(fname, 'r') + except IOError: + print("Warning: Cannot find alias file '%s'" % fname) + return + + re_line = re.compile('alias\s+(\S+)\s+(.*)') + for line in fd.readlines(): + line = line.strip() + if not line or line[0] == '#': + continue + + m = re_line.match(line) + if not m: + print("Warning: Alias file line '%s' not understood" % line) + continue + + list = alias.get(m.group(1), []) + for item in m.group(2).split(','): + item = item.strip() + if item: + list.append(item) + alias[m.group(1)] = list + + fd.close() + +def CreatePatmanConfigFile(config_fname): + """Creates a config file under $(HOME)/.patman if it can't find one. + + Args: + config_fname: Default config filename i.e., $(HOME)/.patman + + Returns: + None + """ + name = gitutil.GetDefaultUserName() + if name == None: + name = raw_input("Enter name: ") + + email = gitutil.GetDefaultUserEmail() + + if email == None: + email = raw_input("Enter email: ") + + try: + f = open(config_fname, 'w') + except IOError: + print("Couldn't create patman config file\n") + raise + + print('''[alias] +me: %s <%s> + +[bounces] +nxp = Zhikang Zhang <zhikang.zhang@nxp.com> +''' % (name, email), file=f) + f.close(); + +def _UpdateDefaults(parser, config): + """Update the given OptionParser defaults based on config. + + We'll walk through all of the settings from the parser + For each setting we'll look for a default in the option parser. + If it's found we'll update the option parser default. + + The idea here is that the .patman file should be able to update + defaults but that command line flags should still have the final + say. + + Args: + parser: An instance of an OptionParser whose defaults will be + updated. + config: An instance of _ProjectConfigParser that we will query + for settings. + """ + defaults = parser.get_default_values() + for name, val in config.items('settings'): + if hasattr(defaults, name): + default_val = getattr(defaults, name) + if isinstance(default_val, bool): + val = config.getboolean('settings', name) + elif isinstance(default_val, int): + val = config.getint('settings', name) + parser.set_default(name, val) + else: + print("WARNING: Unknown setting %s" % name) + +def _ReadAliasFile(fname): + """Read in the U-Boot git alias file if it exists. + + Args: + fname: Filename to read. + """ + if os.path.exists(fname): + bad_line = None + with open(fname) as fd: + linenum = 0 + for line in fd: + linenum += 1 + line = line.strip() + if not line or line.startswith('#'): + continue + words = line.split(None, 2) + if len(words) < 3 or words[0] != 'alias': + if not bad_line: + bad_line = "%s:%d:Invalid line '%s'" % (fname, linenum, + line) + continue + alias[words[1]] = [s.strip() for s in words[2].split(',')] + if bad_line: + print(bad_line) + +def _ReadBouncesFile(fname): + """Read in the bounces file if it exists + + Args: + fname: Filename to read. + """ + if os.path.exists(fname): + with open(fname) as fd: + for line in fd: + if line.startswith('#'): + continue + bounces.add(line.strip()) + +def GetItems(config, section): + """Get the items from a section of the config. + + Args: + config: _ProjectConfigParser object containing settings + section: name of section to retrieve + + Returns: + List of (name, value) tuples for the section + """ + try: + return config.items(section) + except ConfigParser.NoSectionError as e: + return [] + except: + raise + +def Setup(parser, project_name, config_fname=''): + """Set up the settings module by reading config files. + + Args: + parser: The parser to update + project_name: Name of project that we're working on; we'll look + for sections named "project_section" as well. + config_fname: Config filename to read ('' for default) + """ + # First read the git alias file if available + _ReadAliasFile('doc/git-mailrc') + config = _ProjectConfigParser(project_name) + if config_fname == '': + config_fname = '%s/.patman' % os.getenv('HOME') + + if not os.path.exists(config_fname): + print("No config file found ~/.patman\nCreating one...\n") + CreatePatmanConfigFile(config_fname) + + config.read(config_fname) + + for name, value in GetItems(config, 'alias'): + alias[name] = value.split(',') + + _ReadBouncesFile('doc/bounces') + for name, value in GetItems(config, 'bounces'): + bounces.add(value) + + _UpdateDefaults(parser, config) + +# These are the aliases we understand, indexed by alias. Each member is a list. +alias = {} +bounces = set() + +if __name__ == "__main__": + import doctest + + doctest.testmod() diff --git a/tools/u-boot-tools/patman/setup.py b/tools/u-boot-tools/patman/setup.py new file mode 100644 index 0000000..43fdc00 --- /dev/null +++ b/tools/u-boot-tools/patman/setup.py @@ -0,0 +1,12 @@ +# SPDX-License-Identifier: GPL-2.0+ + +from distutils.core import setup +setup(name='patman', + version='1.0', + license='GPL-2.0+', + scripts=['patman'], + packages=['patman'], + package_dir={'patman': ''}, + package_data={'patman': ['README']}, + classifiers=['Environment :: Console', + 'Topic :: Software Development']) diff --git a/tools/u-boot-tools/patman/terminal.py b/tools/u-boot-tools/patman/terminal.py new file mode 100644 index 0000000..4ceab18 --- /dev/null +++ b/tools/u-boot-tools/patman/terminal.py @@ -0,0 +1,161 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (c) 2011 The Chromium OS Authors. +# + +"""Terminal utilities + +This module handles terminal interaction including ANSI color codes. +""" + +from __future__ import print_function + +import os +import sys + +# Selection of when we want our output to be colored +COLOR_IF_TERMINAL, COLOR_ALWAYS, COLOR_NEVER = range(3) + +# Initially, we are set up to print to the terminal +print_test_mode = False +print_test_list = [] + +class PrintLine: + """A line of text output + + Members: + text: Text line that was printed + newline: True to output a newline after the text + colour: Text colour to use + """ + def __init__(self, text, newline, colour): + self.text = text + self.newline = newline + self.colour = colour + + def __str__(self): + return 'newline=%s, colour=%s, text=%s' % (self.newline, self.colour, + self.text) + +def Print(text='', newline=True, colour=None): + """Handle a line of output to the terminal. + + In test mode this is recorded in a list. Otherwise it is output to the + terminal. + + Args: + text: Text to print + newline: True to add a new line at the end of the text + colour: Colour to use for the text + """ + if print_test_mode: + print_test_list.append(PrintLine(text, newline, colour)) + else: + if colour: + col = Color() + text = col.Color(colour, text) + print(text, end='') + if newline: + print() + else: + sys.stdout.flush() + +def SetPrintTestMode(): + """Go into test mode, where all printing is recorded""" + global print_test_mode + + print_test_mode = True + +def GetPrintTestLines(): + """Get a list of all lines output through Print() + + Returns: + A list of PrintLine objects + """ + global print_test_list + + ret = print_test_list + print_test_list = [] + return ret + +def EchoPrintTestLines(): + """Print out the text lines collected""" + for line in print_test_list: + if line.colour: + col = Color() + print(col.Color(line.colour, line.text), end='') + else: + print(line.text, end='') + if line.newline: + print() + + +class Color(object): + """Conditionally wraps text in ANSI color escape sequences.""" + BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE = range(8) + BOLD = -1 + BRIGHT_START = '\033[1;%dm' + NORMAL_START = '\033[22;%dm' + BOLD_START = '\033[1m' + RESET = '\033[0m' + + def __init__(self, colored=COLOR_IF_TERMINAL): + """Create a new Color object, optionally disabling color output. + + Args: + enabled: True if color output should be enabled. If False then this + class will not add color codes at all. + """ + try: + self._enabled = (colored == COLOR_ALWAYS or + (colored == COLOR_IF_TERMINAL and + os.isatty(sys.stdout.fileno()))) + except: + self._enabled = False + + def Start(self, color, bright=True): + """Returns a start color code. + + Args: + color: Color to use, .e.g BLACK, RED, etc. + + Returns: + If color is enabled, returns an ANSI sequence to start the given + color, otherwise returns empty string + """ + if self._enabled: + base = self.BRIGHT_START if bright else self.NORMAL_START + return base % (color + 30) + return '' + + def Stop(self): + """Retruns a stop color code. + + Returns: + If color is enabled, returns an ANSI color reset sequence, + otherwise returns empty string + """ + if self._enabled: + return self.RESET + return '' + + def Color(self, color, text, bright=True): + """Returns text with conditionally added color escape sequences. + + Keyword arguments: + color: Text color -- one of the color constants defined in this + class. + text: The text to color. + + Returns: + If self._enabled is False, returns the original text. If it's True, + returns text with color escape sequences based on the value of + color. + """ + if not self._enabled: + return text + if color == self.BOLD: + start = self.BOLD_START + else: + base = self.BRIGHT_START if bright else self.NORMAL_START + start = base % (color + 30) + return start + text + self.RESET diff --git a/tools/u-boot-tools/patman/test.py b/tools/u-boot-tools/patman/test.py new file mode 100644 index 0000000..e1b94bd --- /dev/null +++ b/tools/u-boot-tools/patman/test.py @@ -0,0 +1,276 @@ +# -*- coding: utf-8 -*- +# SPDX-License-Identifier: GPL-2.0+ +# +# Copyright (c) 2011 The Chromium OS Authors. +# + +import os +import tempfile +import unittest + +import checkpatch +import gitutil +import patchstream +import series + + +class TestPatch(unittest.TestCase): + """Test this program + + TODO: Write tests for the rest of the functionality + """ + + def testBasic(self): + """Test basic filter operation""" + data=''' + +From 656c9a8c31fa65859d924cd21da920d6ba537fad Mon Sep 17 00:00:00 2001 +From: Simon Glass <sjg@chromium.org> +Date: Thu, 28 Apr 2011 09:58:51 -0700 +Subject: [PATCH (resend) 3/7] Tegra2: Add more clock support + +This adds functions to enable/disable clocks and reset to on-chip peripherals. + +cmd/pci.c:152:11: warning: format ‘%llx’ expects argument of type + ‘long long unsigned int’, but argument 3 has type + ‘u64 {aka long unsigned int}’ [-Wformat=] + +BUG=chromium-os:13875 +TEST=build U-Boot for Seaboard, boot + +Change-Id: I80fe1d0c0b7dd10aa58ce5bb1d9290b6664d5413 + +Review URL: http://codereview.chromium.org/6900006 + +Signed-off-by: Simon Glass <sjg@chromium.org> +--- + arch/arm/cpu/armv7/tegra2/Makefile | 2 +- + arch/arm/cpu/armv7/tegra2/ap20.c | 57 ++---- + arch/arm/cpu/armv7/tegra2/clock.c | 163 +++++++++++++++++ +''' + expected=''' + +From 656c9a8c31fa65859d924cd21da920d6ba537fad Mon Sep 17 00:00:00 2001 +From: Simon Glass <sjg@chromium.org> +Date: Thu, 28 Apr 2011 09:58:51 -0700 +Subject: [PATCH (resend) 3/7] Tegra2: Add more clock support + +This adds functions to enable/disable clocks and reset to on-chip peripherals. + +cmd/pci.c:152:11: warning: format ‘%llx’ expects argument of type + ‘long long unsigned int’, but argument 3 has type + ‘u64 {aka long unsigned int}’ [-Wformat=] + +Signed-off-by: Simon Glass <sjg@chromium.org> +--- + + arch/arm/cpu/armv7/tegra2/Makefile | 2 +- + arch/arm/cpu/armv7/tegra2/ap20.c | 57 ++---- + arch/arm/cpu/armv7/tegra2/clock.c | 163 +++++++++++++++++ +''' + out = '' + inhandle, inname = tempfile.mkstemp() + infd = os.fdopen(inhandle, 'w') + infd.write(data) + infd.close() + + exphandle, expname = tempfile.mkstemp() + expfd = os.fdopen(exphandle, 'w') + expfd.write(expected) + expfd.close() + + patchstream.FixPatch(None, inname, series.Series(), None) + rc = os.system('diff -u %s %s' % (inname, expname)) + self.assertEqual(rc, 0) + + os.remove(inname) + os.remove(expname) + + def GetData(self, data_type): + data='''From 4924887af52713cabea78420eff03badea8f0035 Mon Sep 17 00:00:00 2001 +From: Simon Glass <sjg@chromium.org> +Date: Thu, 7 Apr 2011 10:14:41 -0700 +Subject: [PATCH 1/4] Add microsecond boot time measurement + +This defines the basics of a new boot time measurement feature. This allows +logging of very accurate time measurements as the boot proceeds, by using +an available microsecond counter. + +%s +--- + README | 11 ++++++++ + MAINTAINERS | 3 ++ + common/bootstage.c | 50 ++++++++++++++++++++++++++++++++++++ + include/bootstage.h | 71 +++++++++++++++++++++++++++++++++++++++++++++++++++ + include/common.h | 8 ++++++ + 5 files changed, 141 insertions(+), 0 deletions(-) + create mode 100644 common/bootstage.c + create mode 100644 include/bootstage.h + +diff --git a/README b/README +index 6f3748d..f9e4e65 100644 +--- a/README ++++ b/README +@@ -2026,6 +2026,17 @@ The following options need to be configured: + example, some LED's) on your board. At the moment, + the following checkpoints are implemented: + ++- Time boot progress ++ CONFIG_BOOTSTAGE ++ ++ Define this option to enable microsecond boot stage timing ++ on supported platforms. For this to work your platform ++ needs to define a function timer_get_us() which returns the ++ number of microseconds since reset. This would normally ++ be done in your SOC or board timer.c file. ++ ++ You can add calls to bootstage_mark() to set time markers. ++ + - Standalone program support: + CONFIG_STANDALONE_LOAD_ADDR + +diff --git a/MAINTAINERS b/MAINTAINERS +index b167b028ec..beb7dc634f 100644 +--- a/MAINTAINERS ++++ b/MAINTAINERS +@@ -474,3 +474,8 @@ S: Maintained + T: git git://git.denx.de/u-boot.git + F: * + F: */ ++ ++BOOTSTAGE ++M: Simon Glass <sjg@chromium.org> ++L: u-boot@lists.denx.de ++F: common/bootstage.c +diff --git a/common/bootstage.c b/common/bootstage.c +new file mode 100644 +index 0000000..2234c87 +--- /dev/null ++++ b/common/bootstage.c +@@ -0,0 +1,37 @@ ++%s ++/* ++ * Copyright (c) 2011, Google Inc. All rights reserved. ++ * ++ */ ++ ++/* ++ * This module records the progress of boot and arbitrary commands, and ++ * permits accurate timestamping of each. The records can optionally be ++ * passed to kernel in the ATAGs ++ */ ++ ++#include <common.h> ++ ++struct bootstage_record { ++ u32 time_us; ++ const char *name; ++}; ++ ++static struct bootstage_record record[BOOTSTAGE_COUNT]; ++ ++u32 bootstage_mark(enum bootstage_id id, const char *name) ++{ ++ struct bootstage_record *rec = &record[id]; ++ ++ /* Only record the first event for each */ ++%sif (!rec->name) { ++ rec->time_us = (u32)timer_get_us(); ++ rec->name = name; ++ } ++ if (!rec->name && ++ %ssomething_else) { ++ rec->time_us = (u32)timer_get_us(); ++ rec->name = name; ++ } ++%sreturn rec->time_us; ++} +-- +1.7.3.1 +''' + signoff = 'Signed-off-by: Simon Glass <sjg@chromium.org>\n' + license = '// SPDX-License-Identifier: GPL-2.0+' + tab = ' ' + indent = ' ' + if data_type == 'good': + pass + elif data_type == 'no-signoff': + signoff = '' + elif data_type == 'no-license': + license = '' + elif data_type == 'spaces': + tab = ' ' + elif data_type == 'indent': + indent = tab + else: + print('not implemented') + return data % (signoff, license, tab, indent, tab) + + def SetupData(self, data_type): + inhandle, inname = tempfile.mkstemp() + infd = os.fdopen(inhandle, 'w') + data = self.GetData(data_type) + infd.write(data) + infd.close() + return inname + + def testGood(self): + """Test checkpatch operation""" + inf = self.SetupData('good') + result = checkpatch.CheckPatch(inf) + self.assertEqual(result.ok, True) + self.assertEqual(result.problems, []) + self.assertEqual(result.errors, 0) + self.assertEqual(result.warnings, 0) + self.assertEqual(result.checks, 0) + self.assertEqual(result.lines, 62) + os.remove(inf) + + def testNoSignoff(self): + inf = self.SetupData('no-signoff') + result = checkpatch.CheckPatch(inf) + self.assertEqual(result.ok, False) + self.assertEqual(len(result.problems), 1) + self.assertEqual(result.errors, 1) + self.assertEqual(result.warnings, 0) + self.assertEqual(result.checks, 0) + self.assertEqual(result.lines, 62) + os.remove(inf) + + def testNoLicense(self): + inf = self.SetupData('no-license') + result = checkpatch.CheckPatch(inf) + self.assertEqual(result.ok, False) + self.assertEqual(len(result.problems), 1) + self.assertEqual(result.errors, 0) + self.assertEqual(result.warnings, 1) + self.assertEqual(result.checks, 0) + self.assertEqual(result.lines, 62) + os.remove(inf) + + def testSpaces(self): + inf = self.SetupData('spaces') + result = checkpatch.CheckPatch(inf) + self.assertEqual(result.ok, False) + self.assertEqual(len(result.problems), 3) + self.assertEqual(result.errors, 0) + self.assertEqual(result.warnings, 3) + self.assertEqual(result.checks, 0) + self.assertEqual(result.lines, 62) + os.remove(inf) + + def testIndent(self): + inf = self.SetupData('indent') + result = checkpatch.CheckPatch(inf) + self.assertEqual(result.ok, False) + self.assertEqual(len(result.problems), 1) + self.assertEqual(result.errors, 0) + self.assertEqual(result.warnings, 0) + self.assertEqual(result.checks, 1) + self.assertEqual(result.lines, 62) + os.remove(inf) + + +if __name__ == "__main__": + unittest.main() + gitutil.RunTests() diff --git a/tools/u-boot-tools/patman/test/test01.txt b/tools/u-boot-tools/patman/test/test01.txt new file mode 100644 index 0000000..478ea93 --- /dev/null +++ b/tools/u-boot-tools/patman/test/test01.txt @@ -0,0 +1,56 @@ +commit b9da5f937bd5ea4931ea17459bf79b2905d9594d +Author: Simon Glass <sjg@chromium.org> +Date: Sat Apr 15 15:39:08 2017 -0600 + + pci: Correct cast for sandbox + + This gives a warning with some native compilers: + + cmd/pci.c:152:11: warning: format ‘%llx’ expects argument of type + ‘long long unsigned int’, but argument 3 has type + ‘u64 {aka long unsigned int}’ [-Wformat=] + + Fix it with a cast. + + Signed-off-by: Simon Glass <sjg@chromium.org> + Series-notes: + some notes + about some things + from the first commit + END + + Commit-notes: + Some notes about + the first commit + END + +commit 5ab48490f03051875ab13d288a4bf32b507d76fd +Author: Simon Glass <sjg@chromium.org> +Date: Sat Apr 15 15:39:08 2017 -0600 + + fdt: Correct cast for sandbox in fdtdec_setup_mem_size_base() + + This gives a warning with some native compilers: + + lib/fdtdec.c:1203:8: warning: format ‘%llx’ expects argument of type + ‘long long unsigned int’, but argument 3 has type + ‘long unsigned int’ [-Wformat=] + + Fix it with a cast. + + Signed-off-by: Simon Glass <sjg@chromium.org> + Series-to: u-boot + Series-prefix: RFC + Series-cc: Stefan Brüns <stefan.bruens@rwth-aachen.de> + Cover-letter-cc: Lord Mëlchett <clergy@palace.gov> + Series-version: 3 + Patch-cc: fred + Series-changes: 4 + - Some changes + + Cover-letter: + test: A test patch series + This is a test of how the cover + leter + works + END diff --git a/tools/u-boot-tools/patman/test_util.py b/tools/u-boot-tools/patman/test_util.py new file mode 100644 index 0000000..687d407 --- /dev/null +++ b/tools/u-boot-tools/patman/test_util.py @@ -0,0 +1,85 @@ +# SPDX-License-Identifier: GPL-2.0+ +# +# Copyright (c) 2016 Google, Inc +# + +from contextlib import contextmanager +import glob +import os +import sys + +import command + +try: + from StringIO import StringIO +except ImportError: + from io import StringIO + + +def RunTestCoverage(prog, filter_fname, exclude_list, build_dir, required=None): + """Run tests and check that we get 100% coverage + + Args: + prog: Program to run (with be passed a '-t' argument to run tests + filter_fname: Normally all *.py files in the program's directory will + be included. If this is not None, then it is used to filter the + list so that only filenames that don't contain filter_fname are + included. + exclude_list: List of file patterns to exclude from the coverage + calculation + build_dir: Build directory, used to locate libfdt.py + required: List of modules which must be in the coverage report + + Raises: + ValueError if the code coverage is not 100% + """ + # This uses the build output from sandbox_spl to get _libfdt.so + path = os.path.dirname(prog) + if filter_fname: + glob_list = glob.glob(os.path.join(path, '*.py')) + glob_list = [fname for fname in glob_list if filter_fname in fname] + else: + glob_list = [] + glob_list += exclude_list + glob_list += ['*libfdt.py', '*site-packages*'] + cmd = ('PYTHONPATH=$PYTHONPATH:%s/sandbox_spl/tools python-coverage run ' + '--omit "%s" %s -P1 -t' % (build_dir, ','.join(glob_list), prog)) + os.system(cmd) + stdout = command.Output('python-coverage', 'report') + lines = stdout.splitlines() + if required: + # Convert '/path/to/name.py' just the module name 'name' + test_set = set([os.path.splitext(os.path.basename(line.split()[0]))[0] + for line in lines if '/etype/' in line]) + missing_list = required + missing_list.difference_update(test_set) + if missing_list: + print 'Missing tests for %s' % (', '.join(missing_list)) + print stdout + ok = False + + coverage = lines[-1].split(' ')[-1] + ok = True + print coverage + if coverage != '100%': + print stdout + print ("Type 'python-coverage html' to get a report in " + 'htmlcov/index.html') + print 'Coverage error: %s, but should be 100%%' % coverage + ok = False + if not ok: + raise ValueError('Test coverage failure') + + +# Use this to suppress stdout/stderr output: +# with capture_sys_output() as (stdout, stderr) +# ...do something... +@contextmanager +def capture_sys_output(): + capture_out, capture_err = StringIO(), StringIO() + old_out, old_err = sys.stdout, sys.stderr + try: + sys.stdout, sys.stderr = capture_out, capture_err + yield capture_out, capture_err + finally: + sys.stdout, sys.stderr = old_out, old_err diff --git a/tools/u-boot-tools/patman/tools.py b/tools/u-boot-tools/patman/tools.py new file mode 100644 index 0000000..bf09979 --- /dev/null +++ b/tools/u-boot-tools/patman/tools.py @@ -0,0 +1,241 @@ +# SPDX-License-Identifier: GPL-2.0+ +# +# Copyright (c) 2016 Google, Inc +# + +import command +import glob +import os +import shutil +import tempfile + +import tout + +# Output directly (generally this is temporary) +outdir = None + +# True to keep the output directory around after exiting +preserve_outdir = False + +# Path to the Chrome OS chroot, if we know it +chroot_path = None + +# Search paths to use for Filename(), used to find files +search_paths = [] + +# Tools and the packages that contain them, on debian +packages = { + 'lz4': 'liblz4-tool', + } + +# List of paths to use when looking for an input file +indir = [] + +def PrepareOutputDir(dirname, preserve=False): + """Select an output directory, ensuring it exists. + + This either creates a temporary directory or checks that the one supplied + by the user is valid. For a temporary directory, it makes a note to + remove it later if required. + + Args: + dirname: a string, name of the output directory to use to store + intermediate and output files. If is None - create a temporary + directory. + preserve: a Boolean. If outdir above is None and preserve is False, the + created temporary directory will be destroyed on exit. + + Raises: + OSError: If it cannot create the output directory. + """ + global outdir, preserve_outdir + + preserve_outdir = dirname or preserve + if dirname: + outdir = dirname + if not os.path.isdir(outdir): + try: + os.makedirs(outdir) + except OSError as err: + raise CmdError("Cannot make output directory '%s': '%s'" % + (outdir, err.strerror)) + tout.Debug("Using output directory '%s'" % outdir) + else: + outdir = tempfile.mkdtemp(prefix='binman.') + tout.Debug("Using temporary directory '%s'" % outdir) + +def _RemoveOutputDir(): + global outdir + + shutil.rmtree(outdir) + tout.Debug("Deleted temporary directory '%s'" % outdir) + outdir = None + +def FinaliseOutputDir(): + global outdir, preserve_outdir + + """Tidy up: delete output directory if temporary and not preserved.""" + if outdir and not preserve_outdir: + _RemoveOutputDir() + +def GetOutputFilename(fname): + """Return a filename within the output directory. + + Args: + fname: Filename to use for new file + + Returns: + The full path of the filename, within the output directory + """ + return os.path.join(outdir, fname) + +def _FinaliseForTest(): + """Remove the output directory (for use by tests)""" + global outdir + + if outdir: + _RemoveOutputDir() + +def SetInputDirs(dirname): + """Add a list of input directories, where input files are kept. + + Args: + dirname: a list of paths to input directories to use for obtaining + files needed by binman to place in the image. + """ + global indir + + indir = dirname + tout.Debug("Using input directories %s" % indir) + +def GetInputFilename(fname): + """Return a filename for use as input. + + Args: + fname: Filename to use for new file + + Returns: + The full path of the filename, within the input directory + """ + if not indir: + return fname + for dirname in indir: + pathname = os.path.join(dirname, fname) + if os.path.exists(pathname): + return pathname + + raise ValueError("Filename '%s' not found in input path (%s) (cwd='%s')" % + (fname, ','.join(indir), os.getcwd())) + +def GetInputFilenameGlob(pattern): + """Return a list of filenames for use as input. + + Args: + pattern: Filename pattern to search for + + Returns: + A list of matching files in all input directories + """ + if not indir: + return glob.glob(fname) + files = [] + for dirname in indir: + pathname = os.path.join(dirname, pattern) + files += glob.glob(pathname) + return sorted(files) + +def Align(pos, align): + if align: + mask = align - 1 + pos = (pos + mask) & ~mask + return pos + +def NotPowerOfTwo(num): + return num and (num & (num - 1)) + +def PathHasFile(fname): + """Check if a given filename is in the PATH + + Args: + fname: Filename to check + + Returns: + True if found, False if not + """ + for dir in os.environ['PATH'].split(':'): + if os.path.exists(os.path.join(dir, fname)): + return True + return False + +def Run(name, *args): + try: + return command.Run(name, *args, cwd=outdir, capture=True) + except: + if not PathHasFile(name): + msg = "Plesae install tool '%s'" % name + package = packages.get(name) + if package: + msg += " (e.g. from package '%s')" % package + raise ValueError(msg) + raise + +def Filename(fname): + """Resolve a file path to an absolute path. + + If fname starts with ##/ and chroot is available, ##/ gets replaced with + the chroot path. If chroot is not available, this file name can not be + resolved, `None' is returned. + + If fname is not prepended with the above prefix, and is not an existing + file, the actual file name is retrieved from the passed in string and the + search_paths directories (if any) are searched to for the file. If found - + the path to the found file is returned, `None' is returned otherwise. + + Args: + fname: a string, the path to resolve. + + Returns: + Absolute path to the file or None if not found. + """ + if fname.startswith('##/'): + if chroot_path: + fname = os.path.join(chroot_path, fname[3:]) + else: + return None + + # Search for a pathname that exists, and return it if found + if fname and not os.path.exists(fname): + for path in search_paths: + pathname = os.path.join(path, os.path.basename(fname)) + if os.path.exists(pathname): + return pathname + + # If not found, just return the standard, unchanged path + return fname + +def ReadFile(fname): + """Read and return the contents of a file. + + Args: + fname: path to filename to read, where ## signifiies the chroot. + + Returns: + data read from file, as a string. + """ + with open(Filename(fname), 'rb') as fd: + data = fd.read() + #self._out.Info("Read file '%s' size %d (%#0x)" % + #(fname, len(data), len(data))) + return data + +def WriteFile(fname, data): + """Write data into a file. + + Args: + fname: path to filename to write + data: data to write to file, as a string + """ + #self._out.Info("Write file '%s' size %d (%#0x)" % + #(fname, len(data), len(data))) + with open(Filename(fname), 'wb') as fd: + fd.write(data) diff --git a/tools/u-boot-tools/patman/tout.py b/tools/u-boot-tools/patman/tout.py new file mode 100644 index 0000000..4957c7a --- /dev/null +++ b/tools/u-boot-tools/patman/tout.py @@ -0,0 +1,171 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (c) 2016 Google, Inc +# +# Terminal output logging. +# + +import sys + +import terminal + +# Output verbosity levels that we support +ERROR = 0 +WARNING = 1 +NOTICE = 2 +INFO = 3 +DEBUG = 4 + +in_progress = False + +""" +This class handles output of progress and other useful information +to the user. It provides for simple verbosity level control and can +output nothing but errors at verbosity zero. + +The idea is that modules set up an Output object early in their years and pass +it around to other modules that need it. This keeps the output under control +of a single class. + +Public properties: + verbose: Verbosity level: 0=silent, 1=progress, 3=full, 4=debug +""" +def __enter__(): + return + +def __exit__(unused1, unused2, unused3): + """Clean up and remove any progress message.""" + ClearProgress() + return False + +def UserIsPresent(): + """This returns True if it is likely that a user is present. + + Sometimes we want to prompt the user, but if no one is there then this + is a waste of time, and may lock a script which should otherwise fail. + + Returns: + True if it thinks the user is there, and False otherwise + """ + return stdout_is_tty and verbose > 0 + +def ClearProgress(): + """Clear any active progress message on the terminal.""" + global in_progress + if verbose > 0 and stdout_is_tty and in_progress: + _stdout.write('\r%s\r' % (" " * len (_progress))) + _stdout.flush() + in_progress = False + +def Progress(msg, warning=False, trailer='...'): + """Display progress information. + + Args: + msg: Message to display. + warning: True if this is a warning.""" + global in_progress + ClearProgress() + if verbose > 0: + _progress = msg + trailer + if stdout_is_tty: + col = _color.YELLOW if warning else _color.GREEN + _stdout.write('\r' + _color.Color(col, _progress)) + _stdout.flush() + in_progress = True + else: + _stdout.write(_progress + '\n') + +def _Output(level, msg, color=None): + """Output a message to the terminal. + + Args: + level: Verbosity level for this message. It will only be displayed if + this as high as the currently selected level. + msg; Message to display. + error: True if this is an error message, else False. + """ + if verbose >= level: + ClearProgress() + if color: + msg = _color.Color(color, msg) + _stdout.write(msg + '\n') + +def DoOutput(level, msg): + """Output a message to the terminal. + + Args: + level: Verbosity level for this message. It will only be displayed if + this as high as the currently selected level. + msg; Message to display. + """ + _Output(level, msg) + +def Error(msg): + """Display an error message + + Args: + msg; Message to display. + """ + _Output(0, msg, _color.RED) + +def Warning(msg): + """Display a warning message + + Args: + msg; Message to display. + """ + _Output(1, msg, _color.YELLOW) + +def Notice(msg): + """Display an important infomation message + + Args: + msg; Message to display. + """ + _Output(2, msg) + +def Info(msg): + """Display an infomation message + + Args: + msg; Message to display. + """ + _Output(3, msg) + +def Debug(msg): + """Display a debug message + + Args: + msg; Message to display. + """ + _Output(4, msg) + +def UserOutput(msg): + """Display a message regardless of the current output level. + + This is used when the output was specifically requested by the user. + Args: + msg; Message to display. + """ + _Output(0, msg) + +def Init(_verbose=WARNING, stdout=sys.stdout): + """Initialize a new output object. + + Args: + verbose: Verbosity level (0-4). + stdout: File to use for stdout. + """ + global verbose, _progress, _color, _stdout, stdout_is_tty + + verbose = _verbose + _progress = '' # Our last progress message + _color = terminal.Color() + _stdout = stdout + + # TODO(sjg): Move this into Chromite libraries when we have them + stdout_is_tty = hasattr(sys.stdout, 'isatty') and sys.stdout.isatty() + +def Uninit(): + ClearProgress() + +Init() diff --git a/tools/u-boot-tools/pbl_crc32.c b/tools/u-boot-tools/pbl_crc32.c new file mode 100644 index 0000000..06da1d9 --- /dev/null +++ b/tools/u-boot-tools/pbl_crc32.c @@ -0,0 +1,55 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2012 Freescale Semiconductor, Inc. + * + * Cleaned up and refactored by Charles Manning. + */ +#include "pblimage.h" + +static uint32_t crc_table[256]; +static int crc_table_valid; + +static void make_crc_table(void) +{ + uint32_t mask; + int i, j; + uint32_t poly; /* polynomial exclusive-or pattern */ + + if (crc_table_valid) + return; + + /* + * the polynomial used by PBL is 1 + x1 + x2 + x4 + x5 + x7 + x8 + x10 + * + x11 + x12 + x16 + x22 + x23 + x26 + x32. + */ + poly = 0x04c11db7; + + for (i = 0; i < 256; i++) { + mask = i << 24; + for (j = 0; j < 8; j++) { + if (mask & 0x80000000) + mask = (mask << 1) ^ poly; + else + mask <<= 1; + } + crc_table[i] = mask; + } + + crc_table_valid = 1; +} + +uint32_t pbl_crc32(uint32_t in_crc, const char *buf, uint32_t len) +{ + uint32_t crc32_val; + int i; + + make_crc_table(); + + crc32_val = ~in_crc; + + for (i = 0; i < len; i++) + crc32_val = (crc32_val << 8) ^ + crc_table[(crc32_val >> 24) ^ (*buf++ & 0xff)]; + + return crc32_val; +} diff --git a/tools/u-boot-tools/pbl_crc32.h b/tools/u-boot-tools/pbl_crc32.h new file mode 100644 index 0000000..4320a47 --- /dev/null +++ b/tools/u-boot-tools/pbl_crc32.h @@ -0,0 +1,12 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright 2012 Freescale Semiconductor, Inc. + */ + +#ifndef PBLCRC32_H +#define PBLCRC32_H + +#include <stdint.h> +uint32_t pbl_crc32(uint32_t in_crc, const char *buf, uint32_t len); + +#endif diff --git a/tools/u-boot-tools/pbl_crc32.o b/tools/u-boot-tools/pbl_crc32.o new file mode 100644 index 0000000000000000000000000000000000000000..9862217786477537f218dcef560e0bebdc1a65bc GIT binary patch literal 1616 zcmb<-^>JfjWMqH=Mg}_u1P><4z!1QUU^{@B4h%dD+zj2UAWm@WfznuyUS1H})$pW8 z?@R_31_pk42L_kU6OKDL7(ko`kgQ|pVa5ZForfG7KW&#i$l}_1+mYYnkYn=;#?mL9 zj2_JgSsWV=GB7YOdRX2pJ#-vwI><epKfYgRy;P#p{E883k4JBf7~=$w=3|VVhYr4w zFmn5U@DT_9wgbi@jhz=fEdP~0i9XE0z~JueY^9*#lA4s6n5ST&XRK$SYo-ZdfZQVj zB0%<41u-yI2rx?XuyagcWMGhCfMAgHJP_f|C(z8qG>s4B7Z4u@2Dz1m;p2a7(#%*x zmVtqp0V!~iG%|w%5s43Cf`XV4Nt^{F$iToL3-SX43xfibkEDcwnSl)^ib69pu%qx{ z9A*Y^u*0}uTAhJ`K?uqH;4lHn6eQ)uCl@6f8|fu8Bo`&emn0_Tq{f#e=47TI_zZAW zAT1!w1R@w17(g`04}bnc03Vt-C>}sU2_OPV{SS!p|7B?6F!iu-0GSWMP-P4Zdc~Ew zC5cH4dc`G05IO_KDoV{s)GJA?C}Ge`Ni0cZ&`T;VX3$H{&&^HED`C*f%P&dQbN36? zEiOq+&Sua9D^JacPb*5yO@*6JAp_)YP#QvyF-V?cV1S1^)E)sS4WpzP7{E~vQx6lb zfCd6A967=1g@FN<oF$+N)u7_&De@jv9-RWY6<JJ&0b(Y){s&M+3ZTe_vOyHczaSch z4WafMzyzQ)Og|`{LxsQ;%zhYc1=SChL?Zqov5}cDeK0Y9sD5a0!GsxLN#6n?0%pO& z56od;VDQ2b{x_fo!onF8{-E3k(+?8|xd*BgW+a%l07-zt7D{t5Fff2Hx_*$k=wS~s lzyWF~D11SD7={`L<1z>`fXX8{7tZ(q)gK8LfiTeZ0{|uGlfwW2 literal 0 HcmV?d00001 diff --git a/tools/u-boot-tools/pblimage.c b/tools/u-boot-tools/pblimage.c new file mode 100644 index 0000000..d11f9af --- /dev/null +++ b/tools/u-boot-tools/pblimage.c @@ -0,0 +1,329 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2012-2014 Freescale Semiconductor, Inc. + */ +#include "imagetool.h" +#include <image.h> +#include "pblimage.h" +#include "pbl_crc32.h" + +#define roundup(x, y) ((((x) + ((y) - 1)) / (y)) * (y)) +#define PBL_ACS_CONT_CMD 0x81000000 +#define PBL_ADDR_24BIT_MASK 0x00ffffff + +/* + * Initialize to an invalid value. + */ +static uint32_t next_pbl_cmd = 0x82000000; +/* + * need to store all bytes in memory for calculating crc32, then write the + * bytes to image file for PBL boot. + */ +static unsigned char mem_buf[1000000]; +static unsigned char *pmem_buf = mem_buf; +static int pbl_size; +static char *fname = "Unknown"; +static int lineno = -1; +static struct pbl_header pblimage_header; +static int uboot_size; +static int arch_flag; + +static uint32_t pbl_cmd_initaddr; +static uint32_t pbi_crc_cmd1; +static uint32_t pbi_crc_cmd2; +static uint32_t pbl_end_cmd[4]; + +static union +{ + char c[4]; + unsigned char l; +} endian_test = { {'l', '?', '?', 'b'} }; + +#define ENDIANNESS ((char)endian_test.l) + +/* + * The PBL can load up to 64 bytes at a time, so we split the U-Boot + * image into 64 byte chunks. PBL needs a command for each piece, of + * the form "81xxxxxx", where "xxxxxx" is the offset. Calculate the + * start offset by subtracting the size of the u-boot image from the + * top of the allowable 24-bit range. + */ +static void generate_pbl_cmd(void) +{ + uint32_t val = next_pbl_cmd; + next_pbl_cmd += 0x40; + int i; + + for (i = 3; i >= 0; i--) { + *pmem_buf++ = (val >> (i * 8)) & 0xff; + pbl_size++; + } +} + +static void pbl_fget(size_t size, FILE *stream) +{ + unsigned char c = 0xff; + int c_temp; + + while (size) { + c_temp = fgetc(stream); + if (c_temp != EOF) + c = (unsigned char)c_temp; + else if ((c_temp == EOF) && (arch_flag == IH_ARCH_ARM)) + c = 0xff; + *pmem_buf++ = c; + pbl_size++; + size--; + } +} + +/* load split u-boot with PBI command 81xxxxxx. */ +static void load_uboot(FILE *fp_uboot) +{ + next_pbl_cmd = pbl_cmd_initaddr - uboot_size; + while (next_pbl_cmd < pbl_cmd_initaddr) { + generate_pbl_cmd(); + pbl_fget(64, fp_uboot); + } +} + +static void check_get_hexval(char *token) +{ + uint32_t hexval; + int i; + + if (!sscanf(token, "%x", &hexval)) { + printf("Error:%s[%d] - Invalid hex data(%s)\n", fname, + lineno, token); + exit(EXIT_FAILURE); + } + for (i = 3; i >= 0; i--) { + *pmem_buf++ = (hexval >> (i * 8)) & 0xff; + pbl_size++; + } +} + +static void pbl_parser(char *name) +{ + FILE *fd = NULL; + char *line = NULL; + char *token, *saveptr1, *saveptr2; + size_t len = 0; + + fname = name; + fd = fopen(name, "r"); + if (fd == NULL) { + printf("Error:%s - Can't open\n", fname); + exit(EXIT_FAILURE); + } + + while ((getline(&line, &len, fd)) > 0) { + lineno++; + token = strtok_r(line, "\r\n", &saveptr1); + /* drop all lines with zero tokens (= empty lines) */ + if (token == NULL) + continue; + for (line = token;; line = NULL) { + token = strtok_r(line, " \t", &saveptr2); + if (token == NULL) + break; + /* Drop all text starting with '#' as comments */ + if (token[0] == '#') + break; + check_get_hexval(token); + } + } + if (line) + free(line); + fclose(fd); +} + +static uint32_t reverse_byte(uint32_t val) +{ + uint32_t temp; + unsigned char *p1; + int j; + + temp = val; + p1 = (unsigned char *)&temp; + for (j = 3; j >= 0; j--) + *p1++ = (val >> (j * 8)) & 0xff; + return temp; +} + +/* write end command and crc command to memory. */ +static void add_end_cmd(void) +{ + uint32_t crc32_pbl; + int i; + unsigned char *p = (unsigned char *)&pbl_end_cmd; + + if (ENDIANNESS == 'l') { + for (i = 0; i < 4; i++) + pbl_end_cmd[i] = reverse_byte(pbl_end_cmd[i]); + } + + for (i = 0; i < 16; i++) { + *pmem_buf++ = *p++; + pbl_size++; + } + + /* Add PBI CRC command. */ + *pmem_buf++ = 0x08; + *pmem_buf++ = pbi_crc_cmd1; + *pmem_buf++ = pbi_crc_cmd2; + *pmem_buf++ = 0x40; + pbl_size += 4; + + /* calculated CRC32 and write it to memory. */ + crc32_pbl = pbl_crc32(0, (const char *)mem_buf, pbl_size); + *pmem_buf++ = (crc32_pbl >> 24) & 0xff; + *pmem_buf++ = (crc32_pbl >> 16) & 0xff; + *pmem_buf++ = (crc32_pbl >> 8) & 0xff; + *pmem_buf++ = (crc32_pbl) & 0xff; + pbl_size += 4; +} + +void pbl_load_uboot(int ifd, struct image_tool_params *params) +{ + FILE *fp_uboot; + int size; + + /* parse the rcw.cfg file. */ + pbl_parser(params->imagename); + + /* parse the pbi.cfg file. */ + if (params->imagename2[0] != '\0') + pbl_parser(params->imagename2); + + if (params->datafile) { + fp_uboot = fopen(params->datafile, "r"); + if (fp_uboot == NULL) { + printf("Error: %s open failed\n", params->datafile); + exit(EXIT_FAILURE); + } + + load_uboot(fp_uboot); + fclose(fp_uboot); + } + add_end_cmd(); + lseek(ifd, 0, SEEK_SET); + + size = pbl_size; + if (write(ifd, (const void *)&mem_buf, size) != size) { + fprintf(stderr, "Write error on %s: %s\n", + params->imagefile, strerror(errno)); + exit(EXIT_FAILURE); + } +} + +static int pblimage_check_image_types(uint8_t type) +{ + if (type == IH_TYPE_PBLIMAGE) + return EXIT_SUCCESS; + else + return EXIT_FAILURE; +} + +static int pblimage_verify_header(unsigned char *ptr, int image_size, + struct image_tool_params *params) +{ + struct pbl_header *pbl_hdr = (struct pbl_header *) ptr; + + /* Only a few checks can be done: search for magic numbers */ + if (ENDIANNESS == 'l') { + if (pbl_hdr->preamble != reverse_byte(RCW_PREAMBLE)) + return -FDT_ERR_BADSTRUCTURE; + + if (pbl_hdr->rcwheader != reverse_byte(RCW_HEADER)) + return -FDT_ERR_BADSTRUCTURE; + } else { + if (pbl_hdr->preamble != RCW_PREAMBLE) + return -FDT_ERR_BADSTRUCTURE; + + if (pbl_hdr->rcwheader != RCW_HEADER) + return -FDT_ERR_BADSTRUCTURE; + } + return 0; +} + +static void pblimage_print_header(const void *ptr) +{ + printf("Image Type: Freescale PBL Boot Image\n"); +} + +static void pblimage_set_header(void *ptr, struct stat *sbuf, int ifd, + struct image_tool_params *params) +{ + /*nothing need to do, pbl_load_uboot takes care of whole file. */ +} + +int pblimage_check_params(struct image_tool_params *params) +{ + FILE *fp_uboot; + int fd; + struct stat st; + + if (!params) + return EXIT_FAILURE; + + if (params->datafile) { + fp_uboot = fopen(params->datafile, "r"); + if (fp_uboot == NULL) { + printf("Error: %s open failed\n", params->datafile); + exit(EXIT_FAILURE); + } + fd = fileno(fp_uboot); + + if (fstat(fd, &st) == -1) { + printf("Error: Could not determine u-boot image size. %s\n", + strerror(errno)); + exit(EXIT_FAILURE); + } + + /* For the variable size, pad it to 64 byte boundary */ + uboot_size = roundup(st.st_size, 64); + fclose(fp_uboot); + } + + if (params->arch == IH_ARCH_ARM) { + arch_flag = IH_ARCH_ARM; + pbi_crc_cmd1 = 0x61; + pbi_crc_cmd2 = 0; + pbl_cmd_initaddr = params->addr & PBL_ADDR_24BIT_MASK; + pbl_cmd_initaddr |= PBL_ACS_CONT_CMD; + pbl_cmd_initaddr += uboot_size; + pbl_end_cmd[0] = 0x09610000; + pbl_end_cmd[1] = 0x00000000; + pbl_end_cmd[2] = 0x096100c0; + pbl_end_cmd[3] = 0x00000000; + } else if (params->arch == IH_ARCH_PPC) { + arch_flag = IH_ARCH_PPC; + pbi_crc_cmd1 = 0x13; + pbi_crc_cmd2 = 0x80; + pbl_cmd_initaddr = 0x82000000; + pbl_end_cmd[0] = 0x091380c0; + pbl_end_cmd[1] = 0x00000000; + pbl_end_cmd[2] = 0x091380c0; + pbl_end_cmd[3] = 0x00000000; + } + + next_pbl_cmd = pbl_cmd_initaddr; + return 0; +}; + +/* pblimage parameters */ +U_BOOT_IMAGE_TYPE( + pblimage, + "Freescale PBL Boot Image support", + sizeof(struct pbl_header), + (void *)&pblimage_header, + pblimage_check_params, + pblimage_verify_header, + pblimage_print_header, + pblimage_set_header, + NULL, + pblimage_check_image_types, + NULL, + NULL +); diff --git a/tools/u-boot-tools/pblimage.h b/tools/u-boot-tools/pblimage.h new file mode 100644 index 0000000..81c5492 --- /dev/null +++ b/tools/u-boot-tools/pblimage.h @@ -0,0 +1,19 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright 2012 Freescale Semiconductor, Inc. + */ + +#ifndef PBLIMAGE_H +#define PBLIMAGE_H + +#define RCW_BYTES 64 +#define RCW_PREAMBLE 0xaa55aa55 +#define RCW_HEADER 0x010e0100 + +struct pbl_header { + uint32_t preamble; + uint32_t rcwheader; + uint8_t rcw_data[RCW_BYTES]; +}; + +#endif /* PBLIMAGE_H */ diff --git a/tools/u-boot-tools/pblimage.o b/tools/u-boot-tools/pblimage.o new file mode 100644 index 0000000000000000000000000000000000000000..c22d3bbc95ff09e4ecdc46ce15ecd7caa51bb65d GIT binary patch literal 8520 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 literal 0 HcmV?d00001 diff --git a/tools/u-boot-tools/prelink-riscv.c b/tools/u-boot-tools/prelink-riscv.c new file mode 100644 index 0000000..52eb78e --- /dev/null +++ b/tools/u-boot-tools/prelink-riscv.c @@ -0,0 +1,101 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2017 Andes Technology + * Chih-Mao Chen <cmchen@andestech.com> + * + * Statically process runtime relocations on RISC-V ELF images + * so that it can be directly executed when loaded at LMA + * without fixup. Both RV32 and RV64 are supported. + */ + +#if __BYTE_ORDER__ != __ORDER_LITTLE_ENDIAN__ +#error "Only little-endian host is supported" +#endif + +#include <errno.h> +#include <stdbool.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <elf.h> +#include <fcntl.h> +#include <sys/mman.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <unistd.h> + +#ifndef EM_RISCV +#define EM_RISCV 243 +#endif + +#ifndef R_RISCV_32 +#define R_RISCV_32 1 +#endif + +#ifndef R_RISCV_64 +#define R_RISCV_64 2 +#endif + +#ifndef R_RISCV_RELATIVE +#define R_RISCV_RELATIVE 3 +#endif + +const char *argv0; + +#define die(fmt, ...) \ + do { \ + fprintf(stderr, "%s: " fmt "\n", argv0, ## __VA_ARGS__); \ + exit(EXIT_FAILURE); \ + } while (0) + +#define PRELINK_INC_BITS 32 +#include "prelink-riscv.inc" +#undef PRELINK_INC_BITS + +#define PRELINK_INC_BITS 64 +#include "prelink-riscv.inc" +#undef PRELINK_INC_BITS + +int main(int argc, const char *const *argv) +{ + argv0 = argv[0]; + + if (argc < 2) { + fprintf(stderr, "Usage: %s <u-boot>\n", argv0); + exit(EXIT_FAILURE); + } + + int fd = open(argv[1], O_RDWR, 0); + + if (fd < 0) + die("Cannot open %s: %s", argv[1], strerror(errno)); + + struct stat st; + + if (fstat(fd, &st) < 0) + die("Cannot stat %s: %s", argv[1], strerror(errno)); + + void *data = + mmap(0, st.st_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + + if (data == MAP_FAILED) + die("Cannot mmap %s: %s", argv[1], strerror(errno)); + + close(fd); + + unsigned char *e_ident = (unsigned char *)data; + + if (memcmp(e_ident, ELFMAG, SELFMAG) != 0) + die("Invalid ELF file %s", argv[1]); + + bool is64 = e_ident[EI_CLASS] == ELFCLASS64; + + if (is64) + prelink64(data); + else + prelink32(data); + + return 0; +} diff --git a/tools/u-boot-tools/prelink-riscv.inc b/tools/u-boot-tools/prelink-riscv.inc new file mode 100644 index 0000000..d492587 --- /dev/null +++ b/tools/u-boot-tools/prelink-riscv.inc @@ -0,0 +1,111 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2017 Andes Technology + * Chih-Mao Chen <cmchen@andestech.com> + * + * Statically process runtime relocations on RISC-V ELF images + * so that it can be directly executed when loaded at LMA + * without fixup. Both RV32 and RV64 are supported. + */ + +#define CONCAT_IMPL(x, y) x##y +#define CONCAT(x, y) CONCAT_IMPL(x, y) +#define CONCAT3(x, y, z) CONCAT(CONCAT(x, y), z) + +#define prelink_nn CONCAT(prelink, PRELINK_INC_BITS) +#define uintnn_t CONCAT3(uint, PRELINK_INC_BITS, _t) +#define get_offset_nn CONCAT(get_offset_, PRELINK_INC_BITS) +#define Elf_Ehdr CONCAT3(Elf, PRELINK_INC_BITS, _Ehdr) +#define Elf_Phdr CONCAT3(Elf, PRELINK_INC_BITS, _Phdr) +#define Elf_Rela CONCAT3(Elf, PRELINK_INC_BITS, _Rela) +#define Elf_Sym CONCAT3(Elf, PRELINK_INC_BITS, _Sym) +#define Elf_Dyn CONCAT3(Elf, PRELINK_INC_BITS, _Dyn) +#define Elf_Addr CONCAT3(Elf, PRELINK_INC_BITS, _Addr) +#define ELF_R_TYPE CONCAT3(ELF, PRELINK_INC_BITS, _R_TYPE) +#define ELF_R_SYM CONCAT3(ELF, PRELINK_INC_BITS, _R_SYM) + +static void* get_offset_nn (void* data, Elf_Phdr* phdrs, size_t phnum, Elf_Addr addr) +{ + Elf_Phdr *p; + + for (p = phdrs; p < phdrs + phnum; ++p) + if (p->p_vaddr <= addr && p->p_vaddr + p->p_memsz > addr) + return data + p->p_offset + (addr - p->p_vaddr); + + return NULL; +} + +static void prelink_nn(void *data) +{ + Elf_Ehdr *ehdr = data; + Elf_Phdr *p; + Elf_Dyn *dyn; + Elf_Rela *r; + + if (ehdr->e_machine != EM_RISCV) + die("Machine type is not RISC-V"); + + Elf_Phdr *phdrs = data + ehdr->e_phoff; + + Elf_Dyn *dyns = NULL; + for (p = phdrs; p < phdrs + ehdr->e_phnum; ++p) { + if (p->p_type == PT_DYNAMIC) { + dyns = data + p->p_offset; + break; + } + } + + if (dyns == NULL) + die("No dynamic section found"); + + Elf_Rela *rela_dyn = NULL; + size_t rela_count = 0; + Elf_Sym *dynsym = NULL; + for (dyn = dyns;; ++dyn) { + if (dyn->d_tag == DT_NULL) + break; + else if (dyn->d_tag == DT_RELA) + rela_dyn = get_offset_nn(data, phdrs, ehdr->e_phnum, + dyn->d_un.d_ptr); + else if (dyn->d_tag == DT_RELASZ) + rela_count = dyn->d_un.d_val / sizeof(Elf_Rela); + else if (dyn->d_tag == DT_SYMTAB) + dynsym = get_offset_nn(data, phdrs, ehdr->e_phnum, + dyn->d_un.d_ptr); + + } + + if (rela_dyn == NULL) + die("No .rela.dyn found"); + + if (dynsym == NULL) + die("No .dynsym found"); + + for (r = rela_dyn; r < rela_dyn + rela_count; ++r) { + void* buf = get_offset_nn(data, phdrs, ehdr->e_phnum, r->r_offset); + + if (buf == NULL) + continue; + + if (ELF_R_TYPE(r->r_info) == R_RISCV_RELATIVE) + *((uintnn_t*) buf) = r->r_addend; + else if (ELF_R_TYPE(r->r_info) == R_RISCV_32) + *((uint32_t*) buf) = dynsym[ELF_R_SYM(r->r_info)].st_value; + else if (ELF_R_TYPE(r->r_info) == R_RISCV_64) + *((uint64_t*) buf) = dynsym[ELF_R_SYM(r->r_info)].st_value; + } +} + +#undef prelink_nn +#undef uintnn_t +#undef get_offset_nn +#undef Elf_Ehdr +#undef Elf_Phdr +#undef Elf_Rela +#undef Elf_Sym +#undef Elf_Dyn +#undef Elf_Addr +#undef ELF_R_TYPE +#undef ELF_R_SYM + +#undef CONCAT_IMPL +#undef CONCAT +#undef CONCAT3 diff --git a/tools/u-boot-tools/proftool b/tools/u-boot-tools/proftool new file mode 100755 index 0000000000000000000000000000000000000000..729ea29df237dc367205374bafeab8d2799c38f8 GIT binary patch literal 22592 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 F000W^&6fZG literal 0 HcmV?d00001 diff --git a/tools/u-boot-tools/proftool.c b/tools/u-boot-tools/proftool.c new file mode 100644 index 0000000..c1803fa --- /dev/null +++ b/tools/u-boot-tools/proftool.c @@ -0,0 +1,599 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2013 Google, Inc + */ + +/* Decode and dump U-Boot profiling information */ + +#include <assert.h> +#include <ctype.h> +#include <limits.h> +#include <regex.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <sys/param.h> +#include <sys/types.h> + +#include <compiler.h> +#include <trace.h> + +#define MAX_LINE_LEN 500 + +enum { + FUNCF_TRACE = 1 << 0, /* Include this function in trace */ +}; + +struct func_info { + unsigned long offset; + const char *name; + unsigned long code_size; + unsigned long call_count; + unsigned flags; + /* the section this function is in */ + struct objsection_info *objsection; +}; + +enum trace_line_type { + TRACE_LINE_INCLUDE, + TRACE_LINE_EXCLUDE, +}; + +struct trace_configline_info { + struct trace_configline_info *next; + enum trace_line_type type; + const char *name; /* identifier name / wildcard */ + regex_t regex; /* Regex to use if name starts with / */ +}; + +/* The contents of the trace config file */ +struct trace_configline_info *trace_config_head; + +struct func_info *func_list; +int func_count; +struct trace_call *call_list; +int call_count; +int verbose; /* Verbosity level 0=none, 1=warn, 2=notice, 3=info, 4=debug */ +unsigned long text_offset; /* text address of first function */ + +static void outf(int level, const char *fmt, ...) + __attribute__ ((format (__printf__, 2, 3))); +#define error(fmt, b...) outf(0, fmt, ##b) +#define warn(fmt, b...) outf(1, fmt, ##b) +#define notice(fmt, b...) outf(2, fmt, ##b) +#define info(fmt, b...) outf(3, fmt, ##b) +#define debug(fmt, b...) outf(4, fmt, ##b) + + +static void outf(int level, const char *fmt, ...) +{ + if (verbose >= level) { + va_list args; + + va_start(args, fmt); + vfprintf(stderr, fmt, args); + va_end(args); + } +} + +static void usage(void) +{ + fprintf(stderr, + "Usage: proftool -cds -v3 <cmd> <profdata>\n" + "\n" + "Commands\n" + " dump-ftrace\t\tDump out textual data in ftrace format\n" + "\n" + "Options:\n" + " -m <map>\tSpecify Systen.map file\n" + " -t <trace>\tSpecific trace data file (from U-Boot)\n" + " -v <0-4>\tSpecify verbosity\n"); + exit(EXIT_FAILURE); +} + +static int h_cmp_offset(const void *v1, const void *v2) +{ + const struct func_info *f1 = v1, *f2 = v2; + + return (f1->offset / FUNC_SITE_SIZE) - (f2->offset / FUNC_SITE_SIZE); +} + +static int read_system_map(FILE *fin) +{ + unsigned long offset, start = 0; + struct func_info *func; + char buff[MAX_LINE_LEN]; + char symtype; + char symname[MAX_LINE_LEN + 1]; + int linenum; + int alloced; + + for (linenum = 1, alloced = func_count = 0;; linenum++) { + int fields = 0; + + if (fgets(buff, sizeof(buff), fin)) + fields = sscanf(buff, "%lx %c %100s\n", &offset, + &symtype, symname); + if (fields == 2) { + continue; + } else if (feof(fin)) { + break; + } else if (fields < 2) { + error("Map file line %d: invalid format\n", linenum); + return 1; + } + + /* Must be a text symbol */ + symtype = tolower(symtype); + if (symtype != 't' && symtype != 'w') + continue; + + if (func_count == alloced) { + alloced += 256; + func_list = realloc(func_list, + sizeof(struct func_info) * alloced); + assert(func_list); + } + if (!func_count) + start = offset; + + func = &func_list[func_count++]; + memset(func, '\0', sizeof(*func)); + func->offset = offset - start; + func->name = strdup(symname); + func->flags = FUNCF_TRACE; /* trace by default */ + + /* Update previous function's code size */ + if (func_count > 1) + func[-1].code_size = func->offset - func[-1].offset; + } + notice("%d functions found in map file\n", func_count); + text_offset = start; + return 0; +} + +static int read_data(FILE *fin, void *buff, int size) +{ + int err; + + err = fread(buff, 1, size, fin); + if (!err) + return 1; + if (err != size) { + error("Cannot read profile file at pos %ld\n", ftell(fin)); + return -1; + } + return 0; +} + +static struct func_info *find_func_by_offset(uint32_t offset) +{ + struct func_info key, *found; + + key.offset = offset; + found = bsearch(&key, func_list, func_count, sizeof(struct func_info), + h_cmp_offset); + + return found; +} + +/* This finds the function which contains the given offset */ +static struct func_info *find_caller_by_offset(uint32_t offset) +{ + int low; /* least function that could be a match */ + int high; /* greated function that could be a match */ + struct func_info key; + + low = 0; + high = func_count - 1; + key.offset = offset; + while (high > low + 1) { + int mid = (low + high) / 2; + int result; + + result = h_cmp_offset(&key, &func_list[mid]); + if (result > 0) + low = mid; + else if (result < 0) + high = mid; + else + return &func_list[mid]; + } + + return low >= 0 ? &func_list[low] : NULL; +} + +static int read_calls(FILE *fin, int count) +{ + struct trace_call *call_data; + int i; + + notice("call count: %d\n", count); + call_list = (struct trace_call *)calloc(count, sizeof(*call_data)); + if (!call_list) { + error("Cannot allocate call_list\n"); + return -1; + } + call_count = count; + + call_data = call_list; + for (i = 0; i < count; i++, call_data++) { + if (read_data(fin, call_data, sizeof(*call_data))) + return 1; + } + return 0; +} + +static int read_profile(FILE *fin, int *not_found) +{ + struct trace_output_hdr hdr; + + *not_found = 0; + while (!feof(fin)) { + int err; + + err = read_data(fin, &hdr, sizeof(hdr)); + if (err == 1) + break; /* EOF */ + else if (err) + return 1; + + switch (hdr.type) { + case TRACE_CHUNK_FUNCS: + /* Ignored at present */ + break; + + case TRACE_CHUNK_CALLS: + if (read_calls(fin, hdr.rec_count)) + return 1; + break; + } + } + return 0; +} + +static int read_map_file(const char *fname) +{ + FILE *fmap; + int err = 0; + + fmap = fopen(fname, "r"); + if (!fmap) { + error("Cannot open map file '%s'\n", fname); + return 1; + } + if (fmap) { + err = read_system_map(fmap); + fclose(fmap); + } + return err; +} + +static int read_profile_file(const char *fname) +{ + int not_found = INT_MAX; + FILE *fprof; + int err; + + fprof = fopen(fname, "rb"); + if (!fprof) { + error("Cannot open profile data file '%s'\n", + fname); + return 1; + } else { + err = read_profile(fprof, ¬_found); + fclose(fprof); + if (err) + return err; + + if (not_found) { + warn("%d profile functions could not be found in the map file - are you sure that your profile data and map file correspond?\n", + not_found); + return 1; + } + } + return 0; +} + +static int regex_report_error(regex_t *regex, int err, const char *op, + const char *name) +{ + char buf[200]; + + regerror(err, regex, buf, sizeof(buf)); + error("Regex error '%s' in %s '%s'\n", buf, op, name); + return -1; +} + +static void check_trace_config_line(struct trace_configline_info *item) +{ + struct func_info *func, *end; + int err; + + debug("Checking trace config line '%s'\n", item->name); + for (func = func_list, end = func + func_count; func < end; func++) { + err = regexec(&item->regex, func->name, 0, NULL, 0); + debug(" - regex '%s', string '%s': %d\n", item->name, + func->name, err); + if (err == REG_NOMATCH) + continue; + + if (err) { + regex_report_error(&item->regex, err, "match", + item->name); + break; + } + + /* It matches, so perform the action */ + switch (item->type) { + case TRACE_LINE_INCLUDE: + info(" include %s at %lx\n", func->name, + text_offset + func->offset); + func->flags |= FUNCF_TRACE; + break; + + case TRACE_LINE_EXCLUDE: + info(" exclude %s at %lx\n", func->name, + text_offset + func->offset); + func->flags &= ~FUNCF_TRACE; + break; + } + } +} + +static void check_trace_config(void) +{ + struct trace_configline_info *line; + + for (line = trace_config_head; line; line = line->next) + check_trace_config_line(line); +} + +/** + * Check the functions to see if they each have an objsection. If not, then + * the linker must have eliminated them. + */ +static void check_functions(void) +{ + struct func_info *func, *end; + unsigned long removed_code_size = 0; + int not_found = 0; + + /* Look for missing functions */ + for (func = func_list, end = func + func_count; func < end; func++) { + if (!func->objsection) { + removed_code_size += func->code_size; + not_found++; + } + } + + /* Figure out what functions we want to trace */ + check_trace_config(); + + warn("%d functions removed by linker, %ld code size\n", + not_found, removed_code_size); +} + +static int read_trace_config(FILE *fin) +{ + char buff[200]; + int linenum = 0; + struct trace_configline_info **tailp = &trace_config_head; + + while (fgets(buff, sizeof(buff), fin)) { + int len = strlen(buff); + struct trace_configline_info *line; + char *saveptr; + char *s, *tok; + int err; + + linenum++; + if (len && buff[len - 1] == '\n') + buff[len - 1] = '\0'; + + /* skip blank lines and comments */ + for (s = buff; *s == ' ' || *s == '\t'; s++) + ; + if (!*s || *s == '#') + continue; + + line = (struct trace_configline_info *)calloc(1, + sizeof(*line)); + if (!line) { + error("Cannot allocate config line\n"); + return -1; + } + + tok = strtok_r(s, " \t", &saveptr); + if (!tok) { + error("Invalid trace config data on line %d\n", + linenum); + return -1; + } + if (0 == strcmp(tok, "include-func")) { + line->type = TRACE_LINE_INCLUDE; + } else if (0 == strcmp(tok, "exclude-func")) { + line->type = TRACE_LINE_EXCLUDE; + } else { + error("Unknown command in trace config data line %d\n", + linenum); + return -1; + } + + tok = strtok_r(NULL, " \t", &saveptr); + if (!tok) { + error("Missing pattern in trace config data line %d\n", + linenum); + return -1; + } + + err = regcomp(&line->regex, tok, REG_NOSUB); + if (err) { + int r = regex_report_error(&line->regex, err, + "compile", tok); + free(line); + return r; + } + + /* link this new one to the end of the list */ + line->name = strdup(tok); + line->next = NULL; + *tailp = line; + tailp = &line->next; + } + + if (!feof(fin)) { + error("Cannot read from trace config file at position %ld\n", + ftell(fin)); + return -1; + } + return 0; +} + +static int read_trace_config_file(const char *fname) +{ + FILE *fin; + int err; + + fin = fopen(fname, "r"); + if (!fin) { + error("Cannot open trace_config file '%s'\n", fname); + return -1; + } + err = read_trace_config(fin); + fclose(fin); + return err; +} + +static void out_func(ulong func_offset, int is_caller, const char *suffix) +{ + struct func_info *func; + + func = (is_caller ? find_caller_by_offset : find_func_by_offset) + (func_offset); + + if (func) + printf("%s%s", func->name, suffix); + else + printf("%lx%s", func_offset, suffix); +} + +/* + * # tracer: function + * # + * # TASK-PID CPU# TIMESTAMP FUNCTION + * # | | | | | + * # bash-4251 [01] 10152.583854: path_put <-path_walk + * # bash-4251 [01] 10152.583855: dput <-path_put + * # bash-4251 [01] 10152.583855: _atomic_dec_and_lock <-dput + */ +static int make_ftrace(void) +{ + struct trace_call *call; + int missing_count = 0, skip_count = 0; + int i; + + printf("# tracer: ftrace\n" + "#\n" + "# TASK-PID CPU# TIMESTAMP FUNCTION\n" + "# | | | | |\n"); + for (i = 0, call = call_list; i < call_count; i++, call++) { + struct func_info *func = find_func_by_offset(call->func); + ulong time = call->flags & FUNCF_TIMESTAMP_MASK; + + if (TRACE_CALL_TYPE(call) != FUNCF_ENTRY && + TRACE_CALL_TYPE(call) != FUNCF_EXIT) + continue; + if (!func) { + warn("Cannot find function at %lx\n", + text_offset + call->func); + missing_count++; + continue; + } + + if (!(func->flags & FUNCF_TRACE)) { + debug("Funcion '%s' is excluded from trace\n", + func->name); + skip_count++; + continue; + } + + printf("%16s-%-5d [01] %lu.%06lu: ", "uboot", 1, + time / 1000000, time % 1000000); + + out_func(call->func, 0, " <- "); + out_func(call->caller, 1, "\n"); + } + info("ftrace: %d functions not found, %d excluded\n", missing_count, + skip_count); + + return 0; +} + +static int prof_tool(int argc, char * const argv[], + const char *prof_fname, const char *map_fname, + const char *trace_config_fname) +{ + int err = 0; + + if (read_map_file(map_fname)) + return -1; + if (prof_fname && read_profile_file(prof_fname)) + return -1; + if (trace_config_fname && read_trace_config_file(trace_config_fname)) + return -1; + + check_functions(); + + for (; argc; argc--, argv++) { + const char *cmd = *argv; + + if (0 == strcmp(cmd, "dump-ftrace")) + err = make_ftrace(); + else + warn("Unknown command '%s'\n", cmd); + } + + return err; +} + +int main(int argc, char *argv[]) +{ + const char *map_fname = "System.map"; + const char *prof_fname = NULL; + const char *trace_config_fname = NULL; + int opt; + + verbose = 2; + while ((opt = getopt(argc, argv, "m:p:t:v:")) != -1) { + switch (opt) { + case 'm': + map_fname = optarg; + break; + + case 'p': + prof_fname = optarg; + break; + + case 't': + trace_config_fname = optarg; + break; + + case 'v': + verbose = atoi(optarg); + break; + + default: + usage(); + } + } + argc -= optind; argv += optind; + if (argc < 1) + usage(); + + debug("Debug enabled\n"); + return prof_tool(argc, argv, prof_fname, map_fname, + trace_config_fname); +} diff --git a/tools/u-boot-tools/relocate-rela.c b/tools/u-boot-tools/relocate-rela.c new file mode 100644 index 0000000..6a52401 --- /dev/null +++ b/tools/u-boot-tools/relocate-rela.c @@ -0,0 +1,157 @@ +// SPDX-License-Identifier: GPL-2.0+ OR BSD-2-Clause +/* + * Copyright 2013 Freescale Semiconductor, Inc. + * + * 64-bit and little-endian target only until we need to support a different + * arch that needs this. + */ + +#include <elf.h> +#include <errno.h> +#include <inttypes.h> +#include <stdarg.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include "compiler.h" + +#ifndef R_AARCH64_RELATIVE +#define R_AARCH64_RELATIVE 1027 +#endif + +static const bool debug_en; + +static void debug(const char *fmt, ...) +{ + va_list args; + + if (debug_en) { + va_start(args, fmt); + vprintf(fmt, args); + va_end(args); + } +} + +static bool supported_rela(Elf64_Rela *rela) +{ + uint64_t mask = 0xffffffffULL; /* would be different on 32-bit */ + uint32_t type = rela->r_info & mask; + + switch (type) { +#ifdef R_AARCH64_RELATIVE + case R_AARCH64_RELATIVE: + return true; +#endif + default: + fprintf(stderr, "warning: unsupported relocation type %" + PRIu32 " at %" PRIx64 "\n", + type, rela->r_offset); + + return false; + } +} + +static bool read_num(const char *str, uint64_t *num) +{ + char *endptr; + *num = strtoull(str, &endptr, 16); + return str[0] && !endptr[0]; +} + +int main(int argc, char **argv) +{ + FILE *f; + int i, num; + uint64_t rela_start, rela_end, text_base; + + if (argc != 5) { + fprintf(stderr, "Statically apply ELF rela relocations\n"); + fprintf(stderr, "Usage: %s <bin file> <text base> " \ + "<rela start> <rela end>\n", argv[0]); + fprintf(stderr, "All numbers in hex.\n"); + return 1; + } + + f = fopen(argv[1], "r+b"); + if (!f) { + fprintf(stderr, "%s: Cannot open %s: %s\n", + argv[0], argv[1], strerror(errno)); + return 2; + } + + if (!read_num(argv[2], &text_base) || + !read_num(argv[3], &rela_start) || + !read_num(argv[4], &rela_end)) { + fprintf(stderr, "%s: bad number\n", argv[0]); + return 3; + } + + if (rela_start > rela_end || rela_start < text_base || + (rela_end - rela_start) % sizeof(Elf64_Rela)) { + fprintf(stderr, "%s: bad rela bounds\n", argv[0]); + return 3; + } + + rela_start -= text_base; + rela_end -= text_base; + + num = (rela_end - rela_start) / sizeof(Elf64_Rela); + + for (i = 0; i < num; i++) { + Elf64_Rela rela, swrela; + uint64_t pos = rela_start + sizeof(Elf64_Rela) * i; + uint64_t addr; + + if (fseek(f, pos, SEEK_SET) < 0) { + fprintf(stderr, "%s: %s: seek to %" PRIx64 + " failed: %s\n", + argv[0], argv[1], pos, strerror(errno)); + } + + if (fread(&rela, sizeof(rela), 1, f) != 1) { + fprintf(stderr, "%s: %s: read rela failed at %" + PRIx64 "\n", + argv[0], argv[1], pos); + return 4; + } + + swrela.r_offset = cpu_to_le64(rela.r_offset); + swrela.r_info = cpu_to_le64(rela.r_info); + swrela.r_addend = cpu_to_le64(rela.r_addend); + + if (!supported_rela(&swrela)) + continue; + + debug("Rela %" PRIx64 " %" PRIu64 " %" PRIx64 "\n", + swrela.r_offset, swrela.r_info, swrela.r_addend); + + if (swrela.r_offset < text_base) { + fprintf(stderr, "%s: %s: bad rela at %" PRIx64 "\n", + argv[0], argv[1], pos); + return 4; + } + + addr = swrela.r_offset - text_base; + + if (fseek(f, addr, SEEK_SET) < 0) { + fprintf(stderr, "%s: %s: seek to %" + PRIx64 " failed: %s\n", + argv[0], argv[1], addr, strerror(errno)); + } + + if (fwrite(&rela.r_addend, sizeof(rela.r_addend), 1, f) != 1) { + fprintf(stderr, "%s: %s: write failed at %" PRIx64 "\n", + argv[0], argv[1], addr); + return 4; + } + } + + if (fclose(f) < 0) { + fprintf(stderr, "%s: %s: close failed: %s\n", + argv[0], argv[1], strerror(errno)); + return 4; + } + + return 0; +} diff --git a/tools/u-boot-tools/rkcommon.c b/tools/u-boot-tools/rkcommon.c new file mode 100644 index 0000000..831c2ad --- /dev/null +++ b/tools/u-boot-tools/rkcommon.c @@ -0,0 +1,378 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2015 Google, Inc + * Written by Simon Glass <sjg@chromium.org> + * + * (C) 2017 Theobroma Systems Design und Consulting GmbH + * + * Helper functions for Rockchip images + */ + +#include "imagetool.h" +#include <image.h> +#include <rc4.h> +#include "mkimage.h" +#include "rkcommon.h" + +#define DIV_ROUND_UP(n, d) (((n) + (d) - 1) / (d)) + +enum { + RK_SIGNATURE = 0x0ff0aa55, +}; + +/** + * struct header0_info - header block for boot ROM + * + * This is stored at SD card block 64 (where each block is 512 bytes, or at + * the start of SPI flash. It is encoded with RC4. + * + * @signature: Signature (must be RKSD_SIGNATURE) + * @disable_rc4: 0 to use rc4 for boot image, 1 to use plain binary + * @init_offset: Offset in blocks of the SPL code from this header + * block. E.g. 4 means 2KB after the start of this header. + * Other fields are not used by U-Boot + */ +struct header0_info { + uint32_t signature; + uint8_t reserved[4]; + uint32_t disable_rc4; + uint16_t init_offset; + uint8_t reserved1[492]; + uint16_t init_size; + uint16_t init_boot_size; + uint8_t reserved2[2]; +}; + +/** + * struct header1_info + */ +struct header1_info { + uint32_t magic; +}; + +/** + * struct spl_info - spl info for each chip + * + * @imagename: Image name(passed by "mkimage -n") + * @spl_hdr: Boot ROM requires a 4-bytes spl header + * @spl_size: Spl size(include extra 4-bytes spl header) + * @spl_rc4: RC4 encode the SPL binary (same key as header) + */ + +struct spl_info { + const char *imagename; + const char *spl_hdr; + const uint32_t spl_size; + const bool spl_rc4; +}; + +static struct spl_info spl_infos[] = { + { "rk3036", "RK30", 0x1000, false }, + { "rk3128", "RK31", 0x1800, false }, + { "rk3188", "RK31", 0x8000 - 0x800, true }, + { "rk322x", "RK32", 0x8000 - 0x1000, false }, + { "rk3288", "RK32", 0x8000, false }, + { "rk3328", "RK32", 0x8000 - 0x1000, false }, + { "rk3368", "RK33", 0x8000 - 0x1000, false }, + { "rk3399", "RK33", 0x30000 - 0x2000, false }, + { "rv1108", "RK11", 0x1800, false }, +}; + +static unsigned char rc4_key[16] = { + 124, 78, 3, 4, 85, 5, 9, 7, + 45, 44, 123, 56, 23, 13, 23, 17 +}; + +static struct spl_info *rkcommon_get_spl_info(char *imagename) +{ + int i; + + if (!imagename) + return NULL; + + for (i = 0; i < ARRAY_SIZE(spl_infos); i++) + if (!strncmp(imagename, spl_infos[i].imagename, 6)) + return spl_infos + i; + + return NULL; +} + +int rkcommon_check_params(struct image_tool_params *params) +{ + int i; + + if (rkcommon_get_spl_info(params->imagename) != NULL) + return EXIT_SUCCESS; + + /* + * If this is a operation (list or extract), the don't require + * imagename to be set. + */ + if (params->lflag || params->iflag) + return EXIT_SUCCESS; + + fprintf(stderr, "ERROR: imagename (%s) is not supported!\n", + params->imagename ? params->imagename : "NULL"); + + fprintf(stderr, "Available imagename:"); + for (i = 0; i < ARRAY_SIZE(spl_infos); i++) + fprintf(stderr, "\t%s", spl_infos[i].imagename); + fprintf(stderr, "\n"); + + return EXIT_FAILURE; +} + +const char *rkcommon_get_spl_hdr(struct image_tool_params *params) +{ + struct spl_info *info = rkcommon_get_spl_info(params->imagename); + + /* + * info would not be NULL, because of we checked params before. + */ + return info->spl_hdr; +} + + +int rkcommon_get_spl_size(struct image_tool_params *params) +{ + struct spl_info *info = rkcommon_get_spl_info(params->imagename); + + /* + * info would not be NULL, because of we checked params before. + */ + return info->spl_size; +} + +bool rkcommon_need_rc4_spl(struct image_tool_params *params) +{ + struct spl_info *info = rkcommon_get_spl_info(params->imagename); + + /* + * info would not be NULL, because of we checked params before. + */ + return info->spl_rc4; +} + +static void rkcommon_set_header0(void *buf, uint file_size, + struct image_tool_params *params) +{ + struct header0_info *hdr = buf; + + memset(buf, '\0', RK_INIT_OFFSET * RK_BLK_SIZE); + hdr->signature = RK_SIGNATURE; + hdr->disable_rc4 = !rkcommon_need_rc4_spl(params); + hdr->init_offset = RK_INIT_OFFSET; + + hdr->init_size = DIV_ROUND_UP(file_size, RK_BLK_SIZE); + /* + * The init_size has to be a multiple of 4 blocks (i.e. of 2K) + * or the BootROM will not boot the image. + * + * Note: To verify that this is not a legacy constraint, we + * rechecked this against the RK3399 BootROM. + */ + hdr->init_size = ROUND(hdr->init_size, 4); + /* + * init_boot_size needs to be set, as it is read by the BootROM + * to determine the size of the next-stage bootloader (e.g. U-Boot + * proper), when used with the back-to-bootrom functionality. + * + * see https://lists.denx.de/pipermail/u-boot/2017-May/293267.html + * for a more detailed explanation by Andy Yan + */ + hdr->init_boot_size = hdr->init_size + RK_MAX_BOOT_SIZE / RK_BLK_SIZE; + + rc4_encode(buf, RK_BLK_SIZE, rc4_key); +} + +int rkcommon_set_header(void *buf, uint file_size, + struct image_tool_params *params) +{ + struct header1_info *hdr = buf + RK_SPL_HDR_START; + + if (file_size > rkcommon_get_spl_size(params)) + return -ENOSPC; + + rkcommon_set_header0(buf, file_size, params); + + /* Set up the SPL name (i.e. copy spl_hdr over) */ + memcpy(&hdr->magic, rkcommon_get_spl_hdr(params), RK_SPL_HDR_SIZE); + + if (rkcommon_need_rc4_spl(params)) + rkcommon_rc4_encode_spl(buf, RK_SPL_HDR_START, + params->file_size - RK_SPL_HDR_START); + + return 0; +} + +static inline unsigned rkcommon_offset_to_spi(unsigned offset) +{ + /* + * While SD/MMC images use a flat addressing, SPI images are padded + * to use the first 2K of every 4K sector only. + */ + return ((offset & ~0x7ff) << 1) + (offset & 0x7ff); +} + +static int rkcommon_parse_header(const void *buf, struct header0_info *header0, + struct spl_info **spl_info) +{ + unsigned hdr1_offset; + struct header1_info *hdr1_sdmmc, *hdr1_spi; + int i; + + if (spl_info) + *spl_info = NULL; + + /* + * The first header (hdr0) is always RC4 encoded, so try to decrypt + * with the well-known key. + */ + memcpy((void *)header0, buf, sizeof(struct header0_info)); + rc4_encode((void *)header0, sizeof(struct header0_info), rc4_key); + + if (header0->signature != RK_SIGNATURE) + return -EPROTO; + + /* We don't support RC4 encoded image payloads here, yet... */ + if (header0->disable_rc4 == 0) + return -ENOSYS; + + hdr1_offset = header0->init_offset * RK_BLK_SIZE; + hdr1_sdmmc = (struct header1_info *)(buf + hdr1_offset); + hdr1_spi = (struct header1_info *)(buf + + rkcommon_offset_to_spi(hdr1_offset)); + + for (i = 0; i < ARRAY_SIZE(spl_infos); i++) { + if (!memcmp(&hdr1_sdmmc->magic, spl_infos[i].spl_hdr, 4)) { + if (spl_info) + *spl_info = &spl_infos[i]; + return IH_TYPE_RKSD; + } else if (!memcmp(&hdr1_spi->magic, spl_infos[i].spl_hdr, 4)) { + if (spl_info) + *spl_info = &spl_infos[i]; + return IH_TYPE_RKSPI; + } + } + + return -1; +} + +int rkcommon_verify_header(unsigned char *buf, int size, + struct image_tool_params *params) +{ + struct header0_info header0; + struct spl_info *img_spl_info, *spl_info; + int ret; + + ret = rkcommon_parse_header(buf, &header0, &img_spl_info); + + /* If this is the (unimplemented) RC4 case, then rewrite the result */ + if (ret == -ENOSYS) + return 0; + + if (ret < 0) + return ret; + + /* + * If no 'imagename' is specified via the commandline (e.g. if this is + * 'dumpimage -l' w/o any further constraints), we accept any spl_info. + */ + if (params->imagename == NULL) + return 0; + + /* Match the 'imagename' against the 'spl_hdr' found */ + spl_info = rkcommon_get_spl_info(params->imagename); + if (spl_info && img_spl_info) + return strcmp(spl_info->spl_hdr, img_spl_info->spl_hdr); + + return -ENOENT; +} + +void rkcommon_print_header(const void *buf) +{ + struct header0_info header0; + struct spl_info *spl_info; + uint8_t image_type; + int ret; + + ret = rkcommon_parse_header(buf, &header0, &spl_info); + + /* If this is the (unimplemented) RC4 case, then fail silently */ + if (ret == -ENOSYS) + return; + + if (ret < 0) { + fprintf(stderr, "Error: image verification failed\n"); + return; + } + + image_type = ret; + + printf("Image Type: Rockchip %s (%s) boot image\n", + spl_info->spl_hdr, + (image_type == IH_TYPE_RKSD) ? "SD/MMC" : "SPI"); + printf("Data Size: %d bytes\n", header0.init_size * RK_BLK_SIZE); +} + +void rkcommon_rc4_encode_spl(void *buf, unsigned int offset, unsigned int size) +{ + unsigned int remaining = size; + + while (remaining > 0) { + int step = (remaining > RK_BLK_SIZE) ? RK_BLK_SIZE : remaining; + + rc4_encode(buf + offset, step, rc4_key); + offset += RK_BLK_SIZE; + remaining -= step; + } +} + +int rkcommon_vrec_header(struct image_tool_params *params, + struct image_type_params *tparams, + unsigned int alignment) +{ + unsigned int unpadded_size; + unsigned int padded_size; + + /* + * The SPL image looks as follows: + * + * 0x0 header0 (see rkcommon.c) + * 0x800 spl_name ('RK30', ..., 'RK33') + * (start of the payload for AArch64 payloads: we expect the + * first 4 bytes to be available for overwriting with our + * spl_name) + * 0x804 first instruction to be executed + * (start of the image/payload for 32bit payloads) + * + * For AArch64 (ARMv8) payloads, natural alignment (8-bytes) is + * required for its sections (so the image we receive needs to + * have the first 4 bytes reserved for the spl_name). Reserving + * these 4 bytes is done using the BOOT0_HOOK infrastructure. + * + * The header is always at 0x800 (as we now use a payload + * prepadded using the boot0 hook for all targets): the first + * 4 bytes of these images can safely be overwritten using the + * boot magic. + */ + tparams->header_size = RK_SPL_HDR_START; + + /* Allocate, clear and install the header */ + tparams->hdr = malloc(tparams->header_size); + if (!tparams->hdr) + return -ENOMEM; + memset(tparams->hdr, 0, tparams->header_size); + + /* + * If someone passed in 0 for the alignment, we'd better handle + * it correctly... + */ + if (!alignment) + alignment = 1; + + unpadded_size = tparams->header_size + params->file_size; + padded_size = ROUND(unpadded_size, alignment); + + return padded_size - unpadded_size; +} diff --git a/tools/u-boot-tools/rkcommon.h b/tools/u-boot-tools/rkcommon.h new file mode 100644 index 0000000..47f47a5 --- /dev/null +++ b/tools/u-boot-tools/rkcommon.h @@ -0,0 +1,115 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * (C) Copyright 2015 Google, Inc + * Written by Simon Glass <sjg@chromium.org> + */ + +#ifndef _RKCOMMON_H +#define _RKCOMMON_H + +enum { + RK_BLK_SIZE = 512, + RK_INIT_SIZE_ALIGN = 2048, + RK_INIT_OFFSET = 4, + RK_MAX_BOOT_SIZE = 512 << 10, + RK_SPL_HDR_START = RK_INIT_OFFSET * RK_BLK_SIZE, + RK_SPL_HDR_SIZE = 4, + RK_SPL_START = RK_SPL_HDR_START + RK_SPL_HDR_SIZE, + RK_IMAGE_HEADER_LEN = RK_SPL_START, +}; + +/** + * rkcommon_check_params() - check params + * + * @return 0 if OK, -1 if ERROR. + */ +int rkcommon_check_params(struct image_tool_params *params); + +/** + * rkcommon_get_spl_hdr() - get 4-bytes spl hdr for a Rockchip boot image + * + * Rockchip's bootrom requires the spl loader to start with a 4-bytes + * header. The content of this header depends on the chip type. + */ +const char *rkcommon_get_spl_hdr(struct image_tool_params *params); + +/** + * rkcommon_get_spl_size() - get spl size for a Rockchip boot image + * + * Different chip may have different sram size. And if we want to jump + * back to the bootrom after spl, we may need to reserve some sram space + * for the bootrom. + * The spl loader size should be sram size minus reserved size(if needed) + */ +int rkcommon_get_spl_size(struct image_tool_params *params); + +/** + * rkcommon_set_header() - set up the header for a Rockchip boot image + * + * This sets up a 2KB header which can be interpreted by the Rockchip boot ROM. + * + * @buf: Pointer to header place (must be at least 2KB in size) + * @file_size: Size of the file we want the boot ROM to load, in bytes + * @return 0 if OK, -ENOSPC if too large + */ +int rkcommon_set_header(void *buf, uint file_size, + struct image_tool_params *params); + +/** + * rkcommon_verify_header() - verify the header for a Rockchip boot image + * + * @buf: Pointer to the image file + * @file_size: Size of entire bootable image file (incl. all padding) + * @return 0 if OK + */ +int rkcommon_verify_header(unsigned char *buf, int size, + struct image_tool_params *params); + +/** + * rkcommon_print_header() - print the header for a Rockchip boot image + * + * This prints the header, spl_name and whether this is a SD/MMC or SPI image. + * + * @buf: Pointer to the image (can be a read-only file-mapping) + */ +void rkcommon_print_header(const void *buf); + +/** + * rkcommon_need_rc4_spl() - check if rc4 encoded spl is required + * + * Some socs cannot disable the rc4-encryption of the spl binary. + * rc4 encryption is disabled normally except on socs that cannot + * handle unencrypted binaries. + * @return true or false depending on rc4 being required. + */ +bool rkcommon_need_rc4_spl(struct image_tool_params *params); + +/** + * rkcommon_rc4_encode_spl() - encode the spl binary + * + * Encrypts the SPL binary using the generic rc4 key as required + * by some socs. + * + * @buf: Pointer to the SPL data (header and SPL binary) + * @offset: offset inside buf to start at + * @size: number of bytes to encode + */ +void rkcommon_rc4_encode_spl(void *buf, unsigned int offset, unsigned int size); + +/** + * rkcommon_vrec_header() - allocate memory for the header + * + * @params: Pointer to the tool params structure + * @tparams: Pointer tot the image type structure (for setting + * the header and header_size) + * @alignment: Alignment (a power of two) that the image should be + * padded to (e.g. 512 if we want to align with SD/MMC + * blocksizes or 2048 for the SPI format) + * + * @return bytes of padding required/added (does not include the header_size) + */ +int rkcommon_vrec_header(struct image_tool_params *params, + struct image_type_params *tparams, + unsigned int alignment); + +#endif diff --git a/tools/u-boot-tools/rkcommon.o b/tools/u-boot-tools/rkcommon.o new file mode 100644 index 0000000000000000000000000000000000000000..592ab723c6af71b831bd01bbbb9488ccb1f2f7c7 GIT binary patch literal 7744 zcmb<-^>JfjWMqH=Mg}_u1P><4z`!7dU^{@B4h(_}0t}8JJ3(B}&M%=JomYcBI)8g~ ze)l-e1XAMBdC#L)6vX=Mv2-5;69a=!=kpgJzGI_}O6aN&{QRw}85kHGyIoW`S`YBI zECq4+x4WqDbRIhRfa~BR&dy_o2RxF0H9lkb@c)0~qyOv-44l_I81F;P^<ccmFW&$% z(W9HITi#N#M7~?nQm(|Z`2eFw^C5}mADpF6cl`hV|9^C>W6WWGc?ORe(H^}b4j#R% zU|qc|7d$#$8Fol8Fff2rOu!*836<Zm^Z)<<uQ%<u1)^5-%QHBJ`t<68jB*U|?EDkz z)A=^o@U}<u8xFX;N*Q*sF(AC&dZ0wQ`7onL^HGWB-<+lQJ(`bj7#;vQ&oTCJ+60f} z+aM8-UY6+SSkGRUqYREQAPIi?7LY9rjv=8QoqvNpy5l`OT5p#qcy!)>QStx(f5S^2 ztp`d3qdPBvypg5{Q{Npb(0Yl#6%?}Ft~{*=O7D8K-iG<U+ZJS|N3SWE;&-`Wc)$bV z8;{Og9=)<4VQ_ry5@BFq@aX0R3G4$U50753R-fL*Hy9Wgz-rR?<y}CEggv0fcD^$_ z;L&_o0xaue`MC74M>p7}{ai3TyFg||$97(Dj5!R^Z+HpHg@<3ghezvwez5OB;rgQM z-~ay}-3}a}U^+a(qZ?g5>)-$X-3|g^=`<|zUjP38=ilZa0#-1=qua^D1C;haUULj{ z3<bsG6;K#^bRG}x{QN@t-~a#C5d!?}+Kdbg9=)|39-S`@4|p7R1!-XLINth$9n50z zXny{~L-S|jK?V*6hJ!DeKYOe`?#cj4<(}O&DhN-!IQjSg|Hi}rLB@5yhbCl@gATst z1ZR@w!_3Ve7@Hse;oo*3t#j@#Mh1qo#={IOovs`^SQr=>(mGvv(mLn<VT1-en(<kG zA>q@_(Af%d3YxU{-~ay`DnuAcf*rf(s7zsCU}$^|QdZ*ZxDP}-?geGkG+6p+{0-u| zcD`}x{O-c<_8973561V7jmJPzE}hT75$vM*xbzY%Av?w$-th#KMLq<3bYAvod?UaF z3cL^%4v*dv6@eGWe*gd9{Nq-MW9xwmEsySMQ0SDHyjcI||9?<2w({r(DQ&$}V&KtT z#Np9h!2!0zqwxsHpy<Ph^bHj<JP>^toc?zF16g|w?tYKPHxj6JdqM5?XgyF7Xd?mh z5VSzxcfH^NDvqED+@rgI!|(vua{ldGRJg!Kfs?-BfrHOD!4e*gM?h9U?1yHOU5X40 z49GbJMP(Yo{jHZuLL9>!JHI)GLP8#t{{DmpcV33%2T1t4biQ@z{Lb(88tPgf#^=!B z?|cU~S@U)2RY)1)7<ZUo-htsTH2;8&+YgG^P>;^9!JU_nyK#VcV7Gy_dUU!9fCAmH z^>&G3=gsdo9lNK1(?xHK6sWvaJfV3(7o-Jb{$Wu0_kTAi+rGZO;|nOP!G=Jx00RR< zjUO{hC@Uwsu1>XuIIp;%U#O1{gJW4@W=>*KPO3s?Zen_BUSe*l6$7VgF+;G6zOSz{ zLvVm6gG*vbqC#+HRjQSOf`Wo-ib7IlNop|{Ls7P|fw37wkhie`h;3+O0b(11*%lTc z%E+h!BxD3;LxhZtz%s^WV8zBD8DmRJhN3b<Lj#bIp&`t{u0cWmK~@NNDQKt`Ybs<G zE9B*uC={0#6yz6`q^2l>N-VGz*P^2QBB*+Wvecr?w9MqhlFa-(g|x)XoYWMULQjxK z6+$Wt!2Su!PtH!x$ShD$ErwW-l%HP$v6PF!qnkyQgMop8pSznyRRGN3-^QW}DsuU` z5iGbQLOntf>^7nZkOkNjf@(l);uQ?o+XDsI6f-a|fNNtca=6sv5`O@0LSPyMbEmtr zvz3B|OKMVPVxEG9p0S>Ru9+r;0n!8tJrK=U6~w?;A;2ij!_F~*k%7U5fq_8=B*DPI z@E1fo@d@-WdGblLF}v_7G_x?x;^LEV<P&h><8a}2=Hiox1hbux*pA$w%8-G9AqAwL zfq@|%L_6^b^n%Rk0h!ar;=-rV%<9Hxki*5N;moJt$S2_hHprRVolnCREZ`0nU|?VX zHJ~<t%wS+(cnPAxZgPaXDUyp%!WpdCo119~#C<M&9PZrCV5hq9aWF7|{QCu@mw|yn z9z+N73Hb7HFo2?)2b5hI7#Lt_6QouFDkcY2>jPD50Tt&2i8DaV^?{0^o0|d^hnZ`` zz`#%e6-xlo{(J&`Oip|fz06L03O%4OX=8QgGiYXGHfH4F({SWdaDs&kC{PukcA~}^ zNPQb?4@)m|9}@$E3IhX!22`Coi1y;+IDG8P*>j$#>U0<w7+9c5s2rrukx!tR$%}6R z3zH`opMWDDhc~x77oUJLABP(#K0#^11gd^2NWD8qX)+f`I)#se0pzz3sC*<y-VNk8 zSCHR4;C^!ixuFErW3FJ=F@W4N1*$(8q#qo|h;&yAiB=~*4p2yd6kx}oU}a(W_#c}r zGxnUyz|6pmO*sQI1E@KHD#gRVz`%rL4mgi6Ffb@FfXV@cIiPd}(y0X&2Nho+9s>iY zIS68zz{~+T7?wvs5)2Fs%nabhH<X8<m>EC~Wdt9_VrGCgv0wrW3=Dx_^AX~(G?)t& zhw)+QxC<&S3^fBLzZ@zKZtF5IFo4q(R1Gtz)`PN<DP{&xia{2Eu$dV^p$B2Z$U|TT zrhkuv#WDSR1}x5s+}#3;Tm+M>7$J8JDh|%aU^axf4W?KbRKYZYcmQUyF+_lA1n~^a zWM$9<(+J`Xn90Uq0H#q1W_XE$5dXx$04{S7A`lh_BX)6N9OBZ93=BdHj0^?P^aCyz z85kH0ai|BCTd*_<u@OQ#;85?5L);f?Zvr&0g3CMx28JZ4cmq@%7T%!p4&=@SP;r?0 zKB)QwP;qd1$iTp`2r8}s&3fRniGhJ(Cl2=<fT~{rRSzzk7#J8Vaj3rrRqp^za^SLx zfq~&2RD1zc99#y0@*)%Vcws2YPR`HG&Ck<IhBM;}5{rsc<1<ndQ&Ni<ijqy@vr{V> ziVJe$GxO5&ixKM5Q%mAu!g>XXMJ0Nmwj0E}c$j2HN|9c2eol%WsNsetSDaau3Xuf0 z-Jp_rsi`UPAX|$Iav+i>2rG+IOP~&eNSQL|`8XSZ2tyEI3?hs`gawGO1QCW{8AGtB zp#ej@yN|z<qfflQn_IAJNPLK+laFgW*q5n!$@wX%48<iydC9p22s@KAQj@bmfs&Y8 z%urmCl3G;6kXBHXnOBm=kXBxlS(3_-R!~}k6o5#6M;HSW1bG@EitJm2D8icvF>oui z66$-9D^VN?b`MMv7OCK1gm?=SQAlPLr6$7^Cnx6Q<ipYlsQd@zC{S_+)dGJ&gDU_3 z|2F~&g6diZ1_n@`fQeT@#X;&p`4XCZ8FrzG!_?oxA<hcT|1fi4>UE*wAag-^4ko?> zDh^T)D(hk5`;o*!eKVN&F(h$Ny9g$J8A%*8gas47izE*67fk#SR2<|UP*}mlLHQ2k zHIRFd?FIGfVd7#83=A;!Y#@cu@Ki<;7lVp}C}eX~Kmt(p$o3jQ#X%IZdMl6sR6VFI z1+&)~6sh345L7=S+*1z~2l)#*em_ISLB@fYFmqTz3ZdpBtItFd2elbs>W@IhLFR}f z`S&!EIH<`FQ-2#t95g5g6MqI32T`E57EJsrNC4_D&=4j}{6AD2L?NpehL(>Y_aKLx z3X-@qk~=k_;vjR7<EscN4l)Nhe43%+Aoa-MvkysJAIbbokO5HlgIXOh_jjU+!&(w6 z(Zu;diHw1P;Ut<k$gLnLJ&@y&%r^rGGB7aoqKSjT2qd)zO&k;lAaNK5Wdl%~%MmI7 zYZ=1CJ)q*CI0Z3b;=Ujb0|NuR55T~{05T^8Dh_LZz|_O)H4CUbOdO^jW)Dm}0je$l zO2fp#eK!UM2ADidJO`>S14_fhVd^W;#EYSPSoarQ{RF5yOdM7|&Oj4ygz{HFX_)#p zC?D2Ogo(q{AArik#CxIq6Hpo^4jS+S3Bke{lq^6PBnHAT|ANFo7}gco0+NKL6OcHp zUG@V_9M*0U09goC4@$RCWeh4v;vjQi?I{~1agcgg`#b<i9Hf2$h+tq~$UqVYsRxzy zAUkT1#6e17?XL$&;vn^~_C5zl5!9U^Cahg%0pcKu!`fdFXyUMT)(kXpSUZbBuedU| zBr%CWuehWLLTA8OMX5Q7dL^k9B@B8gi6w~)dP&8_40=WRAPz{mp`IZs*Mb48T(2}Q zCo?-Wr;<SrYy?<+QEHA}PJVJ?4uc-3y_A|)!l0LzUy`cl?iZ?CT#}fa4ON|*5ua9+ zn41a;Ey^({9f5)tR{p?D1dVBc*vx1WpgI#I4#S|b6jT<%)WgIzgdi+feDHz$nGEnA z8La$JgNj3oC5R>l28IBrz3BF#^FiersEk6_p8(YlHxSAI`4!3pQ=oxFkQl5T1QrC9 zCs6$}VFFMZq!z@4(I9_=*y#FUUEXGpVk8W5D~Joy1ET$*{)buz5(499XxA7t-USwb z5-@#GE(5522NFZ~{{yIgSbrX-9u(g&Hi(Xc*7YENfcP-%0czARFfeQY8P34K0E$15 z+d&wl1{QyyaucKkR=$JeKzIjKAuOMQ#6TFN7MZSr`X3~QjIToVBa4C9F#RAl2v3CS zhh|@p5E$Qp>W7sdU_k~%`h)R%aQOcRs4;_7PJ#Rn8XbTd%K-B~$UW$GgUp@*Dx^Te zsi4dO8WUz<U;u>$x_(d{i5_<#{SwfG4w|t5@nINbAB+v61wnm45CaV-Kr8myXyWMl E0ZN`wjsO4v literal 0 HcmV?d00001 diff --git a/tools/u-boot-tools/rkimage.c b/tools/u-boot-tools/rkimage.c new file mode 100644 index 0000000..ae50de5 --- /dev/null +++ b/tools/u-boot-tools/rkimage.c @@ -0,0 +1,48 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2015 Google, Inc + * Written by Simon Glass <sjg@chromium.org> + * + * See README.rockchip for details of the rkimage format + */ + +#include "imagetool.h" +#include <image.h> +#include "rkcommon.h" + +static uint32_t header; + +static void rkimage_set_header(void *buf, struct stat *sbuf, int ifd, + struct image_tool_params *params) +{ + memcpy(buf, rkcommon_get_spl_hdr(params), RK_SPL_HDR_SIZE); + + if (rkcommon_need_rc4_spl(params)) + rkcommon_rc4_encode_spl(buf, 4, params->file_size); +} + +static int rkimage_check_image_type(uint8_t type) +{ + if (type == IH_TYPE_RKIMAGE) + return EXIT_SUCCESS; + else + return EXIT_FAILURE; +} + +/* + * rk_image parameters + */ +U_BOOT_IMAGE_TYPE( + rkimage, + "Rockchip Boot Image support", + 0, + &header, + rkcommon_check_params, + NULL, + NULL, + rkimage_set_header, + NULL, + rkimage_check_image_type, + NULL, + NULL +); diff --git a/tools/u-boot-tools/rkimage.o b/tools/u-boot-tools/rkimage.o new file mode 100644 index 0000000000000000000000000000000000000000..e4b5491911133ff2d1f9c030c00d360c4d08bce4 GIT binary patch literal 2640 zcmb<-^>JfjWMqH=Mg}_u1P><4z#zbeU^{@B4h(_}0t|)+92)+M^G`i+IE`Q4g@GZ| zqw}vv=lNid&eI;vZ#Z6nlzMdD?`G(9WdL(p4wQ0xG#}xJjy=pT@4(PKc?tsq14zK5 z^Zh;+1_p-c*q0zpXgDZ8IXgKcvp~TqKfgr5GdD3kRiU`Fpdi1f1WhHKxbDu*Rtg#} zsY#iMc?uSK#(D<2W||NN$OR%Gf`Ng7u_}mxu|j}RnuncZ0wV*100RSq3{;K_N*gdR zFn}<K>j@$p`2^aS-1#J$ncevmqPX}Z9Qgzs`8Zs;z4#<N!Ay`UtQZt`EDRt2W0PdY z8nX-x%nZ!flru0hfZ_vHikpFffeFbRm>*Q2;voNlbb$Q<VlaTrXN9s5lsPDD7!cwd zP$@sKIE=@@5CIhjQOpeNAPNIBGa%H%BthZHh~$1`pD;6kd;wF5pqUwX5F8{HGXu<* z2*E@KNL(UBAgpp6;ypOT7vT`!g+rX7C_59B`1O)utoY=N)a2}VuxNZqWkD)jqBylA zJ|i_TCAEkFAsb&%QWOt01X&m+6z}fi@8swc@9*Xo>>3gu;^^e#8qZLaot&SWo1Yh- zo>~%LT#yr=ky3;Z%u7v8i7!ev0m&dlK?13H$@wX%sNxX!79<uW<`y$3K!Ob%Q-TZ( z41fMZfG(1_5R!N(lDIIEI8+#%GO@)jC|@AQA}pPP#F5j61k@mqI4lo?gh1&DM1#bc zkm3eX7BYbC1&M>yfH2HoATbaIiGi>QhydpmP(DIRLwd!Pxh08740^>SMG!gz#wtq9 zNz^Mzttes8OGzwAWY9}0E@sdJi6WwvL9ZwuBni@PsAmX~1}g_CWY7aeUus?ngI->K zNvfW^U#M<zNn&y~R9$LDd|FXrZYtDq6i^_4gTe@@x&WI73#Ujh4?)1<0VX2NzyMAQ zF!eC;252OCKn3`~<pcvf=fmPn4JwXaqRoWrL#IG)MHbUxfS8G{e-2a;)EQ77#I0}+ z0|SE@4*M5C6`_|@=zK#Q{yzb=80Jrq|3RfEOg~KA5o*5?)IJ!s0?L5ZJy0$<-GbP# zZ~)~m5F6e94?v0-P|IVG5GcMuGz<seaQ_3S`zxRdLGA~+9fUzbF!#qp-4DxeAUP1W z06B_*fk76;LBddBxIZ$W`jM1?nV~R+U><@%7p{fsN6xb#)e%sI2_Oy<hPfXm1o9uq ztLW)B0u)(D^*qS`pxOqeALf6MHRyJO%-#W#U|?YQ0j2q&We~c4PH=q-(t(OMKqK@5 esxUm<V8Wm>7^)N|1g1Nn?r#V45CqgYZ~*}Qt?Io1 literal 0 HcmV?d00001 diff --git a/tools/u-boot-tools/rkmux.py b/tools/u-boot-tools/rkmux.py new file mode 100755 index 0000000..11c192a --- /dev/null +++ b/tools/u-boot-tools/rkmux.py @@ -0,0 +1,218 @@ +#!/usr/bin/env python2 + +# Script to create enums from datasheet register tables +# +# Usage: +# +# First, create a text file from the datasheet: +# pdftotext -layout /path/to/rockchip-3288-trm.pdf /tmp/asc +# +# Then use this script to output the #defines for a particular register: +# ./tools/rkmux.py GRF_GPIO4C_IOMUX +# +# It will create output suitable for putting in a header file, with SHIFT and +# MASK values for each bitfield in the register. +# +# Note: this tool is not perfect and you may need to edit the resulting code. +# But it should speed up the process. + +import csv +import re +import sys + +tab_to_col = 3 + +class RegField: + def __init__(self, cols=None): + if cols: + self.bits, self.attr, self.reset_val, self.desc = ( + [x.strip() for x in cols]) + self.desc = [self.desc] + else: + self.bits = '' + self.attr = '' + self.reset_val = '' + self.desc = [] + + def Setup(self, cols): + self.bits, self.attr, self.reset_val = cols[0:3] + if len(cols) > 3: + self.desc.append(cols[3]) + + def AddDesc(self, desc): + self.desc.append(desc) + + def Show(self): + print self + print + self.__init__() + + def __str__(self): + return '%s,%s,%s,%s' % (self.bits, self.attr, self.reset_val, + '\n'.join(self.desc)) + +class Printer: + def __init__(self, name): + self.first = True + self.name = name + self.re_sel = re.compile("[1-9]'b([01]+): (.*)") + + def __enter__(self): + return self + + def __exit__(self, type, value, traceback): + if not self.first: + self.output_footer() + + def output_header(self): + print '/* %s */' % self.name + print 'enum {' + + def output_footer(self): + print '};'; + + def output_regfield(self, regfield): + lines = regfield.desc + field = lines[0] + #print 'field:', field + if field in ['reserved', 'reserve', 'write_enable', 'write_mask']: + return + if field.endswith('_sel') or field.endswith('_con'): + field = field[:-4] + elif field.endswith(' iomux'): + field = field[:-6] + elif field.endswith('_mode') or field.endswith('_mask'): + field = field[:-5] + #else: + #print 'bad field %s' % field + #return + field = field.upper() + if ':' in regfield.bits: + bit_high, bit_low = [int(x) for x in regfield.bits.split(':')] + else: + bit_high = bit_low = int(regfield.bits) + bit_width = bit_high - bit_low + 1 + mask = (1 << bit_width) - 1 + if self.first: + self.first = False + self.output_header() + else: + print + out_enum(field, 'shift', bit_low) + out_enum(field, 'mask', mask) + next_val = -1 + #print 'lines: %s', lines + for line in lines: + m = self.re_sel.match(line) + if m: + val, enum = int(m.group(1), 2), m.group(2) + if enum not in ['reserved', 'reserve']: + out_enum(field, enum, val, val == next_val) + next_val = val + 1 + + +def process_file(name, fd): + field = RegField() + reg = '' + + fields = [] + + def add_it(field): + if field.bits: + if reg == name: + fields.append(field) + field = RegField() + return field + + def is_field_start(line): + if '=' in line or '+' in line: + return False + if (line.startswith('gpio') or line.startswith('peri_') or + line.endswith('_sel') or line.endswith('_con')): + return True + if not ' ' in line: # and '_' in line: + return True + return False + + for line in fd: + line = line.rstrip() + if line[:4] in ['GRF_', 'PMU_', 'CRU_']: + field = add_it(field) + reg = line + do_this = name == reg + elif not line or not line.startswith(' '): + continue + line = line.replace('\xe2\x80\x99', "'") + leading = len(line) - len(line.lstrip()) + line = line.lstrip() + cols = re.split(' *', line, 3) + if leading > 15 or (len(cols) > 3 and is_field_start(cols[3])): + if is_field_start(line): + field = add_it(field) + field.AddDesc(line) + else: + if cols[0] == 'Bit' or len(cols) < 3: + continue + #print + #print field + field = add_it(field) + field.Setup(cols) + field = add_it(field) + + with Printer(name) as printer: + for field in fields: + #print field + printer.output_regfield(field) + #print + +def out_enum(field, suffix, value, skip_val=False): + str = '%s_%s' % (field.upper(), suffix.upper()) + if not skip_val: + tabs = tab_to_col - len(str) / 8 + if value > 9: + val_str = '%#x' % value + else: + val_str = '%d' % value + + str += '%s= %s' % ('\t' * tabs, val_str) + print '\t%s,' % str + +# Process a CSV file, e.g. from tabula +def process_csv(name, fd): + reader = csv.reader(fd) + + rows = [] + + field = RegField() + for row in reader: + #print field.desc + if not row[0]: + field.desc.append(row[3]) + continue + if field.bits: + if field.bits != 'Bit': + rows.append(field) + #print row + field = RegField(row) + + with Printer(name) as printer: + for row in rows: + #print field + printer.output_regfield(row) + #print + +fname = sys.argv[1] +name = sys.argv[2] + +# Read output from pdftotext -layout +if 1: + with open(fname, 'r') as fd: + process_file(name, fd) + +# Use tabula +# It seems to be better at outputting text for an entire cell in one cell. +# But it does not always work. E.g. GRF_GPIO7CH_IOMUX. +# So there is no point in using it. +if 0: + with open(fname, 'r') as fd: + process_csv(name, fd) diff --git a/tools/u-boot-tools/rksd.c b/tools/u-boot-tools/rksd.c new file mode 100644 index 0000000..24411d8 --- /dev/null +++ b/tools/u-boot-tools/rksd.c @@ -0,0 +1,70 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2015 Google, Inc + * Written by Simon Glass <sjg@chromium.org> + * + * See README.rockchip for details of the rksd format + */ + +#include "imagetool.h" +#include <image.h> +#include <rc4.h> +#include "mkimage.h" +#include "rkcommon.h" + +static void rksd_set_header(void *buf, struct stat *sbuf, int ifd, + struct image_tool_params *params) +{ + unsigned int size; + int ret; + + /* + * We need to calculate this using 'RK_SPL_HDR_START' and not using + * 'tparams->header_size', as the additional byte inserted when + * 'is_boot0' is true counts towards the payload (and not towards the + * header). + */ + size = params->file_size - RK_SPL_HDR_START; + ret = rkcommon_set_header(buf, size, params); + if (ret) { + /* TODO(sjg@chromium.org): This method should return an error */ + printf("Warning: SPL image is too large (size %#x) and will " + "not boot\n", size); + } +} + +static int rksd_check_image_type(uint8_t type) +{ + if (type == IH_TYPE_RKSD) + return EXIT_SUCCESS; + else + return EXIT_FAILURE; +} + +static int rksd_vrec_header(struct image_tool_params *params, + struct image_type_params *tparams) +{ + /* + * Pad to a 2KB alignment, as required for init_size by the ROM + * (see https://lists.denx.de/pipermail/u-boot/2017-May/293268.html) + */ + return rkcommon_vrec_header(params, tparams, RK_INIT_SIZE_ALIGN); +} + +/* + * rk_sd parameters + */ +U_BOOT_IMAGE_TYPE( + rksd, + "Rockchip SD Boot Image support", + 0, + NULL, + rkcommon_check_params, + rkcommon_verify_header, + rkcommon_print_header, + rksd_set_header, + NULL, + rksd_check_image_type, + NULL, + rksd_vrec_header +); diff --git a/tools/u-boot-tools/rksd.o b/tools/u-boot-tools/rksd.o new file mode 100644 index 0000000000000000000000000000000000000000..516932dabc2e55e2513ce68235e2bb8b5fd3af4f GIT binary patch literal 2880 zcmb<-^>JfjWMqH=Mg}_u1P><4z`($cU^{@B4h(_}0t|)+92)*h@J~H(IE`Q4g@Iug z0|x`cOOOHxzq@e?0|P^_N9U>D84N%E|L?r_0wmdbpp-TGFuy!Q=RJ>JTabw1foQNc z5SxL4Aw01tFEcOQN+CGFM<FveF+Eiwvsj@dKVKmyu_!%NL8CacDpf&Mxk6JRF)u}- zJToUpAuqo~At^t<go}ZJAt*mNJ2@k>Kq1&g!6`q#M8OkmMsaCDL4HvQ)Dl`!?#|9u z3K}k{Ntuax3Kn|CdIq{?nh*xaS`iSzz`(#*6~w?;A;2ij!_F~*k%2*gfq_8=DhKih zhzSxi05KRC7+_)`H7-ywLlEuGC(z6k%Eiaw!tKMy0TRKAL2=8%@bN!3NoMRZ&%n&U zj7>QMGXo2TILKxuBy(Wym1AIF0EvVA0@4H%2eFwMSV0sD)`zNBfQo|s2xT)fut8Y} z${rM!Na7%OfMkQg;xHZqLj*_=ikU%a70O1Ym>IZ{xo|c!0}q@HV<dwanC=G&Gs9yG z#Dm}r21p!3cnGo%hxlY1;>&S}ufrjJ1c&%-9O4W`*~KY($zUozIU_YWJ06@1<4Y<F zQX!&cMXAa08L5dWsYMWh;?xotAE7qBprj}sWEcaI7?`i;<7~hX@9yL8<meOc@8%Zl z8WJDk=;Y%X&rp<|oS&PUpBIm62UHNro`RyxyplA8dWcI45{nXZixHw_sYRJ-l`yx# z4F_w6i9(~o7)pauwlD(&!=L{U;0hIIVqjq4M-mr<ii6Y(A&L7UiG%Vw%p9mNIEP@S zCI+xLsHlaMu?!3nPz|7bD+*<UC|JG#iNo9g64w9`;55&`AP!}NC|LOcQV;S2Oh3#X zm^er+2nT=&s5v0@uzU;C2NDBes4@l<C<7!0!Z7t7XyP#a40^?txh08740^>SMG!gz z#wtq9Nz^Mzttes8OGzwAWY9}0E@sdx$_H^k$_@1_81z7j5Ge}62P+53FzA7jN@`vS zgI->KNvfW^U#M<zNn&y~R9$LDd|FXrZYtDq6i^`lfWi*FM1qBDAez~*aDfR+GcbTl zQJ8v|xB#?BNq`FQLE{2elEKo28dMy;gq{f1hfaaqiY%tXzyK}<$<+@kmqBJ4AsfoT zz%T>KkB8DQ3g&*09*`I)UxC=@_WuA$Lfr;sf+?7OFo%JG0Tgbaya%FTVj#Kz8h)^F z0)-zaj$ry>;-NUg51bJhQ0ohjxuEz4(J&0kC)n)ofZD$Tst{y9$Zik@3Bl|Kg%8O8 zu>1*<1K|jeAOiz~EQo`Ip~5idF@Wk5m>7~WhEi;TP`@IptA&bz@+`7=1ynyQ{lVm5 z?uYR!aHL-YP+&7KFu?LZ$p4`74yGUGe~>lkc7x2m0g?d4FH{m#<_aRE2bg|N1_rFH l6A4hHFfcHD1Q~{ep~k{RKxU$+J&<Gt)cx%s4iZM!4*)z+4rBlT literal 0 HcmV?d00001 diff --git a/tools/u-boot-tools/rkspi.c b/tools/u-boot-tools/rkspi.c new file mode 100644 index 0000000..faa18fc --- /dev/null +++ b/tools/u-boot-tools/rkspi.c @@ -0,0 +1,106 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2015 Google, Inc + * Written by Simon Glass <sjg@chromium.org> + * + * See README.rockchip for details of the rkspi format + */ + +#include "imagetool.h" +#include <image.h> +#include <rc4.h> +#include "mkimage.h" +#include "rkcommon.h" + +enum { + RKSPI_SECT_LEN = RK_BLK_SIZE * 4, +}; + +static void rkspi_set_header(void *buf, struct stat *sbuf, int ifd, + struct image_tool_params *params) +{ + int sector; + unsigned int size; + int ret; + + size = params->orig_file_size; + ret = rkcommon_set_header(buf, size, params); + debug("size %x\n", size); + if (ret) { + /* TODO(sjg@chromium.org): This method should return an error */ + printf("Warning: SPL image is too large (size %#x) and will " + "not boot\n", size); + } + + /* + * Spread the image out so we only use the first 2KB of each 4KB + * region. This is a feature of the SPI format required by the Rockchip + * boot ROM. Its rationale is unknown. + */ + for (sector = size / RKSPI_SECT_LEN - 1; sector >= 0; sector--) { + debug("sector %u\n", sector); + memmove(buf + sector * RKSPI_SECT_LEN * 2, + buf + sector * RKSPI_SECT_LEN, + RKSPI_SECT_LEN); + memset(buf + sector * RKSPI_SECT_LEN * 2 + RKSPI_SECT_LEN, + '\0', RKSPI_SECT_LEN); + } +} + +static int rkspi_check_image_type(uint8_t type) +{ + if (type == IH_TYPE_RKSPI) + return EXIT_SUCCESS; + else + return EXIT_FAILURE; +} + +/* + * The SPI payload needs to be padded out to make space for odd half-sector + * layout used in flash (i.e. only the first 2K of each 4K sector is used). + */ +static int rkspi_vrec_header(struct image_tool_params *params, + struct image_type_params *tparams) +{ + int padding = rkcommon_vrec_header(params, tparams, RK_INIT_SIZE_ALIGN); + /* + * The file size has not been adjusted at this point (our caller will + * eventually add the header/padding to the file_size), so we need to + * add up the header_size, file_size and padding ourselves. + */ + int padded_size = tparams->header_size + params->file_size + padding; + + /* + * We need to store the original file-size (i.e. before padding), as + * imagetool does not set this during its adjustment of file_size. + */ + params->orig_file_size = padded_size; + + /* + * Converting to the SPI format (i.e. splitting each 4K page into two + * 2K subpages and then padding these 2K pages up to take a complete + * 4K sector again) will will double the image size. + * + * Thus we return the padded_size as an additional padding requirement + * (be sure to add this to the padding returned from the common code). + */ + return padded_size + padding; +} + +/* + * rk_spi parameters + */ +U_BOOT_IMAGE_TYPE( + rkspi, + "Rockchip SPI Boot Image support", + 0, + NULL, + rkcommon_check_params, + rkcommon_verify_header, + rkcommon_print_header, + rkspi_set_header, + NULL, + rkspi_check_image_type, + NULL, + rkspi_vrec_header +); diff --git a/tools/u-boot-tools/rkspi.o b/tools/u-boot-tools/rkspi.o new file mode 100644 index 0000000000000000000000000000000000000000..2ba893f8f3c1e89bebaed12cfd870904c0dda609 GIT binary patch literal 3200 zcmb<-^>JfjWMqH=Mg}_u1P><4z~I1v#0E1R7z7yv7z__MH2jz3pL*bM8o#^?14HO8 z1`Y-WkIt{b9-Y5En%{7|04eUCJcWUQfjN|e@lfaF84L^z9?eHM7%xP}9(D}z=sXqb z(fK#HdnQPt^B!13>jD1Ol?)6Fo%asD<L-6)-~8i0f6D|028P~g|Nrwdxb!aj|6iWL z<KP3HgO7L|4?f}cNWSEo{KTVojwr*2|Nnh@T|^l?8Q&Pb_0T*Fc9&1*ai7j-V8?kj zz5%%rY>!9p4h{|m2G7m|9>*I%q9E6LG(Z30+4zD%fPul|c*75VkQf7l=8xVEuu$iV zgD;ssd#v`fyivL$I@U4fFuy!Q=RJ>JTaZG-17L$+I{p9upMilPJh3P*GcVmrAvnNC zAu~5IJyjvISfM08Um+*4C_Pm{qd2oFRY6s`LQ^3zFGZm|GbcwOFTX?~DL=o2i-Cb5 zC_gznIU}<`AvnNO!6`q#M8OkmNO5UFL4HvQ$i{)e?#|9u3K}k{Ntuax3Kn|CdIq{? znh*xaK_Vc6fq{XsDu{uxLV!`4hn-^rBLgS^8DyYxApe1wDhvz^1|SBEcH|RiWAfya zXl8cjQ;6b|aOM+m<l_LViGZrR3!<I)1bUcU_$1nxUHBB5S(vtS@ku!H2{`d_xNw8y z@nBFIXJPmViXseE%vf^(0|PSyGlp7_05by%h(f`f3=9lRNan!&ssI%ifU1W18^mR1 zU<FYq*a)iL0V)diH<ZoHzy@U@C}&WZBZ<SZEhyX=VF{dpfdRya>0(9_hlzn`W(Mq` z$jrb4l19OqU>>IXLBh-oFyDiC5S+&VNed7jf^5MdJ`;!dS{&kAaft81A$|#m_)8q( z3`N<+1(|xu5IR0NBQ-fY9-P19ODYRep~7WFsmbvfsfj76MNpCA)DoBgLUnvWNl`r5 zKn5g1h=87tvjIcAyN|z<qfflQn_IAJNPLK+laFgWLs52eer|4lUOcKbP(dV{a#M42 z^UG2h3W_rGO41PeAg(S*EK1BRMu?WB7G<VY!dwZr1gse*%Af>|GEmAFVqjqS^B)3& zk;H|O#CIc!gUSY&dR|aQ1gCKZ22eo@6BmJsgUlC2G6!l5IA>v|LI$unI|Bm)EWb!V zHGqs0gR(&sC?A4okhnOA0mT|n8YB+W3lf6mQ;;|llKC)uVB#RPARGW9z<Ci=4j|Gx z%wCWgAPiN;U;<@;#6TEUKE$Aj!|Y?wE3V8fNlaqUD=sO5&>1jRQEE=2UP)?234>lr zVo4%{UQ%%}gI-ZShyzk?sAs{T2U3JcY!E(JIY@><50r>f^GX=>^72bk_1yhJb&E?9 zle3}fQZwSyiV|~Ep^l?~0)+=C?9fXxm_KDfl1S#m!UZNI&A<RIePQZh;ucT?E1&{= z(71q=hp@O+gNmb<?6GL}LX82ZFBls{>o72YOHXq38$#^|mGdC;U^oG4e*#PZO2ga_ zm1b~->PNT#08{}?Jybi`tuQu-28A0akAY|qABHzT!w(itpzs3;!OVk+hvKmR0n~ov zdIn@RD84~741@9sHv3P2EMs6`SOHZCvL9qO2!n)R_Qyl*hviR@90*&0G7$p<gDi-H zgrUxY$72RmKavtKGZd;H%t0bx`e8z~Q2pr2BcK8uFojSW=6)!b0hB(m`M&^Eh=Cf) zP=z4>gUUOYewhD3?txkbRSBUUKm}p$0F|wR(6kCt0n-m^r=X`Dkh}%7Do1Z1f$Rgx V!!XE9Z2EUV-QNyV2&K{W0{~J5cpLx# literal 0 HcmV?d00001 diff --git a/tools/u-boot-tools/scripts/define2mk.sed b/tools/u-boot-tools/scripts/define2mk.sed new file mode 100644 index 0000000..0f00285 --- /dev/null +++ b/tools/u-boot-tools/scripts/define2mk.sed @@ -0,0 +1,37 @@ +# +# Sed script to parse CPP macros and generate output usable by make +# +# It is expected that this script is fed the output of 'gpp -dM' +# which preprocesses the common.h header files and outputs the final +# list of CPP macros (and whitespace is sanitized) +# + +# Only process values prefixed with #define CONFIG_ +/^#define CONFIG_[A-Za-z0-9_][A-Za-z0-9_]*/ { + # Strip the #define prefix + s/#define *//; + # Change to form CONFIG_*=VALUE + s/ */=/; + # Drop trailing spaces + s/ *$//; + # drop quotes around string values + s/="\(.*\)"$/=\1/; + # Concatenate string values + s/" *"//g; + # Assume strings as default - add quotes around values + s/=\(..*\)/="\1"/; + # but remove again from decimal numbers + s/="\([0-9][0-9]*\)"/=\1/; + # ... and from negative decimal numbers + s/="\(-[1-9][0-9]*\)"/=\1/; + # ... and from hex numbers + s/="\(0[Xx][0-9a-fA-F][0-9a-fA-F]*\)"/=\1/; + # ... and from configs defined from other configs + s/="\(CONFIG_[A-Za-z0-9_][A-Za-z0-9_]*\)"/=$(\1)/; + # Change '1' and empty values to "y" (not perfect, but + # supports conditional compilation in the makefiles + s/=$/=y/; + s/=1$/=y/; + # print the line + p +} diff --git a/tools/u-boot-tools/socfpgaimage.c b/tools/u-boot-tools/socfpgaimage.c new file mode 100644 index 0000000..72d8b96 --- /dev/null +++ b/tools/u-boot-tools/socfpgaimage.c @@ -0,0 +1,402 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2014 Charles Manning <cdhmanning@gmail.com> + * + * Reference documents: + * Cyclone V SoC: https://www.altera.com/content/dam/altera-www/global/en_US/pdfs/literature/hb/cyclone-v/cv_5400a.pdf + * Arria V SoC: https://www.altera.com/content/dam/altera-www/global/en_US/pdfs/literature/hb/arria-v/av_5400a.pdf + * Arria 10 SoC: https://www.altera.com/content/dam/altera-www/global/en_US/pdfs/literature/hb/arria-10/a10_5400a.pdf + * + * Bootable SoCFPGA image requires a structure of the following format + * positioned at offset 0x40 of the bootable image. Endian is LSB. + * + * There are two versions of the SoCFPGA header format, v0 and v1. + * The version 0 is used by Cyclone V SoC and Arria V SoC, while + * the version 1 is used by the Arria 10 SoC. + * + * Version 0: + * Offset Length Usage + * ----------------------- + * 0x40 4 Validation word (0x31305341) + * 0x44 1 Version (0x0) + * 0x45 1 Flags (unused, zero is fine) + * 0x46 2 Length (in units of u32, including the end checksum). + * 0x48 2 Zero (0x0) + * 0x4A 2 Checksum over the header. NB Not CRC32 + * + * Version 1: + * Offset Length Usage + * ----------------------- + * 0x40 4 Validation word (0x31305341) + * 0x44 1 Version (0x1) + * 0x45 1 Flags (unused, zero is fine) + * 0x46 2 Header length (in units of u8). + * 0x48 4 Length (in units of u8). + * 0x4C 4 Image entry offset from standard of header + * 0x50 2 Zero (0x0) + * 0x52 2 Checksum over the header. NB Not CRC32 + * + * At the end of the code we have a 32-bit CRC checksum over whole binary + * excluding the CRC. + * + * Note that the CRC used here is **not** the zlib/Adler crc32. It is the + * CRC-32 used in bzip2, ethernet and elsewhere. + * + * The Image entry offset in version 1 image is relative the the start of + * the header, 0x40, and must not be a negative number. Therefore, it is + * only possible to make the SoCFPGA jump forward. The U-Boot bootloader + * places a trampoline instruction at offset 0x5c, 0x14 bytes from the + * start of the SoCFPGA header, which jumps to the reset vector. + * + * The image is padded out to 64k, because that is what is + * typically used to write the image to the boot medium. + */ + +#include "pbl_crc32.h" +#include "imagetool.h" +#include "mkimage.h" + +#include <image.h> + +#define HEADER_OFFSET 0x40 +#define VALIDATION_WORD 0x31305341 + +static uint8_t buffer_v0[0x10000]; +static uint8_t buffer_v1[0x40000]; + +struct socfpga_header_v0 { + uint32_t validation; + uint8_t version; + uint8_t flags; + uint16_t length_u32; + uint16_t zero; + uint16_t checksum; +}; + +struct socfpga_header_v1 { + uint32_t validation; + uint8_t version; + uint8_t flags; + uint16_t header_u8; + uint32_t length_u8; + uint32_t entry_offset; + uint16_t zero; + uint16_t checksum; +}; + +static unsigned int sfp_hdr_size(uint8_t ver) +{ + if (ver == 0) + return sizeof(struct socfpga_header_v0); + if (ver == 1) + return sizeof(struct socfpga_header_v1); + return 0; +} + +static unsigned int sfp_pad_size(uint8_t ver) +{ + if (ver == 0) + return sizeof(buffer_v0); + if (ver == 1) + return sizeof(buffer_v1); + return 0; +} + +/* + * The header checksum is just a very simple checksum over + * the header area. + * There is still a crc32 over the whole lot. + */ +static uint16_t sfp_hdr_checksum(uint8_t *buf, unsigned char ver) +{ + uint16_t ret = 0; + int len = sfp_hdr_size(ver) - sizeof(ret); + + while (--len) + ret += *buf++; + + return ret; +} + +static void sfp_build_header(uint8_t *buf, uint8_t ver, uint8_t flags, + uint32_t length_bytes) +{ + struct socfpga_header_v0 header_v0 = { + .validation = cpu_to_le32(VALIDATION_WORD), + .version = 0, + .flags = flags, + .length_u32 = cpu_to_le16(length_bytes / 4), + .zero = 0, + }; + + struct socfpga_header_v1 header_v1 = { + .validation = cpu_to_le32(VALIDATION_WORD), + .version = 1, + .flags = flags, + .header_u8 = cpu_to_le16(sizeof(header_v1)), + .length_u8 = cpu_to_le32(length_bytes), + .entry_offset = cpu_to_le32(0x14), /* Trampoline offset */ + .zero = 0, + }; + + uint16_t csum; + + if (ver == 0) { + csum = sfp_hdr_checksum((uint8_t *)&header_v0, 0); + header_v0.checksum = cpu_to_le16(csum); + memcpy(buf, &header_v0, sizeof(header_v0)); + } else { + csum = sfp_hdr_checksum((uint8_t *)&header_v1, 1); + header_v1.checksum = cpu_to_le16(csum); + memcpy(buf, &header_v1, sizeof(header_v1)); + } +} + +/* + * Perform a rudimentary verification of header and return + * size of image. + */ +static int sfp_verify_header(const uint8_t *buf, uint8_t *ver) +{ + struct socfpga_header_v0 header_v0; + struct socfpga_header_v1 header_v1; + uint16_t hdr_csum, sfp_csum; + uint32_t img_len; + + /* + * Header v0 is always smaller than Header v1 and the validation + * word and version field is at the same place, so use Header v0 + * to check for version during verifiction and upgrade to Header + * v1 if needed. + */ + memcpy(&header_v0, buf, sizeof(header_v0)); + + if (le32_to_cpu(header_v0.validation) != VALIDATION_WORD) + return -1; + + if (header_v0.version == 0) { + hdr_csum = le16_to_cpu(header_v0.checksum); + sfp_csum = sfp_hdr_checksum((uint8_t *)&header_v0, 0); + img_len = le16_to_cpu(header_v0.length_u32) * 4; + } else if (header_v0.version == 1) { + memcpy(&header_v1, buf, sizeof(header_v1)); + hdr_csum = le16_to_cpu(header_v1.checksum); + sfp_csum = sfp_hdr_checksum((uint8_t *)&header_v1, 1); + img_len = le32_to_cpu(header_v1.length_u8); + } else { /* Invalid version */ + return -EINVAL; + } + + /* Verify checksum */ + if (hdr_csum != sfp_csum) + return -EINVAL; + + *ver = header_v0.version; + return img_len; +} + +/* Sign the buffer and return the signed buffer size */ +static int sfp_sign_buffer(uint8_t *buf, uint8_t ver, uint8_t flags, + int len, int pad_64k) +{ + uint32_t calc_crc; + + /* Align the length up */ + len = (len + 3) & ~3; + + /* Build header, adding 4 bytes to length to hold the CRC32. */ + sfp_build_header(buf + HEADER_OFFSET, ver, flags, len + 4); + + /* Calculate and apply the CRC */ + calc_crc = ~pbl_crc32(0, (char *)buf, len); + + *((uint32_t *)(buf + len)) = cpu_to_le32(calc_crc); + + if (!pad_64k) + return len + 4; + + return sfp_pad_size(ver); +} + +/* Verify that the buffer looks sane */ +static int sfp_verify_buffer(const uint8_t *buf) +{ + int len; /* Including 32bit CRC */ + uint32_t calc_crc; + uint32_t buf_crc; + uint8_t ver = 0; + + len = sfp_verify_header(buf + HEADER_OFFSET, &ver); + if (len < 0) { + debug("Invalid header\n"); + return -1; + } + + if (len < HEADER_OFFSET || len > sfp_pad_size(ver)) { + debug("Invalid header length (%i)\n", len); + return -1; + } + + /* + * Adjust length to the base of the CRC. + * Check the CRC. + */ + len -= 4; + + calc_crc = ~pbl_crc32(0, (const char *)buf, len); + + buf_crc = le32_to_cpu(*((uint32_t *)(buf + len))); + + if (buf_crc != calc_crc) { + fprintf(stderr, "CRC32 does not match (%08x != %08x)\n", + buf_crc, calc_crc); + return -1; + } + + return 0; +} + +/* mkimage glue functions */ +static int socfpgaimage_verify_header(unsigned char *ptr, int image_size, + struct image_tool_params *params) +{ + if (image_size < 0x80) + return -1; + + return sfp_verify_buffer(ptr); +} + +static void socfpgaimage_print_header(const void *ptr) +{ + if (sfp_verify_buffer(ptr) == 0) + printf("Looks like a sane SOCFPGA preloader\n"); + else + printf("Not a sane SOCFPGA preloader\n"); +} + +static int socfpgaimage_check_params(struct image_tool_params *params) +{ + /* Not sure if we should be accepting fflags */ + return (params->dflag && (params->fflag || params->lflag)) || + (params->fflag && (params->dflag || params->lflag)) || + (params->lflag && (params->dflag || params->fflag)); +} + +static int socfpgaimage_check_image_types_v0(uint8_t type) +{ + if (type == IH_TYPE_SOCFPGAIMAGE) + return EXIT_SUCCESS; + return EXIT_FAILURE; +} + +static int socfpgaimage_check_image_types_v1(uint8_t type) +{ + if (type == IH_TYPE_SOCFPGAIMAGE_V1) + return EXIT_SUCCESS; + return EXIT_FAILURE; +} + +/* + * To work in with the mkimage framework, we do some ugly stuff... + * + * First, socfpgaimage_vrec_header() is called. + * We prepend a fake header big enough to make the file sfp_pad_size(). + * This gives us enough space to do what we want later. + * + * Next, socfpgaimage_set_header() is called. + * We fix up the buffer by moving the image to the start of the buffer. + * We now have some room to do what we need (add CRC and padding). + */ + +static int data_size; + +static int sfp_fake_header_size(unsigned int size, uint8_t ver) +{ + return sfp_pad_size(ver) - size; +} + +static int sfp_vrec_header(struct image_tool_params *params, + struct image_type_params *tparams, uint8_t ver) +{ + struct stat sbuf; + + if (params->datafile && + stat(params->datafile, &sbuf) == 0 && + sbuf.st_size <= (sfp_pad_size(ver) - sizeof(uint32_t))) { + data_size = sbuf.st_size; + tparams->header_size = sfp_fake_header_size(data_size, ver); + } + return 0; + +} + +static int socfpgaimage_vrec_header_v0(struct image_tool_params *params, + struct image_type_params *tparams) +{ + return sfp_vrec_header(params, tparams, 0); +} + +static int socfpgaimage_vrec_header_v1(struct image_tool_params *params, + struct image_type_params *tparams) +{ + return sfp_vrec_header(params, tparams, 1); +} + +static void sfp_set_header(void *ptr, unsigned char ver) +{ + uint8_t *buf = (uint8_t *)ptr; + + /* + * This function is called after vrec_header() has been called. + * At this stage we have the sfp_fake_header_size() dummy bytes + * followed by data_size image bytes. Total = sfp_pad_size(). + * We need to fix the buffer by moving the image bytes back to + * the beginning of the buffer, then actually do the signing stuff... + */ + memmove(buf, buf + sfp_fake_header_size(data_size, ver), data_size); + memset(buf + data_size, 0, sfp_fake_header_size(data_size, ver)); + + sfp_sign_buffer(buf, ver, 0, data_size, 0); +} + +static void socfpgaimage_set_header_v0(void *ptr, struct stat *sbuf, int ifd, + struct image_tool_params *params) +{ + sfp_set_header(ptr, 0); +} + +static void socfpgaimage_set_header_v1(void *ptr, struct stat *sbuf, int ifd, + struct image_tool_params *params) +{ + sfp_set_header(ptr, 1); +} + +U_BOOT_IMAGE_TYPE( + socfpgaimage, + "Altera SoCFPGA Cyclone V / Arria V image support", + 0, /* This will be modified by vrec_header() */ + (void *)buffer_v0, + socfpgaimage_check_params, + socfpgaimage_verify_header, + socfpgaimage_print_header, + socfpgaimage_set_header_v0, + NULL, + socfpgaimage_check_image_types_v0, + NULL, + socfpgaimage_vrec_header_v0 +); + +U_BOOT_IMAGE_TYPE( + socfpgaimage_v1, + "Altera SoCFPGA Arria10 image support", + 0, /* This will be modified by vrec_header() */ + (void *)buffer_v1, + socfpgaimage_check_params, + socfpgaimage_verify_header, + socfpgaimage_print_header, + socfpgaimage_set_header_v1, + NULL, + socfpgaimage_check_image_types_v1, + NULL, + socfpgaimage_vrec_header_v1 +); diff --git a/tools/u-boot-tools/socfpgaimage.o b/tools/u-boot-tools/socfpgaimage.o new file mode 100644 index 0000000000000000000000000000000000000000..5880dee28208c92b6cc257bcb79e793f398f6caa GIT binary patch literal 5848 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 T1Oo#DtbGI#1@)<5EC>w%AeWR; literal 0 HcmV?d00001 diff --git a/tools/u-boot-tools/stm32image.c b/tools/u-boot-tools/stm32image.c new file mode 100644 index 0000000..08b32ba --- /dev/null +++ b/tools/u-boot-tools/stm32image.c @@ -0,0 +1,147 @@ +// SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause +/* + * Copyright (C) 2018, STMicroelectronics - All Rights Reserved + */ + +#include <image.h> +#include "imagetool.h" + +/* magic ='S' 'T' 'M' 0x32 */ +#define HEADER_MAGIC be32_to_cpu(0x53544D32) +#define VER_MAJOR_IDX 2 +#define VER_MINOR_IDX 1 +#define VER_VARIANT_IDX 0 +#define HEADER_VERSION_V1 0x1 +/* default option : bit0 => no signature */ +#define HEADER_DEFAULT_OPTION (cpu_to_le32(0x00000001)) + +struct stm32_header { + uint32_t magic_number; + uint32_t image_signature[64 / 4]; + uint32_t image_checksum; + uint8_t header_version[4]; + uint32_t image_length; + uint32_t image_entry_point; + uint32_t reserved1; + uint32_t load_address; + uint32_t reserved2; + uint32_t version_number; + uint32_t option_flags; + uint32_t ecdsa_algorithm; + uint32_t ecdsa_public_key[64 / 4]; + uint32_t padding[84 / 4]; +}; + +static struct stm32_header stm32image_header; + +static void stm32image_default_header(struct stm32_header *ptr) +{ + if (!ptr) + return; + + ptr->magic_number = HEADER_MAGIC; + ptr->header_version[VER_MAJOR_IDX] = HEADER_VERSION_V1; + ptr->option_flags = HEADER_DEFAULT_OPTION; + ptr->ecdsa_algorithm = 1; +} + +static uint32_t stm32image_checksum(void *start, uint32_t len) +{ + uint32_t csum = 0; + uint32_t hdr_len = sizeof(struct stm32_header); + uint8_t *p; + + if (len < hdr_len) + return 0; + + p = start + hdr_len; + len -= hdr_len; + + while (len > 0) { + csum += *p; + p++; + len--; + } + + return csum; +} + +static int stm32image_check_image_types(uint8_t type) +{ + if (type == IH_TYPE_STM32IMAGE) + return EXIT_SUCCESS; + return EXIT_FAILURE; +} + +static int stm32image_verify_header(unsigned char *ptr, int image_size, + struct image_tool_params *params) +{ + struct stm32_header *stm32hdr = (struct stm32_header *)ptr; + int i; + + if (image_size < sizeof(struct stm32_header)) + return -1; + if (stm32hdr->magic_number != HEADER_MAGIC) + return -1; + if (stm32hdr->header_version[VER_MAJOR_IDX] != HEADER_VERSION_V1) + return -1; + if (stm32hdr->reserved1 || stm32hdr->reserved2) + return -1; + for (i = 0; i < (sizeof(stm32hdr->padding) / 4); i++) { + if (stm32hdr->padding[i] != 0) + return -1; + } + + return 0; +} + +static void stm32image_print_header(const void *ptr) +{ + struct stm32_header *stm32hdr = (struct stm32_header *)ptr; + + printf("Image Type : STMicroelectronics STM32 V%d.%d\n", + stm32hdr->header_version[VER_MAJOR_IDX], + stm32hdr->header_version[VER_MINOR_IDX]); + printf("Image Size : %lu bytes\n", + (unsigned long)le32_to_cpu(stm32hdr->image_length)); + printf("Image Load : 0x%08x\n", + le32_to_cpu(stm32hdr->load_address)); + printf("Entry Point : 0x%08x\n", + le32_to_cpu(stm32hdr->image_entry_point)); + printf("Checksum : 0x%08x\n", + le32_to_cpu(stm32hdr->image_checksum)); + printf("Option : 0x%08x\n", + le32_to_cpu(stm32hdr->option_flags)); +} + +static void stm32image_set_header(void *ptr, struct stat *sbuf, int ifd, + struct image_tool_params *params) +{ + struct stm32_header *stm32hdr = (struct stm32_header *)ptr; + + stm32image_default_header(stm32hdr); + + stm32hdr->load_address = cpu_to_le32(params->addr); + stm32hdr->image_entry_point = cpu_to_le32(params->ep); + stm32hdr->image_length = cpu_to_le32((uint32_t)sbuf->st_size - + sizeof(struct stm32_header)); + stm32hdr->image_checksum = stm32image_checksum(ptr, sbuf->st_size); +} + +/* + * stm32image parameters + */ +U_BOOT_IMAGE_TYPE( + stm32image, + "STMicroelectronics STM32MP Image support", + sizeof(struct stm32_header), + (void *)&stm32image_header, + NULL, + stm32image_verify_header, + stm32image_print_header, + stm32image_set_header, + NULL, + stm32image_check_image_types, + NULL, + NULL +); diff --git a/tools/u-boot-tools/stm32image.o b/tools/u-boot-tools/stm32image.o new file mode 100644 index 0000000000000000000000000000000000000000..161756c496f554575475eb4d525b7ac03abfcd9d GIT binary patch literal 3656 zcmb<-^>JfjWMqH=Mg}_u1P><4z;J>S!FB*M9T<cd1Q`quI5hlM;-7lpa2mh73j>2k z>;Do7j~yT*K=?R&aEPzbF?TOUk52cLZbyes_lRys7msc~gHHE=-iZwV|Nrj{_i6n3 zpMim)%*vy;9b|j!r4rTNiU0ro|L@VeMI7Wh!%O_~4Ipdzw{dthA7ErW<ze}u^iyZJ z3)Ei2OJMedVE%34o*tdQ4G-{dEBEr~wFPN<0V2AKeV|;!15kkobb$bL0T*<E6u3b2 zOOUCJ|G+NyZ?p$_wA8(!-ixu+(WTox#I^N6sYQ2r%#Q#6|Nn3OR%+4RzlMQ<q4i{` zx<_yO1_lNOkH+I5-@fMImuK*3KEUE(`J;rtTcGt)$w#oJ!~F6Lh6fHG2HArPd*&vl zrz(V07NjaDC|D^3hxleD7v-nsq$ZaX<>zH47lVY1jTFLEQ}k3*xEOFL@C{Ia7*$+a zP>^3#!T{j~XI4QBR?R6@NUAJJE#`vC_~a+1fUPvBP&Kfq;9_viD=Df}2*}UOD*?F# zChVM%nw(u+nyUZ;Rthj-|ALat{5+7D0zwd%Cqd$%)C8ibh25Q<trRp|Qj;<h^As%f zjP(q3%`_nlkQE{zf`Ng7u_}mxu|j}RnuncZ0wV*100RSq3{;K_N`u4<pzJy*Ey2LR z-~weAfM`cPfo3MAEItm183`aU1_p){D2<B(d7FjdBPfw!C}9Sr9t=?i24)6kETSNB z77TGd1_lNuBy(VX(uRtI{07nl^B0KC%)knwFfcO%8-|!8)SL-W8E{xIFfc?y#bH(> zhXXSMJ5)7-Dgwm?k~jxcss}8ND4Zgo;vkBd0U-|JgYY7-dZcgw1u#g00TezwP!=l1 z%)pB(z`($;1uTLY4m-i(nBlMwDlP@45d<W2voRPVgutv5V3Li&156``vtTA0Lp+#9 zC72nwQ3V(n7%nkD(i^%2s04(?Es9)mNv^R`Ca6@=OJ+cD;*&x7I36q!Us73+T8u1N zoLUl}k(!v2T7)cGP?VX6O}s3%C^M}RrU7AOd_hT3Ji<x_BpD=81QTY0o{zHugPxDG zA&4*n5yl|G1VorJ#Jl_WJ30Eq`@6XXyN1MvI6C>b#xsE3md3!a5bAj*1_lNp1_p*d zpwjyP|NqON;-HiQiZhrvsN4ogfz%5lsoxF~WME)GSAP&H4pI*)Q()#m;uljrD84{i zK<Y)2%)bc|#A5zqs5nTy7?S!|P;qqke1VFC)Qcmj{{t0AR}U2iXH!sF0pr7HQ1$`k zR#^E5OH&|mkXbNw3Qz_}9Ar1xKn4Z|4K#6BIRP^drXFT4r0u}~)($oYsvl-PNDPFb z!VD%*21pEqVdlW{6-W$(VfMnxA&?jdgDnD?p;ug)TauW>pjTW{1fervtfJJMM7@&K ziV_CBl*E!m2EC->Vg|jUd=Lku+)&Q~m21eL2hxK`-w-}nH%LY=CqFqche0nnKQ}iu zuY^G_FTW&J&)qLnx40xRIUA}vH6uQ)C^0t`<T{EmDEvTSjb3uW!aD^diDU~b9AQGz z(6k9t4-<zK*$Pno{NVDJfdN*2gX&3;S~aLRdd;;2st=?F8KVp9FhI;i*M9(}5Jp1{ z0r?lo1XG~$7o@}p%tH_`{h;~^Aquez#0TjC(I9_=*y#Et!HI~0p#-D|34_#vxG*{d zhyN!)^~1soqz8o2`TjV<UjeER7S5pX2jy3&Fat~+l<q-pK@YzLQ2UYVRgnGY;RmXZ zK=~aM4j_3Lwt!j$%U>Y-LA5ee7;Zl(d_eZY@;yxd52*dJFaanHQVU|j=vrv_fy9up zD7uHBk}!QxF2f|KeyDXYVFn4PeptH&CINFljNgkR{7*m?!rFTv|ASf+P+_?LLGD4f z8)S9|)cy{rBq-lWLen}(9u|L~Foh}w3B$1fG(*3Ki$EA4`yea`DF|-=K-kEn2FPFr O28KDvg3z!+76Je**x%&< literal 0 HcmV?d00001 diff --git a/tools/u-boot-tools/sunxi-spl-image-builder.c b/tools/u-boot-tools/sunxi-spl-image-builder.c new file mode 100644 index 0000000..a367f11 --- /dev/null +++ b/tools/u-boot-tools/sunxi-spl-image-builder.c @@ -0,0 +1,484 @@ +/* + * Allwinner NAND randomizer and image builder implementation: + * + * Copyright © 2016 NextThing Co. + * Copyright © 2016 Free Electrons + * + * Author: Boris Brezillon <boris.brezillon@free-electrons.com> + * + */ + +#include <linux/bch.h> + +#include <getopt.h> +#include <version.h> + +#define BCH_PRIMITIVE_POLY 0x5803 + +#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) +#define DIV_ROUND_UP(n,d) (((n) + (d) - 1) / (d)) + +struct image_info { + int ecc_strength; + int ecc_step_size; + int page_size; + int oob_size; + int usable_page_size; + int eraseblock_size; + int scramble; + int boot0; + off_t offset; + const char *source; + const char *dest; +}; + +static void swap_bits(uint8_t *buf, int len) +{ + int i, j; + + for (j = 0; j < len; j++) { + uint8_t byte = buf[j]; + + buf[j] = 0; + for (i = 0; i < 8; i++) { + if (byte & (1 << i)) + buf[j] |= (1 << (7 - i)); + } + } +} + +static uint16_t lfsr_step(uint16_t state, int count) +{ + state &= 0x7fff; + while (count--) + state = ((state >> 1) | + ((((state >> 0) ^ (state >> 1)) & 1) << 14)) & 0x7fff; + + return state; +} + +static uint16_t default_scrambler_seeds[] = { + 0x2b75, 0x0bd0, 0x5ca3, 0x62d1, 0x1c93, 0x07e9, 0x2162, 0x3a72, + 0x0d67, 0x67f9, 0x1be7, 0x077d, 0x032f, 0x0dac, 0x2716, 0x2436, + 0x7922, 0x1510, 0x3860, 0x5287, 0x480f, 0x4252, 0x1789, 0x5a2d, + 0x2a49, 0x5e10, 0x437f, 0x4b4e, 0x2f45, 0x216e, 0x5cb7, 0x7130, + 0x2a3f, 0x60e4, 0x4dc9, 0x0ef0, 0x0f52, 0x1bb9, 0x6211, 0x7a56, + 0x226d, 0x4ea7, 0x6f36, 0x3692, 0x38bf, 0x0c62, 0x05eb, 0x4c55, + 0x60f4, 0x728c, 0x3b6f, 0x2037, 0x7f69, 0x0936, 0x651a, 0x4ceb, + 0x6218, 0x79f3, 0x383f, 0x18d9, 0x4f05, 0x5c82, 0x2912, 0x6f17, + 0x6856, 0x5938, 0x1007, 0x61ab, 0x3e7f, 0x57c2, 0x542f, 0x4f62, + 0x7454, 0x2eac, 0x7739, 0x42d4, 0x2f90, 0x435a, 0x2e52, 0x2064, + 0x637c, 0x66ad, 0x2c90, 0x0bad, 0x759c, 0x0029, 0x0986, 0x7126, + 0x1ca7, 0x1605, 0x386a, 0x27f5, 0x1380, 0x6d75, 0x24c3, 0x0f8e, + 0x2b7a, 0x1418, 0x1fd1, 0x7dc1, 0x2d8e, 0x43af, 0x2267, 0x7da3, + 0x4e3d, 0x1338, 0x50db, 0x454d, 0x764d, 0x40a3, 0x42e6, 0x262b, + 0x2d2e, 0x1aea, 0x2e17, 0x173d, 0x3a6e, 0x71bf, 0x25f9, 0x0a5d, + 0x7c57, 0x0fbe, 0x46ce, 0x4939, 0x6b17, 0x37bb, 0x3e91, 0x76db, +}; + +static uint16_t brom_scrambler_seeds[] = { 0x4a80 }; + +static void scramble(const struct image_info *info, + int page, uint8_t *data, int datalen) +{ + uint16_t state; + int i; + + /* Boot0 is always scrambled no matter the command line option. */ + if (info->boot0) { + state = brom_scrambler_seeds[0]; + } else { + unsigned seedmod = info->eraseblock_size / info->page_size; + + /* Bail out earlier if the user didn't ask for scrambling. */ + if (!info->scramble) + return; + + if (seedmod > ARRAY_SIZE(default_scrambler_seeds)) + seedmod = ARRAY_SIZE(default_scrambler_seeds); + + state = default_scrambler_seeds[page % seedmod]; + } + + /* Prepare the initial state... */ + state = lfsr_step(state, 15); + + /* and start scrambling data. */ + for (i = 0; i < datalen; i++) { + data[i] ^= state; + state = lfsr_step(state, 8); + } +} + +static int write_page(const struct image_info *info, uint8_t *buffer, + FILE *src, FILE *rnd, FILE *dst, + struct bch_control *bch, int page) +{ + int steps = info->usable_page_size / info->ecc_step_size; + int eccbytes = DIV_ROUND_UP(info->ecc_strength * 14, 8); + off_t pos = ftell(dst); + size_t pad, cnt; + int i; + + if (eccbytes % 2) + eccbytes++; + + memset(buffer, 0xff, info->page_size + info->oob_size); + cnt = fread(buffer, 1, info->usable_page_size, src); + if (!cnt) { + if (!feof(src)) { + fprintf(stderr, + "Failed to read data from the source\n"); + return -1; + } else { + return 0; + } + } + + fwrite(buffer, info->page_size + info->oob_size, 1, dst); + + for (i = 0; i < info->usable_page_size; i++) { + if (buffer[i] != 0xff) + break; + } + + /* We leave empty pages at 0xff. */ + if (i == info->usable_page_size) + return 0; + + /* Restore the source pointer to read it again. */ + fseek(src, -cnt, SEEK_CUR); + + /* Randomize unused space if scrambling is required. */ + if (info->scramble) { + int offs; + + if (info->boot0) { + size_t ret; + + offs = steps * (info->ecc_step_size + eccbytes + 4); + cnt = info->page_size + info->oob_size - offs; + ret = fread(buffer + offs, 1, cnt, rnd); + if (!ret && !feof(rnd)) { + fprintf(stderr, + "Failed to read random data\n"); + return -1; + } + } else { + offs = info->page_size + (steps * (eccbytes + 4)); + cnt = info->page_size + info->oob_size - offs; + memset(buffer + offs, 0xff, cnt); + scramble(info, page, buffer + offs, cnt); + } + fseek(dst, pos + offs, SEEK_SET); + fwrite(buffer + offs, cnt, 1, dst); + } + + for (i = 0; i < steps; i++) { + int ecc_offs, data_offs; + uint8_t *ecc; + + memset(buffer, 0xff, info->ecc_step_size + eccbytes + 4); + ecc = buffer + info->ecc_step_size + 4; + if (info->boot0) { + data_offs = i * (info->ecc_step_size + eccbytes + 4); + ecc_offs = data_offs + info->ecc_step_size + 4; + } else { + data_offs = i * info->ecc_step_size; + ecc_offs = info->page_size + 4 + (i * (eccbytes + 4)); + } + + cnt = fread(buffer, 1, info->ecc_step_size, src); + if (!cnt && !feof(src)) { + fprintf(stderr, + "Failed to read data from the source\n"); + return -1; + } + + pad = info->ecc_step_size - cnt; + if (pad) { + if (info->scramble && info->boot0) { + size_t ret; + + ret = fread(buffer + cnt, 1, pad, rnd); + if (!ret && !feof(rnd)) { + fprintf(stderr, + "Failed to read random data\n"); + return -1; + } + } else { + memset(buffer + cnt, 0xff, pad); + } + } + + memset(ecc, 0, eccbytes); + swap_bits(buffer, info->ecc_step_size + 4); + encode_bch(bch, buffer, info->ecc_step_size + 4, ecc); + swap_bits(buffer, info->ecc_step_size + 4); + swap_bits(ecc, eccbytes); + scramble(info, page, buffer, info->ecc_step_size + 4 + eccbytes); + + fseek(dst, pos + data_offs, SEEK_SET); + fwrite(buffer, info->ecc_step_size, 1, dst); + fseek(dst, pos + ecc_offs - 4, SEEK_SET); + fwrite(ecc - 4, eccbytes + 4, 1, dst); + } + + /* Fix BBM. */ + fseek(dst, pos + info->page_size, SEEK_SET); + memset(buffer, 0xff, 2); + fwrite(buffer, 2, 1, dst); + + /* Make dst pointer point to the next page. */ + fseek(dst, pos + info->page_size + info->oob_size, SEEK_SET); + + return 0; +} + +static int create_image(const struct image_info *info) +{ + off_t page = info->offset / info->page_size; + struct bch_control *bch; + FILE *src, *dst, *rnd; + uint8_t *buffer; + + bch = init_bch(14, info->ecc_strength, BCH_PRIMITIVE_POLY); + if (!bch) { + fprintf(stderr, "Failed to init the BCH engine\n"); + return -1; + } + + buffer = malloc(info->page_size + info->oob_size); + if (!buffer) { + fprintf(stderr, "Failed to allocate the NAND page buffer\n"); + return -1; + } + + memset(buffer, 0xff, info->page_size + info->oob_size); + + src = fopen(info->source, "r"); + if (!src) { + fprintf(stderr, "Failed to open source file (%s)\n", + info->source); + return -1; + } + + dst = fopen(info->dest, "w"); + if (!dst) { + fprintf(stderr, "Failed to open dest file (%s)\n", info->dest); + return -1; + } + + rnd = fopen("/dev/urandom", "r"); + if (!rnd) { + fprintf(stderr, "Failed to open /dev/urandom\n"); + return -1; + } + + while (!feof(src)) { + int ret; + + ret = write_page(info, buffer, src, rnd, dst, bch, page++); + if (ret) + return ret; + } + + return 0; +} + +static void display_help(int status) +{ + fprintf(status == EXIT_SUCCESS ? stdout : stderr, + "sunxi-nand-image-builder %s\n" + "\n" + "Usage: sunxi-nand-image-builder [OPTIONS] source-image output-image\n" + "\n" + "Creates a raw NAND image that can be read by the sunxi NAND controller.\n" + "\n" + "-h --help Display this help and exit\n" + "-c <str>/<step> --ecc=<str>/<step> ECC config (strength/step-size)\n" + "-p <size> --page=<size> Page size\n" + "-o <size> --oob=<size> OOB size\n" + "-u <size> --usable=<size> Usable page size\n" + "-e <size> --eraseblock=<size> Erase block size\n" + "-b --boot0 Build a boot0 image.\n" + "-s --scramble Scramble data\n" + "-a <offset> --address=<offset> Where the image will be programmed.\n" + "\n" + "Notes:\n" + "All the information you need to pass to this tool should be part of\n" + "the NAND datasheet.\n" + "\n" + "The NAND controller only supports the following ECC configs\n" + " Valid ECC strengths: 16, 24, 28, 32, 40, 48, 56, 60 and 64\n" + " Valid ECC step size: 512 and 1024\n" + "\n" + "If you are building a boot0 image, you'll have specify extra options.\n" + "These options should be chosen based on the layouts described here:\n" + " http://linux-sunxi.org/NAND#More_information_on_BROM_NAND\n" + "\n" + " --usable should be assigned the 'Hardware page' value\n" + " --ecc should be assigned the 'ECC capacity'/'ECC page' values\n" + " --usable should be smaller than --page\n" + "\n" + "The --address option is only required for non-boot0 images that are \n" + "meant to be programmed at a non eraseblock aligned offset.\n" + "\n" + "Examples:\n" + " The H27UCG8T2BTR-BC NAND exposes\n" + " * 16k pages\n" + " * 1280 OOB bytes per page\n" + " * 4M eraseblocks\n" + " * requires data scrambling\n" + " * expects a minimum ECC of 40bits/1024bytes\n" + "\n" + " A normal image can be generated with\n" + " sunxi-nand-image-builder -p 16384 -o 1280 -e 0x400000 -s -c 40/1024\n" + " A boot0 image can be generated with\n" + " sunxi-nand-image-builder -p 16384 -o 1280 -e 0x400000 -s -b -u 4096 -c 64/1024\n", + PLAIN_VERSION); + exit(status); +} + +static int check_image_info(struct image_info *info) +{ + static int valid_ecc_strengths[] = { 16, 24, 28, 32, 40, 48, 56, 60, 64 }; + int eccbytes, eccsteps; + unsigned i; + + if (!info->page_size) { + fprintf(stderr, "--page is missing\n"); + return -EINVAL; + } + + if (!info->page_size) { + fprintf(stderr, "--oob is missing\n"); + return -EINVAL; + } + + if (!info->eraseblock_size) { + fprintf(stderr, "--eraseblock is missing\n"); + return -EINVAL; + } + + if (info->ecc_step_size != 512 && info->ecc_step_size != 1024) { + fprintf(stderr, "Invalid ECC step argument: %d\n", + info->ecc_step_size); + return -EINVAL; + } + + for (i = 0; i < ARRAY_SIZE(valid_ecc_strengths); i++) { + if (valid_ecc_strengths[i] == info->ecc_strength) + break; + } + + if (i == ARRAY_SIZE(valid_ecc_strengths)) { + fprintf(stderr, "Invalid ECC strength argument: %d\n", + info->ecc_strength); + return -EINVAL; + } + + eccbytes = DIV_ROUND_UP(info->ecc_strength * 14, 8); + if (eccbytes % 2) + eccbytes++; + eccbytes += 4; + + eccsteps = info->usable_page_size / info->ecc_step_size; + + if (info->page_size + info->oob_size < + info->usable_page_size + (eccsteps * eccbytes)) { + fprintf(stderr, + "ECC bytes do not fit in the NAND page, choose a weaker ECC\n"); + return -EINVAL; + } + + return 0; +} + +int main(int argc, char **argv) +{ + struct image_info info; + + memset(&info, 0, sizeof(info)); + /* + * Process user arguments + */ + for (;;) { + int option_index = 0; + char *endptr = NULL; + static const struct option long_options[] = { + {"help", no_argument, 0, 'h'}, + {"ecc", required_argument, 0, 'c'}, + {"page", required_argument, 0, 'p'}, + {"oob", required_argument, 0, 'o'}, + {"usable", required_argument, 0, 'u'}, + {"eraseblock", required_argument, 0, 'e'}, + {"boot0", no_argument, 0, 'b'}, + {"scramble", no_argument, 0, 's'}, + {"address", required_argument, 0, 'a'}, + {0, 0, 0, 0}, + }; + + int c = getopt_long(argc, argv, "c:p:o:u:e:ba:sh", + long_options, &option_index); + if (c == EOF) + break; + + switch (c) { + case 'h': + display_help(0); + break; + case 's': + info.scramble = 1; + break; + case 'c': + info.ecc_strength = strtol(optarg, &endptr, 0); + if (*endptr == '/') + info.ecc_step_size = strtol(endptr + 1, NULL, 0); + break; + case 'p': + info.page_size = strtol(optarg, NULL, 0); + break; + case 'o': + info.oob_size = strtol(optarg, NULL, 0); + break; + case 'u': + info.usable_page_size = strtol(optarg, NULL, 0); + break; + case 'e': + info.eraseblock_size = strtol(optarg, NULL, 0); + break; + case 'b': + info.boot0 = 1; + break; + case 'a': + info.offset = strtoull(optarg, NULL, 0); + break; + case '?': + display_help(-1); + break; + } + } + + if ((argc - optind) != 2) + display_help(-1); + + info.source = argv[optind]; + info.dest = argv[optind + 1]; + + if (!info.boot0) { + info.usable_page_size = info.page_size; + } else if (!info.usable_page_size) { + if (info.page_size > 8192) + info.usable_page_size = 8192; + else if (info.page_size > 4096) + info.usable_page_size = 4096; + else + info.usable_page_size = 1024; + } + + if (check_image_info(&info)) + display_help(-1); + + return create_image(&info); +} diff --git a/tools/u-boot-tools/ublimage.c b/tools/u-boot-tools/ublimage.c new file mode 100644 index 0000000..1d2e897 --- /dev/null +++ b/tools/u-boot-tools/ublimage.c @@ -0,0 +1,259 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2011 + * Heiko Schocher, DENX Software Engineering, hs@denx.de. + * + * Based on: + * (C) Copyright 2009 + * Stefano Babic, DENX Software Engineering, sbabic@denx.de. + * + * (C) Copyright 2008 + * Marvell Semiconductor <www.marvell.com> + * Written-by: Prafulla Wadaskar <prafulla@marvell.com> + */ + +#include "imagetool.h" +#include <image.h> +#include "ublimage.h" + +/* + * Supported commands for configuration file + */ +static table_entry_t ublimage_cmds[] = { + {CMD_BOOT_MODE, "MODE", "UBL special modes", }, + {CMD_ENTRY, "ENTRY", "Entry point addr for bootloader", }, + {CMD_PAGE, "PAGES", + "number of pages (size of bootloader)", }, + {CMD_ST_BLOCK, "START_BLOCK", + "block number where bootloader is present", }, + {CMD_ST_PAGE, "START_PAGE", + "page number where bootloader is present", }, + {CMD_LD_ADDR, "LD_ADDR", + "load addr", }, + {-1, "", "", }, +}; + +/* + * Supported Boot options for configuration file + * this is needed to set the correct flash offset + */ +static table_entry_t ublimage_bootops[] = { + {UBL_MAGIC_SAFE, "safe", "Safe boot mode", }, + {-1, "", "Invalid", }, +}; + +static struct ubl_header ublimage_header; + +static uint32_t get_cfg_value(char *token, char *name, int linenr) +{ + char *endptr; + uint32_t value; + + errno = 0; + value = strtoul(token, &endptr, 16); + if (errno || (token == endptr)) { + fprintf(stderr, "Error: %s[%d] - Invalid hex data(%s)\n", + name, linenr, token); + exit(EXIT_FAILURE); + } + return value; +} + +static void print_hdr(struct ubl_header *ubl_hdr) +{ + printf("Image Type : Davinci UBL Boot Image\n"); + printf("UBL magic : %08x\n", ubl_hdr->magic); + printf("Entry Point: %08x\n", ubl_hdr->entry); + printf("nr of pages: %08x\n", ubl_hdr->pages); + printf("start block: %08x\n", ubl_hdr->block); + printf("start page : %08x\n", ubl_hdr->page); +} + +static void parse_cfg_cmd(struct ubl_header *ublhdr, int32_t cmd, char *token, + char *name, int lineno, int fld, int dcd_len) +{ + static int cmd_ver_first = ~0; + + switch (cmd) { + case CMD_BOOT_MODE: + ublhdr->magic = get_table_entry_id(ublimage_bootops, + "ublimage special boot mode", token); + if (ublhdr->magic == -1) { + fprintf(stderr, "Error: %s[%d] -Invalid boot mode" + "(%s)\n", name, lineno, token); + exit(EXIT_FAILURE); + } + ublhdr->magic += UBL_MAGIC_BASE; + if (unlikely(cmd_ver_first != 1)) + cmd_ver_first = 0; + break; + case CMD_ENTRY: + ublhdr->entry = get_cfg_value(token, name, lineno); + break; + case CMD_PAGE: + ublhdr->pages = get_cfg_value(token, name, lineno); + break; + case CMD_ST_BLOCK: + ublhdr->block = get_cfg_value(token, name, lineno); + break; + case CMD_ST_PAGE: + ublhdr->page = get_cfg_value(token, name, lineno); + break; + case CMD_LD_ADDR: + ublhdr->pll_m = get_cfg_value(token, name, lineno); + break; + } +} + +static void parse_cfg_fld(struct ubl_header *ublhdr, int32_t *cmd, + char *token, char *name, int lineno, int fld, int *dcd_len) +{ + + switch (fld) { + case CFG_COMMAND: + *cmd = get_table_entry_id(ublimage_cmds, + "ublimage commands", token); + if (*cmd < 0) { + fprintf(stderr, "Error: %s[%d] - Invalid command" + "(%s)\n", name, lineno, token); + exit(EXIT_FAILURE); + } + break; + case CFG_REG_VALUE: + parse_cfg_cmd(ublhdr, *cmd, token, name, lineno, fld, *dcd_len); + break; + default: + break; + } +} +static uint32_t parse_cfg_file(struct ubl_header *ublhdr, char *name) +{ + FILE *fd = NULL; + char *line = NULL; + char *token, *saveptr1, *saveptr2; + int lineno = 0; + int i; + char *ptr = (char *)ublhdr; + int fld; + size_t len; + int dcd_len = 0; + int32_t cmd; + int ublhdrlen = sizeof(struct ubl_header); + + fd = fopen(name, "r"); + if (fd == 0) { + fprintf(stderr, "Error: %s - Can't open DCD file\n", name); + exit(EXIT_FAILURE); + } + + /* Fill header with 0xff */ + for (i = 0; i < ublhdrlen; i++) { + *ptr = 0xff; + ptr++; + } + + /* + * Very simple parsing, line starting with # are comments + * and are dropped + */ + while ((getline(&line, &len, fd)) > 0) { + lineno++; + + token = strtok_r(line, "\r\n", &saveptr1); + if (token == NULL) + continue; + + /* Check inside the single line */ + for (fld = CFG_COMMAND, cmd = CMD_INVALID, + line = token; ; line = NULL, fld++) { + token = strtok_r(line, " \t", &saveptr2); + if (token == NULL) + break; + + /* Drop all text starting with '#' as comments */ + if (token[0] == '#') + break; + + parse_cfg_fld(ublhdr, &cmd, token, name, + lineno, fld, &dcd_len); + } + } + fclose(fd); + + return dcd_len; +} + +static int ublimage_check_image_types(uint8_t type) +{ + if (type == IH_TYPE_UBLIMAGE) + return EXIT_SUCCESS; + else + return EXIT_FAILURE; +} + +static int ublimage_verify_header(unsigned char *ptr, int image_size, + struct image_tool_params *params) +{ + struct ubl_header *ubl_hdr = (struct ubl_header *)ptr; + + if ((ubl_hdr->magic & 0xFFFFFF00) != UBL_MAGIC_BASE) + return -1; + + return 0; +} + +static void ublimage_print_header(const void *ptr) +{ + struct ubl_header *ubl_hdr = (struct ubl_header *) ptr; + + print_hdr(ubl_hdr); +} + +static void ublimage_set_header(void *ptr, struct stat *sbuf, int ifd, + struct image_tool_params *params) +{ + struct ubl_header *ublhdr = (struct ubl_header *)ptr; + + /* Parse configuration file */ + parse_cfg_file(ublhdr, params->imagename); +} + +int ublimage_check_params(struct image_tool_params *params) +{ + if (!params) + return CFG_INVALID; + if (!strlen(params->imagename)) { + fprintf(stderr, "Error: %s - Configuration file not" + "specified, it is needed for ublimage generation\n", + params->cmdname); + return CFG_INVALID; + } + /* + * Check parameters: + * XIP is not allowed and verify that incompatible + * parameters are not sent at the same time + * For example, if list is required a data image must not be provided + */ + return (params->dflag && (params->fflag || params->lflag)) || + (params->fflag && (params->dflag || params->lflag)) || + (params->lflag && (params->dflag || params->fflag)) || + (params->xflag) || !(strlen(params->imagename)); +} + +/* + * ublimage parameters + */ +U_BOOT_IMAGE_TYPE( + ublimage, + "Davinci UBL boot support", + sizeof(struct ubl_header), + (void *)&ublimage_header, + ublimage_check_params, + ublimage_verify_header, + ublimage_print_header, + ublimage_set_header, + NULL, + ublimage_check_image_types, + NULL, + NULL +); diff --git a/tools/u-boot-tools/ublimage.h b/tools/u-boot-tools/ublimage.h new file mode 100644 index 0000000..65d6aa1 --- /dev/null +++ b/tools/u-boot-tools/ublimage.h @@ -0,0 +1,83 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * (C) Copyright 2011 + * Heiko Schocher, DENX Software Engineering, hs@denx.de. + * + * Vased on: + * (C) Copyright 2009 + * Stefano Babic, DENX Software Engineering, sbabic@denx.de. + */ + +#ifndef _UBLIMAGE_H_ +#define _UBLIMAGE_H_ + +enum ublimage_cmd { + CMD_INVALID, + CMD_BOOT_MODE, + CMD_ENTRY, + CMD_PAGE, + CMD_ST_BLOCK, + CMD_ST_PAGE, + CMD_LD_ADDR +}; + +enum ublimage_fld_types { + CFG_INVALID = -1, + CFG_COMMAND, + CFG_REG_VALUE +}; + +/* + * from sprufg5a.pdf Table 110 + * Used by RBL when doing NAND boot + */ +#define UBL_MAGIC_BASE (0xA1ACED00) +/* Safe boot mode */ +#define UBL_MAGIC_SAFE (0x00) +/* DMA boot mode */ +#define UBL_MAGIC_DMA (0x11) +/* I Cache boot mode */ +#define UBL_MAGIC_IC (0x22) +/* Fast EMIF boot mode */ +#define UBL_MAGIC_FAST (0x33) +/* DMA + ICache boot mode */ +#define UBL_MAGIC_DMA_IC (0x44) +/* DMA + ICache + Fast EMIF boot mode */ +#define UBL_MAGIC_DMA_IC_FAST (0x55) + +/* Define max UBL image size */ +#define UBL_IMAGE_SIZE (0x00003800u) + +/* one NAND block */ +#define UBL_BLOCK_SIZE 2048 + +/* from sprufg5a.pdf Table 109 */ +struct ubl_header { + uint32_t magic; /* Magic Number, see UBL_* defines */ + uint32_t entry; /* entry point address for bootloader */ + uint32_t pages; /* number of pages (size of bootloader) */ + uint32_t block; /* + * blocknumber where user bootloader is + * present + */ + uint32_t page; /* + * page number where user bootloader is + * present. + */ + uint32_t pll_m; /* + * PLL setting -Multiplier (only valid if + * Magic Number indicates PLL enable). + */ + uint32_t pll_n; /* + * PLL setting -Divider (only valid if + * Magic Number indicates PLL enable). + */ + uint32_t emif; /* + * fast EMIF setting (only valid if + * Magic Number indicates fast EMIF boot). + */ + /* to fit in one nand block */ + unsigned char res[UBL_BLOCK_SIZE - 8 * 4]; +}; + +#endif /* _UBLIMAGE_H_ */ diff --git a/tools/u-boot-tools/ublimage.o b/tools/u-boot-tools/ublimage.o new file mode 100644 index 0000000000000000000000000000000000000000..ecc367cfdb6d37122253f83530e5429ee934f949 GIT binary patch literal 8632 zcmb<-^>JfjWMqH=Mg}_u1P><4z_3FG!FB*M9T-Fygc%GEI5hm{=AU}ta2mh73j;$p zyTJilhPP`L@=ra$zwN;H8;8@<^!Vjlz*_j_8G=1Je|z-Wg19e0M7J@NXLta@FJ?g( z;6NAPK^G8!3q-#J8H!|O@b3Tr|Nr-B{a>Qs(e3WhV8Kvg(#_xP&e3|ZRJip(se8A( zjN$Fp1N>7DcixCTJYhG;^Q{L;KXr!-v|cKC2omI%cVO`74$tuD2D{s%*A!$4)WHx1 z4Bg=Z5UrO=&%9oZ@Umm5W9L=J5YNspp<u5!zmWj@bC&=E1A|9zi3*2D=l$a#^B6oj zkAkJTLm65xl?r)S#;9<VaC<Z#k%*3UjB$)T47K00^M*_3OP|h1s1Df=ayi%lB=1jf z42P<6408;13<>q<{2T1i{3gPuJJZACxQmJb$ZDU?_h5}4oh~XI9<2xXTl^S7uA9oh z!NB0r`GQ}b;TXezkLCl69+nqMzj*YzsCc*<KJn;{QE>q2kpPJpfLH<`mIa8V07(xX z-99QD9^EA>61%uSKJKnj5rEj$dVs(099SqsMZu%HM#TW;(&i(KFeN4XL1r1a8h$f8 z>Ct%$s=}l5c&Cqw4p@<A=W&nL10~ZNER;(ob^EC3w4UT|RbvLZ`w&byQhIajJl1-E zzvDC$NCC_|!~gK0Kr)lR#s2^Q{|y$({4Hkx|NnPv{>R4O9s|<Y%L@uMm(C9!$t;Z? zj3@p-NP{JDkViPchIlj|iGW0$W1M3=Eb@FhUqM|3R)mN&e)$H7p^tnz-}!X@e{tyV z|Not?B8LBw{0)mRuwHOP&G`p);^cq-{}WJK08^VzSgi|8t@XeE|97&0;t6JX^Fc<# z126Ob{r}G|&*0JtbFUECy#g=2|NsAwNdE}=Pk&Gy*y+lH8tP#GocRm&&*8tQ%CV?j z08=}iu-X!s+U&po|GRX4e5v~%&HX`tK;g~tvil!)_ZxxbSzczN$oq8ug2pGb3<5_@ zrz=DAkN^BFJq!#Cx(vd>an0b8gR#?<fxkuT-~a!|SwWEv!Y`#!%p(wmYyUulXUU)c z|93JmFfhDK`S<_7Z|4t}&WBKkfRY%bq=FVvh(w02?)A0ipN#yiivRxqe<}0#|9=Js zP-zKr77TmlCZ?w<gj5!!Dp)DFB$j37C1)yxI{7F#<>!|ufMvl-U5kqHi>wq>ixqSg zob&V2GSf?o5=%1k^AyrDb5a%Z@=J;fQj;^&GE-A@6f#Q`GK&@RQd3h>Qxwwjixf(e zax%ddrKjelLiB-33kC)TxVh1)DX|K=3Z8joi8+}m3K^*t3Mq*ti5jZKnjoccbs+l_ z^VCZe@(WV)6kMELzzzT@L3JmZdCB>?xruoxFyqkeg;|>f@^x;0N-9jDYhFoFr9we| zW?qRxVoFL8*gGIqIr)hxsYMKVrMXF|MGE<83I&Pjsl^H!#hF#9ATC0UCdgq)Ir+)i z3Q*PM8L3673J4XT&?+cOEl$lV0jULHkZHsy2SrtGVtQt>f`XNTs)0oX7X!q}0U#&C zgz^wBgb5XwBo>t@fSrXZ1hyF_RK&o`#h}2+0FOXuI2I#B!111#1NM#rDCiiFlM%?1 z3dN-b1^GoK48@6QsSLr1X%N3d6f;1Bp264O#gzf%9hiY2vx*s9{X&8w83G*LU4t2d zLmY!b;+=f_oxP!KkT`>nOT43tOArGnO2Cl>3P4gZ2dNq;Q;(hxahV3H^s&i-iUw@r z%-94O7#Kh$JT@tKIfW+4hQ^0+ak|^x+1W}#!zDE-GcixWLeE&wK-WwY!T{+N0TB!g z42)Gl42%^5jM6;p91|EB7(mS=Skn`hi9urEb_)XogAi1o1Oo$u3zVG-q8<4Jnwgm5 z`8XIDR2Uc-5<p@M3=Cx;8Z4K~#mC{q9n8hY;mRGu#mC{oZNb36&;gQXU|;}eG$%fR zUM5#Qi5_MbK7}?GM?Q^aR(C#!A}&4+M?M8dJ_#p20Vh5VM{WiN9|i`7BT&71pulwE z6X<91<df)QcH&d$WpU!u=wWr_GiYOT;j?IFcj24B$ehN=#b@EjXW+=E;l!ulglrtt zBuokv-z*Ft|6|H9Ffe0nxr4;9$2v2XSm$A2U|>Qr2Nve)P;pS$gY1TdJBZE9fIXEk zGhn6?JE%FZ_yflk0|P?{R9pe%L0I@RFfb%R#TP)u!C?!HM@9y2h6Nx8FfcHzfYKm} znSmWdp<qx41f(984q*0A1*JI#MEHQB4yKWrffL4r(;#~ok;Gxe!U3>3+;DXfa26>1 z5#li6D`53Vw!sWy0IBDLDMO)|8Te87AkGsIfhGLG{U9vi{|>C4jX@YpBZ$vnCL4nq zm_`skz)Us<OE8Tf{(_lo;B*gWLJ1~tJh3upK>09=9V!lP<iZ3Q7#MiK;%p4@NMazS zAXGdN#6iO1Q1KKbF%VN0DxMDFAYo;&I4er}07+|r#n~7tK^!Ek2Nq{#xPT-EVwyn3 zA>jcML11PEaDM<Hz`(#@1ueT!1Q{3@0&s{I;}8#J1eI}&@N^C?zZe)8vT>*{!XaLT zL%bV@_+%X7GjNE{!y&#BhxjHO;zx0a!`l;j$#7<Taz<)$c09P{7++FZkcwz^#FwQO zWu{feXQYB#VX)>&d_hrWUI|PzJ+&l0IW0ZDEHS4v6=7;|DqICzUvh2=lHs66OMXEy zLvn5k$fEeP%%b8FJu@Q{V+Mp{;tNWO;$a3Li^IiW448ZLe4Gs!^n9ERL4*;AFa{AO zAi@+xn1Kj$5Mco#3?Zt)$_>HlEy2QI4TfO7hNcYh?mqrbjz01JZf?P@A@L!OPCl;j z3<aenNFIU&WI<w4Vs0@*aY;&QQ4vELIIPkb;^R|`it_T~bMlkH4SI&+lA@CQ(j11= zip&y*G*B~}AwE7mwIn4qCo>l!n;l=o01Dlb#H5_m_*76EIX*LmAuTy4zZg>Dn?o}O zsGTCtz`*ba)NuI!|Gy1X98@xb8pbejSEx8hJ*cjSiG%80kQ7M07Lqw(AVF~5!N4Gi zBpwSDM^_IkH$dh~A*s*9p}qnt4pJ|Tq`n?1j&6P@R2-xp)SiL4|2>kp4w5<Fki=z? z#3AJ_q`jt#Bn~N`AmVyR;{M=r3nH$MB%Tiy2e}8?-g+c)IVAOMP;rpIknIJv(LhcG ziG%7?n7@`n)q~8JM>6LmR2-z<5J}t=q!8K;H9`{ihl+zJ1tjqZkN{M@F_L&8R2)Pp zB8k_61hA-I02K#ON=WLLg9M=I2UJ(i0eO;vfng0)9OQlzB=rZO;vfpy-isgsEcSkf zii0Rn?EwonF=$f+WIkw^5hkt!6$hzDjxRGLab+a;SVP4@<{-zHE0Q>Jd}Tt#LFOa7 zzZ@zKG6y-Fr$WWi)h~vMqpLp%6-QTp9x9Hm-V7uF4S!_!xI@K36ta6#K>}FR7emEC z6ten>AOS4u=R?Io6ta3YXcHIYeo%h`7B9R=;-GXA15GDFP;rns$mv8ADh@IS<n`as za?cV?{0CG#0!bV>9MYiTAoG#k*$)*5nXihJ9%dqmBd4DQP;rns$mwJik~nfY*$x#4 znGb3!!op!9)ZZX;kjr7HFat<E2nRs*gVcfKKw==w0u_ffgF#{-`~s>F)Gt9+e+DWJ z>yLocg0KNp9M+!%iGi>PR2)=yAe)l`iUg1V6oZN<P&`7pa0(<2a@!QB2+UrPIMisc z49t9xxHeb>LV(IH5DO#@at};Dtep%K2Zc3A9gK#F!}>pvxfcd-4-I4vNG%9wfCzA( zf&nyW&#)HCFM!e@b3pFj1m(lp;V|)oP;prQ7bboPDh}%RfS54xQy>ll1H%R=4HG{P z<%9Y|ASO)wA&A4kz;FUegTz6-G+2M{29h|)ozJ2C2T&TO-T}m5U|{%wBo0#l7RraU z&q2yS=4XHy3=9k$P#Q#m)c=I47XS%B!wsY!)V>8t!Q2Is2B~KOF&G#aApHhN_=ChD z{TBuX3#dGZ0*S-I6XsrYaaei)#UTiT>;z#@SplL!Vjv7lCmtX{Xn6$^houi#KL{iS z!m$1@Xyh5B1tbQ-F#o{9A0!6Cuyn<sS6rD}l9<Gx2WsAd=?oaFC^aWhuOzjigh3B9 z<jSCzR9wuUSCkLpfRr2RS)g(a8T7z<AW9${M8g=u2O9-arUz<R<}m1iMh-y(oO*fr zC8>JuexbU>C5g$|P}Qj!@o7bgxsb*q$YT^@Q22x51bu`8mcPzH%OjXIuy}y+L2W)z zzK5xYiB~`eMqWVm3qsQtC{2LWz{&?u*$J{At@vROfI0|dHb^}Tqw_&!ILJ(N{R&Y1 z;6ebVnSp@;<X<E%1E}o<vLDpeLKcVVKLC<Mk^tES;=+VM{sytp^*;b*A_fKqF_0uk z0~Eu!Fd8)817d^HAXEvM`T?~F)J6siKnZmJ`$OAQAa_8OfT;v%0K>{FQ22wK1T~fc zD$M{IR{+_M9)1$gAs?7}u%V#x1IB>Tpn4wUCUpBbK%ET+2Dm*S`$0y+%!Aoq2u&C0 z_A7uCgJ#=6j)U3-qR`_n2Z#R^p!UPs#~}MbqfhAeH$v@)l_MZKKsW<B;0Nk+fcP*B za}JE|g6fCK!{}D1ei$D{!}P)U^P&3D<G%x{A672I)Wh5l<Ilq3{|ZpXWME*J096R` zKWNkprXLpmAorl#4bn0L6j%%l3<A*n3(C%*IvQO+D4NjY4y1ntw4gWxvH&Ci#b{gx Tkh`$y=YS^sl~8>!3SB<{z1vlM literal 0 HcmV?d00001 diff --git a/tools/u-boot-tools/ubsha1.c b/tools/u-boot-tools/ubsha1.c new file mode 100644 index 0000000..90a6f3f --- /dev/null +++ b/tools/u-boot-tools/ubsha1.c @@ -0,0 +1,83 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2007 + * Heiko Schocher, DENX Software Engineering, <hs@denx.de> + */ + +#include "os_support.h" +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <fcntl.h> +#include <errno.h> +#include <string.h> +#include <sys/stat.h> +#include <u-boot/sha1.h> + +int main (int argc, char **argv) +{ + unsigned char output[20]; + int i, len; + + char *imagefile; + char *cmdname = *argv; + unsigned char *ptr; + unsigned char *data; + struct stat sbuf; + unsigned char *ptroff; + int ifd; + int off; + + if (argc > 1) { + imagefile = argv[1]; + ifd = open (imagefile, O_RDWR|O_BINARY); + if (ifd < 0) { + fprintf (stderr, "%s: Can't open %s: %s\n", + cmdname, imagefile, strerror(errno)); + exit (EXIT_FAILURE); + } + if (fstat (ifd, &sbuf) < 0) { + fprintf (stderr, "%s: Can't stat %s: %s\n", + cmdname, imagefile, strerror(errno)); + exit (EXIT_FAILURE); + } + len = sbuf.st_size; + ptr = (unsigned char *)mmap(0, len, + PROT_READ, MAP_SHARED, ifd, 0); + if (ptr == (unsigned char *)MAP_FAILED) { + fprintf (stderr, "%s: Can't read %s: %s\n", + cmdname, imagefile, strerror(errno)); + exit (EXIT_FAILURE); + } + + /* create a copy, so we can blank out the sha1 sum */ + data = malloc (len); + memcpy (data, ptr, len); + off = SHA1_SUM_POS; + ptroff = &data[len + off]; + for (i = 0; i < SHA1_SUM_LEN; i++) { + ptroff[i] = 0; + } + + sha1_csum ((unsigned char *) data, len, (unsigned char *)output); + + printf ("U-Boot sum:\n"); + for (i = 0; i < 20 ; i++) { + printf ("%02X ", output[i]); + } + printf ("\n"); + /* overwrite the sum in the bin file, with the actual */ + lseek (ifd, SHA1_SUM_POS, SEEK_END); + if (write (ifd, output, SHA1_SUM_LEN) != SHA1_SUM_LEN) { + fprintf (stderr, "%s: Can't write %s: %s\n", + cmdname, imagefile, strerror(errno)); + exit (EXIT_FAILURE); + } + + free (data); + (void) munmap((void *)ptr, len); + (void) close (ifd); + } + + return EXIT_SUCCESS; +} diff --git a/tools/u-boot-tools/version.h b/tools/u-boot-tools/version.h new file mode 120000 index 0000000..bb57607 --- /dev/null +++ b/tools/u-boot-tools/version.h @@ -0,0 +1 @@ +../include/version.h \ No newline at end of file diff --git a/tools/u-boot-tools/vybridimage.c b/tools/u-boot-tools/vybridimage.c new file mode 100644 index 0000000..94a6684 --- /dev/null +++ b/tools/u-boot-tools/vybridimage.c @@ -0,0 +1,163 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Image manipulator for Vybrid SoCs + * + * Derived from vybridimage.c + * + * (C) Copyright 2016 DENX Software Engineering GmbH + * Written-by: Albert ARIBAUD <albert.aribaud@3adev.fr> + */ + +#include "imagetool.h" +#include <compiler.h> +#include <image.h> + +/* + * NAND page 0 boot header + */ + +struct nand_page_0_boot_header { + union { + uint32_t fcb[128]; + uint8_t fcb_bytes[512]; + }; /* 0x00000000 - 0x000001ff */ + uint8_t sw_ecc[512]; /* 0x00000200 - 0x000003ff */ + uint32_t padding[65280]; /* 0x00000400 - 0x0003ffff */ + uint8_t ivt_prefix[1024]; /* 0x00040000 - 0x000403ff */ +}; + +/* signature byte for a readable block */ + +static struct nand_page_0_boot_header vybridimage_header; + +static int vybridimage_check_image_types(uint8_t type) +{ + if (type == IH_TYPE_VYBRIDIMAGE) + return EXIT_SUCCESS; + return EXIT_FAILURE; +} + +static uint8_t vybridimage_sw_ecc(uint8_t byte) +{ + uint8_t bit0 = (byte & (1 << 0)) ? 1 : 0; + uint8_t bit1 = (byte & (1 << 1)) ? 1 : 0; + uint8_t bit2 = (byte & (1 << 2)) ? 1 : 0; + uint8_t bit3 = (byte & (1 << 3)) ? 1 : 0; + uint8_t bit4 = (byte & (1 << 4)) ? 1 : 0; + uint8_t bit5 = (byte & (1 << 5)) ? 1 : 0; + uint8_t bit6 = (byte & (1 << 6)) ? 1 : 0; + uint8_t bit7 = (byte & (1 << 7)) ? 1 : 0; + uint8_t res = 0; + + res |= ((bit6 ^ bit5 ^ bit3 ^ bit2) << 0); + res |= ((bit7 ^ bit5 ^ bit4 ^ bit2 ^ bit1) << 1); + res |= ((bit7 ^ bit6 ^ bit5 ^ bit1 ^ bit0) << 2); + res |= ((bit7 ^ bit4 ^ bit3 ^ bit0) << 3); + res |= ((bit6 ^ bit4 ^ bit3 ^ bit2 ^ bit1 ^ bit0) << 4); + + return res; +} + +static int vybridimage_verify_header(unsigned char *ptr, int image_size, + struct image_tool_params *params) +{ + struct nand_page_0_boot_header *hdr = + (struct nand_page_0_boot_header *)ptr; + int idx; + + if (hdr->fcb[1] != 0x46434220) + return -1; + if (hdr->fcb[2] != 1) + return -1; + if (hdr->fcb[7] != 64) + return -1; + if (hdr->fcb[14] != 6) + return -1; + if (hdr->fcb[30] != 0x0001ff00) + return -1; + if (hdr->fcb[43] != 1) + return -1; + if (hdr->fcb[54] != 0) + return -1; + if (hdr->fcb[55] != 8) + return -1; + + /* check software ECC */ + for (idx = 0; idx < sizeof(hdr->fcb_bytes); idx++) { + uint8_t sw_ecc = vybridimage_sw_ecc(hdr->fcb_bytes[idx]); + if (sw_ecc != hdr->sw_ecc[idx]) + return -1; + } + + return 0; +} + +static void vybridimage_set_header(void *ptr, struct stat *sbuf, int ifd, + struct image_tool_params *params) +{ + struct nand_page_0_boot_header *hdr = + (struct nand_page_0_boot_header *)ptr; + int idx; + + /* fill header with 0x00 for first 56 entries then 0xff */ + memset(&hdr->fcb[0], 0x0, 56*sizeof(uint32_t)); + memset(&hdr->fcb[56], 0xff, 72*sizeof(uint32_t)); + /* fill SW ecc and padding with 0xff */ + memset(&hdr->sw_ecc[0], 0xff, sizeof(hdr->sw_ecc)); + memset(&hdr->padding[0], 0xff, sizeof(hdr->padding)); + /* fill IVT prefix with 0x00 */ + memset(&hdr->ivt_prefix[0], 0x00, sizeof(hdr->ivt_prefix)); + + /* populate fcb */ + hdr->fcb[1] = 0x46434220; /* signature */ + hdr->fcb[2] = 0x00000001; /* version */ + hdr->fcb[5] = 2048; /* page size */ + hdr->fcb[6] = (2048+64); /* page + OOB size */ + hdr->fcb[7] = 64; /* pages per block */ + hdr->fcb[14] = 6; /* ECC mode 6 */ + hdr->fcb[26] = 128; /* fw address (0x40000) in 2K pages */ + hdr->fcb[27] = 128; /* fw address (0x40000) in 2K pages */ + hdr->fcb[30] = 0x0001ff00; /* DBBT search area start address */ + hdr->fcb[33] = 2048; /* BB marker physical offset */ + hdr->fcb[43] = 1; /* DISBBM */ + hdr->fcb[54] = 0; /* DISBB_Search */ + hdr->fcb[55] = 8; /* Bad block search limit */ + + /* compute software ECC */ + for (idx = 0; idx < sizeof(hdr->fcb_bytes); idx++) + hdr->sw_ecc[idx] = vybridimage_sw_ecc(hdr->fcb_bytes[idx]); +} + +static void vybridimage_print_hdr_field(struct nand_page_0_boot_header *hdr, + int idx) +{ + printf("header.fcb[%d] = %08x\n", idx, hdr->fcb[idx]); +} + +static void vybridimage_print_header(const void *ptr) +{ + struct nand_page_0_boot_header *hdr = + (struct nand_page_0_boot_header *)ptr; + int idx; + + for (idx = 0; idx < 56; idx++) + vybridimage_print_hdr_field(hdr, idx); +} + +/* + * vybridimage parameters + */ +U_BOOT_IMAGE_TYPE( + vybridimage, + "Vybrid Boot Image", + sizeof(vybridimage_header), + (void *)&vybridimage_header, + NULL, + vybridimage_verify_header, + vybridimage_print_header, + vybridimage_set_header, + NULL, + vybridimage_check_image_types, + NULL, + NULL +); diff --git a/tools/u-boot-tools/vybridimage.o b/tools/u-boot-tools/vybridimage.o new file mode 100644 index 0000000000000000000000000000000000000000..b77b3e0e5d61ba4f964088e5b0e235e16ef9735f GIT binary patch literal 3632 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 literal 0 HcmV?d00001 diff --git a/tools/u-boot-tools/xway-swap-bytes.c b/tools/u-boot-tools/xway-swap-bytes.c new file mode 100644 index 0000000..3a6d82d --- /dev/null +++ b/tools/u-boot-tools/xway-swap-bytes.c @@ -0,0 +1,38 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#ifndef BUFSIZ +# define BUFSIZ 4096 +#endif + +#undef BUFSIZ +# define BUFSIZ 64 +int main (void) +{ + short ibuff[BUFSIZ], obuff[BUFSIZ]; + int rc, i, len; + + while ((rc = read (0, ibuff, sizeof (ibuff))) > 0) { + memset (obuff, 0, sizeof (obuff)); + for (i = 0; i < (rc + 1) / 2; i++) { + obuff[i] = ibuff[i ^ 1]; + } + + len = (rc + 1) & ~1; + + if (write (1, obuff, len) != len) { + perror ("read error"); + return (EXIT_FAILURE); + } + + memset (ibuff, 0, sizeof (ibuff)); + } + + if (rc < 0) { + perror ("read error"); + return (EXIT_FAILURE); + } + return (EXIT_SUCCESS); +} diff --git a/tools/u-boot-tools/zynqimage.c b/tools/u-boot-tools/zynqimage.c new file mode 100644 index 0000000..d3f418b --- /dev/null +++ b/tools/u-boot-tools/zynqimage.c @@ -0,0 +1,303 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2015 Nathan Rossi <nathan@nathanrossi.com> + * + * The following Boot Header format/structures and values are defined in the + * following documents: + * * Xilinx Zynq-7000 Technical Reference Manual (Section 6.3) + * * Xilinx Zynq-7000 Software Developers Guide (Appendix A.7 and A.8) + * + * Expected Header Size = 0x8C0 + * Forced as 'little' endian, 32-bit words + * + * 0x 0 - Interrupt Table (8 words) + * ... (Default value = 0xeafffffe) + * 0x 1f + * 0x 20 - Width Detection + * * DEFAULT_WIDTHDETECTION 0xaa995566 + * 0x 24 - Image Identifier + * * DEFAULT_IMAGEIDENTIFIER 0x584c4e58 + * 0x 28 - Encryption + * * 0x00000000 - None + * * 0xa5c3c5a3 - eFuse + * * 0x3a5c3c5a - bbRam + * 0x 2C - User Field + * 0x 30 - Image Offset + * 0x 34 - Image Size + * 0x 38 - Reserved (0x00000000) (according to spec) + * * FSBL defines this field for Image Destination Address. + * 0x 3C - Image Load + * 0x 40 - Image Stored Size + * 0x 44 - Reserved (0x00000000) (according to spec) + * * FSBL defines this field for QSPI configuration Data. + * 0x 48 - Checksum + * 0x 4c - Unused (21 words) + * ... + * 0x 9c + * 0x a0 - Register Initialization, 256 Address and Data word pairs + * * List is terminated with an address of 0xffffffff or + * ... * at the max number of entries + * 0x89c + * 0x8a0 - Unused (8 words) + * ... + * 0x8bf + * 0x8c0 - Data/Image starts here or above + */ + +#include "imagetool.h" +#include "mkimage.h" +#include <image.h> + +#define HEADER_INTERRUPT_DEFAULT (cpu_to_le32(0xeafffffe)) +#define HEADER_REGINIT_NULL (cpu_to_le32(0xffffffff)) +#define HEADER_WIDTHDETECTION (cpu_to_le32(0xaa995566)) +#define HEADER_IMAGEIDENTIFIER (cpu_to_le32(0x584c4e58)) + +enum { + ENCRYPTION_EFUSE = 0xa5c3c5a3, + ENCRYPTION_BBRAM = 0x3a5c3c5a, + ENCRYPTION_NONE = 0x0, +}; + +struct zynq_reginit { + uint32_t address; + uint32_t data; +}; + +#define HEADER_INTERRUPT_VECTORS 8 +#define HEADER_REGINITS 256 + +struct zynq_header { + uint32_t interrupt_vectors[HEADER_INTERRUPT_VECTORS]; /* 0x0 */ + uint32_t width_detection; /* 0x20 */ + uint32_t image_identifier; /* 0x24 */ + uint32_t encryption; /* 0x28 */ + uint32_t user_field; /* 0x2c */ + uint32_t image_offset; /* 0x30 */ + uint32_t image_size; /* 0x34 */ + uint32_t __reserved1; /* 0x38 */ + uint32_t image_load; /* 0x3c */ + uint32_t image_stored_size; /* 0x40 */ + uint32_t __reserved2; /* 0x44 */ + uint32_t checksum; /* 0x48 */ + uint32_t __reserved3[21]; /* 0x4c */ + struct zynq_reginit register_init[HEADER_REGINITS]; /* 0xa0 */ + uint32_t __reserved4[8]; /* 0x8a0 */ +}; + +static struct zynq_header zynqimage_header; + +static uint32_t zynqimage_checksum(struct zynq_header *ptr) +{ + uint32_t checksum = 0; + + if (ptr == NULL) + return 0; + + checksum += le32_to_cpu(ptr->width_detection); + checksum += le32_to_cpu(ptr->image_identifier); + checksum += le32_to_cpu(ptr->encryption); + checksum += le32_to_cpu(ptr->user_field); + checksum += le32_to_cpu(ptr->image_offset); + checksum += le32_to_cpu(ptr->image_size); + checksum += le32_to_cpu(ptr->__reserved1); + checksum += le32_to_cpu(ptr->image_load); + checksum += le32_to_cpu(ptr->image_stored_size); + checksum += le32_to_cpu(ptr->__reserved2); + checksum = ~checksum; + + return cpu_to_le32(checksum); +} + +static void zynqimage_default_header(struct zynq_header *ptr) +{ + int i; + + if (ptr == NULL) + return; + + ptr->width_detection = HEADER_WIDTHDETECTION; + ptr->image_identifier = HEADER_IMAGEIDENTIFIER; + ptr->encryption = cpu_to_le32(ENCRYPTION_NONE); + + /* Setup not-supported/constant/reserved fields */ + for (i = 0; i < HEADER_INTERRUPT_VECTORS; i++) + ptr->interrupt_vectors[i] = HEADER_INTERRUPT_DEFAULT; + + for (i = 0; i < HEADER_REGINITS; i++) { + ptr->register_init[i].address = HEADER_REGINIT_NULL; + ptr->register_init[i].data = HEADER_REGINIT_NULL; + } + + /* + * Certain reserved fields are required to be set to 0, ensure they are + * set as such. + */ + ptr->__reserved1 = 0x0; + ptr->__reserved2 = 0x0; +} + +/* mkimage glue functions */ +static int zynqimage_verify_header(unsigned char *ptr, int image_size, + struct image_tool_params *params) +{ + struct zynq_header *zynqhdr = (struct zynq_header *)ptr; + + if (image_size < sizeof(struct zynq_header)) + return -1; + + if (zynqhdr->__reserved1 != 0) + return -1; + + if (zynqhdr->__reserved2 != 0) + return -1; + + if (zynqhdr->width_detection != HEADER_WIDTHDETECTION) + return -1; + if (zynqhdr->image_identifier != HEADER_IMAGEIDENTIFIER) + return -1; + + if (zynqimage_checksum(zynqhdr) != zynqhdr->checksum) + return -1; + + return 0; +} + +static void zynqimage_print_header(const void *ptr) +{ + struct zynq_header *zynqhdr = (struct zynq_header *)ptr; + int i; + + printf("Image Type : Xilinx Zynq Boot Image support\n"); + printf("Image Offset : 0x%08x\n", le32_to_cpu(zynqhdr->image_offset)); + printf("Image Size : %lu bytes (%lu bytes packed)\n", + (unsigned long)le32_to_cpu(zynqhdr->image_size), + (unsigned long)le32_to_cpu(zynqhdr->image_stored_size)); + printf("Image Load : 0x%08x\n", le32_to_cpu(zynqhdr->image_load)); + printf("User Field : 0x%08x\n", le32_to_cpu(zynqhdr->user_field)); + printf("Checksum : 0x%08x\n", le32_to_cpu(zynqhdr->checksum)); + + for (i = 0; i < HEADER_INTERRUPT_VECTORS; i++) { + if (zynqhdr->interrupt_vectors[i] == HEADER_INTERRUPT_DEFAULT) + continue; + + printf("Modified Interrupt Vector Address [%d]: 0x%08x\n", i, + le32_to_cpu(zynqhdr->interrupt_vectors[i])); + } + + for (i = 0; i < HEADER_REGINITS; i++) { + if (zynqhdr->register_init[i].address == HEADER_REGINIT_NULL) + break; + + if (i == 0) + printf("Custom Register Initialization:\n"); + + printf(" @ 0x%08x -> 0x%08x\n", + le32_to_cpu(zynqhdr->register_init[i].address), + le32_to_cpu(zynqhdr->register_init[i].data)); + } +} + +static int zynqimage_check_params(struct image_tool_params *params) +{ + if (!params) + return 0; + + if (params->addr != 0x0) { + fprintf(stderr, "Error: Load Address cannot be specified.\n"); + return -1; + } + + /* + * If the entry point is specified ensure it is 64 byte aligned. + */ + if (params->eflag && (params->ep % 64 != 0)) { + fprintf(stderr, + "Error: Entry Point must be aligned to a 64-byte boundary.\n"); + return -1; + } + + return !(params->lflag || params->dflag); +} + +static int zynqimage_check_image_types(uint8_t type) +{ + if (type == IH_TYPE_ZYNQIMAGE) + return EXIT_SUCCESS; + return EXIT_FAILURE; +} + +static void zynqimage_parse_initparams(struct zynq_header *zynqhdr, + const char *filename) +{ + FILE *fp; + struct zynq_reginit reginit; + unsigned int reg_count = 0; + int r, err; + struct stat path_stat; + + /* Expect a table of register-value pairs, e.g. "0x12345678 0x4321" */ + fp = fopen(filename, "r"); + if (!fp) { + fprintf(stderr, "Cannot open initparams file: %s\n", filename); + exit(1); + } + + err = fstat(fileno(fp), &path_stat); + if (err) { + fclose(fp); + return; + } + + if (!S_ISREG(path_stat.st_mode)) { + fclose(fp); + return; + } + + do { + r = fscanf(fp, "%x %x", ®init.address, ®init.data); + if (r == 2) { + zynqhdr->register_init[reg_count] = reginit; + ++reg_count; + } + r = fscanf(fp, "%*[^\n]\n"); /* Skip to next line */ + } while ((r != EOF) && (reg_count < HEADER_REGINITS)); + fclose(fp); +} + +static void zynqimage_set_header(void *ptr, struct stat *sbuf, int ifd, + struct image_tool_params *params) +{ + struct zynq_header *zynqhdr = (struct zynq_header *)ptr; + zynqimage_default_header(zynqhdr); + + /* place image directly after header */ + zynqhdr->image_offset = + cpu_to_le32((uint32_t)sizeof(struct zynq_header)); + zynqhdr->image_size = cpu_to_le32((uint32_t)sbuf->st_size); + zynqhdr->image_stored_size = zynqhdr->image_size; + zynqhdr->image_load = 0x0; + if (params->eflag) + zynqhdr->image_load = cpu_to_le32((uint32_t)params->ep); + + /* User can pass in text file with init list */ + if (strlen(params->imagename2)) + zynqimage_parse_initparams(zynqhdr, params->imagename2); + + zynqhdr->checksum = zynqimage_checksum(zynqhdr); +} + +U_BOOT_IMAGE_TYPE( + zynqimage, + "Xilinx Zynq Boot Image support", + sizeof(struct zynq_header), + (void *)&zynqimage_header, + zynqimage_check_params, + zynqimage_verify_header, + zynqimage_print_header, + zynqimage_set_header, + NULL, + zynqimage_check_image_types, + NULL, + NULL +); diff --git a/tools/u-boot-tools/zynqimage.o b/tools/u-boot-tools/zynqimage.o new file mode 100644 index 0000000000000000000000000000000000000000..cbe874cc58b3b30de1f518b1bad0122237fe11fe GIT binary patch literal 5992 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 literal 0 HcmV?d00001 diff --git a/tools/u-boot-tools/zynqmp_pm_cfg_obj_convert.py b/tools/u-boot-tools/zynqmp_pm_cfg_obj_convert.py new file mode 100755 index 0000000..dd27f47 --- /dev/null +++ b/tools/u-boot-tools/zynqmp_pm_cfg_obj_convert.py @@ -0,0 +1,301 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (C) 2019 Luca Ceresoli <luca@lucaceresoli.net> + +import sys +import re +import struct +import logging +import argparse + +parser = argparse.ArgumentParser( + description='Convert a PMU configuration object from C source to a binary blob.') +parser.add_argument('-D', '--debug', action="store_true") +parser.add_argument( + "in_file", metavar='INPUT_FILE', + help='PMU configuration object (C source as produced by Xilinx XSDK)') +parser.add_argument( + "out_file", metavar='OUTPUT_FILE', + help='PMU configuration object binary blob') +args = parser.parse_args() + +logging.basicConfig(format='%(levelname)s:%(message)s', + level=(logging.DEBUG if args.debug else logging.WARNING)) + +pm_define = { + 'PM_CAP_ACCESS' : 0x1, + 'PM_CAP_CONTEXT' : 0x2, + 'PM_CAP_WAKEUP' : 0x4, + + 'NODE_UNKNOWN' : 0, + 'NODE_APU' : 1, + 'NODE_APU_0' : 2, + 'NODE_APU_1' : 3, + 'NODE_APU_2' : 4, + 'NODE_APU_3' : 5, + 'NODE_RPU' : 6, + 'NODE_RPU_0' : 7, + 'NODE_RPU_1' : 8, + 'NODE_PLD' : 9, + 'NODE_FPD' : 10, + 'NODE_OCM_BANK_0' : 11, + 'NODE_OCM_BANK_1' : 12, + 'NODE_OCM_BANK_2' : 13, + 'NODE_OCM_BANK_3' : 14, + 'NODE_TCM_0_A' : 15, + 'NODE_TCM_0_B' : 16, + 'NODE_TCM_1_A' : 17, + 'NODE_TCM_1_B' : 18, + 'NODE_L2' : 19, + 'NODE_GPU_PP_0' : 20, + 'NODE_GPU_PP_1' : 21, + 'NODE_USB_0' : 22, + 'NODE_USB_1' : 23, + 'NODE_TTC_0' : 24, + 'NODE_TTC_1' : 25, + 'NODE_TTC_2' : 26, + 'NODE_TTC_3' : 27, + 'NODE_SATA' : 28, + 'NODE_ETH_0' : 29, + 'NODE_ETH_1' : 30, + 'NODE_ETH_2' : 31, + 'NODE_ETH_3' : 32, + 'NODE_UART_0' : 33, + 'NODE_UART_1' : 34, + 'NODE_SPI_0' : 35, + 'NODE_SPI_1' : 36, + 'NODE_I2C_0' : 37, + 'NODE_I2C_1' : 38, + 'NODE_SD_0' : 39, + 'NODE_SD_1' : 40, + 'NODE_DP' : 41, + 'NODE_GDMA' : 42, + 'NODE_ADMA' : 43, + 'NODE_NAND' : 44, + 'NODE_QSPI' : 45, + 'NODE_GPIO' : 46, + 'NODE_CAN_0' : 47, + 'NODE_CAN_1' : 48, + 'NODE_EXTERN' : 49, + 'NODE_APLL' : 50, + 'NODE_VPLL' : 51, + 'NODE_DPLL' : 52, + 'NODE_RPLL' : 53, + 'NODE_IOPLL' : 54, + 'NODE_DDR' : 55, + 'NODE_IPI_APU' : 56, + 'NODE_IPI_RPU_0' : 57, + 'NODE_GPU' : 58, + 'NODE_PCIE' : 59, + 'NODE_PCAP' : 60, + 'NODE_RTC' : 61, + 'NODE_LPD' : 62, + 'NODE_VCU' : 63, + 'NODE_IPI_RPU_1' : 64, + 'NODE_IPI_PL_0' : 65, + 'NODE_IPI_PL_1' : 66, + 'NODE_IPI_PL_2' : 67, + 'NODE_IPI_PL_3' : 68, + 'NODE_PL' : 69, + 'NODE_ID_MA' : 70, + + 'XILPM_RESET_PCIE_CFG' : 1000, + 'XILPM_RESET_PCIE_BRIDGE' : 1001, + 'XILPM_RESET_PCIE_CTRL' : 1002, + 'XILPM_RESET_DP' : 1003, + 'XILPM_RESET_SWDT_CRF' : 1004, + 'XILPM_RESET_AFI_FM5' : 1005, + 'XILPM_RESET_AFI_FM4' : 1006, + 'XILPM_RESET_AFI_FM3' : 1007, + 'XILPM_RESET_AFI_FM2' : 1008, + 'XILPM_RESET_AFI_FM1' : 1009, + 'XILPM_RESET_AFI_FM0' : 1010, + 'XILPM_RESET_GDMA' : 1011, + 'XILPM_RESET_GPU_PP1' : 1012, + 'XILPM_RESET_GPU_PP0' : 1013, + 'XILPM_RESET_GPU' : 1014, + 'XILPM_RESET_GT' : 1015, + 'XILPM_RESET_SATA' : 1016, + 'XILPM_RESET_ACPU3_PWRON' : 1017, + 'XILPM_RESET_ACPU2_PWRON' : 1018, + 'XILPM_RESET_ACPU1_PWRON' : 1019, + 'XILPM_RESET_ACPU0_PWRON' : 1020, + 'XILPM_RESET_APU_L2' : 1021, + 'XILPM_RESET_ACPU3' : 1022, + 'XILPM_RESET_ACPU2' : 1023, + 'XILPM_RESET_ACPU1' : 1024, + 'XILPM_RESET_ACPU0' : 1025, + 'XILPM_RESET_DDR' : 1026, + 'XILPM_RESET_APM_FPD' : 1027, + 'XILPM_RESET_SOFT' : 1028, + 'XILPM_RESET_GEM0' : 1029, + 'XILPM_RESET_GEM1' : 1030, + 'XILPM_RESET_GEM2' : 1031, + 'XILPM_RESET_GEM3' : 1032, + 'XILPM_RESET_QSPI' : 1033, + 'XILPM_RESET_UART0' : 1034, + 'XILPM_RESET_UART1' : 1035, + 'XILPM_RESET_SPI0' : 1036, + 'XILPM_RESET_SPI1' : 1037, + 'XILPM_RESET_SDIO0' : 1038, + 'XILPM_RESET_SDIO1' : 1039, + 'XILPM_RESET_CAN0' : 1040, + 'XILPM_RESET_CAN1' : 1041, + 'XILPM_RESET_I2C0' : 1042, + 'XILPM_RESET_I2C1' : 1043, + 'XILPM_RESET_TTC0' : 1044, + 'XILPM_RESET_TTC1' : 1045, + 'XILPM_RESET_TTC2' : 1046, + 'XILPM_RESET_TTC3' : 1047, + 'XILPM_RESET_SWDT_CRL' : 1048, + 'XILPM_RESET_NAND' : 1049, + 'XILPM_RESET_ADMA' : 1050, + 'XILPM_RESET_GPIO' : 1051, + 'XILPM_RESET_IOU_CC' : 1052, + 'XILPM_RESET_TIMESTAMP' : 1053, + 'XILPM_RESET_RPU_R50' : 1054, + 'XILPM_RESET_RPU_R51' : 1055, + 'XILPM_RESET_RPU_AMBA' : 1056, + 'XILPM_RESET_OCM' : 1057, + 'XILPM_RESET_RPU_PGE' : 1058, + 'XILPM_RESET_USB0_CORERESET' : 1059, + 'XILPM_RESET_USB1_CORERESET' : 1060, + 'XILPM_RESET_USB0_HIBERRESET' : 1061, + 'XILPM_RESET_USB1_HIBERRESET' : 1062, + 'XILPM_RESET_USB0_APB' : 1063, + 'XILPM_RESET_USB1_APB' : 1064, + 'XILPM_RESET_IPI' : 1065, + 'XILPM_RESET_APM_LPD' : 1066, + 'XILPM_RESET_RTC' : 1067, + 'XILPM_RESET_SYSMON' : 1068, + 'XILPM_RESET_AFI_FM6' : 1069, + 'XILPM_RESET_LPD_SWDT' : 1070, + 'XILPM_RESET_FPD' : 1071, + 'XILPM_RESET_RPU_DBG1' : 1072, + 'XILPM_RESET_RPU_DBG0' : 1073, + 'XILPM_RESET_DBG_LPD' : 1074, + 'XILPM_RESET_DBG_FPD' : 1075, + 'XILPM_RESET_APLL' : 1076, + 'XILPM_RESET_DPLL' : 1077, + 'XILPM_RESET_VPLL' : 1078, + 'XILPM_RESET_IOPLL' : 1079, + 'XILPM_RESET_RPLL' : 1080, + 'XILPM_RESET_GPO3_PL_0' : 1081, + 'XILPM_RESET_GPO3_PL_1' : 1082, + 'XILPM_RESET_GPO3_PL_2' : 1083, + 'XILPM_RESET_GPO3_PL_3' : 1084, + 'XILPM_RESET_GPO3_PL_4' : 1085, + 'XILPM_RESET_GPO3_PL_5' : 1086, + 'XILPM_RESET_GPO3_PL_6' : 1087, + 'XILPM_RESET_GPO3_PL_7' : 1088, + 'XILPM_RESET_GPO3_PL_8' : 1089, + 'XILPM_RESET_GPO3_PL_9' : 1090, + 'XILPM_RESET_GPO3_PL_10' : 1091, + 'XILPM_RESET_GPO3_PL_11' : 1092, + 'XILPM_RESET_GPO3_PL_12' : 1093, + 'XILPM_RESET_GPO3_PL_13' : 1094, + 'XILPM_RESET_GPO3_PL_14' : 1095, + 'XILPM_RESET_GPO3_PL_15' : 1096, + 'XILPM_RESET_GPO3_PL_16' : 1097, + 'XILPM_RESET_GPO3_PL_17' : 1098, + 'XILPM_RESET_GPO3_PL_18' : 1099, + 'XILPM_RESET_GPO3_PL_19' : 1100, + 'XILPM_RESET_GPO3_PL_20' : 1101, + 'XILPM_RESET_GPO3_PL_21' : 1102, + 'XILPM_RESET_GPO3_PL_22' : 1103, + 'XILPM_RESET_GPO3_PL_23' : 1104, + 'XILPM_RESET_GPO3_PL_24' : 1105, + 'XILPM_RESET_GPO3_PL_25' : 1106, + 'XILPM_RESET_GPO3_PL_26' : 1107, + 'XILPM_RESET_GPO3_PL_27' : 1108, + 'XILPM_RESET_GPO3_PL_28' : 1109, + 'XILPM_RESET_GPO3_PL_29' : 1110, + 'XILPM_RESET_GPO3_PL_30' : 1111, + 'XILPM_RESET_GPO3_PL_31' : 1112, + 'XILPM_RESET_RPU_LS' : 1113, + 'XILPM_RESET_PS_ONLY' : 1114, + 'XILPM_RESET_PL' : 1115, + 'XILPM_RESET_GPIO5_EMIO_92' : 1116, + 'XILPM_RESET_GPIO5_EMIO_93' : 1117, + 'XILPM_RESET_GPIO5_EMIO_94' : 1118, + 'XILPM_RESET_GPIO5_EMIO_95' : 1119, + + 'PM_CONFIG_MASTER_SECTION_ID' : 0x101, + 'PM_CONFIG_SLAVE_SECTION_ID' : 0x102, + 'PM_CONFIG_PREALLOC_SECTION_ID' : 0x103, + 'PM_CONFIG_POWER_SECTION_ID' : 0x104, + 'PM_CONFIG_RESET_SECTION_ID' : 0x105, + 'PM_CONFIG_SHUTDOWN_SECTION_ID' : 0x106, + 'PM_CONFIG_SET_CONFIG_SECTION_ID' : 0x107, + 'PM_CONFIG_GPO_SECTION_ID' : 0x108, + + 'PM_SLAVE_FLAG_IS_SHAREABLE' : 0x1, + 'PM_MASTER_USING_SLAVE_MASK' : 0x2, + + 'PM_CONFIG_GPO1_MIO_PIN_34_MAP' : (1 << 10), + 'PM_CONFIG_GPO1_MIO_PIN_35_MAP' : (1 << 11), + 'PM_CONFIG_GPO1_MIO_PIN_36_MAP' : (1 << 12), + 'PM_CONFIG_GPO1_MIO_PIN_37_MAP' : (1 << 13), + + 'PM_CONFIG_GPO1_BIT_2_MASK' : (1 << 2), + 'PM_CONFIG_GPO1_BIT_3_MASK' : (1 << 3), + 'PM_CONFIG_GPO1_BIT_4_MASK' : (1 << 4), + 'PM_CONFIG_GPO1_BIT_5_MASK' : (1 << 5), + + 'SUSPEND_TIMEOUT' : 0xFFFFFFFF, + + 'PM_CONFIG_IPI_PSU_CORTEXA53_0_MASK' : 0x00000001, + 'PM_CONFIG_IPI_PSU_CORTEXR5_0_MASK' : 0x00000100, + 'PM_CONFIG_IPI_PSU_CORTEXR5_1_MASK' : 0x00000200, +} + +in_file = open(args.in_file, mode='r') +out_file = open(args.out_file, mode='wb') + +num_re = re.compile(r"^([0-9]+)U?$") +const_re = re.compile(r"^([A-Z_][A-Z0-9_]*)$") + +def process_item(item): + logging.debug("* ITEM " + item) + + value = 0 + for item in item.split('|'): + item = item.strip() + + num_match = num_re .match(item) + const_match = const_re.match(item) + + if num_match: + num = int(num_match.group(1)) + logging.debug(" - num " + str(num)) + value |= num + elif const_match: + name = const_match.group(1) + if not name in pm_define: + sys.stderr.write("Unknown define " + name + "!\n") + exit(1) + num = pm_define[name] + logging.debug(" - def " + hex(num)) + value |= num + + logging.debug(" = res " + hex(value)) + out_file.write(struct.pack('<L', value)) + + +# Read all code +code = in_file.read() + +# remove comments +code = re.sub('//.*?\n|/\*.*?\*/', '', code, flags=re.DOTALL) + +# remove everything outside the XPm_ConfigObject array definition +code = re.search('const u32 XPm_ConfigObject.*= {\n(.*)};', + code, flags=re.DOTALL).group(1) + +# Process each comma-separated array item +for item in code.split(','): + item = item.strip() + if item: + process_item(item) + +print("Wrote %d bytes" % out_file.tell()) diff --git a/tools/u-boot-tools/zynqmp_psu_init_minimize.sh b/tools/u-boot-tools/zynqmp_psu_init_minimize.sh new file mode 100755 index 0000000..4ee418f --- /dev/null +++ b/tools/u-boot-tools/zynqmp_psu_init_minimize.sh @@ -0,0 +1,147 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (C) 2018 Michal Simek <michal.simek@xilinx.com> +# Copyright (C) 2019 Luca Ceresoli <luca@lucaceresoli.net> + +usage() +{ + cat <<EOF + +Transform a pair of psu_init_gpl.c and .h files produced by the Xilinx +Vivado tool for ZynqMP into a smaller psu_init_gpl.c file that is almost +checkpatch compliant. Minor coding style might still be needed. Must be +run from the top-level U-Boot source directory. + +Usage: zynqmp_psu_init_minimize.sh INPUT_DIR OUTPUT_DIR +Example: zynqmp_psu_init_minimize.sh \\ + /path/to/original/psu_init_gpl_c_and_h/ \\ + board/xilinx/zynqmp/<my_board>/ + +Notes: INPUT_DIR must contain both .c and .h files. + If INPUT_DIR and OUTPUT_DIR are the same directory, + psu_init_gpl.c will be overwritten. + +EOF +} + +set -o errexit -o errtrace +set -o nounset + +if [ $# -ne 2 ] +then + usage >&2 + exit 1 +fi + +IN="${1}/psu_init_gpl.c" +OUT="${2}/psu_init_gpl.c" +TMP=$(mktemp /tmp/psu_init_gpl.XXXXXX) +trap "rm ${TMP}" ERR + +# Step through a temp file to allow both $IN!=$OUT and $IN==$OUT +sed -e '/sleep.h/d' \ + -e '/xil_io.h/d' \ + ${IN} >${TMP} +cp ${TMP} ${OUT} + +# preprocess to expand defines, then remove cpp lines starting with '#' +gcc -I${1} -E ${OUT} -o ${TMP} +sed '/^#/d' ${TMP} >${OUT} + +# Remove trivial code before psu_pll_init_data() +sed -ni '/psu_pll_init_data/,$p' ${OUT} + +# Functions are lowercase in U-Boot, rename them +sed -i 's/PSU_Mask_Write/psu_mask_write/g' ${OUT} +sed -i 's/mask_pollOnValue/mask_pollonvalue/g' ${OUT} +sed -i 's/RegValue/regvalue/g' ${OUT} +sed -i 's/MaskStatus/maskstatus/g' ${OUT} + +sed -i '/&= psu_peripherals_powerdwn_data()/d' ${OUT} + +FUNCS_TO_REMOVE="psu_protection +psu_..._protection +psu_init_xppu_aper_ram +mask_delay(u32 +mask_read(u32 +mask_poll(u32 +mask_pollonvalue(u32 +psu_ps_pl_reset_config_data +psu_ps_pl_isolation_removal_data +psu_apply_master_tz +psu_post_config_data +psu_post_config_data +psu_peripherals_powerdwn_data +psu_init_ddr_self_refresh +xmpu +xppu +" +for i in $FUNCS_TO_REMOVE; do +sed -i "/$i/,/^}$/d" ${OUT} +done + +scripts/Lindent ${OUT} + +# Prepend 'static' to internal functions +sed -i 's/^.*data(void)$/static &/g' ${OUT} +sed -i 's/^.*psu_afi_config(void)$/static &/g' ${OUT} +sed -i 's/^void init_peripheral/static &/g' ${OUT} +sed -i 's/^int serdes/static &/g' ${OUT} +sed -i 's/^int init_serdes/static &/g' ${OUT} +sed -i 's/^unsigned long /static &/g' ${OUT} + +sed -i 's/()$/(void)/g' ${OUT} +sed -i 's/0X/0x/g' ${OUT} + +# return (0) -> return 0 +sed -ri 's/return \(([0-9]+)\)/return \1/g' ${OUT} + +# Add header +cat << EOF >${TMP} +// SPDX-License-Identifier: GPL-2.0+ +/* + * (c) Copyright 2015 Xilinx, Inc. All rights reserved. + */ + +#include <asm/arch/psu_init_gpl.h> +#include <xil_io.h> + +EOF + +cat ${OUT} >>${TMP} +cp ${TMP} ${OUT} + +# Temporarily convert newlines to do some mangling across lines +tr "\n" "\r" <${OUT} >${TMP} + +# Cleanup empty loops. E.g.: +# |while (e) {| +# | | ==> |while (e)| +# | } | | ; | +# | | +sed -i -r 's| \{\r+(\t*)\}\r\r|\n\1\t;\n|g' ${TMP} + +# Remove empty line between variable declaration +sed -i -r 's|\r(\r\t(unsigned )?int )|\1|g' ${TMP} + +# Remove empty lines at function beginning/end +sed -i -e 's|\r{\r\r|\r{\r|g' ${TMP} +sed -i -e 's|\r\r}\r|\r}\r|g' ${TMP} + +# Remove empty lines after '{' line +sed -i -e 's| {\r\r| {\r|g' ${TMP} + +# Remove braces {} around single statement blocks. E.g.: +# | while (e) { | | while (e) | +# | stg(); | => | stg();| +# | } | +sed -i -r 's| \{(\r[^\r]*;)\r\t*\}|\1|g' ${TMP} + +# Remove Unnecessary parentheses around 'n_code <= 0x3C' and similar. E.g.: +# if ((p_code >= 0x26) && ...) -> if (p_code >= 0x26 && ...) +sed -i -r 's|\((._code .= [x[:xdigit:]]+)\)|\1|g' ${TMP} + +# Convert back newlines +tr "\r" "\n" <${TMP} >${OUT} + +rm ${TMP} diff --git a/tools/u-boot-tools/zynqmpbif.c b/tools/u-boot-tools/zynqmpbif.c new file mode 100644 index 0000000..8c47107 --- /dev/null +++ b/tools/u-boot-tools/zynqmpbif.c @@ -0,0 +1,1017 @@ +/* + * Copyright (C) 2018 Alexander Graf <agraf@suse.de> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include "imagetool.h" +#include "mkimage.h" +#include "zynqmpimage.h" +#include <elf.h> +#include <image.h> + +struct bif_entry { + const char *filename; + uint64_t flags; + uint64_t dest_cpu; + uint64_t exp_lvl; + uint64_t dest_dev; + uint64_t load; + uint64_t entry; + size_t offset; +}; + +enum bif_flag { + BIF_FLAG_AESKEYFILE, + BIF_FLAG_INIT, + BIF_FLAG_UDF_BH, + BIF_FLAG_HEADERSIGNATURE, + BIF_FLAG_PPKFILE, + BIF_FLAG_PSKFILE, + BIF_FLAG_SPKFILE, + BIF_FLAG_SSKFILE, + BIF_FLAG_SPKSIGNATURE, + BIF_FLAG_FSBL_CONFIG, + BIF_FLAG_AUTH_PARAMS, + BIF_FLAG_KEYSRC_ENCRYPTION, + BIF_FLAG_PMUFW_IMAGE, + BIF_FLAG_BOOTLOADER, + BIF_FLAG_TZ, + BIF_FLAG_BH_KEY_IV, + BIF_FLAG_BH_KEYFILE, + BIF_FLAG_PUF_FILE, + BIF_FLAG_AARCH32, + BIF_FLAG_PART_OWNER_UBOOT, + + /* Internal flags */ + BIF_FLAG_BIT_FILE, + BIF_FLAG_ELF_FILE, + BIF_FLAG_BIN_FILE, +}; + +struct bif_flags { + const char name[32]; + uint64_t flag; + char *(*parse)(char *line, struct bif_entry *bf); +}; + +struct bif_file_type { + const char name[32]; + uint32_t header; + int (*add)(struct bif_entry *bf); +}; + +struct bif_output { + size_t data_len; + char *data; + struct image_header_table *imgheader; + struct zynqmp_header *header; + struct partition_header *last_part; +}; + +struct bif_output bif_output; + +static uint32_t zynqmp_csum(void *start, void *end) +{ + uint32_t checksum = 0; + uint32_t *ptr32 = start; + + while (ptr32 != end) { + checksum += le32_to_cpu(*ptr32); + ptr32++; + } + + return ~checksum; +} + +static int zynqmpbif_check_params(struct image_tool_params *params) +{ + if (!params) + return 0; + + if (params->addr != 0x0) { + fprintf(stderr, "Error: Load Address can not be specified.\n"); + return -1; + } + + if (params->eflag) { + fprintf(stderr, "Error: Entry Point can not be specified.\n"); + return -1; + } + + return !(params->lflag || params->dflag); +} + +static int zynqmpbif_check_image_types(uint8_t type) +{ + return (type == IH_TYPE_ZYNQMPBIF) ? EXIT_SUCCESS : EXIT_FAILURE; +} + +static char *parse_dest_cpu(char *line, struct bif_entry *bf) +{ + uint64_t i; + + for (i = 0; i < ARRAY_SIZE(dest_cpus); i++) { + if (!strncmp(line, dest_cpus[i], strlen(dest_cpus[i]))) { + bf->dest_cpu = i << PART_ATTR_DEST_CPU_SHIFT; + return line + strlen(dest_cpus[i]); + } + + /* a5x can also be written as a53 */ + if (!strncmp(dest_cpus[i], "a5x", 3)) { + char a53[] = "a53-X"; + + a53[4] = dest_cpus[i][4]; + if (!strncmp(line, a53, strlen(a53))) { + bf->dest_cpu = i << PART_ATTR_DEST_CPU_SHIFT; + return line + strlen(a53); + } + } + } + + return line; +} + +static char *parse_el(char *line, struct bif_entry *bf) +{ + const char *dest_els[] = { "none", "el-0", "el-1", "el-2", "el-3" }; + int i; + + for (i = 0; i < ARRAY_SIZE(dest_els); i++) { + if (!strncmp(line, dest_els[i], strlen(dest_els[i]))) { + bf->exp_lvl = i; + return line + strlen(dest_els[i]); + } + } + + return line; +} + +static char *parse_load(char *line, struct bif_entry *bf) +{ + char *endptr; + + bf->load = strtoll(line, &endptr, 0); + + return endptr; +} + +static char *parse_entry(char *line, struct bif_entry *bf) +{ + char *endptr; + + bf->entry = strtoll(line, &endptr, 0); + + return endptr; +} + +static char *parse_offset(char *line, struct bif_entry *bf) +{ + char *endptr; + + bf->offset = strtoll(line, &endptr, 0); + + return endptr; +} + +static char *parse_partition_owner(char *line, struct bif_entry *bf) +{ + char *endptr = NULL; + + if (!strncmp(line, "fsbl", 4)) { + endptr = line + 4; + } else if (!strncmp(line, "uboot", 5)) { + bf->flags |= 1ULL << BIF_FLAG_PART_OWNER_UBOOT; + endptr = line + 5; + } else { + printf("ERROR: Unknown partition type '%s'\n", line); + } + + return endptr; +} + +static const struct bif_flags bif_flags[] = { + { "fsbl_config", BIF_FLAG_FSBL_CONFIG }, + { "trustzone", BIF_FLAG_TZ }, + { "pmufw_image", BIF_FLAG_PMUFW_IMAGE }, + { "bootloader", BIF_FLAG_BOOTLOADER }, + { "destination_cpu=", 0, parse_dest_cpu }, + { "exception_level=", 0, parse_el }, + { "load=", 0, parse_load }, + { "startup=", 0, parse_entry }, + { "offset=", 0, parse_offset }, + { "partition_owner=", 0, parse_partition_owner }, +}; + +static char *read_full_file(const char *filename, size_t *size) +{ + char *buf, *bufp; + struct stat sbuf; + int len = 0, r, fd; + + fd = open(filename, O_RDONLY); + if (fd < 0) + return NULL; + + if (fstat(fd, &sbuf) < 0) + return NULL; + + if (size) + *size = sbuf.st_size; + + buf = malloc(sbuf.st_size); + if (!buf) + return NULL; + + bufp = buf; + while (len < sbuf.st_size) { + r = read(fd, bufp, sbuf.st_size - len); + if (r < 0) + return NULL; + len += r; + bufp += r; + } + + close(fd); + + return buf; +} + +static int bif_add_blob(const void *data, size_t len, size_t *offset) +{ + size_t new_size; + uintptr_t header_off; + uintptr_t last_part_off; + uintptr_t imgheader_off; + uintptr_t old_data = (uintptr_t)bif_output.data; + void *new_data; + + header_off = (uintptr_t)bif_output.header - old_data; + last_part_off = (uintptr_t)bif_output.last_part - old_data; + imgheader_off = (uintptr_t)bif_output.imgheader - old_data; + + if (offset && *offset) { + /* Pad to a given offset */ + if (bif_output.data_len > *offset) { + printf("Can not pad to offset %zx\n", *offset); + return -1; + } + + bif_output.data_len = *offset; + } + + new_size = ROUND(bif_output.data_len + len, 64); + new_data = realloc(bif_output.data, new_size); + memcpy(new_data + bif_output.data_len, data, len); + if (offset) + *offset = bif_output.data_len; + bif_output.data = new_data; + bif_output.data_len = new_size; + + /* Readjust internal pointers */ + if (bif_output.header) + bif_output.header = new_data + header_off; + if (bif_output.last_part) + bif_output.last_part = new_data + last_part_off; + if (bif_output.imgheader) + bif_output.imgheader = new_data + imgheader_off; + + return 0; +} + +static int bif_init(void) +{ + struct zynqmp_header header = { { 0 } }; + int r; + + zynqmpimage_default_header(&header); + + r = bif_add_blob(&header, sizeof(header), NULL); + if (r) + return r; + + bif_output.header = (void *)bif_output.data; + + return 0; +} + +static int bif_add_pmufw(struct bif_entry *bf, const char *data, size_t len) +{ + int r; + + if (bif_output.header->image_offset) { + printf("PMUFW expected before bootloader in your .bif file!\n"); + return -1; + } + + r = bif_add_blob(data, len, &bf->offset); + if (r) + return r; + + len = ROUND(len, 64); + bif_output.header->pfw_image_length = cpu_to_le32(len); + bif_output.header->total_pfw_image_length = cpu_to_le32(len); + bif_output.header->image_offset = cpu_to_le32(bf->offset); + + return 0; +} + +static int bif_add_part(struct bif_entry *bf, const char *data, size_t len) +{ + size_t parthdr_offset = 0; + size_t len_padded = ROUND(len, 4); + + struct partition_header parthdr = { + .len_enc = cpu_to_le32(len_padded / 4), + .len_unenc = cpu_to_le32(len_padded / 4), + .len = cpu_to_le32(len_padded / 4), + .entry_point = cpu_to_le64(bf->entry), + .load_address = cpu_to_le64(bf->load), + }; + int r; + uint32_t csum; + + if (len < len_padded) { + char *newdata = malloc(len_padded); + memcpy(newdata, data, len); + memset(newdata + len, 0, len_padded - len); + data = newdata; + } + + if (bf->flags & (1ULL << BIF_FLAG_PMUFW_IMAGE)) + return bif_add_pmufw(bf, data, len); + + r = bif_add_blob(data, len, &bf->offset); + if (r) + return r; + + parthdr.offset = cpu_to_le32(bf->offset / 4); + + if (bf->flags & (1ULL << BIF_FLAG_BOOTLOADER)) { + if (bif_output.last_part) { + printf("ERROR: Bootloader expected before others\n"); + return -1; + } + + parthdr.offset = cpu_to_le32(bif_output.header->image_offset); + parthdr.len = cpu_to_le32((bf->offset + len - + bif_output.header->image_offset) / 4); + parthdr.len_enc = parthdr.len; + parthdr.len_unenc = parthdr.len; + } + + /* Normalize EL */ + bf->exp_lvl = bf->exp_lvl ? bf->exp_lvl - 1 : 3; + parthdr.attributes |= bf->exp_lvl << PART_ATTR_TARGET_EL_SHIFT; + parthdr.attributes |= bf->dest_dev; + parthdr.attributes |= bf->dest_cpu; + if (bf->flags & (1ULL << BIF_FLAG_TZ)) + parthdr.attributes |= PART_ATTR_TZ_SECURE; + if (bf->flags & (1ULL << BIF_FLAG_PART_OWNER_UBOOT)) + parthdr.attributes |= PART_ATTR_PART_OWNER_UBOOT; + switch (bf->dest_cpu) { + case PART_ATTR_DEST_CPU_NONE: + case PART_ATTR_DEST_CPU_A53_0: + case PART_ATTR_DEST_CPU_A53_1: + case PART_ATTR_DEST_CPU_A53_2: + case PART_ATTR_DEST_CPU_A53_3: + if (bf->flags & (1ULL << BIF_FLAG_AARCH32)) + parthdr.attributes |= PART_ATTR_A53_EXEC_AARCH32; + } + + csum = zynqmp_csum(&parthdr, &parthdr.checksum); + parthdr.checksum = cpu_to_le32(csum); + + r = bif_add_blob(&parthdr, sizeof(parthdr), &parthdr_offset); + if (r) + return r; + + /* Add image header table if not there yet */ + if (!bif_output.imgheader) { + size_t imghdr_off = 0; + struct image_header_table imghdr = { + .version = cpu_to_le32(0x01020000), + .nr_parts = 0, + }; + + r = bif_add_blob(&imghdr, sizeof(imghdr), &imghdr_off); + if (r) + return r; + + bif_output.header->image_header_table_offset = imghdr_off; + bif_output.imgheader = (void *)(bif_output.data + imghdr_off); + } + + bif_output.imgheader->nr_parts = cpu_to_le32(le32_to_cpu( + bif_output.imgheader->nr_parts) + 1); + + /* Link to this partition header */ + if (bif_output.last_part) { + bif_output.last_part->next_partition_offset = + cpu_to_le32(parthdr_offset / 4); + + /* Recalc checksum of last_part */ + csum = zynqmp_csum(bif_output.last_part, + &bif_output.last_part->checksum); + bif_output.last_part->checksum = cpu_to_le32(csum); + } else { + bif_output.imgheader->partition_header_offset = + cpu_to_le32(parthdr_offset / 4); + } + bif_output.last_part = (void *)(bif_output.data + parthdr_offset); + + if (bf->flags & (1ULL << BIF_FLAG_BOOTLOADER)) { + bif_output.header->image_load = cpu_to_le32(bf->load); + if (!bif_output.header->image_offset) + bif_output.header->image_offset = + cpu_to_le32(bf->offset); + bif_output.header->image_size = cpu_to_le32(len_padded); + bif_output.header->image_stored_size = cpu_to_le32(len_padded); + + bif_output.header->image_attributes &= ~HEADER_CPU_SELECT_MASK; + switch (bf->dest_cpu) { + default: + case PART_ATTR_DEST_CPU_A53_0: + if (bf->flags & BIF_FLAG_AARCH32) + bif_output.header->image_attributes |= + HEADER_CPU_SELECT_A53_32BIT; + else + bif_output.header->image_attributes |= + HEADER_CPU_SELECT_A53_64BIT; + break; + case PART_ATTR_DEST_CPU_R5_0: + bif_output.header->image_attributes |= + HEADER_CPU_SELECT_R5_SINGLE; + break; + case PART_ATTR_DEST_CPU_R5_L: + bif_output.header->image_attributes |= + HEADER_CPU_SELECT_R5_DUAL; + break; + } + } + + return 0; +} + +/* Add .bit bitstream */ +static int bif_add_bit(struct bif_entry *bf) +{ + char *bit = read_full_file(bf->filename, NULL); + char *bitbin; + uint8_t initial_header[] = { 0x00, 0x09, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, + 0xf0, 0x0f, 0xf0, 0x00, 0x00, 0x01, 0x61 }; + uint16_t len; + uint32_t bitlen; + int i; + + if (!bit) + return -1; + + /* Skip initial header */ + if (memcmp(bit, initial_header, sizeof(initial_header))) + return -1; + + bit += sizeof(initial_header); + + /* Design name */ + len = be16_to_cpu(*(uint16_t *)bit); + bit += sizeof(uint16_t); + debug("Design: %s\n", bit); + bit += len; + + /* Device identifier */ + if (*bit != 'b') + return -1; + bit++; + len = be16_to_cpu(*(uint16_t *)bit); + bit += sizeof(uint16_t); + debug("Device: %s\n", bit); + bit += len; + + /* Date */ + if (*bit != 'c') + return -1; + bit++; + len = be16_to_cpu(*(uint16_t *)bit); + bit += sizeof(uint16_t); + debug("Date: %s\n", bit); + bit += len; + + /* Time */ + if (*bit != 'd') + return -1; + bit++; + len = be16_to_cpu(*(uint16_t *)bit); + bit += sizeof(uint16_t); + debug("Time: %s\n", bit); + bit += len; + + /* Bitstream length */ + if (*bit != 'e') + return -1; + bit++; + bitlen = be32_to_cpu(*(uint32_t *)bit); + bit += sizeof(uint32_t); + bitbin = bit; + + debug("Bitstream Length: 0x%x\n", bitlen); + for (i = 0; i < bitlen; i += sizeof(uint32_t)) { + uint32_t *bitbin32 = (uint32_t *)&bitbin[i]; + *bitbin32 = __swab32(*bitbin32); + } + + if (!bf->dest_dev) + bf->dest_dev = PART_ATTR_DEST_DEVICE_PL; + + bf->load = 0xffffffff; + bf->entry = 0; + + bf->flags |= 1ULL << BIF_FLAG_BIT_FILE; + return bif_add_part(bf, bit, bitlen); +} + +/* Add .bin bitstream */ +static int bif_add_bin(struct bif_entry *bf) +{ + size_t size; + char *bin = read_full_file(bf->filename, &size); + + if (!bf->dest_dev) + bf->dest_dev = PART_ATTR_DEST_DEVICE_PS; + + bf->flags |= 1ULL << BIF_FLAG_BIN_FILE; + return bif_add_part(bf, bin, size); +} + +/* Add elf file */ +static char *elf2flat64(char *elf, size_t *flat_size, size_t *load_addr) +{ + Elf64_Ehdr *ehdr; + Elf64_Shdr *shdr; + size_t min_addr = -1, max_addr = 0; + char *flat; + int i; + + ehdr = (void *)elf; + shdr = (void *)(elf + le64_to_cpu(ehdr->e_shoff)); + + /* Look for smallest / biggest address */ + for (i = 0; i < le64_to_cpu(ehdr->e_shnum); i++, shdr++) { + if (!shdr->sh_size || !shdr->sh_addr || + !(shdr->sh_flags & SHF_ALLOC) || + (shdr->sh_type == SHT_NOBITS)) + continue; + + if (le64_to_cpu(shdr->sh_addr) < min_addr) + min_addr = le64_to_cpu(shdr->sh_addr); + if ((le64_to_cpu(shdr->sh_addr) + le64_to_cpu(shdr->sh_size)) > + max_addr) + max_addr = le64_to_cpu(shdr->sh_addr) + + le64_to_cpu(shdr->sh_size); + } + + *load_addr = min_addr; + *flat_size = max_addr - min_addr; + flat = calloc(1, *flat_size); + if (!flat) + return NULL; + + shdr = (void *)(elf + le64_to_cpu(ehdr->e_shoff)); + for (i = 0; i < le64_to_cpu(ehdr->e_shnum); i++, shdr++) { + char *dst = flat + le64_to_cpu(shdr->sh_addr) - min_addr; + char *src = elf + le64_to_cpu(shdr->sh_offset); + + if (!shdr->sh_size || !shdr->sh_addr || + !(shdr->sh_flags & SHF_ALLOC)) + continue; + + if (shdr->sh_type != SHT_NOBITS) + memcpy(dst, src, le64_to_cpu(shdr->sh_size)); + } + + return flat; +} + +static char *elf2flat32(char *elf, size_t *flat_size, size_t *load_addr) +{ + Elf32_Ehdr *ehdr; + Elf32_Shdr *shdr; + size_t min_addr = -1, max_addr = 0; + char *flat; + int i; + + ehdr = (void *)elf; + shdr = (void *)(elf + le32_to_cpu(ehdr->e_shoff)); + + /* Look for smallest / biggest address */ + for (i = 0; i < le32_to_cpu(ehdr->e_shnum); i++, shdr++) { + if (!shdr->sh_size || !shdr->sh_addr || + !(shdr->sh_flags & SHF_ALLOC) || + (shdr->sh_type == SHT_NOBITS)) + continue; + + if (le32_to_cpu(shdr->sh_addr) < min_addr) + min_addr = le32_to_cpu(shdr->sh_addr); + if ((le32_to_cpu(shdr->sh_addr) + le32_to_cpu(shdr->sh_size)) > + max_addr) + max_addr = le32_to_cpu(shdr->sh_addr) + + le32_to_cpu(shdr->sh_size); + } + + *load_addr = min_addr; + *flat_size = max_addr - min_addr; + flat = calloc(1, *flat_size); + if (!flat) + return NULL; + + shdr = (void *)(elf + le32_to_cpu(ehdr->e_shoff)); + for (i = 0; i < le32_to_cpu(ehdr->e_shnum); i++, shdr++) { + char *dst = flat + le32_to_cpu(shdr->sh_addr) - min_addr; + char *src = elf + le32_to_cpu(shdr->sh_offset); + + if (!shdr->sh_size || !shdr->sh_addr || + !(shdr->sh_flags & SHF_ALLOC)) + continue; + + if (shdr->sh_type != SHT_NOBITS) + memcpy(dst, src, le32_to_cpu(shdr->sh_size)); + } + + return flat; +} + +static int bif_add_elf(struct bif_entry *bf) +{ + size_t size; + size_t elf_size; + char *elf; + char *flat; + size_t load_addr; + Elf32_Ehdr *ehdr32; + Elf64_Ehdr *ehdr64; + + elf = read_full_file(bf->filename, &elf_size); + if (!elf) + return -1; + + ehdr32 = (void *)elf; + ehdr64 = (void *)elf; + + switch (ehdr32->e_ident[EI_CLASS]) { + case ELFCLASS32: + flat = elf2flat32(elf, &size, &load_addr); + bf->entry = le32_to_cpu(ehdr32->e_entry); + bf->flags |= 1ULL << BIF_FLAG_AARCH32; + break; + case ELFCLASS64: + flat = elf2flat64(elf, &size, &load_addr); + bf->entry = le64_to_cpu(ehdr64->e_entry); + break; + default: + printf("Unknown ELF class: %d\n", ehdr32->e_ident[EI_CLASS]); + return -1; + } + + if (!flat) + return -1; + + bf->load = load_addr; + if (!bf->dest_dev) + bf->dest_dev = PART_ATTR_DEST_DEVICE_PS; + + bf->flags |= 1ULL << BIF_FLAG_ELF_FILE; + return bif_add_part(bf, flat, size); +} + +static const struct bif_file_type bif_file_types[] = { + { + .name = "bitstream (.bit)", + .header = 0x00090ff0, + .add = bif_add_bit, + }, + + { + .name = "ELF", + .header = 0x7f454c46, + .add = bif_add_elf, + }, + + /* Anything else is a .bin file */ + { + .name = ".bin", + .add = bif_add_bin, + }, +}; + +static int bif_fsbl_config(struct bif_entry *fsbl_config, + struct bif_entry *entries, int nr_entries) +{ + int i; + int config_set = 0; + struct { + const char *name; + uint64_t flags; + uint64_t dest_cpu; + } configs[] = { + { .name = "a5x_x64", .dest_cpu = PART_ATTR_DEST_CPU_A53_0 }, + { .name = "a53_x64", .dest_cpu = PART_ATTR_DEST_CPU_A53_0 }, + { .name = "a5x_x32", .dest_cpu = PART_ATTR_DEST_CPU_A53_0, + .flags = 1ULL << BIF_FLAG_AARCH32 }, + { .name = "a53_x32", .dest_cpu = PART_ATTR_DEST_CPU_A53_0, + .flags = 1ULL << BIF_FLAG_AARCH32 }, + { .name = "r5_single", .dest_cpu = PART_ATTR_DEST_CPU_R5_0 }, + { .name = "r5_dual", .dest_cpu = PART_ATTR_DEST_CPU_R5_L }, + }; + + /* Set target CPU of bootloader entry */ + for (i = 0; i < nr_entries; i++) { + struct bif_entry *b = &entries[i]; + const char *config_attr = fsbl_config->filename; + int j; + + if (!(b->flags & (1ULL << BIF_FLAG_BOOTLOADER))) + continue; + + for (j = 0; j < ARRAY_SIZE(configs); j++) { + if (!strncmp(config_attr, configs[j].name, + strlen(configs[j].name))) { + b->dest_cpu = configs[j].dest_cpu; + b->flags |= configs[j].flags; + config_set = 1; + } + } + + if (!config_set) { + printf("ERROR: Unsupported fsbl_config: %s\n", + config_attr); + return -1; + } + } + + if (!config_set) { + printf("ERROR: fsbl_config w/o bootloader\n"); + return -1; + } + + return 0; +} + +static const struct bif_flags *find_flag(char *str) +{ + const struct bif_flags *bf; + int i; + + for (i = 0; i < ARRAY_SIZE(bif_flags); i++) { + bf = &bif_flags[i]; + if (!strncmp(bf->name, str, strlen(bf->name))) + return bf; + } + + printf("ERROR: Flag '%s' not found\n", str); + + return NULL; +} + +static int bif_open_file(struct bif_entry *entry) +{ + int fd = open(entry->filename, O_RDONLY); + + if (fd < 0) + printf("Error opening file %s\n", entry->filename); + + return fd; +} + +static const struct bif_file_type *get_file_type(struct bif_entry *entry) +{ + int fd = bif_open_file(entry); + uint32_t header; + int i; + + if (fd < 0) + return NULL; + + if (read(fd, &header, sizeof(header)) != sizeof(header)) { + printf("Error reading file %s", entry->filename); + return NULL; + } + + close(fd); + + for (i = 0; i < ARRAY_SIZE(bif_file_types); i++) { + const struct bif_file_type *type = &bif_file_types[i]; + + if (!type->header) + return type; + if (type->header == be32_to_cpu(header)) + return type; + } + + return NULL; +} + +#define NEXT_CHAR(str, chr) ({ \ + char *_n = strchr(str, chr); \ + if (!_n) \ + goto err; \ + _n; \ +}) + +static char *skip_whitespace(char *str) +{ + while (*str == ' ' || *str == '\t') + str++; + + return str; +} + +int zynqmpbif_copy_image(int outfd, struct image_tool_params *mparams) +{ + char *bif, *bifp, *bifpn; + char *line; + struct bif_entry entries[32] = { { 0 } }; + int nr_entries = 0; + struct bif_entry *entry = entries; + size_t len; + int i; + uint32_t csum; + int bldr = -1; + + bif_init(); + + /* Read .bif input file */ + bif = read_full_file(mparams->datafile, NULL); + if (!bif) + goto err; + + /* Interpret .bif file */ + bifp = bif; + + /* A bif description starts with a { section */ + bifp = NEXT_CHAR(bifp, '{') + 1; + + /* Read every line */ + while (1) { + bifpn = NEXT_CHAR(bifp, '\n'); + + if (bifpn[-1] == '\r') + bifpn[-1] = '\0'; + + *bifpn = '\0'; + bifpn++; + line = bifp; + + line = skip_whitespace(line); + + /* Attributes? */ + if (*line == '[') { + line++; + while (1) { + const struct bif_flags *bf; + + line = skip_whitespace(line); + bf = find_flag(line); + if (!bf) + goto err; + + line += strlen(bf->name); + if (bf->parse) + line = bf->parse(line, entry); + else + entry->flags |= 1ULL << bf->flag; + + if (!line) + goto err; + + /* Go to next attribute or quit */ + if (*line == ']') { + line++; + break; + } + if (*line == ',') + line++; + } + } + + /* End of image description */ + if (*line == '}') + break; + + if (*line) { + line = skip_whitespace(line); + entry->filename = line; + nr_entries++; + entry++; + } + + /* Use next line */ + bifp = bifpn; + } + + for (i = 0; i < nr_entries; i++) { + debug("Entry flags=%#lx name=%s\n", entries[i].flags, + entries[i].filename); + } + + /* + * Some entries are actually configuration option for other ones, + * let's apply them in an intermediate step. + */ + for (i = 0; i < nr_entries; i++) { + struct bif_entry *entry = &entries[i]; + + if (entry->flags & (1ULL << BIF_FLAG_FSBL_CONFIG)) + if (bif_fsbl_config(entry, entries, nr_entries)) + goto err; + } + + /* Make sure PMUFW comes before bootloader */ + for (i = 0; i < nr_entries; i++) { + struct bif_entry *entry = &entries[i]; + + if (entry->flags & (1ULL << BIF_FLAG_BOOTLOADER)) + bldr = i; + if (entry->flags & (1ULL << BIF_FLAG_PMUFW_IMAGE)) { + if (bldr >= 0) { + struct bif_entry tmp = *entry; + + *entry = entries[bldr]; + entries[bldr] = tmp; + } + } + } + + for (i = 0; i < nr_entries; i++) { + struct bif_entry *entry = &entries[i]; + const struct bif_file_type *type; + int r; + + if (entry->flags & (1ULL << BIF_FLAG_FSBL_CONFIG)) + continue; + + type = get_file_type(entry); + if (!type) + goto err; + + debug("type=%s file=%s\n", type->name, entry->filename); + r = type->add(entry); + if (r) + goto err; + } + + /* Calculate checksums */ + csum = zynqmp_csum(&bif_output.header->width_detection, + &bif_output.header->checksum); + bif_output.header->checksum = cpu_to_le32(csum); + + if (bif_output.imgheader) { + csum = zynqmp_csum(bif_output.imgheader, + &bif_output.imgheader->checksum); + bif_output.imgheader->checksum = cpu_to_le32(csum); + } + + /* Write headers and components */ + if (lseek(outfd, 0, SEEK_SET) != 0) + goto err; + + len = bif_output.data_len; + bifp = bif_output.data; + while (len) { + int r; + + r = write(outfd, bifp, len); + if (r < 0) + goto err; + len -= r; + bifp += r; + } + + return 0; + +err: + fprintf(stderr, "Error: Failed to create image.\n"); + return -1; +} + +/* Needs to be stubbed out so we can print after creation */ +static void zynqmpbif_set_header(void *ptr, struct stat *sbuf, int ifd, + struct image_tool_params *params) +{ +} + +static struct zynqmp_header zynqmpimage_header; + +U_BOOT_IMAGE_TYPE( + zynqmpbif, + "Xilinx ZynqMP Boot Image support (bif)", + sizeof(struct zynqmp_header), + (void *)&zynqmpimage_header, + zynqmpbif_check_params, + NULL, + zynqmpimage_print_header, + zynqmpbif_set_header, + NULL, + zynqmpbif_check_image_types, + NULL, + NULL +); diff --git a/tools/u-boot-tools/zynqmpbif.o b/tools/u-boot-tools/zynqmpbif.o new file mode 100644 index 0000000000000000000000000000000000000000..6f4586e11a11d3c7821dca697294295b4705918a GIT binary patch literal 15960 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 literal 0 HcmV?d00001 diff --git a/tools/u-boot-tools/zynqmpimage.c b/tools/u-boot-tools/zynqmpimage.c new file mode 100644 index 0000000..19b2f02 --- /dev/null +++ b/tools/u-boot-tools/zynqmpimage.c @@ -0,0 +1,487 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2016 Michal Simek <michals@xilinx.com> + * Copyright (C) 2015 Nathan Rossi <nathan@nathanrossi.com> + * + * The following Boot Header format/structures and values are defined in the + * following documents: + * * ug1085 ZynqMP TRM doc v1.4 (Chapter 11, Table 11-4) + * * ug1137 ZynqMP Software Developer Guide v6.0 (Chapter 16) + * + * Expected Header Size = 0x9C0 + * Forced as 'little' endian, 32-bit words + * + * 0x 0 - Interrupt table (8 words) + * ... (Default value = 0xeafffffe) + * 0x 1f + * 0x 20 - Width detection + * * DEFAULT_WIDTHDETECTION 0xaa995566 + * 0x 24 - Image identifier + * * DEFAULT_IMAGEIDENTIFIER 0x584c4e58 + * 0x 28 - Encryption + * * 0x00000000 - None + * * 0xa5c3c5a3 - eFuse + * * 0xa5c3c5a7 - obfuscated key in eFUSE + * * 0x3a5c3c5a - bbRam + * * 0xa35c7ca5 - obfuscated key in boot header + * 0x 2C - Image load + * 0x 30 - Image offset + * 0x 34 - PFW image length + * 0x 38 - Total PFW image length + * 0x 3C - Image length + * 0x 40 - Total image length + * 0x 44 - Image attributes + * 0x 48 - Header checksum + * 0x 4c - Obfuscated key + * ... + * 0x 68 + * 0x 6c - Reserved + * 0x 70 - User defined + * ... + * 0x 9c + * 0x a0 - Secure header initialization vector + * ... + * 0x a8 + * 0x ac - Obfuscated key initialization vector + * ... + * 0x b4 + * 0x b8 - Register Initialization, 511 Address and Data word pairs + * * List is terminated with an address of 0xffffffff or + * ... * at the max number of entries + * 0x8b4 + * 0x8b8 - Reserved + * ... + * 0x9bf + * 0x9c0 - Data/Image starts here or above + */ + +#include "imagetool.h" +#include "mkimage.h" +#include "zynqmpimage.h" +#include <image.h> + +static struct zynqmp_header zynqmpimage_header; +static void *dynamic_header; +static FILE *fpmu; + +static uint32_t zynqmpimage_checksum(struct zynqmp_header *ptr) +{ + uint32_t checksum = 0; + + if (ptr == NULL) + return 0; + + checksum += le32_to_cpu(ptr->width_detection); + checksum += le32_to_cpu(ptr->image_identifier); + checksum += le32_to_cpu(ptr->encryption); + checksum += le32_to_cpu(ptr->image_load); + checksum += le32_to_cpu(ptr->image_offset); + checksum += le32_to_cpu(ptr->pfw_image_length); + checksum += le32_to_cpu(ptr->total_pfw_image_length); + checksum += le32_to_cpu(ptr->image_size); + checksum += le32_to_cpu(ptr->image_stored_size); + checksum += le32_to_cpu(ptr->image_attributes); + checksum = ~checksum; + + return cpu_to_le32(checksum); +} + +void zynqmpimage_default_header(struct zynqmp_header *ptr) +{ + int i; + + if (ptr == NULL) + return; + + ptr->width_detection = HEADER_WIDTHDETECTION; + ptr->image_attributes = HEADER_CPU_SELECT_A53_64BIT; + ptr->image_identifier = HEADER_IMAGEIDENTIFIER; + ptr->encryption = cpu_to_le32(ENCRYPTION_NONE); + + /* Setup not-supported/constant/reserved fields */ + for (i = 0; i < HEADER_INTERRUPT_VECTORS; i++) + ptr->interrupt_vectors[i] = HEADER_INTERRUPT_DEFAULT; + + for (i = 0; i < HEADER_REGINITS; i++) { + ptr->register_init[i].address = HEADER_REGINIT_NULL; + ptr->register_init[i].data = 0; + } + + /* + * Certain reserved fields are required to be set to 0, ensure they are + * set as such. + */ + ptr->pfw_image_length = 0x0; + ptr->total_pfw_image_length = 0x0; +} + +/* mkimage glue functions */ +static int zynqmpimage_verify_header(unsigned char *ptr, int image_size, + struct image_tool_params *params) +{ + struct zynqmp_header *zynqhdr = (struct zynqmp_header *)ptr; + + if (image_size < sizeof(struct zynqmp_header)) + return -1; + + if (zynqhdr->width_detection != HEADER_WIDTHDETECTION) + return -1; + if (zynqhdr->image_identifier != HEADER_IMAGEIDENTIFIER) + return -1; + + if (zynqmpimage_checksum(zynqhdr) != zynqhdr->checksum) + return -1; + + return 0; +} + +static void print_partition(const void *ptr, const struct partition_header *ph) +{ + uint32_t attr = le32_to_cpu(ph->attributes); + unsigned long len = le32_to_cpu(ph->len) * 4; + const char *part_owner; + const char *dest_devs[0x8] = { + "none", "PS", "PL", "PMU", "unknown", "unknown", "unknown", + "unknown" + }; + + switch (attr & PART_ATTR_PART_OWNER_MASK) { + case PART_ATTR_PART_OWNER_FSBL: + part_owner = "FSBL"; + break; + case PART_ATTR_PART_OWNER_UBOOT: + part_owner = "U-Boot"; + break; + default: + part_owner = "Unknown"; + break; + } + + printf("%s payload on CPU %s (%s):\n", part_owner, + dest_cpus[(attr & PART_ATTR_DEST_CPU_MASK) >> 8], + dest_devs[(attr & PART_ATTR_DEST_DEVICE_MASK) >> 4]); + + printf(" Offset : 0x%08x\n", le32_to_cpu(ph->offset) * 4); + printf(" Size : %lu (0x%lx) bytes\n", len, len); + printf(" Load : 0x%08llx", + (unsigned long long)le64_to_cpu(ph->load_address)); + if (ph->load_address != ph->entry_point) + printf(" (entry=0x%08llx)\n", + (unsigned long long)le64_to_cpu(ph->entry_point)); + else + printf("\n"); + printf(" Attributes : "); + + if (attr & PART_ATTR_VEC_LOCATION) + printf("vec "); + + if (attr & PART_ATTR_ENCRYPTED) + printf("encrypted "); + + switch (attr & PART_ATTR_CHECKSUM_MASK) { + case PART_ATTR_CHECKSUM_MD5: + printf("md5 "); + break; + case PART_ATTR_CHECKSUM_SHA2: + printf("sha2 "); + break; + case PART_ATTR_CHECKSUM_SHA3: + printf("sha3 "); + break; + } + + if (attr & PART_ATTR_BIG_ENDIAN) + printf("BigEndian "); + + if (attr & PART_ATTR_RSA_SIG) + printf("RSA "); + + if (attr & PART_ATTR_A53_EXEC_AARCH32) + printf("AArch32 "); + + if (attr & PART_ATTR_TARGET_EL_MASK) + printf("EL%d ", (attr & PART_ATTR_TARGET_EL_MASK) >> 1); + + if (attr & PART_ATTR_TZ_SECURE) + printf("secure "); + printf("\n"); + + printf(" Checksum : 0x%08x\n", le32_to_cpu(ph->checksum)); +} + +void zynqmpimage_print_header(const void *ptr) +{ + struct zynqmp_header *zynqhdr = (struct zynqmp_header *)ptr; + int i; + + printf("Image Type : Xilinx ZynqMP Boot Image support\n"); + printf("Image Offset : 0x%08x\n", le32_to_cpu(zynqhdr->image_offset)); + printf("Image Size : %lu bytes (%lu bytes packed)\n", + (unsigned long)le32_to_cpu(zynqhdr->image_size), + (unsigned long)le32_to_cpu(zynqhdr->image_stored_size)); + + if (zynqhdr->pfw_image_length) + printf("PMUFW Size : %lu bytes (%lu bytes packed)\n", + (unsigned long)le32_to_cpu(zynqhdr->pfw_image_length), + (unsigned long)le32_to_cpu( + zynqhdr->total_pfw_image_length)); + + printf("Image Load : 0x%08x\n", le32_to_cpu(zynqhdr->image_load)); + printf("Checksum : 0x%08x\n", le32_to_cpu(zynqhdr->checksum)); + + for (i = 0; i < HEADER_INTERRUPT_VECTORS; i++) { + if (zynqhdr->interrupt_vectors[i] == HEADER_INTERRUPT_DEFAULT) + continue; + + printf("Modified Interrupt Vector Address [%d]: 0x%08x\n", i, + le32_to_cpu(zynqhdr->interrupt_vectors[i])); + } + + for (i = 0; i < HEADER_REGINITS; i++) { + if (zynqhdr->register_init[i].address == HEADER_REGINIT_NULL) + break; + + if (i == 0) + printf("Custom Register Initialization:\n"); + + printf(" @ 0x%08x -> 0x%08x\n", + le32_to_cpu(zynqhdr->register_init[i].address), + le32_to_cpu(zynqhdr->register_init[i].data)); + } + + if (zynqhdr->image_header_table_offset) { + struct image_header_table *iht = (void *)ptr + + zynqhdr->image_header_table_offset; + struct partition_header *ph; + uint32_t ph_offset; + uint32_t next; + int i; + + ph_offset = le32_to_cpu(iht->partition_header_offset) * 4; + ph = (void *)ptr + ph_offset; + for (i = 0; i < le32_to_cpu(iht->nr_parts); i++) { + next = le32_to_cpu(ph->next_partition_offset) * 4; + + /* Partition 0 is the base image itself */ + if (i) + print_partition(ptr, ph); + + ph = (void *)ptr + next; + } + } + + free(dynamic_header); +} + +static int zynqmpimage_check_params(struct image_tool_params *params) +{ + if (!params) + return 0; + + if (params->addr != 0x0) { + fprintf(stderr, "Error: Load Address cannot be specified.\n"); + return -1; + } + + /* + * If the entry point is specified ensure it is 64 byte aligned. + */ + if (params->eflag && (params->ep % 64 != 0)) { + fprintf(stderr, + "Error: Entry Point must be aligned to a 64-byte boundary.\n"); + return -1; + } + + return !(params->lflag || params->dflag); +} + +static int zynqmpimage_check_image_types(uint8_t type) +{ + if (type == IH_TYPE_ZYNQMPIMAGE) + return EXIT_SUCCESS; + return EXIT_FAILURE; +} + +static uint32_t fsize(FILE *fp) +{ + int size, ret, origin; + + origin = ftell(fp); + if (origin < 0) { + fprintf(stderr, "Incorrect file size\n"); + fclose(fp); + exit(2); + } + + ret = fseek(fp, 0L, SEEK_END); + if (ret) { + fprintf(stderr, "Incorrect file SEEK_END\n"); + fclose(fp); + exit(3); + } + + size = ftell(fp); + if (size < 0) { + fprintf(stderr, "Incorrect file size\n"); + fclose(fp); + exit(4); + } + + /* going back */ + ret = fseek(fp, origin, SEEK_SET); + if (ret) { + fprintf(stderr, "Incorrect file SEEK_SET to %d\n", origin); + fclose(fp); + exit(3); + } + + return size; +} + +static void zynqmpimage_pmufw(struct zynqmp_header *zynqhdr, + const char *filename) +{ + uint32_t size; + + /* Setup PMU fw size */ + zynqhdr->pfw_image_length = fsize(fpmu); + zynqhdr->total_pfw_image_length = zynqhdr->pfw_image_length; + + zynqhdr->image_size -= zynqhdr->pfw_image_length; + zynqhdr->image_stored_size -= zynqhdr->total_pfw_image_length; + + /* Read the whole PMUFW to the header */ + size = fread(&zynqhdr->__reserved4[66], 1, + zynqhdr->pfw_image_length, fpmu); + if (size != zynqhdr->pfw_image_length) { + fprintf(stderr, "Cannot read PMUFW file: %s\n", filename); + fclose(fpmu); + exit(1); + } + + fclose(fpmu); +} + +static void zynqmpimage_parse_initparams(struct zynqmp_header *zynqhdr, + const char *filename) +{ + FILE *fp; + struct zynqmp_reginit reginit; + unsigned int reg_count = 0; + int r, err; + struct stat path_stat; + + /* Expect a table of register-value pairs, e.g. "0x12345678 0x4321" */ + fp = fopen(filename, "r"); + if (!fp) { + fprintf(stderr, "Cannot open initparams file: %s\n", filename); + exit(1); + } + + err = fstat(fileno(fp), &path_stat); + if (err) { + fclose(fp); + return; + } + + if (!S_ISREG(path_stat.st_mode)) { + fclose(fp); + return; + } + + do { + r = fscanf(fp, "%x %x", ®init.address, ®init.data); + if (r == 2) { + zynqhdr->register_init[reg_count] = reginit; + ++reg_count; + } + r = fscanf(fp, "%*[^\n]\n"); /* Skip to next line */ + } while ((r != EOF) && (reg_count < HEADER_REGINITS)); + fclose(fp); +} + +static void zynqmpimage_set_header(void *ptr, struct stat *sbuf, int ifd, + struct image_tool_params *params) +{ + struct zynqmp_header *zynqhdr = (struct zynqmp_header *)ptr; + zynqmpimage_default_header(zynqhdr); + + /* place image directly after header */ + zynqhdr->image_offset = + cpu_to_le32((uint32_t)sizeof(struct zynqmp_header)); + zynqhdr->image_size = cpu_to_le32(params->file_size - + sizeof(struct zynqmp_header)); + zynqhdr->image_stored_size = zynqhdr->image_size; + zynqhdr->image_load = 0xfffc0000; + if (params->eflag) + zynqhdr->image_load = cpu_to_le32((uint32_t)params->ep); + + /* PMUFW */ + if (fpmu) + zynqmpimage_pmufw(zynqhdr, params->imagename); + + /* User can pass in text file with init list */ + if (strlen(params->imagename2)) + zynqmpimage_parse_initparams(zynqhdr, params->imagename2); + + zynqhdr->checksum = zynqmpimage_checksum(zynqhdr); +} + +static int zynqmpimage_vrec_header(struct image_tool_params *params, + struct image_type_params *tparams) +{ + struct stat path_stat; + char *filename = params->imagename; + int err; + + /* Handle static case without PMUFW */ + tparams->header_size = sizeof(struct zynqmp_header); + tparams->hdr = (void *)&zynqmpimage_header; + + /* PMUFW name is passed via params->imagename */ + if (strlen(filename) == 0) + return EXIT_SUCCESS; + + fpmu = fopen(filename, "r"); + if (!fpmu) { + fprintf(stderr, "Cannot open PMUFW file: %s\n", filename); + return EXIT_FAILURE; + } + + err = fstat(fileno(fpmu), &path_stat); + if (err) { + fclose(fpmu); + fpmu = NULL; + return EXIT_FAILURE; + } + + if (!S_ISREG(path_stat.st_mode)) { + fclose(fpmu); + fpmu = NULL; + return EXIT_FAILURE; + } + + /* Increase header size by PMUFW file size */ + tparams->header_size += fsize(fpmu); + + /* Allocate buffer with space for PMUFW */ + dynamic_header = calloc(1, tparams->header_size); + tparams->hdr = dynamic_header; + + return EXIT_SUCCESS; +} + +U_BOOT_IMAGE_TYPE( + zynqmpimage, + "Xilinx ZynqMP Boot Image support", + sizeof(struct zynqmp_header), + (void *)&zynqmpimage_header, + zynqmpimage_check_params, + zynqmpimage_verify_header, + zynqmpimage_print_header, + zynqmpimage_set_header, + NULL, + zynqmpimage_check_image_types, + NULL, + zynqmpimage_vrec_header +); diff --git a/tools/u-boot-tools/zynqmpimage.h b/tools/u-boot-tools/zynqmpimage.h new file mode 100644 index 0000000..a1db819 --- /dev/null +++ b/tools/u-boot-tools/zynqmpimage.h @@ -0,0 +1,138 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (C) 2016 Michal Simek <michals@xilinx.com> + * Copyright (C) 2015 Nathan Rossi <nathan@nathanrossi.com> + * + * The following Boot Header format/structures and values are defined in the + * following documents: + * * ug1085 ZynqMP TRM doc v1.4 (Chapter 11, Table 11-4) + * * ug1137 ZynqMP Software Developer Guide v6.0 (Chapter 16) + */ + +#ifndef _ZYNQMPIMAGE_H_ +#define _ZYNQMPIMAGE_H_ + +#include <stdint.h> + +#define HEADER_INTERRUPT_DEFAULT (cpu_to_le32(0xeafffffe)) +#define HEADER_REGINIT_NULL (cpu_to_le32(0xffffffff)) +#define HEADER_WIDTHDETECTION (cpu_to_le32(0xaa995566)) +#define HEADER_IMAGEIDENTIFIER (cpu_to_le32(0x584c4e58)) +#define HEADER_CPU_SELECT_MASK (0x3 << 10) +#define HEADER_CPU_SELECT_R5_SINGLE (0x0 << 10) +#define HEADER_CPU_SELECT_A53_32BIT (0x1 << 10) +#define HEADER_CPU_SELECT_A53_64BIT (0x2 << 10) +#define HEADER_CPU_SELECT_R5_DUAL (0x3 << 10) + +enum { + ENCRYPTION_EFUSE = 0xa5c3c5a3, + ENCRYPTION_OEFUSE = 0xa5c3c5a7, + ENCRYPTION_BBRAM = 0x3a5c3c5a, + ENCRYPTION_OBBRAM = 0xa35c7ca5, + ENCRYPTION_NONE = 0x0, +}; + +struct zynqmp_reginit { + uint32_t address; + uint32_t data; +}; + +#define HEADER_INTERRUPT_VECTORS 8 +#define HEADER_REGINITS 256 + +struct image_header_table { + uint32_t version; /* 0x00 */ + uint32_t nr_parts; /* 0x04 */ + uint32_t partition_header_offset; /* 0x08, divided by 4 */ + uint32_t image_header_offset; /* 0x0c, divided by 4 */ + uint32_t auth_certificate_offset; /* 0x10 */ + uint32_t boot_device; /* 0x14 */ + uint32_t __reserved1[9]; /* 0x18 - 0x38 */ + uint32_t checksum; /* 0x3c */ +}; + +#define PART_ATTR_VEC_LOCATION 0x800000 +#define PART_ATTR_BS_BLOCK_SIZE_MASK 0x700000 +#define PART_ATTR_BS_BLOCK_SIZE_DEFAULT 0x000000 +#define PART_ATTR_BS_BLOCK_SIZE_8MB 0x400000 +#define PART_ATTR_BIG_ENDIAN 0x040000 +#define PART_ATTR_PART_OWNER_MASK 0x030000 +#define PART_ATTR_PART_OWNER_FSBL 0x000000 +#define PART_ATTR_PART_OWNER_UBOOT 0x010000 +#define PART_ATTR_RSA_SIG 0x008000 +#define PART_ATTR_CHECKSUM_MASK 0x007000 +#define PART_ATTR_CHECKSUM_NONE 0x000000 +#define PART_ATTR_CHECKSUM_MD5 0x001000 +#define PART_ATTR_CHECKSUM_SHA2 0x002000 +#define PART_ATTR_CHECKSUM_SHA3 0x003000 +#define PART_ATTR_DEST_CPU_SHIFT 8 +#define PART_ATTR_DEST_CPU_MASK 0x000f00 +#define PART_ATTR_DEST_CPU_NONE 0x000000 +#define PART_ATTR_DEST_CPU_A53_0 0x000100 +#define PART_ATTR_DEST_CPU_A53_1 0x000200 +#define PART_ATTR_DEST_CPU_A53_2 0x000300 +#define PART_ATTR_DEST_CPU_A53_3 0x000400 +#define PART_ATTR_DEST_CPU_R5_0 0x000500 +#define PART_ATTR_DEST_CPU_R5_1 0x000600 +#define PART_ATTR_DEST_CPU_R5_L 0x000700 +#define PART_ATTR_DEST_CPU_PMU 0x000800 +#define PART_ATTR_ENCRYPTED 0x000080 +#define PART_ATTR_DEST_DEVICE_SHIFT 4 +#define PART_ATTR_DEST_DEVICE_MASK 0x000070 +#define PART_ATTR_DEST_DEVICE_NONE 0x000000 +#define PART_ATTR_DEST_DEVICE_PS 0x000010 +#define PART_ATTR_DEST_DEVICE_PL 0x000020 +#define PART_ATTR_DEST_DEVICE_PMU 0x000030 +#define PART_ATTR_DEST_DEVICE_XIP 0x000040 +#define PART_ATTR_A53_EXEC_AARCH32 0x000008 +#define PART_ATTR_TARGET_EL_SHIFT 1 +#define PART_ATTR_TARGET_EL_MASK 0x000006 +#define PART_ATTR_TZ_SECURE 0x000001 + +static const char *dest_cpus[0x10] = { + "none", "a5x-0", "a5x-1", "a5x-2", "a5x-3", "r5-0", "r5-1", + "r5-lockstep", "pmu", "unknown", "unknown", "unknown", "unknown", + "unknown", "unknown", "unknown" +}; + +struct partition_header { + uint32_t len_enc; /* 0x00, divided by 4 */ + uint32_t len_unenc; /* 0x04, divided by 4 */ + uint32_t len; /* 0x08, divided by 4 */ + uint32_t next_partition_offset; /* 0x0c */ + uint64_t entry_point; /* 0x10 */ + uint64_t load_address; /* 0x18 */ + uint32_t offset; /* 0x20, divided by 4 */ + uint32_t attributes; /* 0x24 */ + uint32_t __reserved1; /* 0x28 */ + uint32_t checksum_offset; /* 0x2c, divided by 4 */ + uint32_t __reserved2; /* 0x30 */ + uint32_t auth_certificate_offset; /* 0x34 */ + uint32_t __reserved3; /* 0x38 */ + uint32_t checksum; /* 0x3c */ +}; + +struct zynqmp_header { + uint32_t interrupt_vectors[HEADER_INTERRUPT_VECTORS]; /* 0x0 */ + uint32_t width_detection; /* 0x20 */ + uint32_t image_identifier; /* 0x24 */ + uint32_t encryption; /* 0x28 */ + uint32_t image_load; /* 0x2c */ + uint32_t image_offset; /* 0x30 */ + uint32_t pfw_image_length; /* 0x34 */ + uint32_t total_pfw_image_length; /* 0x38 */ + uint32_t image_size; /* 0x3c */ + uint32_t image_stored_size; /* 0x40 */ + uint32_t image_attributes; /* 0x44 */ + uint32_t checksum; /* 0x48 */ + uint32_t __reserved1[19]; /* 0x4c */ + uint32_t image_header_table_offset; /* 0x98 */ + uint32_t __reserved2[7]; /* 0x9c */ + struct zynqmp_reginit register_init[HEADER_REGINITS]; /* 0xb8 */ + uint32_t __reserved4[66]; /* 0x9c0 */ +}; + +void zynqmpimage_default_header(struct zynqmp_header *ptr); +void zynqmpimage_print_header(const void *ptr); + +#endif /* _ZYNQMPIMAGE_H_ */ diff --git a/tools/u-boot-tools/zynqmpimage.o b/tools/u-boot-tools/zynqmpimage.o new file mode 100644 index 0000000000000000000000000000000000000000..155c339aceef16a514c388355f6b9f955c5e93f8 GIT binary patch literal 12824 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 ZFbuK=oBjk)#%5q(0Il7IsRhyK`T?i^nWg{$ literal 0 HcmV?d00001 -- GitLab