From 60f18e55542f2086b3aee98fc8687367d6bd65e8 Mon Sep 17 00:00:00 2001 From: yosiokat Date: Thu, 6 Sep 2007 05:28:51 +0000 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E8=A6=8F=E8=BF=BD=E5=8A=A0=E3=80=82?= =?UTF-8?q?=EF=BC=88=E3=81=BE=E3=81=A0=E3=83=93=E3=83=AB=E3=83=89=E3=81=A7?= =?UTF-8?q?=E3=81=8D=E3=81=AA=E3=81=84=E3=80=82=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit git-svn-id: file:///Users/lillianskinner/Downloads/platinum/twl/TwlIPL/trunk@1 b08762b0-b915-fc4b-9d8c-17b2551a87ff --- build/buildtools/commondefs | 249 ++++ build/buildtools/modulerules | 80 ++ build/tools/Makefile | 34 + build/tools/acsign/Makefile | 61 + build/tools/acsign/acsign.c | 334 +++++ build/tools/acsign/acsign_gcd.c | 74 ++ build/tools/acsign/acsign_nand.c | 75 ++ build/tools/acsign/acsign_nor.c | 75 ++ build/tools/acsign/aes.h | 139 ++ build/tools/acsign/aes2.c | 133 ++ build/tools/acsign/aes2.h | 21 + build/tools/acsign/aes_e.c | 115 ++ build/tools/acsign/aes_e.h | 284 +++++ build/tools/acsign/aes_e_ecb.c | 48 + build/tools/acsign/aes_sbox.h | 59 + build/tools/acsign/aes_skey.c | 143 +++ build/tools/acsign/bn_add.c | 304 +++++ build/tools/acsign/bn_asm.c | 509 ++++++++ build/tools/acsign/bn_comba.c | 434 +++++++ build/tools/acsign/bn_div.c | 365 ++++++ build/tools/acsign/bn_ex_str.c | 437 +++++++ build/tools/acsign/bn_exp.c | 156 +++ build/tools/acsign/bn_fm_w.c | 63 + build/tools/acsign/bn_gcd.c | 229 ++++ build/tools/acsign/bn_lib.c | 1079 ++++++++++++++++ build/tools/acsign/bn_lsh.c | 112 ++ build/tools/acsign/bn_m_exp.c | 283 +++++ build/tools/acsign/bn_me.c | 251 ++++ build/tools/acsign/bn_mont.c | 231 ++++ build/tools/acsign/bn_ms_w.c | 121 ++ build/tools/acsign/bn_mul.c | 779 ++++++++++++ build/tools/acsign/bn_r_exp.c | 134 ++ build/tools/acsign/bn_rec.c | 232 ++++ build/tools/acsign/bn_recp.c | 202 +++ build/tools/acsign/bn_rsh.c | 123 ++ build/tools/acsign/bn_sqr.c | 273 ++++ build/tools/acsign/bn_wdiv.c | 100 ++ build/tools/acsign/bn_word.c | 222 ++++ build/tools/acsign/include/acsign.h | 49 + build/tools/acsign/include/acsign_gcd.h | 30 + build/tools/acsign/include/acsign_nand.h | 30 + build/tools/acsign/include/acsign_nor.h | 30 + build/tools/acsign/include/bn.h | 1120 +++++++++++++++++ build/tools/acsign/include/bn_lcl.h | 324 +++++ build/tools/acsign/include/bn_thx.h | 301 +++++ build/tools/acsign/include/r_error.h | 210 ++++ build/tools/acsign/include/r_stdiag.h | 82 ++ build/tools/acsign/include/r_types.h | 241 ++++ build/tools/acsign/include/sha.h | 89 ++ build/tools/acsign/include/sha1.h | 73 ++ build/tools/acsign/include/sha_locl.h | 274 ++++ build/tools/acsign/sha1.c | 453 +++++++ build/tools/acsign/sha1dgst.c | 770 ++++++++++++ build/tools/makegcdfirm/Makefile | 148 +++ build/tools/makegcdfirm/compress.c | 292 +++++ build/tools/makegcdfirm/compress.h | 37 + build/tools/makegcdfirm/defval.c | 315 +++++ build/tools/makegcdfirm/defval.h | 38 + build/tools/makegcdfirm/elf.h | 431 +++++++ build/tools/makegcdfirm/format_nlist.h | 53 + build/tools/makegcdfirm/format_rom.h | 58 + build/tools/makegcdfirm/format_sign.h | 31 + .../gcdfirm_header_twlj.template.sbin | Bin 0 -> 16384 bytes build/tools/makegcdfirm/makegcdfirm.c | 105 ++ build/tools/makegcdfirm/makegcdfirm.h | 23 + build/tools/makegcdfirm/misc.c | 627 +++++++++ build/tools/makegcdfirm/misc.h | 120 ++ build/tools/makegcdfirm/out_gcdfirm.c | 994 +++++++++++++++ build/tools/makegcdfirm/path.c | 931 ++++++++++++++ build/tools/makegcdfirm/path.h | 93 ++ build/tools/makegcdfirm/test/Makefile | 55 + .../makegcdfirm/test/nandfirm_print.nand | Bin 0 -> 50688 bytes .../tools/makegcdfirm/test/norfirm_print.nor | Bin 0 -> 30896 bytes build/tools/makegcdfirm/test/rsa_private.der | Bin 0 -> 607 bytes build/tools/makegcdfirm/test/test.gcdsf | 25 + .../makegcdfirm/test/twl_gcdfirm7_print.axf | Bin 0 -> 175676 bytes .../makegcdfirm/test/twl_gcdfirm7_print.sbin | Bin 0 -> 12772 bytes .../makegcdfirm/test/twl_gcdfirm9_print.axf | Bin 0 -> 288636 bytes .../makegcdfirm/test/twl_gcdfirm9_print.sbin | Bin 0 -> 33184 bytes .../tools/makegcdfirm/test/wram_rbin/Makefile | 47 + .../makegcdfirm/test/wram_rbin/wram_regs.c | 88 ++ build/tools/makegcdfirm/wram_regs.c | 91 ++ build/tools/makenandfirm/Makefile | 148 +++ build/tools/makenandfirm/compress.c | 292 +++++ build/tools/makenandfirm/compress.h | 37 + build/tools/makenandfirm/defval.c | 315 +++++ build/tools/makenandfirm/defval.h | 38 + build/tools/makenandfirm/elf.h | 431 +++++++ build/tools/makenandfirm/format_nlist.h | 53 + build/tools/makenandfirm/format_rom.h | 50 + build/tools/makenandfirm/format_sign.h | 31 + build/tools/makenandfirm/makenandfirm.c | 105 ++ build/tools/makenandfirm/makenandfirm.h | 23 + build/tools/makenandfirm/misc.c | 627 +++++++++ build/tools/makenandfirm/misc.h | 120 ++ build/tools/makenandfirm/out_nandfirm.c | 979 ++++++++++++++ build/tools/makenandfirm/path.c | 931 ++++++++++++++ build/tools/makenandfirm/path.h | 93 ++ build/tools/makenandfirm/test/Makefile | 55 + build/tools/makenandfirm/test/rsa_private.der | Bin 0 -> 607 bytes build/tools/makenandfirm/test/test.nandsf | 28 + .../makenandfirm/test/twl_nandfirm7_print.axf | Bin 0 -> 175676 bytes .../test/twl_nandfirm7_print.sbin | Bin 0 -> 12772 bytes .../makenandfirm/test/twl_nandfirm9_print.axf | Bin 0 -> 288636 bytes .../test/twl_nandfirm9_print.sbin | Bin 0 -> 33184 bytes .../makenandfirm/test/wram_rbin/Makefile | 47 + .../makenandfirm/test/wram_rbin/wram_regs.c | 88 ++ build/tools/makenandfirm/wram_regs.c | 91 ++ build/tools/makenorfirm/Makefile | 148 +++ build/tools/makenorfirm/compress.c | 292 +++++ build/tools/makenorfirm/compress.h | 37 + build/tools/makenorfirm/defval.c | 315 +++++ build/tools/makenorfirm/defval.h | 38 + build/tools/makenorfirm/elf.h | 431 +++++++ build/tools/makenorfirm/format_nlist.h | 53 + build/tools/makenorfirm/format_rom.h | 50 + build/tools/makenorfirm/format_sign.h | 31 + build/tools/makenorfirm/makenorfirm.c | 105 ++ build/tools/makenorfirm/makenorfirm.h | 23 + build/tools/makenorfirm/misc.c | 627 +++++++++ build/tools/makenorfirm/misc.h | 121 ++ build/tools/makenorfirm/out_norfirm.c | 984 +++++++++++++++ build/tools/makenorfirm/path.c | 931 ++++++++++++++ build/tools/makenorfirm/path.h | 93 ++ build/tools/makenorfirm/test/Makefile | 57 + build/tools/makenorfirm/test/rsa_private.der | Bin 0 -> 608 bytes build/tools/makenorfirm/test/test.norsf | 30 + .../makenorfirm/test/twl_norfirm7_print.axf | Bin 0 -> 175676 bytes .../makenorfirm/test/twl_norfirm7_print.sbin | Bin 0 -> 12772 bytes .../makenorfirm/test/twl_norfirm9_print.axf | Bin 0 -> 288636 bytes .../makenorfirm/test/twl_norfirm9_print.sbin | Bin 0 -> 33184 bytes .../tools/makenorfirm/test/wram_rbin/Makefile | 47 + .../makenorfirm/test/wram_rbin/wram_regs.c | 88 ++ build/tools/makenorfirm/wram_regs.c | 91 ++ include/firm.h | 29 + include/firm/acsign.h | 32 + include/firm/aes.h | 27 + include/firm/aes/ARM7/aes_ids.h | 41 + include/firm/aes/ARM7/aes_init.h | 34 + include/firm/devices/firm_sdmc/ARM7/sdmc.h | 170 +++ include/firm/format/firm_common.h | 66 + include/firm/format/from_brom.h | 92 ++ include/firm/format/gcdfirm.h | 113 ++ include/firm/format/nandfirm.h | 130 ++ include/firm/format/norfirm.h | 107 ++ include/firm/format/sign.h | 94 ++ include/firm/format/wram_regs.h | 52 + include/firm/gcd.h | 25 + include/firm/gcd/blowfish.h | 41 + include/firm/gcd/gcd.h | 773 ++++++++++++ include/firm/gcd/gcd_misc.h | 468 +++++++ include/firm/hw/ARM7/mmap_firm.h | 44 + include/firm/hw/ARM9/mmap_firm.h | 44 + include/firm/memorymap.h | 28 + include/firm/mi.h | 25 + include/firm/mi/mainMemory.h | 79 ++ include/firm/misc.h | 32 + include/firm/nvram.h | 42 + include/firm/os.h | 37 + include/firm/os/common/boot.h | 74 ++ include/firm/os/common/init.h | 42 + include/firm/os/common/systemCall.h | 94 ++ include/firm/os/common/tick_brom.h | 50 + include/firm/pm.h | 29 + .../specfiles/ARM7-BB-GCDFIRM.lcf.template | 494 ++++++++ include/firm/specfiles/ARM7-BB-GCDFIRM.lsf | 27 + .../specfiles/ARM7-BB-NORFIRM.lcf.template | 494 ++++++++ include/firm/specfiles/ARM7-BB-NORFIRM.lsf | 27 + .../specfiles/ARM7-TS-GCDFIRM.lcf.template | 494 ++++++++ include/firm/specfiles/ARM7-TS-GCDFIRM.lsf | 27 + .../specfiles/ARM7-TS-NORFIRM.lcf.template | 494 ++++++++ include/firm/specfiles/ARM7-TS-NORFIRM.lsf | 27 + .../specfiles/ARM9-BB-GCDFIRM.lcf.template | 589 +++++++++ include/firm/specfiles/ARM9-BB-GCDFIRM.lsf | 41 + .../specfiles/ARM9-BB-NORFIRM.lcf.template | 589 +++++++++ include/firm/specfiles/ARM9-BB-NORFIRM.lsf | 41 + .../specfiles/ARM9-TS-GCDFIRM.lcf.template | 589 +++++++++ include/firm/specfiles/ARM9-TS-GCDFIRM.lsf | 41 + .../specfiles/ARM9-TS-NORFIRM.lcf.template | 589 +++++++++ include/firm/specfiles/ARM9-TS-NORFIRM.lsf | 41 + include/twl/os/common/systemCall.h | 92 ++ tools/bin/makenorfirm.exe | Bin 0 -> 105611 bytes tools/openssl/openssl.exe | Bin 0 -> 286720 bytes 183 files changed, 34025 insertions(+) create mode 100644 build/buildtools/commondefs create mode 100644 build/buildtools/modulerules create mode 100644 build/tools/Makefile create mode 100644 build/tools/acsign/Makefile create mode 100644 build/tools/acsign/acsign.c create mode 100644 build/tools/acsign/acsign_gcd.c create mode 100644 build/tools/acsign/acsign_nand.c create mode 100644 build/tools/acsign/acsign_nor.c create mode 100644 build/tools/acsign/aes.h create mode 100644 build/tools/acsign/aes2.c create mode 100644 build/tools/acsign/aes2.h create mode 100644 build/tools/acsign/aes_e.c create mode 100644 build/tools/acsign/aes_e.h create mode 100644 build/tools/acsign/aes_e_ecb.c create mode 100644 build/tools/acsign/aes_sbox.h create mode 100644 build/tools/acsign/aes_skey.c create mode 100644 build/tools/acsign/bn_add.c create mode 100644 build/tools/acsign/bn_asm.c create mode 100644 build/tools/acsign/bn_comba.c create mode 100644 build/tools/acsign/bn_div.c create mode 100644 build/tools/acsign/bn_ex_str.c create mode 100644 build/tools/acsign/bn_exp.c create mode 100644 build/tools/acsign/bn_fm_w.c create mode 100644 build/tools/acsign/bn_gcd.c create mode 100644 build/tools/acsign/bn_lib.c create mode 100644 build/tools/acsign/bn_lsh.c create mode 100644 build/tools/acsign/bn_m_exp.c create mode 100644 build/tools/acsign/bn_me.c create mode 100644 build/tools/acsign/bn_mont.c create mode 100644 build/tools/acsign/bn_ms_w.c create mode 100644 build/tools/acsign/bn_mul.c create mode 100644 build/tools/acsign/bn_r_exp.c create mode 100644 build/tools/acsign/bn_rec.c create mode 100644 build/tools/acsign/bn_recp.c create mode 100644 build/tools/acsign/bn_rsh.c create mode 100644 build/tools/acsign/bn_sqr.c create mode 100644 build/tools/acsign/bn_wdiv.c create mode 100644 build/tools/acsign/bn_word.c create mode 100644 build/tools/acsign/include/acsign.h create mode 100644 build/tools/acsign/include/acsign_gcd.h create mode 100644 build/tools/acsign/include/acsign_nand.h create mode 100644 build/tools/acsign/include/acsign_nor.h create mode 100644 build/tools/acsign/include/bn.h create mode 100644 build/tools/acsign/include/bn_lcl.h create mode 100644 build/tools/acsign/include/bn_thx.h create mode 100644 build/tools/acsign/include/r_error.h create mode 100644 build/tools/acsign/include/r_stdiag.h create mode 100644 build/tools/acsign/include/r_types.h create mode 100644 build/tools/acsign/include/sha.h create mode 100644 build/tools/acsign/include/sha1.h create mode 100644 build/tools/acsign/include/sha_locl.h create mode 100644 build/tools/acsign/sha1.c create mode 100644 build/tools/acsign/sha1dgst.c create mode 100644 build/tools/makegcdfirm/Makefile create mode 100644 build/tools/makegcdfirm/compress.c create mode 100644 build/tools/makegcdfirm/compress.h create mode 100644 build/tools/makegcdfirm/defval.c create mode 100644 build/tools/makegcdfirm/defval.h create mode 100644 build/tools/makegcdfirm/elf.h create mode 100644 build/tools/makegcdfirm/format_nlist.h create mode 100644 build/tools/makegcdfirm/format_rom.h create mode 100644 build/tools/makegcdfirm/format_sign.h create mode 100644 build/tools/makegcdfirm/gcdfirm_header_twlj.template.sbin create mode 100644 build/tools/makegcdfirm/makegcdfirm.c create mode 100644 build/tools/makegcdfirm/makegcdfirm.h create mode 100644 build/tools/makegcdfirm/misc.c create mode 100644 build/tools/makegcdfirm/misc.h create mode 100644 build/tools/makegcdfirm/out_gcdfirm.c create mode 100644 build/tools/makegcdfirm/path.c create mode 100644 build/tools/makegcdfirm/path.h create mode 100644 build/tools/makegcdfirm/test/Makefile create mode 100644 build/tools/makegcdfirm/test/nandfirm_print.nand create mode 100644 build/tools/makegcdfirm/test/norfirm_print.nor create mode 100644 build/tools/makegcdfirm/test/rsa_private.der create mode 100644 build/tools/makegcdfirm/test/test.gcdsf create mode 100644 build/tools/makegcdfirm/test/twl_gcdfirm7_print.axf create mode 100644 build/tools/makegcdfirm/test/twl_gcdfirm7_print.sbin create mode 100644 build/tools/makegcdfirm/test/twl_gcdfirm9_print.axf create mode 100644 build/tools/makegcdfirm/test/twl_gcdfirm9_print.sbin create mode 100644 build/tools/makegcdfirm/test/wram_rbin/Makefile create mode 100644 build/tools/makegcdfirm/test/wram_rbin/wram_regs.c create mode 100644 build/tools/makegcdfirm/wram_regs.c create mode 100644 build/tools/makenandfirm/Makefile create mode 100644 build/tools/makenandfirm/compress.c create mode 100644 build/tools/makenandfirm/compress.h create mode 100644 build/tools/makenandfirm/defval.c create mode 100644 build/tools/makenandfirm/defval.h create mode 100644 build/tools/makenandfirm/elf.h create mode 100644 build/tools/makenandfirm/format_nlist.h create mode 100644 build/tools/makenandfirm/format_rom.h create mode 100644 build/tools/makenandfirm/format_sign.h create mode 100644 build/tools/makenandfirm/makenandfirm.c create mode 100644 build/tools/makenandfirm/makenandfirm.h create mode 100644 build/tools/makenandfirm/misc.c create mode 100644 build/tools/makenandfirm/misc.h create mode 100644 build/tools/makenandfirm/out_nandfirm.c create mode 100644 build/tools/makenandfirm/path.c create mode 100644 build/tools/makenandfirm/path.h create mode 100644 build/tools/makenandfirm/test/Makefile create mode 100644 build/tools/makenandfirm/test/rsa_private.der create mode 100644 build/tools/makenandfirm/test/test.nandsf create mode 100644 build/tools/makenandfirm/test/twl_nandfirm7_print.axf create mode 100644 build/tools/makenandfirm/test/twl_nandfirm7_print.sbin create mode 100644 build/tools/makenandfirm/test/twl_nandfirm9_print.axf create mode 100644 build/tools/makenandfirm/test/twl_nandfirm9_print.sbin create mode 100644 build/tools/makenandfirm/test/wram_rbin/Makefile create mode 100644 build/tools/makenandfirm/test/wram_rbin/wram_regs.c create mode 100644 build/tools/makenandfirm/wram_regs.c create mode 100644 build/tools/makenorfirm/Makefile create mode 100644 build/tools/makenorfirm/compress.c create mode 100644 build/tools/makenorfirm/compress.h create mode 100644 build/tools/makenorfirm/defval.c create mode 100644 build/tools/makenorfirm/defval.h create mode 100644 build/tools/makenorfirm/elf.h create mode 100644 build/tools/makenorfirm/format_nlist.h create mode 100644 build/tools/makenorfirm/format_rom.h create mode 100644 build/tools/makenorfirm/format_sign.h create mode 100644 build/tools/makenorfirm/makenorfirm.c create mode 100644 build/tools/makenorfirm/makenorfirm.h create mode 100644 build/tools/makenorfirm/misc.c create mode 100644 build/tools/makenorfirm/misc.h create mode 100644 build/tools/makenorfirm/out_norfirm.c create mode 100644 build/tools/makenorfirm/path.c create mode 100644 build/tools/makenorfirm/path.h create mode 100644 build/tools/makenorfirm/test/Makefile create mode 100644 build/tools/makenorfirm/test/rsa_private.der create mode 100644 build/tools/makenorfirm/test/test.norsf create mode 100644 build/tools/makenorfirm/test/twl_norfirm7_print.axf create mode 100644 build/tools/makenorfirm/test/twl_norfirm7_print.sbin create mode 100644 build/tools/makenorfirm/test/twl_norfirm9_print.axf create mode 100644 build/tools/makenorfirm/test/twl_norfirm9_print.sbin create mode 100644 build/tools/makenorfirm/test/wram_rbin/Makefile create mode 100644 build/tools/makenorfirm/test/wram_rbin/wram_regs.c create mode 100644 build/tools/makenorfirm/wram_regs.c create mode 100644 include/firm.h create mode 100644 include/firm/acsign.h create mode 100644 include/firm/aes.h create mode 100644 include/firm/aes/ARM7/aes_ids.h create mode 100644 include/firm/aes/ARM7/aes_init.h create mode 100644 include/firm/devices/firm_sdmc/ARM7/sdmc.h create mode 100644 include/firm/format/firm_common.h create mode 100644 include/firm/format/from_brom.h create mode 100644 include/firm/format/gcdfirm.h create mode 100644 include/firm/format/nandfirm.h create mode 100644 include/firm/format/norfirm.h create mode 100644 include/firm/format/sign.h create mode 100644 include/firm/format/wram_regs.h create mode 100644 include/firm/gcd.h create mode 100644 include/firm/gcd/blowfish.h create mode 100644 include/firm/gcd/gcd.h create mode 100644 include/firm/gcd/gcd_misc.h create mode 100644 include/firm/hw/ARM7/mmap_firm.h create mode 100644 include/firm/hw/ARM9/mmap_firm.h create mode 100644 include/firm/memorymap.h create mode 100644 include/firm/mi.h create mode 100644 include/firm/mi/mainMemory.h create mode 100644 include/firm/misc.h create mode 100644 include/firm/nvram.h create mode 100644 include/firm/os.h create mode 100644 include/firm/os/common/boot.h create mode 100644 include/firm/os/common/init.h create mode 100644 include/firm/os/common/systemCall.h create mode 100644 include/firm/os/common/tick_brom.h create mode 100644 include/firm/pm.h create mode 100644 include/firm/specfiles/ARM7-BB-GCDFIRM.lcf.template create mode 100644 include/firm/specfiles/ARM7-BB-GCDFIRM.lsf create mode 100644 include/firm/specfiles/ARM7-BB-NORFIRM.lcf.template create mode 100644 include/firm/specfiles/ARM7-BB-NORFIRM.lsf create mode 100644 include/firm/specfiles/ARM7-TS-GCDFIRM.lcf.template create mode 100644 include/firm/specfiles/ARM7-TS-GCDFIRM.lsf create mode 100644 include/firm/specfiles/ARM7-TS-NORFIRM.lcf.template create mode 100644 include/firm/specfiles/ARM7-TS-NORFIRM.lsf create mode 100644 include/firm/specfiles/ARM9-BB-GCDFIRM.lcf.template create mode 100644 include/firm/specfiles/ARM9-BB-GCDFIRM.lsf create mode 100644 include/firm/specfiles/ARM9-BB-NORFIRM.lcf.template create mode 100644 include/firm/specfiles/ARM9-BB-NORFIRM.lsf create mode 100644 include/firm/specfiles/ARM9-TS-GCDFIRM.lcf.template create mode 100644 include/firm/specfiles/ARM9-TS-GCDFIRM.lsf create mode 100644 include/firm/specfiles/ARM9-TS-NORFIRM.lcf.template create mode 100644 include/firm/specfiles/ARM9-TS-NORFIRM.lsf create mode 100644 include/twl/os/common/systemCall.h create mode 100644 tools/bin/makenorfirm.exe create mode 100644 tools/openssl/openssl.exe diff --git a/build/buildtools/commondefs b/build/buildtools/commondefs new file mode 100644 index 00000000..e0f0b9c9 --- /dev/null +++ b/build/buildtools/commondefs @@ -0,0 +1,249 @@ +#! make -f +#---------------------------------------------------------------------------- +# Project: TwlFirm - commondefs - common definitions for build system +# File: commondefs +# +# Copyright 2007 Nintendo. All rights reserved. +# +# These coded instructions, statements, and computer programs contain +# proprietary information of Nintendo of America Inc. and/or Nintendo +# Company Ltd., and are protected by Federal copyright law. They may +# not be disclosed to third parties or copied or duplicated in any form, +# in whole or in part, without the prior written consent of Nintendo. +# +# $Log: $ +# $NoKeywords: $ +#---------------------------------------------------------------------------- +ifndef TWLFIRM_COMMONDEFS_ +TWLFIRM_COMMONDEFS_ = TRUE + +NITRO_NO_STD_PCHDR = TRUE # プリコンパイルヘッダ抑止 + +#TWLFIRM_ROOT ?= $(shell cygpath -am ../../) + +export TWLBROM_ROOT := $(TWLFIRM_ROOT)/bootrom + + +EMPTY ?= +SPACE ?= $(EMPTY) $(EMPTY) + +# +# CodeGen Target +# +# FIRM_PLATFORM = [BB/EVA/TS] +# FIRM_MEMSIZE = [4M/8M] +# FIRM_CODEGEN = [ARM/THUMB] +# FIRM_PROC = [ARM9/ARM7] +# +# FIRM_TARGET = [NORFIRM/NANDFIRM/GCDFIRM/APP] +# + +FIRM_PLATFORM ?= BB +FIRM_MEMSIZE ?= 8M +FIRM_CODEGEN ?= ARM +FIRM_PROC ?= ARM9 +FIRM_TARGET ?= APP + +# replace TwlSDK +TWL_PLATFORM = $(FIRM_PLATFORM) +TWL_MEMSIZE = $(FIRM_MEMSIZE) +TWL_CODEGEN = $(FIRM_CODEGEN) +TWL_PROC = $(FIRM_PROC) + +# replace NitroSDK +ifndef CODEGEN_PROC +CODEGEN_PROC := $(FIRM_PROC) +endif + +ifeq ($(FIRM_CODEGEN),ALL) +FIRM_CODEGEN_ALL ?= TRUE +override FIRM_CODEGEN = ARM +endif + +ifeq ($(FIRM_CODEGEN),ARM) +FIRM_CODEGEN_ARCH = +else # ($(FIRM_CODEGEN),THUMB) +FIRM_CODEGEN_ARCH = .thumb +endif + +# +# SDK build type +# +# one of [FIRM_DEBUG/FIRM_RELEASE/FIRM_FINALROM] +# + +ifdef FIRM_DEBUG +FIRM_BUILD_TYPE ?= DEBUG +FIRM_BUILD_DIR ?= Debug + +else +ifdef FIRM_FINALROM +FIRM_BUILD_TYPE ?= FINALROM +FIRM_BUILD_DIR ?= Rom + +else # FIRM_RELEASE (default) +FIRM_BUILD_TYPE ?= RELEASE +FIRM_BUILD_DIR ?= Release + +endif +endif + +# replace TwlSDK +TWL_BUILD_TYPE ?= $(FIRM_BUILD_TYPE) +TWL_BUILD_DIR ?= $(FIRM_BUILD_DIR) + + + +#---------------------------------------------------------------------------- +# BROM-SDK path settings +# + +BROM_ROOT := $(subst $(SPACE),\ ,$(subst \,/,$(TWLBROM_ROOT))) +BROM_KEYSDIR := $(BROM_ROOT)/build/keys + + +#---------------------------------------------------------------------------- +# TWL-FIRM path settings +# + +FIRM_ROOT := $(subst $(SPACE),\ ,$(subst \,/,$(TWLFIRM_ROOT))) +FIRM_BUILDTOOLSDIR := $(FIRM_ROOT)/build/buildtools +FIRM_BUILDSETUPDIR := $(FIRM_ROOT)/build/buildsetup +FIRM_INCDIR := $(FIRM_ROOT)/include +FIRM_TOOLSDIR := $(FIRM_ROOT)/tools +FIRM_COMPONENTSDIR := $(FIRM_ROOT)/components +FIRM_ADDINS ?= $(FIRM_ROOT)/add-ins + +FIRM_TWLSDK_ROOT ?=$(shell cygpath -w $(TWLSDK_ROOT)) +FIRM_NITROSDK_ROOT ?=$(shell cygpath -w $(NITROSDK_ROOT)) + +FIRM_BUILDARCH ?= $(CODEGEN_PROC)-$(FIRM_PLATFORM)$(FIRM_CODEGEN_ARCH) +FIRM_BUILDARCH_ARM9 := ARM9-$(FIRM_PLATFORM)$(FIRM_CODEGEN_ARCH) +FIRM_BUILDARCH_ARM7 := ARM7-$(FIRM_PLATFORM)$(FIRM_CODEGEN_ARCH) + +FIRM_BUILDTYPE ?= $(FIRM_BUILDARCH)/$(FIRM_BUILD_DIR) +FIRM_BUILDTYPE_ARM9 := $(FIRM_BUILDARCH_ARM9)/$(FIRM_BUILD_DIR) +FIRM_BUILDTYPE_ARM7 := $(FIRM_BUILDARCH_ARM7)/$(FIRM_BUILD_DIR) + +FIRM_LIBARCH := $(CODEGEN_PROC)-$(FIRM_PLATFORM) +FIRM_LIBTYPE := $(FIRM_LIBARCH)/$(FIRM_BUILD_DIR) +FIRM_LIBDIR := $(FIRM_ROOT)/lib/$(FIRM_LIBTYPE) +FIRM_LIBSYSCALLDIR := $(FIRM_ROOT)/lib/$(FIRM_LIBARCH)/etc +FIRM_LIBSUFFIX := .firm$(FIRM_CODEGEN_ARCH) + + +FIRM_SPECDIR := $(FIRM_INCDIR)/firm/specfiles +FIRM_LSFARCH := $(FIRM_LIBARCH) +ifneq ($(FIRM_TARGET),APP) +FIRM_LSFARCH := $(addsuffix -$(FIRM_TARGET),$(FIRM_LSFARCH)) +endif # FIRM_TARGET!=APP +FIRM_LCFARCH := $(FIRM_LSFARCH) +DEFAULT_FIRM_LCFILE := $(FIRM_SPECDIR)/$(FIRM_LCFARCH).lcf +DEFAULT_FIRM_LCFILE_TEMPLATE := $(FIRM_SPECDIR)/$(FIRM_LCFARCH)$(LCF_SUFFIX_).lcf.template +DEFAULT_FIRM_LCFILE_SPEC := $(FIRM_SPECDIR)/$(FIRM_LSFARCH).lsf +DEFAULT_FIRM_ROM_SPEC := $(FIRM_SPECDIR)/ROM-$(FIRM_PLATFORM).rsf + +# replace TwlSDK +TWL_BUILDARCH ?= $(FIRM_BUILDARCH) + + +### Compiler & Linker settings + +# replace NitroSDK +ifneq ($(FIRM_TARGET),APP) +LCFILE_TEMPLATE ?= $(DEFAULT_FIRM_LCFILE_TEMPLATE) +LCFILE_SPEC ?= $(DEFAULT_FIRM_LCFILE_SPEC) +endif # FIRM_TARGET!=APP + +LDEPENDS_LCF += $(FIRM_BUILDTOOLSDIR)/commondefs +LDEPENDS_RES += $(FIRM_BUILDTOOLSDIR)/commondefs + + +### SDK Library settings + +ifeq ($(CODEGEN_PROC),ARM9) + +FIRM_LIBS_BASE ?= \ + libos \ + libmi \ + libgcd \ + libacsign \ + +FIRM_TWL_LIBS_BASE ?= \ + +else # ($(CODEGEN_PROC),ARM7) + +FIRM_LIBS_BASE ?= \ + libos_sp \ + libnvram_sp \ + libgcd_sp \ + libaes_sp \ + libacsign_sp \ + libfirmsd_sp \ + +FIRM_TWL_LIBS_BASE ?= \ + +endif + +FIRM_LIBS ?= $(addsuffix $(FIRM_LIBSUFFIX).a,$(FIRM_LIBS_BASE)) +FIRM_LIBS += $(addsuffix $(TWL_LIBSUFFIX).a,$(FIRM_TWL_LIBS_BASE)) + +#---------------------------------------------------------------------------- +### TWL-commondefs +# +include $(TWLSDK_ROOT)/build/buildtools/commondefs + + +#---------------------------------------------------------------------------- +# MY BUILD TOOLS +# +MAKENORFIRM := $(FIRM_TOOLSDIR)/bin/makenorfirm.exe +MAKENANDFIRM := $(FIRM_TOOLSDIR)/bin/makenandfirm.exe +MAKEGCDFIRM := $(FIRM_TOOLSDIR)/bin/makegcdfirm.exe +OPENSSL := $(FIRM_TOOLSDIR)/openssl/openssl.exe + +MAKEFIRM_RSA_PRVKEY ?= $(FIRM_TOOLSDIR)/openssl/rsa_private.der +MAKEFIRM_RSA_PUBKEY ?= $(FIRM_TOOLSDIR)/openssl/rsa_public.der + +MAKEFIRM_FLAGS ?= + +ifneq ($(filter NORFIRM NANDFIRM GCDFIRM,$(FIRM_TARGET)),) +FIRM_STRIP_AXF := TRUE +endif + +#---------------------------------------------------------------------------- + +### Global Library resettings + +GINCLUDES := $(FIRM_INCDIR) $(GINCLUDES) +GLIBRARY_DIRS := $(FIRM_LIBDIR) $(GLIBRARY_DIRS) +GLIBRARIES := $(FIRM_LIBS) $(GLIBRARIES) + + +#---------------------------------------------------------------------------- +# TWLFIRM_INSTALL_ROOT +# + +ifdef TWLFIRM_INSTALL_ROOT +TWLFIRM_INSTALL_ROOT_ := $(TWLFIRM_INSTALL_ROOT) +else +TWLFIRM_INSTALL_ROOT_ := $(TWLFIRM_ROOT) +endif + +FIRM_INSTALL_ROOT := $(subst $(SPACE),\ ,$(subst \,/,$(TWLFIRM_INSTALL_ROOT_))) +FIRM_INSTALL_INCDIR := $(FIRM_INSTALL_ROOT)/include +FIRM_INSTALL_TOOLSDIR := $(FIRM_INSTALL_ROOT)/tools +FIRM_INSTALL_LIBDIR := $(FIRM_INSTALL_ROOT)/lib/$(FIRM_LIBTYPE) +FIRM_INSTALL_PROMDIR := $(FIRM_INSTALL_TOOLSDIR)/prom +FIRM_INSTALL_COMPONENTSDIR := $(FIRM_INSTALL_ROOT)/components +FIRM_INSTALL_ADDINS := $(FIRM_INSTALL_ROOT)/add-ins + + +#---------------------------------------------------------------------------- +# Compiler flags +# +MACRO_FLAGS += -DFIRM_TARGET_$(FIRM_TARGET) + +#---------------------------------------------------------------------------- +endif # TWLFIRM_COMMONDEFS_ +#----- End of commondefs ----- diff --git a/build/buildtools/modulerules b/build/buildtools/modulerules new file mode 100644 index 00000000..e9fb4278 --- /dev/null +++ b/build/buildtools/modulerules @@ -0,0 +1,80 @@ +#! make -f +#---------------------------------------------------------------------------- +# Project: TwlFirm - modulerules - common rules for build system +# File: modulerules +# +# Copyright 2007 Nintendo. All rights reserved. +# +# These coded instructions, statements, and computer programs contain +# proprietary information of Nintendo of America Inc. and/or Nintendo +# Company Ltd., and are protected by Federal copyright law. They may +# not be disclosed to third parties or copied or duplicated in any form, +# in whole or in part, without the prior written consent of Nintendo. +# +# $Log: $ +# $NoKeywords: $ +#---------------------------------------------------------------------------- +ifndef TWLFIRM_MODULERULES_ + + +ifdef MAKEFIRM_ARM9 +MAKEROM_ARM9 = $(MAKEFIRM_ARM9) +endif +ifdef MAKEFIRM_ARM7 +MAKEROM_ARM7 = $(MAKEFIRM_ARM7) +endif + +#---------------------------------------------------------------------------- +### TWL-modulerules +# +include $(TWLSDK_ROOT)/build/buildtools/modulerules + +#---------------------------------------------------------------------------- + +# +# MAKENORFIRM / MAKENANDFIRM / MAKEGCDFIRM +# + +MAKEFIRM_ARM9 ?= $(MAKEROM_ARM9) +MAKEFIRM_ARM7 ?= $(MAKEROM_ARM7) + +MAKEFIRM_DEFS += -DFIRM_ROOT='$(FIRM_ROOT)' \ + -DMAKEFIRM_ARM9='$(basename $(MAKEFIRM_ARM9))' \ + -DMAKEFIRM_ARM7='$(basename $(MAKEFIRM_ARM7))' \ + -DMAKEFIRM_RSA_PRVKEY='$(MAKEFIRM_RSA_PRVKEY)' \ + +SDEPENDS_BIN += $(MAKEFIRM_RSA_PRVKEY) + + +.PHONY: firmtop firmlib + +firmtop: + @$(MAKE) -C $(TWLFIRM_ROOT)/build + +firmlib: + @$(MAKE) -C $(TWLFIRM_ROOT)/build/libraries + +# .nor +$(BINDIR)/%.nor: $(SDEPENDS_BIN) $(ROM_SPEC) $(LDEPENDS_BIN) $(EDEPENDS_BIN) $(MAKEFILE) $(MAKENORFIRM) $(MAKEFIRM_RSA_PRVKEY) + $(MAKENORFIRM) $(MAKEFIRM_FLAGS) $(MAKEFIRM_DEFS) $(ROM_SPEC) $@ + +# .nand +$(BINDIR)/%.nand: $(SDEPENDS_BIN) $(ROM_SPEC) $(LDEPENDS_BIN) $(EDEPENDS_BIN) $(MAKEFILE) $(MAKENANDFIRM) $(MAKEFIRM_RSA_PRVKEY) + $(MAKENANDFIRM) $(MAKEFIRM_FLAGS) $(MAKEFIRM_DEFS) $(ROM_SPEC) $@ + +# .gcd +$(BINDIR)/%.gcd: $(SDEPENDS_BIN) $(ROM_SPEC) $(LDEPENDS_BIN) $(EDEPENDS_BIN) $(MAKEFILE) $(MAKEGCDFIRM) $(MAKEFIRM_RSA_PRVKEY) + $(MAKEGCDFIRM) $(MAKEFIRM_FLAGS) $(MAKEFIRM_DEFS) $(ROM_SPEC) $@ + +# .rbin +$(BINDIR)/$(TARGET_BIN_BASENAME).rbin: $(OBJS) + objcopy -I elf32-little -O binary $< $@ + +# .axf +$(BINDIR)/$(TARGET_BIN_BASENAME).axf: $(OBJS) $(LCFILE) $(MAKEFILE) $(LDEPENDS_NEF) $(EDEPENDS_NEF) $(ALIBRARIES) $(LDRES_FILE) $(CW_LIBCXX) + $(LD) $(LDFLAGS) $(LIBRARY_DIRS) @$(call empath,$(LDRES_FILE)) $(call empath,$(LCFILE)) -o $(call empath,$@) + +#---------------------------------------------------------------------------- +TWLFIRM_MODULERULES_ = TRUE +endif # TWLFIRM_MODULERULES_ +#----- End of modulerules ----- diff --git a/build/tools/Makefile b/build/tools/Makefile new file mode 100644 index 00000000..dceeabab --- /dev/null +++ b/build/tools/Makefile @@ -0,0 +1,34 @@ +#! make -f +#---------------------------------------------------------------------------- +# Project: TwlFirm - firmware +# File: Makefile +# +# Copyright 2007 Nintendo. All rights reserved. +# +# These coded instructions, statements, and computer programs contain +# proprietary information of Nintendo of America Inc. and/or Nintendo +# Company Ltd., and are protected by Federal copyright law. They may +# not be disclosed to third parties or copied or duplicated in any form, +# in whole or in part, without the prior written consent of Nintendo. +# +# $Log: $ +# $NoKeywords: $ +#---------------------------------------------------------------------------- + +include $(TWLSDK_ROOT)/build/buildtools/commondefs + + +#---------------------------------------------------------------------------- + +SUBDIRS = \ + makenorfirm \ + makegcdfirm \ + makenandfirm \ + + +#---------------------------------------------------------------------------- + +include $(TWLSDK_ROOT)/build/buildtools/modulerules + + +#===== End of Makefile ===== diff --git a/build/tools/acsign/Makefile b/build/tools/acsign/Makefile new file mode 100644 index 00000000..1ed36c37 --- /dev/null +++ b/build/tools/acsign/Makefile @@ -0,0 +1,61 @@ +#! make -f +#---------------------------------------------------------------------------- +# Project: NitroSDK - libraries - acsign +# File: Makefile +# +# Copyright 2003,2004 Nintendo. All rights reserved. +# +# These coded instructions, statements, and computer programs contain +# proprietary information of Nintendo of America Inc. and/or Nintendo +# Company Ltd., and are protected by Federal copyright law. They may +# not be disclosed to third parties or copied or duplicated in any form, +# in whole or in part, without the prior written consent of Nintendo. +# +# $NoKeywords: $ +#---------------------------------------------------------------------------- + +#---------------------------------------------------------------------------- +# Codegen for sub processer +NITRO_PROC = ARM9 + +# build ARM & THUMB libraries +NITRO_CODEGEN_ALL = True + +#---------------------------------------------------------------------------- + +SUBDIRS = + + +#---------------------------------------------------------------------------- + +SRCDIR = ./src +INCDIR = ./include $(NITROSDK_ROOT)/build/libraries/mb/include + + +SRCS = acsign.c acmemory.c acsign_util.c + +TARGET_LIB = libacsign_x86.a + + +include $(TWLFIRM_ROOT)/build/buildtools/commondefs + +# CCFLAGS += -DOPT_32_BIT -DSTANDALONE -DNO_SPLIT -DNO_FP_API -DNO_R_DIAG -DNO_STDIO_H -DNO_STDLIB_H +CCFLAGS += -DSMALL_CODE_SIZE \ + -DSTANDALONE \ + -DOPT_32_BIT \ + -DNO_SPLIT \ + -DNO_FP_API \ + -DNO_R_DIAG \ + -DNO_STDIO_H \ + -DNO_STDLIB_H + +INSTALL_TARGETS = $(TARGETS) +INSTALL_DIR = . + + +do-build: $(TARGETS) + +include $(TWLFIRM_ROOT)/build/buildtools/modulerules + + +#===== End of Makefile ===== diff --git a/build/tools/acsign/acsign.c b/build/tools/acsign/acsign.c new file mode 100644 index 00000000..7b63142f --- /dev/null +++ b/build/tools/acsign/acsign.c @@ -0,0 +1,334 @@ +#include +#include "format_sign.h" +#include "acsign.h" + +// SHA1 +#include "sha1dgst.c" + +// RSA + +// BN +#include "bn.h" + +#include "bn_lib.c" +#include "bn_asm.c" +#include "bn_comba.c" +#include "bn_lsh.c" +#include "bn_rsh.c" +#include "bn_word.c" +#include "bn_add.c" +#include "bn_mul.c" +#include "bn_div.c" +#include "bn_exp.c" +#include "bn_sqr.c" +#include "bn_fm_w.c" +#include "bn_gcd.c" +#include "bn_ex_str.c" +#include "bn_ms_w.c" +//#include "bn_me.c" +#include "bn_rec.c" +#include "bn_mont.c" +#include "bn_recp.c" +#include "bn_wdiv.c" +#include "bn_r_exp.c" +#include "bn_m_exp.c" + + + +#define BER_NULL 5 +#define BER_OBJECT 6 +#define BER_SEQUENCE 16 +#define BER_OCTET_STRING 4 +#define BER_CONSTRUCTED 0x20 + + +/** + * Perform the EMSA-PKCS1_v1_5 padding + * + * @param random random context for seed data generation + * @param out destination buffer + * @param out_len length written to out + * @param in data to be padded + * @param in_len length of in + * @param flags mask of operation directives + * + * @pre out_len must contain length of data allocated to out + * @pre out must be the allocated the total length to be padded + * + * @note Method from PKCS#1 v2.1 standard 9.2.1 EMSA-PKCS1-v1_5 + * @li In: Hash is hash funtion, hLen denotes length of octets of hash output + * @li In: M is message to be encoded + * @li In: emLen is intended length of octets + * @li Out: EM encoded message of length emLen + * + * @li Apply the hash function to message M to produce a hash value H + * If hash output is message to long the output message too long and + * stop + * @li Encode the algorithm ID for the hash in function into an ASN1 value + * @li if emLen < 10 + BER encoded message T output message length too short + * and stop + * @li generate octet string PS consisting of emLen - Length(T) - 2 octets + * with value FF + * @li Concatenate PS, DER encoding T and other padding such that EM is: + * 01 || PS || 00 || T + * + * @note it is the out layers responsibility to DER encode the + * data prior to padding as described in the first 2 steps in method + */ +// +// rsa_padding_add_pkcs1_type_1関数相当 +// +static int add_padding(unsigned char *out, + int out_len, const unsigned char *in, int in_len) +{ + unsigned char *p; + int j,i; + + if ((in_len+11) > out_len) + return(1); + + /* First we copy data bytes to the output buffer, this + * way the input and output buffers can be the same + * and things will still work */ + p=out+out_len-in_len; + for (i=in_len-1; i>=0; i--) + p[i]=in[i]; + p=out; + *(p++)=0; + *(p++)=1; /* Private Key BT (Block Type) */ + + /* pad with 0xff data */ + j=out_len-3-in_len; + Memset(p,0xff,j); + p[j]='\0'; + return(0); +} + +static void debug_dump(const void* buf, int len, const char* str, int line_elms) +{ + const u8* bufp = (u8*)buf; + int i,ii; + + if (str) + { + debug_printf("%s :\n", str); + } + for (i=0; i<=len/line_elms; i++) + { + if (i*line_elms >= len) + { + break; + } + for (ii=0; ii= len) + { + break; + } + debug_printf("%02x ", bufp[i*line_elms+ii]); + } + debug_printf("\n"); + } +} + +// +// RSA +// +BOOL ACSign_Encrypto(void *sign, const void *key, const void *data, int length) +{ + BN_CTX *ctx; + BIGNUM src, dst, exp, mod; + u8* key_exp = &((u8*)key)[ACS_RSA_PRVEXP_OFFSET]; + u8* key_mod = &((u8*)key)[ACS_RSA_PRVMOD_OFFSET]; + u8 buf[ACS_ENCRYPTED_HASH_LEN]; + u32 len = length; + BOOL result = TRUE; + + if (NULL == sign || NULL == key || NULL == data || 0 > length) { + return FALSE; + } + + if ( add_padding( buf, ACS_ENCRYPTED_HASH_LEN, data, length ) ) { + debug_printf2("encode_padding was failed.\n"); + result = FALSE; + goto end; + } + + { + debug_dump(buf, ACS_ENCRYPTED_HASH_LEN, "padded hash", 16); + debug_dump(key_mod, ACS_RSA_PRVMOD_LEN, "key mod", 16); + debug_dump(key_exp, ACS_RSA_PRVEXP_LEN, "key exp", 16); + } + + ctx = BN_CTX_new(); + + BN_init(&src); + BN_init(&dst); + BN_init(&exp); + BN_init(&mod); + + BN_bin2bn((u8*)buf, ACS_ENCRYPTED_HASH_LEN, &src); + BN_bin2bn(key_exp, ACS_RSA_PRVEXP_LEN, &exp); + BN_bin2bn(key_mod, ACS_RSA_PRVMOD_LEN, &mod); + + BN_mod_exp( &dst, &src, &exp, &mod, ctx ); + + len = BN_bn2bin( &dst, sign ); + + BN_free(&src); + BN_free(&dst); + BN_free(&exp); + BN_free(&mod); + + if (ctx) { + BN_CTX_free(ctx); + } + + if ( len != ACS_DECRYPTED_HASH_LEN ) { + result = FALSE; + goto end; + } +end: + return result; +} + +BOOL ACSign_Decrypto(void *buf, const void *key, const void *sign, int length) +{ + BN_CTX *ctx; + BIGNUM src, dst, exp, mod; + u32 key_exp = ACS_RSA_EXP; + u8* key_mod = &((u8*)key)[ACS_RSA_PRVMOD_OFFSET]; + u8* bufp = (u8*)buf; + u32 len = length; + BOOL result = TRUE; + + if (NULL == buf || NULL == key || NULL == sign || 0 > length) { + return FALSE; + } + + ctx = BN_CTX_new(); + + BN_init(&src); + BN_init(&dst); + BN_init(&exp); + BN_init(&mod); + + BN_bin2bn((u8*)sign, ACS_ENCRYPTED_HASH_LEN, &src); + BN_bin2bn((u8*)&key_exp, ACS_RSA_PUBEXP_LEN, &exp); + BN_bin2bn(key_mod, ACS_RSA_PRVMOD_LEN, &mod); + + BN_mod_exp( &dst, &src, &exp, &mod, ctx ); + + len = BN_bn2bin( &dst, bufp ); + + BN_free(&src); + BN_free(&dst); + BN_free(&exp); + BN_free(&mod); + + if (ctx) { + BN_CTX_free(ctx); + } + + if ( len != ACS_DECRYPTED_HASH_LEN ) { + result = FALSE; + goto end; + } +end: + return result; +} + +// +int ACSign_DigestUnit( + void* buffer, // 出力領域 + const void* buf, // データへのポインタ + unsigned int len // データの長さ + ) +{ + HASHContext context; + unsigned char *bufferp = buffer; + + HASHReset( &context ); + HASHUpdate( &context, buf, len ); + HASHGetDigest( &context, bufferp ); + + return TRUE; +} + +// +int ACSign_CompareUnit( + const void* decedHash, // ACSign_Decryptoの出力 + const void* digest // ACSign_DigestUnitの出力 + ) +{ + const unsigned char* dgt = digest; + const unsigned char* dgtCmp = decedHash; + int i; + int test = TRUE; + + if ( !decedHash ) return FALSE; + if ( !digest ) return FALSE; + + for ( i = 0; i < ACS_HASH_LEN; i++ ) + { + if ( *dgt++ != *dgtCmp++ ) + { + test = FALSE; + break; + } + } + return test; +} + +/* + 任意バイト数の入力データから + 任意バイト数の出力データを得ます。 + 偏っているかもしれない入力値を一様な値にマップするだけです。 + 入出力メモリがかぶっていても問題ない +*/ +int ACSign_GetKey( + void* dest_ptr, // 出力データへのポインタ + unsigned int dest_len, // 出力データの長さ + const void* src_ptr, // 入力データへのポインタ + unsigned int src_len // 入力データの長さ + ) +{ + HASHContext ctx; + unsigned char *ptr; + unsigned char state[ACS_HASH_LEN]; + unsigned char output[ACS_HASH_LEN]; + int i; + + if (dest_ptr == NULL) + return 1; + if (src_ptr == NULL && src_len > 0) + return 0; + + HASHReset(&ctx); + + HASHUpdate(&ctx, src_ptr, src_len); + HASHGetDigest(&ctx, state); + + ptr = dest_ptr; + while (dest_len > 0) + { + unsigned int len = dest_len < ACS_HASH_LEN ? dest_len : ACS_HASH_LEN; + + // plus one + for (i = 0; i < ACS_HASH_LEN; i++) + { + if (state[ACS_HASH_LEN-1-i]++) + break; + } + + HASHUpdate(&ctx, state, ACS_HASH_LEN); + HASHGetDigest(&ctx, output); + memcpy(ptr, output, len); + ptr += len; + dest_len -= len; + } + memset(state, 0, ACS_HASH_LEN); + memset(output, 0, ACS_HASH_LEN); + return 1; +} + diff --git a/build/tools/acsign/acsign_gcd.c b/build/tools/acsign/acsign_gcd.c new file mode 100644 index 00000000..e8b45670 --- /dev/null +++ b/build/tools/acsign/acsign_gcd.c @@ -0,0 +1,74 @@ +#include +#include "acsign_gcd.h" + +static void debug_dump(void* buf, int len, char* str, int line_elms) +{ + u8* bufp = (u8*)buf; + int i,ii; + + if (str) + { + debug_printf("%s :\n", str); + } + for (i=0; i<=len/line_elms; i++) + { + if (i*line_elms >= len) + { + break; + } + for (ii=0; ii= len) + { + break; + } + debug_printf("%02x ", bufp[i*line_elms+ii]); + } + debug_printf("\n"); + } +} + +// +int ACSign_DigestHeader( + void* buffer, // 出力領域 + GCDHeader* header // ヘッダへのポインタ + ) +{ + HASHContext context; + GCDHeader* nh = header; + unsigned char *bufferp = buffer; + + HASHReset( &context ); + HASHUpdate( &context, (void*)&nh->l, sizeof(GCDHeaderLow) ); + HASHUpdate( &context, (void*)&nh->h, sizeof(GCDHeaderHigh) ); + HASHGetDigest( &context, bufferp ); + + return TRUE; +} + +// +int ACSign_Final( + GCDHeader* header, // ヘッダへのポインタ + void* buffer, // 入力領域 + const void* key + ) +{ + GCDHeader* nh = header; + FIRMSignedContext* sc = buffer; + + if (key) + { + unsigned char decSign[ACS_ENCRYPTED_HASH_LEN]; + + debug_dump(sc->hash, sizeof(sc->hash), "5 hashs of header, norfirms and total", 20); + + ACSign_Encrypto(&nh->sign, key, buffer, sizeof(FIRMSignedContext)); + ACSign_Decrypto(decSign, key, (void*)&nh->sign, ACS_ENCRYPTED_HASH_LEN); + + debug_dump(&nh->sign, ACS_ENCRYPTED_HASH_LEN, "encrypted sign", 16); + debug_dump(decSign, ACS_ENCRYPTED_HASH_LEN, "decrypted sign", 16); + } + + return TRUE; +} + diff --git a/build/tools/acsign/acsign_nand.c b/build/tools/acsign/acsign_nand.c new file mode 100644 index 00000000..8b0b719c --- /dev/null +++ b/build/tools/acsign/acsign_nand.c @@ -0,0 +1,75 @@ +#include +#include "acsign_nand.h" + +static void debug_dump(void* buf, int len, char* str, int line_elms) +{ + u8* bufp = (u8*)buf; + int i,ii; + + if (str) + { + debug_printf("%s :\n", str); + } + for (i=0; i<=len/line_elms; i++) + { + if (i*line_elms >= len) + { + break; + } + for (ii=0; ii= len) + { + break; + } + debug_printf("%02x ", bufp[i*line_elms+ii]); + } + debug_printf("\n"); + } +} + +// +int ACSign_DigestHeader( + void* buffer, // 出力領域 + NANDHeader* header // ヘッダへのポインタ + ) +{ + HASHContext context; + NANDHeader* nh = header; + unsigned char *bufferp = buffer; + + HASHReset( &context ); + HASHUpdate( &context, (void*)&nh->d, sizeof(NORHeaderDS) ); + HASHUpdate( &context, (void*)&nh->l, sizeof(NANDHeaderLow) ); + HASHUpdate( &context, (void*)&nh->h, sizeof(NANDHeaderHigh) ); + HASHGetDigest( &context, bufferp ); + + return TRUE; +} + +// +int ACSign_Final( + NANDHeader* header, // ヘッダへのポインタ + void* buffer, // 入力領域 + const void* key + ) +{ + NANDHeader* nh = header; + FIRMSignedContext* sc = buffer; + + if (key) + { + unsigned char decSign[ACS_ENCRYPTED_HASH_LEN]; + + debug_dump(sc->hash, sizeof(sc->hash), "5 hashs of header, nandfirms and total", 20); + + ACSign_Encrypto(&nh->sign, key, buffer, sizeof(FIRMSignedContext)); + ACSign_Decrypto(decSign, key, (void*)&nh->sign, ACS_ENCRYPTED_HASH_LEN); + + debug_dump(&nh->sign, ACS_ENCRYPTED_HASH_LEN, "encrypted sign", 16); + debug_dump(decSign, ACS_ENCRYPTED_HASH_LEN, "decrypted sign", 16); + } + + return TRUE; +} + diff --git a/build/tools/acsign/acsign_nor.c b/build/tools/acsign/acsign_nor.c new file mode 100644 index 00000000..0bb690c9 --- /dev/null +++ b/build/tools/acsign/acsign_nor.c @@ -0,0 +1,75 @@ +#include +#include "acsign_nor.h" + +static void debug_dump(void* buf, int len, char* str, int line_elms) +{ + u8* bufp = (u8*)buf; + int i,ii; + + if (str) + { + debug_printf("%s :\n", str); + } + for (i=0; i<=len/line_elms; i++) + { + if (i*line_elms >= len) + { + break; + } + for (ii=0; ii= len) + { + break; + } + debug_printf("%02x ", bufp[i*line_elms+ii]); + } + debug_printf("\n"); + } +} + +// +int ACSign_DigestHeader( + void* buffer, // 出力領域 + NORHeader* header // ヘッダへのポインタ + ) +{ + HASHContext context; + NORHeader* nh = header; + unsigned char *bufferp = buffer; + + HASHReset( &context ); + HASHUpdate( &context, (void*)&nh->d, sizeof(NORHeaderDS) ); + HASHUpdate( &context, (void*)&nh->l, sizeof(NORHeaderLow) ); + HASHUpdate( &context, (void*)&nh->h, sizeof(NORHeaderHigh) ); + HASHGetDigest( &context, bufferp ); + + return TRUE; +} + +// +int ACSign_Final( + NORHeader* header, // ヘッダへのポインタ + void* buffer, // 入力領域 + const void* key + ) +{ + NORHeader* nh = header; + FIRMSignedContext* sc = buffer; + + if (key) + { + unsigned char decSign[ACS_ENCRYPTED_HASH_LEN]; + + debug_dump(sc->hash, sizeof(sc->hash), "5 hashs of header, norfirms and total", 20); + + ACSign_Encrypto(&nh->sign, key, buffer, sizeof(FIRMSignedContext)); + ACSign_Decrypto(decSign, key, (void*)&nh->sign, ACS_ENCRYPTED_HASH_LEN); + + debug_dump(&nh->sign, ACS_ENCRYPTED_HASH_LEN, "encrypted sign", 16); + debug_dump(decSign, ACS_ENCRYPTED_HASH_LEN, "decrypted sign", 16); + } + + return TRUE; +} + diff --git a/build/tools/acsign/aes.h b/build/tools/acsign/aes.h new file mode 100644 index 00000000..a7a75d8d --- /dev/null +++ b/build/tools/acsign/aes.h @@ -0,0 +1,139 @@ +/* $Id$ */ +/* + * Copyright (C) 1998-2003 RSA Security Inc. All rights reserved. + * + * This work contains proprietary information of RSA Security. + * Distribution is limited to authorized licensees of RSA + * Security. Any unauthorized reproduction, distribution or + * modification of this work is strictly prohibited. + */ + +#ifndef HEADER_COMMON_AES_H +#define HEADER_COMMON_AES_H + +#ifdef __cplusplus +extern "C" { +#endif + +#define AES_ecb_encrypt(key, out, in) \ + AES_ecb_encrypt_com((key), (out), (in), AES_encrypt) +#define AES_ecb_encrypt_m(key, out, in) \ + AES_ecb_encrypt_com((key), (out), (in), AES_encrypt_m) +#define AES_ecb_encrypt_s(key, out, in) \ + AES_ecb_encrypt_com((key), (out), (in), AES_encrypt_s) + +#define AES_ecb_decrypt(key, out, in) \ + AES_ecb_decrypt_com((key), (out), (in), AES_decrypt) +#define AES_ecb_decrypt_m(key, out, in) \ + AES_ecb_decrypt_com((key), (out), (in), AES_decrypt_m) +#define AES_ecb_decrypt_s(key, out, in) \ + AES_ecb_decrypt_com((key), (out), (in), AES_decrypt_s) + +#define AES_cbc_encrypt(ctx, out, in, len, iv) \ + AES_cbc_encrypt_com((ctx), (out), (in), (len), (iv), AES_encrypt) +#define AES_cbc_encrypt_m(ctx, out, in, len, iv) \ + AES_cbc_encrypt_com((ctx), (out), (in), (len), (iv), AES_encrypt_m) +#define AES_cbc_encrypt_s(ctx, out, in, len, iv) \ + AES_cbc_encrypt_com((ctx), (out), (in), (len), (iv), AES_encrypt_s) + +#define AES_cbc_decrypt(ctx, out, in, len, iv) \ + AES_cbc_decrypt_com((ctx), (out), (in), (len), (iv), AES_decrypt) +#define AES_cbc_decrypt_m(ctx, out, in, len, iv) \ + AES_cbc_decrypt_com((ctx), (out), (in), (len), (iv), AES_decrypt_m) +#define AES_cbc_decrypt_s(ctx, out, in, len, iv) \ + AES_cbc_decrypt_com((ctx), (out), (in), (len), (iv), AES_decrypt_s) + +#define AES_cfb128_encrypt(ks, out, in, len, iv, num) \ + AES_cfb128_encrypt_com((ks), (out), (in), (len), (iv), (num), \ + AES_encrypt) +#define AES_cfb128_encrypt_m(ks, out, in, len, iv, num) \ + AES_cfb128_encrypt_com((ks), (out), (in), (len), (iv), (num), \ + AES_encrypt_m) +#define AES_cfb128_encrypt_s(ks, out, in, len, iv, num) \ + AES_cfb128_encrypt_com((ks), (out), (in), (len), (iv), (num), \ + AES_encrypt_s) + +#define AES_cfb128_decrypt(ks, out, in, len, iv, num) \ + AES_cfb128_decrypt_com((ks), (out), (in), (len), (iv), (num), \ + AES_decrypt) +#define AES_cfb128_decrypt_m(ks, out, in, len, iv, num) \ + AES_cfb128_decrypt_com((ks), (out), (in), (len), (iv), (num), \ + AES_decrypt_m) +#define AES_cfb128_decrypt_s(ks, out, in, len, iv, num) \ + AES_cfb128_decrypt_com((ks), (out), (in), (len), (iv), (num), \ + AES_decrypt_s) + +/** Note. OFB encryption is also used for decryption */ +#define AES_ofb128_encrypt(ks, out, in, len, iv, num) \ + AES_ofb128_encrypt_com((ks), (out), (in), (len), (iv), (num), \ + AES_encrypt) +#define AES_ofb128_encrypt_m(ks, out, in, len, iv, num) \ + AES_ofb128_encrypt_com((ks), (out), (in), (len), (iv), (num), \ + AES_encrypt_m) +#define AES_ofb128_encrypt_s(ks, out, in, len, iv, num) \ + AES_ofb128_encrypt_com((ks), (out), (in), (len), (iv), (num), \ + AES_encrypt_s) + +#define AES_ofb128_decrypt(ks, out, in, len, iv, num) \ + AES_ofb128_encrypt_com((ks), (out), (in), (len), (iv), (num), \ + AES_encrypt) +#define AES_ofb128_decrypt_m(ks, out, in, len, iv, num) \ + AES_ofb128_encrypt_com((ks), (out), (in), (len), (iv), (num), \ + AES_encrypt_m) +#define AES_ofb128_decrypt_s(ks, out, in, len, iv, num) \ + AES_ofb128_encrypt_com((ks), (out), (in), (len), (iv), (num), \ + AES_encrypt_s) + + +#define AES_MAXROUNDS 14 + +#define AES_ENCRYPT 1 +#define AES_DECRYPT 0 + +typedef unsigned int AES_INT4; + +typedef struct aes_key_st { + unsigned int rounds; + unsigned int key_size; + AES_INT4 ks[(AES_MAXROUNDS+1)*8]; + } AES_KEY; + +typedef void (* AES_ENC_DEC_FN)(AES_KEY *, AES_INT4 *); + +void AES_encrypt(AES_KEY *ctx,AES_INT4 *data); +void AES_encrypt_m(AES_KEY *ctx,AES_INT4 *data); +void AES_encrypt_s(AES_KEY *ctx,AES_INT4 *data); +void AES_decrypt(AES_KEY *ctx,AES_INT4 *data); +void AES_decrypt_m(AES_KEY *ctx,AES_INT4 *data); +void AES_decrypt_s(AES_KEY *ctx,AES_INT4 *data); + +void AES_ecb_encrypt_com(AES_KEY *key, unsigned char *out, + const unsigned char *in, AES_ENC_DEC_FN enc_fn); +void AES_ecb_decrypt_com(AES_KEY *key, unsigned char *out, + const unsigned char *in, AES_ENC_DEC_FN dec_fn); +void AES_cbc_encrypt_com(AES_KEY *ctx, unsigned char *out, + const unsigned char *in, long length, unsigned char *iv, + AES_ENC_DEC_FN enc_fn); +void AES_cbc_decrypt_com(AES_KEY *ctx, unsigned char *out, + const unsigned char *in, long length, unsigned char *iv, + AES_ENC_DEC_FN dec_fn); +void AES_ofb128_encrypt_com(AES_KEY *ks,const unsigned char *in, + unsigned char *out, long length, unsigned char *ivec, int *num, + AES_ENC_DEC_FN enc_fn); +void AES_cfb128_encrypt_com(AES_KEY *ks, const unsigned char *in, + unsigned char *out, long length, unsigned char *ivec, int *num, + AES_ENC_DEC_FN enc_fn); +void AES_cfb128_decrypt_com(AES_KEY *ks, const unsigned char *in, + unsigned char *out, long length, unsigned char *ivec, int *num, + AES_ENC_DEC_FN dec_fn); + +int AES_set_key(AES_KEY *ctx, const unsigned char *key, int len); +void AES_convert_key(AES_KEY *ctx); +AES_INT4 AES_rotate(AES_INT4 u); +unsigned char AES_xtime(AES_INT4 x); + +#ifdef __cplusplus +} +#endif + +#endif /* HEADER_COMMON_AES_H */ diff --git a/build/tools/acsign/aes2.c b/build/tools/acsign/aes2.c new file mode 100644 index 00000000..778dc53a --- /dev/null +++ b/build/tools/acsign/aes2.c @@ -0,0 +1,133 @@ +#include +#include + +#include "aes2.h" + +#include "aes_e.c" +#include "aes_e_ecb.c" +#include "aes_skey.c" + +// +// swap with memory allocation +// +static unsigned char* AES_Swap(const unsigned char *src, int len, int unit) +{ + int i; + int j; + unsigned char *dest = malloc(len); + for (i = 0; i < len; i+=unit) + for (j = 0; j < unit; j++) + dest[i + j] = src[i + unit - j - 1]; + return dest; +} + +// +// set keys +// +void AES_SetKey(AES_KEY *key, const unsigned char seed[AES_BLOCK_SIZE], const unsigned char id[AES_BLOCK_SIZE]) +{ + static const unsigned char f[AES_BLOCK_SIZE] = {0xff, 0xfe, 0xfb, 0x4e, 0x29, 0x59, 0x02, 0x58, 0x2a, 0x68, 0x0f, 0x5f, 0x1a, 0x4f, 0x3e, 0x79}; + static const unsigned char s = 0x2a; + + unsigned char key1[AES_BLOCK_SIZE]; + unsigned char key2[AES_BLOCK_SIZE]; + int i; + int o = 0; + + for (i = 0; i < AES_BLOCK_SIZE; i++) { + key1[i] = seed[AES_BLOCK_SIZE-i-1] ^ id[AES_BLOCK_SIZE-i-1]; + } + for (i = AES_BLOCK_SIZE - 1; i >= 0; i--) { + int t = key1[i] + f[i] + o; + o = (t > 0xFF ? 1 : 0); + key1[i] = t & 0xFF; + } + for (i = 0; i < AES_BLOCK_SIZE; i++) { + int j1 = (i + s / 8) % AES_BLOCK_SIZE; + int j2 = (j1 + 1) % AES_BLOCK_SIZE; + int k = s % 8; + key2[i] = ((key1[j1] << k) & 0xFF) | ((key1[j2] >> (8 - k)) & 0xFF); + } + AES_set_key(key, key2, 16); + memset(key2, 0, 16); + memset(key1, 0, 16); +} + +// +// ctr mode +// +#define GETU32(pt) (((unsigned long)(pt)[0] << 24) ^ ((unsigned long)(pt)[1] << 16) ^ ((unsigned long)(pt)[2] << 8) ^ ((unsigned long)(pt)[3])) +#define PUTU32(ct, st) { (ct)[0] = (unsigned char)((st) >> 24); (ct)[1] = (unsigned char)((st) >> 16); (ct)[2] = (unsigned char)((st) >> 8); (ct)[3] = (unsigned char)(st); } + +/* increment counter as u128 */ +static void AES_ctr128_increment(unsigned char *counter) { + unsigned long c; + + c = GETU32(counter + 12); + c++; c &= 0xFFFFFFFF; + PUTU32(counter + 12, c); + if (c) return; // return unless overflow + + c = GETU32(counter + 8); + c++; c &= 0xFFFFFFFF; + PUTU32(counter + 8, c); + if (c) return; // return unless overflow + + c = GETU32(counter + 4); + c++; c &= 0xFFFFFFFF; + PUTU32(counter + 4, c); + if (c) return; // return unless overflow + + c = GETU32(counter + 0); + c++; c &= 0xFFFFFFFF; + PUTU32(counter + 0, c); +} +static void AES_ctr128(const unsigned char *in, unsigned char *out, const unsigned long length, AES_KEY *key, + unsigned char ivec[AES_BLOCK_SIZE], unsigned char ebuf[AES_BLOCK_SIZE], unsigned int *num) { + + unsigned int n; + unsigned long l=length; + + n = *num; + + while (l--) { // loop each byte + if (n == 0) { + AES_ecb_encrypt(key, ebuf, ivec); // encrypt counter + AES_ctr128_increment(ivec); // increment counter + } + *(out++) = *(in++) ^ ebuf[n]; + n = (n+1) % AES_BLOCK_SIZE; + } + + *num=n; +} + +void AES_Ctr(AES_KEY *key, unsigned char *outdata, const unsigned char *indata, int len, unsigned char iv[AES_BLOCK_SIZE]) +{ + unsigned char ebuf[AES_BLOCK_SIZE]; + unsigned char *tmp; + unsigned int nums; + + if (len <= 0 || len > 0xFFFF00 || (len % AES_BLOCK_SIZE) != 0 || indata == NULL || outdata == NULL) + { + return; + } + + // CTR + memset(ebuf, 0, AES_BLOCK_SIZE); + nums = 0; + + tmp = AES_Swap(iv, AES_BLOCK_SIZE, AES_BLOCK_SIZE); + memcpy(iv, tmp, AES_BLOCK_SIZE); + free(tmp); + + tmp = AES_Swap(indata, len, AES_BLOCK_SIZE); + AES_ctr128(tmp, outdata, len, key, iv, ebuf, &nums); + free(tmp); + + tmp = AES_Swap(outdata, len, AES_BLOCK_SIZE); + memcpy(outdata, tmp, len); + free(tmp); + return; +} + diff --git a/build/tools/acsign/aes2.h b/build/tools/acsign/aes2.h new file mode 100644 index 00000000..3e80799d --- /dev/null +++ b/build/tools/acsign/aes2.h @@ -0,0 +1,21 @@ +#ifndef __AES_2_H__ +#define __AES_2_H__ + +#include "aes.h" + +#define AES_BLOCK_SIZE 16 +#define AES_NONCE_SIZE 12 +#define AES_QUANTITY (AES_BLOCK_SIZE - AES_NONCE_SIZE - 1) + +#ifdef __cplusplus +extern "C" { +#endif + +void AES_SetKey(AES_KEY *key, const unsigned char seed[AES_BLOCK_SIZE], const unsigned char id[AES_BLOCK_SIZE]); +void AES_Ctr(AES_KEY *key, unsigned char *outdata, const unsigned char *indata, int len, unsigned char iv[AES_BLOCK_SIZE]); + +#ifdef __cplusplus +} +#endif + +#endif // __AES_2_H__ diff --git a/build/tools/acsign/aes_e.c b/build/tools/acsign/aes_e.c new file mode 100644 index 00000000..fab235c1 --- /dev/null +++ b/build/tools/acsign/aes_e.c @@ -0,0 +1,115 @@ +/* $Id$ */ +/* + * Copyright (C) 1998-2003 RSA Security Inc. All rights reserved. + * + * This work contains proprietary information of RSA Security. + * Distribution is limited to authorized licensees of RSA + * Security. Any unauthorized reproduction, distribution or + * modification of this work is strictly prohibited. + */ + +/** + * @file aes_e.c + * \brief Large code size (thus fast) AES Encryption + */ + +#ifndef NO_AES + +#include "aes.h" + +/* Include the Large aes_submix tables */ +#include "aes_e.h" + +/* AES ECB Encryption of 1 x 128 bit Block */ +void AES_encrypt(AES_KEY *ctx, AES_INT4 *data) +{ + int r; + int rounds; + AES_INT4 *k; + AES_INT4 o0,o1,o2,o3; + AES_INT4 tmp0,tmp1,tmp2,tmp3; + + rounds=ctx->rounds; + k= (AES_INT4 *)ctx->ks; + + /* Encrypt one block. + */ + tmp0=data[0]; + tmp1=data[1]; + tmp2=data[2]; + tmp3=data[3]; +/*printf("K %08X %08X %08X %08X\n",tmp0,tmp1,tmp2,tmp3); +printf("KS %08X %08X %08X %08X\n",k[0],k[1],k[2],k[3]); */ + tmp0^=k[0]; + tmp1^=k[1]; + tmp2^=k[2]; + tmp3^=k[3]; + k+=4; + + for(r = 1; r < rounds; r++) + { +/*printf("%2d %08X %08X %08X %08X\n",r,tmp0,tmp1,tmp2,tmp3); */ + o0 = aes_submix[3][(tmp3 )&0xff]^ + aes_submix[2][(tmp2>> 8)&0xff]^ + aes_submix[1][(tmp1>>16)&0xff]^ + aes_submix[0][(tmp0>>24)&0xff]; + + o1 = aes_submix[3][(tmp0 )&0xff]^ + aes_submix[2][(tmp3>> 8)&0xff]^ + aes_submix[1][(tmp2>>16)&0xff]^ + aes_submix[0][(tmp1>>24)&0xff]; + + o2 = + aes_submix[3][(tmp1 )&0xff]^ + aes_submix[2][(tmp0>> 8)&0xff]^ + aes_submix[1][(tmp3>>16)&0xff]^ + aes_submix[0][(tmp2>>24)&0xff]; + + o3 = + aes_submix[3][(tmp2 )&0xff]^ + aes_submix[2][(tmp1>> 8)&0xff]^ + aes_submix[1][(tmp0>>16)&0xff]^ + aes_submix[0][(tmp3>>24)&0xff]; + +/* +printf(" %08X %08X %08X %08X\n",o0,o1,o2,o3); +printf("KS %08X %08X %08X %08X\n",k[0],k[1],k[2],k[3]); */ + tmp0=o0^k[0]; + tmp1=o1^k[1]; + tmp2=o2^k[2]; + tmp3=o3^k[3]; + k+=4; + } +/*printf("O %08X %08X %08X %08X\n",o0,o1,o2,o3); */ + o0 = (aes_submix[0][(tmp3 )&0xff]>> 8)&0xFF; + o1 = (aes_submix[0][(tmp0 )&0xff]>> 8)&0xFF; + o2 = (aes_submix[0][(tmp1 )&0xff]>> 8)&0xFF; + o3 = (aes_submix[0][(tmp2 )&0xff]>> 8)&0xFF; + + o0|= (aes_submix[0][(tmp2>> 8)&0xff] )&0xFF00; + o1|= (aes_submix[0][(tmp3>> 8)&0xff] )&0xFF00; + o2|= (aes_submix[0][(tmp0>> 8)&0xff] )&0xFF00; + o3|= (aes_submix[0][(tmp1>> 8)&0xff] )&0xFF00; + + o0|= (aes_submix[0][(tmp1>>16)&0xff]<< 8)&0xFF0000; + o1|= (aes_submix[0][(tmp2>>16)&0xff]<< 8)&0xFF0000; + o2|= (aes_submix[0][(tmp3>>16)&0xff]<< 8)&0xFF0000; + o3|= (aes_submix[0][(tmp0>>16)&0xff]<< 8)&0xFF0000; + + o0|= (aes_submix[0][(tmp0>>24) ]<<16)&0xFF000000; + o1|= (aes_submix[0][(tmp1>>24) ]<<16)&0xFF000000; + o2|= (aes_submix[0][(tmp2>>24) ]<<16)&0xFF000000; + o3|= (aes_submix[0][(tmp3>>24) ]<<16)&0xFF000000; +/*printf("P %08X %08X %08X %08X\n",o0,o1,o2,o3); */ + o0^=k[0]; + o1^=k[1]; + o2^=k[2]; + o3^=k[3]; +/*printf("Q %08X %08X %08X %08X\n",o0,o1,o2,o3); */ + data[0]= o0; + data[1]= o1; + data[2]= o2; + data[3]= o3; + } + +#endif /* NO_AES */ diff --git a/build/tools/acsign/aes_e.h b/build/tools/acsign/aes_e.h new file mode 100644 index 00000000..74f6db70 --- /dev/null +++ b/build/tools/acsign/aes_e.h @@ -0,0 +1,284 @@ +/* $Id$ */ +/* + * Copyright (C) 1998-2003 RSA Security Inc. All rights reserved. + * + * This work contains proprietary information of RSA Security. + * Distribution is limited to authorized licensees of RSA + * Security. Any unauthorized reproduction, distribution or + * modification of this work is strictly prohibited. + */ + +#ifndef HEADER_COMMON_AES_E_H +#define HEADER_COMMON_AES_E_H + +#ifdef __cplusplus +extern "C" { +#endif + +const AES_INT4 aes_submix[4][256]={{ + 0xC66363A5,0xF87C7C84,0xEE777799,0xF67B7B8D, + 0xFFF2F20D,0xD66B6BBD,0xDE6F6FB1,0x91C5C554, + 0x60303050,0x02010103,0xCE6767A9,0x562B2B7D, + 0xE7FEFE19,0xB5D7D762,0x4DABABE6,0xEC76769A, + 0x8FCACA45,0x1F82829D,0x89C9C940,0xFA7D7D87, + 0xEFFAFA15,0xB25959EB,0x8E4747C9,0xFBF0F00B, + 0x41ADADEC,0xB3D4D467,0x5FA2A2FD,0x45AFAFEA, + 0x239C9CBF,0x53A4A4F7,0xE4727296,0x9BC0C05B, + 0x75B7B7C2,0xE1FDFD1C,0x3D9393AE,0x4C26266A, + 0x6C36365A,0x7E3F3F41,0xF5F7F702,0x83CCCC4F, + 0x6834345C,0x51A5A5F4,0xD1E5E534,0xF9F1F108, + 0xE2717193,0xABD8D873,0x62313153,0x2A15153F, + 0x0804040C,0x95C7C752,0x46232365,0x9DC3C35E, + 0x30181828,0x379696A1,0x0A05050F,0x2F9A9AB5, + 0x0E070709,0x24121236,0x1B80809B,0xDFE2E23D, + 0xCDEBEB26,0x4E272769,0x7FB2B2CD,0xEA75759F, + 0x1209091B,0x1D83839E,0x582C2C74,0x341A1A2E, + 0x361B1B2D,0xDC6E6EB2,0xB45A5AEE,0x5BA0A0FB, + 0xA45252F6,0x763B3B4D,0xB7D6D661,0x7DB3B3CE, + 0x5229297B,0xDDE3E33E,0x5E2F2F71,0x13848497, + 0xA65353F5,0xB9D1D168,0x00000000,0xC1EDED2C, + 0x40202060,0xE3FCFC1F,0x79B1B1C8,0xB65B5BED, + 0xD46A6ABE,0x8DCBCB46,0x67BEBED9,0x7239394B, + 0x944A4ADE,0x984C4CD4,0xB05858E8,0x85CFCF4A, + 0xBBD0D06B,0xC5EFEF2A,0x4FAAAAE5,0xEDFBFB16, + 0x864343C5,0x9A4D4DD7,0x66333355,0x11858594, + 0x8A4545CF,0xE9F9F910,0x04020206,0xFE7F7F81, + 0xA05050F0,0x783C3C44,0x259F9FBA,0x4BA8A8E3, + 0xA25151F3,0x5DA3A3FE,0x804040C0,0x058F8F8A, + 0x3F9292AD,0x219D9DBC,0x70383848,0xF1F5F504, + 0x63BCBCDF,0x77B6B6C1,0xAFDADA75,0x42212163, + 0x20101030,0xE5FFFF1A,0xFDF3F30E,0xBFD2D26D, + 0x81CDCD4C,0x180C0C14,0x26131335,0xC3ECEC2F, + 0xBE5F5FE1,0x359797A2,0x884444CC,0x2E171739, + 0x93C4C457,0x55A7A7F2,0xFC7E7E82,0x7A3D3D47, + 0xC86464AC,0xBA5D5DE7,0x3219192B,0xE6737395, + 0xC06060A0,0x19818198,0x9E4F4FD1,0xA3DCDC7F, + 0x44222266,0x542A2A7E,0x3B9090AB,0x0B888883, + 0x8C4646CA,0xC7EEEE29,0x6BB8B8D3,0x2814143C, + 0xA7DEDE79,0xBC5E5EE2,0x160B0B1D,0xADDBDB76, + 0xDBE0E03B,0x64323256,0x743A3A4E,0x140A0A1E, + 0x924949DB,0x0C06060A,0x4824246C,0xB85C5CE4, + 0x9FC2C25D,0xBDD3D36E,0x43ACACEF,0xC46262A6, + 0x399191A8,0x319595A4,0xD3E4E437,0xF279798B, + 0xD5E7E732,0x8BC8C843,0x6E373759,0xDA6D6DB7, + 0x018D8D8C,0xB1D5D564,0x9C4E4ED2,0x49A9A9E0, + 0xD86C6CB4,0xAC5656FA,0xF3F4F407,0xCFEAEA25, + 0xCA6565AF,0xF47A7A8E,0x47AEAEE9,0x10080818, + 0x6FBABAD5,0xF0787888,0x4A25256F,0x5C2E2E72, + 0x381C1C24,0x57A6A6F1,0x73B4B4C7,0x97C6C651, + 0xCBE8E823,0xA1DDDD7C,0xE874749C,0x3E1F1F21, + 0x964B4BDD,0x61BDBDDC,0x0D8B8B86,0x0F8A8A85, + 0xE0707090,0x7C3E3E42,0x71B5B5C4,0xCC6666AA, + 0x904848D8,0x06030305,0xF7F6F601,0x1C0E0E12, + 0xC26161A3,0x6A35355F,0xAE5757F9,0x69B9B9D0, + 0x17868691,0x99C1C158,0x3A1D1D27,0x279E9EB9, + 0xD9E1E138,0xEBF8F813,0x2B9898B3,0x22111133, + 0xD26969BB,0xA9D9D970,0x078E8E89,0x339494A7, + 0x2D9B9BB6,0x3C1E1E22,0x15878792,0xC9E9E920, + 0x87CECE49,0xAA5555FF,0x50282878,0xA5DFDF7A, + 0x038C8C8F,0x59A1A1F8,0x09898980,0x1A0D0D17, + 0x65BFBFDA,0xD7E6E631,0x844242C6,0xD06868B8, + 0x824141C3,0x299999B0,0x5A2D2D77,0x1E0F0F11, + 0x7BB0B0CB,0xA85454FC,0x6DBBBBD6,0x2C16163A, + },{ + 0xA5C66363,0x84F87C7C,0x99EE7777,0x8DF67B7B, + 0x0DFFF2F2,0xBDD66B6B,0xB1DE6F6F,0x5491C5C5, + 0x50603030,0x03020101,0xA9CE6767,0x7D562B2B, + 0x19E7FEFE,0x62B5D7D7,0xE64DABAB,0x9AEC7676, + 0x458FCACA,0x9D1F8282,0x4089C9C9,0x87FA7D7D, + 0x15EFFAFA,0xEBB25959,0xC98E4747,0x0BFBF0F0, + 0xEC41ADAD,0x67B3D4D4,0xFD5FA2A2,0xEA45AFAF, + 0xBF239C9C,0xF753A4A4,0x96E47272,0x5B9BC0C0, + 0xC275B7B7,0x1CE1FDFD,0xAE3D9393,0x6A4C2626, + 0x5A6C3636,0x417E3F3F,0x02F5F7F7,0x4F83CCCC, + 0x5C683434,0xF451A5A5,0x34D1E5E5,0x08F9F1F1, + 0x93E27171,0x73ABD8D8,0x53623131,0x3F2A1515, + 0x0C080404,0x5295C7C7,0x65462323,0x5E9DC3C3, + 0x28301818,0xA1379696,0x0F0A0505,0xB52F9A9A, + 0x090E0707,0x36241212,0x9B1B8080,0x3DDFE2E2, + 0x26CDEBEB,0x694E2727,0xCD7FB2B2,0x9FEA7575, + 0x1B120909,0x9E1D8383,0x74582C2C,0x2E341A1A, + 0x2D361B1B,0xB2DC6E6E,0xEEB45A5A,0xFB5BA0A0, + 0xF6A45252,0x4D763B3B,0x61B7D6D6,0xCE7DB3B3, + 0x7B522929,0x3EDDE3E3,0x715E2F2F,0x97138484, + 0xF5A65353,0x68B9D1D1,0x00000000,0x2CC1EDED, + 0x60402020,0x1FE3FCFC,0xC879B1B1,0xEDB65B5B, + 0xBED46A6A,0x468DCBCB,0xD967BEBE,0x4B723939, + 0xDE944A4A,0xD4984C4C,0xE8B05858,0x4A85CFCF, + 0x6BBBD0D0,0x2AC5EFEF,0xE54FAAAA,0x16EDFBFB, + 0xC5864343,0xD79A4D4D,0x55663333,0x94118585, + 0xCF8A4545,0x10E9F9F9,0x06040202,0x81FE7F7F, + 0xF0A05050,0x44783C3C,0xBA259F9F,0xE34BA8A8, + 0xF3A25151,0xFE5DA3A3,0xC0804040,0x8A058F8F, + 0xAD3F9292,0xBC219D9D,0x48703838,0x04F1F5F5, + 0xDF63BCBC,0xC177B6B6,0x75AFDADA,0x63422121, + 0x30201010,0x1AE5FFFF,0x0EFDF3F3,0x6DBFD2D2, + 0x4C81CDCD,0x14180C0C,0x35261313,0x2FC3ECEC, + 0xE1BE5F5F,0xA2359797,0xCC884444,0x392E1717, + 0x5793C4C4,0xF255A7A7,0x82FC7E7E,0x477A3D3D, + 0xACC86464,0xE7BA5D5D,0x2B321919,0x95E67373, + 0xA0C06060,0x98198181,0xD19E4F4F,0x7FA3DCDC, + 0x66442222,0x7E542A2A,0xAB3B9090,0x830B8888, + 0xCA8C4646,0x29C7EEEE,0xD36BB8B8,0x3C281414, + 0x79A7DEDE,0xE2BC5E5E,0x1D160B0B,0x76ADDBDB, + 0x3BDBE0E0,0x56643232,0x4E743A3A,0x1E140A0A, + 0xDB924949,0x0A0C0606,0x6C482424,0xE4B85C5C, + 0x5D9FC2C2,0x6EBDD3D3,0xEF43ACAC,0xA6C46262, + 0xA8399191,0xA4319595,0x37D3E4E4,0x8BF27979, + 0x32D5E7E7,0x438BC8C8,0x596E3737,0xB7DA6D6D, + 0x8C018D8D,0x64B1D5D5,0xD29C4E4E,0xE049A9A9, + 0xB4D86C6C,0xFAAC5656,0x07F3F4F4,0x25CFEAEA, + 0xAFCA6565,0x8EF47A7A,0xE947AEAE,0x18100808, + 0xD56FBABA,0x88F07878,0x6F4A2525,0x725C2E2E, + 0x24381C1C,0xF157A6A6,0xC773B4B4,0x5197C6C6, + 0x23CBE8E8,0x7CA1DDDD,0x9CE87474,0x213E1F1F, + 0xDD964B4B,0xDC61BDBD,0x860D8B8B,0x850F8A8A, + 0x90E07070,0x427C3E3E,0xC471B5B5,0xAACC6666, + 0xD8904848,0x05060303,0x01F7F6F6,0x121C0E0E, + 0xA3C26161,0x5F6A3535,0xF9AE5757,0xD069B9B9, + 0x91178686,0x5899C1C1,0x273A1D1D,0xB9279E9E, + 0x38D9E1E1,0x13EBF8F8,0xB32B9898,0x33221111, + 0xBBD26969,0x70A9D9D9,0x89078E8E,0xA7339494, + 0xB62D9B9B,0x223C1E1E,0x92158787,0x20C9E9E9, + 0x4987CECE,0xFFAA5555,0x78502828,0x7AA5DFDF, + 0x8F038C8C,0xF859A1A1,0x80098989,0x171A0D0D, + 0xDA65BFBF,0x31D7E6E6,0xC6844242,0xB8D06868, + 0xC3824141,0xB0299999,0x775A2D2D,0x111E0F0F, + 0xCB7BB0B0,0xFCA85454,0xD66DBBBB,0x3A2C1616, + },{ + 0x63A5C663,0x7C84F87C,0x7799EE77,0x7B8DF67B, + 0xF20DFFF2,0x6BBDD66B,0x6FB1DE6F,0xC55491C5, + 0x30506030,0x01030201,0x67A9CE67,0x2B7D562B, + 0xFE19E7FE,0xD762B5D7,0xABE64DAB,0x769AEC76, + 0xCA458FCA,0x829D1F82,0xC94089C9,0x7D87FA7D, + 0xFA15EFFA,0x59EBB259,0x47C98E47,0xF00BFBF0, + 0xADEC41AD,0xD467B3D4,0xA2FD5FA2,0xAFEA45AF, + 0x9CBF239C,0xA4F753A4,0x7296E472,0xC05B9BC0, + 0xB7C275B7,0xFD1CE1FD,0x93AE3D93,0x266A4C26, + 0x365A6C36,0x3F417E3F,0xF702F5F7,0xCC4F83CC, + 0x345C6834,0xA5F451A5,0xE534D1E5,0xF108F9F1, + 0x7193E271,0xD873ABD8,0x31536231,0x153F2A15, + 0x040C0804,0xC75295C7,0x23654623,0xC35E9DC3, + 0x18283018,0x96A13796,0x050F0A05,0x9AB52F9A, + 0x07090E07,0x12362412,0x809B1B80,0xE23DDFE2, + 0xEB26CDEB,0x27694E27,0xB2CD7FB2,0x759FEA75, + 0x091B1209,0x839E1D83,0x2C74582C,0x1A2E341A, + 0x1B2D361B,0x6EB2DC6E,0x5AEEB45A,0xA0FB5BA0, + 0x52F6A452,0x3B4D763B,0xD661B7D6,0xB3CE7DB3, + 0x297B5229,0xE33EDDE3,0x2F715E2F,0x84971384, + 0x53F5A653,0xD168B9D1,0x00000000,0xED2CC1ED, + 0x20604020,0xFC1FE3FC,0xB1C879B1,0x5BEDB65B, + 0x6ABED46A,0xCB468DCB,0xBED967BE,0x394B7239, + 0x4ADE944A,0x4CD4984C,0x58E8B058,0xCF4A85CF, + 0xD06BBBD0,0xEF2AC5EF,0xAAE54FAA,0xFB16EDFB, + 0x43C58643,0x4DD79A4D,0x33556633,0x85941185, + 0x45CF8A45,0xF910E9F9,0x02060402,0x7F81FE7F, + 0x50F0A050,0x3C44783C,0x9FBA259F,0xA8E34BA8, + 0x51F3A251,0xA3FE5DA3,0x40C08040,0x8F8A058F, + 0x92AD3F92,0x9DBC219D,0x38487038,0xF504F1F5, + 0xBCDF63BC,0xB6C177B6,0xDA75AFDA,0x21634221, + 0x10302010,0xFF1AE5FF,0xF30EFDF3,0xD26DBFD2, + 0xCD4C81CD,0x0C14180C,0x13352613,0xEC2FC3EC, + 0x5FE1BE5F,0x97A23597,0x44CC8844,0x17392E17, + 0xC45793C4,0xA7F255A7,0x7E82FC7E,0x3D477A3D, + 0x64ACC864,0x5DE7BA5D,0x192B3219,0x7395E673, + 0x60A0C060,0x81981981,0x4FD19E4F,0xDC7FA3DC, + 0x22664422,0x2A7E542A,0x90AB3B90,0x88830B88, + 0x46CA8C46,0xEE29C7EE,0xB8D36BB8,0x143C2814, + 0xDE79A7DE,0x5EE2BC5E,0x0B1D160B,0xDB76ADDB, + 0xE03BDBE0,0x32566432,0x3A4E743A,0x0A1E140A, + 0x49DB9249,0x060A0C06,0x246C4824,0x5CE4B85C, + 0xC25D9FC2,0xD36EBDD3,0xACEF43AC,0x62A6C462, + 0x91A83991,0x95A43195,0xE437D3E4,0x798BF279, + 0xE732D5E7,0xC8438BC8,0x37596E37,0x6DB7DA6D, + 0x8D8C018D,0xD564B1D5,0x4ED29C4E,0xA9E049A9, + 0x6CB4D86C,0x56FAAC56,0xF407F3F4,0xEA25CFEA, + 0x65AFCA65,0x7A8EF47A,0xAEE947AE,0x08181008, + 0xBAD56FBA,0x7888F078,0x256F4A25,0x2E725C2E, + 0x1C24381C,0xA6F157A6,0xB4C773B4,0xC65197C6, + 0xE823CBE8,0xDD7CA1DD,0x749CE874,0x1F213E1F, + 0x4BDD964B,0xBDDC61BD,0x8B860D8B,0x8A850F8A, + 0x7090E070,0x3E427C3E,0xB5C471B5,0x66AACC66, + 0x48D89048,0x03050603,0xF601F7F6,0x0E121C0E, + 0x61A3C261,0x355F6A35,0x57F9AE57,0xB9D069B9, + 0x86911786,0xC15899C1,0x1D273A1D,0x9EB9279E, + 0xE138D9E1,0xF813EBF8,0x98B32B98,0x11332211, + 0x69BBD269,0xD970A9D9,0x8E89078E,0x94A73394, + 0x9BB62D9B,0x1E223C1E,0x87921587,0xE920C9E9, + 0xCE4987CE,0x55FFAA55,0x28785028,0xDF7AA5DF, + 0x8C8F038C,0xA1F859A1,0x89800989,0x0D171A0D, + 0xBFDA65BF,0xE631D7E6,0x42C68442,0x68B8D068, + 0x41C38241,0x99B02999,0x2D775A2D,0x0F111E0F, + 0xB0CB7BB0,0x54FCA854,0xBBD66DBB,0x163A2C16, + },{ + 0x6363A5C6,0x7C7C84F8,0x777799EE,0x7B7B8DF6, + 0xF2F20DFF,0x6B6BBDD6,0x6F6FB1DE,0xC5C55491, + 0x30305060,0x01010302,0x6767A9CE,0x2B2B7D56, + 0xFEFE19E7,0xD7D762B5,0xABABE64D,0x76769AEC, + 0xCACA458F,0x82829D1F,0xC9C94089,0x7D7D87FA, + 0xFAFA15EF,0x5959EBB2,0x4747C98E,0xF0F00BFB, + 0xADADEC41,0xD4D467B3,0xA2A2FD5F,0xAFAFEA45, + 0x9C9CBF23,0xA4A4F753,0x727296E4,0xC0C05B9B, + 0xB7B7C275,0xFDFD1CE1,0x9393AE3D,0x26266A4C, + 0x36365A6C,0x3F3F417E,0xF7F702F5,0xCCCC4F83, + 0x34345C68,0xA5A5F451,0xE5E534D1,0xF1F108F9, + 0x717193E2,0xD8D873AB,0x31315362,0x15153F2A, + 0x04040C08,0xC7C75295,0x23236546,0xC3C35E9D, + 0x18182830,0x9696A137,0x05050F0A,0x9A9AB52F, + 0x0707090E,0x12123624,0x80809B1B,0xE2E23DDF, + 0xEBEB26CD,0x2727694E,0xB2B2CD7F,0x75759FEA, + 0x09091B12,0x83839E1D,0x2C2C7458,0x1A1A2E34, + 0x1B1B2D36,0x6E6EB2DC,0x5A5AEEB4,0xA0A0FB5B, + 0x5252F6A4,0x3B3B4D76,0xD6D661B7,0xB3B3CE7D, + 0x29297B52,0xE3E33EDD,0x2F2F715E,0x84849713, + 0x5353F5A6,0xD1D168B9,0x00000000,0xEDED2CC1, + 0x20206040,0xFCFC1FE3,0xB1B1C879,0x5B5BEDB6, + 0x6A6ABED4,0xCBCB468D,0xBEBED967,0x39394B72, + 0x4A4ADE94,0x4C4CD498,0x5858E8B0,0xCFCF4A85, + 0xD0D06BBB,0xEFEF2AC5,0xAAAAE54F,0xFBFB16ED, + 0x4343C586,0x4D4DD79A,0x33335566,0x85859411, + 0x4545CF8A,0xF9F910E9,0x02020604,0x7F7F81FE, + 0x5050F0A0,0x3C3C4478,0x9F9FBA25,0xA8A8E34B, + 0x5151F3A2,0xA3A3FE5D,0x4040C080,0x8F8F8A05, + 0x9292AD3F,0x9D9DBC21,0x38384870,0xF5F504F1, + 0xBCBCDF63,0xB6B6C177,0xDADA75AF,0x21216342, + 0x10103020,0xFFFF1AE5,0xF3F30EFD,0xD2D26DBF, + 0xCDCD4C81,0x0C0C1418,0x13133526,0xECEC2FC3, + 0x5F5FE1BE,0x9797A235,0x4444CC88,0x1717392E, + 0xC4C45793,0xA7A7F255,0x7E7E82FC,0x3D3D477A, + 0x6464ACC8,0x5D5DE7BA,0x19192B32,0x737395E6, + 0x6060A0C0,0x81819819,0x4F4FD19E,0xDCDC7FA3, + 0x22226644,0x2A2A7E54,0x9090AB3B,0x8888830B, + 0x4646CA8C,0xEEEE29C7,0xB8B8D36B,0x14143C28, + 0xDEDE79A7,0x5E5EE2BC,0x0B0B1D16,0xDBDB76AD, + 0xE0E03BDB,0x32325664,0x3A3A4E74,0x0A0A1E14, + 0x4949DB92,0x06060A0C,0x24246C48,0x5C5CE4B8, + 0xC2C25D9F,0xD3D36EBD,0xACACEF43,0x6262A6C4, + 0x9191A839,0x9595A431,0xE4E437D3,0x79798BF2, + 0xE7E732D5,0xC8C8438B,0x3737596E,0x6D6DB7DA, + 0x8D8D8C01,0xD5D564B1,0x4E4ED29C,0xA9A9E049, + 0x6C6CB4D8,0x5656FAAC,0xF4F407F3,0xEAEA25CF, + 0x6565AFCA,0x7A7A8EF4,0xAEAEE947,0x08081810, + 0xBABAD56F,0x787888F0,0x25256F4A,0x2E2E725C, + 0x1C1C2438,0xA6A6F157,0xB4B4C773,0xC6C65197, + 0xE8E823CB,0xDDDD7CA1,0x74749CE8,0x1F1F213E, + 0x4B4BDD96,0xBDBDDC61,0x8B8B860D,0x8A8A850F, + 0x707090E0,0x3E3E427C,0xB5B5C471,0x6666AACC, + 0x4848D890,0x03030506,0xF6F601F7,0x0E0E121C, + 0x6161A3C2,0x35355F6A,0x5757F9AE,0xB9B9D069, + 0x86869117,0xC1C15899,0x1D1D273A,0x9E9EB927, + 0xE1E138D9,0xF8F813EB,0x9898B32B,0x11113322, + 0x6969BBD2,0xD9D970A9,0x8E8E8907,0x9494A733, + 0x9B9BB62D,0x1E1E223C,0x87879215,0xE9E920C9, + 0xCECE4987,0x5555FFAA,0x28287850,0xDFDF7AA5, + 0x8C8C8F03,0xA1A1F859,0x89898009,0x0D0D171A, + 0xBFBFDA65,0xE6E631D7,0x4242C684,0x6868B8D0, + 0x4141C382,0x9999B029,0x2D2D775A,0x0F0F111E, + 0xB0B0CB7B,0x5454FCA8,0xBBBBD66D,0x16163A2C, + }}; + +#ifdef __cplusplus +} +#endif + +#endif /* HEADER_COMMON_AES_E_H */ diff --git a/build/tools/acsign/aes_e_ecb.c b/build/tools/acsign/aes_e_ecb.c new file mode 100644 index 00000000..55867bd9 --- /dev/null +++ b/build/tools/acsign/aes_e_ecb.c @@ -0,0 +1,48 @@ +/* $Id$ */ +/* + * Copyright (C) 1998-2003 RSA Security Inc. All rights reserved. + * + * This work contains proprietary information of RSA Security. + * Distribution is limited to authorized licensees of RSA + * Security. Any unauthorized reproduction, distribution or + * modification of this work is strictly prohibited. + */ + +#ifndef NO_AES + +#include "aes.h" + + +void AES_ecb_encrypt_com(AES_KEY *key, unsigned char *out, + const unsigned char *in, AES_ENC_DEC_FN enc_fn) + { + AES_INT4 data[4],w; + + data[0]=((in[ 0]<<24)|(in[ 1]<<16)|(in[ 2]<<8)|(in[ 3])); + data[1]=((in[ 4]<<24)|(in[ 5]<<16)|(in[ 6]<<8)|(in[ 7])); + data[2]=((in[ 8]<<24)|(in[ 9]<<16)|(in[10]<<8)|(in[11])); + data[3]=((in[12]<<24)|(in[13]<<16)|(in[14]<<8)|(in[15])); + enc_fn(key,&(data[0])); + w=data[0]; + out[ 0]=(w>>24)&0xff; + out[ 1]=(w>>16)&0xff; + out[ 2]=(w>> 8)&0xff; + out[ 3]=(w )&0xff; + w=data[1]; + out[ 4]=(w>>24)&0xff; + out[ 5]=(w>>16)&0xff; + out[ 6]=(w>> 8)&0xff; + out[ 7]=(w )&0xff; + w=data[2]; + out[ 8]=(w>>24)&0xff; + out[ 9]=(w>>16)&0xff; + out[10]=(w>> 8)&0xff; + out[11]=(w )&0xff; + w=data[3]; + out[12]=(w>>24)&0xff; + out[13]=(w>>16)&0xff; + out[14]=(w>> 8)&0xff; + out[15]=(w )&0xff; + } + +#endif /* NO_AES */ diff --git a/build/tools/acsign/aes_sbox.h b/build/tools/acsign/aes_sbox.h new file mode 100644 index 00000000..6a584f4b --- /dev/null +++ b/build/tools/acsign/aes_sbox.h @@ -0,0 +1,59 @@ +/* $Id$ */ +/* + * Copyright (C) 1998-2003 RSA Security Inc. All rights reserved. + * + * This work contains proprietary information of RSA Security. + * Distribution is limited to authorized licensees of RSA + * Security. Any unauthorized reproduction, distribution or + * modification of this work is strictly prohibited. + */ + +#ifndef HEADER_COMMON_AES_SBOX_H +#define HEADER_COMMON_AES_SBOX_H + +#ifdef __cplusplus +extern "C" { +#endif + +extern const unsigned char aes_sbox[256]; + +const unsigned char aes_sbox[256]={ + 0x63,0x7C,0x77,0x7B,0xF2,0x6B,0x6F,0xC5, + 0x30,0x01,0x67,0x2B,0xFE,0xD7,0xAB,0x76, + 0xCA,0x82,0xC9,0x7D,0xFA,0x59,0x47,0xF0, + 0xAD,0xD4,0xA2,0xAF,0x9C,0xA4,0x72,0xC0, + 0xB7,0xFD,0x93,0x26,0x36,0x3F,0xF7,0xCC, + 0x34,0xA5,0xE5,0xF1,0x71,0xD8,0x31,0x15, + 0x04,0xC7,0x23,0xC3,0x18,0x96,0x05,0x9A, + 0x07,0x12,0x80,0xE2,0xEB,0x27,0xB2,0x75, + 0x09,0x83,0x2C,0x1A,0x1B,0x6E,0x5A,0xA0, + 0x52,0x3B,0xD6,0xB3,0x29,0xE3,0x2F,0x84, + 0x53,0xD1,0x00,0xED,0x20,0xFC,0xB1,0x5B, + 0x6A,0xCB,0xBE,0x39,0x4A,0x4C,0x58,0xCF, + 0xD0,0xEF,0xAA,0xFB,0x43,0x4D,0x33,0x85, + 0x45,0xF9,0x02,0x7F,0x50,0x3C,0x9F,0xA8, + 0x51,0xA3,0x40,0x8F,0x92,0x9D,0x38,0xF5, + 0xBC,0xB6,0xDA,0x21,0x10,0xFF,0xF3,0xD2, + 0xCD,0x0C,0x13,0xEC,0x5F,0x97,0x44,0x17, + 0xC4,0xA7,0x7E,0x3D,0x64,0x5D,0x19,0x73, + 0x60,0x81,0x4F,0xDC,0x22,0x2A,0x90,0x88, + 0x46,0xEE,0xB8,0x14,0xDE,0x5E,0x0B,0xDB, + 0xE0,0x32,0x3A,0x0A,0x49,0x06,0x24,0x5C, + 0xC2,0xD3,0xAC,0x62,0x91,0x95,0xE4,0x79, + 0xE7,0xC8,0x37,0x6D,0x8D,0xD5,0x4E,0xA9, + 0x6C,0x56,0xF4,0xEA,0x65,0x7A,0xAE,0x08, + 0xBA,0x78,0x25,0x2E,0x1C,0xA6,0xB4,0xC6, + 0xE8,0xDD,0x74,0x1F,0x4B,0xBD,0x8B,0x8A, + 0x70,0x3E,0xB5,0x66,0x48,0x03,0xF6,0x0E, + 0x61,0x35,0x57,0xB9,0x86,0xC1,0x1D,0x9E, + 0xE1,0xF8,0x98,0x11,0x69,0xD9,0x8E,0x94, + 0x9B,0x1E,0x87,0xE9,0xCE,0x55,0x28,0xDF, + 0x8C,0xA1,0x89,0x0D,0xBF,0xE6,0x42,0x68, + 0x41,0x99,0x2D,0x0F,0xB0,0x54,0xBB,0x16, + }; + +#ifdef __cplusplus +} +#endif + +#endif /* HEADER_COMMON_AES_SBOX_H */ diff --git a/build/tools/acsign/aes_skey.c b/build/tools/acsign/aes_skey.c new file mode 100644 index 00000000..682766ea --- /dev/null +++ b/build/tools/acsign/aes_skey.c @@ -0,0 +1,143 @@ +/* $Id$ */ +/* + * Copyright (C) 1998-2003 RSA Security Inc. All rights reserved. + * + * This work contains proprietary information of RSA Security. + * Distribution is limited to authorized licensees of RSA + * Security. Any unauthorized reproduction, distribution or + * modification of this work is strictly prohibited. + */ + +#ifndef NO_AES + +#include "aes.h" +#include "aes_sbox.h" + +static const unsigned char Rcon[30]={ + 0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80, + 0x1b,0x36,0x6c,0xd8,0xab,0x4d,0x9a,0x2f, + 0x5e,0xbc,0x63,0xc6,0x97,0x35,0x6a,0xd4, + 0xb3,0x7d,0xfa,0xef,0xc5,0x91, + }; + +int AES_set_key(AES_KEY *ctx, const unsigned char *key, int len) + { + int i,ii; + AES_INT4 *W,tmp,tmp2; + const unsigned char *ip; + int words; + + if (len == 16) + { + i=10; + words=4; + } + else if (len == 24) + { + i=12; + words=6; + } + else if (len == 32) + { + i=14; + words=8; + } + else + return(1); + + ctx->rounds=i; + ctx->key_size=words; + W=(unsigned int *)ctx->ks; + for (i=0; irounds+1); + for (i=words; i> 8)&0xff]<<16; + tmp2|=(AES_INT4)aes_sbox[(tmp>>16)&0xff]<<24; + tmp2|=(AES_INT4)aes_sbox[(tmp>>24) ]; + tmp=tmp2^(((unsigned int)*ip)<<24); + ip++; + } + if ((words == 8) && ((i % words) == 4)) + { + tmp2 =(AES_INT4)aes_sbox[(tmp )&0xff] ; + tmp2|=(AES_INT4)aes_sbox[(tmp>> 8)&0xff]<< 8; + tmp2|=(AES_INT4)aes_sbox[(tmp>>16)&0xff]<<16; + tmp2|=(AES_INT4)aes_sbox[(tmp>>24) ]<<24; + tmp=tmp2; + } + W[i]=W[i-words]^tmp; + } + return(0); + } + +#define rot1(x) (((x) << 24) | ((x) >> 8)) +#define rot2(x) (((x) << 16) | ((x) >> 16)) +#define rot3(x) (((x) << 8) | ((x) >> 24)) + +/* This cute trick does 4 'mul by two' at once. Stolen from + * Dr B. R. Gladman but I'm sure the u-(u>>7) is + * a standard graphics trick + * The key to this is that we need to xor with 0x1b if the top bit is set. + * a 1xxx xxxx 0xxx 0xxx First we mask the 7bit, + * b 1000 0000 0000 0000 then we shift right by 7 puting the 7bit in 0bit, + * c 0000 0001 0000 0000 we then subtract (c) from (b) + * d 0111 1111 0000 0000 and now we and with our mask + * e 0001 1011 0000 0000 + */ +#define mt 0x80808080 +#define ml 0x7f7f7f7f +#define mh 0xfefefefe +#define mm 0x1b1b1b1b +#define mul2(x,t) ((t)=((x)&mt), \ + ((((x)+(x))&mh)^(((t)-((t)>>7))&mm))) + +#define mix_col(x,f2,f3) (\ + (f2)=mul2(x,f3), \ + (f3)=(x)^(f2), \ + (rot3(f3) ^ rot2(x) ^ rot1(x)^(f2))) + +#define inv_mix_col(x,f2,f4,f8,f9) (\ + (f2)=mul2(x,f2), \ + (f4)=mul2(f2,f4), \ + (f8)=mul2(f4,f8), \ + (f9)=(x)^(f8), \ + (f8)=((f2)^(f4)^(f8)), \ + (f2)^=(f9), \ + (f4)^=(f9), \ + (f8)^=rot3(f2), \ + (f8)^=rot2(f4), \ + (f8)^rot1(f9)) + +void AES_convert_key(AES_KEY *ctx) + { + int i; + AES_INT4 *k,w,t1,t2,t3,t4; + + k= ctx->ks; + k+=4; + for (i=ctx->rounds*4; i>4; i--) + { + w= *k; + w = inv_mix_col(w,t1,t2,t3,t4); + *k++ =w; + } + } +#endif /* NO_AES */ diff --git a/build/tools/acsign/bn_add.c b/build/tools/acsign/bn_add.c new file mode 100644 index 00000000..75820c61 --- /dev/null +++ b/build/tools/acsign/bn_add.c @@ -0,0 +1,304 @@ +/* $Id$ */ +/* + * Copyright (C) 1998-2002 RSA Security Inc. All rights reserved. + * + * This work contains proprietary information of RSA Security. + * Distribution is limited to authorized licensees of RSA + * Security. Any unauthorized reproduction, distribution or + * modification of this work is strictly prohibited. + * + */ + +/** + * @file bn_add.c + * @brief BIGNUM addition and subtraction functions + */ + +#include "bn_lcl.h" + +#if !(defined(NO_SPLIT) && defined(SPLIT_FILE)) + +#ifdef NO_SPLIT +#define SPLIT_BN_ADD +#define SPLIT_BN_UADD +#define SPLIT_BN_USUB +#define SPLIT_BN_SUB +#endif /* NO_SPLIT */ + +#ifdef SPLIT_BN_ADD +/* r can == a or b */ +int BN_add(r, a, b) +BIGNUM *r; +BIGNUM *a; +BIGNUM *b; + { + BIGNUM *tmp; + + bn_check_top(a); + bn_check_top(b); + + /* a + b a+b + * a + -b a-b + * -a + b b-a + * -a + -b -(a+b) + */ + if (a->neg ^ b->neg) + { + /* only one is negative */ + if (a->neg) + { tmp=a; a=b; b=tmp; } + + /* we are now a - b */ + + if (BN_ucmp(a,b) < 0) + { + if (!BN_usub(r,b,a)) return(0); + r->neg=1; + } + else + { + if (!BN_usub(r,a,b)) return(0); + r->neg=0; + } + return(1); + } + + if (a->neg) /* both are neg */ + r->neg=1; + else + r->neg=0; + + if (!BN_uadd(r,a,b)) return(0); + return(1); + } +#endif + +#ifdef SPLIT_BN_UADD +/* unsigned add of b to a, r must be large enough */ +int BN_uadd(r,a,b) +BIGNUM *r; +BIGNUM *a; +BIGNUM *b; + { + register int i; + int max,min; + BN_ULONG *ap,*bp,*rp,carry,t1; + BIGNUM *tmp; + + bn_check_top(a); + bn_check_top(b); + + if (a->top < b->top) + { tmp=a; a=b; b=tmp; } + max=a->top; + min=b->top; + + if (bn_wexpand(r,max+1) == NULL) + return(0); + + r->top=max; + + + ap=a->d; + bp=b->d; + rp=r->d; + carry=0; + + carry=bn_add_words(rp,ap,bp,min); + rp+=min; + ap+=min; + bp+=min; + i=min; + + if (carry) + { + while (i < max) + { + i++; + t1= *(ap++); + if ((*(rp++)=(t1+1)&BN_MASK2) >= t1) + { + carry=0; + break; + } + } + if ((i >= max) && carry) + { + *(rp++)=1; + r->top++; + } + } + if (rp != ap) + { + for (; ib from a + * @param r result of subtraction + * @param a pointer to a BIGNUM + * @param b pointer to a BIGNUM + * @pre a must be larger than b + * @return 1 on success, 0 on failure + * @todo On next IRIX port check the validity of IRIX_CC_BUG. + */ +#ifdef SPLIT_BN_USUB +int BN_usub(r, a, b) +BIGNUM *r; +BIGNUM *a; +BIGNUM *b; + { + int max,min; + register BN_ULONG t1,t2,*ap,*bp,*rp; + int i,carry; +#if defined(IRIX_CC_BUG) && !defined(LINT) + int dummy; +#endif + + bn_check_top(a); + bn_check_top(b); + + /* + * check for pre-condition violation + */ + if (a->top < b->top) + { +#ifndef NO_ERR + BNerr(BN_F_BN_USUB,BN_R_ARG2_LT_ARG3); +#endif + return(0); + } + + max=a->top; + min=b->top; + if (bn_wexpand(r,max) == NULL) return(0); + + ap=a->d; + bp=b->d; + rp=r->d; + + /* + * perform the subtraction and see if we + * have a (final) carry + */ + carry=0; + for (i=0; i t2) break; + } + } + + if (rp != ap) + { +#ifdef BN_LIBRARY_SMALL + Memcpy(rp,ap,sizeof(*rp)*(max-i)); +#else /* BN_LIBRARY_SMALL */ + for (;;) + { + if (i++ >= max) break; + rp[0]=ap[0]; + if (i++ >= max) break; + rp[1]=ap[1]; + if (i++ >= max) break; + rp[2]=ap[2]; + if (i++ >= max) break; + rp[3]=ap[3]; + rp+=4; + ap+=4; + } +#endif /* BN_LIBRARY_SMALL */ + } + + r->top=max; + bn_fix_top(r); + return(1); + } +#endif + +#ifdef SPLIT_BN_SUB +int BN_sub(r, a, b) +BIGNUM *r; +BIGNUM *a; +BIGNUM *b; + { + int max; + int add=0,neg=0; + BIGNUM *tmp; + + bn_check_top(a); + bn_check_top(b); + + /* a - b a-b + * a - -b a+b + * -a - b -(a+b) + * -a - -b b-a + */ + if (a->neg) + { + if (b->neg) + { tmp=a; a=b; b=tmp; } + else + { add=1; neg=1; } + } + else + { + if (b->neg) { add=1; neg=0; } + } + + if (add) + { + if (!BN_uadd(r,a,b)) return(0); + r->neg=neg; + return(1); + } + + /* We are actually doing a - b */ + + max=(a->top > b->top)?a->top:b->top; + if (bn_wexpand(r,max) == NULL) return(0); + if (BN_ucmp(a,b) < 0) + { + if (!BN_usub(r,b,a)) return(0); + r->neg=1; + } + else + { + if (!BN_usub(r,a,b)) return(0); + r->neg=0; + } + return(1); + } +#endif + +#endif diff --git a/build/tools/acsign/bn_asm.c b/build/tools/acsign/bn_asm.c new file mode 100644 index 00000000..59326c1f --- /dev/null +++ b/build/tools/acsign/bn_asm.c @@ -0,0 +1,509 @@ +/* $Id$ */ +/* + * Copyright (C) 1998-2002 RSA Security Inc. All rights reserved. + * + * This work contains proprietary information of RSA Security. + * Distribution is limited to authorized licensees of RSA + * Security. Any unauthorized reproduction, distribution or + * modification of this work is strictly prohibited. + * + */ + +#ifndef OPT_BN_ASM + +#define BN_MUL_ADD_WORDS + +#ifndef BN_MUL_WORDS +#define BN_MUL_WORDS +#endif + +#ifndef BN_SQR_WORDS +#define BN_SQR_WORDS +#endif + +#ifndef BN_ADD_WORDS +#define BN_ADD_WORDS +#endif + +#ifndef BN_SUB_WORDS +#define BN_SUB_WORDS +#endif + +#endif + + +#include "bn_lcl.h" + +#ifdef BN_LLONG + +/* + * bn_mul_add_words + * + * for(i=0;i=0; i--) + fprintf(stderr,BN_HEX_FMT,ap[i]); + fprintf(stderr,"*"); + fprintf(stderr,BN_HEX_FMT,w); + fprintf(stderr,"+"); + for (i=num-1; i>=0; i--) + fprintf(stderr,BN_HEX_FMT,rp[i]); +#endif + + for (;;) + { + mul_add(rp[0],ap[0],w,c1); + if (--num == 0) break; + mul_add(rp[1],ap[1],w,c1); + if (--num == 0) break; + mul_add(rp[2],ap[2],w,c1); + if (--num == 0) break; + mul_add(rp[3],ap[3],w,c1); + if (--num == 0) break; + ap+=4; + rp+=4; + } + +#if defined(BN_ASM_DEBUG) && !defined(NO_FP_API) + fprintf(stderr,"-"); + fprintf(stderr,BN_HEX_FMT,c1); + for (i=nn-1; i>=0; i--) + fprintf(stderr,BN_HEX_FMT,rp[i]); + fprintf(stderr,"\n"); + } +#endif + return(c1); + } +#endif + +#ifdef BN_MUL_WORDS +BN_ULONG bn_mul_words(rp,ap,num,w) +BN_ULONG *rp,*ap; +int num; +BN_ULONG w; + { + BN_ULONG c1=0; + + bn_check_num(num); + if (num <= 0) return(c1); + +#if defined(BN_ASM_DEBUG) && !defined(NO_FP_API) + { + int i,nn=num; + + for (i=num-1; i>=0; i--) + fprintf(stderr,BN_HEX_FMT,ap[i]); + fprintf(stderr,"*"); + fprintf(stderr,BN_HEX_FMT,w); +#endif + + for (;;) + { + mul(rp[0],ap[0],w,c1); + if (--num == 0) break; + mul(rp[1],ap[1],w,c1); + if (--num == 0) break; + mul(rp[2],ap[2],w,c1); + if (--num == 0) break; + mul(rp[3],ap[3],w,c1); + if (--num == 0) break; + ap+=4; + rp+=4; + } + +#if defined(BN_ASM_DEBUG) && !defined(NO_FP_API) + fprintf(stderr,"-"); + fprintf(stderr,BN_HEX_FMT,c1); + for (i=nn-1; i>=0; i--) + fprintf(stderr,BN_HEX_FMT,rp[i]); + fprintf(stderr,"\n"); + } +#endif + return(c1); + } +#endif + +#ifdef BN_SQR_WORDS +void bn_sqr_words(r,a,n) +BN_ULONG *r,*a; +int n; + { + bn_check_num(n); + if (n <= 0) return; + +#if defined(BN_ASM_DEBUG) && !defined(NO_FP_API) + { + int i,nn=n; + + for (i=n-1; i>=0; i--) + { + fprintf(stderr,BN_HEX_FMT,a[i]); + if (i != 0) + fprintf(stderr,"^2*2^%X+",i*2*BN_BITS2); + else + fprintf(stderr,"^2"); + } +#endif + + for (;;) + { + BN_ULLONG t; + + t=(BN_ULLONG)(a[0])*(a[0]); + r[0]=Lw(t); r[1]=Hw(t); + if (--n == 0) break; + + t=(BN_ULLONG)(a[1])*(a[1]); + r[2]=Lw(t); r[3]=Hw(t); + if (--n == 0) break; + + t=(BN_ULLONG)(a[2])*(a[2]); + r[4]=Lw(t); r[5]=Hw(t); + if (--n == 0) break; + + t=(BN_ULLONG)(a[3])*(a[3]); + r[6]=Lw(t); r[7]=Hw(t); + if (--n == 0) break; + + a+=4; + r+=8; + } +#if defined(BN_ASM_DEBUG) && !defined(NO_FP_API) + + fprintf(stderr,"-"); + for (i=nn+nn-1; i>=0; i--) + fprintf(stderr,BN_HEX_FMT,r[i]); + fprintf(stderr,"\n"); + } +#endif + } +#endif + +#else + +#ifdef BN_MUL_ADD_WORDS + +BN_ULONG bn_mul_add_words(rp,ap,num,w) +BN_ULONG *rp,*ap; +int num; +BN_ULONG w; + { + BN_ULONG c=0; + BN_ULONG bl,bh,b_hl; + + bn_check_num(num); + if (num <= 0) return((BN_ULONG)0); + + bl=LBITS(w); + bh=HBITS(w); + b_hl=bh-bl; +/* +{ int i,nnum=num; +BN_ULONG *rrp=rp; +for (i=num-1; i>=0; i--) printf("%02X",rp[i]); +printf("+"); +for (i=num-1; i>=0; i--) printf("%02X",ap[i]); +printf("*%02X - ",w); +*/ + for (;;) + { + mul_add(rp[0],ap[0],bl,bh,b_hl,c); + if (--num == 0) break; + mul_add(rp[1],ap[1],bl,bh,b_hl,c); + if (--num == 0) break; + mul_add(rp[2],ap[2],bl,bh,b_hl,c); + if (--num == 0) break; + mul_add(rp[3],ap[3],bl,bh,b_hl,c); + if (--num == 0) break; + ap+=4; + rp+=4; + } + +/* +printf("%02X",c); +for (i=nnum-1; i>=0; i--) printf("%02X",rrp[i]); +printf("\n"); +} +*/ + return(c); + } + +#endif + +#ifdef BN_MUL_WORDS +BN_ULONG bn_mul_words(rp,ap,num,w) +BN_ULONG *rp,*ap; +int num; +BN_ULONG w; + { + BN_ULONG carry=0; + BN_ULONG bl,bh; + + bn_check_num(num); + if (num <= 0) return((BN_ULONG)0); + + bl=LBITS(w); + bh=HBITS(w); + + for (;;) + { + mul(rp[0],ap[0],bl,bh,carry); + if (--num == 0) break; + mul(rp[1],ap[1],bl,bh,carry); + if (--num == 0) break; + mul(rp[2],ap[2],bl,bh,carry); + if (--num == 0) break; + mul(rp[3],ap[3],bl,bh,carry); + if (--num == 0) break; + ap+=4; + rp+=4; + } + return(carry); + } +#endif + +#ifdef BN_SQR_WORDS +void bn_sqr_words(r,a,n) +BN_ULONG *r,*a; +int n; + { + bn_check_num(n); + if (n <= 0) return; + for (;;) + { + sqr64(r[0],r[1],a[0]); + if (--n == 0) break; + + sqr64(r[2],r[3],a[1]); + if (--n == 0) break; + + sqr64(r[4],r[5],a[2]); + if (--n == 0) break; + + sqr64(r[6],r[7],a[3]); + if (--n == 0) break; + + a+=4; + r+=8; + } + } +#endif + +#endif + +#ifdef BN_ADD_WORDS +#ifdef BN_LLONG +BN_ULONG bn_add_words(r,a,b,n) +BN_ULONG *r,*a,*b; +int n; + { + BN_ULLONG ll=0; + + bn_check_num(n); + if (n <= 0) return((BN_ULONG)0); + +#if defined(BN_ASM_DEBUG) && !defined(NO_FP_API) + { + int i,nn=n; + + for (i=n-1; i>=0; i--) + fprintf(stderr,BN_HEX_FMT,a[i]); + fprintf(stderr,"+"); + for (i=n-1; i>=0; i--) + fprintf(stderr,BN_HEX_FMT,b[i]); +#endif + + for (;;) + { + ll+=(BN_ULLONG)a[0]+b[0]; + r[0]=(BN_ULONG)ll&BN_MASK2; + ll>>=BN_BITS2; + if (--n <= 0) break; + + ll+=(BN_ULLONG)a[1]+b[1]; + r[1]=(BN_ULONG)ll&BN_MASK2; + ll>>=BN_BITS2; + if (--n <= 0) break; + + ll+=(BN_ULLONG)a[2]+b[2]; + r[2]=(BN_ULONG)ll&BN_MASK2; + ll>>=BN_BITS2; + if (--n <= 0) break; + + ll+=(BN_ULLONG)a[3]+b[3]; + r[3]=(BN_ULONG)ll&BN_MASK2; + ll>>=BN_BITS2; + if (--n <= 0) break; + + a+=4; + b+=4; + r+=4; + } +#if defined(BN_ASM_DEBUG) && !defined(NO_FP_API) + fprintf(stderr,"-"); + fprintf(stderr,BN_HEX_FMT,(BN_ULONG)ll); + for (i=nn-1; i>=0; i--) + fprintf(stderr,BN_HEX_FMT,r[i]); + fprintf(stderr,"\n"); + } +#endif + return((BN_ULONG)ll); + } +#else +BN_ULONG bn_add_words(r,a,b,n) +BN_ULONG *r,*a,*b; +int n; + { + BN_ULONG c,l,t; + + bn_check_num(n); + if (n <= 0) return((BN_ULONG)0); + + c=0; + for (;;) + { + t=a[0]; + t=(t+c)&BN_MASK2; + c=(t < c); + l=(t+b[0])&BN_MASK2; + c+=(l < t); + r[0]=l; + if (--n <= 0) break; + + t=a[1]; + t=(t+c)&BN_MASK2; + c=(t < c); + l=(t+b[1])&BN_MASK2; + c+=(l < t); + r[1]=l; + if (--n <= 0) break; + + t=a[2]; + t=(t+c)&BN_MASK2; + c=(t < c); + l=(t+b[2])&BN_MASK2; + c+=(l < t); + r[2]=l; + if (--n <= 0) break; + + t=a[3]; + t=(t+c)&BN_MASK2; + c=(t < c); + l=(t+b[3])&BN_MASK2; + c+=(l < t); + r[3]=l; + if (--n <= 0) break; + + a+=4; + b+=4; + r+=4; + } + + return((BN_ULONG)c); + } +#endif +#endif + +#ifdef BN_SUB_WORDS +BN_ULONG bn_sub_words(r,a,b,n) +BN_ULONG *r,*a,*b; +int n; + { + BN_ULONG c,t1,t2; + + bn_check_num(n); + if (n <= 0) return((BN_ULONG)0); + +#if defined(BN_ASM_DEBUG) && !defined(NO_FP_API) + { + int i,nn=n; + + for (i=n-1; i>=0; i--) + fprintf(stderr,BN_HEX_FMT,a[i]); + fprintf(stderr,"-"); + for (i=n-1; i>=0; i--) + fprintf(stderr,BN_HEX_FMT,b[i]); +#endif + + c=0; + for (;;) + { + t1=a[0]; t2=b[0]; + r[0]=(t1-t2-c)&BN_MASK2; + if (t1 != t2) c=(t1 < t2); + if (--n <= 0) break; + + t1=a[1]; t2=b[1]; + r[1]=(t1-t2-c)&BN_MASK2; + if (t1 != t2) c=(t1 < t2); + if (--n <= 0) break; + + t1=a[2]; t2=b[2]; + r[2]=(t1-t2-c)&BN_MASK2; + if (t1 != t2) c=(t1 < t2); + if (--n <= 0) break; + + t1=a[3]; t2=b[3]; + r[3]=(t1-t2-c)&BN_MASK2; + if (t1 != t2) c=(t1 < t2); + if (--n <= 0) break; + + a+=4; + b+=4; + r+=4; + } + +#if defined(BN_ASM_DEBUG) && !defined(NO_FP_API) + fprintf(stderr,"- "); + if (c) fprintf(stderr,"-"); + for (i=nn-1; i>=0; i--) + fprintf(stderr,BN_HEX_FMT,r[i]); + fprintf(stderr,"\n"); + } +#endif + return(c); + } +#endif + diff --git a/build/tools/acsign/bn_comba.c b/build/tools/acsign/bn_comba.c new file mode 100644 index 00000000..61dbc937 --- /dev/null +++ b/build/tools/acsign/bn_comba.c @@ -0,0 +1,434 @@ +/* $Id$ */ +/* + * Copyright (C) 1998-2002 RSA Security Inc. All rights reserved. + * + * This work contains proprietary information of RSA Security. + * Distribution is limited to authorized licensees of RSA + * Security. Any unauthorized reproduction, distribution or + * modification of this work is strictly prohibited. + * + */ + +#include "bn_lcl.h" + +#if !(defined(NO_SPLIT) && defined(SPLIT_FILE)) + +#ifdef NO_SPLIT +#define SPLIT_BN_MUL_COMBA8 +#define SPLIT_BN_MUL_COMBA4 +#define SPLIT_BN_SQR_COMBA8 +#define SPLIT_BN_SQR_COMBA4 +#endif /* NO_SPLIT */ + +#ifndef OPT_BN_ASM +/* #if 1 */ + +#ifdef BN_LLONG +#define mul_add_c(a,b,c0,c1,c2) \ + t=(BN_ULLONG)a*b; \ + t1=(BN_ULONG)Lw(t); \ + t2=(BN_ULONG)Hw(t); \ + c0=(c0+t1)&BN_MASK2; if ((c0) < t1) t2++; \ + c1=(c1+t2)&BN_MASK2; if ((c1) < t2) c2++; + +#define mul_add_c2(a,b,c0,c1,c2) \ + t=(BN_ULLONG)a*b; \ + tt=(t+t)&BN_MASK; \ + if (tt < t) c2++; \ + t1=(BN_ULONG)Lw(tt); \ + t2=(BN_ULONG)Hw(tt); \ + c0=(c0+t1)&BN_MASK2; \ + if ((c0 < t1) && (((++t2)&BN_MASK2) == 0)) c2++; \ + c1=(c1+t2)&BN_MASK2; if ((c1) < t2) c2++; + +#define sqr_add_c(a,i,c0,c1,c2) \ + t=(BN_ULLONG)a[i]*a[i]; \ + t1=(BN_ULONG)Lw(t); \ + t2=(BN_ULONG)Hw(t); \ + c0=(c0+t1)&BN_MASK2; if ((c0) < t1) t2++; \ + c1=(c1+t2)&BN_MASK2; if ((c1) < t2) c2++; + +#define sqr_add_c2(a,i,j,c0,c1,c2) \ + mul_add_c2((a)[i],(a)[j],c0,c1,c2) +#else +#define mul_add_c(a,b,c0,c1,c2) \ + t1=LBITS(a); t2=HBITS(a); \ + bl=LBITS(b); bh=HBITS(b); \ + mul64(t1,t2,bl,bh); \ + c0=(c0+t1)&BN_MASK2; if ((c0) < t1) t2++; \ + c1=(c1+t2)&BN_MASK2; if ((c1) < t2) c2++; + +#define mul_add_c2(a,b,c0,c1,c2) \ + t1=LBITS(a); t2=HBITS(a); \ + bl=LBITS(b); bh=HBITS(b); \ + mul64(t1,t2,bl,bh); \ + if (t2 & BN_TBIT) c2++; \ + t2=(t2+t2)&BN_MASK2; \ + if (t1 & BN_TBIT) t2++; \ + t1=(t1+t1)&BN_MASK2; \ + c0=(c0+t1)&BN_MASK2; \ + if ((c0 < t1) && (((++t2)&BN_MASK2) == 0)) c2++; \ + c1=(c1+t2)&BN_MASK2; if ((c1) < t2) c2++; + +#define sqr_add_c(a,i,c0,c1,c2) \ + sqr64(t1,t2,(a)[i]); \ + c0=(c0+t1)&BN_MASK2; if ((c0) < t1) t2++; \ + c1=(c1+t2)&BN_MASK2; if ((c1) < t2) c2++; + +#define sqr_add_c2(a,i,j,c0,c1,c2) \ + mul_add_c2((a)[i],(a)[j],c0,c1,c2) +#endif + +#if defined(BN_MUL_COMBA) + +#ifdef SPLIT_BN_MUL_COMBA8 +#undef bn_mul_comba8 +void bn_mul_comba8(r,a,b) +BN_ULONG *r,*a,*b; + { +#ifdef BN_LLONG + BN_ULLONG t; +#else + BN_ULONG bl,bh; +#endif + BN_ULONG t1,t2; + BN_ULONG c1,c2,c3; + + c1=0; + c2=0; + c3=0; + mul_add_c(a[0],b[0],c1,c2,c3); + r[0]=c1; + c1=0; + mul_add_c(a[0],b[1],c2,c3,c1); + mul_add_c(a[1],b[0],c2,c3,c1); + r[1]=c2; + c2=0; + mul_add_c(a[2],b[0],c3,c1,c2); + mul_add_c(a[1],b[1],c3,c1,c2); + mul_add_c(a[0],b[2],c3,c1,c2); + r[2]=c3; + c3=0; + mul_add_c(a[0],b[3],c1,c2,c3); + mul_add_c(a[1],b[2],c1,c2,c3); + mul_add_c(a[2],b[1],c1,c2,c3); + mul_add_c(a[3],b[0],c1,c2,c3); + r[3]=c1; + c1=0; + mul_add_c(a[4],b[0],c2,c3,c1); + mul_add_c(a[3],b[1],c2,c3,c1); + mul_add_c(a[2],b[2],c2,c3,c1); + mul_add_c(a[1],b[3],c2,c3,c1); + mul_add_c(a[0],b[4],c2,c3,c1); + r[4]=c2; + c2=0; + mul_add_c(a[0],b[5],c3,c1,c2); + mul_add_c(a[1],b[4],c3,c1,c2); + mul_add_c(a[2],b[3],c3,c1,c2); + mul_add_c(a[3],b[2],c3,c1,c2); + mul_add_c(a[4],b[1],c3,c1,c2); + mul_add_c(a[5],b[0],c3,c1,c2); + r[5]=c3; + c3=0; + mul_add_c(a[6],b[0],c1,c2,c3); + mul_add_c(a[5],b[1],c1,c2,c3); + mul_add_c(a[4],b[2],c1,c2,c3); + mul_add_c(a[3],b[3],c1,c2,c3); + mul_add_c(a[2],b[4],c1,c2,c3); + mul_add_c(a[1],b[5],c1,c2,c3); + mul_add_c(a[0],b[6],c1,c2,c3); + r[6]=c1; + c1=0; + mul_add_c(a[0],b[7],c2,c3,c1); + mul_add_c(a[1],b[6],c2,c3,c1); + mul_add_c(a[2],b[5],c2,c3,c1); + mul_add_c(a[3],b[4],c2,c3,c1); + mul_add_c(a[4],b[3],c2,c3,c1); + mul_add_c(a[5],b[2],c2,c3,c1); + mul_add_c(a[6],b[1],c2,c3,c1); + mul_add_c(a[7],b[0],c2,c3,c1); + r[7]=c2; + c2=0; + mul_add_c(a[7],b[1],c3,c1,c2); + mul_add_c(a[6],b[2],c3,c1,c2); + mul_add_c(a[5],b[3],c3,c1,c2); + mul_add_c(a[4],b[4],c3,c1,c2); + mul_add_c(a[3],b[5],c3,c1,c2); + mul_add_c(a[2],b[6],c3,c1,c2); + mul_add_c(a[1],b[7],c3,c1,c2); + r[8]=c3; + c3=0; + mul_add_c(a[2],b[7],c1,c2,c3); + mul_add_c(a[3],b[6],c1,c2,c3); + mul_add_c(a[4],b[5],c1,c2,c3); + mul_add_c(a[5],b[4],c1,c2,c3); + mul_add_c(a[6],b[3],c1,c2,c3); + mul_add_c(a[7],b[2],c1,c2,c3); + r[9]=c1; + c1=0; + mul_add_c(a[7],b[3],c2,c3,c1); + mul_add_c(a[6],b[4],c2,c3,c1); + mul_add_c(a[5],b[5],c2,c3,c1); + mul_add_c(a[4],b[6],c2,c3,c1); + mul_add_c(a[3],b[7],c2,c3,c1); + r[10]=c2; + c2=0; + mul_add_c(a[4],b[7],c3,c1,c2); + mul_add_c(a[5],b[6],c3,c1,c2); + mul_add_c(a[6],b[5],c3,c1,c2); + mul_add_c(a[7],b[4],c3,c1,c2); + r[11]=c3; + c3=0; + mul_add_c(a[7],b[5],c1,c2,c3); + mul_add_c(a[6],b[6],c1,c2,c3); + mul_add_c(a[5],b[7],c1,c2,c3); + r[12]=c1; + c1=0; + mul_add_c(a[6],b[7],c2,c3,c1); + mul_add_c(a[7],b[6],c2,c3,c1); + r[13]=c2; + c2=0; + mul_add_c(a[7],b[7],c3,c1,c2); + r[14]=c3; + r[15]=c1; + } +#endif + +#ifdef SPLIT_BN_MUL_COMBA4 +#undef bn_mul_comba4 +void bn_mul_comba4(r,a,b) +BN_ULONG *r,*a,*b; + { +#ifdef BN_LLONG + BN_ULLONG t; +#else + BN_ULONG bl,bh; +#endif + BN_ULONG t1,t2; + BN_ULONG c1,c2,c3; + + c1=0; + c2=0; + c3=0; + mul_add_c(a[0],b[0],c1,c2,c3); + r[0]=c1; + c1=0; + mul_add_c(a[0],b[1],c2,c3,c1); + mul_add_c(a[1],b[0],c2,c3,c1); + r[1]=c2; + c2=0; + mul_add_c(a[2],b[0],c3,c1,c2); + mul_add_c(a[1],b[1],c3,c1,c2); + mul_add_c(a[0],b[2],c3,c1,c2); + r[2]=c3; + c3=0; + mul_add_c(a[0],b[3],c1,c2,c3); + mul_add_c(a[1],b[2],c1,c2,c3); + mul_add_c(a[2],b[1],c1,c2,c3); + mul_add_c(a[3],b[0],c1,c2,c3); + r[3]=c1; + c1=0; + mul_add_c(a[3],b[1],c2,c3,c1); + mul_add_c(a[2],b[2],c2,c3,c1); + mul_add_c(a[1],b[3],c2,c3,c1); + r[4]=c2; + c2=0; + mul_add_c(a[2],b[3],c3,c1,c2); + mul_add_c(a[3],b[2],c3,c1,c2); + r[5]=c3; + c3=0; + mul_add_c(a[3],b[3],c1,c2,c3); + r[6]=c1; + r[7]=c2; + } +#endif + +#else + +#ifdef SPLIT_BN_MUL_COMBA4 +#undef bn_mul_comba4 +void bn_mul_comba4(r,a,b) +BN_ULONG *r,*a,*b; + { + r[4]=bn_mul_words( &(r[0]),a,4,b[0]); + r[5]=bn_mul_add_words(&(r[1]),a,4,b[1]); + r[6]=bn_mul_add_words(&(r[2]),a,4,b[2]); + r[7]=bn_mul_add_words(&(r[3]),a,4,b[3]); + } +#endif + +#ifdef SPLIT_BN_MUL_COMBA8 +#undef bn_mul_comba8 +void bn_mul_comba8(r,a,b) +BN_ULONG *r,*a,*b; + { + r[ 8]=bn_mul_words( &(r[0]),a,8,b[0]); + r[ 9]=bn_mul_add_words(&(r[1]),a,8,b[1]); + r[10]=bn_mul_add_words(&(r[2]),a,8,b[2]); + r[11]=bn_mul_add_words(&(r[3]),a,8,b[3]); + r[12]=bn_mul_add_words(&(r[4]),a,8,b[4]); + r[13]=bn_mul_add_words(&(r[5]),a,8,b[5]); + r[14]=bn_mul_add_words(&(r[6]),a,8,b[6]); + r[15]=bn_mul_add_words(&(r[7]),a,8,b[7]); + } +#endif + +#endif /* BN_MUL_COMBA */ + +#ifdef BN_SQR_COMBA + +#ifdef SPLIT_BN_SQR_COMBA8 +#undef bn_sqr_comba8 +void bn_sqr_comba8(r,a) +BN_ULONG *r,*a; + { +#ifdef BN_LLONG + BN_ULLONG t,tt; +#else + BN_ULONG bl,bh; +#endif + BN_ULONG t1,t2; + BN_ULONG c1,c2,c3; + + c1=0; + c2=0; + c3=0; + sqr_add_c(a,0,c1,c2,c3); + r[0]=c1; + c1=0; + sqr_add_c2(a,1,0,c2,c3,c1); + r[1]=c2; + c2=0; + sqr_add_c(a,1,c3,c1,c2); + sqr_add_c2(a,2,0,c3,c1,c2); + r[2]=c3; + c3=0; + sqr_add_c2(a,3,0,c1,c2,c3); + sqr_add_c2(a,2,1,c1,c2,c3); + r[3]=c1; + c1=0; + sqr_add_c(a,2,c2,c3,c1); + sqr_add_c2(a,3,1,c2,c3,c1); + sqr_add_c2(a,4,0,c2,c3,c1); + r[4]=c2; + c2=0; + sqr_add_c2(a,5,0,c3,c1,c2); + sqr_add_c2(a,4,1,c3,c1,c2); + sqr_add_c2(a,3,2,c3,c1,c2); + r[5]=c3; + c3=0; + sqr_add_c(a,3,c1,c2,c3); + sqr_add_c2(a,4,2,c1,c2,c3); + sqr_add_c2(a,5,1,c1,c2,c3); + sqr_add_c2(a,6,0,c1,c2,c3); + r[6]=c1; + c1=0; + sqr_add_c2(a,7,0,c2,c3,c1); + sqr_add_c2(a,6,1,c2,c3,c1); + sqr_add_c2(a,5,2,c2,c3,c1); + sqr_add_c2(a,4,3,c2,c3,c1); + r[7]=c2; + c2=0; + sqr_add_c(a,4,c3,c1,c2); + sqr_add_c2(a,5,3,c3,c1,c2); + sqr_add_c2(a,6,2,c3,c1,c2); + sqr_add_c2(a,7,1,c3,c1,c2); + r[8]=c3; + c3=0; + sqr_add_c2(a,7,2,c1,c2,c3); + sqr_add_c2(a,6,3,c1,c2,c3); + sqr_add_c2(a,5,4,c1,c2,c3); + r[9]=c1; + c1=0; + sqr_add_c(a,5,c2,c3,c1); + sqr_add_c2(a,6,4,c2,c3,c1); + sqr_add_c2(a,7,3,c2,c3,c1); + r[10]=c2; + c2=0; + sqr_add_c2(a,7,4,c3,c1,c2); + sqr_add_c2(a,6,5,c3,c1,c2); + r[11]=c3; + c3=0; + sqr_add_c(a,6,c1,c2,c3); + sqr_add_c2(a,7,5,c1,c2,c3); + r[12]=c1; + c1=0; + sqr_add_c2(a,7,6,c2,c3,c1); + r[13]=c2; + c2=0; + sqr_add_c(a,7,c3,c1,c2); + r[14]=c3; + r[15]=c1; + } +#endif + +#ifdef SPLIT_BN_SQR_COMBA4 +#undef bn_sqr_comba4 +void bn_sqr_comba4(r,a) +BN_ULONG *r,*a; + { +#ifdef BN_LLONG + BN_ULLONG t,tt; +#else + BN_ULONG bl,bh; +#endif + BN_ULONG t1,t2; + BN_ULONG c1,c2,c3; + + c1=0; + c2=0; + c3=0; + sqr_add_c(a,0,c1,c2,c3); + r[0]=c1; + c1=0; + sqr_add_c2(a,1,0,c2,c3,c1); + r[1]=c2; + c2=0; + sqr_add_c(a,1,c3,c1,c2); + sqr_add_c2(a,2,0,c3,c1,c2); + r[2]=c3; + c3=0; + sqr_add_c2(a,3,0,c1,c2,c3); + sqr_add_c2(a,2,1,c1,c2,c3); + r[3]=c1; + c1=0; + sqr_add_c(a,2,c2,c3,c1); + sqr_add_c2(a,3,1,c2,c3,c1); + r[4]=c2; + c2=0; + sqr_add_c2(a,3,2,c3,c1,c2); + r[5]=c3; + c3=0; + sqr_add_c(a,3,c1,c2,c3); + r[6]=c1; + r[7]=c2; + } +#endif +#else + +#ifdef SPLIT_BN_SQR_COMBA4 +/* hmm... is it faster just to do a multiply? */ +#undef bn_sqr_comba4 +void bn_sqr_comba4(r,a) +BN_ULONG *r,*a; + { + BN_ULONG t[8]; + bn_sqr_normal(r,a,4,t); + } +#endif + +#ifdef SPLIT_BN_SQR_COMBA8 +#undef bn_sqr_comba8 +void bn_sqr_comba8(r,a) +BN_ULONG *r,*a; + { + BN_ULONG t[16]; + bn_sqr_normal(r,a,8,t); + } +#endif + +#endif + +#endif /* OPT_BN_ASM */ + +#endif diff --git a/build/tools/acsign/bn_div.c b/build/tools/acsign/bn_div.c new file mode 100644 index 00000000..d2c12575 --- /dev/null +++ b/build/tools/acsign/bn_div.c @@ -0,0 +1,365 @@ +/* $Id$ */ +/* + * Copyright (C) 1998-2002 RSA Security Inc. All rights reserved. + * + * This work contains proprietary information of RSA Security. + * Distribution is limited to authorized licensees of RSA + * Security. Any unauthorized reproduction, distribution or + * modification of this work is strictly prohibited. + * + */ +/** + * @file bn_div.c + * @brief Division and modulus functions + */ +#include "bn_lcl.h" + +#if !(defined(NO_SPLIT) && defined(SPLIT_FILE)) + +#ifdef NO_SPLIT +#define SPLIT_BN_DIV +#define SPLIT_BN_MOD +#endif /* NO_SPLIT */ + +#ifdef SPLIT_BN_DIV +/* The old slow way */ +#ifdef OLD_BN_DIVISION +int BN_div(dv, rem, m, d,ctx) +BIGNUM *dv; +BIGNUM *rem; +BIGNUM *m; +BIGNUM *d; +BN_CTX *ctx; + { + int i,nm,nd; + BIGNUM *D; + + bn_check_top(m); + bn_check_top(d); + if (BN_is_zero(d)) + { +#ifndef NO_ERR + BNerr(BN_F_BN_DIV,BN_R_DIV_BY_ZERO); +#endif + return(0); + } + + if (BN_ucmp(m,d) < 0) + { + if (rem != NULL) + { if (BN_copy(rem,m) == NULL) return(0); } + if (dv != NULL) BN_zero(dv); + return(1); + } + + D= &(ctx->bn[ctx->tos]); + if (dv == NULL) dv= &(ctx->bn[ctx->tos+1]); + if (rem == NULL) rem= &(ctx->bn[ctx->tos+2]); + + nd=BN_num_bits(d); + nm=BN_num_bits(m); + if (BN_copy(D,d) == NULL) return(0); + if (BN_copy(rem,m) == NULL) return(0); + + /* The next 2 are needed so we can do a dv->d[0]|=1 later + * since BN_lshift1 will only work once there is a value + */ + BN_zero(dv); + bn_wexpand(dv,1); + dv->top=1; + + if (!BN_lshift(D,D,nm-nd)) return(0); + for (i=nm-nd; i>=0; i--) + { + if (!BN_lshift1(dv,dv)) return(0); + if (BN_ucmp(rem,D) >= 0) + { + dv->d[0]|=1; + if (!BN_usub(rem,rem,D)) return(0); + } +/* CAN IMPROVE (and have now :=) */ + if (!BN_rshift1(D,D)) return(0); + } + rem->neg=BN_is_zero(rem)?0:m->neg; + dv->neg=m->neg^d->neg; + return(1); + } + +#else +/** + * Performs Big number division + * + * @param dv [In] division + * @param rm [Out] remainder + * @param num [In] number + * @param divisor [In] divisor + * @param ctx [In] Temporary data storage + * + * @todo add more comments to this function + */ +int BN_div(BIGNUM *dv, BIGNUM *rm, BIGNUM *num, BIGNUM *divisor, BN_CTX *ctx) + { + int norm_shift,i,j,loop; + BIGNUM *tmp,wnum,*snum,*sdiv,*res; + BN_ULONG *resp,*wnump; + BN_ULONG d0,d1; + int num_n,div_n; + + bn_check_top(num); + bn_check_top(divisor); + + if (BN_is_zero(divisor)) + { +#ifndef NO_ERR + BNerr(BN_F_BN_DIV,BN_R_DIV_BY_ZERO); +#endif + return(0); + } + + if (BN_ucmp(num,divisor) < 0) + { + if (rm != NULL) + { if (BN_copy(rm,num) == NULL) return(0); } + if (dv != NULL) (void)BN_zero(dv); + return(1); + } + + tmp= &(ctx->bn[ctx->tos]); + tmp->neg=0; + snum= &(ctx->bn[ctx->tos+1]); + sdiv= &(ctx->bn[ctx->tos+2]); + if (dv == NULL) + res= &(ctx->bn[ctx->tos+3]); + else res=dv; + + /* First we normalise the numbers */ + norm_shift=BN_BITS2-((BN_num_bits(divisor))%BN_BITS2); + if (!BN_lshift(sdiv,divisor,norm_shift)) return(0); + sdiv->neg=0; + norm_shift+=BN_BITS2; + if (!BN_lshift(snum,num,norm_shift)) return(0); + snum->neg=0; + div_n=sdiv->top; + num_n=snum->top; + loop=num_n-div_n; + + /* Lets setup a 'window' into snum + * This is the part that corresponds to the current + * 'area' being divided */ + BN_init(&wnum); + wnum.d= &(snum->d[loop]); + wnum.top= div_n; + wnum.max= snum->max+1; /* a bit of a lie */ + + /* Get the top 2 words of sdiv */ + /* i=sdiv->top; */ + d0=sdiv->d[div_n-1]; + d1=(div_n == 1)?0:sdiv->d[div_n-2]; + + /* pointer to the 'top' of snum */ + wnump= &(snum->d[num_n-1]); + + /* Setup to 'res' */ + if (!bn_wexpand(res,(loop+1))) goto err; + res->neg= (num->neg^divisor->neg); + res->top=loop; + resp= &(res->d[loop-1]); + + /* space for temp */ + if (!bn_wexpand(tmp,(div_n+1))) goto err; + + if (BN_ucmp(&wnum,sdiv) >= 0) + { + if (!BN_usub(&wnum,&wnum,sdiv)) goto err; + *resp=1; + res->d[res->top-1]=1; + } + else + res->top--; + resp--; + + for (i=0; i>BN_BITS2) || + (t2 <= ((BN_ULLONG)(rem< t1l) t3h++; + t3h=(t1h-t3h)&BN_MASK2; + + /*if ((t3>>BN_BITS2) || + (t2 <= ((t3<d,sdiv->d,div_n,q); + tmp->d[div_n]=l0; + for (j=div_n+1; j>0; j--) + if (tmp->d[j-1]) break; + tmp->top=j; + + j=wnum.top; +#ifdef BN_DEBUG + /* Sometimes this is 0 now (wnum.top is not). + * We need to look at this some time, I am quite + * sure it has no affect. + */ + bn_fix_top(&wnum); +#endif + (void)BN_sub(&wnum,&wnum,tmp); + + snum->top=snum->top+wnum.top-j; + + if (wnum.neg) + { + q--; + j=wnum.top; + (void)BN_add(&wnum,&wnum,sdiv); + snum->top+=wnum.top-j; + } + *(resp--)=q; + wnump--; + } + bn_fix_top(snum); + if (rm != NULL) + { + i=num->neg; /* just in case num == rm */ + if (BN_rshift(rm,snum,norm_shift) == 0) + return(0); + rm->neg=i; + } + return(1); +err: + return(0); + } +#endif +#endif + +#ifdef SPLIT_BN_MOD +/** + * Calculate the remainder where rem = m mod d + * + * @param rem [Out] Modulus result + * @param m [In] Base value + * @param d [In] Divisor + * @param ctx [In] BN_CTX for data space + * + * @pre variables are initialised and valid + * @post rem contained modulus + * + * @retval 1 success + * @retval 0 failure + * + * @note when BN_LIBRARY_SMALL define uses following algorithm + * otherwise calls through to BN_div + * + * @note m < d rem = m + * (length m = length of d) and (m > d) , rem = m - d + * alg otherwise + * let rem = m + * loop while rem > d + * let dv = shift left d, n bits to = length rem + * if dv > rem + * let dv = shift left d, n - 1 bits ( = length rem - 1) + * end if + * let rem = rem - dv; + * end loop + * + * @relates BN_div + */ +int BN_mod(BIGNUM *rem, BIGNUM *m, BIGNUM *d, BN_CTX *ctx) +{ +#ifdef BN_LIBRARY_SMALL + int nm,nd; + BIGNUM *dv, *dv2; + + /* if m < d , mod = m */ + if (BN_ucmp(m,d) < 0) + return((BN_copy(rem,m) == NULL)?0:1); + + dv= &(ctx->bn[ctx->tos]); + dv2 = &(ctx->bn[ctx->tos+1]); + + nm=BN_num_bits(m); + nd=BN_num_bits(d); + + /* if bits m = bits d and m >= d (from above) + * rem = m - d + */ + if(nm == nd) + { + BN_usub(rem, m, d); + goto end; + } + + if (BN_copy(rem, m) == NULL) + { + return 0; + } + + /* while the rem > d */ + while(BN_ucmp(rem,d) > 0) + { + nm=BN_num_bits(rem); + if(!BN_lshift(dv2,d,nm-nd)) + { + return(0); + } + + if(BN_ucmp(dv2, rem) >0) + { + if(!BN_lshift(dv2,d,nm-nd-1)) + { + return(0); + } + } + + BN_usub(rem,rem,dv2); + } +end: + return(1); +#else + return(BN_div(NULL,rem,m,d,ctx)); +#endif +} +#endif + +#endif diff --git a/build/tools/acsign/bn_ex_str.c b/build/tools/acsign/bn_ex_str.c new file mode 100644 index 00000000..dc40f5c0 --- /dev/null +++ b/build/tools/acsign/bn_ex_str.c @@ -0,0 +1,437 @@ +/* $Id$ */ +/* + * Copyright (C) 1998-2002 RSA Security Inc. All rights reserved. + * + * This work contains proprietary information of RSA Security. + * Distribution is limited to authorized licensees of RSA + * Security. Any unauthorized reproduction, distribution or + * modification of this work is strictly prohibited. + * + */ +#include "bn_lcl.h" + +const static unsigned char p2 []={0,8,1,1,1,1,1,0,0,0}; +const static unsigned char p4 []={0,8,1,1,1,4,1,0,0,0}; +const static unsigned char p16[]={0,8,1,1,1,16,1,0,0,0}; + +/* The following defines allow for redefinition of the window size + * of the exponent string at compile time, this affects the size + * of temporary data required in montgomery operations. + * Larger window sizes have more memory and are slightly faster + */ +#ifndef MAX_WIN_SIZE +#define MAX_WIN_SIZE 5 +#endif + +#if (MAX_WIN_SIZE == 6) +#define MAX_NUM_SIZE 16 +#endif + +#if (MAX_WIN_SIZE == 5) +#define MAX_NUM_SIZE 16 +#endif + +#if (MAX_WIN_SIZE == 4) +#define MAX_NUM_SIZE 8 +#endif + +#if (MAX_WIN_SIZE == 3) +#define MAX_NUM_SIZE 4 +#endif + +#ifndef MAX_NUM_SIZE +#define MAX_NUM_SIZE 16 +#endif + +/* This table is used to calculate how far to shift a window to find + * the next 1 bit within the window, for a given window size + * Comment next to each value represents window size, value of the window + * and the number of shifts to find the next 1 bit. + * where the value of the window is 0, the shift is the size of the window + * and thus may not necessary yield a 1 bit, but refreshes the window + */ +const static unsigned char shift[64]= + {6, /* window 6, bits 000000, shift 6 */ + 0, /* 6, bits 000001, shift 0 */ + 1, /* 6, bits 000010, shift 1 */ + 0, /* 6, bits 000011, shift 0 */ + 2, /* 6, bits 000100, shift 2 */ + 0, /* 6, bits 000101, shift 0 */ + 1, /* 6, bits 000110, shift 1 */ + 0, /* 6, bits 000111, shift 0 */ + 3, /* 6, bits 001000, shift 3 */ + 0, /* 6, bits 001001, shift 0 */ + 1, /* 6, bits 001010, shift 1 */ + 0, /* 6, bits 001011, shift 0 */ + 2, /* 6, bits 001100, shift 2 */ + 0, /* 6, bits 001101, shift 0 */ + 1, /* 6, bits 001110, shift 1 */ + 0, /* 6, bits 001111, shift 0 */ + 4, /* 6, bits 010000, shift 4 */ + 0, /* 6, bits 010001, shift 0 */ + 1, /* 6, bits 010010, shift 1 */ + 0, /* 6, bits 010011, shift 0 */ + 2, /* 6, bits 010100, shift 2 */ + 0, /* 6, bits 010101, shift 0 */ + 1, /* 6, bits 010110, shift 1 */ + 0, /* 6, bits 010111, shift 0 */ + 3, /* 6, bits 011000, shift 3 */ + 0, /* 6, bits 011001, shift 0 */ + 1, /* 6, bits 011010, shift 1 */ + 0, /* 6, bits 011011, shift 0 */ + 2, /* 6, bits 011100, shift 2 */ + 0, /* 6, bits 011101, shift 0 */ + 1, /* 6, bits 011110, shift 1 */ + 0, /* 6, bits 011111, shift 0 */ + 5, /* 6, bits 100000, shift 5 */ + /* also window 5, bits 00000, shift 5 */ + 0, /* 5 ,bits 00001, shift 0 */ + 1, /* 5 ,bits 00010, shift 1 */ + 0, /* 5 ,bits 00011, shift 0 */ + 2, /* 5 ,bits 00100, shift 2 */ + 0, /* 5 ,bits 00101, shift 0 */ + 1, /* 5 ,bits 00110, shift 1 */ + 0, /* 5 ,bits 00111, shift 0 */ + 3, /* 5 ,bits 01000, shift 3 */ + 0, /* 5 ,bits 01001, shift 0 */ + 1, /* 5 ,bits 01010, shift 1 */ + 0, /* 5 ,bits 01011, shift 0 */ + 2, /* 5 ,bits 01100, shift 2 */ + 0, /* 5 ,bits 01101, shift 0 */ + 1, /* 5 ,bits 01110, shift 1 */ + 0, /* 5 ,bits 01111, shift 0 */ + 4, /* 5 ,bits 10000, shift 1 */ + /* also window 4, bits 0000, shift 4 */ + 0, /* 4 ,bits 0001, shift 0 */ + 1, /* 4 ,bits 0010, shift 1 */ + 0, /* 4 ,bits 0011, shift 0 */ + 2, /* 4 ,bits 0100, shift 2 */ + 0, /* 4 ,bits 0101, shift 0 */ + 1, /* 4 ,bits 0110, shift 1 */ + 0, /* 4 ,bits 0111, shift 0 */ + 3, /* 4 ,bits 1000, shift 3 */ + /* also window 3, bits 000, shift 3 */ + 0, /* 3 ,bits 001, shift 0 */ + 1, /* 3 ,bits 010, shift 1 */ + 0, /* 3 ,bits 011, shift 0 */ + 2, /* 3 ,bits 100, shift 2 */ + 0, /* 3 ,bits 101, shift 0 */ + 1, /* 3 ,bits 110, shift 1 */ + 0 /* 3 ,bits 111, shift 0 */ + }; + +/* This table defines the starting point in the shift table for + * a particular window size + */ +const static unsigned char *shift_val[7]= + { + &(shift[63]), /* window 0 - unused */ + &(shift[62]), /* window 1 */ + &(shift[60]), /* window 2 - unused */ + &(shift[56]), /* window 3 */ + &(shift[48]), /* window 4 */ + &(shift[32]), /* window 5 */ + &(shift[ 0]), /* window 6 - unused */ + }; + +/** + * Calculates a Montgomery exponent string. + * + * For a supplied exponent p, generate an exponent string strp, which + * defines in pairs the number of multiplies and square operations + * required by a particular bit pattern, commonly used exponents + * 3, 11 and F4 have predefined constant string values, the rest + * are calculated into a cast unsigned char * array via the data + * pointer of a BIGNUM taken from the BN_CTX stack of BIGNUMs + * + * @param p [In] Exponent + * @param strp [Out] Exponent string result + * @param flags [In] Unused + * @param ctx [In] Temporary data storage + * + * @pre p, and ctx are initialised and valid + * @post strp points to required exponent string + * + * @notes String length value in strp[2] is invalid for strings + * greater than length 255 + * string terminates with pattern, 0, 0 this should be + * used in accurately determining the length of a returned + * string strp. + * + * @note strings with value sqr = 255, mul = 0, sqr value should be + * treated as value 256, and added to the next sqr value, this + * is used by exponent strings where more then 256 contiguous + * zero bits are in the exponent bit representation. + * + * @note code contains conditional compilation of code dependent on + * the OS int/long sizes + */ +int BN_gen_exp_bits(p,strp,flags,ctx) +BIGNUM *p; +unsigned char **strp; +int flags; +BN_CTX *ctx; + { + int bits,i,j,window,num; + unsigned char *str=NULL; + BIGNUM *tmp; + + flags=flags; + bits=p->top*BN_BITS2; + tmp=&(ctx->bn[ctx->tos]); + if (p->top == 0) + return(0); + +#if (BN_BITS2 > 17) + if (p->top == 1) +#else + if (bits <= 32) +#endif + { +#if (BN_BITS2 > 17) + if ((p->top == 1) && (p->d[0] == 0x10001)) + str=(unsigned char *)p16; +#endif +#if (16 >= BN_BITS2) && (BN_BITS > 8) + if ( (p->top == 2) && + (p->d[0] == 0x0001) && + (p->d[1] == 0x0001)) + str=(unsigned char *)p16; +#endif +#if (8 >= BN_BITS) + if ( (p->top == 3) && + (p->d[0] == 0x01) && + (p->d[1] == 0x00) && + (p->d[2] == 0x01)) + str=(unsigned char *)p16; +#endif + else if ((p->d[0] == 0x11) && (p->top == 1)) + str=(unsigned char *)p4; + else if ((p->d[0] == 0x3) && (p->top == 1)) + str=(unsigned char *)p2; + window=1; + num=1; + i=BN_BITS2; + } + else if (bits >= 256) + { + window=MAX_WIN_SIZE; /* max size of window */ + num=MAX_NUM_SIZE; + i=(BN_BITS2+(MAX_WIN_SIZE -1))/MAX_WIN_SIZE; + } + else if (bits >= 128) + { + window=4; + num=8; + i=(BN_BITS2+3)/4; + } + else /* 128 to 33 */ + { + window=3; + num=4; + i=(BN_BITS2+2)/3; + } + + /* Number of tmp words */ + j=(p->top*i*2+BN_BYTES-1+4)/BN_BYTES; + + if (str == NULL) + { + if (!bn_wexpand(tmp,j)) + return(0); + str=(unsigned char *)tmp->d; + i=BN_gen_exp_string(&(str[4]),p,window); + i+=2; + str[0]=(unsigned char)((i>>8)&0xff); + str[1]=(unsigned char)((i )&0xff); + str[2]=(unsigned char)window; + str[3]=(unsigned char)num; + } + else + { + i=8; + } + *strp=str; + return(i+2); + } + + +/** + * Generates the Montgomery exponent string. + * + * This function is used to generate an 'exponent string' + * which is an array of bytes that encode how to perform the steps in + * the a^p%m operation. + * + * @param str [Out] Containing the generated string + * @param p [In] Exponent to generate the string for + * @param bits [In] Size of the window for shifting the values of the BIGNUM + * + * @pre p is initialised and value BIGNUM, bits is not 0 + * @post str points to generated exponent string + * + * @note str is cast assigned the data of a BIGNUM allocated and + * expanded from the BN_CTX of the calling function BN_gen_exp_bits + * it does not need to be de-allocated + * + * @note string consisted of unsigned char pairs and 4 byte init + * pairs are sqr count and multiply, where strings are greater + * than 256 bit, length in position str[2] is invalid + * + * @note strings with value sqr = 255, mul = 0, sqr value should be + * treated as value 256, and added to the next sqr value, this + * is used by exponent strings where more then 256 contiguous + * zero bits are in the exponent bit representation. + * + */ +int BN_gen_exp_string(str,p,bits) +unsigned char *str; +BIGNUM *p; +int bits; + { + unsigned char *sp; + unsigned int mask; + const unsigned char *shift; + BN_ULONG w,wh,wl,*d; + unsigned int i,mul,sqr,t,s,ss; + int top; + + if (bits > 6) bits=6; + shift= shift_val[bits]; + + /* This is the mask for the bits we wish to operate on */ + mask=(1<top*BN_BITS2+bits-1)/bits)*2+2]); + *sp-- = 0; + *sp-- = 0; + top=p->top; /* Total words we will shift in */ + d=p->d; + w=wl= *d++; + if (top <= 1) + wh=0; + else + wh= *d++; + sqr=0; + i=0; + + for (;;) + { + /* t will contain how far we need to shift to set a 1 + * in the bottom bit. */ + for (;;) + { + t=w&mask; /* retrieve our window */ + s=shift[t]; /* get the shift value for the window */ + if (s == 0) break; /* no shift write out the vals */ + sqr+=s; /* add the shifted zero count to sqr */ + ss+=s; /* ss is total shift for wl */ + if (ss >= BN_BITS2) /* we have shifted > word len */ + { + if (top <= 1) break; /* no more to do */ + top--; /* dec the count */ + + wl=wh; /* copy the next word */ + /* load the word after or 0 if no more */ + wh=(top <= 1)?0:(*d++); + /* adjust our shift by len of word */ + ss-=BN_BITS2; + } + /* reset our window word w */ + if (ss == 0) + w=wl; + else + w=(wl>>ss)|(wh<<(BN_BITS2-ss)); + } + + /* At this point we have the 0th bit set */ + mul=t; + if (t == 0) break; /* we have reached the end of p */ + /* write out sqr/mul pair */ + *sp-- = (unsigned char)(sqr & 0xff); + *sp-- = (unsigned char)(mul & 0xff); + if(sqr >= 256) /* check whether sqr exceeds max uchar */ + { + /* output the expanded list of to allow for this + * and numbers will require to be added together + * at interpret time + */ + while(sqr >= 256) + { + *sp-- = 255; + *sp-- = 0; /* mul is never zero normally */ + sqr-= 256; + } + } + sqr=bits; /* set sqr to be the window size */ + + ss+=bits; /* ss is total shift for wl */ + if (ss >= BN_BITS2) /* adjust window words w,wl, wh again */ + { + if (top <= 1) break; + top--; + + wl=wh; + wh=(top <= 1)?0:(*(d++)); + ss-=BN_BITS2; + } + + if (ss == 0) + w=wl; + else + w=(wl>>ss)|(wh<<(BN_BITS2-ss)); + } + sp++; + i=2; + /* reverse the string from the top of the exponent string + * and copy to the bottom, allocated exponent string is 2 * max length + * expected for exponent string + */ + while (sp[0] != 0 || sp[1] != 0) + { + str[0]=sp[0]; + str[1]=sp[1]; + str+=2; + sp+=2; + i+=2; + } + str[0]=0; + str[1]=0; + + return(i); + } + +#ifdef MAIN +main() + { + BIGNUM p; + unsigned char buf[512],*pp; + int i; + + BN_init(&p); + BN_rand(&p,33,1,0); +#ifndef NO_FP_API + BN_print_fp(stdout,&p); fprintf(stdout,"\n"); +#endif + + BN_rand(&p,512,1,1); +#ifndef NO_FP_API + BN_print_fp(stdout,&p); fprintf(stdout,"\n"); +#endif + for (i=0; i<10000; i++) + BN_gen_exp_string(buf,&p,5); + +#if 0 + BN_gen_exp_string(buf,&p,3); + + pp=buf; + for (;;) + { + printf("mul %d sqr %d\n",pp[0],pp[1]); + if (pp[1] == 0) break; + pp+=2; + } +#endif + } +#endif diff --git a/build/tools/acsign/bn_exp.c b/build/tools/acsign/bn_exp.c new file mode 100644 index 00000000..08634e7e --- /dev/null +++ b/build/tools/acsign/bn_exp.c @@ -0,0 +1,156 @@ +/* $Id$ */ +/* + * Copyright (C) 1998-2002 RSA Security Inc. All rights reserved. + * + * This work contains proprietary information of RSA Security. + * Distribution is limited to authorized licensees of RSA + * Security. Any unauthorized reproduction, distribution or + * modification of this work is strictly prohibited. + * + */ + +#include "bn_lcl.h" + +#if !(defined(NO_SPLIT) && defined(SPLIT_FILE)) + +#ifdef NO_SPLIT +#define SPLIT_BN_MOD_EXP_ORIG +#define SPLIT_BN_MOD_EXP +#endif /* NO_SPLIT */ + +#define TABLE_SIZE 16 + +#ifdef SPLIT_BN_MOD_EXP_ORIG +#if 0 +/* this one works - simple but works */ +int BN_mod_exp_orig(r,a,p,m,ctx) +BIGNUM *r,*a,*p,*m; +BN_CTX *ctx; + { + int i,bits,ret=0; + BIGNUM *v,*tmp; + + v= &(ctx->bn[ctx->tos++]); + tmp= &(ctx->bn[ctx->tos++]); + + if (BN_copy(v,a) == NULL) goto err; + bits=BN_num_bits(p); + + if (BN_is_odd(p)) + { if (BN_copy(r,a) == NULL) goto err; } + else { if (!BN_one(r)) goto err; } + + for (i=1; itos-=2; + return(ret); + } + +#endif + +/* this one works - simple but works */ +int BN_exp(r,a,p,ctx) +BIGNUM *r,*a,*p; +BN_CTX *ctx; + { + int i,bits,ret=0,tos; + BIGNUM *v,*rr; + + bn_check_top(a); + bn_check_top(p); + + tos=ctx->tos; + v= &(ctx->bn[ctx->tos++]); + if ((r == a) || (r == p)) + rr= &(ctx->bn[ctx->tos++]); + else + rr=r; + + if (BN_copy(v,a) == NULL) goto err; + bits=BN_num_bits(p); + + if (BN_is_odd(p)) + { if (BN_copy(rr,a) == NULL) goto err; } + else { if (!BN_one(rr)) goto err; } + + for (i=1; itos=tos; + if (r != rr) (void)BN_copy(r,rr); + return(ret); + } +#endif + +#ifdef SPLIT_BN_MOD_EXP +/** + * Perform mod exp on BIGNUM + * @param r Pointer to return value BIGNUM + * @param a Pointer to data value BIGNUM + * @param p Pointer to Public exponent BIGNUM + * @param m Pointer to modulus BIGNUM + * @param ctx Pointer to BN_CTX + * @return 0 success + * @pre BIGNUMs a, p, m and BN_CTX ctx exist and are valid + * @post BIGNUM r points to evaluated mod_exp + * @note BN_mod_exp_mont is only available if library compiled + * with define BN_MONT_MUL, and will only be used to compute + * mod_exp operation where the modulus m is odd. + * BN_mod_exp_recp is only available if library is compiled + * with define RECP_MUL_MOD. + * BN_mod_exp_simple is the default method, not available if + * library is compiled with define RECP_MUL_MOD. + * if BN_mod_exp_mont is available, which ever of BN_mod_exp_simple + * or BN_mod_exp_recp is available will only be used for even + * modulus m values. + * @relates BN_mod_exp_mont + * @relates BN_mod_exp_recp + * @relates BN_mod_exp_simple + */ +int BN_mod_exp(r,a,p,m,ctx) +BIGNUM *r; +BIGNUM *a; +BIGNUM *p; +BIGNUM *m; +BN_CTX *ctx; + { + int ret; + + bn_check_top(a); + bn_check_top(p); + bn_check_top(m); + +#ifdef MONT_MUL_MOD + + if (BN_is_odd(m)) + { ret=BN_mod_exp_mont(r,a,p,m,ctx,NULL); } + else +#endif +#ifdef RECP_MUL_MOD + { ret=BN_mod_exp_recp(r,a,p,m,ctx); } +#else + { ret=BN_mod_exp_simple(r,a,p,m,ctx); } +#endif + + return(ret); + } +#endif + +#endif diff --git a/build/tools/acsign/bn_fm_w.c b/build/tools/acsign/bn_fm_w.c new file mode 100644 index 00000000..999ed55f --- /dev/null +++ b/build/tools/acsign/bn_fm_w.c @@ -0,0 +1,63 @@ +/* $Id$ */ +/* + * Copyright (C) 1998-2002 RSA Security Inc. All rights reserved. + * + * This work contains proprietary information of RSA Security. + * Distribution is limited to authorized licensees of RSA + * Security. Any unauthorized reproduction, distribution or + * modification of this work is strictly prohibited. + * + */ +#include "bn_lcl.h" + +/* rp has w words, the top w words are 0 */ +void bn_from_montgomery_words(ret,a,np,w,n0) +BN_ULONG *ret; +BN_ULONG *a; +BN_ULONG *np; +int w; +BN_ULONG n0; + { + BN_ULONG v0,v1,*ap,*wap; + int i; + + v1=0; + + ap=a; + wap= &(a[w]); + /* Consider putting this loop in ASM */ + for (i=0; i0; i--) + if (ap[i] != np[i]) break; + } + v1=(ap[i] >= np[i]); + } + if (v1) + (void)bn_sub_words(ret,ap,np,w); + else + { +#if 0 /* Alpha does not like Memcpy */ + Memcpy(ret,ap,sizeof(BN_ULONG)*w); +#else + for (i=0; ibn[ctx->tos]); + b= &(ctx->bn[ctx->tos+1]); + + if (BN_copy(a,in_a) == NULL) goto err; + if (BN_copy(b,in_b) == NULL) goto err; + + if (BN_cmp(a,b) < 0) { t=a; a=b; b=t; } + t=bn_euclid(a,b); + if (t == NULL) goto err; + + if (BN_copy(r,t) == NULL) goto err; + ret=1; +err: + return(ret); + } + +BIGNUM *bn_euclid(a,b) +BIGNUM *a,*b; + { + BIGNUM *t; + int shifts=0; + + bn_check_top(a); + bn_check_top(b); + + for (;;) + { + if (BN_is_zero(b)) + break; + + if (BN_is_odd(a)) + { + if (BN_is_odd(b)) + { + if (!BN_sub(a,a,b)) goto err; + if (!BN_rshift1(a,a)) goto err; + if (BN_cmp(a,b) < 0) + { t=a; a=b; b=t; } + } + else /* a odd - b even */ + { + if (!BN_rshift1(b,b)) goto err; + if (BN_cmp(a,b) < 0) + { t=a; a=b; b=t; } + } + } + else /* a is even */ + { + if (BN_is_odd(b)) + { + if (!BN_rshift1(a,a)) goto err; + if (BN_cmp(a,b) < 0) + { t=a; a=b; b=t; } + } + else /* a even - b even */ + { + if (!BN_rshift1(a,a)) goto err; + if (!BN_rshift1(b,b)) goto err; + shifts++; + } + } + } + if (shifts) + { + if (!BN_lshift(a,a,shifts)) goto err; + } + return(a); +err: + return(NULL); + } +#endif + +#ifdef SPLIT_BN_MOD_INVERSE +/* solves ax == 1 (mod n) */ +BIGNUM *BN_mod_inverse(in, a, n, ctx) +BIGNUM *in; +BIGNUM *a; +BIGNUM *n; +BN_CTX *ctx; + { + BIGNUM *A,*B,*X,*Y,*M,*D,*R; + BIGNUM *T,*ret=NULL; + int sign; + + bn_check_top(a); + bn_check_top(n); + + A= &(ctx->bn[ctx->tos]); + B= &(ctx->bn[ctx->tos+1]); + X= &(ctx->bn[ctx->tos+2]); + D= &(ctx->bn[ctx->tos+3]); + M= &(ctx->bn[ctx->tos+4]); + Y= &(ctx->bn[ctx->tos+5]); + ctx->tos+=6; + if (in == NULL) + R=BN_new(); + else + R=in; + if (R == NULL) goto err; + + (void)BN_zero(X); + (void)BN_one(Y); + if (BN_copy(A,a) == NULL) goto err; + if (BN_copy(B,n) == NULL) goto err; + sign=1; + + while (!BN_is_zero(B)) + { + if (!BN_div(D,M,A,B,ctx)) goto err; + T=A; + A=B; + B=M; + /* T has a struct, M does not */ + + if (!BN_mul(T,D,X,ctx)) goto err; + if (!BN_add(T,T,Y)) goto err; + M=Y; + Y=X; + X=T; + sign= -sign; + } + if (sign < 0) + { + if (!BN_sub(Y,n,Y)) goto err; + } + + if (BN_is_one(A)) + { if (!BN_mod(R,Y,n,ctx)) goto err; } + else + { +#ifndef NO_ERR + BNerr(BN_F_BN_MOD_INVERSE,BN_R_NO_INVERSE); +#endif + goto err; + } + ret=R; +err: + if ((ret == NULL) && (in == NULL)) BN_free(R); + ctx->tos-=6; + return(ret); + } +#endif + +#ifdef SPLIT_BN_MOD_INVERSE_WORD +/* solves 1 == (ret*(1<l + * @param l [In] a word (size of word depends on machine) + * @returns number of bits in parameter l + */ +#ifdef SPLIT_BN_NUM_BITS_WORD +int BN_num_bits_word(l) +BN_ILONG l; + { + int i; + + /* + * This table represents the number of bits required to + * represent a "number", where "number" is indexed from + * 0 into the table. So: + * number 0 = # bits req'd to represent 0 = bits[0] = 0 + * number 1 = # bits req'd to represent 1 = bits[1] = 1 + * number 2 = # bits req'd to represent 2 = bits[2] = 2 + * number 3 = # bits req'd to represent 3 = bits[3] = 2 + * number 4 = # bits req'd to represent 4 = bits[4] = 3 + * etc. + * + * The small code size table exists to save some space, + * but requires (at most) an extra shift, an extra AND, + * and an extra add. + */ +#ifdef SMALL_CODE_SIZE + const static char bits[16]={ + 0,1,2,2,3,3,3,3,4,4,4,4,4,4,4,4 + }; +#else + const static char bits[256]={ + 0,1,2,2,3,3,3,3,4,4,4,4,4,4,4,4, + 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, + 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, + 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + }; +#endif + +#if defined(SIXTY_FOUR_BIT_LONG) + if (l & 0xffffffff00000000L) + { + if (l & 0xffff000000000000L) + { + if (l & 0xff00000000000000L) + i=56; + else i=48; + } + else + { + if (l & 0x0000ff0000000000L) + i=40; + else i=32; + } + } + else +#else +#ifdef SIXTY_FOUR_BIT + if (l & 0xffffffff00000000LL) + { + if (l & 0xffff000000000000LL) + { + if (l & 0xff00000000000000LL) + i=56; + else i=48; + } + else + { + if (l & 0x0000ff0000000000LL) + i=40; + else i=32; + } + } + else +#endif +#endif + { +#if defined(THIRTY_BIT) || defined(THIRTY_ONE_BIT) || defined(THIRTY_TWO_BIT) || defined(SIXTY_FOUR_BIT) || defined(SIXTY_FOUR_BIT_LONG) + if (l & 0xffff0000L) + { + if (l & 0xff000000L) + i=24; + else i=16; + } + else +#endif + { + if (l & 0xff00L) + i=8; + else + i=0; + } + } +#ifndef SMALL_CODE_SIZE + return(i+bits[l>>i]); +#else + l>>=i; + if (l & 0xf0) + return(bits[l>>4]+i+4); + else + return(bits[l]+i); +#endif + } +#endif + +#ifdef SPLIT_BN_NUM_BITS +int BN_num_bits(a) +BIGNUM *a; + { + BN_ULONG l; + int i; + + bn_check_top(a); + + if (a->top == 0) return(0); + l=a->d[a->top-1]; + i=(a->top-1)*BN_BITS2; +#ifdef BN_DEBUG + if (l == 0) + { +#if !defined(NO_FP_API) && !defined(WIN16) + fprintf(stderr,"BAD TOP VALUE\n"); +#endif + abort(); /* BN_DEBUG */ + /* TODO: need to understand what sort of error can + * be reported for this error + */ + return(0); + } +#endif + return(i+BN_num_bits_word(l)); + } +#endif + +/** + * Clears and free a BIGNUM + * + * @param a [In] BIGNUM to clear then free + */ +#ifdef SPLIT_BN_CLEAR_FREE +void BN_clear_free(BIGNUM *a) +{ + if (a == NULL) + { + return; + } + + BN_clear(a); + BN_free(a); +} +#endif + +#ifdef SPLIT_BN_FREE +/** + * Frees a BIGNUM + * + * @param a [In] BIGNUM to free + */ +void BN_free(a) +BIGNUM *a; + { + if (a == NULL) return; + if ((a->d != NULL) && !(BN_get_flags(a,BN_FLG_STATIC_DATA))) + Free(a->d); + a->flags|=BN_FLG_FREE; /* REMOVE? */ + if (a->flags & BN_FLG_MALLOCED) + Free(a); + } +#endif + +#ifdef SPLIT_BN_INIT + +#ifdef SMALL_CODE_SIZE +/** + * Zero expands a bignum + * + * @param a [In] BIGNUM to expand + * @param n [In] number of bits to expand to + * + * @pre a is a valid BIGNUM. + */ +void bn_zexpand(a,n) +BIGNUM *a; +int n; + { + if ((a)->top < n) + { + int i; + bn_wexpand((a),n); + if (a->d!=NULL) + { + for (i=(a)->top; id[i]=0; + } + } + } + +/** + * Fixes the top value in the BIGNUM to be the count of BN_ULONGs with data. + * + * @param a [In] BIGNUM to free + * + * @pre a is a valid BIGNUM. + */ +void bn_fix_top(a) +BIGNUM *a; + { + BN_ULONG *ftl; + if ((a)->top > 0) + { + for (ftl= &((a)->d[(a)->top-1]); (a)->top > 0; (a)->top--) + if (*(ftl--)) break; + } + } +#endif + +/** + * Initializes a BIGNUM + * + * @param a [In] BIGNUM reference + * + * @pre a is a valid BIGNUM. + */ +void BN_init(a) +BIGNUM *a; + { + (void)Memset(a,0,sizeof(BIGNUM)); + } +#endif + +#ifdef SPLIT_BN_NEW +/** + * Allocates a BIGNUM + * + * @return pointer to allocated BIGNUM + */ +BIGNUM *BN_new() + { + BIGNUM *ret; + + if ((ret=(BIGNUM *)Malloc(sizeof(BIGNUM))) == NULL) + { +#ifndef NO_ERR + BNerr(BN_F_BN_NEW,ERR_R_MALLOC_FAILURE); +#endif + return(NULL); + } + ret->flags=BN_FLG_MALLOCED; + ret->top=0; + ret->neg=0; + ret->max=0; + ret->d=NULL; + return(ret); + } +#endif + +#ifdef SPLIT_BN_CTX_NEW +/** + * Allocates a BIGNUM context + * + * @return pointer to allocated BIGNUM context + */ +BN_CTX *BN_CTX_new() + { + BN_CTX *ret; + + ret=(BN_CTX *)Malloc(sizeof(BN_CTX)); + if (ret == NULL) + { +#ifndef NO_ERR + BNerr(BN_F_BN_CTX_NEW,ERR_R_MALLOC_FAILURE); +#endif + return(NULL); + } + + BN_CTX_init(ret); + ret->flags=BN_FLG_MALLOCED; + return(ret); + } +#endif + +#ifdef SPLIT_CTX_INIT +/** + * Initializes a BIGNUM context + * + * @param a [In] BIGNUM context reference + * + * @pre a is a valid BIGNUM context. + */ +void BN_CTX_init(ctx) +BN_CTX *ctx; + { + (void)Memset(ctx,0,sizeof(BN_CTX)); + ctx->tos=0; + ctx->flags=0; + } +#endif + +#ifdef SPLIT_BN_CTX_FREE +/** + * Frees a BIGNUM context + * + * @param a [In] BIGNUM context reference + * + * @pre a is a valid BIGNUM context. + */ +void BN_CTX_free(c) +BN_CTX *c; + { + int i; + + for (i=0; ibn[i])); + if (c->flags & BN_FLG_MALLOCED) + Free(c); + } +#endif + +#ifdef SPLIT_BN_EXPAND2 +BIGNUM *bn_expand2(b, words) +BIGNUM *b; +int words; + { + BN_ULONG *A,*B,*a; + + R_DIAG_CHECK_STACK; + + bn_check_top(b); + + if (words > b->max) + { + bn_check_top(b); + if (BN_get_flags(b,BN_FLG_STATIC_DATA)) + { +#ifndef NO_ERR + BNerr(BN_F_BN_EXPAND2,BN_R_EXPAND_ON_STATIC_BIGNUM_DATA); +#endif + return(NULL); + } + a=A=(BN_ULONG *)Malloc(sizeof(BN_ULONG)*(words+1)); + if (A == NULL) + { +#ifndef NO_ERR + BNerr(BN_F_BN_EXPAND2,ERR_R_MALLOC_FAILURE); +#endif + return(NULL); + } + /* during development this is a nice way of making sure + * that we are not relying on the top byte being 0 or + * other such things + */ +#ifdef BN_DEBUG + (void)Memset(A,0x5c,sizeof(BN_ULONG)*(words+1)); +#endif + + B=b->d; + if (B != NULL) + { +#ifndef SMALL_CODE_SIZE + int i; + + /* for (i=b->top&(~7); i>0; i-=8) */ + /* The above line can induce a SunC compiler bug */ + for (i=(b->top>>3); i>0; i--) + { + BN_ULONG T0,T1,T2,T3; + + T0=B[0]; + T1=B[1]; + T2=B[2]; + T3=B[3]; + A[0]=T0; + A[1]=T1; + A[2]=T2; + A[3]=T3; + T0=B[4]; + T1=B[5]; + T2=B[6]; + T3=B[7]; + A[4]=T0; + A[5]=T1; + A[6]=T2; + A[7]=T3; + A+=8; + B+=8; + } + switch (b->top&7) + { + case 7: + A[6]=B[6]; + case 6: + A[5]=B[5]; + case 5: + A[4]=B[4]; + case 4: + A[3]=B[3]; + case 3: + A[2]=B[2]; + case 2: + A[1]=B[1]; + case 1: + A[0]=B[0]; + case 0: + /* I need the 'case 0' entry for utrix cc. + * If the optimiser is turned on, it does the + * switch table by doing + * a=top&7 + * a--; + * goto jump_table[a]; + * If top is 0, this makes us jump to 0xffffffc + * which is rather bad. + * eric 23-Apr-1998 + */ + ; + } +#if 0 /* Not needed */ + B= &(b->d[b->top]); + j=b->max-8; + for (i=b->top; itop); +#endif + Free(b->d); + } + + b->d=a; + b->max=words; + } + return(b); + } +#endif + +#ifdef SPLIT_BN_DUP +/** + * Duplicates a BIGNUM + * + * @param a [In] BIGNUM reference + * + * @returns pointer to duplicate BIGNUM of a + */ +BIGNUM *BN_dup(a) +BIGNUM *a; + { + BIGNUM *r; + BIGNUM *ret; + + bn_check_top(a); + + r=BN_new(); + if (r == NULL) return(NULL); + ret = (BIGNUM *)BN_copy(r,a); + if (ret == NULL) + { + BN_free(r); + } + return(ret); + } +#endif + +#ifdef SPLIT_BN_COPY +/** + * Copies a BIGNUM to an existing BIGNUM + * + * @param a [In] To BIGNUM reference + * @param b [Out] From BIGNUM reference + * + * @pre Both a and b are valid BIGNUMs + * + * @returns pointer to duplicate BIGNUM of a + */ +BIGNUM *BN_copy(a, b) +BIGNUM *a; +BIGNUM *b; + { +#ifndef SMALL_CODE_SIZE + int i; + BN_ULONG *A,*B; +#endif + + bn_check_top(b); + + if (a == b) return(a); + if (bn_wexpand(a,b->top) == NULL) return(NULL); +#ifndef SMALL_CODE_SIZE + A=a->d; + B=b->d; + +/* for (i=b->top&~0x07; i>0; i-=8) */ +/* The above version of this loop has been removed. It appeared + to generate a compiler bug in SunC 4.x and 5.x. I do not believe + that the code was to blame. Compiling with -fast was the problem + since this would loop unroll the loop below another 3 times. + The compiler would generate generate + andcc %o0,-8,%o1 # load ~0x07 + ld [%i2],%o3 # load *B + ble .L77000125 # exit loop unless 8 or more + ld [%i1],%o2 # load *A + sub %o1,-7,%o0 # + sdivcc %o0,8,%o0 # when do we clear %y? + bvs,a .L900001510 + sethi %hi(0x80000000),%o0 +.L900001510: + cmp %o0,3 + bl .L77000114 # one loop + ld [%o2],%o0 + # three loop code + + The loop would then be unrolled 3 times. The problem I believe + I was seeing was that the sdivcc operates is %y:%o0 / 8. + %y is not being cleared, and so occasionally this would + cause problems. The replacement code sequence stops + the compiler generating the sdiv instruction. + */ + for (i=(b->top>>3); i>0; i--) + { + BN_ULONG T0,T1,T2,T3; + T0=B[0]; + T1=B[1]; + T2=B[2]; + T3=B[3]; + A[0]=T0; + A[1]=T1; + A[2]=T2; + A[3]=T3; + T0=B[4]; + T1=B[5]; + T2=B[6]; + T3=B[7]; + A[4]=T0; + A[5]=T1; + A[6]=T2; + A[7]=T3; + A+=8; + B+=8; + } + switch (b->top&0x07) + { + case 7: + A[6]=B[6]; + case 6: + A[5]=B[5]; + case 5: + A[4]=B[4]; + case 4: + A[3]=B[3]; + case 3: + A[2]=B[2]; + case 2: + A[1]=B[1]; + case 1: + A[0]=B[0]; + case 0: + /* I need the 'case 0' entry for utrix cc. + * If the optimiser is turned on, it does the + * switch table by doing + * a=top&7 + * a--; + * goto jump_table[a]; + * If top is 0, this makes us jump to 0xffffffc which is + * rather bad. + * eric 23-Apr-1998 + */ + ; + } +#else + (void)Memcpy(a->d,b->d,sizeof(b->d[0])*b->top); +#endif + +/* (void)Memset(&(a->d[b->top]),0,sizeof(a->d[0])*(a->max-b->top));*/ + a->top=b->top; + if ((a->top == 0) && (a->d != NULL)) + a->d[0]=0; + a->neg=b->neg; + return(a); + } +#endif + +#ifdef SPLIT_BN_CLEAR +/** + * Clears a BIGNUM + * + * @param a [In] BIGNUM to clear + * + * @pre a is valid + */ +void BN_clear(a) +BIGNUM *a; + { + if (a->d != NULL) + (void)Memset(a->d,0,a->max*sizeof(a->d[0])); + a->top=0; + a->neg=0; + } +#endif + +#ifdef SPLIT_BN_GET_WORD +BN_ULONG BN_get_word(a) +BIGNUM *a; + { + int i,n; + BN_ULONG ret=0; + + n=BN_num_bytes(a); + if (n > ((int) sizeof(BN_ULONG))) + return(BN_MASK2); + for (i=a->top-1; i>=0; i--) + { +#ifndef SIXTY_FOUR_BIT /* the data item > unsigned long */ + ret<<=BN_BITS4; /* stops the compiler complaining */ + ret<<=BN_BITS4; +#else + ret=0; +#endif + ret|=a->d[i]; + } + return(ret); + } +#endif + +#ifdef SPLIT_BN_SET_WORD +/** + * Set the BIGNUM to the supplied BN_ULONG value. + * + * @param a [In/Out] BIGNUM to be updated + * @param w [In] Value to be set in a + * + * @notes Function will expand a to be an + * array of 8 BN_ULONGs in size. + * @todo Review behaviour of expansion without checking + * of size first. + */ +int BN_set_word(a,w) +BIGNUM *a; +BN_ULONG w; + { + int i,n; + + if (bn_expand(a,(int)(sizeof(BN_ULONG)*8)) == NULL) return(0); + +#ifdef BN_BYTES + /* This is used when a BN_ULONG is greater in size + * than the BN_BYTES of the array, eg BN_ULONG 64 bits + * and actual a->d array of 32 bits. + */ + n=sizeof(BN_ULONG)/BN_BYTES; +#else + n=1; +#endif + a->neg=0; + a->top=0; + a->d[0]=(BN_ULONG)w&BN_MASK2; + if (a->d[0] != 0) a->top=1; + for (i=1; i>=BN_BITS2 so compilers don't complain + * on builds where sizeof(long) == BN_TYPES */ +#ifndef SIXTY_FOUR_BIT /* the data item > unsigned long */ + w>>=BN_BITS4; + w>>=BN_BITS4; +#else + w=0; +#endif + a->d[i]=(BN_ULONG)w&BN_MASK2; + if (a->d[i] != 0) a->top=i+1; + } + return(1); + } +#endif + +#ifdef BN_BYTES +#ifdef SPLIT_BN_BIN2BN +/* ignore negative */ +BIGNUM *BN_bin2bn(s, len, ret) +unsigned char *s; +int len; +BIGNUM *ret; + { + unsigned int i,m; + unsigned int n; + BN_ULONG l; + + if (ret == NULL) ret=BN_new(); + if (ret == NULL) return(NULL); + l=0; + n=len; + if (n == 0) + { + ret->top=0; + return(ret); + } + if (bn_expand(ret,(int)(n+2)*8) == NULL) + return(NULL); + i=((n-1)/BN_BYTES)+1; + m=((n-1)%(BN_BYTES)); + ret->top=i; + while (n-- > 0) + { + l=(l<<8L)| *(s++); + if (m-- == 0) + { + ret->d[--i]=l; + l=0; + m=BN_BYTES-1; + } + } + /* need to call this due to clear byte at top if avoiding + * having the top bit set (-ve number) */ + bn_fix_top(ret); + return(ret); + } +#endif + +#ifdef SPLIT_BN_BN2BIN +/* ignore negative */ +int BN_bn2bin(a, to) +BIGNUM *a; +unsigned char *to; + { + int n,i; + BN_ULONG l; + + bn_check_top(a); + + n=i=BN_num_bytes(a); + while (i-- > 0) + { + l=a->d[i/BN_BYTES]; + *(to++)=(unsigned char)((l>>(8*(i%BN_BYTES)))&0xff); + } + return(n); + } +#endif +#else +#ifdef SPLIT_BN_BIN2BN +/* ignore negative */ +BIGNUM *BN_bin2bn(s, len, ret) +unsigned char *s; +int len; +BIGNUM *ret; + { + int i,w,r,b,j,v; + int n; + BN_ULONG l; + + if (ret == NULL) ret=BN_new(); + if (ret == NULL) return(NULL); + while (len > 0) + { + if (*s != 0) break; + s++; + len--; + } + n=len*8; + if (bn_expand(ret,n) == NULL) + return(NULL); + for (i=0; imax; i++) + ret->d[i]=0; + j=0; + for (i=len-1; i>=0; i--) + { + v=((int)s[i])&0xff; + w=j/BN_BITS2; + b=j%BN_BITS2; + r=BN_BITS2-b; + j+=8; + if (r >= 8) + { + ret->d[w]|=(((BN_ULONG)v)<d[w] =(ret->d[w]|(((BN_ULONG)v)<d[w+1]=(ret->d[w+1]|(((BN_ULONG)v)>>r)); + } + } + + ret->top=((n-1)/BN_BITS2)+1; + bn_fix_top(ret); + return(ret); + } +#endif + +#ifdef SPLIT_BN_BN2BIN +int BN_bn2bin(a, to) +BIGNUM *a; +unsigned char *to; + { + int num,i,bi,w,b,r,max; + BN_ULONG l,*lp; + + bn_check_top(a); + + num=bi=BN_num_bytes(a); + lp=a->d; + for (i=0; i= 8) + to[bi]=(lp[w]>>b)&0xff; + else + { + to[bi]=(lp[w]>>b); + if (w+1 < a->top) + to[bi]|=lp[w+1]<top-b->top; + if (i != 0) return(i); + ap=a->d; + bp=b->d; + for (i=a->top-1; i>=0; i--) + { + t1= ap[i]; + t2= bp[i]; + if (t1 != t2) + return(t1 > t2?1:-1); + } + return(0); + } +#endif + +#ifdef SPLIT_BN_CMP +int BN_cmp(a, b) +BIGNUM *a; +BIGNUM *b; + { + int i; + int gt,lt; + BN_ULONG t1,t2; + + if ((a == NULL) || (b == NULL)) + { + if (a != NULL) + return(-1); + else if (b != NULL) + return(1); + else + return(0); + } + + bn_check_top(a); + bn_check_top(b); + + if (a->neg != b->neg) + { + if (a->neg) + return(-1); + else return(1); + } + if (a->neg == 0) + { gt=1; lt= -1; } + else { gt= -1; lt=1; } + + if (a->top > b->top) return(gt); + if (a->top < b->top) return(lt); + for (i=a->top-1; i>=0; i--) + { + t1=a->d[i]; + t2=b->d[i]; + if (t1 > t2) return(gt); + if (t1 < t2) return(lt); + } + return(0); + } +#endif + +#ifdef SPLIT_BN_SET_BIT +int BN_set_bit(a, n) +BIGNUM *a; +int n; + { + int i,j,k; + + bn_check_top(a); + + i=n/BN_BITS2; + j=n%BN_BITS2; + if (a->top <= i) + { + if (bn_wexpand(a,i+1) == NULL) return(0); + for(k=a->top; kd[k]=0; + a->top=i+1; + } + + a->d[i]|=(((BN_ULONG)1)<top <= i) return(0); + + a->d[i]&=(~(((BN_ULONG)1)<top <= i) return(0); + return((a->d[i]&(((BN_ULONG)1)<= a->top) return(0); + if (b == 0) + a->top=w; + else + { + a->top=w+1; + a->d[w]&= ~(((BN_ULONG)BN_MASK2)< bb)?1:-1); + for (i=n-2; i>=0; i--) + { + aa=a[i]; + bb=b[i]; + if (aa != bb) return((aa > bb)?1:-1); + } + return(0); + } +#endif +#endif + diff --git a/build/tools/acsign/bn_lsh.c b/build/tools/acsign/bn_lsh.c new file mode 100644 index 00000000..133b33ed --- /dev/null +++ b/build/tools/acsign/bn_lsh.c @@ -0,0 +1,112 @@ +/* $Id$ */ +/* + * Copyright (C) 1998-2002 RSA Security Inc. All rights reserved. + * + * This work contains proprietary information of RSA Security. + * Distribution is limited to authorized licensees of RSA + * Security. Any unauthorized reproduction, distribution or + * modification of this work is strictly prohibited. + * + */ + +#include "bn_lcl.h" + +#if !(defined(NO_SPLIT) && defined(SPLIT_FILE)) + +#ifdef NO_SPLIT +#define SPLIT_BN_LSHIFT1 +#define SPLIT_BN_LSHIFT +#endif /* NO_SPLIT */ + +#ifdef SPLIT_BN_LSHIFT1 +#ifdef SMALL_CODE_SIZE + +int BN_lshift1(r, a) +BIGNUM *r,*a; + { + return(BN_lshift(r, a, 1)); + } + +#else + +int BN_lshift1(r, a) +BIGNUM *r; +BIGNUM *a; + { + register BN_ULONG *ap,*rp,t,c; + int i; + + bn_check_top(a); + + if (r != a) + { + if (bn_wexpand(r,a->top+1) == NULL) return(0); + r->neg=a->neg; + r->top=a->top; + } + else + { + if (bn_wexpand(r,a->top+1) == NULL) return(0); + } + ap=a->d; + rp=r->d; + c=0; + for (i=0; itop; i++) + { + t= *(ap++); + *(rp++)=((t<<1L)|c)&BN_MASK2; + c=(t & BN_TBIT)?1:0; + } + if (c) + { + *rp=1; + r->top++; + } + return(1); + } +#endif +#endif + +#ifdef SPLIT_BN_LSHIFT +int BN_lshift(r, a, n) +BIGNUM *r; +BIGNUM *a; +int n; + { + int i,nw,lb,rb; + BN_ULONG *t,*f; + BN_ULONG l; + + bn_check_top(a); + +#ifndef SMALL_CODE_SIZE + if (n == 1) return(BN_lshift1(r,a)); +#endif + if (bn_wexpand(r,a->top+(n/BN_BITS2)+1) == NULL) return(0); + r->neg=a->neg; + nw=n/BN_BITS2; + lb=n%BN_BITS2; + rb=BN_BITS2-lb; + f=a->d; + t=r->d; + t[a->top+nw]=0; + if (lb == 0) + for (i=a->top-1; i>=0; i--) + t[nw+i]=f[i]; + else + for (i=a->top-1; i>=0; i--) + { + l=f[i]; + t[nw+i+1]|=(l>>rb)&BN_MASK2; + t[nw+i]=(l<top=a->top+nw+1; + bn_fix_top(r); + return(1); + } +#endif + +#endif diff --git a/build/tools/acsign/bn_m_exp.c b/build/tools/acsign/bn_m_exp.c new file mode 100644 index 00000000..8d8afe94 --- /dev/null +++ b/build/tools/acsign/bn_m_exp.c @@ -0,0 +1,283 @@ +/* $Id$ */ +/* + * Copyright (C) 1998-2002 RSA Security Inc. All rights reserved. + * + * This work contains proprietary information of RSA Security. + * Distribution is limited to authorized licensees of RSA + * Security. Any unauthorized reproduction, distribution or + * modification of this work is strictly prohibited. + * + */ + +#if 0 //RSA +#include +#endif +#include "bn_lcl.h" + +/* #ifdef MONT_MUL_MOD */ +/** + * Generic form of Montgomery Exponentiation, can be called directly + * out side of a BN_ME_METH + * + * @param rr_in BIGNUM pointer for result + * @param a_in BIGNUM pointer for base + * @param p BIGNUM pointer to the exponent + * @param m BIGNUM pointer to the modulus + * @param ctx BN_CTX pointer for temp values are argument storage + * @param in_mont BN_MONT_CTX pointer to structure of montgomery values + * + * @pre arguments are all initialised and not NULL or zero + * @post rr_in contains evaluated result + * + * @note + */ +int BN_mod_exp_mont(rr_in,a_in,p,m,ctx,in_mont) +BIGNUM *rr_in; +BIGNUM *a_in; +BIGNUM *p; +BIGNUM *m; +BN_CTX *ctx; +BN_MONT_CTX *in_mont; + { + int i,j,k,ret=0,tos,top; + int mul,sqr,num; + BIGNUM *d,*aa,*r,*a,*t; + BN_ULONG *vall[BN_EXP_TABLE_SIZE],*dp,*rp,n0,*rr,*tmp,*np; + BN_MONT_CTX *mont=NULL; + unsigned char *str=NULL; +#ifdef BN_SURRENDER + R_SURRENDER *surrender=NULL; + int count=0; +#endif + + bn_check_top(a_in); + bn_check_top(p); + bn_check_top(m); + + if (!(m->d[0] & 1)) + { +#ifndef NO_ERR + BNerr(BN_F_BN_MOD_EXP_MONT,BN_R_CALLED_WITH_EVEN_MODULUS); +#endif + return(0); + } + + tos=ctx->tos; + + if (BN_is_zero(a_in)) + { (void)BN_zero(rr_in); return(1); } + if (BN_is_zero(p)) + { (void)BN_one(rr_in); return(1); } + if (BN_is_one(p)) + { (void)BN_copy(rr_in,a_in); return(1); } + + /* If this is not done, things will break in the montgomery + * part */ + +#if 1 + if (in_mont != NULL) + mont=in_mont; + else +#endif + { + if ((mont=BN_MONT_CTX_new()) == NULL) goto err; + if (!BN_MONT_CTX_set(mont,m,ctx)) goto err; + } + +#ifdef BN_SURRENDER + surrender=ctx->surrender; +#endif + + if (!BN_gen_exp_bits(p,&str,0,ctx)) goto err; + /* Remember this function uses another element from the passed ctx */ + ctx->tos++; + num=str[3]; + i=(BN_BITS+str[2]-1)/str[2]; + str+=4; + + + top=mont->N.top; + if ((a_in->top == top) && (a_in->d[top-1] < m->d[top-1])) + { + a=a_in; + } + else if (a_in->top < top) + { + a= &(ctx->bn[ctx->tos++]); + a->top=a_in->top; + bn_zexpand(a,top); + for (j=0; jtop; j++) + a->d[j]=a_in->d[j]; + } + else /* if (a_in->top > i) */ + { + a= &(ctx->bn[ctx->tos++]); + if (!BN_mod(a,a_in,m,ctx)) goto err; + bn_zexpand(a,top); + } + + /* At this point a is the size of the modulus and is 0 padded + * out to its size if needed. + */ + + /* Number of tmp words */ + k=num*top; + j=k+(p->top*i*2+BN_BYTES-1+4)/BN_BYTES; + + aa=&(ctx->bn[ctx->tos++]); + d= &(ctx->bn[ctx->tos++]); + r= &(ctx->bn[ctx->tos++]); + t= &(ctx->bn[ctx->tos++]); + + if (bn_wexpand(rr_in,top) == NULL) goto err; + if (bn_wexpand(d,top*4) == NULL) goto err; /* *2? */ + if (bn_wexpand(r,top*2) == NULL) goto err; + if (bn_wexpand(aa,j) == NULL) goto err; + if (bn_wexpand(t,top*2) == NULL) goto err; + dp=d->d; + rp=r->d; + tmp=t->d; + rr=mont->RR.d; + + n0=mont->n0; + np=mont->N.d; + + vall[0]=aa->d; + + bn_mul_normal(tmp,a->d,top,rr,top); + bn_from_montgomery_words(vall[0],tmp,np,top,n0); + + if (num > 1) + { + bn_sqr_normal(tmp,vall[0],top,dp); + bn_from_montgomery_words(dp,tmp,np,top,n0); + + for (i=1; i>1],sizeof(BN_ULONG)*top); + while (sqr != 0) + { +#ifdef BN_SURRENDER + /* + * Check this at the start of the loop rather than the + * end so we avoid as many of the bn operations as possible + */ + if (surrender != NULL) + { + if (surrender->callback(surrender,0xff, count++) != 0) + { + goto err; + } + } +#endif /* BN_SURRENDER */ + + /* + * check the abort flag at the start of the loop so we avoid + * as many of the bn operations as possible + */ + if (BN_CTX_get_flags(ctx, BN_CTX_FLG_ABORT)) + { + goto err; + } + + for (i=0; i>1],top); + bn_from_montgomery_words(rp,tmp,np,top,n0); + } + else + { + bn_mul_normal(tmp,rp,top,a->d,top); + bn_from_montgomery_words(rr_in->d,tmp,np,top,n0); + goto end; + } + } + + /* if (mul != 1) */ + { + for (i=top; id,rp,np,top,n0); + } +end: /* Use this to avoid an if */ + + /* + * Even if the operation has completed successfully, if the + * abort flag is set we want to abort so that anything + * calling this function will know not to continue. + */ + if (BN_CTX_get_flags(ctx, BN_CTX_FLG_ABORT)) + { + goto err; + } + + rr_in->top=top; + bn_fix_top(rr_in); + ret=1; +err: +#ifdef BN_SURRENDER + /* + * Even if the operation completed successfully we want to abort + * if the abort flag has been is set, so that subsequent operations + * do not continue. Allow the caller change to return value and + * cause the function to fail. + */ + if (surrender != NULL) + { + if (surrender->callback(surrender, 0xff, -1) != 0) + { + ret = 0; + } + } +#endif + if ((in_mont == NULL) && (mont != NULL)) BN_MONT_CTX_free(mont); + ctx->tos=tos; + return(ret); + } +/* #endif */ + diff --git a/build/tools/acsign/bn_me.c b/build/tools/acsign/bn_me.c new file mode 100644 index 00000000..250967c9 --- /dev/null +++ b/build/tools/acsign/bn_me.c @@ -0,0 +1,251 @@ +/* $Id$ */ +/* + * Copyright (C) 1998-2002 RSA Security Inc. All rights reserved. + * + * This work contains proprietary information of RSA Security. + * Distribution is limited to authorized licensees of RSA + * Security. Any unauthorized reproduction, distribution or + * modification of this work is strictly prohibited. + * + */ +/** + * @file bn_me.c + * @brief BN method functions and utilities + */ +#include "bn_lcl.h" +#include "bn_thx.h" + +#if !(defined(NO_SPLIT) && defined(SPLIT_FILE)) + +#ifdef NO_SPLIT +#define SPLIT_BN_MOD_EXP_METH_DEFAULT +#define SPLIT_BN_MONT_CTX_USEIT +#define SPLIT_BN_ME_CTX_NAME +#define SPLIT_BN_ME_CTX_NEW +#define SPLIT_BN_ME_CTX_FREE +#define SPLIT_BN_ME_CTX_SET +#define SPLIT_BN_ME_CTX_MOD_EXP +#endif /* NO_SPLIT */ + +#ifndef NOPROTO +BN_ME_METH *bn_mod_exp_meth_default(int fallback); +#else +BN_ME_METH *bn_mod_exp_meth_default(); +#endif + +#ifdef SPLIT_BN_MOD_EXP_METH_DEFAULT + +/** + * Returns the default mod_exp method, choose first available in the + * method table or a hardwired default + * + * @param fallback [In] Flag for choice of method + * - 0 use the first value in the method table + * - 1 use the all encompassing default method + * + * @pre BN_default_init() has been run setting up library + * + * @post + * + * @internal uses global table bnme[] and counter me_num + * + * @internal only read the BN_ME_METH_INFO table no need for locking + * + * @relates BN_library_init + * @relates BN_default_init + * @relates BN_ME_CTX_new + * @relates BN_ME_CTX_free + * + */ +BN_ME_METH *bn_mod_exp_meth_default(int fallback) +{ + const BN_ME_METH *tmp; + +#ifndef BN_LIBRARY_SMALL + BN_ME_METH_INFO *meth_info; + + if(!fallback && ((meth_info = BN_bnme_get_info(0)) != NULL)) + { + tmp = meth_info->meth(); + } + else +#endif + { + /* set in bn.h or override bn_thx.h */ + tmp = BN_ME_METH_DEFAULT(); + } + return((BN_ME_METH *)tmp); +} +#endif + +#ifdef SPLIT_BN_MONT_CTX_USEIT + +#ifndef NOPROTO +int bn_mont_ctx_useit(int argi); +#else +int bn_mont_ctx_useit(); +#endif + +/** + * Montgomery method default useit function + * + * @param argi [In] reserved for future use + * + * @return 0 success + */ +int bn_mont_ctx_useit(int argi) + { + return(0); + } +#endif + +#ifdef SPLIT_BN_ME_CTX_NAME +/** + * Retrieve the BN method name string + * + * @param ctx [In] Method + * + * @pre ctx is valid and not NULL + * + * @return string pointer + */ +char *BN_ME_CTX_name(BN_ME_CTX *ctx) + { + return(ctx->meth->name); + } +#endif + +#ifdef SPLIT_BN_ME_CTX_NEW +/** + * Allocate a new BN_ME_CTX structure and assign method + * + * @param meth [In] Method to assign + * @param in [Out] Result BN_ME_CTX + * + * @note if meth is NULL system method default is used + * + * @retval pointer to BN_ME_CTX success + * @retval NULL failure + */ +BN_ME_CTX *BN_ME_CTX_new(BN_ME_METH *meth, BN_ME_CTX **in) + { + int i; + BN_ME_CTX *ret; + + if (in == NULL) + in= &ret; + + if (meth == NULL) + meth=bn_mod_exp_meth_default(0); + i=meth->init_ctx(meth,in); + if (i) + return(NULL); + else + return(*in); + } +#endif + +#ifdef SPLIT_BN_ME_CTX_FREE +/** + * Free context method + * + * @param mctx [In] Context object to be deallocated + * + * @note it is the callers responsibility to set mctx to NULL after free + * + * @pre mctx is not NULL and is valide BN_ME_CTX + */ +void BN_ME_CTX_free(BN_ME_CTX *mctx) + { + mctx->meth->free_ctx(mctx); + } +#endif + +#ifdef SPLIT_BN_ME_CTX_SET +/** + * Set value to BN_ME_CTX by identifier + * + * @param mctx [In] Method context object + * @param b [In] Big number + * @param cmd [In] Command identifier + * @param flags [In] Mask operation directives + * @param ctx [In] BN data store + * + * @note cmd values: + * @li BN_ME_SET_MOD will cause a method lookup is method not set + * + * @note passes control to set method in mctx + * + * @return result of mctx method set operation + */ +int BN_ME_CTX_set(BN_ME_CTX *mctx, BIGNUM *b, int cmd, int flags, BN_CTX *ctx) + { +#ifndef BN_LIBRARY_SMALL +#ifndef NOPROTO + const BN_ME_METH *(*meth)(void); +#else + const BN_ME_METH *(*meth)(); +#endif +#endif /* BN_LIBRARY_SMALL */ + + /* If we are loading the modulus, and we do not have the + * no_lookup flag (without a null method) + */ + if ((cmd == BN_ME_SET_MOD) && + ((mctx->meth != NULL) && !(flags & BN_ME_SET_FLG_NO_LOOKUP))) + { +#ifndef BN_LIBRARY_SMALL + meth=(const BN_ME_METH*(*)(void)) + BN_bnme_get(BN_num_bits(b),BN_BNME_F_BITS); + if (meth != NULL) + mctx->meth=meth(); + else +#endif + mctx->meth=bn_mod_exp_meth_default(1); + } + return(mctx->meth->set(mctx,b,cmd,flags,ctx)); + } +#endif + +#ifdef SPLIT_BN_ME_CTX_MOD_EXP +/** + * Call method mod_exp operation + * + * @param mctx [In] Method context object + * @param ret [Out] Result + * @param a [In] Base value + * @param p [In] Exponent + * @param m [In] Modulus + * @param ctx [In] Data storage + * + * @pre a must be expandable + * + * @return 0 success + */ +int BN_ME_CTX_mod_exp(BN_ME_CTX *mctx, BIGNUM *ret, BIGNUM *a, BIGNUM *p, + BIGNUM *m, BN_CTX *ctx) + { + int wtop; + + /* Before we call the method, make sure we are not doing 0^p, + * and p is not 0 or 1 */ + if (BN_is_zero(a)) + { BN_zero(ret); return(0); } + if (p != NULL) + { + if (BN_is_zero(p)) + { BN_one(ret); return(0); } + else if (BN_is_one(p)) + { BN_copy(ret,a); return(0); } + } + + /* wtop is the size 'a' needs to be for the method */ + wtop=mctx->meth->num; + bn_zexpand(a,wtop); + + return(mctx->meth->mod_exp(mctx,ret,a,p,ctx)); + } +#endif + +#endif + diff --git a/build/tools/acsign/bn_mont.c b/build/tools/acsign/bn_mont.c new file mode 100644 index 00000000..6fc5fea4 --- /dev/null +++ b/build/tools/acsign/bn_mont.c @@ -0,0 +1,231 @@ +/* $Id$ */ +/* + * Copyright (C) 1998-2002 RSA Security Inc. All rights reserved. + * + * This work contains proprietary information of RSA Security. + * Distribution is limited to authorized licensees of RSA + * Security. Any unauthorized reproduction, distribution or + * modification of this work is strictly prohibited. + * + */ + +#undef BN_DEBUG +#include "bn_lcl.h" + +#if !(defined(NO_SPLIT) && defined(SPLIT_FILE)) + +#ifdef NO_SPLIT +#define SPLIT_BN_MOD_MUL_MONTGOMERY +#define SPLIT_BN_FROM_MONTGOMERY +#define SPLIT_BN_MONT_CTX_NEW +#define SPLIT_BN_MONT_CTX_INIT +#define SPLIT_BN_MONT_CTX_FREE +#define SPLIT_BN_MONT_CTX_COPY +#endif /* NO_SPLIT */ + +/* #define DEBUG */ + +#ifdef SPLIT_BN_MOD_MUL_MONTGOMERY +/* We multiply a by b, generating a number 2n words long. + * This is then reduced by the montogmery number, which is + * n words. */ +int BN_mod_mul_montgomery(r,a,b,mont,ctx) +BIGNUM *r,*a,*b; +BN_MONT_CTX *mont; +BN_CTX *ctx; + { + BIGNUM *tmp,*tmp2; + int ret=0,w; + int i; + + bn_check_top(a); + bn_check_top(b); + + w=mont->riw; + + tmp= &(ctx->bn[ctx->tos++]); + tmp2= &(ctx->bn[ctx->tos++]); + bn_check_top(tmp); + bn_check_top(tmp2); + if (bn_wexpand(tmp,w+w) == NULL) goto err; + if (bn_wexpand(r,w+w) == NULL) goto err; + + if (a == b) + { + if (bn_wexpand(tmp2,w+w) == NULL) goto err; +#ifdef BN_SQR_COMBA + if ((a->top == 8) && (a->top == b->top)) + bn_sqr_comba8(tmp->d,a->d); + else +#endif + { + bn_sqr_normal(tmp->d,a->d,a->top,tmp2->d); + } + } + else + { +#ifdef BN_MUL_COMBA + if (a->top == 8) + bn_mul_comba8(tmp->d,a->d,b->d); + else +#endif + { + bn_mul_normal(tmp->d,a->d,a->top,b->d,b->top); + } + } + + i=a->top+b->top; + while (i<(w+w)) + { + tmp->d[i]=0; + i++; + } + tmp->top=w+w; + tmp->neg=0; + + /* reduce from aRR to aR */ + /* tmp->d is 2w words */ +#if 0 + bn_fix_top(tmp); + BN_from_montgomery_words(r,tmp,mont,ctx); +#else + bn_from_montgomery_words(r->d,tmp->d,mont->N.d,w,mont->n0); +#endif + r->top=w; + r->neg=0; + bn_fix_top(r); + + ret=1; +err: + ctx->tos-=2; + return(ret); + } +#endif + +#ifdef SPLIT_BN_FROM_MONTGOMERY +int BN_from_montgomery(ret,a,mont,ctx) +BIGNUM *ret; +BIGNUM *a; +BN_MONT_CTX *mont; +BN_CTX *ctx; + { + BIGNUM *n,*r; + BN_ULONG *np,*rp,n0,v0,v1,*nrp; + int al,nl,max,i; + int retn=0; + + bn_check_top(a); + + r= &(ctx->bn[ctx->tos]); + + if (!BN_copy(r,a)) goto err1; + n= &(mont->N); + + /* mont->riw is the size of mont->N in bits/words */ + al=mont->riw; + + nl=n->top; + if ((al == 0) || (nl == 0)) { r->top=0; return(1); } + + max=(nl+al+1); /* allow for overflow (no?) XXX */ + if (bn_wexpand(r,max) == NULL) goto err1; + if (bn_wexpand(ret,max) == NULL) goto err1; + + r->neg=a->neg^n->neg; + np=n->d; + rp=r->d; + nrp= &(r->d[nl]); + + /* clear the top words of T */ + for (i=r->top; id[i]=0; + + r->top=max; + n0=mont->n0; + + v1=0; + for (i=0; iriw will be a multiple of the word size */ + (void)BN_rshift(ret,r,mont->riw*BN_BITS2); + + if (BN_ucmp(ret, &(mont->N)) >= 0) + { + (void)BN_usub(ret,ret,&(mont->N)); /* XXX */ + } + retn=1; + +err1: + return(retn); + } +#endif + +#ifdef SPLIT_BN_MONT_CTX_NEW +BN_MONT_CTX *BN_MONT_CTX_new() + { + BN_MONT_CTX *ret; + + if ((ret=(BN_MONT_CTX *)Malloc(sizeof(BN_MONT_CTX))) == NULL) + return(NULL); + + BN_MONT_CTX_init(ret); + ret->flags=BN_FLG_MALLOCED; + return(ret); + } +#endif + +#ifdef SPLIT_BN_MONT_CTX_INIT +void BN_MONT_CTX_init(ctx) +BN_MONT_CTX *ctx; + { + ctx->use_word=0; + ctx->riw=0; + BN_init(&(ctx->RR)); + BN_init(&(ctx->N)); + BN_init(&(ctx->Ni)); + ctx->flags=0; + } +#endif + +#ifdef SPLIT_BN_MONT_CTX_FREE +void BN_MONT_CTX_free(mont) +BN_MONT_CTX *mont; + { + BN_free(&(mont->RR)); + BN_free(&(mont->N)); + BN_free(&(mont->Ni)); + if (mont->flags & BN_FLG_MALLOCED) + Free(mont); + } +#endif + +#ifdef SPLIT_BN_MONT_CTX_COPY +BN_MONT_CTX *BN_MONT_CTX_copy(to, from) +BN_MONT_CTX *to, *from; + { + if (to == from) return(to); + + (void)BN_copy(&(to->RR),&(from->RR)); + (void)BN_copy(&(to->N),&(from->N)); + (void)BN_copy(&(to->Ni),&(from->Ni)); + bn_zexpand(&(to->RR),from->riw); + bn_zexpand(&(to->Ni),from->riw); + to->use_word=from->use_word; + to->riw=from->riw; + to->n0=from->n0; + return(to); + } +#endif + +#endif diff --git a/build/tools/acsign/bn_ms_w.c b/build/tools/acsign/bn_ms_w.c new file mode 100644 index 00000000..1ddd40e3 --- /dev/null +++ b/build/tools/acsign/bn_ms_w.c @@ -0,0 +1,121 @@ +/* $Id$ */ +/* + * Copyright (C) 1998-2002 RSA Security Inc. All rights reserved. + * + * This work contains proprietary information of RSA Security. + * Distribution is limited to authorized licensees of RSA + * Security. Any unauthorized reproduction, distribution or + * modification of this work is strictly prohibited. + * + */ + +#include "bn_lcl.h" + +int BN_MONT_CTX_set_word(mont,mod,ctx) +BN_MONT_CTX *mont; +BIGNUM *mod; +BN_CTX *ctx; + { + BIGNUM Ri,*R; +#if 1 + BN_ULONG tmod,rr; +#else + BN_ULONG buf[2]; + BIGNUM tmod; +#endif + + + bn_check_top(mod); + if (mod->top == 0) return(0); + + R= &(mont->RR); /* grab RR as a temp */ + if (BN_copy(&(mont->N),mod) == NULL) /* Set N */ + return(0); + + BN_init(&Ri); + + mont->use_word=1; + +/* EAY is this number of words to shift, or the number to shift to end up + * with a '1' in the next word? + * for 8 bit words, is 0x01ab == 1 or 2 + */ + mont->riw=(BN_num_bits(mod)+(BN_BITS2-1))/BN_BITS2; + if (!BN_zero(R)) return(0); + + /* We are now setting a number which is larger than our current + * one after we do the shift + */ + if (!BN_set_bit(R,BN_BITS2)) + goto err; + +#if 0 + tmod.d=buf; + tmod.top=1; + tmod.max=mod->max; + tmod.neg=mod->neg; + buf[0]=mod->d[0]&BN_MASK2; + buf[1]=0; + + if ((BN_mod_inverse(&Ri,R,&tmod,ctx)) == NULL) + goto err; +#else + tmod=mod->d[0]; + rr=BN_mod_inverse_word(tmod); + if (!BN_set_word(&Ri,rr)) + goto err; +#endif + + /* R*Ri */ + if (!BN_lshift(&Ri,&Ri,BN_BITS2)) + goto err; + + if (!BN_is_zero(&Ri)) + (void)BN_sub_word(&Ri,1); + else + { + /* This is not common..., 1 in BN_MASK2, + * It happens when buf[0] was == 1. So for 8 bit, + * this is 1/256, 16bit, 1 in 2^16 etc. + */ + if (!BN_set_word(&Ri,BN_MASK2)) + goto err; + } +#if 0 + BN_div(&Ri,NULL,&Ri,&tmod,ctx); + //BN_div_word(&Ri,tmod); + mont->n0=Ri.d[0]; +#else +#if 0 + { + BN_ULONG h,l; + + h=l=0; + if (Ri.top >= 2) h=Ri.d[1]; + if (Ri.top >= 1) l=Ri.d[0]; + mont->n0=bn_div_words(h,l,tmod); + } +#endif + mont->n0=bn_div_words( + (Ri.top >= 2)?Ri.d[1]:0, + (Ri.top >= 1)?Ri.d[0]:0, + tmod); +#endif + /* mod->top=z; */ + + /* setup RR for conversions */ + (void)BN_zero(&(mont->RR)); + if (!BN_set_bit(&(mont->RR),mont->riw*2*BN_BITS2)) + goto err; + (void)BN_mod(&(mont->RR),&(mont->RR),&(mont->N),ctx); + bn_zexpand(&(mont->RR),mont->riw); +#if 0 + bn_zexpand(&(mont->Ni),mont->riw); /*This is not used? */ +#endif + +err: + BN_free(&Ri); + return(1); + } + + diff --git a/build/tools/acsign/bn_mul.c b/build/tools/acsign/bn_mul.c new file mode 100644 index 00000000..4dd0e235 --- /dev/null +++ b/build/tools/acsign/bn_mul.c @@ -0,0 +1,779 @@ +/* $Id$ */ +/* + * Copyright (C) 1998-2002 RSA Security Inc. All rights reserved. + * + * This work contains proprietary information of RSA Security. + * Distribution is limited to authorized licensees of RSA + * Security. Any unauthorized reproduction, distribution or + * modification of this work is strictly prohibited. + * + */ + +#include "bn_lcl.h" + +#if !(defined(NO_SPLIT) && defined(SPLIT_FILE)) + +#ifdef NO_SPLIT +#define SPLIT_BN_MUL_RECURSIVE +#define SPLIT_BN_MUL_PART_RECURSIVE +#define SPLIT_BN_MUL_LOW_RECURSIVE +#define SPLIT_BN_MUL_HIGH +#define SPLIT_BN_MUL +#define SPLIT_BN_MUL_NORMAL +#define SPLIT_BN_MUL_LOW_NORMAL +#endif /* NO_SPLIT */ + +#ifdef SMALL_CODE_SIZE +#undef BN_RECURSION_MUL +#endif + +#ifdef BN_RECURSION_MUL +#if 0 /* Replaced by bn_mul_rec_words() */ +/* r is 2*n2 words in size, + * a and b are both n2 words in size. + * n2 must be a power of 2. + * We multiply and return the result. + * t must be 2*n2 words in size + * We calulate + * a[0]*b[0] + * a[0]*b[0]+a[1]*b[1]+(a[0]-a[1])*(b[1]-b[0]) + * a[1]*b[1] + */ +#ifdef SPLIT_BN_MUL_RECURSIVE +void bn_mul_recursive(r,a,b,n2,t) +BN_ULONG *r,*a,*b; +int n2; +BN_ULONG *t; + { + int n=n2/2,c1,c2; + unsigned int neg,zero; + BN_ULONG ln,lo,*p; + +#ifdef BN_COUNT +printf(" bn_mul_recursive %d * %d\n",n2,n2); +#endif +#ifdef BN_MUL_COMBA +/* if (n2 == 4) + { + bn_mul_comba4(r,a,b); + return; + } + else */ if (n2 == 8) + { + bn_mul_comba8(r,a,b); + return; + } +#endif + if (n2 < BN_MUL_RECURSIVE_SIZE_NORMAL) + { + /* This should not happen */ + bn_mul_normal(r,a,n2,b,n2); + return; + } + /* r=(a[0]-a[1])*(b[1]-b[0]) */ + c1=bn_cmp_words(a,&(a[n]),n); + c2=bn_cmp_words(&(b[n]),b,n); + zero=neg=0; + switch (c1*3+c2) + { + case -4: + bn_sub_words(t, &(a[n]),a, n); /* - */ + bn_sub_words(&(t[n]),b, &(b[n]),n); /* - */ + break; + case -3: + zero=1; + break; + case -2: + bn_sub_words(t, &(a[n]),a, n); /* - */ + bn_sub_words(&(t[n]),&(b[n]),b, n); /* + */ + neg=1; + break; + case -1: + case 0: + case 1: + zero=1; + break; + case 2: + bn_sub_words(t, a, &(a[n]),n); /* + */ + bn_sub_words(&(t[n]),b, &(b[n]),n); /* - */ + neg=1; + break; + case 3: + zero=1; + break; + case 4: + bn_sub_words(t, a, &(a[n]),n); + bn_sub_words(&(t[n]),&(b[n]),b, n); + break; + } + +#ifdef BN_MUL_COMBA + if (n == 4) + { + if (!zero) + bn_mul_comba4(&(t[n2]),t,&(t[n])); + else + Memset(&(t[n2]),0,8*sizeof(BN_ULONG)); + + bn_mul_comba4(r,a,b); + bn_mul_comba4(&(r[n2]),&(a[n]),&(b[n])); + } + else if (n == 8) + { + if (!zero) + bn_mul_comba8(&(t[n2]),t,&(t[n])); + else + Memset(&(t[n2]),0,16*sizeof(BN_ULONG)); + + bn_mul_comba8(r,a,b); + bn_mul_comba8(&(r[n2]),&(a[n]),&(b[n])); + } + else +#endif + { + p= &(t[n2*2]); + if (!zero) + bn_mul_recursive(&(t[n2]),t,&(t[n]),n,p); + else + Memset(&(t[n2]),0,n2*sizeof(BN_ULONG)); + bn_mul_recursive(r,a,b,n,p); + bn_mul_recursive(&(r[n2]),&(a[n]),&(b[n]),n,p); + } + + /* t[32] holds (a[0]-a[1])*(b[1]-b[0]), c1 is the sign + * r[10] holds (a[0]*b[0]) + * r[32] holds (b[1]*b[1]) + */ + + c1=(int)bn_add_words(t,r,&(r[n2]),n2); + + if (neg) /* if t[32] is negative */ + { + c1-=(int)bn_sub_words(&(t[n2]),t,&(t[n2]),n2); + } + else + { + /* Might have a carry */ + c1+=(int)bn_add_words(&(t[n2]),&(t[n2]),t,n2); + } + + /* t[32] holds (a[0]-a[1])*(b[1]-b[0])+(a[0]*b[0])+(a[1]*b[1]) + * r[10] holds (a[0]*b[0]) + * r[32] holds (b[1]*b[1]) + * c1 holds the carry bits + */ + c1+=(int)bn_add_words(&(r[n]),&(r[n]),&(t[n2]),n2); + if (c1) + { + p= &(r[n+n2]); + lo= *p; + ln=(lo+c1)&BN_MASK2; + *p=ln; + + /* The overflow will stop before we over write + * words we should not overwrite */ + if (ln < (BN_ULONG)c1) + { + do { + p++; + lo= *p; + ln=(lo+1)&BN_MASK2; + *p=ln; + } while (ln == 0); + } + } + } +#endif +#endif + +#if 0 +#ifdef SPLIT_BN_MUL_PART_RECURSIVE +/* n+tn is the word length + * t must be n*4 is size, as does r */ +void bn_mul_part_recursive(r,a,b,tn,n,t) +BN_ULONG *r,*a,*b; +int tn,n; +BN_ULONG *t; + { + int i,j,n2=n*2; + int c1; + BN_ULONG ln,lo,*p; + +#ifdef BN_COUNT +printf(" bn_mul_part_recursive %d * %d\n",tn+n,tn+n); +#endif + if (n < 8) + { + i=tn+n; + bn_mul_normal(r,a,i,b,i); + return; + } + + /* r=(a[0]-a[1])*(b[1]-b[0]) */ + bn_sub_words(t, a, &(a[n]),n); /* + */ + bn_sub_words(&(t[n]),b, &(b[n]),n); /* - */ + +#ifdef BN_MUL_COMBA +/* if (n == 4) + { + bn_mul_comba4(&(t[n2]),t,&(t[n])); + bn_mul_comba4(r,a,b); + bn_mul_normal(&(r[n2]),&(a[n]),tn,&(b[n]),tn); + Memset(&(r[n2+tn*2]),0,sizeof(BN_ULONG)*(n2-tn*2)); + } + else */ if (n == 8) + { + bn_mul_comba8(&(t[n2]),t,&(t[n])); + bn_mul_comba8(r,a,b); + bn_mul_normal(&(r[n2]),&(a[n]),tn,&(b[n]),tn); + Memset(&(r[n2+tn*2]),0,sizeof(BN_ULONG)*(n2-tn*2)); + } + else +#endif + { + p= &(t[n2*2]); + bn_mul_recursive(&(t[n2]),t,&(t[n]),n,p); + bn_mul_recursive(r,a,b,n,p); + i=n/2; + /* If there is only a bottom half to the number, + * just do it */ + j=tn-i; + if (j == 0) + { + bn_mul_recursive(&(r[n2]),&(a[n]),&(b[n]),i,p); + Memset(&(r[n2+i*2]),0,sizeof(BN_ULONG)*(n2-i*2)); + } + else if (j > 0) /* eg, n == 16, i == 8 and tn == 11 */ + { + bn_mul_part_recursive(&(r[n2]),&(a[n]),&(b[n]), + j,i,p); + Memset(&(r[n2+tn*2]),0, + sizeof(BN_ULONG)*(n2-tn*2)); + } + else /* (j < 0) eg, n == 16, i == 8 and tn == 5 */ + { + Memset(&(r[n2]),0,sizeof(BN_ULONG)*n2); + if (tn < BN_MUL_RECURSIVE_SIZE_NORMAL) + { + bn_mul_normal(&(r[n2]),&(a[n]),tn,&(b[n]),tn); + } + else + { + for (;;) + { + i/=2; + if (i < tn) + { + bn_mul_part_recursive(&(r[n2]), + &(a[n]),&(b[n]), + tn-i,i,p); + break; + } + else if (i == tn) + { + bn_mul_recursive(&(r[n2]), + &(a[n]),&(b[n]), + i,p); + break; + } + } + } + } + } + + /* t[32] holds (a[0]-a[1])*(b[1]-b[0]), c1 is the sign + * r[10] holds (a[0]*b[0]) + * r[32] holds (b[1]*b[1]) + */ + + c1=(int)bn_add_words(t,r,&(r[n2]),n2); + c1-=(int)bn_sub_words(&(t[n2]),t,&(t[n2]),n2); + + /* t[32] holds (a[0]-a[1])*(b[1]-b[0])+(a[0]*b[0])+(a[1]*b[1]) + * r[10] holds (a[0]*b[0]) + * r[32] holds (b[1]*b[1]) + * c1 holds the carry bits + */ + c1+=(int)bn_add_words(&(r[n]),&(r[n]),&(t[n2]),n2); + if (c1) + { + p= &(r[n+n2]); + lo= *p; + ln=(lo+c1)&BN_MASK2; + *p=ln; + + /* The overflow will stop before we over write + * words we should not overwrite */ + if (ln < (BN_ULONG)c1) + { + do { + p++; + lo= *p; + ln=(lo+1)&BN_MASK2; + *p=ln; + } while (ln == 0); + } + } + } +#endif +#endif + +#if 0 +#ifdef SPLIT_BN_MUL_LOW_RECURSIVE +/* a and b must be the same size, which is n2. + * r must be n2 words and t must be n2*2 + */ +void bn_mul_low_recursive(r,a,b,n2,t) +BN_ULONG *r,*a,*b; +int n2; +BN_ULONG *t; + { + int n=n2/2; + +#ifdef BN_COUNT +printf(" bn_mul_low_recursive %d * %d\n",n2,n2); +#endif + + bn_mul_recursive(r,a,b,n,&(t[0])); + if (n >= BN_MUL_LOW_RECURSIVE_SIZE_NORMAL) + { + bn_mul_low_recursive(&(t[0]),&(a[0]),&(b[n]),n,&(t[n2])); + bn_add_words(&(r[n]),&(r[n]),&(t[0]),n); + bn_mul_low_recursive(&(t[0]),&(a[n]),&(b[0]),n,&(t[n2])); + bn_add_words(&(r[n]),&(r[n]),&(t[0]),n); + } + else + { + bn_mul_low_normal(&(t[0]),&(a[0]),&(b[n]),n); + bn_mul_low_normal(&(t[n]),&(a[n]),&(b[0]),n); + bn_add_words(&(r[n]),&(r[n]),&(t[0]),n); + bn_add_words(&(r[n]),&(r[n]),&(t[n]),n); + } + } +#endif +#endif + +#ifdef SPLIT_BN_MUL_HIGH +#if 0 +/* a and b must be the same size, which is n2. + * r must be n2 words and t must be n2*2 + * l is the low words of the output. + * t must be n2*3 + */ +void bn_mul_high(r,a,b,l,n2,t) +BN_ULONG *r,*a,*b,*l; +int n2; +BN_ULONG *t; + { + int i,n; + int c1,c2; + int neg,oneg,zero; + BN_ULONG ll,lc,*lp,*mp; + +#ifdef BN_COUNT +printf(" bn_mul_high %d * %d\n",n2,n2); +#endif + n=n2/2; + + /* Calculate (al-ah)*(bh-bl) */ + neg=zero=0; + c1=bn_cmp_words(&(a[0]),&(a[n]),n); + c2=bn_cmp_words(&(b[n]),&(b[0]),n); + switch (c1*3+c2) + { + case -4: + bn_sub_words(&(r[0]),&(a[n]),&(a[0]),n); + bn_sub_words(&(r[n]),&(b[0]),&(b[n]),n); + break; + case -3: + zero=1; + break; + case -2: + bn_sub_words(&(r[0]),&(a[n]),&(a[0]),n); + bn_sub_words(&(r[n]),&(b[n]),&(b[0]),n); + neg=1; + break; + case -1: + case 0: + case 1: + zero=1; + break; + case 2: + bn_sub_words(&(r[0]),&(a[0]),&(a[n]),n); + bn_sub_words(&(r[n]),&(b[0]),&(b[n]),n); + neg=1; + break; + case 3: + zero=1; + break; + case 4: + bn_sub_words(&(r[0]),&(a[0]),&(a[n]),n); + bn_sub_words(&(r[n]),&(b[n]),&(b[0]),n); + break; + } + + oneg=neg; + /* t[10] = (a[0]-a[1])*(b[1]-b[0]) */ + /* r[10] = (a[1]*b[1]) */ +#ifdef BN_MUL_COMBA + if (n == 8) + { + bn_mul_comba8(&(t[0]),&(r[0]),&(r[n])); + bn_mul_comba8(r,&(a[n]),&(b[n])); + } + else +#endif + { +#ifdef BN_MUL_RECURSION + bn_mul_recursive(&(t[0]),&(r[0]),&(r[n]),n,&(t[n2])); + bn_mul_recursive(r,&(a[n]),&(b[n]),n,&(t[n2])); +#else + bn_mul_normal(&(t[0]),&(r[0]),n,&(r[n]),n); + bn_mul_normal(r,&(a[n]),n,&(b[n]),n); +#endif + } + + /* s0 == low(al*bl) + * s1 == low(ah*bh)+low((al-ah)*(bh-bl))+low(al*bl)+high(al*bl) + * We know s0 and s1 so the only unknown is high(al*bl) + * high(al*bl) == s1 - low(ah*bh+s0+(al-ah)*(bh-bl)) + * high(al*bl) == s1 - (r[0]+l[0]+t[0]) + */ + if (l != NULL) + { + lp= &(t[n2+n]); + c1=(int)bn_add_words(lp,&(r[0]),&(l[0]),n); + } + else + { + c1=0; + lp= &(r[0]); + } + + if (neg) + neg=(int)bn_sub_words(&(t[n2]),lp,&(t[0]),n); + else + { + bn_add_words(&(t[n2]),lp,&(t[0]),n); + neg=0; + } + + if (l != NULL) + { + bn_sub_words(&(t[n2+n]),&(l[n]),&(t[n2]),n); + } + else + { + lp= &(t[n2+n]); + mp= &(t[n2]); + for (i=0; i 0) + { + lc=c1; + do { + ll=(r[i]+lc)&BN_MASK2; + r[i++]=ll; + lc=(lc > ll); + } while (lc); + } + else + { + lc= -c1; + do { + ll=r[i]; + r[i++]=(ll-lc)&BN_MASK2; + lc=(lc > ll); + } while (lc); + } + } + if (c2 != 0) /* Add starting at r[1] */ + { + i=n; + if (c2 > 0) + { + lc=c2; + do { + ll=(r[i]+lc)&BN_MASK2; + r[i++]=ll; + lc=(lc > ll); + } while (lc); + } + else + { + lc= -c2; + do { + ll=r[i]; + r[i++]=(ll-lc)&BN_MASK2; + lc=(lc > ll); + } while (lc); + } + } + } +#endif +#endif +#endif + +#ifdef SPLIT_BN_MUL +int BN_mul(r,a,b,ctx) +BIGNUM *r,*a,*b; +BN_CTX *ctx; + { + int top,al,bl,neg; + BIGNUM *rr; +#ifdef BN_RECURSION_MUL + BIGNUM *t; + int i,j,k,l; +#endif + +#ifdef BN_COUNT +printf("BN_mul %d * %d\n",a->top,b->top); +#endif + + bn_check_top(a); + bn_check_top(b); + bn_check_top(r); + + al=a->top; + bl=b->top; + + if ((al == 0) || (bl == 0)) + { + (void)BN_zero(r); + return(1); + } + top=al+bl; + neg=a->neg^b->neg; + + if ((r == a) || (r == b)) + rr= &(ctx->bn[ctx->tos+1]); + else + rr=r; + + if (bn_wexpand(rr,top) == NULL) return(0); + rr->top=top; + +#if defined(BN_MUL_COMBA) || defined(BN_RECURSION_MUL) + if (al == bl) + { +# ifdef BN_MUL_COMBA +/* if (al == 4) + { + bn_mul_comba4(rr->d,a->d,b->d); + goto end; + } + else */ if (al == 8) + { + bn_mul_comba8(rr->d,a->d,b->d); + goto end; + } + else +# endif +#ifdef BN_RECURSION_MUL + if (al < BN_MULL_SIZE_NORMAL) +#endif + { + bn_mul_normal(rr->d,a->d,al,b->d,bl); + goto end; + } +# ifdef BN_RECURSION_MUL + goto symetric; +# endif + } +#endif +#ifdef BN_RECURSION_MUL + else if ((al < BN_MULL_SIZE_NORMAL) || (bl < BN_MULL_SIZE_NORMAL)) + { + bn_mul_normal(rr->d,a->d,al,b->d,bl); + goto end; + } + else + { + i=(al-bl); + if ((i == 1) && !BN_get_flags(b,BN_FLG_STATIC_DATA)) + { + bn_wexpand(b,al); + b->d[bl]=0; + bl++; + goto symetric; + } + else if ((i == -1) && !BN_get_flags(a,BN_FLG_STATIC_DATA)) + { + bn_wexpand(a,bl); + a->d[al]=0; + al++; + goto symetric; + } + } +#endif + +#ifdef BN_RECURSION_MUL +normal_mul: +#endif + bn_mul_normal(rr->d,a->d,al,b->d,bl); + +#ifdef BN_RECURSION_MUL + if (0) + { +symetric: + /* symetric and > 4 */ + /* 16 or larger */ + l=BN_num_bits_word((BN_ULONG)al); + j=1<<(l-1); + k=j+j; + t= &(ctx->bn[ctx->tos]); + if (al == j) /* exact multiple */ + { + BN_REC rec; + rec.depth=l-5; + rec.n=j; + rec.mul=bn_mul_comba8; + rec.sqr=bn_sqr_comba8; + if (bn_wexpand(t,k+k) == NULL) + return(0); + if (bn_wexpand(rr,k) == NULL) + return(0); + bn_mul_rec_words(rr->d,a->d,b->d,t->d,&rec); + } + else + goto normal_mul; +#if 0 + { + bn_zexpand(a,k); + bn_zexpand(b,k); + bn_wexpand(t,k); + bn_wexpand(rr,k); + bn_mul_part_recursive(rr->d,a->d,b->d,al-j,j,t->d); + } +#endif + } +#endif +#if defined(BN_MUL_COMBA) || defined(BN_RECURSION_MUL) +end: +#endif + + r->neg=neg; + bn_fix_top(rr); + if (r != rr) (void)BN_copy(r,rr); + return(1); + } +#endif + + +#ifdef SPLIT_BN_MUL_NORMAL +void bn_mul_normal(r,a,na,b,nb) +BN_ULONG *r,*a; +int na; +BN_ULONG *b; +int nb; + { + BN_ULONG *rr; + +#ifdef BN_COUNT +printf(" bn_mul_normal %d * %d\n",na,nb); +#endif + /* asymetric and >= 4 */ +#if 0 + if ((na == nb) && (na == 8)) + { + bn_mul_normal(r,a,na,b,nb); + return; + } +#endif + + if (na < nb) + { + int itmp; + BN_ULONG *ltmp; + + itmp=na; na=nb; nb=itmp; + ltmp=a; a=b; b=ltmp; + + } + rr= &(r[na]); + rr[0]=bn_mul_words(r,a,na,b[0]); + + for (;;) + { + if (--nb <= 0) return; + rr[1]=bn_mul_add_words(&(r[1]),a,na,b[1]); + if (--nb <= 0) return; + rr[2]=bn_mul_add_words(&(r[2]),a,na,b[2]); + if (--nb <= 0) return; + rr[3]=bn_mul_add_words(&(r[3]),a,na,b[3]); + if (--nb <= 0) return; + rr[4]=bn_mul_add_words(&(r[4]),a,na,b[4]); + rr+=4; + r+=4; + b+=4; + } + } +#endif + +#ifdef SPLIT_BN_MUL_LOW_NORMAL +void bn_mul_low_normal(r,a,b,n) +BN_ULONG *r,*a,*b; +int n; + { +#ifdef BN_COUNT +printf(" bn_mul_low_normal %d * %d\n",n,n); +#endif + (void)bn_mul_words(r,a,n,b[0]); + + for (;;) + { + if (--n <= 0) return; + (void)bn_mul_add_words(&(r[1]),a,n,b[1]); + if (--n <= 0) return; + (void)bn_mul_add_words(&(r[2]),a,n,b[2]); + if (--n <= 0) return; + (void)bn_mul_add_words(&(r[3]),a,n,b[3]); + if (--n <= 0) return; + (void)bn_mul_add_words(&(r[4]),a,n,b[4]); + r+=4; + b+=4; + } + } +#endif +#endif diff --git a/build/tools/acsign/bn_r_exp.c b/build/tools/acsign/bn_r_exp.c new file mode 100644 index 00000000..9465e402 --- /dev/null +++ b/build/tools/acsign/bn_r_exp.c @@ -0,0 +1,134 @@ +/* $Id$ */ +/* + * Copyright (C) 1998-2002 RSA Security Inc. All rights reserved. + * + * This work contains proprietary information of RSA Security. + * Distribution is limited to authorized licensees of RSA + * Security. Any unauthorized reproduction, distribution or + * modification of this work is strictly prohibited. + * + */ + +#include "bn_lcl.h" + +/* #ifdef RECP_MUL_MOD */ +int BN_mod_exp_recp(r,a,p,m,ctx) +BIGNUM *r; +BIGNUM *a; +BIGNUM *p; +BIGNUM *m; +BN_CTX *ctx; + { + int i,j,bits,ret=0,wstart,wend,window,wvalue; + int start=1,ts=0; + BIGNUM *aa; + BIGNUM val[BN_EXP_TABLE_SIZE]; + BN_RECP_CTX recp; + + bits=BN_num_bits(p); + + if (BN_is_zero(a)) + { (void)BN_zero(r); return(1); } + if (BN_is_zero(p)) + { (void)BN_one(r); return(1); } + if (BN_is_one(p)) + { (void)BN_copy(r,a); return(1); } + + BN_RECP_CTX_init(&recp); + if (BN_RECP_CTX_set(&recp,m,ctx) <= 0) goto err; + + BN_init(&(val[0])); + ts=1; + + aa= &(ctx->bn[ctx->tos++]); + + if (!BN_mod(&(val[0]),a,m,ctx)) goto err; /* 1 */ + if (!BN_mod_mul_reciprocal(aa,&(val[0]),&(val[0]),&recp,ctx)) + goto err; /* 2 */ + + if (bits <= 17) /* This is probably 3 or 0x10001, so just do singles */ + window=1; + else if (bits >= 256) + window=5; /* max size of window */ + else if (bits >= 128) + window=4; + else + window=3; + + j=1<<(window-1); + for (i=1; i>1]),&recp,ctx)) + goto err; + + /* move the 'window' down further */ + wstart-=wend+1; + wvalue=0; + start=0; + if (wstart < 0) break; + } + ret=1; +err: + ctx->tos--; + for (i=0; in; + int n2=n/2; + int c1,neg=0; + + + if (rec->depth == 0) + { + /* t needs to have space for 4*n words + * The multiply needs to be a n2*n2 -> n + */ +#ifndef NOPROTO + void (PRE_CCONV CCONV *rmul)(BN_ULONG *r, BN_ULONG *a, BN_ULONG *b); +#else + void (PRE_CCONV CCONV *rmul)(); +#endif + rmul=rec->mul; + +#ifdef _EXTRA_ARG_ + /* THIS IS WRONG - there is no extra int arg on the end */ + rmul(&(r[0]),&(a[0]),&(b[0]),n2); + rmul(&(r[n]),&(a[n2]),&(b[n2]),n2); +#else + rmul(&(r[0]),&(a[0]),&(b[0])); + rmul(&(r[n]),&(a[n2]),&(b[n2])); +#endif + + if (bn_sub_words(&(tt[n+0]), &(a[0]),&(a[n2]),n2)) + { + bn_2s_comp(&(tt[n+ 0]),&(tt[n+ 0]),n2); + neg=1; + } + else + neg=0; + if (bn_sub_words(&(tt[n+n2]),&(b[n2]),&(b[0]),n2)) + { + neg^=1; + bn_2s_comp(&(tt[n+n2]),&(tt[n+n2]),n2); + } + +#ifdef _EXTRA_ARG_ + rmul(&(tt[0]),&(tt[n+0]),&(tt[n+n2]),n2); +#else + rmul(&(tt[0]),&(tt[n+0]),&(tt[n+n2])); +#endif + } + else /* Leaf node */ + { + /* t has 4*n words taken per call. + * Since n is half the size per call, we need + * 6*n words for our current level and all sub levels + * The multiply needs to be a n2*n2 -> n + */ + rec->depth--; + rec->n=n2; + bn_mul_rec_words(&(r[0]), &(a[0]), &(b[0]), &(tt[n+n]),rec); + bn_mul_rec_words(&(r[n]), &(a[n2]),&(b[n2]), &(tt[n+n]),rec); + + neg=0; + if (bn_sub_words(&(tt[n+0]), &(a[0]),&(a[n2]),n2)) + { + bn_2s_comp(&(tt[n+ 0]),&(tt[n+ 0]),n2); + neg=1; + } + else + neg=0; + if (bn_sub_words(&(tt[n+n2]),&(b[n2]),&(b[0]),n2)) + { + neg^=1; + bn_2s_comp(&(tt[n+n2]),&(tt[n+n2]),n2); + } + + bn_mul_rec_words(&(tt[0]),&(tt[n]),&(tt[n+n2]),&(tt[n+n]),rec); + rec->n=n; + rec->depth++; + } + + c1=bn_add_words(&(tt[n]),&(r[0]),&(r[n]),n); + + if (neg) + c1-=bn_sub_words(&(tt[0]),&(tt[n]),&(tt[0]),n); + else + c1+=bn_add_words(&(tt[0]),&(tt[n]),&(tt[0]),n); + tt[n]=c1; + c1=bn_add_words(&(r[n2]),&(r[n2]),&(tt[0]),n+1); + if (c1) + { + tt= &(r[n+n2+1]); + do { + } while (++(*(tt++)) == 0); + } + } +#endif + +#ifdef SPLIT_BN_SQR_REC_WORDS +/* tt needs to be 3*n words */ +void bn_sqr_rec_words(r,a,tt,rec) +BN_ULONG *r,*a,*tt; +BN_REC *rec; + { + int n=rec->n; + int n2=n/2; + int c1; + + if (rec->depth == 0) + { + /* t needs to have space for 4*n words + * The multiply needs to be a n2*n2 -> n + */ +#ifndef NOPROTO + void (PRE_CCONV CCONV *rsqr)(BN_ULONG *r, BN_ULONG *a); +#else + void (PRE_CCONV CCONV *rsqr)(); +#endif + + rsqr=rec->sqr; + +#ifdef _EXTRA_ARG_ + /* THIS IS WRONG - there is no extra int arg on the end */ + rsqr(&(r[0]), &(a[0]), n2); + rsqr(&(r[n]), &(a[n2]),n2); +#else + rsqr(&(r[0]), &(a[0])); + rsqr(&(r[n]), &(a[n2])); +#endif + + if (bn_sub_words(&(tt[n+0]), &(a[0]),&(a[n2]),n2)) + bn_2s_comp(&(tt[n+0]),&tt[n+0], n2); + +#ifdef _EXTRA_ARG_ + rsqr(&(tt[0]),&(tt[n]), n2); +#else + rsqr(&(tt[0]),&(tt[n])); +#endif + + } + else /* Leaf node */ + { + /* t has 4*n words taken per call. + * Since n is half the size per call, we need + * 6*n words for our current level and all sub levels + * The multiply needs to be a n2*n2 -> n + */ + rec->depth--; + rec->n=n2; + bn_sqr_rec_words(&(r[0]), &(a[0]), &(tt[n+n]),rec); + bn_sqr_rec_words(&(r[n]), &(a[n2]),&(tt[n+n]),rec); + + if (bn_sub_words(&(tt[n+0]), &(a[0]),&(a[n2]),n2)) + bn_2s_comp(&(tt[n+0]),&tt[n+0], n2); + bn_sqr_rec_words(&(tt[0]),&(tt[n]),&(tt[n+n]),rec); + + rec->n=n; + rec->depth++; + } + + c1=bn_add_words(&(tt[n]),&(r[0]),&(r[n]),n); + c1-=bn_sub_words(&(tt[0]),&(tt[n]),&(tt[0]),n); + + tt[n]=c1; + c1=bn_add_words(&(r[n2]),&(r[n2]),&(tt[0]),n+1); + if (c1) + { + tt= &(r[n+n2+1]); + do { + } while (++(*(tt++)) == 0); + } + } +#endif + +#endif /* !NO_SPLIT || !SPLIT_FILE */ diff --git a/build/tools/acsign/bn_recp.c b/build/tools/acsign/bn_recp.c new file mode 100644 index 00000000..e762a819 --- /dev/null +++ b/build/tools/acsign/bn_recp.c @@ -0,0 +1,202 @@ +/* $Id$ */ +/* + * Copyright (C) 1998-2002 RSA Security Inc. All rights reserved. + * + * This work contains proprietary information of RSA Security. + * Distribution is limited to authorized licensees of RSA + * Security. Any unauthorized reproduction, distribution or + * modification of this work is strictly prohibited. + * + */ + +#include "bn_lcl.h" + +void BN_RECP_CTX_init(recp) +BN_RECP_CTX *recp; + { + BN_init(&(recp->N)); + BN_init(&(recp->Nr)); + recp->num_bits=0; + recp->flags=0; + } + +BN_RECP_CTX *BN_RECP_CTX_new() + { + BN_RECP_CTX *ret; + + if ((ret=(BN_RECP_CTX *)Malloc(sizeof(BN_RECP_CTX))) == NULL) + return(NULL); + + BN_RECP_CTX_init(ret); + ret->flags=BN_FLG_MALLOCED; + return(ret); + } + +void BN_RECP_CTX_free(recp) +BN_RECP_CTX *recp; + { + BN_free(&(recp->N)); + BN_free(&(recp->Nr)); + if (recp->flags & BN_FLG_MALLOCED) + Free(recp); + } + +int BN_RECP_CTX_set(recp,d,ctx) +BN_RECP_CTX *recp; +BIGNUM *d; +BN_CTX *ctx; + { + (void)BN_copy(&(recp->N),d); + (void)BN_zero(&(recp->Nr)); + recp->num_bits=BN_num_bits(d); + recp->shift=0; + ctx=ctx; /* Stop warning */ + return(1); + } + +int BN_mod_mul_reciprocal(r, x, y, recp, ctx) +BIGNUM *r; +BIGNUM *x; +BIGNUM *y; +BN_RECP_CTX *recp; +BN_CTX *ctx; + { + int ret=0; + BIGNUM *a; + + bn_check_top(x); + bn_check_top(y); + + a= &(ctx->bn[ctx->tos++]); + if (y != NULL) + { + if (x == y) + { if (!BN_sqr(a,x,ctx)) goto err; } + else + { if (!BN_mul(a,x,y,ctx)) goto err; } + } + else + a=x; /* Just do the mod */ + + (void)BN_div_recp(NULL,r,a,recp,ctx); + ret=1; +err: + ctx->tos--; + return(ret); + } + +int BN_div_recp(dv,rem,m,recp,ctx) +BIGNUM *dv; +BIGNUM *rem; +BIGNUM *m; +BN_RECP_CTX *recp; +BN_CTX *ctx; + { + int i,j,tos,ret=0,ex; + BIGNUM *a,*b,*d,*r; + + bn_check_top(m); + + tos=ctx->tos; + a= &(ctx->bn[ctx->tos++]); + b= &(ctx->bn[ctx->tos++]); + if (dv != NULL) + d=dv; + else + d= &(ctx->bn[ctx->tos++]); + if (rem != NULL) + r=rem; + else + r= &(ctx->bn[ctx->tos++]); + + if (BN_ucmp(m,&(recp->N)) < 0) + { + (void)BN_zero(d); + (void)BN_copy(r,m); + ctx->tos=tos; + return(1); + } + + /* We want the remainder + * Given input of ABCDEF / ab + * we need multiply ABCDEF by 3 digests of the reciprocal of ab + * + */ + i=BN_num_bits(m); + + j=recp->num_bits*2; + if (j > i) + { + i=j; + ex=0; + } + else + { + ex=(i-j)/2; + } + + j=i/2; + + if (i != recp->shift) + recp->shift=BN_reciprocal(&(recp->Nr),&(recp->N), + i,ctx); + + if (!BN_rshift(a,m,j-ex)) goto err; + if (!BN_mul(b,a,&(recp->Nr),ctx)) goto err; + if (!BN_rshift(d,b,j+ex)) goto err; + d->neg=0; + if (!BN_mul(b,&(recp->N),d,ctx)) goto err; + if (!BN_usub(r,m,b)) goto err; + r->neg=0; + + j=0; +#if 1 + while (BN_ucmp(r,&(recp->N)) >= 0) + { + if (j++ > 2) + { +#ifndef NO_ERR + BNerr(BN_F_BN_DIV_RECP,BN_R_BAD_RECIPROCAL); +#endif + goto err; + } + if (!BN_usub(r,r,&(recp->N))) goto err; + if (!BN_add_word(d,1)) goto err; + } +#endif + + r->neg=BN_is_zero(r)?0:m->neg; + d->neg=m->neg^recp->N.neg; + ret=1; +err: + ctx->tos=tos; + return(ret); + } + +/* len is the expected size of the result + * We actually calculate with an extra word of precision, so + * we can do faster division if the remainder is not required. + */ +int BN_reciprocal(r,m,len,ctx) +BIGNUM *r; +BIGNUM *m; +int len; +BN_CTX *ctx; + { + int ret= -1; + BIGNUM t; + + bn_check_top(m); + + BN_init(&t); + + (void)BN_zero(&t); + if (!BN_set_bit(&t,len)) goto err; + + if (!BN_div(r,NULL,&t,m,ctx)) goto err; + ret=len; +err: + BN_free(&t); + return(ret); + } + diff --git a/build/tools/acsign/bn_rsh.c b/build/tools/acsign/bn_rsh.c new file mode 100644 index 00000000..909cd9a1 --- /dev/null +++ b/build/tools/acsign/bn_rsh.c @@ -0,0 +1,123 @@ +/* $Id$ */ +/* + * Copyright (C) 1998-2002 RSA Security Inc. All rights reserved. + * + * This work contains proprietary information of RSA Security. + * Distribution is limited to authorized licensees of RSA + * Security. Any unauthorized reproduction, distribution or + * modification of this work is strictly prohibited. + * + */ + +#include "bn_lcl.h" + +#if !(defined(NO_SPLIT) && defined(SPLIT_FILE)) + +#ifdef NO_SPLIT +#define SPLIT_BN_RSHIFT1 +#define SPLIT_BN_RSHIFT +#endif /* NO_SPLIT */ + +#ifdef SPLIT_BN_RSHIFT1 +#ifdef SMALL_CODE_SIZE +int BN_rshift1(r, a) +BIGNUM *r; +BIGNUM *a; + { + return(BN_rshift(r,a,1)); + } + +#else + +int BN_rshift1(r, a) +BIGNUM *r; +BIGNUM *a; + { + BN_ULONG *ap,*rp,t,c; + int i; + + bn_check_top(a); + + if (BN_is_zero(a)) + { + BN_zero(r); + return(1); + } + if (a != r) + { + if (bn_wexpand(r,a->top) == NULL) return(0); + r->top=a->top; + r->neg=a->neg; + } + ap=a->d; + rp=r->d; + c=0; + for (i=a->top-1; i>=0; i--) + { + t=ap[i]; + rp[i]=((t>>1)&BN_MASK2)|c; + c=(t&1)?BN_TBIT:0; + } + bn_fix_top(r); + return(1); + } +#endif +#endif + +#ifdef SPLIT_BN_RSHIFT +int BN_rshift(r, a, n) +BIGNUM *r; +BIGNUM *a; +int n; + { + int i,j,nw,lb,rb; + BN_ULONG *t,*f; + BN_ULONG l,tmp; + + bn_check_top(a); + +#ifndef SMALL_CODE_SIZE + if (n == 1) return(BN_rshift1(r,a)); +#endif + nw=n/BN_BITS2; + rb=n%BN_BITS2; + lb=BN_BITS2-rb; + if (nw > a->top) + { + (void)BN_zero(r); + return(1); + } + if (r != a) + { + if (bn_wexpand(r,a->top-nw+1+1/*???*/) == NULL) return(0); + r->neg=a->neg; + } + + f= &(a->d[nw]); + t=r->d; + j=a->top-nw; + r->top=j; + + if (rb == 0) + { + for (i=j+1; i > 0; i--) + *(t++)= *(f++); + } + else + { + l= *(f++); + for (i=1; i>rb)&BN_MASK2; + l= *(f++); + *(t++) =(tmp|(l<>rb)&BN_MASK2; + *t=0; + } + bn_fix_top(r); + return(1); + } +#endif + +#endif diff --git a/build/tools/acsign/bn_sqr.c b/build/tools/acsign/bn_sqr.c new file mode 100644 index 00000000..4762cd9d --- /dev/null +++ b/build/tools/acsign/bn_sqr.c @@ -0,0 +1,273 @@ +/* $Id$ */ +/* + * Copyright (C) 1998-2002 RSA Security Inc. All rights reserved. + * + * This work contains proprietary information of RSA Security. + * Distribution is limited to authorized licensees of RSA + * Security. Any unauthorized reproduction, distribution or + * modification of this work is strictly prohibited. + * + */ + +#include "bn_lcl.h" + +#ifdef SMALL_CODE_SIZE +#undef BN_RECURSION_SQR +#endif + +#if !(defined(NO_SPLIT) && defined(SPLIT_FILE)) + +#ifdef NO_SPLIT +#define SPLIT_BN_SQR +#define SPLIT_BN_SQR_NORMAL +#define SPLIT_BN_RECURSION_SQR +#endif /* NO_SPLIT */ + +#ifdef SPLIT_BN_SQR +/* r must not be a */ +int BN_sqr(r, a, ctx) +BIGNUM *r; +BIGNUM *a; +BN_CTX *ctx; + { + int max,al; + BIGNUM *tmp,*rr; + +#ifdef BN_COUNT +printf("BN_sqr %d * %d\n",a->top,a->top); +#endif + bn_check_top(a); + tmp= &(ctx->bn[ctx->tos]); + rr=(a != r)?r: (&ctx->bn[ctx->tos+1]); + + al=a->top; + if (al <= 0) + { + r->top=0; + return(1); + } + + max=(al+al); + if (bn_wexpand(rr,max) == NULL) return(0); + + rr->top=max; + rr->neg=0; + + if (al == 4) + { +#ifndef BN_SQR_COMBA + BN_ULONG t[8]; + bn_sqr_normal(rr->d,a->d,4,t); +#else + bn_sqr_comba4(rr->d,a->d); +#endif + } + else if (al == 8) + { +#ifndef BN_SQR_COMBA + BN_ULONG t[16]; + bn_sqr_normal(rr->d,a->d,8,t); +#else + bn_sqr_comba8(rr->d,a->d); +#endif + } + else + { +#if 1 && defined(BN_RECURSION_SQR) + if (al < BN_SQR_RECURSIVE_SIZE_NORMAL) + { + BN_ULONG t[BN_SQR_RECURSIVE_SIZE_NORMAL*2]; + bn_sqr_normal(rr->d,a->d,al,t); + } + else + { + int j,l,k; + + l=BN_num_bits_word((BN_ULONG)al); + j=1<<(l-1); + k=j+j; + if ((al == j) && !BN_get_flags(a,BN_FLG_STATIC_DATA)) + { + BN_REC rec; + if (bn_wexpand(tmp,k*2) == NULL) return(0); + rec.depth=l-5; + rec.n=j; + rec.mul=bn_mul_comba8; + rec.sqr=bn_sqr_comba8; + + bn_sqr_rec_words(rr->d,a->d,tmp->d,&rec); + } + else + { + if (bn_wexpand(tmp,max) == NULL) return(0); + bn_assert(al*2 <= max); + bn_sqr_normal(rr->d,a->d,al,tmp->d); + } + } +#else + if (bn_wexpand(tmp,max) == NULL) return(0); + bn_assert(al*2 <= max); + bn_sqr_normal(rr->d,a->d,al,tmp->d); +#endif +#ifdef BN_DEBUG + tmp->top=0; +#endif + } + + if ((max > 0) && (rr->d[max-1] == 0)) rr->top--; + if (rr != r) (void)BN_copy(r,rr); + return(1); + } +#endif + +#ifdef SPLIT_BN_SQR_NORMAL +/* tmp must have 2*n words */ +void bn_sqr_normal(r, a, n, tmp) +BN_ULONG *r; +BN_ULONG *a; +int n; +BN_ULONG *tmp; + { + int i,j,max; + BN_ULONG *ap,*rp,m; + + max=n*2; + ap=a; + rp=r; + rp[0]=rp[max-1]=0; + rp++; + j=n; + + if (--j > 0) + { + m= (*ap++); + rp[j]=bn_mul_words(rp,ap,j,m); + rp+=2; + } + + for (i=n-2; i>0; i--) + { + j--; + m= *(ap++); + rp[j]=bn_mul_add_words(rp,ap,j,m); + rp+=2; + } + + (void)bn_add_words(r,r,r,max); + + /* There will not be a carry */ + + bn_sqr_words(tmp,a,n); + + (void)bn_add_words(r,r,tmp,max); + } +#endif + +#if 0 /* replaced by bn_sqr_rec_words() AND this has bugs */ +#ifdef SPLIT_BN_RECURSION_SQR +#ifdef BN_RECURSION_SQR +/* r is 2*n words in size, + * a and b are both n words in size. + * n must be a power of 2. + * We multiply and return the result. + * t must be 2*n words in size + * We calulate + * a[0]*b[0] + * a[0]*b[0]+a[1]*b[1]+(a[0]-a[1])*(b[1]-b[0]) + * a[1]*b[1] + */ +void bn_sqr_recursive(r,a,n2,t) +BN_ULONG *r,*a; +int n2; +BN_ULONG *t; + { + int n=n2/2; + int zero,c1; + BN_ULONG ln,lo,*p; + +#ifdef BN_COUNT +printf(" bn_sqr_recursive %d * %d\n",n2,n2); +#endif + if (n2 == 4) + { +#ifndef BN_SQR_COMBA + bn_sqr_normal(r,a,4,t); +#else + bn_sqr_comba4(r,a); +#endif + return; + } + else if (n2 == 8) + { +#ifndef BN_SQR_COMBA + bn_sqr_normal(r,a,8,t); +#else + bn_sqr_comba8(r,a); +#endif + return; + } + if (n2 < BN_SQR_RECURSIVE_SIZE_NORMAL) + { + bn_sqr_normal(r,a,n2,t); + return; + } + /* r=(a[0]-a[1])*(a[1]-a[0]) */ + c1=bn_cmp_words(a,&(a[n]),n); + zero=0; + if (c1 > 0) + bn_sub_words(t,a,&(a[n]),n); + else if (c1 < 0) + bn_sub_words(t,&(a[n]),a,n); + else + zero=1; + + /* The result will always be negative unless it is zero */ + p= &(t[n2*2]); + + if (!zero) + bn_sqr_recursive(&(t[n2]),t,n,p); + else + Memset(&(t[n2]),0,n*sizeof(BN_ULONG)); + bn_sqr_recursive(r,a,n,p); + bn_sqr_recursive(&(r[n2]),&(a[n]),n,p); + + /* t[32] holds (a[0]-a[1])*(a[1]-a[0]), it is negative or zero + * r[10] holds (a[0]*b[0]) + * r[32] holds (b[1]*b[1]) + */ + + c1=(int)(bn_add_words(t,r,&(r[n2]),n2)); + + /* t[32] is negative */ + c1-=(int)(bn_sub_words(&(t[n2]),t,&(t[n2]),n2)); + + /* t[32] holds (a[0]-a[1])*(a[1]-a[0])+(a[0]*a[0])+(a[1]*a[1]) + * r[10] holds (a[0]*a[0]) + * r[32] holds (a[1]*a[1]) + * c1 holds the carry bits + */ + c1+=(int)(bn_add_words(&(r[n]),&(r[n]),&(t[n2]),n2)); + if (c1) + { + p= &(r[n+n2]); + lo= *p; + ln=(lo+c1)&BN_MASK2; + *p=ln; + + /* The overflow will stop before we over write + * words we should not overwrite */ + if (ln < (BN_ULONG)c1) + { + do { + p++; + lo= *p; + ln=(lo+1)&BN_MASK2; + *p=ln; + } while (ln == 0); + } + } + } +#endif +#endif +#endif +#endif diff --git a/build/tools/acsign/bn_wdiv.c b/build/tools/acsign/bn_wdiv.c new file mode 100644 index 00000000..d76752a4 --- /dev/null +++ b/build/tools/acsign/bn_wdiv.c @@ -0,0 +1,100 @@ +/* $Id$ */ +/* + * Copyright (C) 1998-2002 RSA Security Inc. All rights reserved. + * + * This work contains proprietary information of RSA Security. + * Distribution is limited to authorized licensees of RSA + * Security. Any unauthorized reproduction, distribution or + * modification of this work is strictly prohibited. + * + */ + +#include "bn_lcl.h" + +#ifndef OPT_BN_ASM + +#ifndef BN_DIV_WORDS +#define BN_DIV_WORDS +#endif + +#endif + +#ifdef BN_DIV_WORDS +#if defined(BN_LLONG) +BN_ULONG bn_div_words(h,l,d) +BN_ULONG h,l,d; + { + return((BN_ULONG)(((((BN_ULLONG)h)< ((BN_ULONG)1)<= d) h-=d; + + if (i) + { + d=(d<>(BN_BITS2-i)))&BN_MASK2; + l=(l<>BN_BITS4)&BN_MASK2lh; + dl=(d&BN_MASK2l); + for (;;) + { + if ((h>>BN_BITS4) == dh) + q=BN_MASK2l; + else + q=h/dh; + + for (;;) + { + t=(h-q*dh)&BN_MASK2; + if ((t&BN_MASK2h) || + (((dl*q)&BN_MASK2) <= (( + (t<>BN_BITS4)&BN_MASK2lh))&BN_MASK2))) + break; + q--; + } + th=(q*dh)&BN_MASK2; + tl=(q*dl)&BN_MASK2; + t=(tl>>BN_BITS4); + tl=(tl&BN_MASK2lh)<>BN_BITS4))&BN_MASK2; + l=(l&BN_MASK2l)<top-1; i>=0; i--) + { +#ifndef BN_LLONG + ret=((ret<d[i]>>BN_BITS4)&BN_MASK2l))%w; + ret=((ret<d[i]&BN_MASK2l))%w; +#else + ret=(BN_ULLONG)(((ret<<(BN_ULLONG)BN_BITS2)|a->d[i])% + (BN_ULLONG)w); +#endif + } + return((BN_ULONG)ret); + } +#endif + +#ifdef SPLIT_BN_DIV_WORD +BN_ULONG BN_div_word(a, w) +BIGNUM *a; +BN_ULONG w; + { + BN_ULONG ret; + int i; + + if (a->top == 0) return(0); + ret=0; + w=w&BN_MASK2;//w&=BN_MASK2; + for (i=a->top-1; i>=0; i--) + { + BN_ULONG l,d; + + l=a->d[i]; + d=bn_div_words(ret,l,w); + ret=(l-((d*w)&BN_MASK2))&BN_MASK2; + a->d[i]=d; + } + if ((a->top > 0) && (a->d[a->top-1] == 0)) + a->top--; + return(ret); + } +#endif + +#ifdef SPLIT_BN_ADD_WORD +int BN_add_word(a, w) +BIGNUM *a; +BN_ULONG w; + { + BN_ULONG l; + int i; + + if (w == 0) return(1); + bn_check_top(a); + + w=w&BN_MASK2;//w&=BN_MASK2; + if (a->neg) + { + if(a->top > 1) + { + a->neg=0; + i=BN_sub_word(a,w); + a->neg=1; + return(i); + } + else + { /* a->top == 1, it cannot be 0 */ + l=a->d[0]; + if (l > w) + a->d[0]=l-w; + else if (l < w) + { + a->neg=0; + a->d[0]=w-l; + } + else + { + a->neg=0; + a->top=0; + } + return(1); + } + } + w=w&BN_MASK2;//w&=BN_MASK2; + if (bn_wexpand(a,a->top+1) == NULL) return(0); + a->d[a->top]=0; + i=0; + for (;;) + { + l=(a->d[i]+(BN_ULONG)w)&BN_MASK2; + a->d[i]=l; + if (w > l) + w=1; + else + break; + i++; + } + if (i >= a->top) + a->top++; + return(1); + } +#endif + +#ifdef SPLIT_BN_SUB_WORD +int BN_sub_word(a, w) +BIGNUM *a; +BN_ULONG w; + { + int i; + + bn_check_top(a); + if (w == 0) return(1); + + w=w&BN_MASK2;//w&=BN_MASK2; + if (a->neg) + { + a->neg=0; + i=BN_add_word(a,w); + a->neg=1; + return(i); + } + + w=w&BN_MASK2;//w&=BN_MASK2; + if (a->top <= 1) + { + BN_ULONG l; + + if (a->top == 0) + { + if (bn_wexpand(a,1) == NULL) return(0); + a->d[0]=w; + a->neg = 1; + a->top = 1; + return(1); + } + l=a->d[0]; + if (l == w) + a->top=0; + else if (l > w) + a->d[0]=l-w; + else + { + a->neg=1; + a->d[0]=w-l; + } + return(1); + } + i=0; + for (;;) + { + if (a->d[i] >= w) + { + a->d[i]-=w; + break; + } + else + { + a->d[i]=(a->d[i]-w)&BN_MASK2; + i++; + w=1; + } + } + if ((a->d[i] == 0) && (i == (a->top-1))) + a->top--; + return(1); + } +#endif + +#ifdef SPLIT_BN_MUL_WORD +int BN_mul_word(a,w) +BIGNUM *a; +BN_ULONG w; + { + BN_ULONG ll; + + w=w&BN_MASK2;//w&=BN_MASK2; + if (a->top) + { + ll=bn_mul_words(a->d,a->d,a->top,w); + if (ll) + { + if (bn_wexpand(a,a->top+1) == NULL) return(0); + a->d[a->top++]=ll; + } + } + return(1); + } +#endif + +#endif diff --git a/build/tools/acsign/include/acsign.h b/build/tools/acsign/include/acsign.h new file mode 100644 index 00000000..6e11663c --- /dev/null +++ b/build/tools/acsign/include/acsign.h @@ -0,0 +1,49 @@ +#ifndef _ACSIGN_H_ +#define _ACSIGN_H_ + +#include "sha.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define HASHContext SHA_CTX + +#define HASHReset( _context ) \ +do{ \ + (_context)->sha_block = NULL; \ + (void)SHA1_Init( _context ); \ +} while(0) +#define HASHUpdate( _context, _ptr, _len ) (void)SHA1_Update( _context, _ptr, _len ) +#define HASHGetDigest( _context, _ptr ) (void)SHA1_Final( _ptr, _context ) + +// +BOOL ACSign_Encrypto(void *sign, const void *key, const void *data, int length); +BOOL ACSign_Decrypto(void *buf, const void *key, const void *sign, int length); + +// +int ACSign_DigestUnit( + void* buffer, // 出力領域 + const void* buf, // データへのポインタ + unsigned int len // データの長さ + ); + +// +int ACSign_CompareUnit( + const void* decedHash, // ACSign_Decryptoの出力 + const void* digest // ACSign_DigestUnitの出力 + ); + +// +int ACSign_GetKey( + void* dest_ptr, // 出力データへのポインタ + unsigned int dest_len, // 出力データの長さ + const void* src_ptr, // 入力データへのポインタ + unsigned int src_len // 入力データの長さ + ); + +#ifdef __cplusplus +} +#endif + +#endif //_ACSIGN_H_ diff --git a/build/tools/acsign/include/acsign_gcd.h b/build/tools/acsign/include/acsign_gcd.h new file mode 100644 index 00000000..2c9853e6 --- /dev/null +++ b/build/tools/acsign/include/acsign_gcd.h @@ -0,0 +1,30 @@ +#ifndef _ACSIGN_GCD_H_ +#define _ACSIGN_GCD_H_ + +#include "sha.h" +#include "format_sign.h" +#include "format_rom.h" +#include "acsign.h" + +#ifdef __cplusplus +extern "C" { +#endif + +// +int ACSign_Final( + GCDHeader* header, // ヘッダへのポインタ + void* buffer, // 出力領域 + const void* key + ); + +// +int ACSign_DigestHeader( + void* buffer, // 出力領域 + GCDHeader* header // データへのポインタ + ); + +#ifdef __cplusplus +} +#endif + +#endif //_ACSIGN_GCD_H_ diff --git a/build/tools/acsign/include/acsign_nand.h b/build/tools/acsign/include/acsign_nand.h new file mode 100644 index 00000000..97afa980 --- /dev/null +++ b/build/tools/acsign/include/acsign_nand.h @@ -0,0 +1,30 @@ +#ifndef _ACSIGN_NAND_H_ +#define _ACSIGN_NAND_H_ + +#include "sha.h" +#include "format_sign.h" +#include "format_rom.h" +#include "acsign.h" + +#ifdef __cplusplus +extern "C" { +#endif + +// +int ACSign_Final( + NANDHeader* header, // ヘッダへのポインタ + void* buffer, // 出力領域 + const void* key + ); + +// +int ACSign_DigestHeader( + void* buffer, // 出力領域 + NANDHeader* header // データへのポインタ + ); + +#ifdef __cplusplus +} +#endif + +#endif //_ACSIGN_NAND_H_ diff --git a/build/tools/acsign/include/acsign_nor.h b/build/tools/acsign/include/acsign_nor.h new file mode 100644 index 00000000..eb097b50 --- /dev/null +++ b/build/tools/acsign/include/acsign_nor.h @@ -0,0 +1,30 @@ +#ifndef _ACSIGN_NOR_H_ +#define _ACSIGN_NOR_H_ + +#include "sha.h" +#include "format_sign.h" +#include "format_rom.h" +#include "acsign.h" + +#ifdef __cplusplus +extern "C" { +#endif + +// +int ACSign_Final( + NORHeader* header, // ヘッダへのポインタ + void* buffer, // 出力領域 + const void* key + ); + +// +int ACSign_DigestHeader( + void* buffer, // 出力領域 + NORHeader* header // データへのポインタ + ); + +#ifdef __cplusplus +} +#endif + +#endif //_ACSIGN_NOR_H_ diff --git a/build/tools/acsign/include/bn.h b/build/tools/acsign/include/bn.h new file mode 100644 index 00000000..f68b3d22 --- /dev/null +++ b/build/tools/acsign/include/bn.h @@ -0,0 +1,1120 @@ +/* $Id$ */ +/* + * Copyright (C) 1998-2002 RSA Security Inc. All rights reserved. + * + * This work contains proprietary information of RSA Security. + * Distribution is limited to authorized licensees of RSA + * Security. Any unauthorized reproduction, distribution or + * modification of this work is strictly prohibited. + * + */ + +#ifndef HEADER_COMMON_BN_H +#define HEADER_COMMON_BN_H + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef FLAT_INC +#include "r_types.h" +#else +#include "../include/r_types.h" +#endif + +#if !defined(CCONV) +#define CCONV +#endif + +#ifndef PRE_CCONV +#define PRE_CCONV +#endif + +/* convert from the new to the old option names */ +#if defined(OPT_BN_LLONG) +#define BN_LLONG /* comment to make sure Configure leaves this alone */ +#endif + +#if defined(OPT_BN_MUL_LATENCY) +#define BN_MUL_LATENCY +#endif + +/* by default we have the following switched on - unless we have an option + * that switched them off for a specific platform + */ +#ifdef OPT_NO_COMBA +#define OPT_NO_BN_MUL_COMBA +#define OPT_NO_BN_SQR_COMBA +#endif + +#ifdef OPT_NO_REC +#define OPT_NO_BN_RECURSION_MUL +#define OPT_NO_BN_RECURSION_SQR +#endif + +#ifndef OPT_NO_BN_MUL_COMBA +#define BN_MUL_COMBA +#endif +#ifndef OPT_NO_BN_SQR_COMBA +#define BN_SQR_COMBA +#endif +#ifndef OPT_NO_BN_RECURSION_MUL +#define BN_RECURSION_MUL +#endif +#ifndef OPT_NO_BN_RECURSION_SQR +#define BN_RECURSION_SQR +#endif +#ifndef OPT_NO_BN_RECURSION_MONT +#undef BN_RECURSION_MONT /* DO NOT TURN THIS ON, IT IS BROKEN */ +#endif + +#if (!defined(OPT_NO_BN_MUL_COMBA) && !defined(OPT_NO_BN_SQR_COMBA)) +#if OPT_MONT_REDUCE_COMBA /* TEMP UNTIL C IS IMPLEMENTED-DEF'd ON PLATFORMS W/ ASM */ +#define BN_REDUCE_COMBA +#endif +#endif + +#define RECP_MUL_MOD +#define MONT_MUL_MOD + +#ifndef OPT_NO_BN_SURRENDER +#define BN_SURRENDER +#else +#undef BN_SURRENDER +#endif + +#ifdef SMALL_CODE_SIZE +#undef BN_MUL_COMBA /* stop modification */ +#undef BN_SQR_COMBA /* stop modification */ +#undef BN_REDUCE_COMBA /* stop modification */ +#undef BN_RECURSION_MUL /* stop modification */ +#undef BN_RECURSION_SQR /* stop modification */ +#undef BN_RECURSION_MONT /* stop modification */ +#endif + + + +/* This next option uses the C libraries (2 word)/(1 word) function. + * If it is not defined, I use my C version (which is slower). + * The reason for this flag is that when the particular C compiler + * library routine is used, and the library is linked with a different + * compiler, the library is missing. This mostly happens when the + * library is built with gcc and then linked using nornal cc. This would + * be a common occurance because gcc normally produces code that is + * 2 times faster than system compilers for the big number stuff. + * For machines with only one compiler (or shared libraries), this should + * be on. Again this in only really a problem on machines + * using "long long's", are 32bit, and are not using my assember code. */ +#if defined(MSDOS) || defined(WINDOWS) || defined(linux) +#define BN_DIV2W +#endif + +/* Only one for the following should be defined */ +/* The prime number generation stuff may not work when + * EIGHT_BIT but I don't care since I've only used this mode + * for debuging the bignum libraries */ +#undef SIXTY_FOUR_BIT_LONG +#undef SIXTY_FOUR_BIT +#undef SIXTY_BIT +#undef THIRTY_TWO_BIT +#undef THIRTY_BIT +#undef SIXTEEN_BIT +#undef EIGHT_BIT +#undef TEST_EIGHT_BIT + +#if defined(OPT_64_BIT_LONG) +#define SIXTY_FOUR_BIT_LONG +#endif +#if defined(OPT_64_BIT) +#define SIXTY_FOUR_BIT +#endif +#if defined(OPT_60_BIT) +#define SIXTY_BIT +#endif +#if defined(OPT_32_BIT) +#define THIRTY_TWO_BIT +#endif +#if defined(OPT_32_BIT_INT) +#define THIRTY_TWO_BIT +#endif +#if defined(OPT_30_BIT) +#define THIRTY_BIT +#endif +#if defined(OPT_16_BIT) +#define SIXTEEN_BIT +#endif +#if defined(OPT_8_BIT) +#define EIGHT_BIT +#endif +#if defined(OPT_8_BIT_TEST) +#define TEST_EIGHT_BIT +#endif + +/* This define is used for those few functions that 'break' when + * things are compiled for 8 bit words. Basically the size of an + * integer. + */ +#define BN_ILONG BN_ULONG + +/* assuming long is 64bit - this is the DEC Alpha + * unsigned long long is only 64 bits, don't define + * BN_LLONG for the DEC Alpha */ +#ifdef SIXTY_FOUR_BIT_LONG +#undef BN_LLONG +#define BN_ULLONG unsigned long long +#define BN_ULONG unsigned long +#define BN_LONG long +#define BN_BITS 128 +#define BN_BYTES 8 +#define BN_BITS2 64 +#define BN_BITS4 32 +#define BN_MASK (0xffffffffffffffffffffffffffffffffLL) +#define BN_MASK2 (0xffffffffffffffffL) +#define BN_MASK2l (0xffffffffL) +#define BN_MASK2lh (0xffffffffL) +#define BN_MASK2h (0xffffffff00000000L) +#define BN_MASK2h1 (0xffffffff80000000L) +#define BN_TBIT (0x8000000000000000L) +#define BN_DEC_CONV (10000000000000000000UL) +#define BN_DEC_FMT1 "%lu" +#define BN_DEC_FMT2 "%019lu" +#define BN_DEC_NUM 19 +#define BN_HEX_FMT "%016lX" +#endif + +/* This is where the long long data type is 64 bits, but long is 32. + * For machines where there are 64bit registers, this is the mode to use. + * IRIX, on R4000 and above should use this mode, along with the relevent + * assember code. Do NOT define BN_LLONG. + */ +#ifdef SIXTY_FOUR_BIT +#undef BN_LLONG /* Protect against config */ +/* #define BN_ULLONG unsigned long long */ +#ifdef WIN64 +#define BN_ULONG unsigned _int64 +#define BN_LONG _int64 +#else +#define BN_ULONG unsigned long long +#define BN_LONG long long +#endif +#define BN_BITS 128 +#define BN_BYTES 8 +#define BN_BITS2 64 +#define BN_BITS4 32 +#define BN_MASK2 (0xffffffffffffffffLL) +#define BN_MASK2l (0xffffffffL) +#define BN_MASK2lh (0xffffffffL) +#define BN_MASK2h (0xffffffff00000000LL) +#define BN_MASK2h1 (0xffffffff80000000LL) +#define BN_TBIT (0x8000000000000000LL) +#define BN_DEC_CONV (10000000000000000000ULL) +#ifdef WIN64 +#define BN_DEC_FMT1 "%I64u" +#define BN_DEC_FMT2 "%019I64u" +#define BN_DEC_NUM 19 +#define BN_HEX_FMT "%016I64X" +#else +#define BN_DEC_FMT1 "%llu" +#define BN_DEC_FMT2 "%019llu" +#define BN_DEC_NUM 19 +#define BN_HEX_FMT "%016llX" +#endif +#endif + +#ifdef SIXTY_BIT +#undef SIXTY_FOUR_BIT +#undef BN_LLONG /* Protect against config */ +/* #define BN_ULLONG unsigned long long */ +#define BN_ULONG unsigned long long +#define BN_LONG long long +#define BN_BITS 120 +#define BN_BITS2 60 +#define BN_BITS4 30 +#define BN_MASK2 (0x0fffffffffffffffLL) +#define BN_MASK2l ( 0x3fffffffL) +#define BN_MASK2lh ( 0x3fffffffL) +#define BN_MASK2h (0x0fffffffc0000000LL) +#define BN_MASK2h1 (0x0fffffffe0000000LL) +#define BN_TBIT (0x0800000000000000LL) +#define BN_DEC_CONV (100000000000000000LL) +#define BN_DEC_FMT1 "%9u" +#define BN_DEC_FMT2 "%017llu" +#define BN_DEC_NUM 17 +#define BN_HEX_FMT "%016llX" +#endif + +#ifdef THIRTY_TWO_BIT +#ifdef WIN32 +#if defined(__BORLANDC__) +#define BN_ULLONG unsigned __int64 +#else /* !__BORLANDC__ */ +#if defined(__MINGW32__) || defined(__DJGPP__) +#define BN_ULLONG unsigned long long +#else +#define BN_ULLONG unsigned _int64 +#endif /* __MINGW32__ || __DJGPP__ */ +#endif /* __BORLANDC__ */ +#else /* !WIN32 */ +#define BN_ULLONG unsigned long long +#endif /* WIN32 */ +#ifdef OPT_32_BIT_INT +#define BN_ULONG unsigned int +#define BN_LONG int +#else +#define BN_ULONG unsigned long +#define BN_LONG long +#endif +#define BN_BITS 64 +#define BN_BYTES 4 +#define BN_BITS2 32 +#define BN_BITS4 16 +/* This is needed because the Watcom compiler pre-processor + * under QNX tries to parses the 'LL' part even though it is + * never used. + */ +#ifdef BN_LLONG +#ifndef WIN32 +#define BN_MASK (0xffffffffffffffffLL) +#else +#define BN_MASK (0xffffffffffffffffL) +#endif +#endif +#define BN_MASK2 (0xffffffffL) +#define BN_MASK2l (0xffff) +#define BN_MASK2lh (0xffff) +#define BN_MASK2h1 (0xffff8000L) +#define BN_MASK2h (0xffff0000L) +#define BN_TBIT (0x80000000L) +#define BN_DEC_CONV (1000000000L) +#define BN_DEC_FMT1 "%lu" +#define BN_DEC_FMT2 "%09lu" +#define BN_DEC_NUM 9 +#define BN_HEX_FMT "%08lX" +#endif + +#ifdef THIRTY_BIT +#ifdef WIN32 +#if defined(__MINGW32__) || defined(__DJGPP__) +#define BN_ULLONG unsigned long long +#else /* ! (__MINGW32__ || __DJGPP__) */ +#define BN_ULLONG unsigned _int64 +#endif /* __MINGW32__ || __DJGPP__ */ +#else /* !WIN32 */ +#define BN_ULLONG unsigned long long +#endif /* WIN32 */ +#define BN_ULONG unsigned long +#define BN_LONG long +#define BN_BITS 60 +#define BN_BITS2 30 +#define BN_BITS4 15 +/* This is needed because the Watcom compiler pre-processor + * under QNX tries to parses the 'LL' part even though it is + * never used. + */ +#ifdef BN_LLONG +#ifndef WIN32 +#define BN_MASK (0x0fffffffffffffffLL) +#else +#define BN_MASK (0x0fffffffffffffffL) +#endif +#endif +#define BN_MASK2 (0x3fffffffL) +#define BN_MASK2l (0x7fff) +#define BN_MASK2lh (0x7fff) +#define BN_MASK2h1 (0x3fffc000L) +#define BN_MASK2h (0x3fff8000L) +#define BN_TBIT (0x20000000L) +#define BN_DEC_CONV (1000000000L) +#define BN_DEC_FMT1 "%lu" +#define BN_DEC_FMT2 "%09lu" +#define BN_DEC_NUM 9 +#define BN_HEX_FMT "%08lX" +#endif + +#ifdef SIXTEEN_BIT +#ifndef BN_DIV2W +#define BN_DIV2W +#endif +#define BN_ULLONG unsigned long +#define BN_ULONG unsigned short +#define BN_LONG short +#define BN_BITS 32 +#define BN_BYTES 2 +#define BN_BITS2 16 +#define BN_BITS4 8 +#define BN_MASK (0xffffffff) +#define BN_MASK2 (0xffff) +#define BN_MASK2l (0xff) +#define BN_MASK2lh (0xff) +#define BN_MASK2h1 (0xff80) +#define BN_MASK2h (0xff00) +#define BN_TBIT (0x8000) +#define BN_DEC_CONV (10000) +#define BN_DEC_FMT1 "%u" +#define BN_DEC_FMT2 "%04u" +#define BN_DEC_NUM 4 +#define BN_HEX_FMT "%04X" +#endif + +#ifdef TEST_EIGHT_BIT +#define EIGHT_BIT /* comment to stop editing */ +#endif + +#ifdef EIGHT_BIT +#undef BN_ILONG +#define BN_ILONG unsigned int +#ifndef BN_DIV2W +#define BN_DIV2W +#endif +#ifdef TEST_EIGHT_BIT +#define BN_ULLONG unsigned int +#define BN_ULONG unsigned int +#define BN_LONG int +#else +#define BN_ULLONG unsigned short +#define BN_ULONG unsigned char +#define BN_LONG char +#endif +#define BN_BITS 16 +#define BN_BYTES 1 +#define BN_BITS2 8 +#define BN_BITS4 4 +#define BN_MASK (0xffff) +#define BN_MASK2 (0xff) +#define BN_MASK2lh (0xf) +#define BN_MASK2l (0xf) +#define BN_MASK2h1 (0xf8) +#define BN_MASK2h (0xf0) +#define BN_TBIT (0x80) +#define BN_DEC_CONV (100) +#define BN_DEC_FMT1 "%u" +#define BN_DEC_FMT2 "%02u" +#define BN_DEC_NUM 2 +#define BN_HEX_FMT "%02X" +#endif + +#ifdef BIGNUM +#undef BIGNUM +#endif + +#define BN_FLG_MALLOCED 0x01 +#define BN_FLG_STATIC_DATA 0x02 +#define BN_FLG_FREE 0x8000 /* used for debuging */ +#define BN_CTX_FLG_ABORT 0x4000 /* used for aborting RSA operations*/ +#define BN_set_flags(b,n) ((b)->flags|=(n)) +#define BN_get_flags(b,n) ((b)->flags&(n)) +#define BN_CTX_set_flags(b,n) ((b)->flags|=(n)) +#define BN_CTX_get_flags(b,n) ((b)->flags&(n)) + +#ifndef HEADER_COMMON_BN_H_TYPEDEF_DEF +#define HEADER_COMMON_BN_H_TYPEDEF_DEF +typedef struct bignum_st BIGNUM; +typedef struct bignum_ctx BN_CTX; +typedef struct bn_mont_ctx_st BN_MONT_CTX; +typedef struct bn_recp_ctx_st BN_RECP_CTX; +typedef struct bn_prime_ctx_st BN_PRIME_CTX; +typedef struct bn_blind_ctx_st BN_BLIND_CTX; +typedef struct bn_blind_meth_st BN_BLIND_METH; +#endif + +typedef struct bn_mod_exp_meth_st BN_ME_METH; +typedef struct bn_mod_exp_ctx_st BN_ME_CTX; + + +/* The data array d must always have an extra 'valid' word in location + * bn->d[bn->max]. It must be ok for reading. This is needed if + * BN_MUL_LATENCY is defined. + */ +struct bignum_st + { + BN_ULONG *d; /* Pointer to an array of 'BN_BITS2' bit chunks. */ + int top; /* Index of last used d +1. */ + /* The next are internal book keeping for bn_expand. */ + int max; /* Size of the d array. */ + int neg; /* one if the number is negative */ + int flags; + }; + +/* Used for temp variables */ +#define BN_CTX_NUM 12 +struct bignum_ctx + { + int tos; + BIGNUM bn[BN_CTX_NUM+1]; + int flags; + R_SURRENDER *surrender; + }; + +#if defined(WIN64) || defined(__ia64__) || defined(CPU_IA64) +#define BN_USHORT unsigned int +#else +#define BN_USHORT unsigned short +#endif + +/* Used for prime number generation */ +struct bn_prime_ctx_st + { + BN_USHORT *primes; + BN_USHORT *mods; + int num_primes; + int prime_checks; + R_SURRENDER *surrender; + R_RANDOM *random; + int flags; + }; + +struct bn_blind_ctx_st + { + int init; + BN_BLIND_METH *meth; + BIGNUM B; + BIGNUM Bi; + BIGNUM mod; + }; + +struct bn_blind_meth_st + { + void (*init) (BN_BLIND_CTX *); + void (*ctx_free)(BN_BLIND_CTX *); + int (*set) (BN_BLIND_CTX *,R_RANDOM *,BIGNUM *,BIGNUM *, BN_ME_CTX *,BN_CTX *); + int (*convert) (BN_BLIND_CTX *,BIGNUM *,BN_CTX *); + int (*invert) (BN_BLIND_CTX *,BIGNUM *,BN_CTX *); + int (*update) (BN_BLIND_CTX *,BN_CTX *); + int (*copy) (BN_BLIND_CTX *,BN_BLIND_CTX *); + }; + +/* Used for montgomery multiplication */ +struct bn_mont_ctx_st + { + int use_word; /* 0 for word form, 1 for long form */ + int ri; /* number of bits in R */ + int riw; /* number of words in R */ + BIGNUM RR; /* used to convert to montgomery form */ + BIGNUM N; /* The modulus */ + BIGNUM Ni; /* The inverse of N */ + BN_ULONG n0; /* word form of inverse, normally only one of + * Ni or n0 is defined */ + int flags; + }; + +/* Used by the recursive exponentiation implementations */ +typedef struct bn_mod_exp_rec_meth_st + { +#ifndef NOPROTO + void (PRE_CCONV CCONV *mul)(BN_ULONG *r, BN_ULONG *a, BN_ULONG *b); + void (PRE_CCONV CCONV *sqr)(BN_ULONG *r, BN_ULONG *a); + void (PRE_CCONV CCONV *low_mul)(); +#else + void (PRE_CCONV CCONV *mul)(); + void (PRE_CCONV CCONV *sqr)(); + void (PRE_CCONV CCONV *low_mul)(); +#endif + } BN_MOD_EXP_REC_METH; + +/* If this flag is set, if there is a custom method for a modulus + * two times larger, use it. This is mostly mean for use on the + * itanium where a 512*512 routine is 10 times faster than the C + * code version, so we need to detect that we can turn of CRT + * for RSA if there is an native IA64 512*512 present. + */ +#define BN_ME_FLG_FAST_ASM 0x0001 + +struct bn_mod_exp_meth_st + { + int num; /* Word size we target */ + char *name; /* Identify the method */ +#ifndef NOPROTO + int (*useit)(const BN_ME_METH *meth); + /* If 'power' is null, reuse the old one */ + int (*mod_exp)(BN_ME_CTX *mctx,BIGNUM *r,BIGNUM *a,BIGNUM *p, + BN_CTX *ctx); + /* Create the BN_ME_CTX */ + int (*init_ctx)(const BN_ME_METH *meth,BN_ME_CTX **mctx); + /* Get rid of it */ + int (*free_ctx)(BN_ME_CTX *mctx); + /* Assign the modulus */ + int (*set)(BN_ME_CTX *mctx,BIGNUM *n,int cmd,int flags,BN_CTX *ctx); +#else + int (*useit)(); + int (*mod_exp)(); + int (*init_ctx)(); + int (*free_ctx)(); + int (*set)(); +#endif + int argi; + char *argp; /* 'Extra stuff' */ + }; + +#define BN_ME_METH_TABLE_MAX 32 +/* Used in BN_library_init */ +#define BN_INIT_LAST 0x00 +#define BN_INIT_BN_ME_METH 0x01 + +#define BN_BNME_F_DEFAULT 0x01 +/* Used only when loading, kept as bits internally, normally loaded as + * words */ +#define BN_BNME_F_BITS 0x02 +typedef struct bn_me_meth_info_st + { +#ifndef NOPROTO + const BN_ME_METH *(*meth)(void); +#else + const BN_ME_METH *(*meth)(); +#endif + int min; + int max; + int flags; + } BN_ME_METH_INFO; + +#define BN_ME_SET_MOD 0x01 +#define BN_ME_SET_BASE 0x02 +#define BN_ME_SET_EXP 0x03 +#define BN_ME_SET_FLG_NO_LOOKUP 0x01 /* Passed to BN_ME_CTX_set() */ + +#define BN_FLG_CACHE 0x10 /* Cache values */ +#define BN_ME_FLG_BASE 0x40 /* base has been cached */ +#define BN_ME_FLG_EXP 0x80 /* power has been cached */ +/* When one of these structures is setup, it is intended that the + * base or power, if not null, will be used for the current calculation. + * If the base and/or power are cached, they will only be used if the input + * value is null. + * For RSA, the power would normally be set. + * For DH, the base and power would be set. The initial value generated + * would be the public key, which is exchanged. The phase2 part would + * change the base but not the power. + * For DSA signing, a fixed base is used, but a random power. + * For DSA verification, there are two bases and two powers, + * a^p%m * b^q%m + * One way to implement this is to use a special function to + * generate a composite power value, and or a special base form. + * The other option is to have a special function to generate + * the 'base' array. The problem is that we are exponentiating with + * a 3 value power value, instead of the normal two. It should be possible + * to use the standard a^b%m function. + */ +struct bn_mod_exp_ctx_st + { + const BN_ME_METH *meth; + char *callback; + char *cb_arg; + int flags; + /* Evil hack for storage of data values, it should really be here + * other than the first value */ + char *modulus; /* eg BN_MONT_CTX */ + char *power; /* eg power representation */ + char *base; /* eg base representation */ + char *arg; + }; + +/* Used for reciprocal division/mod functions + * It cannot be shared between threads + */ +struct bn_recp_ctx_st + { + BIGNUM N; /* the divisor */ + BIGNUM Nr; /* the reciprocal */ + int num_bits; + int shift; + int flags; + }; + +typedef struct bn_rec_st + { + int depth; + int n; +#ifndef NOPROTO + void (PRE_CCONV CCONV *mul)(BN_ULONG *r, BN_ULONG *a, BN_ULONG *b); + void (PRE_CCONV CCONV *sqr)(BN_ULONG *r, BN_ULONG *a); + void (PRE_CCONV CCONV *low_mul)(); +#else + void (PRE_CCONV CCONV *mul)(); + void (PRE_CCONV CCONV *sqr)(); + void (PRE_CCONV CCONV *low_mul)(); +#endif + } BN_REC; + +#define BN_to_montgomery(r,a,mont,ctx) BN_mod_mul_montgomery(\ + r,a,&((mont)->RR),(mont),ctx) + +#define BN_prime_checks (5) + +#define BN_num_bytes(a) ((BN_num_bits(a)+7)/8) +#define BN_length(a) ((a)->top * BN_BYTES) +#define BN_is_word(a,w) (((a)->top == 1) && ((a)->d[0] == (BN_ULONG)(w))) +#define BN_is_zero(a) (((a)->top == 0) || BN_is_word(a,0)) +#define BN_is_one(a) (BN_is_word((a),1)) +#define BN_is_odd(a) (((a)->top > 0) && ((a)->d[0] & 1)) +#define BN_one(a) (BN_set_word((a),(BN_ULONG)1)) +#define BN_zero(a) (BN_set_word((a),(BN_ULONG)0)) + +/*#define BN_ascii2bn(a) BN_hex2bn(a) */ +/*#define BN_bn2ascii(a) BN_bn2hex(a) */ + +#define bn_expand(n,b) ((((((b+BN_BITS2-1))/BN_BITS2)) <= (n)->max)?\ + (n):bn_expand2((n),(b)/BN_BITS2+1)) +#define bn_wexpand(n,b) (((b) <= (n)->max)?(n):bn_expand2((n),(b))) + +#ifdef SMALL_CODE_SIZE +void bn_zexpand(BIGNUM *a,int n); +void bn_fix_top(BIGNUM *a); +#else +#define bn_zexpand(a,n) \ + if ((a)->top < n) \ + { \ + int i; \ + bn_wexpand((a),n); \ + if ((a)->d!=NULL) \ + { \ + for (i=(a)->top; id[i]=0; \ + } \ + } + +#define bn_fix_top(a) \ + { \ + BN_ULONG *ftl; \ + if ((a)->top > 0) \ + { \ + for (ftl= &((a)->d[(a)->top-1]); (a)->top > 0; (a)->top--) \ + if (*(ftl--)) break; \ + } \ + } +#endif + +#define BN_MONT_CTX_set(a,b,c) BN_MONT_CTX_set_word((a),(b),(c)) + +#ifndef NOPROTO + + +const BN_ME_METH *BN_ME_METH_word(void); +const BN_ME_METH *BN_ME_METH_full(void); + +#if (defined(BN_MUL_COMBA) || defined(BN_SQR_COMBA)) + +/* word4 - used for 64bit multiprime*/ +const BN_ME_METH *BN_ME_METH_word4(void); +const BN_ME_METH *BN_ME_METH_rec4_word8(void); +const BN_ME_METH *BN_ME_METH_rec4_word16(void); +const BN_ME_METH *BN_ME_METH_rec4_word32(void); +const BN_ME_METH *BN_ME_METH_rec4_word64(void); +const BN_ME_METH *BN_ME_METH_rec4_word128(void); + +/* word6 - used for 64bit multiprime*/ +const BN_ME_METH *BN_ME_METH_word6(void); +const BN_ME_METH *BN_ME_METH_rec6_word12(void); +const BN_ME_METH *BN_ME_METH_rec6_word24(void); +const BN_ME_METH *BN_ME_METH_rec6_word48(void); +const BN_ME_METH *BN_ME_METH_rec6_word96(void); + +const BN_ME_METH *BN_ME_METH_word8(void); +const BN_ME_METH *BN_ME_METH_rec8_word16(void); +const BN_ME_METH *BN_ME_METH_rec8_word32(void); +const BN_ME_METH *BN_ME_METH_rec8_word64(void); +const BN_ME_METH *BN_ME_METH_rec8_word128(void); +const BN_ME_METH *BN_ME_METH_rec8_word256(void); + +const BN_ME_METH *BN_ME_METH_word11(void); +const BN_ME_METH *BN_ME_METH_rec11_word22(void); +const BN_ME_METH *BN_ME_METH_rec11_word44(void); +const BN_ME_METH *BN_ME_METH_rec11_word88(void); + +const BN_ME_METH *BN_ME_METH_word16(void); +const BN_ME_METH *BN_ME_METH_rec16_word32(void); +const BN_ME_METH *BN_ME_METH_rec16_word64(void); +const BN_ME_METH *BN_ME_METH_rec16_word128(void); +const BN_ME_METH *BN_ME_METH_rec16_word256(void); + +#endif + +#ifdef CPU_IA64 +const BN_ME_METH *BN_ME_METH_ia64_384(void); +const BN_ME_METH *BN_ME_METH_ia64_512(void); +const BN_ME_METH *BN_ME_METH_ia64_1024(void); +#endif + +#if (defined(CPU_SPARC_V8PLUS) || defined (CPU_SPARC_V9)) +const BN_ME_METH *BN_ME_METH_usparc(void); +const BN_ME_METH *BN_ME_METH_usparc_352(void); +const BN_ME_METH *BN_ME_METH_usparc_512(void); +#endif + +#if 1 || defined(CPU_X86) +const BN_ME_METH *BN_ME_METH_pentium4_29(void); +const BN_ME_METH *BN_ME_METH_pentium4_28(void); +#endif + +/* NCipher Nfast hardware accelerator method prototype */ +const BN_ME_METH *BN_ME_METH_nfast(void); + + +BIGNUM *BN_value_one(void); +const char * BN_options(void); +BN_CTX *BN_CTX_new(void); +void BN_CTX_init(BN_CTX *c); +void BN_CTX_free(BN_CTX *c); +#ifndef NO_BN_RAND +int BN_rand(BIGNUM *rnd, R_RANDOM *rand, int bits, int top,int bottom); +#endif +int BN_num_bits(BIGNUM *a); +int BN_num_bits_word(BN_ILONG); +BIGNUM *BN_new(void); +void BN_init(BIGNUM *); +void BN_clear_free(BIGNUM *a); +BIGNUM *BN_copy(BIGNUM *a, BIGNUM *b); +BIGNUM *BN_bin2bn(unsigned char *s,int len,BIGNUM *ret); +int BN_bn2bin(BIGNUM *a, unsigned char *to); +BIGNUM *BN_mpi2bn(unsigned char *s,int len,BIGNUM *ret); +int BN_bn2mpi(BIGNUM *a, unsigned char *to); +int BN_sub(BIGNUM *r, BIGNUM *a, BIGNUM *b); +int BN_usub(BIGNUM *r, BIGNUM *a, BIGNUM *b); +int BN_uadd(BIGNUM *r, BIGNUM *a, BIGNUM *b); +int BN_add(BIGNUM *r, BIGNUM *a, BIGNUM *b); +int BN_mod(BIGNUM *rem, BIGNUM *m, BIGNUM *d, BN_CTX *ctx); +int BN_div(BIGNUM *dv, BIGNUM *rem, BIGNUM *m, BIGNUM *d, BN_CTX *ctx); +int BN_mul(BIGNUM *r, BIGNUM *a, BIGNUM *b,BN_CTX *ctx); +int BN_sqr(BIGNUM *r, BIGNUM *a,BN_CTX *ctx); +BN_ULONG BN_mod_word(BIGNUM *a, BN_ULONG w); +BN_ULONG BN_div_word(BIGNUM *a, BN_ULONG w); +int BN_mul_word(BIGNUM *a, BN_ULONG w); +int BN_add_word(BIGNUM *a, BN_ULONG w); +int BN_sub_word(BIGNUM *a, BN_ULONG w); +int BN_set_word(BIGNUM *a, BN_ULONG w); +BN_ULONG BN_get_word(BIGNUM *a); +int BN_cmp(BIGNUM *a, BIGNUM *b); +void BN_free(BIGNUM *a); +int BN_is_bit_set(BIGNUM *a, int n); +int BN_lshift(BIGNUM *r, BIGNUM *a, int n); +int BN_lshift1(BIGNUM *r, BIGNUM *a); +int BN_exp(BIGNUM *r, BIGNUM *a, BIGNUM *p,BN_CTX *ctx); +int BN_mod_exp(BIGNUM *r, BIGNUM *a, BIGNUM *p, BIGNUM *m,BN_CTX *ctx); +int BN_mod_exp_mont(BIGNUM *r, BIGNUM *a, BIGNUM *p, BIGNUM *m,BN_CTX *ctx, + BN_MONT_CTX *m_ctx); +int BN_mod_exp2_mont(BIGNUM *r, BIGNUM *a1, BIGNUM *p1,BIGNUM *a2, + BIGNUM *p2,BIGNUM *m,BN_CTX *ctx,BN_MONT_CTX *m_ctx); +int BN_mod_exp_simple(BIGNUM *r, BIGNUM *a, BIGNUM *p, + BIGNUM *m,BN_CTX *ctx); +int BN_mask_bits(BIGNUM *a,int n); +int BN_mod_mul(BIGNUM *ret, BIGNUM *a, BIGNUM *b, BIGNUM *m, + BN_CTX *ctx); +#if !defined(WIN16) && !defined(NO_FP_API) +int BN_print_fp(FILE *fp, BIGNUM *a); +#endif +#ifdef HEADER_COMMON_BIO_H +int BN_print(BIO *fp, BIGNUM *a); +#else +/* int BN_print(char *fp, BIGNUM *a); */ +#endif +int BN_reciprocal(BIGNUM *r, BIGNUM *m, int len, BN_CTX *ctx); +int BN_rshift(BIGNUM *r, BIGNUM *a, int n); +int BN_rshift1(BIGNUM *r, BIGNUM *a); +void BN_clear(BIGNUM *a); +BIGNUM *bn_expand2(BIGNUM *b, int bits); +BIGNUM *BN_dup(BIGNUM *a); +int BN_ucmp(BIGNUM *a, BIGNUM *b); +int BN_set_bit(BIGNUM *a, int n); +int BN_clear_bit(BIGNUM *a, int n); +char * BN_bn2hex(BIGNUM *a); +char * BN_bn2dec(BIGNUM *a); +int BN_hex2bn(BIGNUM **a,char *str); +int BN_dec2bn(BIGNUM **a,char *str); +int BN_gcd(BIGNUM *r,BIGNUM *in_a,BIGNUM *in_b,BN_CTX *ctx); +BIGNUM *BN_mod_inverse(BIGNUM *ret,BIGNUM *a, BIGNUM *n,BN_CTX *ctx); +/* Return R, where it is R*(1< +#include +//#include "r_error.h" +//#include "err.h" +#include + +#ifndef STANDALONE +#include "r_com.h" +#else +#define NO_STDLIB_MAPPING +#define Malloc(a) malloc(a) +#define Free(a) free(a) +#define Memset(a,b,c) memset(a,b,c) +#define Memcpy(a,b,c) memcpy(a,b,c) +#endif + +/* TEMP FIX */ +#undef BNerr +#define BNerr(a,b) +#include "bn.h" + +#define BN_EXP_TABLE_SIZE 16 + +/* see the bn_limit_ fields which are the start of a runtime + * tunable set of values to match these fixed compile-time constants + */ +/* Pentium pro 16,16,16,32,64 */ +/* Alpha 16,16,16,16.64 */ +/* StrongARM */ +#define BN_MULL_SIZE_NORMAL (16) /* 32 */ +#define BN_MUL_RECURSIVE_SIZE_NORMAL (16) /* 32 */ /* less than */ +#define BN_SQR_RECURSIVE_SIZE_NORMAL (16) /* 32 */ +#define BN_MUL_LOW_RECURSIVE_SIZE_NORMAL (32) /* 32 */ +#define BN_MONT_CTX_SET_SIZE_WORD (64) /* 32 */ + +/************************************************************* + * Using the long long type + */ +#define Lw(t) (((BN_ULONG)(t))&BN_MASK2) +#define Hw(t) (((BN_ULONG)((t)>>BN_BITS2))&BN_MASK2) + +/* These are used for internal error checking and are not normally used */ +#ifdef BN_DEBUG +#define bn_check_top(a) \ + { if ( ((a)->top < 0) || \ + ((a)->top > (a)->max) || \ + (((a)->top == 0) && (a)->neg) || \ + (((a)->top == 1) && (a->d[0] == 0))) \ + { char *nullp=NULL; *nullp='z'; } } +#define bn_check_num(a) if ((a) < 0) { char *nullp=NULL; *nullp='z'; } +#define bn_assert(a) if (!(a)) { char *nullp=NULL; *nullp='z'; } +#define bn_set_used_check(l,size,num) bn_su_check(l,size,num) +#ifdef BN_DEBUG2 +#define bn_do_used_check(fp,str,l,size,num) bn_du_check(fp,str,l,size,num) +#else +#define bn_do_used_check(fp,str,l,size,num) +#endif +#else +#define bn_assert(a) +#define bn_check_top(a) +#define bn_check_num(a) +#define bn_set_used_check(l,size,num) +#define bn_do_used_check(fp,str,l,size,num) +#endif + + +/*********************************************** +* IA64 32 bit build SWIZZLE code +* This essentially casts our points which are +* 32bits under abi32 to 64bit unsigned long +* longs and then grabs the top two bits of the +* pointer and places it in the 2nd and 3rd bits +* of the unsigned long long. +**********************************************/ +#ifdef HPUX_IA64_32 +#define SWIZLLE(VALUE) ((((unsigned long long)(VALUE) & 0xc0000000LL) <<31)\ +|((unsigned long long)(VALUE))) +#else +#define SWIZZLE(VALUE) (VALUE) +#endif + +#define bn_neg_words(a,n) \ + { \ + int iii; \ + \ + for (iii=0; iiimax=(r)->top,BN_set_flags((r),BN_FLG_STATIC_DATA)) +#else +#define bn_set_max(r) +#endif + +/* These macros are used to 'take' a section of a bignum for read only use */ +#define bn_set_low(r,a,n) \ + { \ + (r)->top=((a)->top > (n))?(n):(a)->top; \ + (r)->d=(a)->d; \ + (r)->neg=(a)->neg; \ + (r)->flags|=BN_FLG_STATIC_DATA; \ + bn_set_max(r); \ + } + +#define bn_set_high(r,a,n) \ + { \ + if ((a)->top > (n)) \ + { \ + (r)->top=(a)->top-n; \ + (r)->d= &((a)->d[n]); \ + } \ + else \ + (r)->top=0; \ + (r)->neg=(a)->neg; \ + (r)->flags|=BN_FLG_STATIC_DATA; \ + bn_set_max(r); \ + } + +/* #define bn_expand(n,b) ((((b)/BN_BITS2) <= (n)->max)?(n):bn_expand2((n),(b))) */ + +#ifdef BN_LLONG +#define mul_add(r,a,w,c) { \ + BN_ULLONG t; \ + t=(BN_ULLONG)w * (a) + (r) + (c); \ + (r)= Lw(t); \ + (c)= Hw(t); \ + } + +#define mul(r,a,w,c) { \ + BN_ULLONG t; \ + t=(BN_ULLONG)w * (a) + (c); \ + (r)= Lw(t); \ + (c)= Hw(t); \ + } + +#else +/************************************************************* + * No long long type + */ + +#define LBITS(a) ((a)&BN_MASK2l) +#define HBITS(a) (((a)>>BN_BITS4)&BN_MASK2lh) +#define L2HBITS(a) ((BN_ULONG)((a)&BN_MASK2lh)<>(BN_BITS4-1); \ + m =((m&BN_MASK2l)<<(BN_BITS4+1))&BN_MASK2; \ + l=(l+m)&BN_MASK2; if (l < m) h++; \ + (lo)=l; \ + (ho)=h; \ + } + +#if 0 +#define mul_add(r,a,bl,bh,b_hl,c) { \ + BN_ULONG l,h; \ + BN_ULONG lt,mt; \ + \ + h= (a); \ + l=LBITS(h); \ + h=HBITS(h); \ + mt=bl*l; \ + lt=mt+c+(r); \ + c=bh*h; \ + mt+=c+(l-h)*b_hl; \ + mt+=(lt>>BN_BITS4); \ + (r)=(lt&BN_MASK2l)|((mt&BN_MASK2l)<>BN_BITS4); \ + } +#else /* Normal version */ +#define mul_add(r,a,bl,bh,b_hl,c) { \ + BN_ULONG l,h; \ + \ + h= (a); \ + l=LBITS(h); \ + h=HBITS(h); \ + mul64(l,h,(bl),(bh)); \ + \ + /* non-multiply part */ \ + l=(l+(c))&BN_MASK2; if (l < (c)) h++; \ + (c)=(r); \ + l=(l+(c))&BN_MASK2; if (l < (c)) h++; \ + (c)=h&BN_MASK2; \ + (r)=l; \ + } +#endif + +#define mul(r,a,bl,bh,c) { \ + BN_ULONG l,h; \ + \ + h= (a); \ + l=LBITS(h); \ + h=HBITS(h); \ + mul64(l,h,(bl),(bh)); \ + \ + /* non-multiply part */ \ + l+=(c); if ((l&BN_MASK2) < (c)) h++; \ + (c)=h&BN_MASK2; \ + (r)=l&BN_MASK2; \ + } + +#endif + +#ifndef BN_MUL_COMBA +#define bn_mul_comba4(r,a,b) bn_mul_normal(r,a,4,b,4) +#define bn_mul_comba5(r,a,b) bn_mul_normal(r,a,5,b,5) +#define bn_mul_comba6(r,a,b) bn_mul_normal(r,a,6,b,6) +#define bn_mul_comba8(r,a,b) bn_mul_normal(r,a,8,b,8) +#define bn_mul_comba11(r,a,b) bn_mul_normal(r,a,11,b,11) +#define bn_mul_comba12(r,a,b) bn_mul_normal(r,a,12,b,12) +#define bn_mul_comba16(r,a,b) bn_mul_normal(r,a,16,b,16) +#endif +#if 1 +#ifndef BN_SQR_COMBA +#define bn_sqr_comba4(r,a) bn_mul_normal(r,a,4,a,4) +#define bn_sqr_comba5(r,a) bn_mul_normal(r,a,5,a,5) +#define bn_sqr_comba6(r,a) bn_mul_normal(r,a,6,a,6) +#define bn_sqr_comba8(r,a) bn_mul_normal(r,a,8,a,8) +#define bn_sqr_comba11(r,a) bn_mul_normal(r,a,11,a,11) +#define bn_sqr_comba12(r,a) bn_mul_normal(r,a,12,a,12) +#define bn_sqr_comba16(r,a) bn_mul_normal(r,a,16,a,16) +#endif +#else +#ifndef BN_SQR_COMBA +#define bn_sqr_comba4(r,a) bn_mul_comba4(r,a,a) +#define bn_sqr_comba5(r,a) bn_mul_comba5(r,a,a) +#define bn_sqr_comba6(r,a) bn_mul_comba6(r,a,a) +#define bn_sqr_comba8(r,a) bn_mul_comba8(r,a,a) +#define bn_sqr_comba11(r,a) bn_mul_comba11(r,a,a) +#define bn_sqr_comba12(r,a) bn_mul_comba12(r,a,a) +#define bn_sqr_comba16(r,a) bn_mul_comba16(r,a,a) +#endif +#endif +#ifndef BN_REDUCE_COMBA +#define r0_bn_mont_comba4(r,a,n,num,n0) bn_from_montgomery_words(r,a,n,num,n0) +#define r0_bn_mont_comba5(r,a,n,num,n0) bn_from_montgomery_words(r,a,n,num,n0) +#define r0_bn_mont_comba6(r,a,n,num,n0) bn_from_montgomery_words(r,a,n,num,n0) +#define r0_bn_mont_comba8(r,a,n,num,n0) bn_from_montgomery_words(r,a,n,num,n0) +#define r0_bn_mont_comba11(r,a,n,num,n0) bn_from_montgomery_words(r,a,n,num,n0) +#define r0_bn_mont_comba12(r,a,n,num,n0) bn_from_montgomery_words(r,a,n,num,n0) +#define r0_bn_mont_comba16(r,a,n,num,n0) bn_from_montgomery_words(r,a,n,num,n0) +#endif + +#ifndef NOPROTO + +BIGNUM *bn_expand2(BIGNUM *b, int bits); + +#ifdef X86_ASM +void bn_add_words(BN_ULONG *r,BN_ULONG *a,int num); +#endif + +#else + +BIGNUM *bn_expand2(); +#ifdef X86_ASM +BN_ULONG bn_add_words(); +#endif + +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* HEADER_COMMON_BN_LCL_H */ + +void bn_mul_low_recursive(BN_ULONG *r,BN_ULONG *a,BN_ULONG *b,int n2,BN_ULONG *t); +void bn_mul_high(BN_ULONG *r,BN_ULONG *a,BN_ULONG *b,BN_ULONG *l,int n2, BN_ULONG *t); +int BN_gen_exp_string(unsigned char *str, BIGNUM *p, int bits); +void bn_from_montgomery_rec_full(BN_ULONG *rp, BN_ULONG *ap, + BN_ULONG *np, BN_ULONG *nip, BN_ULONG *tmp,BN_REC *rec); + +int bn_mont_ctx_new_word(const BN_ME_METH *meth, BN_ME_CTX **retp); + diff --git a/build/tools/acsign/include/bn_thx.h b/build/tools/acsign/include/bn_thx.h new file mode 100644 index 00000000..13e157b7 --- /dev/null +++ b/build/tools/acsign/include/bn_thx.h @@ -0,0 +1,301 @@ +/* $Id$ */ +/* + * Copyright (C) 1998-2002 RSA Security Inc. All rights reserved. + * + * This work contains proprietary information of RSA Security. + * Distribution is limited to authorized licensees of RSA + * Security. Any unauthorized reproduction, distribution or + * modification of this work is strictly prohibited. + * + */ +/***************************************************************************** + * Copyright (c). 2001 RSA Security Inc. All rights reserved. + * This work contains proprietary information of RSA Security. + * Distribution is limited to authorized licensees of RSA + * Security. Any unauthorized reproduction, distribution or + * modification of this work is strictly prohibited. + ****************************************************************************/ +/** + * @file bn_thx.h + * @brief header file created for used as interface defines for systems + * containing hetergeneous environments where some bn operations + * will be performed in isolation from the rest of the library + */ +#ifndef HEADER_COMMON_BN_THX_H +#define HEADER_COMMON_BN_THX_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* These defines are required on systems so that the mod_exp code + * to be used in isolation from the bn operation with library will work, + * if these are not available, the header files with equivalent functionality + * should be added inside a CPU or Operating system define + */ +#ifndef NO_STDIO_H +#include +#endif + +#ifndef NO_STDLIB_H +#include +#endif + +#ifndef NO_STRING_H +#include +#endif + +/* Interface for ARM/DSP systems, this interface requires only + * the standard defines and includes at the moment + */ +#ifdef CPU_TMS320 +#define THX_RECIPIENT +#define CPU_DSP +#endif + +#ifdef THX_RECIPIENT + +#ifndef restrict +#define restrict +#endif + +#ifndef Malloc +#define Malloc malloc +#endif + +#ifndef Memcpy +#define Memcpy memcpy +#endif + +#ifndef Memmove +#define Memmove memmove +#endif + +#ifndef Memset +#define Memset memset +#endif + +#ifndef Free +#define Free free +#endif + +#endif /* THX_RECIPIENT */ + +#ifndef THX_RECIPIENT /* not the recipient, we must be the caller */ + +#include "bn_lcl.h" + +#endif /* ! THX_RECIPIENT */ + +/* The follow define protected inclusions are excerpts from the + * header file bn.h, this has been done, so that code written for the + * THX systems can be used in isolation from the library + * This code may need to be updated in line with changes to the bn.h + * header file. + */ +#ifndef HEADER_COMMON_BN_H +#define HEADER_COMMON_BN_H + +#if !defined(CCONV) +#define CCONV +#endif + +#ifndef PRE_CCONV +#define PRE_CCONV +#endif + +/* convert from the new to the old option names */ +#if defined(OPT_BN_LLONG) +#define BN_LLONG /* comment to make sure Configure leaves this alone */ +#endif + +#if defined(OPT_32_BIT_INT) || defined(OPT_32_BIT) +#define THIRTY_TWO_BIT +#endif + +#define BN_ILONG BN_ULONG + +#ifdef THIRTY_TWO_BIT +#define BN_ULLONG unsigned long long +#ifdef OPT_32_BIT_INT +#define BN_ULONG unsigned int +#define BN_LONG int +#else +#define BN_ULONG unsigned long +#define BN_LONG long +#endif +#define BN_BITS 64 +#define BN_BYTES 4 +#define BN_BITS2 32 +#define BN_BITS4 16 +/* This is needed because the Watcom compiler pre-processor + * under QNX is perverted and tries to parses the 'LL' + * part even though it is never used. + */ +#ifdef BN_LLONG +#define BN_MASK (0xffffffffffffffffL) +#endif +#define BN_MASK2 (0xffffffffL) +#define BN_MASK2l (0xffff) +#define BN_MASK2lh (0xffff) +#define BN_MASK2h1 (0xffff8000L) +#define BN_MASK2h (0xffff0000L) +#define BN_TBIT (0x80000000L) +#define BN_DEC_CONV (1000000000L) +#define BN_DEC_FMT1 "%lu" +#define BN_DEC_FMT2 "%09lu" +#define BN_DEC_NUM 9 +#define BN_HEX_FMT "%08lX" +#endif + +#define BN_EXP_TABLE_SIZE 16 + +typedef struct bignum_st BIGNUM; + +struct bignum_st + { + BN_ULONG *d; /* Pointer to an array of 'BN_BITS2' bit chunks. */ + int top; /* Index of last used d +1. */ + /* The next are internal book keeping for bn_expand. */ + int max; /* Size of the d array. */ + int neg; /* one if the number is negative */ + int flags; + }; + +PRE_CCONV BN_ULONG CCONV bn_mul_add_words(BN_ULONG *rp,BN_ULONG *ap, + int num, BN_ULONG w); +PRE_CCONV BN_ULONG CCONV bn_mul_words(BN_ULONG *rp, BN_ULONG *ap, + int num, BN_ULONG w); +PRE_CCONV void CCONV bn_sqr_words(BN_ULONG *rp, BN_ULONG *ap, int num); +BN_ULONG CCONV bn_div_words(BN_ULONG h, BN_ULONG l, BN_ULONG d); +PRE_CCONV BN_ULONG CCONV bn_add_words(BN_ULONG *rp, BN_ULONG *ap, + BN_ULONG *bp,int num); +PRE_CCONV BN_ULONG CCONV bn_sub_words(BN_ULONG *rp, BN_ULONG *ap, + BN_ULONG *bp,int num); + +#ifndef BN_ME_METH +#define BN_ME_METH void +#endif + +#endif /* ! HEADER_COMMON_BN_H */ + +/* The follow define protected inclusions are excerpts from the + * header file r_error.h, this has been done, so that code written for the + * THX systems can be used in isolation from the library + * This code may need to be updated in line with changes to the r_error.h + * header file. + */ +#ifndef HEADER_COMMON_R_ERROR_H +#define HEADER_COMMON_R_ERROR_H + +/* The FATAL_INTERNAL_ERROR is a flag that is set with the following + * error codes: + * R_ERROR_INVALID_STATE + * R_ERROR_INIT_NOT_CALLED + * R_ERROR_SHOULD_NOT_HAVE_BEEN_CALLED + */ +#define R_ERROR_FATAL_INTERNAL_ERROR 64 + +/* The BAD_PARAMETER is a flag that is set with the following error + * codes: + * R_ERROR_NULL_ARG + * R_ERROR_BUFFER_TOO_SMALL + * R_ERROR_BAD_VALUE + * R_ERROR_BAD_RANGE + * R_ERROR_BAD_FORMAT + * R_ERROR_BAD_TYPE + * R_ERROR_BAD_DATA + * R_ERROR_BAD_LENGTH + */ +#define R_ERROR_BAD_PARAMETER 32 + +/* Base value for all general errors used through all products */ +#define R_ERROR_BASE 10000 + +#define R_ERROR_NONE 0 + +#define R_ERROR_FAILED (R_ERROR_BASE+1) +#define R_ERROR_IO (R_ERROR_BASE+2) +#define R_ERROR_PROTOCOL (R_ERROR_BASE+3) +#define R_ERROR_EOF (R_ERROR_BASE+4) +#define R_ERROR_ALLOC_FAILURE (R_ERROR_BASE+5) +#define R_ERROR_EVAL_RESTRICTION (R_ERROR_BASE+6) +#define R_ERROR_EVAL_EXPIRED (R_ERROR_BASE+7) +#define R_ERROR_NOT_FOUND (R_ERROR_BASE+8) +#define R_ERROR_NOT_AVAILABLE (R_ERROR_BASE+9) +#define R_ERROR_NOT_IMPLEMENTED (R_ERROR_BASE+10) +#define R_ERROR_NOT_SUPPORTED (R_ERROR_BASE+11) +#define R_ERROR_INVALID_STATE \ + ((R_ERROR_BASE+12) | R_ERROR_FATAL_INTERNAL_ERROR) +#define R_ERROR_INIT_NOT_CALLED \ + ((R_ERROR_BASE+13) | R_ERROR_FATAL_INTERNAL_ERROR) +#define R_ERROR_SHOULD_NOT_HAVE_BEEN_CALLED \ + ((R_ERROR_BASE+14) | R_ERROR_FATAL_INTERNAL_ERROR) +#define R_ERROR_METHOD_UNDEFINED \ + ((R_ERROR_BASE+15) | R_ERROR_FATAL_INTERNAL_ERROR) +#define R_ERROR_BUFFER_TOO_SMALL \ + ((R_ERROR_BASE+16) | R_ERROR_BAD_PARAMETER) +#define R_ERROR_NULL_ARG ((R_ERROR_BASE+17) | R_ERROR_BAD_PARAMETER) +#define R_ERROR_BAD_VALUE ((R_ERROR_BASE+18) | R_ERROR_BAD_PARAMETER) +#define R_ERROR_BAD_RANGE ((R_ERROR_BASE+19) | R_ERROR_BAD_PARAMETER) +#define R_ERROR_BAD_FORMAT ((R_ERROR_BASE+20) | R_ERROR_BAD_PARAMETER) +#define R_ERROR_BAD_TYPE ((R_ERROR_BASE+21) | R_ERROR_BAD_PARAMETER) +#define R_ERROR_BAD_DATA ((R_ERROR_BASE+22) | R_ERROR_BAD_PARAMETER) +#define R_ERROR_BAD_LENGTH ((R_ERROR_BASE+23) | R_ERROR_BAD_PARAMETER) +#define R_ERROR_RCOM_LIBRARY_NOT_SUPPORTED (R_ERROR_BASE+24) + +/* Resource Manager base for errors */ +#define R_COM_ERR_LIB_CTX_BASE 10100 + +#endif /* HEADER_COMMON_R_ERROR_H */ + + +const BN_ME_METH *BN_ME_METH_thxc(void); +/** + * @note THX code only needs to store rr, n0, str + * + */ +typedef struct thxc_mont_ctx_st + { + BIGNUM *rr; + unsigned char *str; + unsigned int str_len; + BN_ULONG n0; + } THXC_MONT_CTX; + + +int Rx_thxr_mod_exp_mont( BN_ULONG *result, BN_ULONG *ap, BN_ULONG *np, + BN_ULONG *rrp, BN_ULONG n0, int top, int tmp_len, unsigned char *str); + +int Ri_thxr_mod_exp_mont( BN_ULONG *result, BN_ULONG *ap, BN_ULONG *np, + BN_ULONG *rrp, BN_ULONG *dp, BN_ULONG *aap, BN_ULONG *rp, BN_ULONG *tmp, + BN_ULONG n0, int top, unsigned char *str); + +void Ri_thxr_mul_normal(BN_ULONG *r,BN_ULONG *a,int na,BN_ULONG *b,int nb); +void Ri_thxr_sqr_normal(BN_ULONG *r, BN_ULONG *a, int n, BN_ULONG *tmp); + +void Ri_thxr_from_montgomery_words(BN_ULONG *ret,BN_ULONG *ap,BN_ULONG *np, + int w, BN_ULONG n0); + +BN_ULONG Ri_thxr_from_mont_words(BN_ULONG *ap,BN_ULONG *wap, + BN_ULONG *np, int w, BN_ULONG n0); + +#ifdef CPU_TMS320 + +/* Method and functions required for the ARM/DSP interface */ + +const BN_ME_METH *BN_ME_METH_dspc(void); + +int Rx_dsp_mod_exp_mont( BN_ULONG *result, BN_ULONG *ap, BN_ULONG *np, + BN_ULONG *rrp, BN_ULONG n0, int top, int tmp_len, unsigned char *str); + +int Ri_dsp_mod_exp_mont( BN_ULONG *result, BN_ULONG *ap, BN_ULONG *np, + BN_ULONG *rrp, BN_ULONG *dp, BN_ULONG *aap, BN_ULONG *rp, BN_ULONG *tmp, + BN_ULONG n0, int top, unsigned char *str); + +#endif + +#ifdef __cplusplus +} +#endif +#endif /* HEADER_COMMON_BN_THX_H */ diff --git a/build/tools/acsign/include/r_error.h b/build/tools/acsign/include/r_error.h new file mode 100644 index 00000000..8e269f36 --- /dev/null +++ b/build/tools/acsign/include/r_error.h @@ -0,0 +1,210 @@ +/* $Id$ */ +/* + * Copyright (C) 1998-2002 RSA Security Inc. All rights reserved. + * + * This work contains proprietary information of RSA Security. + * Distribution is limited to authorized licensees of RSA + * Security. Any unauthorized reproduction, distribution or + * modification of this work is strictly prohibited. + * + */ +/** + * @file r_error.h + * This file contains the error definitions for the toolkit. + */ +#ifndef HEADER_COMMON_R_ERROR_H +#define HEADER_COMMON_R_ERROR_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @defgroup R_ERROR_IDS Error Identifiers + * This section outlines the error codes returned, enabling identification of + * specific errors. For more information, see the + * Developer's Guide. + * + * @{ + */ +/** + * Indicates that a fatal error has occurred in the operation performed. + * This flag is set with the following error codes:
+ *
    + *
  • #R_ERROR_INVALID_STATE.
  • + *
  • #R_ERROR_INIT_NOT_CALLED.
  • + *
  • #R_ERROR_SHOULD_NOT_HAVE_BEEN_CALLED.
  • + *
+ */ +#define R_ERROR_FATAL_INTERNAL_ERROR 64 + +/** + * Indicates that the specified parameter is incorrect or contains invalid + * information. This flag is set with the following error codes:
+ *
    + *
  • #R_ERROR_NULL_ARG.
  • + *
  • #R_ERROR_BUFFER_TOO_SMALL.
  • + *
  • #R_ERROR_BAD_VALUE.
  • + *
  • #R_ERROR_BAD_RANGE.
  • + *
  • #R_ERROR_BAD_FORMAT.
  • + *
  • #R_ERROR_BAD_TYPE.
  • + *
  • #R_ERROR_BAD_DATA.
  • + *
  • #R_ERROR_BAD_LENGTH.
  • + *
+ */ +#define R_ERROR_BAD_PARAMETER 32 + +/* Indicates the base value for all general errors used through all products */ +#define R_ERROR_BASE 10000 + +/** + * Indicates that no errors were detected in the requested operation. + */ +#define R_ERROR_NONE 0 + +/** + * Indicates that the requested operation failed. + */ +#define R_ERROR_FAILED (R_ERROR_BASE+1) +/** + * Indicates that the requested operation detected an Input/Output error. + */ +#define R_ERROR_IO (R_ERROR_BASE+2) +/** + * Indicates that the requested operation detected a protocol error. + */ +#define R_ERROR_PROTOCOL (R_ERROR_BASE+3) +/** + * Indicates that the requested operation detected an End-of-File (EOF) error. + */ +#define R_ERROR_EOF (R_ERROR_BASE+4) +/** + * Indicates that the requested operation failed in a memory allocation + * operation. + */ +#define R_ERROR_ALLOC_FAILURE (R_ERROR_BASE+5) +/** + * Indicates that the requested operation is restricted in an evaluation + * version of the library. + */ +#define R_ERROR_EVAL_RESTRICTION (R_ERROR_BASE+6) +/** + * Indicates that the evaluation period of the library has expired. + */ +#define R_ERROR_EVAL_EXPIRED (R_ERROR_BASE+7) +/** + * Indicates that the requested operation detected that a pre-requirement was + * not found. + */ +#define R_ERROR_NOT_FOUND (R_ERROR_BASE+8) +/** + * Indicates that the requested operation detected that a pre-requirement was + * unavailable. + */ +#define R_ERROR_NOT_AVAILABLE (R_ERROR_BASE+9) +/** + * Indicates that the requested operation is not implemented in the current + * instantiation of the library. + */ +#define R_ERROR_NOT_IMPLEMENTED (R_ERROR_BASE+10) +/** + * Indicates that the requested operation is not supported in the current + * instantiation of the library. + */ +#define R_ERROR_NOT_SUPPORTED (R_ERROR_BASE+11) +/** + * Indicates that the requested operation entered an invalid state. + */ +#define R_ERROR_INVALID_STATE \ + ((R_ERROR_BASE+12) | R_ERROR_FATAL_INTERNAL_ERROR) +/** + * Indicates that the requested operation detected that an initialization + * operation has not been performed. + */ +#define R_ERROR_INIT_NOT_CALLED \ + ((R_ERROR_BASE+13) | R_ERROR_FATAL_INTERNAL_ERROR) +/** + * Indicates that the requested operation should not have been called. + */ +#define R_ERROR_SHOULD_NOT_HAVE_BEEN_CALLED \ + ((R_ERROR_BASE+14) | R_ERROR_FATAL_INTERNAL_ERROR) +/** + * Indicates that the requested method is not defined. + */ +#define R_ERROR_METHOD_UNDEFINED \ + ((R_ERROR_BASE+15) | R_ERROR_FATAL_INTERNAL_ERROR) +/** + * Indicates that the requested operation detected an inadequately sized + * buffer. + */ +#define R_ERROR_BUFFER_TOO_SMALL \ + ((R_ERROR_BASE+16) | R_ERROR_BAD_PARAMETER) +/** + * Indicates that the requested operation detected that a required argument + * was passed as NULL. + */ +#define R_ERROR_NULL_ARG ((R_ERROR_BASE+17) | R_ERROR_BAD_PARAMETER) +/** + * Indicates that the requested operation detected that a required argument + * passed contained a bad or incorrect value. + */ +#define R_ERROR_BAD_VALUE ((R_ERROR_BASE+18) | R_ERROR_BAD_PARAMETER) +/** + * Indicates that the requested operation detected that a required argument + * passed was out of the allowed range. + */ +#define R_ERROR_BAD_RANGE ((R_ERROR_BASE+19) | R_ERROR_BAD_PARAMETER) +/** + * Indicates that the requested operation detected that a required argument + * passed used a format that is not supported or incorrect. + */ +#define R_ERROR_BAD_FORMAT ((R_ERROR_BASE+20) | R_ERROR_BAD_PARAMETER) +/** + * Indicates that the requested operation detected that a required argument + * passed used a type that is not supported or incorrect. + */ +#define R_ERROR_BAD_TYPE ((R_ERROR_BASE+21) | R_ERROR_BAD_PARAMETER) +/** + * Indicates that the requested operation detected that a required argument + * passed contained incorrect or invalid data. + */ +#define R_ERROR_BAD_DATA ((R_ERROR_BASE+22) | R_ERROR_BAD_PARAMETER) +/** + * Indicates that the requested operation detected that a required argument + * passed contained an incorrect length value. + */ +#define R_ERROR_BAD_LENGTH ((R_ERROR_BASE+23) | R_ERROR_BAD_PARAMETER) +/** + * Indicates that the library instantiation requested is not supported. + */ +#define R_ERROR_RCOM_LIBRARY_NOT_SUPPORTED (R_ERROR_BASE+24) +/** + * Indicates that the request operation was denied. + */ +#define R_ERROR_DENIED (R_ERROR_BASE+25) +/** + * Indicates that authentication is required before the operation + * can succeed. + */ +#define R_ERROR_AUTHENTICATION_REQUIRED (R_ERROR_BASE+26) + +/** + * Indicates that a required module was unable to be loaded. + */ +#define R_ERROR_MODULE_LOAD_FAILED \ + ((R_ERROR_BASE+27)|R_ERROR_FATAL_INTERNAL_ERROR) + +/* Resource Manager base for errors */ +#define R_COM_ERR_LIB_CTX_BASE 10100 + +/** + * @} + */ + +#ifdef __cplusplus +} +#endif +#endif /* HEADER_COMMON_R_ERROR_H */ + + + diff --git a/build/tools/acsign/include/r_stdiag.h b/build/tools/acsign/include/r_stdiag.h new file mode 100644 index 00000000..566de6fa --- /dev/null +++ b/build/tools/acsign/include/r_stdiag.h @@ -0,0 +1,82 @@ +/* $Id$ */ +/* + * Copyright (C) 1998-2002 RSA Security Inc. All rights reserved. + * + * This work contains proprietary information of RSA Security. + * Distribution is limited to authorized licensees of RSA + * Security. Any unauthorized reproduction, distribution or + * modification of this work is strictly prohibited. + * + */ + +/* + * *************************************************************************** + * + * Purpose: + * + * This file defines an API for performing stack diagnostics. + * + * It provides: + * Peek Stack Usage statistics + * To use it: + * 1. Build the library without "NO_R_DIAG" defined + * 2. Make a call to R_DIAG_set_stack_datum() prior to + * calling the first library function. + * 3. Add calls to the macro R_DIAG_CHECK_STACK where potential + * high stack usage points are. + * 4. Call R_DIAG_get_stack_low_water_mark() at then end of the + * library calls to return the maximum stack usage OR + * 5. Call R_DIAG_print_stack_depth() to display the peek stack + * usage + * + * + * + * History: + * 3-Aug-00 mjs development + * + * ************************************************************************** + */ + + +#ifndef HEADER_COMMON_R_STDIAG_H +#define HEADER_COMMON_R_STDIAG_H + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef NO_R_DIAG + +#ifdef NOPROTO +int R_DIAG_set_stack_datum(char *ptr); +int R_DIAG_check_stack(char *ptr, char *file, int line, int hit); +int R_DIAG_get_stack_low_water_mark(int *depth, char **file, int *line, + int *hit); +int R_DIAG_print_stack_depth(BIO *bio); + +#else + +int R_DIAG_set_stack_datum(); +int R_DIAG_check_stack(); +int R_DIAG_get_stack_low_water_mark(); +int R_DIAG_print_stack_depth(); + +#endif + +#define R_DIAG_CHECK_STACK {\ + char r_diag_stack_check_var = 0;\ + static int r_diag_stack_check_cnt = 0;\ +\ + R_DIAG_check_stack(&r_diag_stack_check_var, __FILE__,\ + __LINE__,++r_diag_stack_check_cnt);\ + } +#else +#define R_DIAG_CHECK_STACK +#endif + + +#ifdef __cplusplus +} +#endif +#endif /* HEADER_COMMON_R_STDIAG_H */ + diff --git a/build/tools/acsign/include/r_types.h b/build/tools/acsign/include/r_types.h new file mode 100644 index 00000000..d35556ad --- /dev/null +++ b/build/tools/acsign/include/r_types.h @@ -0,0 +1,241 @@ +/* $Id$ */ +/* + * Copyright (C) 1998-2002 RSA Security Inc. All rights reserved. + * + * This work contains proprietary information of RSA Security. + * Distribution is limited to authorized licensees of RSA + * Security. Any unauthorized reproduction, distribution or + * modification of this work is strictly prohibited. + * + */ +/** + * @file r_types.h + * This file contains the library structure and type definitions. + */ +#ifndef HEADER_COMMON_R_TYPES_H +#define HEADER_COMMON_R_TYPES_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @defgroup DATA_MNG_FLAGS Data Management Flags + * This section lists the data management flags. + * @{ + */ + +/** + * @} + */ + +/* This gets set if the flags field if the we need to 'free' the + * R_RANDOM when we call R_rand_free(). If it is not set, the + * rand_ctrl will only be called to clean things up. + */ +#define R_RANDOM_FLG_MALLOCED 0x01 + +/* portable form of a random callback function */ +/** + * @typedef R_RANDOM + * The random object type. + */ +typedef struct r_random_st R_RANDOM; +/** + * This structure is used by the random object type. + */ +struct r_random_st +{ + /** + * A method function for generation of random data. + * + * @see R_rand_bytes(). + */ + int (*rand_bytes)(R_RANDOM *ctx,unsigned char *bytes,int len); + /** + * A method function for seeding of the random generator. + * @see R_rand_seed(). + */ + int (*rand_seed)(R_RANDOM *ctx,unsigned char *bytes,int len); + /** + * A method function for control of the random generator used by + * other functions as a single interface. + * + * @see R_rand_entropy_count(), R_rand_set_entropy_func(), + * R_rand_get_entropy_func(), R_rand_load_file(), R_rand_write_file() + * and R_rand_file_name(). + */ + int (*rand_ctrl)(R_RANDOM *ctx,int op,char *arg); + /** + * The data used by the random implementation. + */ + char *arg; + /** + * A method flag for used for keeping the state of the #R_RANDOM. + */ + int flags; /* normally set to zero */ +}; +/** + * @typedef R_SURRENDER + * The surrender function and argument. + */ +typedef struct r_surrender_st R_SURRENDER; +/** + * This structure provides a status update of a time intensive operation. It + * also allows you to abort the operation if required. + */ +struct r_surrender_st +{ + /* Return 0 to continue, non-zero to 'cancel' function */ + /** + * The surrender function pointer used as a generic method of + * passing user-defined callback or information collection functions. + */ + int (*callback)(R_SURRENDER *ctx,int function,int num); + /** + * The argument data used by the callback operation. + */ + char *arg; +}; + +typedef struct r_fixed_point_number_st R_FIXED_POINT_NUMBER; +struct r_fixed_point_number_st +{ + unsigned long hi; + unsigned long lo; +}; + +/** + * @typedef R_INDEXED_INFO + * This structure details the index containing information for get and set + * functions. + */ +typedef struct r_indexed_info_st R_INDEXED_INFO; +/** This structure details the index containing information for get and set + * functions. + */ +struct r_indexed_info_st +{ + /** The index into the list of the item to work with. */ + int index; + /** The data of the item in the list. */ + void *data; + /** The length of the data. */ + unsigned int len; + /** The data value as an integer. */ + int value; +}; + +/** + * @typedef R_ID_INFO + * The information to work with requires a second identity value to find it. + */ +typedef struct r_id_info_st R_ID_INFO; +/** The information to get or set requires a second information identifier. + * This type is used when calling set_info and get_info to get a number of + * information items in one call. + */ +struct r_id_info_st +{ + /** The information identifier. */ + int info_id; + /** The information data associated with the identifier. */ + void *value; +}; + +/** @defgroup R_FLAG_COPY_IDS Copy Flag Identifiers + * + * This section provides information on the copy mode. When data is passed + * to/from the library you can specify whether or not the copy should be done + * by value, by reference or by direct assignment. + * @ingroup DATA_MNG_FLAGS + * @{ + */ + +/** + * The copy flag. When data is passed to/from the library you can specify + * whether or not the copy should be done by value, by reference or + * by direct assignment. + */ +typedef int R_FLAG_COPY; + +/* + * action flag app lib + * obj_get BY_VAL F X + * BY_REF X F [app will not free "obj" until it is + * BY_ASSIGN F X finished with the returned reference] + * obj_set BY_VAL F X + * BY_REF F X [app will not free passed reference + * BY_ASSIGN X F until after it has freed "obj"] + * + * BY_REF makes the lifetime of the two objects linked and it is up to + * the application to manage the objects in a manner that preserves this + * relationship. + */ + +/** + * Indicates to copy by value. A complete clone of the object is made. + * The lifecycles of the two objects are independent. + */ +#define R_FLAG_COPY_BY_VALUE 0x0000 + +/** + * Indicates to copy by reference. A reference to the object is made. + * The lifecycles of objects sharing references are linked. + */ +#define R_FLAG_COPY_BY_REFERENCE 0x0001 + +/** + * Indicates to copy by assignment. The object is assigned to the required + * party. The caller does not keep a reference to the passed object. + */ +#define R_FLAG_COPY_BY_ASSIGN 0x0002 + +/** + * Indicates to copy using the default flag settings. + */ +#define R_FLAG_COPY_DEFAULT R_FLAG_COPY_BY_VALUE + +/** + * @} + */ + +/** @defgroup R_FLAG_SHARE_IDS Shared Flag Identifiers + * + * This section provides information on the shared flags available. + * @ingroup DATA_MNG_FLAGS + * @{ + */ + +/** + * The constructor flag. Indicates whether or not it is permissible to share + * the internal state between objects. + */ +typedef int R_FLAG_SHARE; + +/** + * Indicates to share nothing. The states of the two objects are completely + * separate. + */ +#define R_FLAG_SHARE_NONE 0x0000 + +/** + * Indicates to share data. Constructed objects may reference the state of + * the object from which it was constructed. The lifecycles of the two objects + * are linked. + */ +#define R_FLAG_SHARE_DATA 0x0001 + +/** Indicates the share default in which nothing is shared. */ +#define R_FLAG_SHARE_DEFAULT R_FLAG_SHARE_NONE + +/** + * @} + */ + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/build/tools/acsign/include/sha.h b/build/tools/acsign/include/sha.h new file mode 100644 index 00000000..d6eeed2b --- /dev/null +++ b/build/tools/acsign/include/sha.h @@ -0,0 +1,89 @@ +/* $Id$ */ +/* + * Copyright (C) 1998-2002 RSA Security Inc. All rights reserved. + * + * This work contains proprietary information of RSA Security. + * Distribution is limited to authorized licensees of RSA + * Security. Any unauthorized reproduction, distribution or + * modification of this work is strictly prohibited. + * + */ + +#ifndef HEADER_COMMON_SHA_H +#define HEADER_COMMON_SHA_H + +#ifdef __cplusplus +extern "C" { +#endif + +#if !defined(CCONV) +#define CCONV +#endif + +#ifndef PRE_CCONV +#define PRE_CCONV +#endif + +#define SHA_CBLOCK 64 +#define SHA_LBLOCK 16 +#define SHA_BLOCK 16 +#define SHA_LAST_BLOCK 56 +#define SHA_LENGTH_BLOCK 8 +#define SHA_DIGEST_LENGTH 20 + +/* the default is to use unsigned long */ +#if !defined(OPT_SHA_INT) && !defined(OPT_SHA_LONG) +#define OPT_SHA_LONG +#endif + +#ifdef OPT_SHA_LONG +#define SHA_LONG unsigned long +#endif + +#ifdef OPT_SHA_INT +#define SHA_LONG unsigned int +#endif + +typedef struct sha_ctx_st SHA_CTX; + +struct sha_ctx_st + { + SHA_LONG h0,h1,h2,h3,h4; + SHA_LONG Nl,Nh; + SHA_LONG data[SHA_LBLOCK]; + int num; + void (PRE_CCONV CCONV *sha_block)(SHA_CTX *c, const unsigned char *W, + int num); + }; + +#ifndef NOPROTO +int SHA1_Setup(SHA_CTX *c, void (PRE_CCONV CCONV *sha_block)(SHA_CTX *c, + const unsigned char *W, int num)); +void SHA_Init(SHA_CTX *c); +void SHA_Update(SHA_CTX *c, const unsigned char *data, unsigned long len); +void SHA_Final(unsigned char *md, SHA_CTX *c); +unsigned char *SHA(const unsigned char *d, unsigned long n,unsigned char *md); +void SHA_Transform(SHA_CTX *c, const unsigned char *data); +void SHA1_Init(SHA_CTX *c); +void SHA1_Update(SHA_CTX *c, const unsigned char *data, unsigned long len); +void SHA1_Final(unsigned char *md, SHA_CTX *c); +unsigned char *SHA1(const unsigned char *d, unsigned long n,unsigned char *md); +void SHA1_Transform(SHA_CTX *c, const unsigned char *data); +#else +void SHA_Init(); +void SHA_Update(); +void SHA_Final(); +unsigned char *SHA(); +void SHA_Transform(); +void SHA1_Init(); +void SHA1_Update(); +void SHA1_Final(); +unsigned char *SHA1(); +void SHA1_Transform(); +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* HEADER_COMMON_SHA_H */ diff --git a/build/tools/acsign/include/sha1.h b/build/tools/acsign/include/sha1.h new file mode 100644 index 00000000..ed4a672e --- /dev/null +++ b/build/tools/acsign/include/sha1.h @@ -0,0 +1,73 @@ +/* + * sha1.h + * + * Description: + * This is the header file for code which implements the Secure + * Hashing Algorithm 1 as defined in FIPS PUB 180-1 published + * April 17, 1995. + * + * Many of the variable names in this code, especially the + * single character names, were used because those were the names + * used in the publication. + * + * Please read the file sha1.c for more information. + * + */ + +#ifndef _SHA1_H_ +#define _SHA1_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef _SHA_enum_ +#define _SHA_enum_ +enum +{ + shaSuccess = 0, + shaNull, /* Null pointer parameter */ + shaInputTooLong, /* input data too long */ + shaStateError /* called Input after Result */ +}; +#endif + +/* + * This structure will hold context information for the SHA-1 + * hashing operation + */ +typedef struct SHA1Context +{ + unsigned long Intermediate_Hash[20/4]; /* Message Digest */ + + unsigned long Length_Low; /* Message length in bits */ + unsigned long Length_High; /* Message length in bits */ + + int Message_Block_Index; /* Index into message block array */ + unsigned char Message_Block[64]; /* 512-bit message blocks */ + + int Computed; /* Is the digest computed? */ + int Corrupted; /* Is the message digest corrupted? */ +} SHA1Context; + +/* + * Function Prototypes + */ + +int SHA1Reset( SHA1Context *); +int SHA1Input( SHA1Context *, + const unsigned char *, + unsigned int); +int SHA1Result( SHA1Context *, + unsigned char Message_Digest[20]); + +#if defined( SHA1_TEST ) +int SHA1Test( ); +#endif // SHA1_TEST + + +#ifdef __cplusplus +} +#endif + +#endif // _SHA1_H_ diff --git a/build/tools/acsign/include/sha_locl.h b/build/tools/acsign/include/sha_locl.h new file mode 100644 index 00000000..2361282b --- /dev/null +++ b/build/tools/acsign/include/sha_locl.h @@ -0,0 +1,274 @@ +/* $Id$ */ +/* + * Copyright (C) 1998-2002 RSA Security Inc. All rights reserved. + * + * This work contains proprietary information of RSA Security. + * Distribution is limited to authorized licensees of RSA + * Security. Any unauthorized reproduction, distribution or + * modification of this work is strictly prohibited. + * + */ + +#ifndef HEADER_COMMON_SHA_LOCL_H +#define HEADER_COMMON_SHA_LOCL_H + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef STANDALONE +//#include "r_com.h" +#else +#if 1 //RSA +#include +#include +#include +//#define Memcpy memcpy +//#define Memset memset +#define Strcpy strcpy +#else //NITRO +#include "acmemory.h" +#define Malloc(a) ACMemory_Alloc(a) +#define Free(a) ACMemory_Free(a) +#define Memset(a,b,c) ACMemory_Memset(a,b,c) +#define Memcpy(a,b,c) ACMemory_Memcpy(a,b,c) +#endif +#endif + +#ifdef undef +/* one or the other must be defined */ +#ifndef SHA_1 /* FIPE 180-1 */ +#define SHA_0 /* FIPS 180 */ +#endif +#endif + +#define ULONG unsigned long +#define UCHAR unsigned char +#define UINT unsigned int + +#ifdef NOCONST +#define const +#endif + +#undef c2nl +#define c2nl(c,l) (l =(((unsigned long)(*((c)++)))<<24), \ + l|=(((unsigned long)(*((c)++)))<<16), \ + l|=(((unsigned long)(*((c)++)))<< 8), \ + l|=(((unsigned long)(*((c)++))) )) + +#undef p_c2nl +#define p_c2nl(c,l,n) { \ + switch (n) { \ + case 0: l =((unsigned long)(*((c)++)))<<24; \ + case 1: l|=((unsigned long)(*((c)++)))<<16; \ + case 2: l|=((unsigned long)(*((c)++)))<< 8; \ + case 3: l|=((unsigned long)(*((c)++))); \ + } \ + } + +#undef c2nl_p +/* NOTE the pointer is not incremented at the end of this */ +#define c2nl_p(c,l,n) { \ + l=0; \ + (c)+=n; \ + switch (n) { \ + case 3: l =((unsigned long)(*(--(c))))<< 8; \ + case 2: l|=((unsigned long)(*(--(c))))<<16; \ + case 1: l|=((unsigned long)(*(--(c))))<<24; \ + } \ + } + +#undef p_c2nl_p +#define p_c2nl_p(c,l,sc,len) { \ + switch (sc) \ + { \ + case 0: l =((unsigned long)(*((c)++)))<<24; \ + if (--len == 0) break; \ + case 1: l|=((unsigned long)(*((c)++)))<<16; \ + if (--len == 0) break; \ + case 2: l|=((unsigned long)(*((c)++)))<< 8; \ + } \ + } + +#undef nl2c +#define nl2c(l,c) (*((c)++)=(unsigned char)(((l)>>24)&0xff), \ + *((c)++)=(unsigned char)(((l)>>16)&0xff), \ + *((c)++)=(unsigned char)(((l)>> 8)&0xff), \ + *((c)++)=(unsigned char)(((l) )&0xff)) + +#undef c2l +#define c2l(c,l) (l =(((unsigned long)(*((c)++))) ), \ + l|=(((unsigned long)(*((c)++)))<< 8), \ + l|=(((unsigned long)(*((c)++)))<<16), \ + l|=(((unsigned long)(*((c)++)))<<24)) + +#undef p_c2l +#define p_c2l(c,l,n) { \ + switch (n) { \ + case 0: l =((unsigned long)(*((c)++))); \ + case 1: l|=((unsigned long)(*((c)++)))<< 8; \ + case 2: l|=((unsigned long)(*((c)++)))<<16; \ + case 3: l|=((unsigned long)(*((c)++)))<<24; \ + } \ + } + +#undef c2l_p +/* NOTE the pointer is not incremented at the end of this */ +#define c2l_p(c,l,n) { \ + l=0; \ + (c)+=n; \ + switch (n) { \ + case 3: l =((unsigned long)(*(--(c))))<<16; \ + case 2: l|=((unsigned long)(*(--(c))))<< 8; \ + case 1: l|=((unsigned long)(*(--(c)))); \ + } \ + } + +#undef p_c2l_p +#define p_c2l_p(c,l,sc,len) { \ + switch (sc) \ + { \ + case 0: l =((unsigned long)(*((c)++))); \ + if (--len == 0) break; \ + case 1: l|=((unsigned long)(*((c)++)))<< 8; \ + if (--len == 0) break; \ + case 2: l|=((unsigned long)(*((c)++)))<<16; \ + } \ + } + +#undef l2c +#define l2c(l,c) (*((c)++)=(unsigned char)(((l) )&0xff), \ + *((c)++)=(unsigned char)(((l)>> 8)&0xff), \ + *((c)++)=(unsigned char)(((l)>>16)&0xff), \ + *((c)++)=(unsigned char)(((l)>>24)&0xff)) + +#ifdef OPT_SHA_B_ENDIAN +#define B_ENDIAN +#endif +#ifdef OPT_SHA_L_ENDIAN +#define L_ENDIAN +#endif + +#undef ROTATE +#if defined(WIN32) +#define ROTATE(a,n) _lrotl(a,n) +#else +#define ROTATE(a,n) (((a)<<(n))|(((a)&0xffffffff)>>(32-(n)))) +#endif + +/* byte order reversal */ +#if defined(WIN32) +/* 5 instructions with rotate instruction, else 9 */ +#define Endian_Reverse32(a) \ + { \ + unsigned long l=(a); \ + (a)=((ROTATE(l,8)&0x00FF00FF)|(ROTATE(l,24)&0xFF00FF00)); \ + } +#else +/* 6 instructions with rotate instruction, else 8 */ +#define Endian_Reverse32(a) \ + { \ + unsigned long l=(a); \ + l=(((l&0xFF00FF00)>>8L)|((l&0x00FF00FF)<<8L)); \ + (a)=ROTATE(l,16L); \ + } +#endif + +/* F() below can be + * simplified to the code in F_00_19. + * #define F(x,y,z) (((x) & (y)) | ((~(x)) & (z))) + * another tweak to be made + * in F_40_59, (x&a)|(y&a) -> (x|y)&a + */ +#define F_00_19(b,c,d) ((((c) ^ (d)) & (b)) ^ (d)) +#define F_20_39(b,c,d) ((b) ^ (c) ^ (d)) +#define F_40_59(b,c,d) (((b) & (c)) | (((b)|(c)) & (d))) +#define F_60_79(b,c,d) F_20_39(b,c,d) + +#ifdef SHA_0 +#undef Xupdate +#define Xupdate(a,i,ia,ib,ic,id) X[(i)&0x0f]=(a)=\ + (ia[(i)&0x0f]^ib[((i)+2)&0x0f]^ic[((i)+8)&0x0f]^id[((i)+13)&0x0f]); +#endif +#ifdef SHA_1 +#undef Xupdate +#define Xupdate(a,i,ia,ib,ic,id) (a)=\ + (ia[(i)&0x0f]^ib[((i)+2)&0x0f]^ic[((i)+8)&0x0f]^id[((i)+13)&0x0f]);\ + X[(i)&0x0f]=(a)=ROTATE((a),1); +#endif + +#define BODY_00_15(i,a,b,c,d,e,f,xa) \ + (f)=xa[i]+(e)+K_00_19+ROTATE((a),5)+F_00_19((b),(c),(d)); \ + (b)=ROTATE((b),30); + +#define BODY_60_79(i,a,b,c,d,e,f,xa) \ + Xupdate(f,i,xa,xa,xa,xa); \ + (f)=X[(i)&0x0f]+(e)+K_60_79+ROTATE((a),5)+F_60_79((b),(c),(d)); \ + (b)=ROTATE((b),30); + +/* + * The CodeWarrior compiler has a problem correctly expanding things like + * + * (f)+=(e)+K_00_19+ROTATE((a),5)+F_00_19((b),(c),(d)); + * + * so we separate this to 3 explicit adds, just for PalmOS. + */ +#ifndef UNDER_PALMOS + +#define BODY_16_19(i,a,b,c,d,e,f,xa,xb,xc,xd) \ + Xupdate(f,i,xa,xb,xc,xd); \ + (f)+=(e)+K_00_19+ROTATE((a),5)+F_00_19((b),(c),(d)); \ + (b)=ROTATE((b),30); + +#define BODY_20_31(i,a,b,c,d,e,f,xa,xb,xc,xd) \ + Xupdate(f,i,xa,xb,xc,xd); \ + (f)+=(e)+K_20_39+ROTATE((a),5)+F_20_39((b),(c),(d)); \ + (b)=ROTATE((b),30); + +#define BODY_32_39(i,a,b,c,d,e,f,xa) \ + Xupdate(f,i,xa,xa,xa,xa); \ + (f)+=(e)+K_20_39+ROTATE((a),5)+F_20_39((b),(c),(d)); \ + (b)=ROTATE((b),30); + +#define BODY_40_59(i,a,b,c,d,e,f,xa) \ + Xupdate(f,i,xa,xa,xa,xa); \ + (f)+=(e)+K_40_59+ROTATE((a),5)+F_40_59((b),(c),(d)); \ + (b)=ROTATE((b),30); + +#else /* UNDER_PALMOS */ + +#define BODY_16_19(i,a,b,c,d,e,f,xa,xb,xc,xd) \ + Xupdate(f,i,xa,xb,xc,xd); \ + (f)+=(e); \ + (f)+=K_00_19; \ + (f)+=ROTATE((a),5)+F_00_19((b),(c),(d)); \ + (b)=ROTATE((b),30); + +#define BODY_20_31(i,a,b,c,d,e,f,xa,xb,xc,xd) \ + Xupdate(f,i,xa,xb,xc,xd); \ + (f)+=(e); \ + (f)+=K_20_39; \ + (f)+=ROTATE((a),5)+F_20_39((b),(c),(d)); \ + (b)=ROTATE((b),30); + +#define BODY_32_39(i,a,b,c,d,e,f,xa) \ + Xupdate(f,i,xa,xa,xa,xa); \ + (f)+=(e); \ + (f)+=K_20_39; \ + (f)+=ROTATE((a),5)+F_20_39((b),(c),(d)); \ + (b)=ROTATE((b),30); + +#define BODY_40_59(i,a,b,c,d,e,f,xa) \ + Xupdate(f,i,xa,xa,xa,xa); \ + (f)+=(e); \ + (f)+=K_40_59; \ + (f)+=ROTATE((a),5)+F_40_59((b),(c),(d)); \ + (b)=ROTATE((b),30); + +#endif /* UNDER_PALMOS */ + +#ifdef __cplusplus +} +#endif + +#endif /* HEADER_COMMON_SHA_LOCL_H */ diff --git a/build/tools/acsign/sha1.c b/build/tools/acsign/sha1.c new file mode 100644 index 00000000..6bebd125 --- /dev/null +++ b/build/tools/acsign/sha1.c @@ -0,0 +1,453 @@ +/* + * sha1.c + * + * Description: + * This file implements the Secure Hashing Algorithm 1 as + * defined in FIPS PUB 180-1 published April 17, 1995. + * + * The SHA-1, produces a 160-bit message digest for a given + * data stream. It should take about 2**n steps to find a + * message with the same digest as a given message and + * 2**(n/2) to find any two messages with the same digest, + * when n is the digest size in bits. Therefore, this + * algorithm can serve as a means of providing a + * "fingerprint" for a message. + * + * Portability Issues: + * SHA-1 is defined in terms of 32-bit "words". This code + * uses (included via "sha1.h" to define 32 and 8 + * bit unsigned integer types. If your C compiler does not + * support 32 bit unsigned integers, this code is not + * appropriate. + * + * Caveats: + * SHA-1 is designed to work with messages less than 2^64 bits + * long. Although SHA-1 allows a message digest to be generated + * for messages of any number of bits less than 2^64, this + * implementation only works with messages with a length that is + * a multiple of the size of an 8-bit character. + * + */ + +#include "sha1.h" + +/* + * Define the SHA1 circular left shift macro + */ +#define SHA1CircularShift(bits,word) \ + (((word) << (bits)) | ((word) >> (32-(bits)))) + +/* Local Function Prototyptes */ +void SHA1PadMessage(SHA1Context *); +void SHA1ProcessMessageBlock(SHA1Context *); + +/* + * SHA1Reset + * + * Description: + * This function will initialize the SHA1Context in preparation + * for computing a new SHA1 message digest. + * + * Parameters: + * context: [in/out] + * The context to reset. + * + * Returns: + * sha Error Code. + * + */ +int SHA1Reset(SHA1Context *context) +{ + if (!context) + { + return shaNull; + } + + context->Length_Low = 0; + context->Length_High = 0; + context->Message_Block_Index = 0; + + context->Intermediate_Hash[0] = 0x67452301; + context->Intermediate_Hash[1] = 0xEFCDAB89; + context->Intermediate_Hash[2] = 0x98BADCFE; + context->Intermediate_Hash[3] = 0x10325476; + context->Intermediate_Hash[4] = 0xC3D2E1F0; + + context->Computed = 0; + context->Corrupted = 0; + + return shaSuccess; +} + +/* + * SHA1Result + * + * Description: + * This function will return the 160-bit message digest into the + * Message_Digest array provided by the caller. + * NOTE: The first octet of hash is stored in the 0th element, + * the last octet of hash in the 19th element. + * + * Parameters: + * context: [in/out] + * The context to use to calculate the SHA-1 hash. + * Message_Digest: [out] + * Where the digest is returned. + * + * Returns: + * sha Error Code. + * + */ +int SHA1Result( SHA1Context *context, + unsigned char Message_Digest[20]) +{ + int i; + + if (!context || !Message_Digest) + { + return shaNull; + } + + if (context->Corrupted) + { + return context->Corrupted; + } + + if (!context->Computed) + { + SHA1PadMessage(context); + for(i=0; i<64; ++i) + { + /* message may be sensitive, clear it out */ + context->Message_Block[i] = 0; + } + context->Length_Low = 0; /* and clear length */ + context->Length_High = 0; + context->Computed = 1; + + } + + for(i = 0; i < 20; ++i) + { + Message_Digest[i] = (unsigned char) + (context->Intermediate_Hash[i>>2] + >> 8 * ( 3 - ( i & 0x03 ) )); + } + + return shaSuccess; +} + +/* + * SHA1Input + * + * Description: + * This function accepts an array of octets as the next portion + * of the message. + * + * Parameters: + * context: [in/out] + * The SHA context to update + * message_array: [in] + * An array of characters representing the next portion of + * the message. + * length: [in] + * The length of the message in message_array + * + * Returns: + * sha Error Code. + * + */ +int SHA1Input( SHA1Context *context, + const unsigned char *message_array, + unsigned int length) +{ + if (!length) + { + return shaSuccess; + } + + if (!context || !message_array) + { + return shaNull; + } + + if (context->Computed) + { + context->Corrupted = shaStateError; + + return shaStateError; + } + + if (context->Corrupted) + { + return context->Corrupted; + } + while(length-- && !context->Corrupted) + { + context->Message_Block[context->Message_Block_Index++] = + (unsigned char)(*message_array & 0xFF); + + context->Length_Low += 8; + if (context->Length_Low == 0) + { + context->Length_High++; + if (context->Length_High == 0) + { + /* Message is too long */ + context->Corrupted = 1; + } + } + + if (context->Message_Block_Index == 64) + { + SHA1ProcessMessageBlock(context); + } + + message_array++; + } + + return shaSuccess; +} + +/* + * SHA1ProcessMessageBlock + * + * Description: + * This function will process the next 512 bits of the message + * stored in the Message_Block array. + * + * Parameters: + * None. + * + * Returns: + * Nothing. + * + * Comments: + * + * Many of the variable names in this code, especially the + * single character names, were used because those were the + * names used in the publication. + * + * + */ +void SHA1ProcessMessageBlock(SHA1Context *context) +{ + const unsigned long K[] = { /* Constants defined in SHA-1 */ + 0x5A827999, + 0x6ED9EBA1, + 0x8F1BBCDC, + 0xCA62C1D6 + }; + int t; /* Loop counter */ + unsigned long temp; /* Temporary word value */ + unsigned long W[80]; /* Word sequence */ + unsigned long A, B, C, D, E; /* Word buffers */ + + /* + * Initialize the first 16 words in the array W + */ + for(t = 0; t < 16; t++) + { + W[t] = context->Message_Block[t * 4 ] << 24; + W[t] |= context->Message_Block[t * 4 + 1] << 16; + W[t] |= context->Message_Block[t * 4 + 2] << 8; + W[t] |= context->Message_Block[t * 4 + 3]; + } + + for(t = 16; t < 80; t++) + { + W[t] = SHA1CircularShift(1,W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16]); + } + + A = context->Intermediate_Hash[0]; + B = context->Intermediate_Hash[1]; + C = context->Intermediate_Hash[2]; + D = context->Intermediate_Hash[3]; + E = context->Intermediate_Hash[4]; + + for(t = 0; t < 20; t++) + { + temp = SHA1CircularShift(5,A) + + ((B & C) | ((~B) & D)) + E + W[t] + K[0]; + E = D; + D = C; + C = SHA1CircularShift(30,B); + + B = A; + A = temp; + } + + for(t = 20; t < 40; t++) + { + temp = SHA1CircularShift(5,A) + (B ^ C ^ D) + E + W[t] + K[1]; + E = D; + D = C; + C = SHA1CircularShift(30,B); + B = A; + A = temp; + } + + for(t = 40; t < 60; t++) + { + temp = SHA1CircularShift(5,A) + + ((B & C) | (B & D) | (C & D)) + E + W[t] + K[2]; + E = D; + D = C; + C = SHA1CircularShift(30,B); + B = A; + A = temp; + } + + for(t = 60; t < 80; t++) + { + temp = SHA1CircularShift(5,A) + (B ^ C ^ D) + E + W[t] + K[3]; + E = D; + D = C; + C = SHA1CircularShift(30,B); + B = A; + A = temp; + } + + context->Intermediate_Hash[0] += A; + context->Intermediate_Hash[1] += B; + context->Intermediate_Hash[2] += C; + context->Intermediate_Hash[3] += D; + context->Intermediate_Hash[4] += E; + + context->Message_Block_Index = 0; +} + +/* + * SHA1PadMessage + * + + * Description: + * According to the standard, the message must be padded to an even + * 512 bits. The first padding bit must be a '1'. The last 64 + * bits represent the length of the original message. All bits in + * between should be 0. This function will pad the message + * according to those rules by filling the Message_Block array + * accordingly. It will also call the ProcessMessageBlock function + * provided appropriately. When it returns, it can be assumed that + * the message digest has been computed. + * + * Parameters: + * context: [in/out] + * The context to pad + * ProcessMessageBlock: [in] + * The appropriate SHA*ProcessMessageBlock function + * Returns: + * Nothing. + * + */ + +void SHA1PadMessage(SHA1Context *context) +{ + /* + * Check to see if the current message block is too small to hold + * the initial padding bits and length. If so, we will pad the + * block, process it, and then continue padding into a second + * block. + */ + if (context->Message_Block_Index > 55) + { + context->Message_Block[context->Message_Block_Index++] = 0x80; + while(context->Message_Block_Index < 64) + { + context->Message_Block[context->Message_Block_Index++] = 0; + } + + SHA1ProcessMessageBlock(context); + + while(context->Message_Block_Index < 56) + { + context->Message_Block[context->Message_Block_Index++] = 0; + } + } + else + { + context->Message_Block[context->Message_Block_Index++] = 0x80; + while(context->Message_Block_Index < 56) + { + + context->Message_Block[context->Message_Block_Index++] = 0; + } + } + + /* + * Store the message length as the last 8 octets + */ + context->Message_Block[56] = (unsigned char)(context->Length_High >> 24); + context->Message_Block[57] = (unsigned char)(context->Length_High >> 16); + context->Message_Block[58] = (unsigned char)(context->Length_High >> 8); + context->Message_Block[59] = (unsigned char)(context->Length_High ); + context->Message_Block[60] = (unsigned char)(context->Length_Low >> 24); + context->Message_Block[61] = (unsigned char)(context->Length_Low >> 16); + context->Message_Block[62] = (unsigned char)(context->Length_Low >> 8); + context->Message_Block[63] = (unsigned char)(context->Length_Low ); + + SHA1ProcessMessageBlock(context); +} + + + + +////////////////////////////////////////////////////////////////////////////// +#if defined( SHA1_TEST ) + +#include "string.h" + +char* samplearray[4] = +{ + "abc", + "abcdbcdecdefdefgefghfghighijhi" "jkijkljklmklmnlmnomnopnopq", + "a", + "01234567012345670123456701234567" "01234567012345670123456701234567" +}; + +char* resultarray[ 4 ] = +{ + "\xA9\x99\x3E\x36\x47\x06\x81\x6A\xBA\x3E\x25\x71\x78\x50\xC2\x6C\x9C\xD0\xD8\x9D", + "\x84\x98\x3E\x44\x1C\x3B\xD2\x6E\xBA\xAE\x4A\xA1\xF9\x51\x29\xE5\xE5\x46\x70\xF1", + "\x34\xAA\x97\x3C\xD4\xC4\xDA\xA4\xF6\x1E\xEB\x2B\xDB\xAD\x27\x31\x65\x34\x01\x6F", + "\xDE\xA3\x56\xA2\xCD\xDD\x90\xC7\xA7\xEC\xED\xC5\xEB\xB5\x63\x93\x4F\x46\x04\x52" +}; + +long int repeatcount[4] = { 1, 1, 1000000, 10 }; + +int SHA1Test( ) +{ + SHA1Context sha; + unsigned char digest[20]; + int i, j, error; + + error = 0; + for ( j = 0; j < 4; ++j ) + { + if ( !error ) + error = SHA1Reset(&sha); + + for(i = 0; i < repeatcount[j]; ++i) + { + if ( !error ) + error = SHA1Input(&sha, + (const unsigned char *)samplearray[j], + (unsigned int)strlen(samplearray[j])); + } + + if ( !error ) + error = SHA1Result(&sha, digest); + + if ( !error ) + error = memcmp( digest, resultarray[j], 20 ); + + if (error) + break; + } + + return error ? 0 : 1; +} + +#endif // SHA1_TEST +////////////////////////////////////////////////////////////////////////////// + diff --git a/build/tools/acsign/sha1dgst.c b/build/tools/acsign/sha1dgst.c new file mode 100644 index 00000000..f9759158 --- /dev/null +++ b/build/tools/acsign/sha1dgst.c @@ -0,0 +1,770 @@ +/* $Id$ */ +/* + * Copyright (C) 1998-2002 RSA Security Inc. All rights reserved. + * + * This work contains proprietary information of RSA Security. + * Distribution is limited to authorized licensees of RSA + * Security. Any unauthorized reproduction, distribution or + * modification of this work is strictly prohibited. + * + */ + +////!!!!#include "r_com.h" + +#ifndef NO_SHA1 +#undef SHA_0 +#define SHA_1 +#include "sha.h" +#include "sha_locl.h" +#ifdef CPU_X86 +#include "r_cpuid.h" +#endif /* NO_SHA1 */ + +const char *SHA1_version="SHA1 part of RCOM 2.3.0 11-Jun-2002"; + +/* Implemented from SHA-1 document - The Secure Hash Algorithm + */ + +#define INIT_DATA_h0 (SHA_LONG)0x67452301L +#define INIT_DATA_h1 (SHA_LONG)0xefcdab89L +#define INIT_DATA_h2 (SHA_LONG)0x98badcfeL +#define INIT_DATA_h3 (SHA_LONG)0x10325476L +#define INIT_DATA_h4 (SHA_LONG)0xc3d2e1f0L + +#define K_00_19 (SHA_LONG)0x5a827999L +#define K_20_39 (SHA_LONG)0x6ed9eba1L +#define K_40_59 (SHA_LONG)0x8f1bbcdcL +#define K_60_79 (SHA_LONG)0xca62c1d6L + +#ifndef CCONV +#define CCONV +#endif + +#ifndef PRE_CCONV +#define PRE_CCONV +#endif + +/* Endian flags are only used for the assembler code */ +#ifndef OPT_SHA1_ASM +#undef L_ENDIAN +#undef B_ENDIAN +#endif + + +#ifdef OPT_SHA1_ASM + +#ifdef CPU_X86 +void CCONV sha1_block_586(SHA_CTX *c,const unsigned char *p, int num); +void CCONV sha1_block_686(SHA_CTX *c,const unsigned char *p, int num); +void CCONV sha1_block_786(SHA_CTX *c,const unsigned char *p, int num); +unsigned long r_cpuid(unsigned long *,char **name); + +#elif OPT_SHA1_ARM +PRE_CCONV void CCONV sha1_arm4_fast(SHA_CTX *c, const unsigned char *p, + int num); +PRE_CCONV void CCONV sha1_arm4_small(SHA_CTX *c, const unsigned char *p, + int num); +#else +void CCONV sha1_block_asm(SHA_CTX *c, const unsigned char *p, int num); +#endif /* CPU_X86 */ + +#else /* OPT_SHA1_ASM */ + +void sha1_block(SHA_CTX *c, SHA_LONG *p, int num); + +#endif /* OPT_SHA1_ASM */ + +#undef M_c2nl +#undef M_p_c2nl +#undef M_c2nl_p +#undef M_p_c2nl_p +#undef M_nl2c + +#if defined(L_ENDIAN) && !defined(OPT_SHA1_ASM) +# define M_c2nl c2l +# define M_p_c2nl p_c2l +# define M_c2nl_p c2l_p +# define M_p_c2nl_p p_c2l_p +# define M_nl2c l2c +#else +# define M_c2nl c2nl +# define M_p_c2nl p_c2nl +# define M_c2nl_p c2nl_p +# define M_p_c2nl_p p_c2nl_p +# define M_nl2c nl2c +#endif /* defined(L_ENDIAN) && !defined(OPT_SHA1_ASM) */ + +int SHA1_Setup(c,sha_block) +SHA_CTX *c; +void (PRE_CCONV CCONV *sha_block)(SHA_CTX *c, const unsigned char *W, int num); + { + c->sha_block=sha_block; + return(0); + } + +void SHA1_Init(c) +SHA_CTX *c; + { + c->h0=INIT_DATA_h0; + c->h1=INIT_DATA_h1; + c->h2=INIT_DATA_h2; + c->h3=INIT_DATA_h3; + c->h4=INIT_DATA_h4; + c->Nl=0; + c->Nh=0; + c->num=0; + +#ifdef OPT_SHA1_ASM +#ifdef CPU_X86 + if (c->sha_block == NULL) + { + unsigned long cpu,attrib; + + /* We should make the methods loadable */ + cpu=r_cpuid(&attrib,NULL); + if (attrib & R_CPU_X86_HAS_PENTIUM_IV) + c->sha_block=sha1_block_786; + else if (attrib & R_CPU_X86_HAS_PENTIUM_PRO) + c->sha_block=sha1_block_686; + else + c->sha_block=sha1_block_586; + } +#else /* CPU_X86 */ + +#ifndef OPT_SHA1_ARM + c->sha_block=sha1_block_asm; +#else /* OPT_SHA1_ARM */ + if (c->sha_block == NULL) + { +#ifdef SMALL_CODE_SIZE + c->sha_block = sha1_arm4_small; +#else /* SMALL_CODE_SIZE */ + c->sha_block = sha1_arm4_fast; +#endif /* SMALL_CODE_SIZE */ + } +#endif /* OPT_SHA1_ARM */ +#endif /* CPU_X86 */ + +#else /* OPT_SHA1_ASM */ + c->sha_block=(void (PRE_CCONV CCONV *)(SHA_CTX *, const unsigned char *, int))sha1_block; +#endif /* OPT_SHA1_ASM */ + } + +#ifdef OPT_SHA1_ASM + +void SHA1_Update(c, data, len) +SHA_CTX *c; +const register unsigned char *data; +unsigned long len; + { + int i; + unsigned int alignment; + unsigned long l; + unsigned char *cp=(unsigned char *)c->data; + + if (len == 0) return; + + l=(c->Nl+(len<<3))&0xffffffffL; + if (l < c->Nl) /* overflow */ + c->Nh++; + c->Nh+=(len>>29); + c->Nl=l; + + if (c->num != 0) + { + if (c->num+len >= SHA_CBLOCK) + { + i=SHA_CBLOCK-c->num; + Memcpy(&(cp[c->num]),data,i); + len-=i; + data+=i; + + c->sha_block(c,cp,64); + c->num=0; + /* drop through and do the rest */ + } + else + { + Memcpy(&(cp[c->num]),data,len); + c->num+=(int)len; + return; + } + } + /* we now can process the input data in blocks of SHA_CBLOCK + * chars and save the leftovers to c->data. */ + if (len >= SHA_CBLOCK) + { + i=(int)(len& ~63); + len-=i; + + /* + * Check to see if the input data lies on a word boundary. + * Do this as the ASM relies on input data being word aligned. + */ + alignment = (((unsigned int)data) & (sizeof(unsigned int)-1)) + & 0x03; + if (alignment == 0) + { + c->sha_block(c,data,i); + data+=i; + } + else + { + do { + Memcpy(cp, data, SHA_CBLOCK); + data += SHA_CBLOCK; + c->sha_block(c, cp, SHA_CBLOCK); + i -= SHA_CBLOCK; + } while (i > 0); + } + } + c->num=len; + if (len) + { + Memcpy(cp,data,(int)len); + } + } + +void SHA1_Transform(c,b) +SHA_CTX *c; +const unsigned char *b; + { + c->sha_block(c,b,64); + } + +void SHA1_Final(md, c) +unsigned char *md; +SHA_CTX *c; + { + register int i,j; + register SHA_LONG l; + register SHA_LONG *p; + const static unsigned char end[4]={0x80,0x00,0x00,0x00}; + unsigned char *cp= (unsigned char *)end; + unsigned char *pc; + + /* c->num should definitly have room for at least one more byte. */ + p=c->data; + j=c->num; + i=j>>2; + +#ifdef PURIFY + /* PURIFY */ + /* we reference uninitialised data but don't keep the result + * which purify complains about ... and we don't want to have + * to come back here to find a non-existant problem later + */ + + /* purify often complains about the following line as an + * Uninitialized Memory Read. While this can be true, the + * following p_c2l macro will reset l when that case is true. + * This is because j&0x03 contains the number of 'valid' bytes + * already in p[i]. If and only if j&0x03 == 0, the UMR will + * occur but this is also the only time p_c2l will do + * l= *(cp++) instead of l|= *(cp++) + */ + if ((j&0x03) == 0) p[i]=0; +#endif + + pc=(unsigned char *)c->data; + pc[j]=0x80; + for (j++; j & 0x03; j++) + pc[j]=0; + i++; + /* i is the next 'undefined word' */ + if (c->num >= SHA_LAST_BLOCK) + { + for (; isha_block(c,(unsigned char *)p,64); + i=0; + } + for (; i<(SHA_LBLOCK-2); i++) + p[i]=0; + + l=c->Nl; + pc[63]=(unsigned char)((l )&0xff); + pc[62]=(unsigned char)((l>> 8)&0xff); + pc[61]=(unsigned char)((l>>16)&0xff); + pc[60]=(unsigned char)((l>>24)&0xff); + l=c->Nh; + pc[59]=(unsigned char)((l )&0xff); + pc[58]=(unsigned char)((l>> 8)&0xff); + pc[57]=(unsigned char)((l>>16)&0xff); + pc[56]=(unsigned char)((l>>24)&0xff); + + c->sha_block(c,(unsigned char *)p,64); + cp=md; + l=c->h0; nl2c(l,cp); + l=c->h1; nl2c(l,cp); + l=c->h2; nl2c(l,cp); + l=c->h3; nl2c(l,cp); + l=c->h4; nl2c(l,cp); + + /* clear stuff, sha1_block_asm may be leaving some stuff on the stack + * but I'm not worried :-) */ + c->num=0; +/* Memset((char *)&c,0,sizeof(c));*/ + } + +#else /* !OPT_SHA1_ASM */ + +void SHA1_Update(c, data, len) +SHA_CTX *c; +const register unsigned char *data; +unsigned long len; + { + register SHA_LONG *p; + int ew,ec,sw,sc; + SHA_LONG l; + + if (len == 0) return; + + l=(c->Nl+(len<<3))&0xffffffffL; + if (l < c->Nl) /* overflow */ + c->Nh++; + c->Nh+=(len>>29); + c->Nl=l; + + if (c->num != 0) + { + p=c->data; + sw=c->num>>2; + sc=c->num&0x03; + + if ((c->num+len) >= SHA_CBLOCK) + { + l= p[sw]; + M_p_c2nl(data,l,sc); + p[sw++]=l; + for (; swnum); + + c->sha_block(c,(unsigned char *)p,64); + c->num=0; + /* drop through and do the rest */ + } + else + { + c->num+=(int)len; + if ((sc+len) < 4) /* ugly, add char's to a word */ + { + l= p[sw]; + M_p_c2nl_p(data,l,sc,len); + p[sw]=l; + } + else + { + ew=(c->num>>2); + ec=(c->num&0x03); + l= p[sw]; + M_p_c2nl(data,l,sc); + p[sw++]=l; + for (; sw < ew; sw++) + { M_c2nl(data,l); p[sw]=l; } + if (ec) + { + M_c2nl_p(data,l,ec); + p[sw]=l; + } + } + return; + } + } + /* We can only do the following code for assember, the reason + * being that the sha1_block 'C' version changes the values + * in the 'data' array. The assember code avoids this and + * copies it to a local array. I should be able to do this for + * the C version as well.... + */ +#if defined(B_ENDIAN) || defined(OPT_SHA1_ASM) + if ((((unsigned long)data)%sizeof(SHA_LONG)) == 0) + { + sw=len/SHA_CBLOCK; + if (sw) + { + sw*=SHA_CBLOCK; + c->sha_block(c,(SHA_LONG *)data,sw); + data+=sw; + len-=sw; + } + } +#endif + /* we now can process the input data in blocks of SHA_CBLOCK + * chars and save the leftovers to c->data. */ + p=c->data; + while (len >= SHA_CBLOCK) + { +#if defined(B_ENDIAN) || defined(L_ENDIAN) + if (p != (SHA_LONG *)data) + Memcpy(p,data,SHA_CBLOCK); + data+=SHA_CBLOCK; +# ifdef L_ENDIAN +# ifndef OPT_SHA1_ASM /* Will not happen */ + for (sw=(SHA_LBLOCK/4); sw; sw--) + { + Endian_Reverse32(p[0]); + Endian_Reverse32(p[1]); + Endian_Reverse32(p[2]); + Endian_Reverse32(p[3]); + p+=4; + } + p=c->data; +# endif +# endif +#else + for (sw=(SHA_BLOCK/4); sw; sw--) + { + M_c2nl(data,l); *(p++)=l; + M_c2nl(data,l); *(p++)=l; + M_c2nl(data,l); *(p++)=l; + M_c2nl(data,l); *(p++)=l; + } + p=c->data; +#endif + c->sha_block(c,(unsigned char *)p,64); + len-=SHA_CBLOCK; + } + ec=(int)len; + c->num=ec; + ew=(ec>>2); + ec&=0x03; + + for (sw=0; sw < ew; sw++) + { M_c2nl(data,l); p[sw]=l; } + M_c2nl_p(data,l,ec); + p[sw]=l; + } + +void SHA1_Transform(c,b) +SHA_CTX *c; +const unsigned char *b; + { + SHA_LONG p[16]; +#ifndef B_ENDIAN + SHA_LONG *q; + int i; +#endif + +#if defined(B_ENDIAN) || defined(L_ENDIAN) + Memcpy(p,b,64); +#ifdef L_ENDIAN + q=p; + for (i=(SHA_LBLOCK/4); i; i--) + { + Endian_Reverse32(q[0]); + Endian_Reverse32(q[1]); + Endian_Reverse32(q[2]); + Endian_Reverse32(q[3]); + q+=4; + } +#endif +#else + q=p; + for (i=(SHA_LBLOCK/4); i; i--) + { + SHA_LONG l; + c2nl(b,l); *(q++)=l; + c2nl(b,l); *(q++)=l; + c2nl(b,l); *(q++)=l; + c2nl(b,l); *(q++)=l; + } +#endif + c->sha_block(c,(unsigned char *)p,64); + } + + +void sha1_block(c, W, num) +SHA_CTX *c; +SHA_LONG *W; +int num; + { +#ifndef SMALL_CODE_SIZE + register SHA_LONG A,B,C,D,E,T; + SHA_LONG X[16]; + + A=c->h0; + B=c->h1; + C=c->h2; + D=c->h3; + E=c->h4; + + for (;;) + { + BODY_00_15( 0,A,B,C,D,E,T,W); + BODY_00_15( 1,T,A,B,C,D,E,W); + BODY_00_15( 2,E,T,A,B,C,D,W); + BODY_00_15( 3,D,E,T,A,B,C,W); + BODY_00_15( 4,C,D,E,T,A,B,W); + BODY_00_15( 5,B,C,D,E,T,A,W); + BODY_00_15( 6,A,B,C,D,E,T,W); + BODY_00_15( 7,T,A,B,C,D,E,W); + BODY_00_15( 8,E,T,A,B,C,D,W); + BODY_00_15( 9,D,E,T,A,B,C,W); + BODY_00_15(10,C,D,E,T,A,B,W); + BODY_00_15(11,B,C,D,E,T,A,W); + BODY_00_15(12,A,B,C,D,E,T,W); + BODY_00_15(13,T,A,B,C,D,E,W); + BODY_00_15(14,E,T,A,B,C,D,W); + BODY_00_15(15,D,E,T,A,B,C,W); + BODY_16_19(16,C,D,E,T,A,B,W,W,W,W); + BODY_16_19(17,B,C,D,E,T,A,W,W,W,W); + BODY_16_19(18,A,B,C,D,E,T,W,W,W,W); + BODY_16_19(19,T,A,B,C,D,E,W,W,W,X); + + BODY_20_31(20,E,T,A,B,C,D,W,W,W,X); + BODY_20_31(21,D,E,T,A,B,C,W,W,W,X); + BODY_20_31(22,C,D,E,T,A,B,W,W,W,X); + BODY_20_31(23,B,C,D,E,T,A,W,W,W,X); + BODY_20_31(24,A,B,C,D,E,T,W,W,X,X); + BODY_20_31(25,T,A,B,C,D,E,W,W,X,X); + BODY_20_31(26,E,T,A,B,C,D,W,W,X,X); + BODY_20_31(27,D,E,T,A,B,C,W,W,X,X); + BODY_20_31(28,C,D,E,T,A,B,W,W,X,X); + BODY_20_31(29,B,C,D,E,T,A,W,W,X,X); + BODY_20_31(30,A,B,C,D,E,T,W,X,X,X); + BODY_20_31(31,T,A,B,C,D,E,W,X,X,X); + BODY_32_39(32,E,T,A,B,C,D,X); + BODY_32_39(33,D,E,T,A,B,C,X); + BODY_32_39(34,C,D,E,T,A,B,X); + BODY_32_39(35,B,C,D,E,T,A,X); + BODY_32_39(36,A,B,C,D,E,T,X); + BODY_32_39(37,T,A,B,C,D,E,X); + BODY_32_39(38,E,T,A,B,C,D,X); + BODY_32_39(39,D,E,T,A,B,C,X); + + BODY_40_59(40,C,D,E,T,A,B,X); + BODY_40_59(41,B,C,D,E,T,A,X); + BODY_40_59(42,A,B,C,D,E,T,X); + BODY_40_59(43,T,A,B,C,D,E,X); + BODY_40_59(44,E,T,A,B,C,D,X); + BODY_40_59(45,D,E,T,A,B,C,X); + BODY_40_59(46,C,D,E,T,A,B,X); + BODY_40_59(47,B,C,D,E,T,A,X); + BODY_40_59(48,A,B,C,D,E,T,X); + BODY_40_59(49,T,A,B,C,D,E,X); + BODY_40_59(50,E,T,A,B,C,D,X); + BODY_40_59(51,D,E,T,A,B,C,X); + BODY_40_59(52,C,D,E,T,A,B,X); + BODY_40_59(53,B,C,D,E,T,A,X); + BODY_40_59(54,A,B,C,D,E,T,X); + BODY_40_59(55,T,A,B,C,D,E,X); + BODY_40_59(56,E,T,A,B,C,D,X); + BODY_40_59(57,D,E,T,A,B,C,X); + BODY_40_59(58,C,D,E,T,A,B,X); + BODY_40_59(59,B,C,D,E,T,A,X); + + BODY_60_79(60,A,B,C,D,E,T,X); + BODY_60_79(61,T,A,B,C,D,E,X); + BODY_60_79(62,E,T,A,B,C,D,X); + BODY_60_79(63,D,E,T,A,B,C,X); + BODY_60_79(64,C,D,E,T,A,B,X); + BODY_60_79(65,B,C,D,E,T,A,X); + BODY_60_79(66,A,B,C,D,E,T,X); + BODY_60_79(67,T,A,B,C,D,E,X); + BODY_60_79(68,E,T,A,B,C,D,X); + BODY_60_79(69,D,E,T,A,B,C,X); + BODY_60_79(70,C,D,E,T,A,B,X); + BODY_60_79(71,B,C,D,E,T,A,X); + BODY_60_79(72,A,B,C,D,E,T,X); + BODY_60_79(73,T,A,B,C,D,E,X); + BODY_60_79(74,E,T,A,B,C,D,X); + BODY_60_79(75,D,E,T,A,B,C,X); + BODY_60_79(76,C,D,E,T,A,B,X); + BODY_60_79(77,B,C,D,E,T,A,X); + BODY_60_79(78,A,B,C,D,E,T,X); + BODY_60_79(79,T,A,B,C,D,E,X); + + c->h0=(c->h0+E)&0xffffffffL; + c->h1=(c->h1+T)&0xffffffffL; + c->h2=(c->h2+A)&0xffffffffL; + c->h3=(c->h3+B)&0xffffffffL; + c->h4=(c->h4+C)&0xffffffffL; + + num-=64; + if (num <= 0) break; + + A=c->h0; + B=c->h1; + C=c->h2; + D=c->h3; + E=c->h4; + + W+=16; + } +#else /* SMALL_CODE_SIZE */ + SHA_LONG A,B,C,D,E,T; + SHA_LONG X[16]; + SHA_LONG *a1,*a2,*a3; + + A=c->h0; + B=c->h1; + C=c->h2; + D=c->h3; + E=c->h4; + + for (;;) + { + int i; + + for (i=0; i<16; i++) + { + BODY_00_15(i,A,B,C,D,E,T,W); + E=D; D=C; C=B; B=A; A=T; + } + + a1=W; + for (i=16; i<20; i++) + { + if (i == 19) a1=X; + BODY_16_19(i,A,B,C,D,E,T,W,W,W,a1); + E=D; D=C; C=B; B=A; A=T; + } + + a1=a2=a3=W; + for (i=20; i<40; i++) + { + if (i == 24) a3=X; + if (i == 30) a2=X; + if (i == 32) a1=X; + BODY_20_31(i,A,B,C,D,E,T,a1,a2,a3,X); + E=D; D=C; C=B; B=A; A=T; + } + + for (i=40; i<60; i++) + { + BODY_40_59(i,A,B,C,D,E,T,X); + E=D; D=C; C=B; B=A; A=T; + } + + for (i=60; i<80; i++) + { + BODY_60_79(i,A,B,C,D,E,T,X); + E=D; D=C; C=B; B=A; A=T; + } + + c->h0=(c->h0+A)&0xffffffffL; + c->h1=(c->h1+B)&0xffffffffL; + c->h2=(c->h2+C)&0xffffffffL; + c->h3=(c->h3+D)&0xffffffffL; + c->h4=(c->h4+E)&0xffffffffL; + + num-=64; + if (num <= 0) break; + + A=c->h0; + B=c->h1; + C=c->h2; + D=c->h3; + E=c->h4; + + W+=16; + } +#endif /* SMALL_CODE_SIZE */ + } + +void SHA1_Final(md, c) +unsigned char *md; +SHA_CTX *c; + { + register int i,j; + register SHA_LONG l; + register SHA_LONG *p; + const static unsigned char end[4]={0x80,0x00,0x00,0x00}; + unsigned char *cp= (unsigned char *)end; + + /* c->num should definitly have room for at least one more byte. */ + p=c->data; + j=c->num; + i=j>>2; + +#ifdef PURIFY + /* PURIFY */ + /* we reference uninitialised data but don't keep the result + * which purify complains about ... and we don't want to have + * to come back here to find a non-existant problem later + */ + + /* purify often complains about the following line as an + * Uninitialized Memory Read. While this can be true, the + * following p_c2l macro will reset l when that case is true. + * This is because j&0x03 contains the number of 'valid' bytes + * already in p[i]. If and only if j&0x03 == 0, the UMR will + * occur but this is also the only time p_c2l will do + * l= *(cp++) instead of l|= *(cp++) + */ + if ((j&0x03) == 0) p[i]=0; +#endif + + l=p[i]; + M_p_c2nl(cp,l,j&0x03); + p[i]=l; + i++; + /* i is the next 'undefined word' */ + if (c->num >= SHA_LAST_BLOCK) + { + for (; isha_block(c,(unsigned char *)p,64); + i=0; + } + for (; i<(SHA_LBLOCK-2); i++) + p[i]=0; + p[SHA_LBLOCK-2]=c->Nh; + p[SHA_LBLOCK-1]=c->Nl; +#if defined(L_ENDIAN) + Endian_Reverse32(p[SHA_LBLOCK-2]); + Endian_Reverse32(p[SHA_LBLOCK-1]); +#endif + c->sha_block(c,(unsigned char *)p,64); + cp=md; + l=c->h0; nl2c(l,cp); + l=c->h1; nl2c(l,cp); + l=c->h2; nl2c(l,cp); + l=c->h3; nl2c(l,cp); + l=c->h4; nl2c(l,cp); + + /* clear stuff, sha1_block may be leaving some stuff on the stack + * but I'm not worried :-) */ + c->num=0; +/* Memset((char *)&c,0,sizeof(c));*/ + } +#endif + +#if 0 +int pr(ctx) +SHA_CTX *ctx; + { + int i,j; + unsigned char *p=(unsigned char *)(ctx->data); + + fprintf(stderr,"num = %08X%08X\n",ctx->Nh,ctx->Nl); + fprintf(stderr," %08X %08X %08X %08X %08X\n", + ctx->h0,ctx->h1,ctx->h2,ctx->h3,ctx->h4); + fprintf(stderr,"bufnum = %d\n",ctx->num); + fprintf(stderr," "); + for (j=0; j<64; j+=16) + { + for (i=0; i<16; i++) + { +/* + if ((i+j) >= ctx->num) + fprintf(stderr,"--"); + else +*/ + fprintf(stderr,"%02X",p[i+j]); + } + if ((j+16) >=64) + fprintf(stderr,"\n"); + else + fprintf(stderr,"\n "); + } + } + +#endif + +#endif /* NO_SHA1 */ diff --git a/build/tools/makegcdfirm/Makefile b/build/tools/makegcdfirm/Makefile new file mode 100644 index 00000000..6e91da85 --- /dev/null +++ b/build/tools/makegcdfirm/Makefile @@ -0,0 +1,148 @@ +#! make -f +#--------------------------------------------------------------------------- +# Project: TwlFirm - tools - makegcdfirm +# File: Makefile +# +# Copyright 2007 Nintendo. All rights reserved. +# +# These coded instructions, statements, and computer programs contain +# proprietary information of Nintendo of America Inc. and/or Nintendo +# Company Ltd., and are protected by Federal copyright law. They may +# not be disclosed to third parties or copied or duplicated in any form, +# in whole or in part, without the prior written consent of Nintendo. +# +# $Log: $ +# $NoKeywords: $ +#--------------------------------------------------------------------------- + +SUPPORT_ECC = 0 + +ifeq ($(SUPPORT_ECC),1) +ECC_SRCDIR = ../../libraries/acsign_ecc/common \ + ../../libraries/acsign_ecc/common/algae/common/ecc \ + ../../libraries/acsign_ecc/common/algae/cmp \ + ../../libraries/acsign_ecc/common/algae/ecsource \ + +ECC_INCDIR = ../../libraries/acsign_ecc/include \ + ../../libraries/acsign_ecc/common/algae/include \ + ../../libraries/acsign_ecc/common/algae/common/include \ + +ECC_SRCS = acsign_ecc.c acsign_cryptoc.c \ + \ + cmparith.c cmpbits.c cmpcnv.c cmpdiv.c cmpmem.c \ + cmpmod.c cmpmuldv.c cmpspprt.c cmpsqr.c cmpvectr.c \ + computem.c ecfpatbl.c ecfpsmul.c \ + spcprime.c secfpcom.c \ + \ + p224v1.c p224v1a.c \ + +ECC_DEFS = -DRSA_PROTOTYPES=RSA_ENABLED \ + -DRCOM_BUILD=RSA_ENABLED -DRSA_FAST_INVERSE=RSA_ENABLED \ + -DRSA_STD_MEM_FUNCS=RSA_ENABLED -DRSA_STD_ALLOC_FUNCS=RSA_ENABLED \ +else +ECC_SRCDIR = +ECC_INCDIR = +ECC_SRCS = +ECC_DEFS = +endif + +SRCDIR += ../acsign $(ECC_SRCDIR) +INCDIR += ../acsign/include $(ECC_INCDIR) $(ECC_SRCDIR) + + +include $(TWLFIRM_ROOT)/build/buildtools/commondefs + +TARGETS = makegcdfirm.exe + +SOURCES_C = makegcdfirm.c \ + out_gcdfirm.c \ + misc.c \ + path.c \ + defval.c \ + compress.c \ + wram_regs.c \ + acsign.c \ + acsign_gcd.c \ + aes2.c \ + $(ECC_SRCS) + +SOURCES = $(SORUCES_C) + +OBJECTS = $(SOURCES_C:.c=.o) + +HEADERS = format_nlist.h \ + makegcdfirm.h \ + path.h \ + format_rom.h \ + misc.h \ + defval.h \ + compress.h \ + +MACROS += -DSMALL_CODE_SIZE \ + -DSTANDALONE \ + -DOPT_32_BIT \ + -DNO_SPLIT \ + -DNO_FP_API \ + -DNO_R_DIAG \ + $(ECC_DEFS) + +INSTALL_DIR = $(FIRM_INSTALL_TOOLSDIR)/bin +INSTALL_TARGETS = $(TARGETS) + +LDIRT_CLEAN = $(OBJECTS) $(TARGETS) version.h + + +VPATH = $(SRCDIR) +NITRO_INCDIR := $(FIRM_INCDIR) -I$(TWL_INCDIR) -I$(NITRO_INCDIR) $(addprefix -I,$(INCDIR)) + +include $(TWL_NITROSDK_ROOT)/build/buildtools/modulerules.x86 + +#---------------------------------------------------------------------------- +# build +#---------------------------------------------------------------------------- +do-build: $(TARGETS) + +$(TARGETS): $(OBJECTS) + $(CC_X86) $+ -o $@ + +makegcdfirm.o: makegcdfirm.c makegcdfirm.h format_rom.h path.h version.h +out_gcdfirm.o: out_gcdfirm.c misc.h format_rom.h format_nlist.h format_sign.h elf.h compress.h \ + $(FIRM_INCDIR)/firm/format/sign.h \ + $(FIRM_INCDIR)/firm/format/wram_regs.h \ + $(FIRM_INCDIR)/firm/format/gcdfirm.h \ + +misc.o: misc.c misc.h +path.o: path.c path.h +compress.o: compress.c compress.h +wram_regs.o: wram_regs.c +acsign.o: acsign.c ../acsign/include/acsign.h +acsign_gcd.o: acsign_gcd.c format_sign.h \ + $(FIRM_INCDIR)/firm/format/sign.h \ + $(FIRM_INCDIR)/firm/format/wram_regs.h \ + $(FIRM_INCDIR)/firm/format/gcdfirm.h \ + +aes2.o: aes2.c aes2.h + +$(FIRM_INCDIR)/firm/format/sign.h: +$(FIRM_INCDIR)/firm/format/wram_regs.h: +$(FIRM_INCDIR)/firm/format/gcdfirm.h: +format_nlist.h: +format_rom.h: +makegcdfirm.h: +acsign.h: +acsign_gcd.h: +path.h: + +# avoid to warning message +misc.o:WARNING += -Wno-format-y2k + +# + +version.h: $(SOURCES) $(HEADERS) + @for i in $^ ; \ + do \ + date -r $$i +'#define SDK_DATE_OF_LATEST_FILE %Y%m%dUL'; \ + done | sort | tail -1 > $@ + +test: path.c misc.c + $(CC_X86) -DTEST $+ -o $@ diff --git a/build/tools/makegcdfirm/compress.c b/build/tools/makegcdfirm/compress.c new file mode 100644 index 00000000..023e49f4 --- /dev/null +++ b/build/tools/makegcdfirm/compress.c @@ -0,0 +1,292 @@ +/*---------------------------------------------------------------------------* + Project: TwlFirm - tools - makegcdfirm + File: compress.c + + Copyright 2007 Nintendo. All rights reserved. + + These coded instructions, statements, and computer programs contain + proprietary information of Nintendo of America Inc. and/or Nintendo + Company Ltd., and are protected by Federal copyright law. They may + not be disclosed to third parties or copied or duplicated in any form, + in whole or in part, without the prior written consent of Nintendo. + + $Log: $ + $NoKeywords: $ + *---------------------------------------------------------------------------*/ +#include +#include // atoi() +#include // strcmp() +#include // isprint() +#include // chdir() +#include +#include // UCHAR_MAX +#include +#include // stat() +#include "elf.h" +#include "misc.h" +#include "defval.h" +#include "format_rom.h" +#include "format_nlist.h" +#include "makegcdfirm.h" + +//#define ADD_HEADER + +#define DIFF_CODE_HEADER (0x80) +#define RL_CODE_HEADER (0x30) +#define LZ_CODE_HEADER (0x10) +#define HUFF_CODE_HEADER (0x20) +#define CODE_HEADER_MASK (0xF0) + +//=========================================================================== +// LZ77圧縮 +//=========================================================================== +static u8 SearchLZ(const u8 *nextp, u32 remainSize, u16 *offset); + +static u16 windowPos; +static u16 windowLen; + +static s16 LZOffsetTable[4096]; +static s16 LZByteTable[256]; +static s16 LZEndTable[256]; + + +static void LZInitTable(void) +{ + u16 i; + + for (i = 0; i < 256; i++) + { + LZByteTable[i] = -1; + LZEndTable[i] = -1; + } + windowPos = 0; + windowLen = 0; +} + +static void SlideByte(const u8 *srcp) +{ + s16 offset; + u8 in_data = *srcp; + u16 insert_offset; + + if (windowLen == 4096) + { + u8 out_data = *(srcp - 4096); + if ((LZByteTable[out_data] = LZOffsetTable[LZByteTable[out_data]]) == -1) + { + LZEndTable[out_data] = -1; + } + insert_offset = windowPos; + } + else + { + insert_offset = windowLen; + } + + offset = LZEndTable[in_data]; + if (offset == -1) + { + LZByteTable[in_data] = insert_offset; + } + else + { + LZOffsetTable[offset] = insert_offset; + } + LZEndTable[in_data] = insert_offset; + LZOffsetTable[insert_offset] = -1; + + if (windowLen == 4096) + { + windowPos = (u16)((windowPos + 1) % 0x1000); + } + else + { + windowLen++; + } +} + +static void LZSlide(const u8 *srcp, u32 n) +{ + u32 i; + + for (i = 0; i < n; i++) + { + SlideByte(srcp++); + } +} + +/*---------------------------------------------------------------------------* + Name: MI_CompressLZ + + Description: LZ77圧縮を行なう関数 + + Arguments: srcp 圧縮元データへのポインタ + size 圧縮元データサイズ + dstp 圧縮先データへのポインタ + 圧縮元データよりも大きいサイズのバッファが必要です。 + + Returns: 圧縮後のデータサイズ。 + 圧縮後のデータが圧縮前よりも大きくなる場合には圧縮を中断し0を返します。 + *---------------------------------------------------------------------------*/ +u32 LZCompWrite(u8 *srcp, u32 size, u8 *dstp, int boundary) +{ + u32 LZDstCount; // 圧縮データのバイト数 + u8 LZCompFlags; // 圧縮の有無を示すフラグ系列 + u8 *LZCompFlagsp; // LZCompFlags を格納するメモリ領域をポイント + u16 lastOffset; // 一致データまでのオフセット (その時点での最長一致データ) + u8 lastLength; // 一致データ長 (その時点での最長一致データ) + u8 i; + u32 dstMax; + +#ifdef ADD_HEADER + *(u32 *)dstp = size << 8 | LZ_CODE_HEADER; // データ・ヘッダ + dstp += 4; +#endif + LZDstCount = 4; + dstMax = size; + LZInitTable(); + + while (size > 0) + { + LZCompFlags = 0; + LZCompFlagsp = dstp++; // フラグ系列の格納先 + LZDstCount++; + + // フラグ系列が8ビットデータとして格納されるため、8回ループ + for (i = 0; i < 8; i++) + { + LZCompFlags <<= 1; // 初回 (i=0) は特に意味はない + if (size <= 0) + { + // 終端に来た場合はフラグを最後までシフトさせてから終了 + continue; + } + + if ((lastLength = SearchLZ(srcp, size, &lastOffset))) + { + // 圧縮可能な場合はフラグを立てる + LZCompFlags |= 0x1; + + // オフセットは上位4ビットと下位8ビットに分けて格納 + *dstp++ = (u8)((lastLength - 3) << 4 | (lastOffset - 1) >> 8); + *dstp++ = (u8)((lastOffset - 1) & 0xff); + LZDstCount += 2; + LZSlide(srcp, lastLength); + srcp += lastLength; + size -= lastLength; + } + else + { + // 圧縮なし + LZSlide(srcp, 1); + *dstp++ = *srcp++; + size--; + LZDstCount++; + } + } // 8回ループ終了 + *LZCompFlagsp = LZCompFlags; // フラグ系列を格納 + } + + // 16バイト境界アラインメント + // アラインメント用データ0 はデータサイズに含める + i = 0; + while (LZDstCount & (boundary - 1)) +// while ((LZDstCount + i) & 0x3) + { + *dstp++ = 0; + LZDstCount++; + i++; + } + + return LZDstCount; +} + +//-------------------------------------------------------- +// LZ77圧縮でスライド窓の中から最長一致列を検索します。 +// Arguments: startp データの開始位置を示すポインタ +// nextp 検索を開始するデータのポインタ +// remainSize 残りデータサイズ +// offset 一致したオフセットを格納する領域へのポインタ +// Return : 一致列が見つかった場合は TRUE +// 見つからなかった場合は FALSE +//-------------------------------------------------------- +static u8 SearchLZ(const u8 *nextp, u32 remainSize, u16 *offset) +{ + const u8 *searchp; + const u8 *headp, *searchHeadp; + u16 maxOffset; + u8 maxLength = 2; + u8 tmpLength; + s32 w_offset; + + if (remainSize < 3) + { + return 0; + } + + w_offset = LZByteTable[*nextp]; + + while (w_offset != -1) + { + if (w_offset < windowPos) + { + searchp = nextp - windowPos + w_offset; + } + else + { + searchp = nextp - windowLen - windowPos + w_offset; + } + + /* 無くても良いが、僅かに高速化する */ + if (*(searchp + 1) != *(nextp + 1) || *(searchp + 2) != *(nextp + 2)) + { + w_offset = LZOffsetTable[w_offset]; + continue; + } + + if (nextp - searchp < 2) + { + // VRAMは2バイトアクセスなので (VRAMからデータを読み出す場合があるため)、 + // 検索対象データは2バイト前からのデータにしなければならない。 + // + // オフセットは12ビットで格納されるため、4096以下 + break; + } + tmpLength = 3; + searchHeadp = searchp + 3; + headp = nextp + 3; + + while (((u32)(headp - nextp) < remainSize) && (*headp == *searchHeadp)) + { + headp++; + searchHeadp++; + tmpLength++; + + // データ長は4ビットで格納されるため、18以下 (3の下駄をはかせる) + if (tmpLength == (0xF + 3)) + { + break; + } + } + if (tmpLength > maxLength) + { + // 最大長オフセットを更新 + maxLength = tmpLength; + maxOffset = (u16)(nextp - searchp); + if (maxLength == (0xF + 3)) + { + // 一致長が最大なので、検索を終了する。 + break; + } + } + w_offset = LZOffsetTable[w_offset]; + } + + if (maxLength < 3) + { + return 0; + } + *offset = maxOffset; + return maxLength; +} + diff --git a/build/tools/makegcdfirm/compress.h b/build/tools/makegcdfirm/compress.h new file mode 100644 index 00000000..fcdb3ac0 --- /dev/null +++ b/build/tools/makegcdfirm/compress.h @@ -0,0 +1,37 @@ +/*---------------------------------------------------------------------------* + Project: TwlFirm - tools - makenorfirm + File: compress.h + + Copyright 2007 Nintendo. All rights reserved. + + These coded instructions, statements, and computer programs contain + proprietary information of Nintendo of America Inc. and/or Nintendo + Company Ltd., and are protected by Federal copyright law. They may + not be disclosed to third parties or copied or duplicated in any form, + in whole or in part, without the prior written consent of Nintendo. + + $Log: $ + $NoKeywords: $ + *---------------------------------------------------------------------------*/ +#ifndef COMPRESS_H_ +#define COMPRESS_H_ + +#include "misc.h" + + +/*---------------------------------------------------------------------------* + Name: MI_CompressLZ + + Description: LZ77圧縮を行なう関数 + + Arguments: srcp 圧縮元データへのポインタ + size 圧縮元データサイズ + dstp 圧縮先データへのポインタ + 圧縮元データよりも大きいサイズのバッファが必要です。 + + Returns: 圧縮後のデータサイズ。 + *---------------------------------------------------------------------------*/ +u32 LZCompWrite(u8 *srcp, u32 size, u8 *dstp, int boundary); + + +#endif //COMPRESS_H_ diff --git a/build/tools/makegcdfirm/defval.c b/build/tools/makegcdfirm/defval.c new file mode 100644 index 00000000..679fb0e6 --- /dev/null +++ b/build/tools/makegcdfirm/defval.c @@ -0,0 +1,315 @@ +/*---------------------------------------------------------------------------* + Project: NitroSDK - tools - makerom + File: defval.c + + Copyright 2003-2006 Nintendo. All rights reserved. + + These coded instructions, statements, and computer programs contain + proprietary information of Nintendo of America Inc. and/or Nintendo + Company Ltd., and are protected by Federal copyright law. They may + not be disclosed to third parties or copied or duplicated in any form, + in whole or in part, without the prior written consent of Nintendo. + + $Log: defval.c,v $ + Revision 1.10 2006/01/18 02:11:19 kitase_hirotake + do-indent + + Revision 1.9 2005/02/28 05:26:03 yosizaki + do-indent. + + Revision 1.8 2004/08/05 13:50:13 yasu + Support -M option + + Revision 1.7 2004/06/29 04:55:40 yasu + Use VBuffer to resolve variables + + Revision 1.6 2004/06/23 07:51:02 yasu + fix a bug as illegal memory freeing at ResolveDefVal() + + Revision 1.5 2004/05/27 00:40:49 yasu + care also about current directory (dot ".") + + Revision 1.4 2004/05/27 00:25:46 yasu + care about double-dots ".." for defvalue option :r, :e + + Revision 1.3 2004/05/27 00:11:19 yasu + fix a error when searching a "dot" of file extension + + Revision 1.2 2004/05/26 12:02:47 yasu + support :h, :t, :r, :e option for variable name + + Revision 1.1 2004/03/26 05:06:45 yasu + support variables like as -DNAME=VALUE + + $NoKeywords: $ + *---------------------------------------------------------------------------*/ +#include +#include // getenv() +#include // strcasecmp() +#include // getopt() +#include "misc.h" +#include "defval.h" + +typedef struct tValdef +{ + struct tValdef *next; + char *name; + char *value; +} +tValdef; + +tValdef *valdef_top = NULL; + + +// +// Add new define value via file +// +// opt : "DEFINE=VALUE" +// +BOOL AddDefValFromFile(char *filename) +{ + char *buffer; + int buffer_size; + int read_size; + FILE *fp; + + if (filename[0] == '-' && filename[1] == '\0') + { + fp = stdin; + } + else if (NULL == (fp = fopen(filename, "rb"))) + { + fprintf(stderr, "Cannot open file \"%s\".\n", filename); + return FALSE; + } + + buffer_size = DEFVAL_DEFAULT_BUFFER_SIZE; + + if (NULL == (buffer = malloc(buffer_size))) + { + fprintf(stderr, "Cannot allocate memory.\n"); + return FALSE; + } + + read_size = 0; + + while (NULL != fgets(buffer + read_size, buffer_size - read_size, fp)) + { + read_size = strlen(buffer); + + if (read_size == buffer_size - 1 && buffer[read_size - 1] != '\n') + { + buffer_size *= 2; + + if (NULL == (buffer = realloc(buffer, buffer_size))) + { + fprintf(stderr, "Cannot allocate memory.\n"); + return FALSE; + } + continue; + } + + AddDefVal(buffer); + read_size = 0; + } + + if (fp != stdin) + { + fclose(fp); + } + free(buffer); + + return TRUE; +} + + +// +// Add new define value +// +// opt : "DEFINE=VALUE" +// +void AddDefVal(char *opt) +{ + int i; + tValdef *t; + + for (i = 0;; i++) + { + if ('=' == opt[i] || '\0' == opt[i]) + { + break; + } + } + + if (i > 0) + { + t = Alloc(sizeof(tValdef)); + t->name = strncpy(Alloc(i + 1), opt, i); + t->name[i] = '\0'; + + if (opt[i] == '=') + { + i++; + } + t->value = strdup(opt + i); + + t->next = valdef_top; + valdef_top = t; + + debug_printf("DEFINE:$(%s)=\"%s\"\n", t->name, t->value); + } + return; +} + +// +// Search define value +// +// Return: value of specified name +// +char *SearchDefVal(char *name) +{ + tValdef *t; + + for (t = valdef_top; t; t = t->next) + { + if (!strcmp(t->name, name)) + { + return t->value; + } + } + + return getenv(name); +} + + +// +// Search define value and Modify it by : option +// +// Return: duplicated value of specified name modified by :x option +// +char *SearchDefValWithOption(char *name) +{ + int len_name = strlen(name); + char *value; + char option = '\0'; + + if (len_name > 2 && name[len_name - 2] == ':') + { + name[len_name - 2] = '\0'; + option = name[len_name - 1]; + } + + value = SearchDefVal(name); + + if (value) + { + int value_len = strlen(value); + int col_dot = value_len; + int col_filename = 0; + int i; + + for (i = 0; i < value_len; i++) + { + switch (value[i]) + { + case '.': + if (col_filename == i && + (value[i + 1] == '\0' || (value[i + 1] == '.' && value[i + 2] == '\0'))) + { + i = value_len; // exit loop if last entry is . or .. + } + else + { + col_dot = i; // Save the last dot column + } + break; + + case '/': + case '\\': + case ':': + col_filename = i + 1; // Save the last filename + col_dot = value_len; // Reset dot position + break; + + default: + ; + } + } + + switch (option) + { + case 'h': // Dirname with the last slash + value = strdup(value); + value[col_filename] = '\0'; + break; + + case 't': // Filename + value = strdup(value + col_filename); + break; + + case 'r': // All without . file extension + value = strdup(value); + value[col_dot] = '\0'; + break; + + case 'e': // File extension + value = strdup(value + col_dot + 1); + break; + + default: + value = strdup(value); + } + } + return value; +} + + +// +// Resolve define value +// +// Return: new string +// +char *ResolveDefVal(char *str) +{ + int i, j; + char *val; + VBuffer buf; + + InitVBuffer(&buf); + + for (i = 0; '\0' != str[i]; i++) + { + // search $(XXX) + if ('$' == str[i] && '(' == str[i + 1]) + { + for (j = i + 2; '\0' != str[j]; j++) + { + if (')' == str[j]) + { + str[j] = '\0'; + + // get value of XXX + val = SearchDefValWithOption(&str[i + 2]); + + // copy value of XXX + if (val) + { + char *s = val; + + while (*s) + { + PutVBuffer(&buf, *s); + s++; + } + free(val); + } + i = j; + goto next; + } + } + } + PutVBuffer(&buf, str[i]); + next:; + } + return GetVBuffer(&buf); // pass allocated buffer, should be freed by caller +} diff --git a/build/tools/makegcdfirm/defval.h b/build/tools/makegcdfirm/defval.h new file mode 100644 index 00000000..71355101 --- /dev/null +++ b/build/tools/makegcdfirm/defval.h @@ -0,0 +1,38 @@ +/*---------------------------------------------------------------------------* + Project: NitroSDK - tools - makerom + File: defval.h + + Copyright 2003-2006 Nintendo. All rights reserved. + + These coded instructions, statements, and computer programs contain + proprietary information of Nintendo of America Inc. and/or Nintendo + Company Ltd., and are protected by Federal copyright law. They may + not be disclosed to third parties or copied or duplicated in any form, + in whole or in part, without the prior written consent of Nintendo. + + $Log: defval.h,v $ + Revision 1.4 2006/01/18 02:11:19 kitase_hirotake + do-indent + + Revision 1.3 2005/02/28 05:26:03 yosizaki + do-indent. + + Revision 1.2 2004/08/05 13:50:13 yasu + Support -M option + + Revision 1.1 2004/03/26 05:06:45 yasu + support variables like as -DNAME=VALUE + + $NoKeywords: $ + *---------------------------------------------------------------------------*/ +#ifndef DEFVAL_H_ +#define DEFVAL_H_ + +#define DEFVAL_DEFAULT_BUFFER_SIZE (1024) + +BOOL AddDefValFromFile(char *filename); +void AddDefVal(char *opt); +char *SearchDefVal(char *name); +char *ResolveDefVal(char *str); + +#endif //DEFVAL_H_ diff --git a/build/tools/makegcdfirm/elf.h b/build/tools/makegcdfirm/elf.h new file mode 100644 index 00000000..c360cd33 --- /dev/null +++ b/build/tools/makegcdfirm/elf.h @@ -0,0 +1,431 @@ +/*---------------------------------------------------------------------------* + Project: TwlFirm - ELF + File: elf.h + + Copyright 2007 Nintendo. All rights reserved. + + These coded instructions, statements, and computer programs contain + proprietary information of Nintendo of America Inc. and/or Nintendo + Company Ltd., and are protected by Federal copyright law. They may + not be disclosed to third parties or copied or duplicated in any form, + in whole or in part, without the prior written consent of Nintendo. + *---------------------------------------------------------------------------*/ + +#ifndef ELF_H_ +#define ELF_H_ + +#include "misc.h" + +/*--------------------------------------------------------- + 型定義 + --------------------------------------------------------*/ +typedef u32 Elf32_Addr; /* size:4, align:4 Unsigned program address */ +typedef u16 Elf32_Half; /* size:2, align:2 Unsigned medium int */ +typedef u32 Elf32_Off; /* size:4, align:4 Unsigned file offset */ +typedef s32 Elf32_Sword; /* size:4, align:4 Signed large int */ +typedef u32 Elf32_Word; /* size:4, align:4 Unsigned large int */ + +/*--------------------------------------------------------- + ELF Header + --------------------------------------------------------*/ +/* e_identのインデックス */ +#define EI_MAG0 0 /* File identification */ +#define EI_MAG1 1 /* File identification */ +#define EI_MAG2 2 /* File identification */ +#define EI_MAG3 3 /* File identification */ +#define EI_CLASS 4 /* File class 0=invalid, 1=32bit, 2=64bit */ +#define EI_DATA 5 /* Data encoding 0=invalid, 1=LSB, 2=MSB */ +#define EI_VERSION 6 /* File version 現在は1 */ +#define EI_PAD 7 /* Start of padding bytes */ +#define EI_NIDENT 16 /* Size of e_ident[] */ + +typedef struct { + unsigned char e_ident[EI_NIDENT]; + Elf32_Half e_type; /* ELFの形式(再配置可能, 実行可能など) */ + Elf32_Half e_machine; /* ファイルで要求されるアーキテクチャ */ + Elf32_Word e_version; /* ELFフォーマットのバージョン(現在は1) */ + Elf32_Addr e_entry; /* プログラムのエントリポイント。指定無しなら0。 */ + Elf32_Off e_phoff; /* プログラムヘッダテーブルのファイル先頭からのオフセット */ + Elf32_Off e_shoff; /* セクションヘッダテーブルのファイル先頭からのオフセット */ + Elf32_Word e_flags; /* プロセッサ固有のフラグ */ + Elf32_Half e_ehsize; /* ELFヘッダのサイズ */ + Elf32_Half e_phentsize; /* 1プログラムヘッダのサイズ */ + Elf32_Half e_phnum; /* プログラムヘッダの数 */ + Elf32_Half e_shentsize; /* 1セクションヘッダのサイズ */ + Elf32_Half e_shnum; /* セクションヘッダの数 */ + Elf32_Half e_shstrndx; /* セクション名文字列テーブルセクションへのインデックス */ +} Elf32_Ehdr; + +/* e_ident[EI_*]の中身定義 */ +#define ELFMAG0 0x7f +#define ELFMAG1 'E' +#define ELFMAG2 'L' +#define ELFMAG3 'F' +#define ELFCLASSNONE 0 /* invalid */ +#define ELFCLASS32 1 /* ARM and Thumb processors use 32-bit ELF. */ +#define ELFCLASS64 2 +#define ELFDATANONE 0 /* invalid */ +#define ELFDATA2LSB 1 /* little-endian */ +#define ELFDATA2MSB 2 /* big-endian */ + + +/* [e_type] */ +#define ET_NONE 0 /* No file type */ +#define ET_REL 1 /* Re-locatable file */ +#define ET_EXEC 2 /* Executable file */ +#define ET_DYN 3 /* Shared object file */ +#define ET_CORE 4 /* Core file */ +#define ET_LOPROC 0xff00 /* Processor-specific */ +#define ET_HIPROC 0xffff /* Processor-specific */ + +/* [e_machine] */ +#define EM_NONE 0 /* No machine */ +#define EM_M32 1 +#define EM_SPARC 2 +#define EM_386 3 +#define EM_68K 4 +#define EM_88K 5 +#define EM_860 7 +#define EM_MIPS 8 +#define EM_MIPS_RS4_BE 10 +#define EM_ARM 40 /* ARM/Thumb Architecture */ + + +/* [e_version] This member identifies the object file version.*/ +#define EV_NONE 0 /* Invalid version */ +#define EV_CURRENT 1 /* Current version */ + + +/* + ARM-specific e_flags + e_flags Field Value Meaning + EF_ARM_HASENTRY (0x02) e_entry contains a program-loader entry point + (see section 4.1.1, Entry points, below). + EF_ARM_SYMSARESORTED (0x04) Each subsection of the symbol table is sorted by symbol value + (see section 4.4.8, Symbol table order, below) + EF_ARM_DYNSYMSUSESEGIDX (0x8) Symbols in dynamic symbol tables that are defined in sections + included in program segment n have st_shndx = n + 1. + (see section 4.4.9, Dynamic symbol table entries, below). + EF_ARM_MAPSYMSFIRST (0x10) Mapping symbols precede other local symbols in the symbol table + (see section 4.4.8, Symbol table order, below). + + EF_ARM_EABIMASK (0xFF000000)(current version is 0x02000000) + This masks an 8-bit version number, the version of the ARM + EABI to which this ELF file conforms. This EABI is version 2. A + value of 0 denotes unknown conformance. +*/ +#define EF_ARM_HASENTRY 0x02 +#define EF_ARM_SYMSARESORTED 0x04 +#define EF_ARM_DYNSYMSUSESEGIDX 0x8 +#define EF_ARM_MAPSYMSFIRST 0x10 +#define EF_ARM_EABIMASK 0xFF000000 + + +/*--------------------------------------------------------- + Program headers + --------------------------------------------------------*/ +typedef struct { + Elf32_Word p_type; + Elf32_Off p_offset; + Elf32_Addr p_vaddr; + Elf32_Addr p_paddr; + Elf32_Word p_filesz; + Elf32_Word p_memsz; + Elf32_Word p_flags; + Elf32_Word p_align; +} Elf32_Phdr; + +/* [p_type] */ +#define PT_NULL 0 /* 使われないエントリで、他のメンバの値の意味は未定義 */ +#define PT_LOAD 1 /* 実行時にロードされるセグメント */ +#define PT_DYNAMIC 2 /* 動的構造体配列を保持するセグメント */ +#define PT_INTERP 3 /* ファイルの解釈に使われるインタプリタのパスを保持するセグメント */ +#define PT_NOTE 4 /* ファイルの解釈には使われない情報を保持するセグメント */ +#define PT_SHLIB 5 /* 予約 */ +#define PT_PHDR 6 /* プログラムヘッダテーブル(プログラムのメモリイメージの一部である場合のみ存在) */ +//#define PT_TLS ? /* スレッド局所記憶領域のテンプレート */ + +#define PT_LOOS 0x60000000 /* OS固有に予約された領域 */ +#define PT_HIOS 0x6fffffff + +#define PT_LOPROC 0x70000000 /* プロセッサ固有に予約された領域 */ +#define PT_HIPROC 0x7fffffff + +/* [p_flags]*/ +#define PF_X 1 /*実行可能*/ +#define PF_W 2 /*書き込み可能*/ +#define PF_R 4 /*読み出し可能*/ +#define PF_ARM_SB 0x10000000 /*The segment contains the location addressed by the static base*/ +#define PF_ARM_PI 0x20000000 /*The segment is position-independent*/ +#define PF_ARM_ENTRY 0x80000000 /*The segment contains the entry point*/ +#define PF_MASKPROC 0xf0000000 + + +/*--------------------------------------------------------- + Section headers + --------------------------------------------------------*/ +typedef struct { + Elf32_Word sh_name; /*セクションヘッダ文字列テーブルセクションのインデックス*/ + Elf32_Word sh_type; /* タイプ(下記定義参照) */ + Elf32_Word sh_flags; + Elf32_Addr sh_addr; /* */ + Elf32_Off sh_offset; /* ファイルの先頭からのオフセット */ + Elf32_Word sh_size; /* バイト単位のサイズ */ + Elf32_Word sh_link; /* sh_typeによって値の意味が変わる */ + Elf32_Word sh_info; /* sh_typeによって値の意味が変わる */ + Elf32_Word sh_addralign; /* アラインメント制限(0or1で制限なし,4で4ByteAlign) */ + Elf32_Word sh_entsize; /* 固定サイズのエントリテーブルがある場合、1要素のサイズ */ +} Elf32_Shdr; + +/* sh_addr mod sh_addralign = 0 でなければならない */ + +/* Section Types, [sh_type] */ +#define SHT_NULL 0 +#define SHT_PROGBITS 1 +#define SHT_SYMTAB 2 +#define SHT_STRTAB 3 +#define SHT_RELA 4 +#define SHT_HASH 5 +#define SHT_DYNAMIC 6 +#define SHT_NOTE 7 +#define SHT_NOBITS 8 +#define SHT_REL 9 +#define SHT_SHLIB 10 +#define SHT_DYNSYM 11 +#define SHT_LOPROC 0x70000000 +#define SHT_HIPROC 0x7fffffff +#define SHT_LOUSER 0x80000000 +#define SHT_HIUSER 0xffffffff + + +/* [sh_flags] */ +#define SHF_WRITE 0x1 +#define SHF_ALLOC 0x2 +#define SHF_EXECINSTR 0x4 +#define SHF_MASKPROC 0xf0000000 +/* ARM-EABI-specific */ +#define SHF_ENTRYSECT 0x10000000 /* The section contains an entry point. */ +#define SHF_COMDEF 0x80000000 /* The section may be multiply defined in the input to a link step. */ +/* others */ +#define SHF_LINK_ORDER 0x80 + +/*セクションインデックス*/ +//Sym->st_shndxなど +#define SHN_UNDEF 0 +#define SHN_LORESERVE 0xff00 +#define SHN_LOPROC 0xff00 +#define SHN_HIPROC 0xff1f +#define SHN_ABS 0xfff1 +#define SHN_COMMON 0xfff2 +#define SHN_HIRESERVE 0xffff + + +//ここからはヘッダでなく実体データ構造 + +/*--------------------------------------------------------- + Symbol Table Entry + --------------------------------------------------------*/ +typedef struct { + Elf32_Word st_name; /* シンボル文字列テーブルのインデックス */ + Elf32_Addr st_value; /* おそらく関連するセクション内でのオフセット値 */ + Elf32_Word st_size; /* サイズがないか、不明な場合は 0 */ + unsigned char st_info; /* バインド と タイプ */ + unsigned char st_other; /* 現在は 0 が入る */ + Elf32_Half st_shndx; /* 関連するセクションヘッダテーブルのインデックス */ +} Elf32_Sym; + + +/* st_info */ +#define ELF32_ST_BIND(i) ((i)>>4) +#define ELF32_ST_TYPE(i) ((i)&0xf) +#define ELF32_ST_INFO(b,t) (((b)<<4)+((t)&0xf)) + +/* st_info の BIND */ +#define STB_LOCAL 0 +#define STB_GLOBAL 1 +#define STB_WEAK 2 +#define STB_LOPROC 13 +#define STB_HIPROC 15 + +/* st_info の TYPE */ +#define STT_NOTYPE 0 /*未定義*/ +#define STT_OBJECT 1 /*データオブジェクト*/ +#define STT_FUNC 2 +#define STT_SECTION 3 +#define STT_FILE 4 +#define STT_LOPROC 13 +#define STT_HIPROC 15 + + +/*--------------------------------------------------------- + Relocation Entry + --------------------------------------------------------*/ +typedef struct { + Elf32_Addr r_offset; + Elf32_Word r_info; +} Elf32_Rel; + +typedef struct { + Elf32_Addr r_offset; + Elf32_Word r_info; + Elf32_Sword r_addend; +} Elf32_Rela; + +#define ELF32_R_SYM(i) ((i)>>8) +#define ELF32_R_TYPE(i) ((unsigned char)(i)) +#define ELF32_R_INFO(s,t) (((s)<<8)+(unsigned char)(t)) + + +/* r_info の TYPE */ +#define R_ARM_NONE 0 /* Any No relocation. Encodes dependencies between sections. */ +#define R_ARM_PC24 1 /* ARM B/BL S . P + A */ +#define R_ARM_ABS32 2 /* 32-bit word S + A */ +#define R_ARM_REL32 3 /* 32-bit word S . P + A */ +#define R_ARM_PC13 4 /* ARM LDR r, [pc,…] S . P + A */ +#define R_ARM_ABS16 5 /* 16-bit half-word S + A */ +#define R_ARM_ABS12 6 /* ARM LDR/STR S + A */ +#define R_ARM_THM_ABS5 7 /* Thumb LDR/STR S + A */ +#define R_ARM_ABS8 8 /* 8-bit byte S + A */ +#define R_ARM_SBREL32 9 /* 32-bit word S . B + A */ +#define R_ARM_THM_PC22 10 /* Thumb BL pair S . P+ A */ +#define R_ARM_THM_PC8 11 /* Thumb LDR r, [pc,…] S . P + A */ +#define R_ARM_AMP_VCALL9 12 /* AMP VCALL Obsolete.SA-1500 only. */ +#define R_ARM_SWI24 13 /* ARM SWI S + A */ +#define R_ARM_THM_SWI8 14 /* Thumb SWI S + A */ +#define R_ARM_XPC25 15 /* ARM BLX S . P+ A */ +#define R_ARM_THM_XPC22 16 /* Thumb BLX pair S . P+ A */ + +/* 17-31, reserved to ARM Linux */ +//17-19 Reserved to ARM LINUX +#define R_ARM_COPY 20 /* 32 bit word Copy symbol at dynamic link time. */ +#define R_ARM_GLOB_DAT 21 /* 32 bit word Create GOT entry. */ +#define R_ARM_JUMP_SLOT 22 /* 32 bit word Create PLT entry. */ +#define R_ARM_RELATIVE 23 /* 32 bit word Adjust by program base. */ +#define R_ARM_GOTOFF 24 /* 32 bit word Offset relative to start of GOT. */ +#define R_ARM_GOTPC 25 /* 32 bit word Insert address of GOT. */ +#define R_ARM_GOT32 26 /* 32 bit word Entry in GOT. */ +#define R_ARM_PLT32 27 /* ARM BL Entry in PLT. */ + +/* 28-31 Reserved to ARM LINUX */ +#define R_ARM_ALU_PCREL_7_0 32 /* ARM ADD/SUB (S . P + A) & 0x000000FF */ +#define R_ARM_ALU_PCREL_15_8 33 /* ARM ADD/SUB (S . P + A) & 0x0000FF00 */ +#define R_ARM_ALU_PCREL_23_15 34 /* ARM ADD/SUB (S . P + A) & 0x00FF0000 */ +#define R_ARM_LDR_SBREL_11_0 35 /* ARM LDR/STR (S . B + A) & 0x00000FFF */ +#define R_ARM_ALU_SBREL_19_12 36 /* ARM ADD/SUB (S . B + A) & 0x000FF000 */ +#define R_ARM_ALU_SBREL_27_20 37 /* ARM ADD/SUB (S . B + A) & 0x0FF00000 */ + +#define R_ARM_TARGET1 38 +#define R_ARM_ROSEGREL32 39 +#define R_ARM_V4BX 40 +#define R_ARM_TARGET2 41 +#define R_ARM_PREL31 42 + +/* 96-111, reserved to ARM g++ */ +#define R_ARM_GNU_VTENTRY 100 /* 32 bit word Record C++ vtable entry. */ +#define R_ARM_GNU_VTINHERIT 101 /* 32 bit word Record C++ member usage. */ +#define R_ARM_THM_PC11 102 /* Thumb B S . P + A */ +#define R_ARM_THM_PC9 103 /* Thumb B S . P + A */ + +/* 112-127, reserved for private experiments */ + +/* 128-248, reserved to ARM */ +#define R_ARM_RXPC25 249 /* ARM BLX (ΔS . ΔP) + A #define For calls between program segments. */ +#define R_ARM_RSBREL32 250 /* Word (ΔS . ΔSB) + A For an offset from SB, the static base. */ +#define R_ARM_THM_RPC22 251 /* Thumb BL/BLX pair (ΔS . ΔP) + A For calls between program segments. */ +#define R_ARM_RREL32 252 /* Word (ΔS . ΔP) + A For on offset between two segments. */ +#define R_ARM_RABS32 253 /* Word ΔS + A For the address of a location in the target segment. */ +#define R_ARM_RPC24 254 /* ARM B/BL (ΔS . ΔP) + A For calls between program segments. */ +#define R_ARM_RBASE 255 /* None None.Identifies the segment being relocated by the following + relocation directives. The ARM EABI poses two problems for relocating + executables and shared objects encoded in */ + + +// shirait +#define R_ARM_LDR_PC_G0 4 //LDR + +#define R_ARM_ABS12 6 //LDR, STR + +#define R_ARM_THM_CALL 10 //R_ARM_THM_PC22と同じ + +#define R_ARM_CALL 28 //BL/BLX +#define R_ARM_JUMP24 29 //B/BL +#define R_ARM_THM_JUMP24 30 + +#define R_ARM_MOVW_ABS_NC 43 //MOVW +#define R_ARM_MOVT_ABS 44 //MOVT +#define R_ARM_MOVW_PREL_NC 45 //MOVW +#define R_ARM_MOVT_PREL 46 //MOVT + +#define R_ARM_ALU_PC_G0_NC 57 //ADD, SUB +#define R_ARM_ALU_PC_G0 58 //ADD, SUB +#define R_ARM_ALU_PC_G1_NC 59 //ADD, SUB +#define R_ARM_ALU_PC_G1 60 //ADD, SUB +#define R_ARM_ALU_PC_G2 61 //ADD, SUB +#define R_ARM_LDR_PC_G1 62 //LDR, STR, LDRB, STRB +#define R_ARM_LDR_PC_G2 63 //LDR, STR, LDRB, STRB +#define R_ARM_LDRS_PC_G0 64 //LDRD, STRD, LDRH, STRH, LDRSH, LDRSB +#define R_ARM_LDRS_PC_G1 65 //LDRD, STRD, LDRH, STRH, LDRSH, LDRSB +#define R_ARM_LDRS_PC_G2 66 //LDRD, STRD, LDRH, STRH, LDRSH, LDRSB +#define R_ARM_LDC_PC_G0 67 //LDC, STC +#define R_ARM_LDC_PC_G1 68 //LDC, STC +#define R_ARM_LDC_PC_G2 69 //LDC, STC +#define R_ARM_ALU_SB_G0_NC 70 //ADD, SUB +#define R_ARM_ALU_SB_G0 71 //ADD, SUB +#define R_ARM_ALU_SB_G1_NC 72 //ADD, SUB +#define R_ARM_ALU_SB_G1 73 //ADD, SUB +#define R_ARM_ALU_SB_G2 74 //ADD, SUB +#define R_ARM_LDR_SB_G0 75 //LDR, STR, LDRB, STRB +#define R_ARM_LDR_SB_G1 76 //LDR, STR, LDRB, STRB +#define R_ARM_LDR_SB_G2 77 //LDR, STR, LDRB, STRB +#define R_ARM_LDRS_SB_G0 78 //LDRD, STRD, LDRH, STRH, LDRSH, LDRSB +#define R_ARM_LDRS_SB_G1 79 //LDRD, STRD, LDRH, STRH, LDRSH, LDRSB +#define R_ARM_LDRS_SB_G2 80 //LDRD, STRD, LDRH, STRH, LDRSH, LDRSB +#define R_ARM_LDC_SB_G0 81 //LDC, STC +#define R_ARM_LDC_SB_G1 82 //LDC, STC +#define R_ARM_LDC_SB_G2 83 //LDC, STC +#define R_ARM_MOVW_BREL_NC 84 //MOVW +#define R_ARM_MOVT_BREL 85 //MOVT +#define R_ARM_MOVW_BREL 86 //MOVW + +#define R_ARM_GOT_BREL12 97 //LDR +#define R_ARM_GOTOFF12 98 //LDR, STR + +#define R_ARM_TLS_LDO12 109 //LDR, STR +#define R_ARM_TLS_LE12 110 //LDR, STR +#define R_ARM_TLS_TE12GP 111 //LDR + + + +/*--------------------------------------------------------- + Dynamic Section elf_v1.2 + --------------------------------------------------------*/ +typedef struct { + Elf32_Sword d_tag; + union { + Elf32_Word d_val; + Elf32_Addr d_ptr; + } d_un; +} Elf32_Dyn; + + +/* Additional symbol types for Thumb. */ +#define STT_ARM_TFUNC STT_LOPROC /* A Thumb function. */ +#define STT_ARM_16BIT STT_HIPROC /* A Thumb label. */ + + + + + + + + + +/*--------------------------------------------------------- + ELFヘッダを読み出す + --------------------------------------------------------*/ +void *ELF_LoadELFHeader(const void *buf, Elf32_Ehdr *ehdr); + + + +#endif /* ELF_H_ */ + diff --git a/build/tools/makegcdfirm/format_nlist.h b/build/tools/makegcdfirm/format_nlist.h new file mode 100644 index 00000000..87db583a --- /dev/null +++ b/build/tools/makegcdfirm/format_nlist.h @@ -0,0 +1,53 @@ +/*---------------------------------------------------------------------------* + Project: TwlFirm - tools - makegcdfirm + File: format_nlist.h + + Copyright 2007 Nintendo. All rights reserved. + + These coded instructions, statements, and computer programs contain + proprietary information of Nintendo of America Inc. and/or Nintendo + Company Ltd., and are protected by Federal copyright law. They may + not be disclosed to third parties or copied or duplicated in any form, + in whole or in part, without the prior written consent of Nintendo. + + $Log: $ + $NoKeywords: $ + *---------------------------------------------------------------------------*/ +#ifndef FORMAT_NLIST_H_ +#define FORMAT_NLIST_H_ + +#include +#include "misc.h" +#include "path.h" + +#define SPECFILE_MAGIC_ID "#NORSF" + +#define CRC16_INIT_VALUE 0xffff + +//--------------------------------------------------------------------------- +// Banner Spec File +//--------------------------------------------------------------------------- + +// Command List +typedef struct +{ + char *string; + BOOL (*funcp) (char *, int num); + +} +tCommandDesc; + + +// F Command +typedef struct +{ + u32 offsetStart; + u32 offsetEnd; + u32 padding; + char fullPathSrc[FILENAME_MAX]; + +} +tFileDesc; + + +#endif // FORMAT_NLIST_H_ diff --git a/build/tools/makegcdfirm/format_rom.h b/build/tools/makegcdfirm/format_rom.h new file mode 100644 index 00000000..ca2dfd18 --- /dev/null +++ b/build/tools/makegcdfirm/format_rom.h @@ -0,0 +1,58 @@ +/*---------------------------------------------------------------------------* + Project: TwlFirm - tools - makegcdfirm + File: format_rom.h + + Copyright 2007 Nintendo. All rights reserved. + + These coded instructions, statements, and computer programs contain + proprietary information of Nintendo of America Inc. and/or Nintendo + Company Ltd., and are protected by Federal copyright law. They may + not be disclosed to third parties or copied or duplicated in any form, + in whole or in part, without the prior written consent of Nintendo. + + $Log: $ + $NoKeywords: $ + *---------------------------------------------------------------------------*/ +#ifndef FORMAT_ROM_H_ +#define FORMAT_ROM_H_ + +#include "misc.h" +#include + + +#define DEFAULT_ALIGN 0x200 +#define FIRM_ALIGN DEFAULT_ALIGN +#define FIRM_ALIGN_MASK (FIRM_ALIGN - 1) + +#define NML_AREA_ALIGN 0x80000 // 512KB +#define TWL_AREA_ALIGN 0x80000 // 512KB +#define SEC2_AREA_OFFSET 0x3000 // 12KB +#define GAME2_AREA_OFFSET 0x7000 // 28KB + +#define DEFAULT_HOSTROOT "." +#define DEFAULT_ROOT "/" + +#define DEFAULT_REJECT_1 "CVS" +#define DEFAULT_REJECT_2 "vssver.scc" + +#define FORMAT_VERSION "1.0" + +#define ENTRYNAME_MAX 127 + +#define ROM_SIZE_MIN 0x20000 + +#define DEFAULT_ROMHEADER_TEMPLATE "gcdfirm_header_twlj.template.sbin" +#define DEFAULT_LISTFILE "default.nlf" + +#define DEFAULT_NORFIRM_SUFFIX ".gcd" +#define DEFAULT_SPECFILE_SUFFIX ".gcdsf" + +/*===========================================================================* + * ROM FORMAT + *===========================================================================*/ + +//--------------------------------------------------------------------------- +// ROM HEADER +//--------------------------------------------------------------------------- + +#endif //FORMAT_ROM_H_ diff --git a/build/tools/makegcdfirm/format_sign.h b/build/tools/makegcdfirm/format_sign.h new file mode 100644 index 00000000..e5cc43df --- /dev/null +++ b/build/tools/makegcdfirm/format_sign.h @@ -0,0 +1,31 @@ +/*---------------------------------------------------------------------------* + Project: TwlFirm - SS + File: format_sign.h + + Copyright 2006 Nintendo. All rights reserved. + + These coded instructions, statements, and computer programs contain + proprietary information of Nintendo of America Inc. and/or Nintendo + Company Ltd., and are protected by Federal copyright law. They may + not be disclosed to third parties or copied or duplicated in any form, + in whole or in part, without the prior written consent of Nintendo. + + $Log: $ + $NoKeywords: $ + *---------------------------------------------------------------------------*/ +#ifndef FIRM_MAKENORFIRM_ACSIGN_FORMAT_H_ +#define FIRM_MAKENORFIRM_ACSIGN_FORMAT_H_ + +#include "format_rom.h" + + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +/* FIRM_MAKENORFIRM_ACSIGN_FORMAT_H_ */ +#endif diff --git a/build/tools/makegcdfirm/gcdfirm_header_twlj.template.sbin b/build/tools/makegcdfirm/gcdfirm_header_twlj.template.sbin new file mode 100644 index 0000000000000000000000000000000000000000..60ed6b7f1c5e8d50dba1c3820d4dcff7ea73a12b GIT binary patch literal 16384 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 Cvf-cr literal 0 HcmV?d00001 diff --git a/build/tools/makegcdfirm/makegcdfirm.c b/build/tools/makegcdfirm/makegcdfirm.c new file mode 100644 index 00000000..5d7c2abd --- /dev/null +++ b/build/tools/makegcdfirm/makegcdfirm.c @@ -0,0 +1,105 @@ +/*---------------------------------------------------------------------------* + Project: TwlFirm - tools - makegcdfirm + File: makegcdfirm.c + + Copyright 2007 Nintendo. All rights reserved. + + These coded instructions, statements, and computer programs contain + proprietary information of Nintendo of America Inc. and/or Nintendo + Company Ltd., and are protected by Federal copyright law. They may + not be disclosed to third parties or copied or duplicated in any form, + in whole or in part, without the prior written consent of Nintendo. + + $Log: $ + $NoKeywords: $ + *---------------------------------------------------------------------------*/ +#include +#include +#include // strcasecmp() +#include // getopt() +#include "makegcdfirm.h" +#include "format_rom.h" +#include "path.h" +#include "defval.h" +#include "version.h" + +static int makegcdfirm(const char *specFile, const char *norFile); + +//--------------------------------------------------------------------------- +// Main +//--------------------------------------------------------------------------- + +int main(int argc, char *argv[]) +{ + int n; + int narg; + char *gcdfirmFile; + + InitAppName(argv[0]); + + while ((n = getopt(argc, argv, "D:hvpd")) != -1) + { + switch (n) + { + case 'h': + case 'v': + goto usage; + + case 'D': + AddDefVal(optarg); + break; + + case 'p': + PrintMode = TRUE; + break; + + case 'd': + DebugMode = TRUE; + break; + + default: + break; + } + } + + narg = argc - optind; + if (narg > 0) + { + // Make SpecFile->GcdfirmFile + gcdfirmFile = + strdup(narg > + 1 ? argv[optind + 1] : ChangeSuffix(argv[optind], DEFAULT_NORFIRM_SUFFIX)); + return makegcdfirm(argv[optind], gcdfirmFile); + } + + usage: + { + char *makegcdfirm = GetAppName(); + + fprintf(stderr, + "NITRO-SDK Development Tool - %s - Make gcdfirm file \n" + "Build %lu\n\n" + "Usage: %s [-phv] [-DNAME=VALUE ...] SPECFILE [NORFIRMFILE]\n\n", + makegcdfirm, SDK_DATE_OF_LATEST_FILE, makegcdfirm); + } + return 1; +} + + +//--------------------------------------------------------------------------- +// makegcdfirm +//--------------------------------------------------------------------------- + +static int makegcdfirm(const char *specFile, const char *norFile) +{ + debug_printf("makegcdfirm(): '%s' -> '%s'\n", specFile, norFile); + + // Check identical + if (specFile && norFile && !strcasecmp(specFile, norFile)) + { + error("gcdfirm spec file is identical '%s'", norFile); + return 1; + } + + return OutputGcdfirmFile(specFile, norFile) ? 0 : 1; +} diff --git a/build/tools/makegcdfirm/makegcdfirm.h b/build/tools/makegcdfirm/makegcdfirm.h new file mode 100644 index 00000000..6120c48f --- /dev/null +++ b/build/tools/makegcdfirm/makegcdfirm.h @@ -0,0 +1,23 @@ +/*---------------------------------------------------------------------------* + Project: TwlFirm - tools - makegcdfirm + File: makegcdfirm.h + + Copyright 2007 Nintendo. All rights reserved. + + These coded instructions, statements, and computer programs contain + proprietary information of Nintendo of America Inc. and/or Nintendo + Company Ltd., and are protected by Federal copyright law. They may + not be disclosed to third parties or copied or duplicated in any form, + in whole or in part, without the prior written consent of Nintendo. + + $Log: $ + $NoKeywords: $ + *---------------------------------------------------------------------------*/ +#ifndef MAKEGCDFIRM_H_ +#define MAKEGCDFIRM_H_ + +#include "misc.h" + +BOOL OutputGcdfirmFile(const char *specFile, const char *gcdFile); + +#endif //MAKEGCDFIRM_H_ diff --git a/build/tools/makegcdfirm/misc.c b/build/tools/makegcdfirm/misc.c new file mode 100644 index 00000000..e11bd855 --- /dev/null +++ b/build/tools/makegcdfirm/misc.c @@ -0,0 +1,627 @@ +/*---------------------------------------------------------------------------* + Project: TwlFirm - tools - makenorfirm + File: misc.c + + Copyright 2007 Nintendo. All rights reserved. + + These coded instructions, statements, and computer programs contain + proprietary information of Nintendo of America Inc. and/or Nintendo + Company Ltd., and are protected by Federal copyright law. They may + not be disclosed to third parties or copied or duplicated in any form, + in whole or in part, without the prior written consent of Nintendo. + + $Log: $ + $NoKeywords: $ + *---------------------------------------------------------------------------*/ +#include +#include // calloc() +#include // free(), exit() +#include // setmode() +#include // stat() +#include // setmode() +#include // strlen() +#include // va_start(),va_end() +#include // localtime() + +#include +#include "misc.h" + +BOOL DebugMode = FALSE; +BOOL PrintMode = FALSE; +char *PubkeyFileName = NULL; + +/*---------------------------------------------------------------------------* + * File Output Utilities + * + * BOOL OpenFile( const char* filename ) + * void CloseFile( void ) + * BOOL CheckResult( void ) + * void PutBuffer( const void* ptr, int len ) + * void PutByte( u8 c ) + * void PutWord( u16 c ) + * void PutWord( u32 c ) + * void PutString( const char *str ) + *---------------------------------------------------------------------------*/ + +static FILE *OutFile = NULL; +static const char *FileName = NULL; +static BOOL Status = FALSE; + + +// +// File Open +// + +BOOL OpenFile(const char *filename) +{ + if (OutFile) + CloseFile(); + + if (filename) + { + if (NULL == (OutFile = fopen(filename, "wb+"))) + { + error("Can't write '%s'", filename); + Status = FALSE; + return FALSE; + } + } + else + { + setmode(1, O_BINARY); + OutFile = stdout; // out to console if filename == NULL + } + FileName = filename; + Status = TRUE; + + return TRUE; +} + + +// +// File Close +// + +BOOL CloseFile(void) +{ + if (OutFile) + { + if (FileName) + { + if (fclose(OutFile) == -1) + { + error("Can't close '%s'", FileName); + Status = FALSE; + } + } + else + { + setmode(1, O_TEXT); + } + } + OutFile = NULL; + + return Status; +} + + +// +// File Seek +// + +void SeekFile(long pos) +{ + if (OutFile && fseek(OutFile, pos, SEEK_SET)) + { + error("Can't seek '%s'", FileName ? FileName : ""); + CloseFile(); + Status = FALSE; + } +} + + +// +// Error Check +// + +BOOL CheckResult(void) +{ + return Status; +} + + +// +// Delete outfile +// + +void DeleteOutFile(void) +{ + // Delete outfile + if (FileName) + { + debug_printf("Delete '%s'\n", FileName); + (void)unlink(FileName); + FileName = NULL; + } + return; +} + + +// +// Buffer Output +// + +void PutBuffer(const void *ptr, int len) /* If error, close file */ +{ + if (OutFile && len != fwrite(ptr, 1, len, OutFile)) + { + error("Can't write buffer to '%s'", FileName ? FileName : ""); + CloseFile(); + Status = FALSE; + } +} + +// +// Buffer Input +// + +void GetBuffer(void *ptr, int len) /* If error, close file */ +{ + if (OutFile && len != fread(ptr, 1, len, OutFile)) + { + error("Can't read '%s'", FileName ? FileName : ""); + CloseFile(); + Status = FALSE; + } +} + +// +// Byte/Half/Word Output +// + +void PutByte(u8 c) +{ + PutBuffer(&c, 1); +} +void PutHalf(u16 c) +{ + PutBuffer(&c, 2); +} +void PutWord(u32 c) +{ + PutBuffer(&c, 4); +} +void PutString(const char *str) +{ + PutBuffer(str, strlen(str)); +} + + +// +// Printf +// + +void PrintString(const char *fmt, ...) +{ + char *buffer; + va_list va; + int nchars; + int bufsize = FILENAME_MAX; + + while (1) + { + buffer = Alloc(bufsize); + va_start(va, fmt); + nchars = vsnprintf(buffer, bufsize, fmt, va); + va_end(va); + + if (0 <= nchars && nchars < bufsize) + { + break; + } + + Free(&buffer); + bufsize *= 2; + } + + if (nchars > 0) + { + PutBuffer(buffer, nchars); + } + Free(&buffer); +} + + +/*---------------------------------------------------------------------------* + * File Read/Write Utilities + * + * int ReadFile( const char* filename, void** buffer ) + * + * Read a file to buffer allocated internally + * Return read size + * Add '\0' at tail of file for help to handle text file + * + * BOOL WriteFile( const char* filename, void** buffer, int size ) + *---------------------------------------------------------------------------*/ + +int ReadFile(const char *filename, void *filebuffer, int size) +{ + FILE *fp; + struct stat fileStat; + int fileSize, readSize; + void **buffer = (void **)filebuffer; + + /* Check file */ + if (stat(filename, &fileStat) || !S_ISREG(fileStat.st_mode)) + { + goto error; + } + fileSize = fileStat.st_size; + if (fileSize < 0) + { + goto error; + } + + /* Open file */ + fp = fopen(filename, "rb"); + if (!fp) + { + goto error; + } + + if (size && (size < fileSize)) + { + readSize = size; + } + else + { + readSize = fileSize; + } + + /* Read file */ + *buffer = Alloc(readSize + 1); /* error handle is done in Alloc() */ + /* size+1 for '\0' to handle text file */ + if (readSize != fread(*buffer, sizeof(char), readSize, fp)) + { + goto error_free_close; + } + + (*(char **)buffer)[readSize] = '\0'; /* Works as terminater if file is text file */ + + /* Close file */ + fclose(fp); + return readSize; + + error_free_close: + Free(buffer); + fclose(fp); + error: + error("Can't read '%s'", filename); + return -1; +} + + +int ReadFileWithPadding(const char *filename, void *filebuffer, int size, int boundary) +{ + FILE *fp; + struct stat fileStat; + int fileSize, readSize, padSize; + void **buffer = (void **)filebuffer; + + if (!boundary) + { + return ReadFile(filename, buffer, size); + } + + /* Check file */ + if (stat(filename, &fileStat) || !S_ISREG(fileStat.st_mode)) + { + goto error; + } + fileSize = fileStat.st_size; + if (fileSize < 0) + { + goto error; + } + + /* Open file */ + fp = fopen(filename, "rb"); + if (!fp) + { + goto error; + } + + if (size && (size < fileSize)) + { + readSize = size; + } + else + { + readSize = fileSize; + } + + padSize = (boundary - (readSize & (boundary-1))) & (boundary-1); + + /* Read file */ + *buffer = Alloc(readSize + padSize); /* error handle is done in Alloc() */ + /* size+1 for '\0' to handle text file */ + if (readSize != fread(*buffer, sizeof(char), readSize, fp)) + { + goto error_free_close; + } + + memset((char*)*buffer + readSize, 0, padSize); + + /* Close file */ + fclose(fp); + return readSize + padSize; + + error_free_close: + Free(buffer); + fclose(fp); + error: + error("Can't read '%s'", filename); + return -1; +} + + +BOOL WriteFile(const char *filename, void *buffer, int size) +{ + debug_printf("WriteFile %s\n", filename); + + (void)OpenFile(filename); + PutBuffer(buffer, size); + CloseFile(); + Free(&buffer); + return CheckResult(); +} + + +/*---------------------------------------------------------------------------* + * Time Format Utilities + * + * char* GetGMTime( const time_t time ) Show GMT + * char* GetTime( const time_t time ) Show local Time + *---------------------------------------------------------------------------*/ + +char *GetGMTime(const time_t time) +{ + static char timebuffer[32]; + strftime(timebuffer, sizeof(timebuffer), "%y/%m/%d-%H:%M:%S", gmtime(&time)); + return timebuffer; +} + + +char *GetTime(const time_t time) +{ + static char timebuffer[32]; + strftime(timebuffer, sizeof(timebuffer), "%y/%m/%d-%H:%M:%S", localtime(&time)); + return timebuffer; +} + + +/*---------------------------------------------------------------------------* + * Memory Allocation Utilities + * + * void* Alloc( size_t size ) + *---------------------------------------------------------------------------*/ + +void *Alloc(size_t size) +{ + void *t = calloc(1, size); + + if (t == NULL) + { + error("Can't allocate memory."); + exit(10); + } + return t; +} + + +void Free(void *p) +{ + void **ptr = (void **)p; + + if (*ptr) + { + free(*ptr); + (*ptr) = NULL; + } +} + + +/*---------------------------------------------------------------------------* + * VBuffer + * + * void PutVBuffer( VBuffer* vbuf, char c ) + *---------------------------------------------------------------------------*/ + +void PutVBuffer(VBuffer * vbuf, char c) +{ + int size; + char *tmp_buffer; + + if (vbuf->buffer == 0) + { + vbuf->size = VBUFFER_INITIAL_SIZE; + vbuf->buffer = Alloc(vbuf->size); // buffer is CALLOCed + } + size = strlen(vbuf->buffer); + + if (size >= vbuf->size - 1) + { + // Need buffer expansion + vbuf->size *= 2; + tmp_buffer = Alloc(vbuf->size); // buffer is CALLOCed + strcpy(tmp_buffer, vbuf->buffer); + Free(&vbuf->buffer); + vbuf->buffer = tmp_buffer; + } + vbuf->buffer[size] = c; + return; +} + +char *GetVBuffer(VBuffer * vbuf) +{ + return vbuf->buffer; +} + +void InitVBuffer(VBuffer * vbuf) +{ + vbuf->buffer = 0; + vbuf->size = 0; +} + +void FreeVBuffer(VBuffer * vbuf) +{ + Free(&vbuf->buffer); +} + + +/*---------------------------------------------------------------------------* + * File Path Utilities + * + * char* ChangeBackSlash( char* path ) + *---------------------------------------------------------------------------*/ + +char *ChangeBackSlash(char *path) +{ + char *p = path; + + while (*p) + { + if (*p == '\\') + { + *p = '/'; + } + p++; + } + return path; +} + + +/*---------------------------------------------------------------------------* + * Math + * + * u16 CalcCRC16( u16 start, u8 *data, int size ) + *---------------------------------------------------------------------------*/ + +static u16 crc16_table[16] = { + 0x0000, 0xCC01, 0xD801, 0x1400, + 0xF001, 0x3C00, 0x2800, 0xE401, + 0xA001, 0x6C00, 0x7800, 0xB401, + 0x5000, 0x9C01, 0x8801, 0x4400 +}; + +u16 CalcCRC16(u16 start, u8 *data, int size) +{ + u16 r1; + u16 total = start; + + while (size-- > 0) + { + // 下位4bit + r1 = crc16_table[total & 0xf]; + total = (total >> 4) & 0x0fff; + total = total ^ r1 ^ crc16_table[*data & 0xf]; + + // 上位4bit + r1 = crc16_table[total & 0xf]; + total = (total >> 4) & 0x0fff; + total = total ^ r1 ^ crc16_table[(*data >> 4) & 0xf]; + + data++; + } + return total; +} + + +/*---------------------------------------------------------------------------* + * for firm header + * + *---------------------------------------------------------------------------*/ + +static u8 ConvertAlign( u32 ofs ) +{ + u8 i; + + ofs /= 4; + for (i=0; i<7; i++) + { + if ( ofs & 1 ) + { + break; + } + ofs >>= 1; + } + + return i; +} + +tROMAddrConvType ConvertHeaderRamAddr( s32 p ) +{ + tROMAddrConvType retval; + + retval.align = ConvertAlign( p ); + retval.address = (u16)((p - HW_WRAM)/(4< + +typedef enum +{ + FALSE = 0, + TRUE = 1 +} +BOOL; + +typedef unsigned char u8; +typedef unsigned short int u16; +typedef unsigned long int u32; +typedef unsigned long long u64; +typedef signed char s8; +typedef signed short int s16; +typedef signed long int s32; +typedef signed long long s64; + +#define error(...) do { fprintf(stderr, "Error: "); \ + fprintf(stderr, __VA_ARGS__); \ + fprintf(stderr, "\n"); } while(0) + +#define warning(...) do { fprintf(stderr, "Warning: "); \ + fprintf(stderr, __VA_ARGS__); \ + fprintf(stderr, "\n"); } while(0) + +BOOL OpenFile(const char *filename); +BOOL CloseFile(void); +void SeekFile(long pos); +BOOL CheckResult(void); +void DeleteOutFile(void); +void PutBuffer(const void *ptr, int len); +void GetBuffer(void *ptr, int len); +void PutByte(u8 c); +void PutHalf(u16 c); +void PutWord(u32 c); +void PutString(const char *str); +void PrintString(const char *fmt, ...); + +#define READ_ALL 0 +int ReadFile(const char *filename, void *filebuffer, int size); +int ReadFileWithPadding(const char *filename, void *filebuffer, int size, int boundary); +BOOL WriteFile(const char *filename, void *buffer, int size); + +char *GetGMTime(const time_t time); +char *GetTime(const time_t time); + +void *Alloc(size_t size); +void Free(void *p); + +typedef struct +{ + char *buffer; + int size; +} +VBuffer; + +#define VBUFFER_INITIAL_SIZE 1024 +void InitVBuffer(VBuffer * vbuf); +void FreeVBuffer(VBuffer * vbuf); +void PutVBuffer(VBuffer * vbuf, char c); +char *GetVBuffer(VBuffer * vbuf); + +char *ChangeBackSlash(char *path); + +u16 CalcCRC16(u16 start, u8 *data, int size); +const char *WrapNull(const char *str); + +typedef struct +{ + u16 address; + u8 align; +} +tROMAddrConvType; + +tROMAddrConvType ConvertHeaderRamAddr( s32 p ); +tROMAddrConvType ConvertHeaderRomOffset( s32 p ); +u16 ConvertHeaderRomOffsetAlign( s32 p, u32 align ); + +typedef union +{ + struct + { + u32 sign:1; + u32 header_hash:1; + u32 arm9_hash:1; + u32 arm7_hash:1; + u32 hash_table_hash:1; + u32 final_hash:1; + u32 header_footer:1; + } + e; + u32 raw; +} +tErrorFlags; + +extern BOOL DebugMode; +extern BOOL PrintMode; +extern char *PubkeyFileName; +void debug_printf(const char *str, ...); +void debug_printf2(const char *str, ...); + +#endif //MISC_H_ diff --git a/build/tools/makegcdfirm/out_gcdfirm.c b/build/tools/makegcdfirm/out_gcdfirm.c new file mode 100644 index 00000000..eddf95eb --- /dev/null +++ b/build/tools/makegcdfirm/out_gcdfirm.c @@ -0,0 +1,994 @@ +/*---------------------------------------------------------------------------* + Project: TwlFirm - tools - makegcdfirm + File: out_gcdfirm.c + + Copyright 2007 Nintendo. All rights reserved. + + These coded instructions, statements, and computer programs contain + proprietary information of Nintendo of America Inc. and/or Nintendo + Company Ltd., and are protected by Federal copyright law. They may + not be disclosed to third parties or copied or duplicated in any form, + in whole or in part, without the prior written consent of Nintendo. + + $Log: $ + $NoKeywords: $ + *---------------------------------------------------------------------------*/ +#include +#include // atoi() +#include // strcmp() +#include // isprint() +#include // chdir() +#include +#include // UCHAR_MAX +#include +#include // stat() +#include "elf.h" +#include "misc.h" +#include "defval.h" +#include "format_rom.h" +#include "format_nlist.h" +#include "makegcdfirm.h" +#include "format_sign.h" +#include "acsign_gcd.h" +#include "compress.h" + +#define SDK_ASM +#include + +#include +#include "../acsign/aes2.h" + +#define ROMHDRCMD "ROM_HDR" +#define SBIN9CMD "ARM9_SBIN" +#define SBIN7CMD "ARM7_SBIN" +#define ELF9CMD "ARM9_ELF" +#define ELF7CMD "ARM7_ELF" +#define GAMECODECMD "GAMECODE" +#define ARM9X2CMD "ARM9_X2" +#define NMLOFSCMD "NML_OFS" +#define TWLOFSCMD "TWL_OFS" +#define VERCMD "VERSION" +#define RSAKEYCMD "RSA_KEY" +#define OUTKEYCMD "OUT_KEY" +#define WREGCMD "WRAM_RBIN" +#define NANDFMCMD "NANDFIRM" +#define NORFMCMD "NORFIRM" +#define ERRCMD "ERROR" + +static BOOL ConstructGcdfirmFile(char * specFile); +static BOOL ReadRomHeaderFile(const char *fileName); +static BOOL ReadSbinFile(const char *fileName, void* minfo, void* hash, BOOL comp); +static size_t ReadFirmFile(const char *fileName); +static BOOL ReadKeyFile(const char *fileName); +static BOOL ReadWramRegFile(const char *fileName); +static s32 GetRamAddr(const char *fileName); + +static BOOL EncryptBuffer(char *buffer, int length); + +static BOOL RomHeader_Command(char * line, int num); +static BOOL Sbin9_Command(char * line, int num); +static BOOL Sbin7_Command(char * line, int num); +static BOOL Elf9_Command(char * line, int num); +static BOOL Elf7_Command(char * line, int num); +static BOOL GAMECODE_Command(char * line, int num); +static BOOL ARM9X2_Command(char * line, int num); +static BOOL NMLOFS_Command(char * line, int num); +static BOOL TWLOFS_Command(char * line, int num); +static BOOL VERSION_Command(char * line, int num); +static BOOL RSAKEY_Command(char * line, int num); +static BOOL OUTKEY_Command(char * line, int num); +static BOOL WramRegs_Command(char * line, int num); +static BOOL NorFirm_Command(char * line, int num); +static BOOL NandFirm_Command(char * line, int num); +static BOOL ERROR_Command(char * line, int num); + +static BOOL InitializeAesKey(void); +static BOOL InitializeGcdfirmFile(void); +static BOOL FinalizeGcdfirmFile(const char *gcdFile); + +static s32 Offset; // Current offset +static int LineNum; // Line number for error message +static const char *specFileName; // specFile name for error message + +GCDHeader gcdHeader; // Gcdfirm Header Shadow +FIRMSignedContext signedContext; +u8 *keyFileBuf; +BOOL compArm9 = FALSE; +BOOL compArm7 = FALSE; +tErrorFlags errFlags; + +//--------------------------------------------------------------------------- +// Output - gcdfirm File +//--------------------------------------------------------------------------- + +BOOL OutputGcdfirmFile(const char *specFile, const char *gcdFile) +{ + char *buffer; + BOOL state; + + if (ReadFile(specFile, &buffer, READ_ALL) <= 0) + { + return FALSE; + } + + if (!OpenFile(gcdFile)) + { + return FALSE; + } + + specFileName = specFile; + + state = InitializeGcdfirmFile() && ConstructGcdfirmFile(buffer) && + FinalizeGcdfirmFile(gcdFile) && CloseFile(); + + if (!state) + { + DeleteOutFile(); + } + + return state; +} + + +//--------------------------------------------------------------------------- +// Output - Gcdfirm File +//--------------------------------------------------------------------------- + +static const tCommandDesc command[] = { + {ROMHDRCMD, RomHeader_Command}, + {SBIN9CMD, Sbin9_Command}, {SBIN7CMD, Sbin7_Command}, + {ELF9CMD, Elf9_Command},{ELF7CMD, Elf7_Command}, + {VERCMD, VERSION_Command}, + {GAMECODECMD, GAMECODE_Command}, + {ARM9X2CMD, ARM9X2_Command}, + {NMLOFSCMD, NMLOFS_Command}, + {TWLOFSCMD, TWLOFS_Command}, + {RSAKEYCMD, RSAKEY_Command}, + {OUTKEYCMD, OUTKEY_Command}, + {WREGCMD, WramRegs_Command}, + {NANDFMCMD, NandFirm_Command}, + {NORFMCMD, NorFirm_Command}, + {ERRCMD, ERROR_Command}, +}; + +BOOL ConstructGcdfirmFile(char * specFile) +{ + char *line; + char *line_top; + char *p; + int i; + + LineNum = 0; + Offset = 0x4000; + SeekFile(Offset); + + line = specFile++; + + while (*line != '\0') + { + LineNum++; + + // Get command line + line_top = line; + while (*line != '\0') + { + if (*line++ == '\n') + { + break; + } + } + + // Print for debug + debug_printf("GCDSF Line%4d [", LineNum, line); + for (p = line_top; p != line; p++) + { + if (isprint(*p)) + { + debug_printf("%c", *p); + } + } + debug_printf("]\n"); + + if (*line_top == '#') + { + } + else + { + for (i = 0; i < (sizeof(command) / sizeof(command[0])); i++) + { + if (!strncmp(line_top, command[i].string, strlen(command[i].string))) + { + if (command[i].funcp != NULL) + { + char line_cmd[FILENAME_MAX]; + char line_scan[FILENAME_MAX]; + char* line_conv; + int num; + + num = sscanf( line_top, + "%1024[^ :] : %1024[^ \x0d:#\n]", + line_cmd, line_scan + ); + line_conv = ResolveDefVal(line_scan); + + debug_printf("line_cmd = %s, line_conv = %s\n", line_cmd, line_conv); + + if (!command[i].funcp(line_conv, num - 1)) + return FALSE; + } + } + } + } + + } + return TRUE; +} + + +//--------------------------------------------------------------------------- +// Output - 'RomHeader' Command +//--------------------------------------------------------------------------- + +static BOOL RomHeader_Command(char * line, int num) +{ + if (num != 1) + { + error("Wrong format spec file '%s' line:%d", specFileName, LineNum); + return FALSE; + } + + debug_printf2("hom header file = %s\n", line); + + return ReadRomHeaderFile(line); +} + + +static BOOL ReadRomHeaderFile(const char *fileName) +{ + char *file; + int file_size; + struct stat st; + + if (FILESTATUS_FILE != GetFileStatus(&st, fileName)) + { + error("'%s' is not regular file.", fileName); + return FALSE; + } + + if ((file_size = ReadFile(fileName, &file, READ_ALL)) < 0) + return FALSE; + + gcdHeader = *(GCDHeader*)file; + + // Output file image with fitting region + SeekFile(0L); + PutBuffer(file, file_size); + + Free(&file); + + return CheckResult(); +} + +//--------------------------------------------------------------------------- +// Output - 'WramRegs' Command +//--------------------------------------------------------------------------- + +extern MIHeader_WramRegs wram_regs_init; +MIHeader_WramRegs* wram_regs; + +static BOOL WramRegs_Command(char * line, int num) +{ + if (num != 1) + { + error("Wrong format spec file '%s' line:%d", specFileName, LineNum); + return FALSE; + } + + debug_printf2("wram regs file = %s\n", line); + + return ReadWramRegFile(line); +} + +static BOOL ReadWramRegFile(const char *fileName) +{ + int file_size; + struct stat st; + + if (FILESTATUS_FILE != GetFileStatus(&st, fileName)) + { + error("'%s' is not regular file.", fileName); + return FALSE; + } + + if ((file_size = ReadFile(fileName, &wram_regs, READ_ALL)) < 0) + return FALSE; + + gcdHeader.h.w = *wram_regs; + + return CheckResult(); +} + +//--------------------------------------------------------------------------- +// Output - 'RSAKEY' Command +//--------------------------------------------------------------------------- + +static BOOL RSAKEY_Command(char * line, int num) +{ + if (num != 1) + { + error("Wrong format spec file '%s' line:%d", specFileName, LineNum); + return FALSE; + } + + debug_printf2("rsa key = %s\n", line); + + return ReadKeyFile(line); +} + +static BOOL ReadKeyFile(const char *fileName) +{ + int file_size; + struct stat st; + + if (FILESTATUS_FILE != GetFileStatus(&st, fileName)) + { + error("'%s' is not regular file.", fileName); + return FALSE; + } + + if ((file_size = ReadFile(fileName, &keyFileBuf, READ_ALL)) < 0) + return FALSE; + + return CheckResult(); +} + + +//--------------------------------------------------------------------------- +// Output - 'OUTKEY' Command +//--------------------------------------------------------------------------- + +static BOOL OUTKEY_Command(char * line, int num) +{ + if (num != 1) + { + error("Wrong format spec file '%s' line:%d", specFileName, LineNum); + return FALSE; + } + + debug_printf2("out key = %s\n", line); + + PubkeyFileName = line; + + return CheckResult(); +} + + +//--------------------------------------------------------------------------- +// Output - 'VERSION' Command +//--------------------------------------------------------------------------- + +static u8 ConvToBCD8(int x) +{ + u8 bcd = 0; + + x %= 100; + bcd |= (x / 10)<<4; + bcd |= (x % 10); + + return bcd; +} + +static BOOL VERSION_Command(char * line, int num) +{ + char scan[FILENAME_MAX]; + u64 version = -1; + + // rescan + num = sscanf( line, + "0x%1024[^\x0d\n]", + scan + ); + + if (num == 1) + { + // convert version info + version = strtoull(scan, NULL, 16); + } + else if (num == 0) + { + // generate version info + u8* ver8 = (u8*)&version; + time_t timer; + struct tm *t_st; + + time(&timer); + t_st = localtime(&timer); + + ver8[0] = ConvToBCD8(t_st->tm_min); + ver8[1] = ConvToBCD8(t_st->tm_hour); + ver8[2] = ConvToBCD8(t_st->tm_mday); + ver8[3] = ConvToBCD8(t_st->tm_mon+1); + ver8[4] = ConvToBCD8(t_st->tm_year); + ver8[5] = 0xff; + ver8[6] = 0xff; + ver8[7] = 0xff; + } + + debug_printf2("version = %08llx\n", version); + + return CheckResult(); +} + + +//--------------------------------------------------------------------------- +// Output - 'NMLOFS' Command +//--------------------------------------------------------------------------- + +static BOOL NMLOFS_Command(char * line, int num) +{ + u32 offset = 0; + + if (num != 1) + { + error("Wrong format spec file '%s' line:%d", specFileName, LineNum); + return FALSE; + } + + offset = strtoul(line, NULL, 0); + + gcdHeader.l.normal_area_offset = offset / NML_AREA_ALIGN; + + debug_printf2("normal area offset = %#x\n", offset); + + return CheckResult(); +} + + +//--------------------------------------------------------------------------- +// Output - 'TWLOFS' Command +//--------------------------------------------------------------------------- + +static BOOL TWLOFS_Command(char * line, int num) +{ + u32 offset = 0; + + if (num != 1) + { + error("Wrong format spec file '%s' line:%d", specFileName, LineNum); + return FALSE; + } + + offset = strtoul(line, NULL, 0); + + gcdHeader.l.twl_area_offset = offset / TWL_AREA_ALIGN; + + debug_printf2("twl area offset = %#x\n", offset); + + return CheckResult(); +} + + +//--------------------------------------------------------------------------- +// Output - 'ARM9_X2' Command +//--------------------------------------------------------------------------- + +static BOOL ARM9X2_Command(char * line, int num) +{ + char* dbg_str = "FALSE"; + + if (num != 1) + { + error("Wrong format spec file '%s' line:%d", specFileName, LineNum); + return FALSE; + } + + if (!strcmp(line, "TRUE")) + { + gcdHeader.l.arm9_x2 = TRUE; + dbg_str = "TRUE"; + } + + debug_printf2("arm9 x2 = %s\n", dbg_str); + + return CheckResult(); +} + + +//--------------------------------------------------------------------------- +// Output - 'GAMECODE' Command +//--------------------------------------------------------------------------- + +static BOOL GAMECODE_Command(char * line, int num) +{ + u32 key; + + if (num != 1) + { + error("Wrong format spec file '%s' line:%d", specFileName, LineNum); + return FALSE; + } + + key = strtoul(line, NULL, 0); + + debug_printf2("keyinfo = %#x\n", key); + + return CheckResult(); +} + + +//--------------------------------------------------------------------------- +// Output - 'Elf9' Command +//--------------------------------------------------------------------------- + +static BOOL Elf9_Command(char * line, int num) +{ + if (num != 1) + { + error("Wrong format spec file '%s' line:%d", specFileName, LineNum); + return FALSE; + } + + debug_printf2("arm9 elf = %s\n", line); + + { + s32 ramAddr = GetRamAddr(line); + gcdHeader.l.main_ram_address = (void*)ramAddr; + } + + return CheckResult(); +} + + +//--------------------------------------------------------------------------- +// Output - 'Elf7' Command +//--------------------------------------------------------------------------- + +static BOOL Elf7_Command(char * line, int num) +{ + if (num != 1) + { + error("Wrong format spec file '%s' line:%d", specFileName, LineNum); + return FALSE; + } + + debug_printf2("arm7 elf = %s\n", line); + + { + s32 ramAddr = GetRamAddr(line); + gcdHeader.l.sub_ram_address = (void*)ramAddr; + } + + return CheckResult(); +} + + +static s32 GetRamAddr(const char *fileName) +{ + Elf32_Ehdr *ehdr; + s32 ramAddr; + int file_size; + struct stat st; + + if (FILESTATUS_FILE != GetFileStatus(&st, fileName)) + { + error("'%s' is not regular file.", fileName); + return FALSE; + } + + if ((file_size = ReadFile(fileName, &ehdr, sizeof(Elf32_Ehdr))) < 0) + return FALSE; + + ramAddr = ehdr->e_entry; + + Free(&ehdr); + + debug_printf2("ramaddr = 0x%08x\n", ramAddr); + + return ramAddr; +} + + +//--------------------------------------------------------------------------- +// Output - 'NANDFIRM' Command +//--------------------------------------------------------------------------- + +static BOOL NandFirm_Command(char * line, int num) +{ + if (num != 1) + { + error("Wrong format spec file '%s' line:%d", specFileName, LineNum); + return FALSE; + } + + debug_printf2("nandfirm = %s\n", line); + + // Set NANDFIRM ROM Offset + if (!Offset) + { + Offset = sizeof(GCDHeader); + SeekFile(Offset); + } + debug_printf2("romoffset = %#x\n", Offset); + { + gcdHeader.l.nandfirm_offset = Offset; + gcdHeader.l.nandfirm_size = ReadFirmFile(line); + } + + return CheckResult(); +} + + +//--------------------------------------------------------------------------- +// Output - 'NORFIRM' Command +//--------------------------------------------------------------------------- + +static BOOL NorFirm_Command(char * line, int num) +{ + if (num != 1) + { + error("Wrong format spec file '%s' line:%d", specFileName, LineNum); + return FALSE; + } + + debug_printf2("norfirm = %s\n", line); + + // Set NORFIRM ROM Offset + if (!Offset) + { + Offset = sizeof(GCDHeader); + SeekFile(Offset); + } + debug_printf2("romoffset = %#x\n", Offset); + { + gcdHeader.l.norfirm_offset = Offset; + gcdHeader.l.norfirm_size = ReadFirmFile(line); + } + + return CheckResult(); +} + + +static size_t ReadFirmFile(const char *fileName) +{ + char *file; + int file_size; + struct stat st; + + if (FILESTATUS_FILE != GetFileStatus(&st, fileName)) + { + error("'%s' is not regular file.", fileName); + return FALSE; + } + + if ((file_size = ReadFileWithPadding(fileName, &file, READ_ALL, FIRM_ALIGN)) < 0) + return FALSE; + + Offset += file_size; + + // Output file image with fitting region + PutBuffer(file, file_size); + + Free(&file); + + return file_size; +} + + +//--------------------------------------------------------------------------- +// Output - 'Sbin9' Command +//--------------------------------------------------------------------------- + +static BOOL Sbin9_Command(char * line, int num) +{ + FIRMSignedContext* sc = &signedContext; + + if (num != 1) + { + error("Wrong format spec file '%s' line:%d", specFileName, LineNum); + return FALSE; + } + + debug_printf2("arm9 sbin = %s\n", line); + + // Set ARM9 ROM Offset + if (!Offset) + + { + Offset = sizeof(GCDHeader); + SeekFile(Offset); + } + debug_printf2("romoffset = %#x\n", Offset); + { + gcdHeader.l.main_rom_offset = Offset; + } + + return ReadSbinFile(line, &gcdHeader.l.main_rom_offset, &sc->hash[FIRM_SIGNED_HASH_IDX_ARM9], compArm9); +} + +//--------------------------------------------------------------------------- +// Output - 'Sbin7' Command +//--------------------------------------------------------------------------- + +static BOOL Sbin7_Command(char * line, int num) +{ + FIRMSignedContext* sc = &signedContext; + + if (num != 1) + { + error("Wrong format spec file '%s' line:%d", specFileName, LineNum); + return FALSE; + } + + debug_printf2("arm7 sbin = %s\n", line); + + // Set ARM7 ROM Offset + if (!Offset) + { + Offset = sizeof(GCDHeader); + SeekFile(Offset); + } + debug_printf2("romoffset = %#x\n", Offset); + { + gcdHeader.l.sub_rom_offset = Offset; + } + + return ReadSbinFile(line, &gcdHeader.l.sub_rom_offset, &sc->hash[FIRM_SIGNED_HASH_IDX_ARM7], compArm7); +} + + +static BOOL ReadSbinFile(const char *fileName, void* minfo, void* hash, BOOL comp) +{ + const GCDHeader_ModuleInfo *m = minfo; + u32 *size = (void*)&m->size; + u32 *orig_size = (void*)&m->decomp_size; + char *buffer; + char *file; + int file_size; + struct stat st; + + if (FILESTATUS_FILE != GetFileStatus(&st, fileName)) + { + error("'%s' is not regular file.", fileName); + return FALSE; + } + + if ((file_size = ReadFile(fileName, &file, READ_ALL)) < 0) + return FALSE; + + *orig_size = file_size; + + // Digest file image + if (hash) + { + ACSign_DigestUnit(hash, file, file_size); + } + + // Compress file image with fitting region + buffer = Alloc(file_size * 2); + if ( comp ) + { + file_size = LZCompWrite(file, file_size, buffer, FIRM_ALIGN); + } + else + { + memcpy(&buffer[0], file, file_size); + { + u32 pad_size = (DEFAULT_ALIGN - (file_size % FIRM_ALIGN)) % FIRM_ALIGN; + if (pad_size) + memset(&buffer[file_size], 0, pad_size); + file_size += pad_size; + } + } + Free(&file); + file = buffer; + + if (size) + { + *size = file_size; + } + Offset += file_size; + + // Encrypt file image + EncryptBuffer(file, file_size); + + // Output file image with fitting region + PutBuffer(file, file_size); + + Free(&file); + + return CheckResult(); +} + +typedef struct +{ + unsigned long e[4]; +} u128; + +static BOOL EncryptBuffer(char *buffer, int length) +{ + const u128 id = {{ AES_IDS_ID2_A, AES_IDS_ID2_B, AES_IDS_ID2_C, AES_IDS_ID2_D }}; + u128 iv = {{ length, -length, ~length, 0 }}; + FIRMSignedContext* sc = &signedContext; + char *buffer2 = Alloc(length); + AES_KEY key; + if (!buffer2) + return FALSE; + AES_SetKey(&key, sc->aes_key, (unsigned char*)&id); + AES_Ctr(&key, buffer2, buffer, length, (unsigned char*)&iv); + memcpy(buffer, buffer2, length); + memset(buffer2, 0, length); + Free(buffer2); + return TRUE; +} + + +//--------------------------------------------------------------------------- +// Output - 'ERROR' Command +//--------------------------------------------------------------------------- +static char* error_type[] = +{ + "SIGN", + "HEADER_HASH", + "ARM9_HASH", + "ARM7_HASH", + "HASH_TABLE_HASH", + "FINAL_HASH", + "HEADER_FOOTER", +}; + +static BOOL ERROR_Command(char * line, int num) +{ + char* dbg_str = "UNKNOWN"; + int i; + + if (num != 1) + { + error("Wrong format spec file '%s' line:%d", specFileName, LineNum); + return FALSE; + } + + for (i=0; ihash[FIRM_SIGNED_HASH_IDX_HEADER][0] ^= 1; + } + if ( errFlags.e.arm9_hash ) + { + sc->hash[FIRM_SIGNED_HASH_IDX_ARM9][0] ^= 1; + } + if ( errFlags.e.arm7_hash ) + { + sc->hash[FIRM_SIGNED_HASH_IDX_ARM7][0] ^= 1; + } + if ( errFlags.e.hash_table_hash ) + { + sc->hash[FIRM_SIGNED_HASH_IDX_HASH_TABLE][0] ^= 1; + } +} + +static void SetFinalHashError(FIRMSignedContext* sc) +{ + if ( errFlags.e.final_hash ) + { + sc->hash[FIRM_SIGNED_HASH_IDX_FINAL][0] ^= 1; + } +} + +static void SetSignError(GCDHeader* nh) +{ + if ( errFlags.e.sign ) + { + nh->sign.raw[0] ^= 1; + } + +} + +static void SetFooterError(GCDHeader* nh) +{ + if ( errFlags.e.header_footer ) + { + nh->h.reserved_footer[sizeof(nh->h.reserved_footer)-1] ^= 1; + } + +} + +//--------------------------------------------------------------------------- +// Output - Initialize AES Key +//--------------------------------------------------------------------------- +static BOOL InitializeAesKey(void) +{ + FIRMSignedContext* sc = &signedContext; + struct stat specstat; + time_t spectime; + if (stat(specFileName, &specstat) != 0) + return FALSE; + time(&spectime); + memcpy(&sc->aes_key[0], &specstat.st_atime, 4); + memcpy(&sc->aes_key[4], &specstat.st_mtime, 4); + memcpy(&sc->aes_key[8], &specstat.st_ctime, 4); + memcpy(&sc->aes_key[12], &spectime, 4); + ACSign_GetKey(sc->aes_key, sizeof(sc->aes_key), sc->aes_key, sizeof(sc->aes_key)); + return TRUE; +} + + +//--------------------------------------------------------------------------- +// Output - Initialize Gcdfirm File +//--------------------------------------------------------------------------- + +static BOOL InitializeGcdfirmFile(void) +{ + ReadRomHeaderFile( GetSrcPath(GetAppBaseName(), DEFAULT_ROMHEADER_TEMPLATE) ); + + memset(&signedContext.hash[FIRM_SIGNED_HASH_IDX_HASH_TABLE], 0xff, sizeof(signedContext.hash[0])); + gcdHeader.h.w = wram_regs_init; + InitializeAesKey(); + return TRUE; +} + +//--------------------------------------------------------------------------- +// Output - Finalize Gcdfirm File +//--------------------------------------------------------------------------- + +static BOOL FinalizeGcdfirmFile(const char *gcdFile) +{ + GCDHeader* nh = &gcdHeader; + FIRMSignedContext* sc = &signedContext; + u8* key = keyFileBuf; + int size; + + // set rom size + size = ROM_SIZE_MIN; + nh->l.rom_size = 0; + while (size < Offset) + { + size <<= 1; + nh->l.rom_size++; + } + + ACSign_DigestHeader(&sc->hash[FIRM_SIGNED_HASH_IDX_HEADER], nh); + + SetUnitHashErrors(sc); + + ACSign_DigestUnit(&sc->hash[FIRM_SIGNED_HASH_IDX_FINAL], sc, FIRM_HEADER_2ND_HASH_AREA_LEN); + + SetFinalHashError(sc); + + if (key) + { + ACSign_Final(nh, sc, key); + } + + SetFooterError(nh); + + SetSignError(nh); + + // Output file image + SeekFile(0L); + PutBuffer(&gcdHeader, sizeof(gcdHeader)); + + // Output public key (modulus) + if (PubkeyFileName) + { + WriteFile(PubkeyFileName, &keyFileBuf[ACS_RSA_PRVMOD_OFFSET], ACS_RSA_PRVMOD_LEN); + } + + return TRUE; +} diff --git a/build/tools/makegcdfirm/path.c b/build/tools/makegcdfirm/path.c new file mode 100644 index 00000000..eeeb9895 --- /dev/null +++ b/build/tools/makegcdfirm/path.c @@ -0,0 +1,931 @@ +/*---------------------------------------------------------------------------* + Project: NitroSDK - tools - makebanner + File: path.c + + Copyright 2003-2006 Nintendo. All rights reserved. + + These coded instructions, statements, and computer programs contain + proprietary information of Nintendo of America Inc. and/or Nintendo + Company Ltd., and are protected by Federal copyright law. They may + not be disclosed to third parties or copied or duplicated in any form, + in whole or in part, without the prior written consent of Nintendo. + + $Log: path.c,v $ + Revision 1.3 2006/01/18 02:11:20 kitase_hirotake + do-indent + + Revision 1.2 2005/02/28 05:26:13 yosizaki + do-indent. + + Revision 1.1 2004/08/30 08:41:14 yasu + makebanner moves into CVS tree + + $NoKeywords: $ + *---------------------------------------------------------------------------*/ +#include +#include // free() +#include // strcasecmp() +#include // stat() +#include // opendir()/readdir()/closedir() +#include // getcwd() +#ifdef __CYGWIN__ +#include // cygwin_conv_to_win32_path() +#endif +#include "path.h" + +//--------------------------------------------------------------------------- +// Get File Statue +//--------------------------------------------------------------------------- + +tFileStatus GetFileStatus(struct stat *s, const char *filename) +{ + // Get file status + if (stat(filename, s)) + { + error("Can't get status %s", filename); + return FILESTATUS_ERROR; + } + + if (S_ISREG(s->st_mode)) + { + return FILESTATUS_FILE; + } + else if (S_ISDIR(s->st_mode)) + { + return FILESTATUS_DIR; + } + + error("Unknown file type %s", filename); + return FILESTATUS_ERROR; +} + + +//--------------------------------------------------------------------------- +// File Globbing & Dir Listing +//--------------------------------------------------------------------------- + +typedef struct +{ + tCallBack callBack; + void *param; + tWildCard *accept; + tWildCard *reject; + +} +tForeachEntryParam; + + +static BOOL isAcceptEntryName(char *pathName, tWildCard * accept, tWildCard * reject) +{ + char *p = pathName; + + while (*p) + { + if (*p == '/') + pathName = p + 1; + p++; + } + + if (accept) + { + while (accept) + { + if (WildCardCmp(accept->name, pathName)) + { + goto accepted; + } + accept = accept->next; + } + return FALSE; + } + accepted: + + while (reject) + { + if (WildCardCmp(reject->name, pathName)) + { + return FALSE; + } + reject = reject->next; + } + return TRUE; +} + + +static BOOL ForeachEntry_CallBack(char *pathName, void *param) +{ + tForeachEntryParam *t = (tForeachEntryParam *) param; + struct stat fstat; + + if (!isAcceptEntryName(pathName, t->accept, t->reject)) + { + // Reject!!! ignored + return TRUE; + } + + switch (GetFileStatus(&fstat, pathName)) + { + case FILESTATUS_FILE: + return t->callBack(pathName, t->param); + + case FILESTATUS_DIR: + return ForeachDirList(pathName, ForeachEntry_CallBack, param); + + default: + break; + } + return FALSE; +} + + +BOOL ForeachEntry(const char *pathName, tWildCard * reject, tCallBack callBack, void *param) +{ + tForeachEntryParam t; + + t.callBack = callBack; + t.param = param; + t.accept = NULL; + t.reject = reject; + + return ForeachPathGlobbing(pathName, ForeachEntry_CallBack, &t); +} + + +typedef struct +{ + tCallBack callBack; + void *param; + char *baseName; + +} +tForeachFileParam; + + +static BOOL ForeachFile_CallBack(char *pathName, void *param) +{ + tForeachFileParam *t = (tForeachFileParam *) param; + + int len = strlen(t->baseName); + + debug_printf(" ForeachFile_CallBack path[%s] base[%s]\n", pathName, t->baseName); + + if (strncmp(pathName, t->baseName, len)) + { + error("Wildcard in Root is not supported"); + return FALSE; + } + + return t->callBack(pathName + len, t->param); +} + + +BOOL ForeachFile(const char *baseName, const char *fileName, tWildCard * reject, tCallBack callBack, + void *param) +{ + char *cBaseName; + char *cPathName; + BOOL state; + tForeachFileParam t; + + debug_printf("ForeachFile : baseName[%s] fileName[%s]\n", baseName, fileName); + + cBaseName = GetSrcPath(baseName, ""); + cPathName = GetSrcPath(baseName, fileName); + + debug_printf("ForeachFile : cBaseName[%s] cPathName[%s]\n", cBaseName, cPathName); + + t.callBack = callBack; + t.param = param; + t.baseName = cBaseName; + + state = ForeachEntry(cPathName, reject, ForeachFile_CallBack, &t); + + free(cBaseName); + free(cPathName); + + return state; +} + + +//--------------------------------------------------------------------------- +// FilePath Globbing +//--------------------------------------------------------------------------- + +typedef struct +{ + char *baseName; + char *pathName; + tCallBack callBack; + void *param; + +} +tGlobParam; + + +static int CountFile; +static BOOL ForeachPathGlobbing_Entry(tGlobParam * pg); +static BOOL ForeachPathGlobbing_WildCard(char *pathName, void *param); + +BOOL ForeachPathGlobbing(const char *pathName, tCallBack callBack, void *param) +{ + tGlobParam g; + BOOL ret; + + g.baseName = NULL; + g.pathName = PathNormalize(pathName, TRUE); + g.callBack = callBack; + g.param = param; + CountFile = 0; + + debug_printf("PathGlobbing : Name [%s]->[%s]\n", pathName, g.pathName); + + ret = ForeachPathGlobbing_Entry(&g); + + free(g.pathName); + + if (ret && CountFile == 0) + { + error("No file or directory matched %s", pathName); + return FALSE; + } + return ret; +} + + +static BOOL ForeachPathGlobbing_Entry(tGlobParam * pg) +{ + tGlobParam g; + char *entryName; + struct stat s; + BOOL state; + + if (pg->pathName) + { + entryName = PathDup(pg->pathName); + + g = *pg; + g.pathName = PathGetDirLevelDown(pg->pathName); + + if (pg->baseName) + { + g.baseName = Alloc(strlen(pg->baseName) + strlen(entryName) + 2); + sprintf(g.baseName, "%s/%s", pg->baseName, entryName); + } + else + { + g.baseName = strdup(entryName); + } + + // Check if wildcard ? + if (isPathWildCard(entryName)) + { + state = ForeachDirList(pg->baseName, ForeachPathGlobbing_WildCard, &g); + } + else + { + state = ForeachPathGlobbing_Entry(&g); + } + + Free(&entryName); + Free(&g.baseName); + } + else + { + // Check if file exists + if (!stat(pg->baseName, &s)) + { + debug_printf(" File Found [%s]\n", pg->baseName); + + // Globbing done, exec callback + + state = pg->callBack(pg->baseName, pg->param); + CountFile++; + } + else + { + debug_printf(" File Not Found [%s]\n", pg->baseName); + state = TRUE; // Ignored + } + } + return state; +} + + +static BOOL ForeachPathGlobbing_WildCard(char *pathName, void *param) +{ + tGlobParam g; + tGlobParam *pg = (tGlobParam *) param; + + debug_printf(" WildCardCmp: [%s] [%s]\n", pg->baseName, pathName); + + if (WildCardCmp(pg->baseName, pathName)) + { + g = *pg; + g.baseName = pathName; + return ForeachPathGlobbing_Entry(&g); + } + + return TRUE; // Ignored +} + + +//--------------------------------------------------------------------------- +// Directory Listing +// Listing directory & Exec CallBack +//--------------------------------------------------------------------------- + +BOOL ForeachDirList(const char *dirName, tCallBack callBack, void *param) +{ + DIR *dir; + struct dirent *entry; + char *pathName; + BOOL state = TRUE; + + if (!dirName) + { + dirName = "."; + } + + debug_printf("DirectoryList: Name [%s]\n", dirName); + + // Open directory + if (NULL == (dir = opendir(dirName))) + { + error("Can't read directory %s", dirName); + return FALSE; + } + + // Store new files + while (NULL != (entry = readdir(dir))) + { + pathName = entry->d_name; + + if (!strcmp(pathName, ".") || !strcmp(pathName, "..")) + { + continue; + } + + debug_printf(" :%s\n", pathName); + pathName = GetSrcPath(dirName, pathName); + state = callBack(pathName, param); + free(pathName); + + if (!state) + break; + } + + closedir(dir); + return state; +} + + +//--------------------------------------------------------------------------- +// +// PathName Utilities +// +//--------------------------------------------------------------------------- + +//--------------------------------------------------------------------------- +// StrCmp/StrCpy entry name terminated / or \0 +//--------------------------------------------------------------------------- + +int PathCmp(const char *path, const char *cmp) +{ + char c; + + do + { + c = *path; + if (c == '/') + c = '\0'; // end of string if '/' + if (c != *cmp) + return 1; + path++; + cmp++; + } + while (c); + + return 0; +} + + +char *PathCpy(char *dest, const char *src) +{ + while (*src != '\0' && *src != '/') + { + *dest++ = *src++; + } + return dest; // Don't set '\0' +} + + +int PathLen(const char *path) +{ + int n = 0; + + while (*path != '\0' && *path != '/') + { + n++; + path++; + } + return n; +} + + +char *PathDup(const char *src) +{ + int n = PathLen(src); + char *dest = Alloc(n + 2); + + PathCpy(dest, src); + dest[n] = '\0'; + + return dest; +} + + +BOOL WildCardCmp(const char *wildcard, const char *path) +{ + if (*wildcard == '*') + { + if (*path != '\0' && WildCardCmp(wildcard, path + 1)) + return TRUE; + if (WildCardCmp(wildcard + 1, path)) + return TRUE; + } + + else if (*wildcard == '?') + { + return *path != '\0' && WildCardCmp(wildcard + 1, path + 1); + } + + else if (*wildcard == *path) + { + return *path == '\0' || WildCardCmp(wildcard + 1, path + 1); + } + + return FALSE; +} + + +BOOL isPathWildCard(const char *path) +{ + while (*path != '\0' && *path != '/') + { + if (*path == '*' || *path == '?') + { + return TRUE; + } + path++; + } + return FALSE; +} + + +//--------------------------------------------------------------------------- +// Go up/down directory level +//--------------------------------------------------------------------------- + +char *PathGetDirLevelDown(const char *path) +{ + while (*path) + { + if (*path == '/') + return (char *)path + 1; + path++; + } + return NULL; +} + + +//--------------------------------------------------------------------------- +// Get Basename +//--------------------------------------------------------------------------- + +char *GetBaseName(const char *path) +{ + int i; + char *new_path; + + for (i = strlen(path) - 1; i >= 0; i--) + { + if (path[i] == '/') + { + new_path = strdup(path); + new_path[i] = '\0'; + return new_path; + } + if (path[i] == ':') + { + new_path = Alloc(i + 3); + strncpy(new_path, path, i); + strcpy(new_path + i, ":."); + return new_path; + } + } + + new_path = strdup("."); + return new_path; +} + + +//--------------------------------------------------------------------------- +// Get Filename +//--------------------------------------------------------------------------- + +char *GetFileName(const char *path) +{ + int i; + char *new_file; + + for (i = strlen(path) - 1; i >= 0; i--) + { + if (path[i] == '/' || path[i] == ':') + { + new_file = strdup(path + i + 1); + return new_file; + } + } + new_file = strdup(path); + return new_file; +} + + +//--------------------------------------------------------------------------- +// Reconstruct path name +// +// - Resolve '.' or '..' in path name +// - Work around . and / to translate regular form +// +// Regular form of path: +// Absolute Path [Drive:]/.[/Entry...] +// Relative Path [Drive:].[/Entry]... +// +// ex) +// abc/def -> ./abc/def +// /aaa -> /./aaa +// D:/aaa -> D:/./aaa +// / -> /. +// . -> . +// ../aa -> ./../aa +//--------------------------------------------------------------------------- + +char *PathNormalize(const char *pathName, BOOL isTreatDotDot) +{ + int i, level, level_root, n; + BOOL isAbsolute; + const char *entry[DIRLEVEL_MAX]; + + char *pathNormal = Alloc(strlen(pathName) + 4); + const char *p_org; + char *p_new; + + // + // Check if drive letter C: D: E: + // Check if absolute path + // + p_new = pathNormal; + p_org = SkipDriveName(pathName); + n = (int)p_org - (int)pathName; + + if (n > 0) + { + strncpy(p_new, pathName, n); + p_new += n; + } + isAbsolute = isAbsolutePath(p_org); + + // + // Resolve '.' and '..' + // + // Slice the path at point of / , put them into entry[] + // + + level = level_root = 0; + + for (; p_org; p_org = PathGetDirLevelDown(p_org)) + { + if (!PathCmp(p_org, "") || !PathCmp(p_org, ".")) + { + // skip it + continue; + } + else if (!PathCmp(p_org, "..") && isTreatDotDot) + { + if (level > level_root) + { + // Back to parent dir + level--; + continue; + } + + // if pathname starts with '/', no directory to go up + if (isAbsolute) + { + error("Can't go up directory, '..' Ignored. %s", pathName); + continue; + } + + // keep '..' + level_root = level + 1; + } + + // name entry + entry[level] = p_org; + level++; + } + + // Reconstruct pathname + if (isAbsolute) + { + *p_new++ = '/'; + } + *p_new++ = '.'; + + for (i = 0; i < level; i++) + { + *p_new++ = '/'; + p_new = PathCpy(p_new, entry[i]); + } + *p_new = '\0'; + +#if 0 + if (strcmp(pathNormal, pathName)) + { + debug_printf(" PathNormal: [%s] -> [%s]\n", pathName, pathNormal); + } +#endif + return pathNormal; +} + + +//--------------------------------------------------------------------------- +// Get Src Path +// Normalize BASENAME +// Normalize FILENAME +// Concat both +//--------------------------------------------------------------------------- + +char *GetSrcPath(const char *baseName, const char *fileName) +{ + char *base; + char *file; + char *t; + char *path; + + base = PathNormalize(baseName, TRUE); + file = PathNormalize(fileName, TRUE); + t = Alloc(strlen(base) + strlen(file) + 2); + + // Concat base + '/' + file + sprintf(t, "%s/%s", base, file); + path = PathNormalize(t, FALSE); + + free(base); + free(file); + free(t); + + debug_printf(" GetSrcPath: [%s]\n", path); + return path; +} + + +//--------------------------------------------------------------------------- +// Get Dest Path +// Concat BASENAME + FILENAME +// Normalize it +//--------------------------------------------------------------------------- + +char *GetDestPath(const char *baseName, const char *fileName) +{ + char *t; + char *path; + + t = Alloc(strlen(baseName) + strlen(fileName) + 2); + + // Concat base + '/' + file + sprintf(t, "%s/%s", baseName, fileName); + path = PathNormalize(t, TRUE); + + free(t); + + debug_printf(" GetDestPath: [%s]\n", path); + return path; +} + + +//--------------------------------------------------------------------------- +// Remake the path into familier shape +// Delete ./ +//--------------------------------------------------------------------------- + +char *PathDenormalize(char *path) +{ + char *p; + + p = (char *)SkipDriveName(path); + if (*p == '/') + { + p++; + } + + // Cut './' + if (*p == '.' && *(p + 1) == '/') + { + while ('\0' != (*p = *(p + 2))) + { + p++; + } + + if (p == path) + { + + } + } + + + + return path; +} + + +//--------------------------------------------------------------------------- +// Get PC Path +//--------------------------------------------------------------------------- + +char *GetWin32Path(char *cygpath) +{ + static char buffer[FILENAME_MAX]; + +#ifdef __CYGWIN__ + if (*cygpath == '/') + { + cygwin_conv_to_win32_path(cygpath, buffer); + } + else +#endif + { + strcpy(buffer, cygpath); + } + + return ChangeBackSlash(buffer); +} + +char *ChangeWin32Path(char *cygpath) +{ + char *win32path = strdup(GetWin32Path(cygpath)); + + free(cygpath); + return win32path; +} + +//--------------------------------------------------------------------------- +// Change suffix +//--------------------------------------------------------------------------- +char *ChangeSuffix(const char *file, const char *suffix) +{ + int i, n; + char *path; + + n = strlen(file); + + for (i = n; file[i] != '.'; i--) + { + if (file[i] == '/' || i == 0) + { + i = n; + break; + } + } + + path = Alloc(i + strlen(suffix) + 1); + strncpy(path, file, i); + strcpy(path + i, suffix); + + return path; +} + + +//--------------------------------------------------------------------------- +// Get Current Dir +//--------------------------------------------------------------------------- + +char *GetCurrentDirectory(void) +{ + static char buffer[FILENAME_MAX]; + char *cwd; + + cwd = getcwd(buffer, FILENAME_MAX); + if (!cwd) + { + error("Can't access current directory"); + exit(10); + } + return cwd; +} + + +//--------------------------------------------------------------------------- +// Check if absolute path +// +// Return True in case of ... +// +// /dirA/dirB/fileC +// D:/dirA/dirB/fileC +// +// Return False in case of ... +// +// dirX/dirY/fileZ +// D:dirX/dirY/fileZ +//--------------------------------------------------------------------------- + +BOOL isAbsolutePath(const char *path) +{ + const char *p = path; + + while (*p != '\0') + { + if (*p == '/' || *p == '\\') + { + if (p == path || p[-1] == ':') + { + return TRUE; + } + } + p++; + } + return FALSE; +} + +//--------------------------------------------------------------------------- +// Check if drive name +// +// Return next character of ':' if drive name +// Return head of path if no drive name +//--------------------------------------------------------------------------- + +const char *SkipDriveName(const char *path) +{ + const char *p = path; + + while (*p != '\0' && *p != '/' && *p != '\\') + { + if (*p == ':') + { + return p + 1; + } + p++; + } + return path; +} + + +//--------------------------------------------------------------------------- +// App Name Utilities +//--------------------------------------------------------------------------- +static char *appName; +static char *appBaseName; +static char *appFileName; + +void InitAppName(const char *path) +{ + char *slash_path = ChangeBackSlash(strdup(path)); + + appBaseName = GetBaseName(slash_path); + appFileName = GetFileName(slash_path); + appName = ChangeSuffix(appFileName, ""); + + free(slash_path); +} + +char *GetAppName(void) +{ + return appName; +} + +char *GetAppBaseName(void) +{ + return appBaseName; +} + +char *GetAppFileName(void) +{ + return appFileName; +} + + +#ifdef TEST +int main(int argc, char *argv[]) +{ + int i; + char *s; + + for (i = 1; i < argc; i++) + { + s = PathNormalize(argv[i], TRUE); + printf("[%s] -> [%s]\n", argv[i], s); + free(s); + } + return 0; +} +#endif diff --git a/build/tools/makegcdfirm/path.h b/build/tools/makegcdfirm/path.h new file mode 100644 index 00000000..e9956dc6 --- /dev/null +++ b/build/tools/makegcdfirm/path.h @@ -0,0 +1,93 @@ +/*---------------------------------------------------------------------------* + Project: NitroSDK - tools - makebanner + File: path.h + + Copyright 2003-2006 Nintendo. All rights reserved. + + These coded instructions, statements, and computer programs contain + proprietary information of Nintendo of America Inc. and/or Nintendo + Company Ltd., and are protected by Federal copyright law. They may + not be disclosed to third parties or copied or duplicated in any form, + in whole or in part, without the prior written consent of Nintendo. + + $Log: path.h,v $ + Revision 1.3 2006/01/18 02:11:20 kitase_hirotake + do-indent + + Revision 1.2 2005/02/28 05:26:13 yosizaki + do-indent. + + Revision 1.1 2004/08/30 08:41:14 yasu + makebanner moves into CVS tree + + $NoKeywords: $ + *---------------------------------------------------------------------------*/ +#ifndef PATH_H_ +#define PATH_H_ + +#include // struct tat +#include "misc.h" + +#define DIRLEVEL_MAX 256 +#ifndef FILENAME_MAX +#define FILENAME_MAX 1024 +#endif + +typedef enum +{ + FILESTATUS_ERROR = -1, + FILESTATUS_FILE = 0, + FILESTATUS_DIR = 1 +} +tFileStatus; + + +// Item Reject Control + +typedef struct tWildCard +{ + struct tWildCard *next; + char *name; + +} +tWildCard; + + +// CallBacks + +typedef BOOL (*tCallBack) (char *, void *); + + +// Prototypes + +tFileStatus GetFileStatus(struct stat *s, const char *filename); +BOOL ForeachEntry(const char *pathName, tWildCard * reject, tCallBack callBack, void *param); +BOOL ForeachFile(const char *baseName, const char *fileName, tWildCard * reject, + tCallBack callBack, void *param); +BOOL ForeachPathGlobbing(const char *pathName, tCallBack callBack, void *param); +BOOL ForeachDirList(const char *dirName, tCallBack callBack, void *param); +int PathCmp(const char *path, const char *cmp); +char *PathCpy(char *dest, const char *src); +int PathLen(const char *path); +char *PathDup(const char *src); +char *PathGetDirLevelDown(const char *path); +char *GetBaseName(const char *path); +char *GetFileName(const char *path); +BOOL WildCardCmp(const char *wildcard, const char *path); +BOOL isPathWildCard(const char *path); +char *PathNormalize(const char *pathName, BOOL isTreatDotDot); +char *PathDenormalize(char *path); +char *GetSrcPath(const char *base, const char *file); +char *GetDestPath(const char *base, const char *file); +char *GetWin32Path(char *cygpath); +char *ChangeWin32Path(char *cygpath); +char *ChangeSuffix(const char *file, const char *suffix); +char *GetCurrentDirectory(void); +BOOL isAbsolutePath(const char *path); +const char *SkipDriveName(const char *path); +void InitAppName(const char *path); +char *GetAppName(void); +char *GetAppBaseName(void); +char *GetAppFileName(void); + +#endif //PATH_H_ diff --git a/build/tools/makegcdfirm/test/Makefile b/build/tools/makegcdfirm/test/Makefile new file mode 100644 index 00000000..154c5cb1 --- /dev/null +++ b/build/tools/makegcdfirm/test/Makefile @@ -0,0 +1,55 @@ +#! make -f +#--------------------------------------------------------------------------- +# Project: TwlFirm - tools - makenorfirm +# File: Makefile +# +# Copyright 2007 Nintendo. All rights reserved. +# +# These coded instructions, statements, and computer programs contain +# proprietary information of Nintendo of America Inc. and/or Nintendo +# Company Ltd., and are protected by Federal copyright law. They may +# not be disclosed to third parties or copied or duplicated in any form, +# in whole or in part, without the prior written consent of Nintendo. +# +# $Log: $ +# $NoKeywords: $ +#--------------------------------------------------------------------------- + +SUBDIRS = wram_rbin \ + +include $(TWLFIRM_ROOT)/build/buildtools/commondefs + +MAKEGCDFIRM = ../makegcdfirm.exe + +MAKEFIRM_ARM9 = ./twl_gcdfirm9_print.axf +MAKEFIRM_ARM7 = ./twl_gcdfirm7_print.axf +SDEPENDS_BIN += $(MAKEFIRM_ARM9) $(MAKEFIRM_ARM7) +MAKEFIRM_FLAGS += -d +MAKEFIRM_DEFS += -DFIRM_ROOT='$(FIRM_ROOT)' \ + -DMAKEFIRM_ARM9='$(basename $(MAKEFIRM_ARM9))' \ + -DMAKEFIRM_ARM7='$(basename $(MAKEFIRM_ARM7))' \ + -DMAKEFIRM_RSA_PRVKEY='$(MAKEFIRM_RSA_PRVKEY)' \ + +TARGET = test.srl + +%.srl: %.gcdsf $(MAKEGCDFIRM) + $(MAKEGCDFIRM) $(MAKEFIRM_FLAGS) $(MAKEFIRM_DEFS) $< $@ + +.PHONY: build install do-autotest clean clobber + +define ECHO_CURDIR + echo "==== $(CURDIR)"; +endef + +build: + @$(ECHO_CURDIR) + @$(MAKE) $(TARGET) + +install do-autotest: + @$(ECHO_CURDIR) + +clean clobber super-clobber: + @$(ECHO_CURDIR) + -rm -f $(TARGET) *~ + +test-utf16.bsf: icon.nbfc icon.nbfp diff --git a/build/tools/makegcdfirm/test/nandfirm_print.nand b/build/tools/makegcdfirm/test/nandfirm_print.nand new file mode 100644 index 0000000000000000000000000000000000000000..999ce185fdcfb579f464763668963b8df624cbfe GIT binary patch literal 50688 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 literal 0 HcmV?d00001 diff --git a/build/tools/makegcdfirm/test/norfirm_print.nor b/build/tools/makegcdfirm/test/norfirm_print.nor new file mode 100644 index 0000000000000000000000000000000000000000..2a6baecd577a7a206c2142f3d4d150b051f973ca GIT binary patch literal 30896 zcmeFXL$EMR&^2~#-{;!4ZQHhO+qP}nwr$(C?f?CfEVB5Q*(8-yb!w)&cRe*#U40G! z!2cfX=)jQRe}8}dfB$Fwzw7^x{||QHfB*Xbw=4(%Kyk$X#QwklivJy|007}^{}cKF z*AxCv{{PjOOW_|DTte;l>Qkgyn%JRRT!}87V`EC^1gBubl@fpejSwfe zJ8wc+EiA?G%3@k~*I3(9Mgv!lwA-d^!?QPhi)(#Uce{suEeih2r?mNZd+j1g|4^C^ z`B=oc+C7HOs#hp(#fUo6=!lM8bhCd{D)+{$SSuxICKjzD@M5qVQvN6>ZZI0~C4j61 z@GP-*8(p7|Q*C1Qa>##ov_sxp^)CXeE5(LC-(?}P)awd_z5*ox-hQOgGl5QbRzT_^ z<6RO%hxOoAU@ao3w}NZiy{+3mo5P334EEY@txQ?0J5i8Qr)6&W(X{KCB7jA;&fTg{ z&CL6#luClt;kUQdyDBh#wDjEsqvG@BT@^mq`NxHT0UPf3KFE&oBXIuc5tTP#dQMt0 zceb!TD;dFX)qxQuvHW^*u*AukZoHGysEfff2(WAyRlw|E!C~-T%f0+tjEpw;93!F? z!6U`_`DfMbNw{ZAssMt6lg1E}f>pmh9!t_7Lg~lw>`q`T1gU$DDTgy;Q)|rxu^2|R z#N$IL9yzV3t-j%9`#gspNl4R}xFOTCNb&N)y0r9%V?xj{5eOnzc0An$q{+41Uua*m z>U{z0-{Q~4J#dAuhDMTj*``ljA58UM^R9U_V{!Hbjh2oyl7F44QQ-iT;CByRWkgAg}+OHy}K^!D4bFYbM!9v zv3?hWGzV&2s0qzT)T6%-rLm4BEFCN~_(6PO9laz#TOzo`VCUSP^uR%4BW=7lkg9Lx z^J`Tko%Ek`Q!35Ku4CAO{Z{e9QV97oHv{T|k$yg${XF$2wZYzlP6_>Y)Mm_$QFl z4(q1P!e>2K#Zr6-*jD^FVyt`MpGXnr^V$kW~&OQw+)w&8J-(DHQ5 zw&hf8K|~;9r*;Nn_!ynwD-=})TZ?&nRL2K1UJNC4dWR zt7sa=e;Qx!3+1o^|Jc}X(ezYx*>1V^wE-;Qedytzbn+Z<{ivu_%V!@@TBc=`mS)Ny z%{=Ij$K%w-`Bx%9i%HZP0HuHsb%4y9zn2X-OYIc}<4PU{`soaKsHH;E!qF{?GvG>G z2F8P}48UJlBoeLXi{MN8+QAVefra9*Y^w-W^5H4k?ZsihpW&AUfsBY?x%$zz6g9Fr zptYN6V=r`|g2N#E+P_FbbjJ!E@3WoC49G`1*Z|H{_fB#l?LGe4I?#zkoax1Cemj$p z41+SaDxXV5wOVhpr2icJ2}LbBnOc6F9Cec(xqAT_*4BhZZ!<9co-+Ldk;qRf+Rz5r zAB!dIq32*YU`VVz3e}TH+(AF3~Xo;s5suS2TjX9!|f=32Oqumnu9SJ7O3P zuDJQiU#>_Whg}~kgpJXNz6dB!1z-YeBh6)e%ytx6PX~NnY}D7eHFJ+U0ZXXpyvtFi zM3Si4ovFS&O?@hue*DCz{j1fiB(dhM)of$4Ji$h0jRMrA{yan8tK9Z)(8D_Tvc%lkWKAUydiLZS$b_JDx$1B})ezd~b!50jHG6Sa-RZMTc1kPK}+Jkww-JcG=4LMg*V)ZK8Lp9_+ zL}$!kx1Ob>>ncm=M5n#u@F9)K?3gK}S6>*4cnWWD(YQnuYRIX;AXSe6dLwri7t!>U zD*5qC*YAMzHcmH>PamACB7sM7mwa)vMlJkxyvP?R5hNwCEiPGz&kmU&)XMH@4204 zbDzbM+Kvw^2dgc~jMU=eTM2Txy99H7{-1v=aJa~{^2M9gF{D+p+l?US8?e_X*)p<( zl3~*RC%ngD3!FYE0(?rF^e<81L9@@PhF`i;Y$1Hs_aF`iB83a$WzxA*9T{<(T3v`$ z(?zHSZLU3P6<4*(g{1@eL_1pJ^9K_h6v31AN#&MpB3n@WM;b>fTDpHFL7 zBX;;atK?NJjeCwtZe_ky0KLkc@fXPwAJ;+9u33h(rWh97s}`6m`dTp{OH`0BzNitq zEC;dQ{0U+AYyWZA(}W>F!>-$Yoc?ce9_hf@=v>g{`5MhAk26uPVfkCcDilvJ|v=Q^2fJU zk?ZMLbx8W|=0@1Tx|kWiXEmw$-#9==_}nN*Ih+(t$ZaqD=0=fU*RtMSG@u#gs(80J zbdIHR{GHv2C%20~RGy*M)e+rQqyFy1Zf#zA&0$PcxYgig@dl~7=Tu2lr@P&8H|;P( zbnJe^3{x7(u3NMQQVPvj5D)>cG(R|*=*>~@ts^G|jf7x=jJ}h68Od$SoSNkWtX2Pu zz9G$AP+Qo?cGsOXQ`9b#2ID(ucBOwwrMzzQsJ5jGCw@iMpVeG6^wgaEQl&%H=6WY$ zS*A*H5_J)WT@XK}Z9(oogk5?o-}F=@yWF+SVX83>;lpLYVTR*K#<-VeQ9XGJ z&!V@Pa_QVh8!pUDFPvu|3W?uT7#+q2Qt7uNLxSDKrO1{*a*t! z3K=ClRF2#%w*)4#8FZnv07~uBp)b6?4VI!{1e7Nan3UMBa4P_qBMUW18M}eNRB)sj z^1edYVNK}MbiMGKjOhTO@qVr(HWZz{tJ?)iVDs26Z9PWz$%5dtIq~Y)<>eWN zl#MUZoa$&+wH_eO{);lQ8eQ)@*dIe~-@y+@Lp}5ubbx!t-P;t@Q?ToPk8FM#nbEL4 z!3TGHDg0VM+0QHC3HyRVtMc$@P%-G#b*$IXn}d>ODzpo(yS)C9Lm zHrs}9TDFpG`w*p_cy<z<(2Y zDn^N*?0+&pK^|q?*oqJnO__4c+29Aa?*clj@UIkyU)=wIe&jh^ zs1(p-kq(0sGscS%oP0|&AYhChsKyC{e;CMA6xPS5VOFYIC?XN9NVZhRwkjqTK!*6_PrGu4c zbYik{^X9-gzp3tI_tjZ8o&xTYG)k$Q;qM1nXC}p>$i9v){5e10{+ty#555*HK+VfM zt(ai;4}aP!KEy3lnZRa?Id!6mNpt@S!oHn4Zl;vrfyN4|GLIq+F;*{w>jY_-*QVS5 zET{N!fSHGui@ojAc71GHxLa~SN^*)_V@Wl_O?w!D2a{Tx)VsqhVaEWYV;SjE=hV%oFM{7u+P}GSeKfv!Hiz7r! z+hgFTw(p1Kw@naWe)6CO<ue5T=2*xyhWvHRpzSM#*87SWq!JSU+=K5o8GA%pk(_3; zQgBpL`T9Hh_EyV;+(CVx%GNuS(UHftwf@KJ1n4mOtKa9Xr3qtP!w52>-@!2dwwk03 z)b`(iuPI8S*DOnhp3p-tHhp$9X3cYH@K%u zznXtp&g}v)mG|%JId;3TaGOslWe7+00cC|gZP$feSwLM67N#T)Y0J_~dHkqhsCKj) z?=NJ@u}aV7_p)g32bkTS(H3Aqa~Twg=&dNjSR3My zu!V#D`1-24J|C_e$Wysd#8M;dxJ!&{EVeNo& z7Cj&ELK_)(vdr9Pz~QyD*xYHe{cL%yg?Z6(K^9MuO^#I}^w&t4&f}B6_ z2|t3Dz;o00*I*b4*0y^!#TM45R{foD{ml#TxvNrT>0iF)wdldDiL+iq|f$QXn((WzH@y98v|37Z0(d~BDHxvVUlGlx-| z`41F#4dNoz%LaQsMNHW$fGxIgdry_ozLBIJ({eENnNJ~0l9{TuLcfT4aDgXb3f zO2BY-bC*ZcD0*4oOr%h}CGZvl9~E1EoZj9BthRWq^)+l-%rA3?FAY6D^hRL{rjkH@ zEaIuFhrGga)rhUM;qhkMT$-B+C8I!_B1{O}dvQQ`V>65jMJZQp;i3i35$D?G0P$ke z8x9o+`$u@T9X+4XOrXuX9g&|b7b=QkqHFUpVuCFS|11dF|2;uXw~+Sa;cx4aIH1T4 z(xmj|wrZ7AZa9e&kphXl$^+}O`(Le|LT$lkR=V&ZhpfSV{`3G0#b(`7b&HuZC1$@F z@d^n^cY9Mqc^_r=oN$xQ@`wV<$Cm658Z(AseEa@gZrY#RmN)|xxebUFMeb+Hm$K(xl&X#2^LVA$ zc-YR=TUroeN6$r6O`U8TUU^(J0TGBLjJw#--MA!3(ESV!qQut4pU zqmP2zESY4UH5@#v&h<)8{~MSR)7Z_;!1nE z{L`Ot=x8)MqFnDISL76`?kSE69pablo$T-c_bgVe_pCIC3`23wUIQVI9+8c!0gUQcEk30(OI0|XAE1Yx=ZYlY&6AvB%eZ_JAPT99= z^?oI3VK9|E;H@;+y~AiaFy%7tMYW4tYK*eG(fgR^ulgJTb616gm)5fh^VCEAGChqQ zI7maQ(@Gck&dDR!H)@4`dt$nz4CfheAB>`=a3%UAE&rhqiQ?tlDQZlADY;P*`X^!g zQkl+B+pi!10Ko-#gp}QmJa<*%?^~3DVK2^}jwnxByyG`P?Z-6J6KW(J9N@#e<-Dhy z>3x{$=ehK;Tt^R1j{)Bi1YSp9_AX~t#Jz!bTMW5;d89wM^VWuboKgcKgMhKg!wOY3Ml>mq7fVBm6Zkq`7AOnoJ)I z3J`0^zx?Z5g)nw_V0Qm^M)4F|q42{()Jkm)^i{*%g7x9&$o*`^&xaDI&^As$^lNwF z4;zScayhoLIa#piEnArkS|}iZmg=tfCNW!AW|S8U(mU0w=^6LYko&AKnL|WcqP6+x zH2ajdpsK)aSe$GcE#s_(;sMSyr<3@r)#iGd&!hH9&f)Qs(6vN9{v!!St)WiwMA8&# zawilZDWak27Bb+|%QtATdEs?b>%yj4^VeAAPj$ZPGI z4l}X>9)7nT%JprE)>?25AFt?YCpZge@`)8MBf#>TfyJ<|tCntzG^lI$2sE0Vh($Bv zWaMVK-*v8yaVA+*MDJGOusD62`-RMWE_7Ew^Y7Y=RxF70Onx8yXB8LXe1o%4wxFpr zEjI*XB&TC);Ov%>1@w&!&WXyduJ~DBS8}{!az?z=g%yA~;eWs}3Y`di=FOo9XDn$N zmU~BAuVm50dQpS4+Dg`cPbcli7_V^x%o#Mi)3tHm^6io)I>;2G4Wn)Qm~g+})U<&6 z4e9IN`J;Ymt2J+wvV>|4SLr!oqCyQvL%GCZEy=us3)y)kTfG4Qml(rKbs?}%y`e;t zfiqa|A2+I4H`*M$RsUM%lNK9&Eid8xZgc>_0XPcMym!&z#QlvqV{Y9S9W_OIC{qj` z3zZ&|1a^#;tvPh`;#MM^?g!B&01wOvS-Xc=5wSQCYB{tW$!&r5=0Gi2rQVmkQayC~ zSwlTEYK`@+9K$Bzv&3o{H!ltua&*?$)^%l)wN|NO&y>cY4&}pyq_`KSK)(VkmkWak zG{yXg<`BkUh|(UR08diIek)0uk-nBvTNn@?f2VR9`WhWJ$@mH-Mh3f~d&T&K74hM~ z)HFAw=qfge*^3Q3g&$J81B+Pb1KbK>tUo^i8?60K8+hvh!a+>{rnY3`(XXF-iz^!*<@vi zp>n|&d_E`mxRQvvh-BJh&y%b(7roJCHi;EH1!my=)atACftMpxIRHrYJ{xG`M3PZ?*c5z~VyeiD(m$IpGG(pqI{!dorZ zmAUAwO}3%qKpn5geAgnIUX%p|t4vK_O}8UPkTyMqR^V>R$bGl0bAQ8itFbOLd8u>Y zE_R{QI56MEBUMOsAH}VjHgtA3&aoC-Ev1%1Lf)3|li@A}?`TC0!)ln5SOW)Ij$D|3 zu9RW*!9=NEgyKadU$8Df(1lkZNGfHzBLqeTsh+_X1-tGcL90k@7J4R7pX!N#*6PCU zfk@a-|7IZ;0%&;5e-}QLxUMlgO{U^vo|qUw8@XEia?>kW69yNX#FPfIU zFJ2ltGK#2J8hu>YC9!Wa3qzdiw##q-1kA9)gZ;A7!;f8Peqno90>R?EhTa-AsMimw z#T*uH0R61oI(XoKNhQuMB#$+W^S)j<-?UKJhCrpS)iY5TM6)hQ=Z704QpQkog``QP zrF|3_g_cl7U2!)7Pp<-?_kY@})6RYKxD{z|DyMH(=N=}PVq&Q}Q9c~)7W1IKib8_= z={{V1`;oI-@s<4HXFTTqVT`Imh(<_bjNuc)(r z-??08jrdI*+tL4H+93WiZ;7u|)<=w1XYap<+x+55{!!7S7rKpE0Twft-AexwLu@LZ z!GZIBHP9DY2{FI$MBa2#I+x+A%^=G?Q#qUo?bUy*R&y}Ab%6IA?;~QrTtJ1|3J@@ zud}V!&BiLF50)4D=QI+#nFn_BC|$50^YTi>y5@jY5QGG;mafqGse7;bYaeS&2gQEb zj!TTy!Ed)hQYvLt&%u`rp()>y@D=O!nx}m7Wc$`!L6vrP_n^Z%iO_|Su!5>w<&)jb zH_*>L_{>h=&2JA}>0?zW${?f#ihno0DI0Hf1nY|$vN3J#D+&f51sVL;G7^=44S5Jf zrj-(jMa1iMp^lDu49|vE+h8zV6eq3GG%1%M23U}A&OrI|K9;}0@KO<{gppwJ7zwJU zI?J-iS7a*BKhsQwecYui%)MJu72QpCA!*<6d<}zDhpfD**sNm1v0Nulf)Zr$*vmTe zS&X+dSz(JO_dxSZ#U|UwF2FpK)K`cnRhGuEaNz96O(80Bd^~GxfW;u<3{>e$iQ&`V zhVLm~;sYra%7}U z0C{O?pYU@sC+5_f+?8*0^})KjRNI^6vr?y2+o1FL#js`CS>U#tMrNB6yDK|qV}Ngf zvjZB(nth6B6<^pQ(cc%zsTSFRvAy_kjB7GX@)x5xkLBEkRQDuOfCS#9) zl78eo<(82dc>8?C-^;^OTci)CbT3w1s+DiM(5q53g}|NnTB-BYZW}(GOsu9DDSl7+ z=1!+smB(9CK%$MZmKyFo=BGrj+_Fgc*+z4^tnsKG^4_F`e2yf|Q9Bf|m;{DSfmE6c zm<0?{AQzIO>_*Fj#}HujOeR3*d}^xqLm^C%Z`Ya6-h4x0;~(aF+_)ehDpE+>br*m# zq;gKMJx_E zaI*PF3b}u&rNm%8p&yh2@t|J|+sgGWJ=Hb5R1;Gv& zTfd2}mE;$dV5i}_ttn&4vl_XgHAv06m3c?l1Tj<)(XaD6lJn^Dz+EpTy*~d6e=>!h zj)v|ryIG&xxM0KxkyFbp^r?d8R9H>{Ap>ddXBRGi>rNWn?RoE$qG!S6oSF%K3dj#c z1Z~tF>dev^yRz5mFc%e0Go(33LY@&VJ0`cRUtb%XfRJ4~9wcfQ2BqVaQ_xz1aapC<9S$+?&80I+8A_3wZ%H6UPC*K`#ggXOyp zS{1YK#-YRI(c~8-0kcsSnLohQ>@%T5_Z6+ju3y1ippJ<8zMbr{|5;bNvXQ0r+_L^< z>2WDdP&cY%*j^AoRdqD~Z%8T!^~1PIhZ46i!!M%c!|;*zzBN)VIn-8UAPP-WR`@6Z zpF6d|M^SMWvk@ z8djp_A#9`h0gDC`&7#6R{9b}tjqE%BePVnQ$JUJRSBE*$`=Mf+X>2}Rw>IQC4F*S0 zQVlm>v_PD*R1AcAk9G-?g9eiiYLuGMn7Aj=e1M_p^*&lzJy(|-F?gQ}Ug2t5k_5(t zeQkPb?8hiD?H>titSer$tAwWe^@l6ELeAddrYhQ~M>cf&Mqh$FvYEVG9J_3(y_MH2^X}T*D4@#%JwqM@5i6UJ@!g_7h2Z;&)^Fs=JAF%r zs7Lj*JgLT)Ola{C;yS(Sl%7B2?1a^ef~V+esxcsPJUp~+U?&!m5m?V$Xs{D~W)W3M7YC8V@A&C?1q)iJ z_yOXok^VlaGpd;{b)R@ad#^YjCDCeSjZ!j_QvjQ9KhLKGJpn{ESGY)vfpkp)XzUK9 zH;Xf&#^h+m7?o+fShaQZ$}NFSc1_IXDU+(0(y2w;CNpYx`r_jPmL*%u_jV8mUtxs1 zfU01ZS)3s7I)z=SHAu@cJ8bFY3EN5^i~);zBkNCoYe{cR8JN$Xb~*rvy%!Q~x2`2` zR=j4h-&#`9H^f@>MU$`k1sbo@S3!EehKNrB)0`;B4T3|1K?-IkMCwjow{D(7*)6%k z8ehHZ$2ECndz6^&>ybARfFYyXkjOUnM@(}7nm}%mDgofN0WmrpTXfFHRAb7IWe_NAd*V#5CE6)b1+R^>SV$RB$n}79d zyKgfg>frzfP{(Y3SebU%!h2Ev4#DakE`qJTxG$Xy(jz@hHM?Yo6&U>U3ZCfDGj*NQ zyQ^pA9G)|=-=fHn#$xj|n1NCvkgY$bei@Qj6ei~U|4$G5D1eh=&+;IWT z6GQm!_+16mfKd)Yh9YSU+I@ZDzTok`1t88TpZgIp*LNFZZl3rARE91@as z@mmqD>C(8AFgUa{a^gsl^FMugyQ?sdG|+{Z2U;wTjUdQg9HP6Cg)ldhJVk7YxV}av z^4-%^E+sWN+dqvlJaGiSD>3D#@I3$<@2{-M*8@45!`LUi?v#yOaU-)5bM;=UhHV@0Il(aY)iQw zgOoFH4(nW$79|d4_d2XT=v{CK7#ZWI{*ZObm)`jfTqOqGZSou>29w$_&EP7=m3>xn z)Y&*`*3esoj@tA{sg4XMS;e)Ai+d8-Uygp;T2^jH_;|s!FdznYeET738J5@XsVz)q z4}qSj{{>OPiy*q|+>Hy7>Kco8D2EQ~Ff)(ui~p+q=h>kjcj!u5 zXVQ1*echV+wAZ|eO@fyi)^$=)+ZJwuykK`23sPr3wzqi3Q8(rY>Ggso(}53h_&gvQm-6R#&{nz+pQS2s+N()^@#q+7}LlJMQ&!~iyqxbROOG3Xpl zSrsHk=0S#t0cYTL;D#FJS1L_6p464wtYYP~ZBV6Uu*~NlTj2h$yhLS1oI0T_wX?}_ zM|fb#sJv3rR-53!3peFWsgv%-1B3RMc^h;JvFWGo zw>|yxl;L_5M6Xjl<|LdY5mq6OdZ6;?JUiwwz-5t^Mds@RqXMkE&NH`jXOzQ7#+F2@ zEiUDPW@P)}L($(An&bAiwX6(P)RQ_oN9ycwiTfW)zjQLUYZ)t&qAf-hbiNnv>4dp+ z1IB6~Mky>d+>H_h`>Ww0u$!7u{q%>moXDk%A5?sB0iL4otgY5C-dFNB)(h=ue7A;m z8}M!WO(uN664o8(=CVW~7e4MlSN)IYb}AMg7`V4rvCwkI4h@q2$7;0$U>&$;QS&;R zg=IBLYdZzM zl*>18CVxcH+IzG#{)v~!U!EwLk-}G$3Rt=-*{}HV^x??+L{fWeO&?Pd)#F)c_0sKA zA?&egKz`QHlAOyg0)Tta6)&?LlIPL`sm;S(@jiUHx`R>};*=xmF+mnQ3N?T01baS2 zqij-miyz0JrLgXd07V|+wo?|vhUwd))+EGA ztmEp%vSl24YL-73O-9$uD0eLz86`{w#a{+Jweokapa%Oo{aj)aE%Ph+*m;xWSu#3# zp%_K;EKPLP#4Azr@sM>yC>ExQ**IuG9A;xJZtfJu*w)w_H7q4qfF*&@JOy@% zCUUdUBRYVmzChVw_Hc3TV)UKs?i)AJDimT^EGVwsDT}$#rRw=Aj>!W(=Oq#+TSA72 z)!NpS75r5c#aH%M%@ScGayS7?UbcSVAes)|Hoc*ufFXw?gPw<*962=2_g5V^Ty;dz zFSNoYptM^I1#q?99Kbl@0!o$`lUB2{&GXnNRze?Vs#geVCE}bw3SrT}Vdy+MM9Q?G#@TZc+8Nfw#=NwpD zULKTefZEQakvDL_dtD@8w^|}a@}$~9t(?C zNtc9Cdd@=kHDS~qCkEiT{AWj%JZ_2|{3E~GxH+HnhA!F5iZK|A? z&NN9nDd+&@IXUGz(`y!ldb{VIN0cleubh>Zqt>6rDQ9M(EziJOtEqF72r;Sd|+6%xE#?jL<$cqgeuIx6$MuH!Lbk|>f9Bh^e-WNX_pcW}gI*l`jgh&Z= zEzlA~Gk#igj3d}d{AJ!`4vSF3d~TjiIK(14A0=kgfuSRrjM8vx#o&O;EC^EUZyxnG zzI8s>n&b-tETP&u#wQ5Cet_|1DZJhx+3z^KQzTIaYbQp@;*pogFF01e*#Z7|h9oD6 z8p;kV>H8Nj@B)acKb1=1;;B#{s+-IV(v9%FGB`w!ep1p~dusJMV=baOs{!gy_;qps zJu7dh5e{(n5jICNGagkvP5bzj#F1;MdV{2HkWY{iJ%jbv397uS(&!`xW1gn5A5*gR zh{+kfiFDkwyGYr>@;oLtb5>EJeGzN+g&1SatrYXTTUiwLfPWf*6v+Q<6hii@3=EW6 z(>Yn1Z6zmgj%I(PH&8;X-6TE!);c^TX&OPh_~sf`{R2!8lVDT&qptR)NCeg%7Hy9y zkbt<;Qj3H*F&y;@D?_`Hn$Y~uv#m)cK1e+#xB4r8NyRb_6?_iJrv7sppMEUb-8bQ8 zX>V_@$ca=SW2d8ysnszk+N~e*mFitO_>xbzn2!F41Y>c6UM#H^o+P93@zefJBTuvj z^Eno$_pDx_GQspfc|0T`EZhH}@SE2)`i6EK6@k9*Yqc`IhrOOJz8HN2_OVg5m6e`h zM^nC!f~n9^iCL6+%^qt4a%f!3G<*4iNsCwMxuReTRVvLnwx20gX6_dR$+f-hB^iGA z4s>e}BG|TA?mwl#C!)5yt6)JWBdq(%F@jaj#oUF>rqQj$<5{u1^7TO%9vn2#%F*zO z=yO%T$`PrNZr_Afvc*4~ul0U?uF~|=L9irIbGib|Hipo6U{u+}K&Cl2D>vGpa5
!FLVkp-gh|pyn{u^Nvd$Xre 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 OP^yOlYL>lFFw77|zB@Sp literal 0 HcmV?d00001 diff --git a/build/tools/makegcdfirm/test/rsa_private.der b/build/tools/makegcdfirm/test/rsa_private.der new file mode 100644 index 0000000000000000000000000000000000000000..06f5f4ccbac2e2f50ebc312c9d601a422754c370 GIT binary patch literal 607 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
U4<#z|> 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 V0e$D4tJkd|WOOI~ka7)h{{^{CZ=wJI literal 0 HcmV?d00001 diff --git a/build/tools/makegcdfirm/test/twl_gcdfirm9_print.axf b/build/tools/makegcdfirm/test/twl_gcdfirm9_print.axf new file mode 100644 index 0000000000000000000000000000000000000000..3d064b38a5509e8ea9e4ce4cc5302e3f0684d513 GIT binary patch literal 288636 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 literal 0 HcmV?d00001 diff --git a/build/tools/makegcdfirm/test/wram_rbin/Makefile b/build/tools/makegcdfirm/test/wram_rbin/Makefile new file mode 100644 index 00000000..310ef0c2 --- /dev/null +++ b/build/tools/makegcdfirm/test/wram_rbin/Makefile @@ -0,0 +1,47 @@ +#! make -f +#---------------------------------------------------------------------------- +# Project: TwlFirm - tools - gcdfirm-print +# File: Makefile +# +# Copyright 2007 Nintendo. All rights reserved. +# +# These coded instructions, statements, and computer programs contain +# proprietary information of Nintendo of America Inc. and/or Nintendo +# Company Ltd., and are protected by Federal copyright law. They may +# not be disclosed to third parties or copied or duplicated in any form, +# in whole or in part, without the prior written consent of Nintendo. +# +# $Log: $ +# $NoKeywords: $ +#---------------------------------------------------------------------------- + +SUBDIRS = + +LINCLUDES = ../include + +#---------------------------------------------------------------------------- + +TARGET_BIN = wram_regs.rbin + +SRCS = \ + wram_regs.c \ + +#SRCDIR = # using default +#LCFILE = # using default + + +include $(TWLFIRM_ROOT)/build/buildtools/commondefs + +INSTALL_DIR = . +INSTALL_TARGETS = $(BINDIR)/$(TARGET_BIN) + + +#---------------------------------------------------------------------------- + +do-build: $(TARGETS) + + +include $(TWLFIRM_ROOT)/build/buildtools/modulerules + + +#===== End of Makefile ===== diff --git a/build/tools/makegcdfirm/test/wram_rbin/wram_regs.c b/build/tools/makegcdfirm/test/wram_rbin/wram_regs.c new file mode 100644 index 00000000..ade0c709 --- /dev/null +++ b/build/tools/makegcdfirm/test/wram_rbin/wram_regs.c @@ -0,0 +1,88 @@ +/*---------------------------------------------------------------------------* + Project: TwlFirm - tools - makegcdfirm + File: wram_regs.c + + Copyright 2007 Nintendo. All rights reserved. + + These coded instructions, statements, and computer programs contain + proprietary information of Nintendo of America Inc. and/or Nintendo + Company Ltd., and are protected by Federal copyright law. They may + not be disclosed to third parties or copied or duplicated in any form, + in whole or in part, without the prior written consent of Nintendo. + + $Log: $ + $NoKeywords: $ + *---------------------------------------------------------------------------*/ +#include +#include + +MIHeader_WramRegs wram_regs = +{ + // ARM9 + { + REG_WRAM_A_BNK_PACK(0, MI_WRAM_A_ARM7, MI_WRAM_A_OFS_0KB, TRUE), + REG_WRAM_A_BNK_PACK(1, MI_WRAM_A_ARM7, MI_WRAM_A_OFS_64KB, TRUE), + REG_WRAM_A_BNK_PACK(2, MI_WRAM_A_ARM7, MI_WRAM_A_OFS_128KB, TRUE), + REG_WRAM_A_BNK_PACK(3, MI_WRAM_A_ARM7, MI_WRAM_A_OFS_192KB, TRUE), + }, + { + REG_WRAM_B_BNK_PACK(0, MI_WRAM_B_ARM7, MI_WRAM_B_OFS_0KB, TRUE), + REG_WRAM_B_BNK_PACK(1, MI_WRAM_B_ARM7, MI_WRAM_B_OFS_32KB, TRUE), + REG_WRAM_B_BNK_PACK(2, MI_WRAM_B_ARM7, MI_WRAM_B_OFS_64KB, TRUE), + REG_WRAM_B_BNK_PACK(3, MI_WRAM_B_ARM7, MI_WRAM_B_OFS_96KB, TRUE), + REG_WRAM_B_BNK_PACK(4, MI_WRAM_B_ARM7, MI_WRAM_B_OFS_128KB, TRUE), + REG_WRAM_B_BNK_PACK(5, MI_WRAM_B_ARM7, MI_WRAM_B_OFS_160KB, TRUE), + REG_WRAM_B_BNK_PACK(6, MI_WRAM_B_ARM7, MI_WRAM_B_OFS_192KB, TRUE), + REG_WRAM_B_BNK_PACK(7, MI_WRAM_B_ARM7, MI_WRAM_B_OFS_224KB, TRUE), + }, + { + REG_WRAM_C_BNK_PACK(0, MI_WRAM_C_ARM9, MI_WRAM_C_OFS_0KB, TRUE), + REG_WRAM_C_BNK_PACK(1, MI_WRAM_C_ARM9, MI_WRAM_C_OFS_32KB, TRUE), + REG_WRAM_C_BNK_PACK(2, MI_WRAM_C_ARM9, MI_WRAM_C_OFS_64KB, TRUE), + REG_WRAM_C_BNK_PACK(3, MI_WRAM_C_ARM9, MI_WRAM_C_OFS_96KB, TRUE), + REG_WRAM_C_BNK_PACK(4, MI_WRAM_C_ARM9, MI_WRAM_C_OFS_128KB, TRUE), + REG_WRAM_C_BNK_PACK(5, MI_WRAM_C_ARM9, MI_WRAM_C_OFS_160KB, TRUE), + REG_WRAM_C_BNK_PACK(6, MI_WRAM_C_ARM9, MI_WRAM_C_OFS_192KB, TRUE), + REG_WRAM_C_BNK_PACK(7, MI_WRAM_C_ARM9, MI_WRAM_C_OFS_224KB, TRUE), + }, + REG_WRAM_A_MAP_PACK(MI_WRAM_MAP_NULL, + MI_WRAM_MAP_NULL, + MI_WRAM_A_IMG_128KB + ), + REG_WRAM_B_MAP_PACK(MI_WRAM_MAP_NULL, + MI_WRAM_MAP_NULL, + MI_WRAM_B_IMG_128KB + ), + REG_WRAM_C_MAP_PACK(HW_WRAM_AREA_HALF, + HW_WRAM_AREA_HALF + 0x00020000, + MI_WRAM_C_IMG_128KB + ), + + // ARM7 + REG_WRAM_A_MAP_PACK(HW_WRAM_AREA_HALF, + HW_WRAM_AREA_HALF + 0x00020000, + MI_WRAM_A_IMG_128KB + ), + REG_WRAM_B_MAP_PACK(HW_WRAM_AREA_HALF + 0x00020000, + HW_WRAM_AREA_HALF + 0x00040000, + MI_WRAM_B_IMG_128KB + ), + REG_WRAM_C_MAP_PACK(MI_WRAM_MAP_NULL, + MI_WRAM_MAP_NULL, + MI_WRAM_C_IMG_128KB + ), + // WRAM Lock + { + 0, + 0, + 0, + }, + + // WRAM-0/1 + MI_WRAM_ARM7_ALL, + + // VRAM-C + 7, + // VRAM-D + 7, +}; diff --git a/build/tools/makegcdfirm/wram_regs.c b/build/tools/makegcdfirm/wram_regs.c new file mode 100644 index 00000000..9472c5e8 --- /dev/null +++ b/build/tools/makegcdfirm/wram_regs.c @@ -0,0 +1,91 @@ +/*---------------------------------------------------------------------------* + Project: TwlFirm - tools - makegcdfirm + File: wram_regs.c + + Copyright 2007 Nintendo. All rights reserved. + + These coded instructions, statements, and computer programs contain + proprietary information of Nintendo of America Inc. and/or Nintendo + Company Ltd., and are protected by Federal copyright law. They may + not be disclosed to third parties or copied or duplicated in any form, + in whole or in part, without the prior written consent of Nintendo. + + $Log: $ + $NoKeywords: $ + *---------------------------------------------------------------------------*/ +#include "format_rom.h" +#define SDK_ASM +#include +#include + +MIHeader_WramRegs wram_regs_init = +{ + // ARM9 + { + REG_WRAM_A_BNK_PACK(0, MI_WRAM_A_ARM7, MI_WRAM_A_OFS_0KB, TRUE), + REG_WRAM_A_BNK_PACK(1, MI_WRAM_A_ARM7, MI_WRAM_A_OFS_64KB, TRUE), + REG_WRAM_A_BNK_PACK(2, MI_WRAM_A_ARM7, MI_WRAM_A_OFS_128KB, TRUE), + REG_WRAM_A_BNK_PACK(3, MI_WRAM_A_ARM7, MI_WRAM_A_OFS_192KB, TRUE), + }, + { + REG_WRAM_B_BNK_PACK(0, MI_WRAM_B_ARM7, MI_WRAM_B_OFS_0KB, TRUE), + REG_WRAM_B_BNK_PACK(1, MI_WRAM_B_ARM7, MI_WRAM_B_OFS_32KB, TRUE), + REG_WRAM_B_BNK_PACK(2, MI_WRAM_B_ARM7, MI_WRAM_B_OFS_64KB, TRUE), + REG_WRAM_B_BNK_PACK(3, MI_WRAM_B_ARM7, MI_WRAM_B_OFS_96KB, TRUE), + REG_WRAM_B_BNK_PACK(4, MI_WRAM_B_ARM7, MI_WRAM_B_OFS_128KB, TRUE), + REG_WRAM_B_BNK_PACK(5, MI_WRAM_B_ARM7, MI_WRAM_B_OFS_160KB, TRUE), + REG_WRAM_B_BNK_PACK(6, MI_WRAM_B_ARM7, MI_WRAM_B_OFS_192KB, TRUE), + REG_WRAM_B_BNK_PACK(7, MI_WRAM_B_ARM7, MI_WRAM_B_OFS_224KB, TRUE), + }, + { + REG_WRAM_C_BNK_PACK(0, MI_WRAM_C_ARM9, MI_WRAM_C_OFS_0KB, TRUE), + REG_WRAM_C_BNK_PACK(1, MI_WRAM_C_ARM9, MI_WRAM_C_OFS_32KB, TRUE), + REG_WRAM_C_BNK_PACK(2, MI_WRAM_C_ARM9, MI_WRAM_C_OFS_64KB, TRUE), + REG_WRAM_C_BNK_PACK(3, MI_WRAM_C_ARM9, MI_WRAM_C_OFS_96KB, TRUE), + REG_WRAM_C_BNK_PACK(4, MI_WRAM_C_ARM9, MI_WRAM_C_OFS_128KB, TRUE), + REG_WRAM_C_BNK_PACK(5, MI_WRAM_C_ARM9, MI_WRAM_C_OFS_160KB, TRUE), + REG_WRAM_C_BNK_PACK(6, MI_WRAM_C_ARM9, MI_WRAM_C_OFS_192KB, TRUE), + REG_WRAM_C_BNK_PACK(7, MI_WRAM_C_ARM9, MI_WRAM_C_OFS_224KB, TRUE), + }, + REG_WRAM_A_MAP_PACK(MI_WRAM_MAP_NULL, + MI_WRAM_MAP_NULL, + MI_WRAM_A_IMG_128KB + ), + REG_WRAM_B_MAP_PACK(MI_WRAM_MAP_NULL, + MI_WRAM_MAP_NULL, + MI_WRAM_B_IMG_128KB + ), + REG_WRAM_C_MAP_PACK(HW_WRAM_AREA_HALF, + HW_WRAM_AREA_HALF + 0x00020000, + MI_WRAM_C_IMG_128KB + ), + + // ARM7 + REG_WRAM_A_MAP_PACK(HW_WRAM_AREA_HALF, + HW_WRAM_AREA_HALF + 0x00020000, + MI_WRAM_A_IMG_128KB + ), + REG_WRAM_B_MAP_PACK(HW_WRAM_AREA_HALF + 0x00020000, + HW_WRAM_AREA_HALF + 0x00040000, + MI_WRAM_B_IMG_128KB + ), + REG_WRAM_C_MAP_PACK(MI_WRAM_MAP_NULL, + MI_WRAM_MAP_NULL, + MI_WRAM_C_IMG_128KB + ), + // WRAM Lock + { + 0, + 0, + 0, + }, + + // WRAM-0/1 + 3, + + // VRAM-C + 7, + // VRAM-D + 7, +}; + diff --git a/build/tools/makenandfirm/Makefile b/build/tools/makenandfirm/Makefile new file mode 100644 index 00000000..2e8c7b9c --- /dev/null +++ b/build/tools/makenandfirm/Makefile @@ -0,0 +1,148 @@ +#! make -f +#--------------------------------------------------------------------------- +# Project: TwlFirm - tools - makenandfirm +# File: Makefile +# +# Copyright 2007 Nintendo. All rights reserved. +# +# These coded instructions, statements, and computer programs contain +# proprietary information of Nintendo of America Inc. and/or Nintendo +# Company Ltd., and are protected by Federal copyright law. They may +# not be disclosed to third parties or copied or duplicated in any form, +# in whole or in part, without the prior written consent of Nintendo. +# +# $Log: $ +# $NoKeywords: $ +#--------------------------------------------------------------------------- + +SUPPORT_ECC = 0 + +ifeq ($(SUPPORT_ECC),1) +ECC_SRCDIR = ../../libraries/acsign_ecc/common \ + ../../libraries/acsign_ecc/common/algae/common/ecc \ + ../../libraries/acsign_ecc/common/algae/cmp \ + ../../libraries/acsign_ecc/common/algae/ecsource \ + +ECC_INCDIR = ../../libraries/acsign_ecc/include \ + ../../libraries/acsign_ecc/common/algae/include \ + ../../libraries/acsign_ecc/common/algae/common/include \ + +ECC_SRCS = acsign_ecc.c acsign_cryptoc.c \ + \ + cmparith.c cmpbits.c cmpcnv.c cmpdiv.c cmpmem.c \ + cmpmod.c cmpmuldv.c cmpspprt.c cmpsqr.c cmpvectr.c \ + computem.c ecfpatbl.c ecfpsmul.c \ + spcprime.c secfpcom.c \ + \ + p224v1.c p224v1a.c \ + +ECC_DEFS = -DRSA_PROTOTYPES=RSA_ENABLED \ + -DRCOM_BUILD=RSA_ENABLED -DRSA_FAST_INVERSE=RSA_ENABLED \ + -DRSA_STD_MEM_FUNCS=RSA_ENABLED -DRSA_STD_ALLOC_FUNCS=RSA_ENABLED \ +else +ECC_SRCDIR = +ECC_INCDIR = +ECC_SRCS = +ECC_DEFS = +endif + +SRCDIR += ../acsign $(ECC_SRCDIR) +INCDIR += ../acsign/include $(ECC_INCDIR) $(ECC_SRCDIR) + + +include $(TWLFIRM_ROOT)/build/buildtools/commondefs + +TARGETS = makenandfirm.exe + +SOURCES_C = makenandfirm.c \ + out_nandfirm.c \ + misc.c \ + path.c \ + defval.c \ + compress.c \ + wram_regs.c \ + acsign.c \ + acsign_nand.c \ + aes2.c \ + $(ECC_SRCS) + +SOURCES = $(SORUCES_C) + +OBJECTS = $(SOURCES_C:.c=.o) + +HEADERS = format_nlist.h \ + makenandfirm.h \ + path.h \ + format_rom.h \ + misc.h \ + defval.h \ + compress.h \ + +MACROS += -DSMALL_CODE_SIZE \ + -DSTANDALONE \ + -DOPT_32_BIT \ + -DNO_SPLIT \ + -DNO_FP_API \ + -DNO_R_DIAG \ + $(ECC_DEFS) + +INSTALL_DIR = $(FIRM_INSTALL_TOOLSDIR)/bin +INSTALL_TARGETS = $(TARGETS) + +LDIRT_CLEAN = $(OBJECTS) $(TARGETS) version.h + + +VPATH = $(SRCDIR) +NITRO_INCDIR := $(FIRM_INCDIR) -I$(TWL_INCDIR) -I$(NITRO_INCDIR) $(addprefix -I,$(INCDIR)) + +include $(TWL_NITROSDK_ROOT)/build/buildtools/modulerules.x86 + +#---------------------------------------------------------------------------- +# build +#---------------------------------------------------------------------------- +do-build: $(TARGETS) + +$(TARGETS): $(OBJECTS) + $(CC_X86) $+ -o $@ + +makenandfirm.o: makenandfirm.c makenandfirm.h format_rom.h path.h version.h +out_nandfirm.o: out_nandfirm.c misc.h format_rom.h format_nlist.h format_sign.h elf.h compress.h \ + $(FIRM_INCDIR)/firm/format/sign.h \ + $(FIRM_INCDIR)/firm/format/wram_regs.h \ + $(FIRM_INCDIR)/firm/format/nandfirm.h \ + +misc.o: misc.c misc.h +path.o: path.c path.h +compress.o: compress.c compress.h +wram_regs.o: wram_regs.c +acsign.o: acsign.c ../acsign/include/acsign.h +acsign_nand.o: acsign_nand.c format_sign.h \ + $(FIRM_INCDIR)/firm/format/sign.h \ + $(FIRM_INCDIR)/firm/format/wram_regs.h \ + $(FIRM_INCDIR)/firm/format/nandfirm.h \ + +aes2.o: aes2.c aes2.h + +$(FIRM_INCDIR)/firm/format/sign.h: +$(FIRM_INCDIR)/firm/format/wram_regs.h: +$(FIRM_INCDIR)/firm/format/nandfirm.h: +format_nlist.h: +format_rom.h: +makenandfirm.h: +acsign.h: +acsign_nand.h: +path.h: + +# avoid to warning message +misc.o:WARNING += -Wno-format-y2k + +# + +version.h: $(SOURCES) $(HEADERS) + @for i in $^ ; \ + do \ + date -r $$i +'#define SDK_DATE_OF_LATEST_FILE %Y%m%dUL'; \ + done | sort | tail -1 > $@ + +test: path.c misc.c + $(CC_X86) -DTEST $+ -o $@ diff --git a/build/tools/makenandfirm/compress.c b/build/tools/makenandfirm/compress.c new file mode 100644 index 00000000..acc7fdd2 --- /dev/null +++ b/build/tools/makenandfirm/compress.c @@ -0,0 +1,292 @@ +/*---------------------------------------------------------------------------* + Project: TwlFirm - tools - makenandfirm + File: compress.c + + Copyright 2007 Nintendo. All rights reserved. + + These coded instructions, statements, and computer programs contain + proprietary information of Nintendo of America Inc. and/or Nintendo + Company Ltd., and are protected by Federal copyright law. They may + not be disclosed to third parties or copied or duplicated in any form, + in whole or in part, without the prior written consent of Nintendo. + + $Log: $ + $NoKeywords: $ + *---------------------------------------------------------------------------*/ +#include +#include // atoi() +#include // strcmp() +#include // isprint() +#include // chdir() +#include +#include // UCHAR_MAX +#include +#include // stat() +#include "elf.h" +#include "misc.h" +#include "defval.h" +#include "format_rom.h" +#include "format_nlist.h" +#include "makenandfirm.h" + +//#define ADD_HEADER + +#define DIFF_CODE_HEADER (0x80) +#define RL_CODE_HEADER (0x30) +#define LZ_CODE_HEADER (0x10) +#define HUFF_CODE_HEADER (0x20) +#define CODE_HEADER_MASK (0xF0) + +//=========================================================================== +// LZ77圧縮 +//=========================================================================== +static u8 SearchLZ(const u8 *nextp, u32 remainSize, u16 *offset); + +static u16 windowPos; +static u16 windowLen; + +static s16 LZOffsetTable[4096]; +static s16 LZByteTable[256]; +static s16 LZEndTable[256]; + + +static void LZInitTable(void) +{ + u16 i; + + for (i = 0; i < 256; i++) + { + LZByteTable[i] = -1; + LZEndTable[i] = -1; + } + windowPos = 0; + windowLen = 0; +} + +static void SlideByte(const u8 *srcp) +{ + s16 offset; + u8 in_data = *srcp; + u16 insert_offset; + + if (windowLen == 4096) + { + u8 out_data = *(srcp - 4096); + if ((LZByteTable[out_data] = LZOffsetTable[LZByteTable[out_data]]) == -1) + { + LZEndTable[out_data] = -1; + } + insert_offset = windowPos; + } + else + { + insert_offset = windowLen; + } + + offset = LZEndTable[in_data]; + if (offset == -1) + { + LZByteTable[in_data] = insert_offset; + } + else + { + LZOffsetTable[offset] = insert_offset; + } + LZEndTable[in_data] = insert_offset; + LZOffsetTable[insert_offset] = -1; + + if (windowLen == 4096) + { + windowPos = (u16)((windowPos + 1) % 0x1000); + } + else + { + windowLen++; + } +} + +static void LZSlide(const u8 *srcp, u32 n) +{ + u32 i; + + for (i = 0; i < n; i++) + { + SlideByte(srcp++); + } +} + +/*---------------------------------------------------------------------------* + Name: MI_CompressLZ + + Description: LZ77圧縮を行なう関数 + + Arguments: srcp 圧縮元データへのポインタ + size 圧縮元データサイズ + dstp 圧縮先データへのポインタ + 圧縮元データよりも大きいサイズのバッファが必要です。 + + Returns: 圧縮後のデータサイズ。 + 圧縮後のデータが圧縮前よりも大きくなる場合には圧縮を中断し0を返します。 + *---------------------------------------------------------------------------*/ +u32 LZCompWrite(u8 *srcp, u32 size, u8 *dstp, int boundary) +{ + u32 LZDstCount; // 圧縮データのバイト数 + u8 LZCompFlags; // 圧縮の有無を示すフラグ系列 + u8 *LZCompFlagsp; // LZCompFlags を格納するメモリ領域をポイント + u16 lastOffset; // 一致データまでのオフセット (その時点での最長一致データ) + u8 lastLength; // 一致データ長 (その時点での最長一致データ) + u8 i; + u32 dstMax; + +#ifdef ADD_HEADER + *(u32 *)dstp = size << 8 | LZ_CODE_HEADER; // データ・ヘッダ + dstp += 4; +#endif + LZDstCount = 4; + dstMax = size; + LZInitTable(); + + while (size > 0) + { + LZCompFlags = 0; + LZCompFlagsp = dstp++; // フラグ系列の格納先 + LZDstCount++; + + // フラグ系列が8ビットデータとして格納されるため、8回ループ + for (i = 0; i < 8; i++) + { + LZCompFlags <<= 1; // 初回 (i=0) は特に意味はない + if (size <= 0) + { + // 終端に来た場合はフラグを最後までシフトさせてから終了 + continue; + } + + if ((lastLength = SearchLZ(srcp, size, &lastOffset))) + { + // 圧縮可能な場合はフラグを立てる + LZCompFlags |= 0x1; + + // オフセットは上位4ビットと下位8ビットに分けて格納 + *dstp++ = (u8)((lastLength - 3) << 4 | (lastOffset - 1) >> 8); + *dstp++ = (u8)((lastOffset - 1) & 0xff); + LZDstCount += 2; + LZSlide(srcp, lastLength); + srcp += lastLength; + size -= lastLength; + } + else + { + // 圧縮なし + LZSlide(srcp, 1); + *dstp++ = *srcp++; + size--; + LZDstCount++; + } + } // 8回ループ終了 + *LZCompFlagsp = LZCompFlags; // フラグ系列を格納 + } + + // 16バイト境界アラインメント + // アラインメント用データ0 はデータサイズに含める + i = 0; + while (LZDstCount & (boundary - 1)) +// while ((LZDstCount + i) & 0x3) + { + *dstp++ = 0; + LZDstCount++; + i++; + } + + return LZDstCount; +} + +//-------------------------------------------------------- +// LZ77圧縮でスライド窓の中から最長一致列を検索します。 +// Arguments: startp データの開始位置を示すポインタ +// nextp 検索を開始するデータのポインタ +// remainSize 残りデータサイズ +// offset 一致したオフセットを格納する領域へのポインタ +// Return : 一致列が見つかった場合は TRUE +// 見つからなかった場合は FALSE +//-------------------------------------------------------- +static u8 SearchLZ(const u8 *nextp, u32 remainSize, u16 *offset) +{ + const u8 *searchp; + const u8 *headp, *searchHeadp; + u16 maxOffset; + u8 maxLength = 2; + u8 tmpLength; + s32 w_offset; + + if (remainSize < 3) + { + return 0; + } + + w_offset = LZByteTable[*nextp]; + + while (w_offset != -1) + { + if (w_offset < windowPos) + { + searchp = nextp - windowPos + w_offset; + } + else + { + searchp = nextp - windowLen - windowPos + w_offset; + } + + /* 無くても良いが、僅かに高速化する */ + if (*(searchp + 1) != *(nextp + 1) || *(searchp + 2) != *(nextp + 2)) + { + w_offset = LZOffsetTable[w_offset]; + continue; + } + + if (nextp - searchp < 2) + { + // VRAMは2バイトアクセスなので (VRAMからデータを読み出す場合があるため)、 + // 検索対象データは2バイト前からのデータにしなければならない。 + // + // オフセットは12ビットで格納されるため、4096以下 + break; + } + tmpLength = 3; + searchHeadp = searchp + 3; + headp = nextp + 3; + + while (((u32)(headp - nextp) < remainSize) && (*headp == *searchHeadp)) + { + headp++; + searchHeadp++; + tmpLength++; + + // データ長は4ビットで格納されるため、18以下 (3の下駄をはかせる) + if (tmpLength == (0xF + 3)) + { + break; + } + } + if (tmpLength > maxLength) + { + // 最大長オフセットを更新 + maxLength = tmpLength; + maxOffset = (u16)(nextp - searchp); + if (maxLength == (0xF + 3)) + { + // 一致長が最大なので、検索を終了する。 + break; + } + } + w_offset = LZOffsetTable[w_offset]; + } + + if (maxLength < 3) + { + return 0; + } + *offset = maxOffset; + return maxLength; +} + diff --git a/build/tools/makenandfirm/compress.h b/build/tools/makenandfirm/compress.h new file mode 100644 index 00000000..d8aa290a --- /dev/null +++ b/build/tools/makenandfirm/compress.h @@ -0,0 +1,37 @@ +/*---------------------------------------------------------------------------* + Project: TwlFirm - tools - makenandfirm + File: compress.h + + Copyright 2007 Nintendo. All rights reserved. + + These coded instructions, statements, and computer programs contain + proprietary information of Nintendo of America Inc. and/or Nintendo + Company Ltd., and are protected by Federal copyright law. They may + not be disclosed to third parties or copied or duplicated in any form, + in whole or in part, without the prior written consent of Nintendo. + + $Log: $ + $NoKeywords: $ + *---------------------------------------------------------------------------*/ +#ifndef COMPRESS_H_ +#define COMPRESS_H_ + +#include "misc.h" + + +/*---------------------------------------------------------------------------* + Name: MI_CompressLZ + + Description: LZ77圧縮を行なう関数 + + Arguments: srcp 圧縮元データへのポインタ + size 圧縮元データサイズ + dstp 圧縮先データへのポインタ + 圧縮元データよりも大きいサイズのバッファが必要です。 + + Returns: 圧縮後のデータサイズ。 + *---------------------------------------------------------------------------*/ +u32 LZCompWrite(u8 *srcp, u32 size, u8 *dstp, int boundary); + + +#endif //COMPRESS_H_ diff --git a/build/tools/makenandfirm/defval.c b/build/tools/makenandfirm/defval.c new file mode 100644 index 00000000..679fb0e6 --- /dev/null +++ b/build/tools/makenandfirm/defval.c @@ -0,0 +1,315 @@ +/*---------------------------------------------------------------------------* + Project: NitroSDK - tools - makerom + File: defval.c + + Copyright 2003-2006 Nintendo. All rights reserved. + + These coded instructions, statements, and computer programs contain + proprietary information of Nintendo of America Inc. and/or Nintendo + Company Ltd., and are protected by Federal copyright law. They may + not be disclosed to third parties or copied or duplicated in any form, + in whole or in part, without the prior written consent of Nintendo. + + $Log: defval.c,v $ + Revision 1.10 2006/01/18 02:11:19 kitase_hirotake + do-indent + + Revision 1.9 2005/02/28 05:26:03 yosizaki + do-indent. + + Revision 1.8 2004/08/05 13:50:13 yasu + Support -M option + + Revision 1.7 2004/06/29 04:55:40 yasu + Use VBuffer to resolve variables + + Revision 1.6 2004/06/23 07:51:02 yasu + fix a bug as illegal memory freeing at ResolveDefVal() + + Revision 1.5 2004/05/27 00:40:49 yasu + care also about current directory (dot ".") + + Revision 1.4 2004/05/27 00:25:46 yasu + care about double-dots ".." for defvalue option :r, :e + + Revision 1.3 2004/05/27 00:11:19 yasu + fix a error when searching a "dot" of file extension + + Revision 1.2 2004/05/26 12:02:47 yasu + support :h, :t, :r, :e option for variable name + + Revision 1.1 2004/03/26 05:06:45 yasu + support variables like as -DNAME=VALUE + + $NoKeywords: $ + *---------------------------------------------------------------------------*/ +#include +#include // getenv() +#include // strcasecmp() +#include // getopt() +#include "misc.h" +#include "defval.h" + +typedef struct tValdef +{ + struct tValdef *next; + char *name; + char *value; +} +tValdef; + +tValdef *valdef_top = NULL; + + +// +// Add new define value via file +// +// opt : "DEFINE=VALUE" +// +BOOL AddDefValFromFile(char *filename) +{ + char *buffer; + int buffer_size; + int read_size; + FILE *fp; + + if (filename[0] == '-' && filename[1] == '\0') + { + fp = stdin; + } + else if (NULL == (fp = fopen(filename, "rb"))) + { + fprintf(stderr, "Cannot open file \"%s\".\n", filename); + return FALSE; + } + + buffer_size = DEFVAL_DEFAULT_BUFFER_SIZE; + + if (NULL == (buffer = malloc(buffer_size))) + { + fprintf(stderr, "Cannot allocate memory.\n"); + return FALSE; + } + + read_size = 0; + + while (NULL != fgets(buffer + read_size, buffer_size - read_size, fp)) + { + read_size = strlen(buffer); + + if (read_size == buffer_size - 1 && buffer[read_size - 1] != '\n') + { + buffer_size *= 2; + + if (NULL == (buffer = realloc(buffer, buffer_size))) + { + fprintf(stderr, "Cannot allocate memory.\n"); + return FALSE; + } + continue; + } + + AddDefVal(buffer); + read_size = 0; + } + + if (fp != stdin) + { + fclose(fp); + } + free(buffer); + + return TRUE; +} + + +// +// Add new define value +// +// opt : "DEFINE=VALUE" +// +void AddDefVal(char *opt) +{ + int i; + tValdef *t; + + for (i = 0;; i++) + { + if ('=' == opt[i] || '\0' == opt[i]) + { + break; + } + } + + if (i > 0) + { + t = Alloc(sizeof(tValdef)); + t->name = strncpy(Alloc(i + 1), opt, i); + t->name[i] = '\0'; + + if (opt[i] == '=') + { + i++; + } + t->value = strdup(opt + i); + + t->next = valdef_top; + valdef_top = t; + + debug_printf("DEFINE:$(%s)=\"%s\"\n", t->name, t->value); + } + return; +} + +// +// Search define value +// +// Return: value of specified name +// +char *SearchDefVal(char *name) +{ + tValdef *t; + + for (t = valdef_top; t; t = t->next) + { + if (!strcmp(t->name, name)) + { + return t->value; + } + } + + return getenv(name); +} + + +// +// Search define value and Modify it by : option +// +// Return: duplicated value of specified name modified by :x option +// +char *SearchDefValWithOption(char *name) +{ + int len_name = strlen(name); + char *value; + char option = '\0'; + + if (len_name > 2 && name[len_name - 2] == ':') + { + name[len_name - 2] = '\0'; + option = name[len_name - 1]; + } + + value = SearchDefVal(name); + + if (value) + { + int value_len = strlen(value); + int col_dot = value_len; + int col_filename = 0; + int i; + + for (i = 0; i < value_len; i++) + { + switch (value[i]) + { + case '.': + if (col_filename == i && + (value[i + 1] == '\0' || (value[i + 1] == '.' && value[i + 2] == '\0'))) + { + i = value_len; // exit loop if last entry is . or .. + } + else + { + col_dot = i; // Save the last dot column + } + break; + + case '/': + case '\\': + case ':': + col_filename = i + 1; // Save the last filename + col_dot = value_len; // Reset dot position + break; + + default: + ; + } + } + + switch (option) + { + case 'h': // Dirname with the last slash + value = strdup(value); + value[col_filename] = '\0'; + break; + + case 't': // Filename + value = strdup(value + col_filename); + break; + + case 'r': // All without . file extension + value = strdup(value); + value[col_dot] = '\0'; + break; + + case 'e': // File extension + value = strdup(value + col_dot + 1); + break; + + default: + value = strdup(value); + } + } + return value; +} + + +// +// Resolve define value +// +// Return: new string +// +char *ResolveDefVal(char *str) +{ + int i, j; + char *val; + VBuffer buf; + + InitVBuffer(&buf); + + for (i = 0; '\0' != str[i]; i++) + { + // search $(XXX) + if ('$' == str[i] && '(' == str[i + 1]) + { + for (j = i + 2; '\0' != str[j]; j++) + { + if (')' == str[j]) + { + str[j] = '\0'; + + // get value of XXX + val = SearchDefValWithOption(&str[i + 2]); + + // copy value of XXX + if (val) + { + char *s = val; + + while (*s) + { + PutVBuffer(&buf, *s); + s++; + } + free(val); + } + i = j; + goto next; + } + } + } + PutVBuffer(&buf, str[i]); + next:; + } + return GetVBuffer(&buf); // pass allocated buffer, should be freed by caller +} diff --git a/build/tools/makenandfirm/defval.h b/build/tools/makenandfirm/defval.h new file mode 100644 index 00000000..71355101 --- /dev/null +++ b/build/tools/makenandfirm/defval.h @@ -0,0 +1,38 @@ +/*---------------------------------------------------------------------------* + Project: NitroSDK - tools - makerom + File: defval.h + + Copyright 2003-2006 Nintendo. All rights reserved. + + These coded instructions, statements, and computer programs contain + proprietary information of Nintendo of America Inc. and/or Nintendo + Company Ltd., and are protected by Federal copyright law. They may + not be disclosed to third parties or copied or duplicated in any form, + in whole or in part, without the prior written consent of Nintendo. + + $Log: defval.h,v $ + Revision 1.4 2006/01/18 02:11:19 kitase_hirotake + do-indent + + Revision 1.3 2005/02/28 05:26:03 yosizaki + do-indent. + + Revision 1.2 2004/08/05 13:50:13 yasu + Support -M option + + Revision 1.1 2004/03/26 05:06:45 yasu + support variables like as -DNAME=VALUE + + $NoKeywords: $ + *---------------------------------------------------------------------------*/ +#ifndef DEFVAL_H_ +#define DEFVAL_H_ + +#define DEFVAL_DEFAULT_BUFFER_SIZE (1024) + +BOOL AddDefValFromFile(char *filename); +void AddDefVal(char *opt); +char *SearchDefVal(char *name); +char *ResolveDefVal(char *str); + +#endif //DEFVAL_H_ diff --git a/build/tools/makenandfirm/elf.h b/build/tools/makenandfirm/elf.h new file mode 100644 index 00000000..c360cd33 --- /dev/null +++ b/build/tools/makenandfirm/elf.h @@ -0,0 +1,431 @@ +/*---------------------------------------------------------------------------* + Project: TwlFirm - ELF + File: elf.h + + Copyright 2007 Nintendo. All rights reserved. + + These coded instructions, statements, and computer programs contain + proprietary information of Nintendo of America Inc. and/or Nintendo + Company Ltd., and are protected by Federal copyright law. They may + not be disclosed to third parties or copied or duplicated in any form, + in whole or in part, without the prior written consent of Nintendo. + *---------------------------------------------------------------------------*/ + +#ifndef ELF_H_ +#define ELF_H_ + +#include "misc.h" + +/*--------------------------------------------------------- + 型定義 + --------------------------------------------------------*/ +typedef u32 Elf32_Addr; /* size:4, align:4 Unsigned program address */ +typedef u16 Elf32_Half; /* size:2, align:2 Unsigned medium int */ +typedef u32 Elf32_Off; /* size:4, align:4 Unsigned file offset */ +typedef s32 Elf32_Sword; /* size:4, align:4 Signed large int */ +typedef u32 Elf32_Word; /* size:4, align:4 Unsigned large int */ + +/*--------------------------------------------------------- + ELF Header + --------------------------------------------------------*/ +/* e_identのインデックス */ +#define EI_MAG0 0 /* File identification */ +#define EI_MAG1 1 /* File identification */ +#define EI_MAG2 2 /* File identification */ +#define EI_MAG3 3 /* File identification */ +#define EI_CLASS 4 /* File class 0=invalid, 1=32bit, 2=64bit */ +#define EI_DATA 5 /* Data encoding 0=invalid, 1=LSB, 2=MSB */ +#define EI_VERSION 6 /* File version 現在は1 */ +#define EI_PAD 7 /* Start of padding bytes */ +#define EI_NIDENT 16 /* Size of e_ident[] */ + +typedef struct { + unsigned char e_ident[EI_NIDENT]; + Elf32_Half e_type; /* ELFの形式(再配置可能, 実行可能など) */ + Elf32_Half e_machine; /* ファイルで要求されるアーキテクチャ */ + Elf32_Word e_version; /* ELFフォーマットのバージョン(現在は1) */ + Elf32_Addr e_entry; /* プログラムのエントリポイント。指定無しなら0。 */ + Elf32_Off e_phoff; /* プログラムヘッダテーブルのファイル先頭からのオフセット */ + Elf32_Off e_shoff; /* セクションヘッダテーブルのファイル先頭からのオフセット */ + Elf32_Word e_flags; /* プロセッサ固有のフラグ */ + Elf32_Half e_ehsize; /* ELFヘッダのサイズ */ + Elf32_Half e_phentsize; /* 1プログラムヘッダのサイズ */ + Elf32_Half e_phnum; /* プログラムヘッダの数 */ + Elf32_Half e_shentsize; /* 1セクションヘッダのサイズ */ + Elf32_Half e_shnum; /* セクションヘッダの数 */ + Elf32_Half e_shstrndx; /* セクション名文字列テーブルセクションへのインデックス */ +} Elf32_Ehdr; + +/* e_ident[EI_*]の中身定義 */ +#define ELFMAG0 0x7f +#define ELFMAG1 'E' +#define ELFMAG2 'L' +#define ELFMAG3 'F' +#define ELFCLASSNONE 0 /* invalid */ +#define ELFCLASS32 1 /* ARM and Thumb processors use 32-bit ELF. */ +#define ELFCLASS64 2 +#define ELFDATANONE 0 /* invalid */ +#define ELFDATA2LSB 1 /* little-endian */ +#define ELFDATA2MSB 2 /* big-endian */ + + +/* [e_type] */ +#define ET_NONE 0 /* No file type */ +#define ET_REL 1 /* Re-locatable file */ +#define ET_EXEC 2 /* Executable file */ +#define ET_DYN 3 /* Shared object file */ +#define ET_CORE 4 /* Core file */ +#define ET_LOPROC 0xff00 /* Processor-specific */ +#define ET_HIPROC 0xffff /* Processor-specific */ + +/* [e_machine] */ +#define EM_NONE 0 /* No machine */ +#define EM_M32 1 +#define EM_SPARC 2 +#define EM_386 3 +#define EM_68K 4 +#define EM_88K 5 +#define EM_860 7 +#define EM_MIPS 8 +#define EM_MIPS_RS4_BE 10 +#define EM_ARM 40 /* ARM/Thumb Architecture */ + + +/* [e_version] This member identifies the object file version.*/ +#define EV_NONE 0 /* Invalid version */ +#define EV_CURRENT 1 /* Current version */ + + +/* + ARM-specific e_flags + e_flags Field Value Meaning + EF_ARM_HASENTRY (0x02) e_entry contains a program-loader entry point + (see section 4.1.1, Entry points, below). + EF_ARM_SYMSARESORTED (0x04) Each subsection of the symbol table is sorted by symbol value + (see section 4.4.8, Symbol table order, below) + EF_ARM_DYNSYMSUSESEGIDX (0x8) Symbols in dynamic symbol tables that are defined in sections + included in program segment n have st_shndx = n + 1. + (see section 4.4.9, Dynamic symbol table entries, below). + EF_ARM_MAPSYMSFIRST (0x10) Mapping symbols precede other local symbols in the symbol table + (see section 4.4.8, Symbol table order, below). + + EF_ARM_EABIMASK (0xFF000000)(current version is 0x02000000) + This masks an 8-bit version number, the version of the ARM + EABI to which this ELF file conforms. This EABI is version 2. A + value of 0 denotes unknown conformance. +*/ +#define EF_ARM_HASENTRY 0x02 +#define EF_ARM_SYMSARESORTED 0x04 +#define EF_ARM_DYNSYMSUSESEGIDX 0x8 +#define EF_ARM_MAPSYMSFIRST 0x10 +#define EF_ARM_EABIMASK 0xFF000000 + + +/*--------------------------------------------------------- + Program headers + --------------------------------------------------------*/ +typedef struct { + Elf32_Word p_type; + Elf32_Off p_offset; + Elf32_Addr p_vaddr; + Elf32_Addr p_paddr; + Elf32_Word p_filesz; + Elf32_Word p_memsz; + Elf32_Word p_flags; + Elf32_Word p_align; +} Elf32_Phdr; + +/* [p_type] */ +#define PT_NULL 0 /* 使われないエントリで、他のメンバの値の意味は未定義 */ +#define PT_LOAD 1 /* 実行時にロードされるセグメント */ +#define PT_DYNAMIC 2 /* 動的構造体配列を保持するセグメント */ +#define PT_INTERP 3 /* ファイルの解釈に使われるインタプリタのパスを保持するセグメント */ +#define PT_NOTE 4 /* ファイルの解釈には使われない情報を保持するセグメント */ +#define PT_SHLIB 5 /* 予約 */ +#define PT_PHDR 6 /* プログラムヘッダテーブル(プログラムのメモリイメージの一部である場合のみ存在) */ +//#define PT_TLS ? /* スレッド局所記憶領域のテンプレート */ + +#define PT_LOOS 0x60000000 /* OS固有に予約された領域 */ +#define PT_HIOS 0x6fffffff + +#define PT_LOPROC 0x70000000 /* プロセッサ固有に予約された領域 */ +#define PT_HIPROC 0x7fffffff + +/* [p_flags]*/ +#define PF_X 1 /*実行可能*/ +#define PF_W 2 /*書き込み可能*/ +#define PF_R 4 /*読み出し可能*/ +#define PF_ARM_SB 0x10000000 /*The segment contains the location addressed by the static base*/ +#define PF_ARM_PI 0x20000000 /*The segment is position-independent*/ +#define PF_ARM_ENTRY 0x80000000 /*The segment contains the entry point*/ +#define PF_MASKPROC 0xf0000000 + + +/*--------------------------------------------------------- + Section headers + --------------------------------------------------------*/ +typedef struct { + Elf32_Word sh_name; /*セクションヘッダ文字列テーブルセクションのインデックス*/ + Elf32_Word sh_type; /* タイプ(下記定義参照) */ + Elf32_Word sh_flags; + Elf32_Addr sh_addr; /* */ + Elf32_Off sh_offset; /* ファイルの先頭からのオフセット */ + Elf32_Word sh_size; /* バイト単位のサイズ */ + Elf32_Word sh_link; /* sh_typeによって値の意味が変わる */ + Elf32_Word sh_info; /* sh_typeによって値の意味が変わる */ + Elf32_Word sh_addralign; /* アラインメント制限(0or1で制限なし,4で4ByteAlign) */ + Elf32_Word sh_entsize; /* 固定サイズのエントリテーブルがある場合、1要素のサイズ */ +} Elf32_Shdr; + +/* sh_addr mod sh_addralign = 0 でなければならない */ + +/* Section Types, [sh_type] */ +#define SHT_NULL 0 +#define SHT_PROGBITS 1 +#define SHT_SYMTAB 2 +#define SHT_STRTAB 3 +#define SHT_RELA 4 +#define SHT_HASH 5 +#define SHT_DYNAMIC 6 +#define SHT_NOTE 7 +#define SHT_NOBITS 8 +#define SHT_REL 9 +#define SHT_SHLIB 10 +#define SHT_DYNSYM 11 +#define SHT_LOPROC 0x70000000 +#define SHT_HIPROC 0x7fffffff +#define SHT_LOUSER 0x80000000 +#define SHT_HIUSER 0xffffffff + + +/* [sh_flags] */ +#define SHF_WRITE 0x1 +#define SHF_ALLOC 0x2 +#define SHF_EXECINSTR 0x4 +#define SHF_MASKPROC 0xf0000000 +/* ARM-EABI-specific */ +#define SHF_ENTRYSECT 0x10000000 /* The section contains an entry point. */ +#define SHF_COMDEF 0x80000000 /* The section may be multiply defined in the input to a link step. */ +/* others */ +#define SHF_LINK_ORDER 0x80 + +/*セクションインデックス*/ +//Sym->st_shndxなど +#define SHN_UNDEF 0 +#define SHN_LORESERVE 0xff00 +#define SHN_LOPROC 0xff00 +#define SHN_HIPROC 0xff1f +#define SHN_ABS 0xfff1 +#define SHN_COMMON 0xfff2 +#define SHN_HIRESERVE 0xffff + + +//ここからはヘッダでなく実体データ構造 + +/*--------------------------------------------------------- + Symbol Table Entry + --------------------------------------------------------*/ +typedef struct { + Elf32_Word st_name; /* シンボル文字列テーブルのインデックス */ + Elf32_Addr st_value; /* おそらく関連するセクション内でのオフセット値 */ + Elf32_Word st_size; /* サイズがないか、不明な場合は 0 */ + unsigned char st_info; /* バインド と タイプ */ + unsigned char st_other; /* 現在は 0 が入る */ + Elf32_Half st_shndx; /* 関連するセクションヘッダテーブルのインデックス */ +} Elf32_Sym; + + +/* st_info */ +#define ELF32_ST_BIND(i) ((i)>>4) +#define ELF32_ST_TYPE(i) ((i)&0xf) +#define ELF32_ST_INFO(b,t) (((b)<<4)+((t)&0xf)) + +/* st_info の BIND */ +#define STB_LOCAL 0 +#define STB_GLOBAL 1 +#define STB_WEAK 2 +#define STB_LOPROC 13 +#define STB_HIPROC 15 + +/* st_info の TYPE */ +#define STT_NOTYPE 0 /*未定義*/ +#define STT_OBJECT 1 /*データオブジェクト*/ +#define STT_FUNC 2 +#define STT_SECTION 3 +#define STT_FILE 4 +#define STT_LOPROC 13 +#define STT_HIPROC 15 + + +/*--------------------------------------------------------- + Relocation Entry + --------------------------------------------------------*/ +typedef struct { + Elf32_Addr r_offset; + Elf32_Word r_info; +} Elf32_Rel; + +typedef struct { + Elf32_Addr r_offset; + Elf32_Word r_info; + Elf32_Sword r_addend; +} Elf32_Rela; + +#define ELF32_R_SYM(i) ((i)>>8) +#define ELF32_R_TYPE(i) ((unsigned char)(i)) +#define ELF32_R_INFO(s,t) (((s)<<8)+(unsigned char)(t)) + + +/* r_info の TYPE */ +#define R_ARM_NONE 0 /* Any No relocation. Encodes dependencies between sections. */ +#define R_ARM_PC24 1 /* ARM B/BL S . P + A */ +#define R_ARM_ABS32 2 /* 32-bit word S + A */ +#define R_ARM_REL32 3 /* 32-bit word S . P + A */ +#define R_ARM_PC13 4 /* ARM LDR r, [pc,…] S . P + A */ +#define R_ARM_ABS16 5 /* 16-bit half-word S + A */ +#define R_ARM_ABS12 6 /* ARM LDR/STR S + A */ +#define R_ARM_THM_ABS5 7 /* Thumb LDR/STR S + A */ +#define R_ARM_ABS8 8 /* 8-bit byte S + A */ +#define R_ARM_SBREL32 9 /* 32-bit word S . B + A */ +#define R_ARM_THM_PC22 10 /* Thumb BL pair S . P+ A */ +#define R_ARM_THM_PC8 11 /* Thumb LDR r, [pc,…] S . P + A */ +#define R_ARM_AMP_VCALL9 12 /* AMP VCALL Obsolete.SA-1500 only. */ +#define R_ARM_SWI24 13 /* ARM SWI S + A */ +#define R_ARM_THM_SWI8 14 /* Thumb SWI S + A */ +#define R_ARM_XPC25 15 /* ARM BLX S . P+ A */ +#define R_ARM_THM_XPC22 16 /* Thumb BLX pair S . P+ A */ + +/* 17-31, reserved to ARM Linux */ +//17-19 Reserved to ARM LINUX +#define R_ARM_COPY 20 /* 32 bit word Copy symbol at dynamic link time. */ +#define R_ARM_GLOB_DAT 21 /* 32 bit word Create GOT entry. */ +#define R_ARM_JUMP_SLOT 22 /* 32 bit word Create PLT entry. */ +#define R_ARM_RELATIVE 23 /* 32 bit word Adjust by program base. */ +#define R_ARM_GOTOFF 24 /* 32 bit word Offset relative to start of GOT. */ +#define R_ARM_GOTPC 25 /* 32 bit word Insert address of GOT. */ +#define R_ARM_GOT32 26 /* 32 bit word Entry in GOT. */ +#define R_ARM_PLT32 27 /* ARM BL Entry in PLT. */ + +/* 28-31 Reserved to ARM LINUX */ +#define R_ARM_ALU_PCREL_7_0 32 /* ARM ADD/SUB (S . P + A) & 0x000000FF */ +#define R_ARM_ALU_PCREL_15_8 33 /* ARM ADD/SUB (S . P + A) & 0x0000FF00 */ +#define R_ARM_ALU_PCREL_23_15 34 /* ARM ADD/SUB (S . P + A) & 0x00FF0000 */ +#define R_ARM_LDR_SBREL_11_0 35 /* ARM LDR/STR (S . B + A) & 0x00000FFF */ +#define R_ARM_ALU_SBREL_19_12 36 /* ARM ADD/SUB (S . B + A) & 0x000FF000 */ +#define R_ARM_ALU_SBREL_27_20 37 /* ARM ADD/SUB (S . B + A) & 0x0FF00000 */ + +#define R_ARM_TARGET1 38 +#define R_ARM_ROSEGREL32 39 +#define R_ARM_V4BX 40 +#define R_ARM_TARGET2 41 +#define R_ARM_PREL31 42 + +/* 96-111, reserved to ARM g++ */ +#define R_ARM_GNU_VTENTRY 100 /* 32 bit word Record C++ vtable entry. */ +#define R_ARM_GNU_VTINHERIT 101 /* 32 bit word Record C++ member usage. */ +#define R_ARM_THM_PC11 102 /* Thumb B S . P + A */ +#define R_ARM_THM_PC9 103 /* Thumb B S . P + A */ + +/* 112-127, reserved for private experiments */ + +/* 128-248, reserved to ARM */ +#define R_ARM_RXPC25 249 /* ARM BLX (ΔS . ΔP) + A #define For calls between program segments. */ +#define R_ARM_RSBREL32 250 /* Word (ΔS . ΔSB) + A For an offset from SB, the static base. */ +#define R_ARM_THM_RPC22 251 /* Thumb BL/BLX pair (ΔS . ΔP) + A For calls between program segments. */ +#define R_ARM_RREL32 252 /* Word (ΔS . ΔP) + A For on offset between two segments. */ +#define R_ARM_RABS32 253 /* Word ΔS + A For the address of a location in the target segment. */ +#define R_ARM_RPC24 254 /* ARM B/BL (ΔS . ΔP) + A For calls between program segments. */ +#define R_ARM_RBASE 255 /* None None.Identifies the segment being relocated by the following + relocation directives. The ARM EABI poses two problems for relocating + executables and shared objects encoded in */ + + +// shirait +#define R_ARM_LDR_PC_G0 4 //LDR + +#define R_ARM_ABS12 6 //LDR, STR + +#define R_ARM_THM_CALL 10 //R_ARM_THM_PC22と同じ + +#define R_ARM_CALL 28 //BL/BLX +#define R_ARM_JUMP24 29 //B/BL +#define R_ARM_THM_JUMP24 30 + +#define R_ARM_MOVW_ABS_NC 43 //MOVW +#define R_ARM_MOVT_ABS 44 //MOVT +#define R_ARM_MOVW_PREL_NC 45 //MOVW +#define R_ARM_MOVT_PREL 46 //MOVT + +#define R_ARM_ALU_PC_G0_NC 57 //ADD, SUB +#define R_ARM_ALU_PC_G0 58 //ADD, SUB +#define R_ARM_ALU_PC_G1_NC 59 //ADD, SUB +#define R_ARM_ALU_PC_G1 60 //ADD, SUB +#define R_ARM_ALU_PC_G2 61 //ADD, SUB +#define R_ARM_LDR_PC_G1 62 //LDR, STR, LDRB, STRB +#define R_ARM_LDR_PC_G2 63 //LDR, STR, LDRB, STRB +#define R_ARM_LDRS_PC_G0 64 //LDRD, STRD, LDRH, STRH, LDRSH, LDRSB +#define R_ARM_LDRS_PC_G1 65 //LDRD, STRD, LDRH, STRH, LDRSH, LDRSB +#define R_ARM_LDRS_PC_G2 66 //LDRD, STRD, LDRH, STRH, LDRSH, LDRSB +#define R_ARM_LDC_PC_G0 67 //LDC, STC +#define R_ARM_LDC_PC_G1 68 //LDC, STC +#define R_ARM_LDC_PC_G2 69 //LDC, STC +#define R_ARM_ALU_SB_G0_NC 70 //ADD, SUB +#define R_ARM_ALU_SB_G0 71 //ADD, SUB +#define R_ARM_ALU_SB_G1_NC 72 //ADD, SUB +#define R_ARM_ALU_SB_G1 73 //ADD, SUB +#define R_ARM_ALU_SB_G2 74 //ADD, SUB +#define R_ARM_LDR_SB_G0 75 //LDR, STR, LDRB, STRB +#define R_ARM_LDR_SB_G1 76 //LDR, STR, LDRB, STRB +#define R_ARM_LDR_SB_G2 77 //LDR, STR, LDRB, STRB +#define R_ARM_LDRS_SB_G0 78 //LDRD, STRD, LDRH, STRH, LDRSH, LDRSB +#define R_ARM_LDRS_SB_G1 79 //LDRD, STRD, LDRH, STRH, LDRSH, LDRSB +#define R_ARM_LDRS_SB_G2 80 //LDRD, STRD, LDRH, STRH, LDRSH, LDRSB +#define R_ARM_LDC_SB_G0 81 //LDC, STC +#define R_ARM_LDC_SB_G1 82 //LDC, STC +#define R_ARM_LDC_SB_G2 83 //LDC, STC +#define R_ARM_MOVW_BREL_NC 84 //MOVW +#define R_ARM_MOVT_BREL 85 //MOVT +#define R_ARM_MOVW_BREL 86 //MOVW + +#define R_ARM_GOT_BREL12 97 //LDR +#define R_ARM_GOTOFF12 98 //LDR, STR + +#define R_ARM_TLS_LDO12 109 //LDR, STR +#define R_ARM_TLS_LE12 110 //LDR, STR +#define R_ARM_TLS_TE12GP 111 //LDR + + + +/*--------------------------------------------------------- + Dynamic Section elf_v1.2 + --------------------------------------------------------*/ +typedef struct { + Elf32_Sword d_tag; + union { + Elf32_Word d_val; + Elf32_Addr d_ptr; + } d_un; +} Elf32_Dyn; + + +/* Additional symbol types for Thumb. */ +#define STT_ARM_TFUNC STT_LOPROC /* A Thumb function. */ +#define STT_ARM_16BIT STT_HIPROC /* A Thumb label. */ + + + + + + + + + +/*--------------------------------------------------------- + ELFヘッダを読み出す + --------------------------------------------------------*/ +void *ELF_LoadELFHeader(const void *buf, Elf32_Ehdr *ehdr); + + + +#endif /* ELF_H_ */ + diff --git a/build/tools/makenandfirm/format_nlist.h b/build/tools/makenandfirm/format_nlist.h new file mode 100644 index 00000000..e832edc0 --- /dev/null +++ b/build/tools/makenandfirm/format_nlist.h @@ -0,0 +1,53 @@ +/*---------------------------------------------------------------------------* + Project: TwlFirm - tools - makenandfirm + File: format_nlist.h + + Copyright 2007 Nintendo. All rights reserved. + + These coded instructions, statements, and computer programs contain + proprietary information of Nintendo of America Inc. and/or Nintendo + Company Ltd., and are protected by Federal copyright law. They may + not be disclosed to third parties or copied or duplicated in any form, + in whole or in part, without the prior written consent of Nintendo. + + $Log: $ + $NoKeywords: $ + *---------------------------------------------------------------------------*/ +#ifndef FORMAT_NLIST_H_ +#define FORMAT_NLIST_H_ + +#include +#include "misc.h" +#include "path.h" + +#define SPECFILE_MAGIC_ID "#NANDSF" + +#define CRC16_INIT_VALUE 0xffff + +//--------------------------------------------------------------------------- +// Banner Spec File +//--------------------------------------------------------------------------- + +// Command List +typedef struct +{ + char *string; + BOOL (*funcp) (char *, int num); + +} +tCommandDesc; + + +// F Command +typedef struct +{ + u32 offsetStart; + u32 offsetEnd; + u32 padding; + char fullPathSrc[FILENAME_MAX]; + +} +tFileDesc; + + +#endif // FORMAT_NLIST_H_ diff --git a/build/tools/makenandfirm/format_rom.h b/build/tools/makenandfirm/format_rom.h new file mode 100644 index 00000000..43decb01 --- /dev/null +++ b/build/tools/makenandfirm/format_rom.h @@ -0,0 +1,50 @@ +/*---------------------------------------------------------------------------* + Project: TwlFirm - tools - makenandfirm + File: format_rom.h + + Copyright 2007 Nintendo. All rights reserved. + + These coded instructions, statements, and computer programs contain + proprietary information of Nintendo of America Inc. and/or Nintendo + Company Ltd., and are protected by Federal copyright law. They may + not be disclosed to third parties or copied or duplicated in any form, + in whole or in part, without the prior written consent of Nintendo. + + $Log: $ + $NoKeywords: $ + *---------------------------------------------------------------------------*/ +#ifndef FORMAT_ROM_H_ +#define FORMAT_ROM_H_ + +#include "misc.h" +#include + + +#define DEFAULT_ALIGN 0x200 +#define FIRM_ALIGN DEFAULT_ALIGN +#define FIRM_ALIGN_MASK (FIRM_ALIGN - 1) + +#define DEFAULT_HOSTROOT "." +#define DEFAULT_ROOT "/" + +#define DEFAULT_REJECT_1 "CVS" +#define DEFAULT_REJECT_2 "vssver.scc" + +#define FORMAT_VERSION "1.0" + +#define ENTRYNAME_MAX 127 + +#define DEFAULT_LISTFILE "default.nlf" + +#define DEFAULT_NANDFIRM_SUFFIX ".nand" +#define DEFAULT_SPECFILE_SUFFIX ".nandsf" + +/*===========================================================================* + * ROM FORMAT + *===========================================================================*/ + +//--------------------------------------------------------------------------- +// ROM HEADER +//--------------------------------------------------------------------------- + +#endif //FORMAT_ROM_H_ diff --git a/build/tools/makenandfirm/format_sign.h b/build/tools/makenandfirm/format_sign.h new file mode 100644 index 00000000..a965863b --- /dev/null +++ b/build/tools/makenandfirm/format_sign.h @@ -0,0 +1,31 @@ +/*---------------------------------------------------------------------------* + Project: TwlFirm - SS + File: format_sign.h + + Copyright 2006 Nintendo. All rights reserved. + + These coded instructions, statements, and computer programs contain + proprietary information of Nintendo of America Inc. and/or Nintendo + Company Ltd., and are protected by Federal copyright law. They may + not be disclosed to third parties or copied or duplicated in any form, + in whole or in part, without the prior written consent of Nintendo. + + $Log: $ + $NoKeywords: $ + *---------------------------------------------------------------------------*/ +#ifndef FIRM_MAKENANDFIRM_ACSIGN_FORMAT_H_ +#define FIRM_MAKENANDFIRM_ACSIGN_FORMAT_H_ + +#include "format_rom.h" + + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +/* FIRM_MAKENANDFIRM_ACSIGN_FORMAT_H_ */ +#endif diff --git a/build/tools/makenandfirm/makenandfirm.c b/build/tools/makenandfirm/makenandfirm.c new file mode 100644 index 00000000..48d08797 --- /dev/null +++ b/build/tools/makenandfirm/makenandfirm.c @@ -0,0 +1,105 @@ +/*---------------------------------------------------------------------------* + Project: TwlFirm - tools - makenandfirm + File: makenandfirm.c + + Copyright 2007 Nintendo. All rights reserved. + + These coded instructions, statements, and computer programs contain + proprietary information of Nintendo of America Inc. and/or Nintendo + Company Ltd., and are protected by Federal copyright law. They may + not be disclosed to third parties or copied or duplicated in any form, + in whole or in part, without the prior written consent of Nintendo. + + $Log: $ + $NoKeywords: $ + *---------------------------------------------------------------------------*/ +#include +#include +#include // strcasecmp() +#include // getopt() +#include "makenandfirm.h" +#include "format_rom.h" +#include "path.h" +#include "defval.h" +#include "version.h" + +static int makenandfirm(const char *specFile, const char *nandFile); + +//--------------------------------------------------------------------------- +// Main +//--------------------------------------------------------------------------- + +int main(int argc, char *argv[]) +{ + int n; + int narg; + char *nandfirmFile; + + InitAppName(argv[0]); + + while ((n = getopt(argc, argv, "D:hvpd")) != -1) + { + switch (n) + { + case 'h': + case 'v': + goto usage; + + case 'D': + AddDefVal(optarg); + break; + + case 'p': + PrintMode = TRUE; + break; + + case 'd': + DebugMode = TRUE; + break; + + default: + break; + } + } + + narg = argc - optind; + if (narg > 0) + { + // Make SpecFile->NandfirmFile + nandfirmFile = + strdup(narg > + 1 ? argv[optind + 1] : ChangeSuffix(argv[optind], DEFAULT_NANDFIRM_SUFFIX)); + return makenandfirm(argv[optind], nandfirmFile); + } + + usage: + { + char *makenandfirm = GetAppName(); + + fprintf(stderr, + "NITRO-SDK Development Tool - %s - Make nandfirm file \n" + "Build %lu\n\n" + "Usage: %s [-phv] [-DNAME=VALUE ...] SPECFILE [NANDFIRMFILE]\n\n", + makenandfirm, SDK_DATE_OF_LATEST_FILE, makenandfirm); + } + return 1; +} + + +//--------------------------------------------------------------------------- +// makenandfirm +//--------------------------------------------------------------------------- + +static int makenandfirm(const char *specFile, const char *nandFile) +{ + debug_printf("makenandfirm(): '%s' -> '%s'\n", specFile, nandFile); + + // Check identical + if (specFile && nandFile && !strcasecmp(specFile, nandFile)) + { + error("nandfirm spec file is identical '%s'", nandFile); + return 1; + } + + return OutputNandfirmFile(specFile, nandFile) ? 0 : 1; +} diff --git a/build/tools/makenandfirm/makenandfirm.h b/build/tools/makenandfirm/makenandfirm.h new file mode 100644 index 00000000..69abe085 --- /dev/null +++ b/build/tools/makenandfirm/makenandfirm.h @@ -0,0 +1,23 @@ +/*---------------------------------------------------------------------------* + Project: TwlFirm - tools - makenandfirm + File: makenandfirm.h + + Copyright 2007 Nintendo. All rights reserved. + + These coded instructions, statements, and computer programs contain + proprietary information of Nintendo of America Inc. and/or Nintendo + Company Ltd., and are protected by Federal copyright law. They may + not be disclosed to third parties or copied or duplicated in any form, + in whole or in part, without the prior written consent of Nintendo. + + $Log: $ + $NoKeywords: $ + *---------------------------------------------------------------------------*/ +#ifndef MAKENANDFIRM_H_ +#define MAKENANDFIRM_H_ + +#include "misc.h" + +BOOL OutputNandfirmFile(const char *specFile, const char *nandFile); + +#endif //MAKENANDFIRM_H_ diff --git a/build/tools/makenandfirm/misc.c b/build/tools/makenandfirm/misc.c new file mode 100644 index 00000000..56f06bf6 --- /dev/null +++ b/build/tools/makenandfirm/misc.c @@ -0,0 +1,627 @@ +/*---------------------------------------------------------------------------* + Project: TwlFirm - tools - makenandfirm + File: misc.c + + Copyright 2007 Nintendo. All rights reserved. + + These coded instructions, statements, and computer programs contain + proprietary information of Nintendo of America Inc. and/or Nintendo + Company Ltd., and are protected by Federal copyright law. They may + not be disclosed to third parties or copied or duplicated in any form, + in whole or in part, without the prior written consent of Nintendo. + + $Log: $ + $NoKeywords: $ + *---------------------------------------------------------------------------*/ +#include +#include // calloc() +#include // free(), exit() +#include // setmode() +#include // stat() +#include // setmode() +#include // strlen() +#include // va_start(),va_end() +#include // localtime() + +#include +#include "misc.h" + +BOOL DebugMode = FALSE; +BOOL PrintMode = FALSE; +char *PubkeyFileName = NULL; + +/*---------------------------------------------------------------------------* + * File Output Utilities + * + * BOOL OpenFile( const char* filename ) + * void CloseFile( void ) + * BOOL CheckResult( void ) + * void PutBuffer( const void* ptr, int len ) + * void PutByte( u8 c ) + * void PutWord( u16 c ) + * void PutWord( u32 c ) + * void PutString( const char *str ) + *---------------------------------------------------------------------------*/ + +static FILE *OutFile = NULL; +static const char *FileName = NULL; +static BOOL Status = FALSE; + + +// +// File Open +// + +BOOL OpenFile(const char *filename) +{ + if (OutFile) + CloseFile(); + + if (filename) + { + if (NULL == (OutFile = fopen(filename, "wb+"))) + { + error("Can't write '%s'", filename); + Status = FALSE; + return FALSE; + } + } + else + { + setmode(1, O_BINARY); + OutFile = stdout; // out to console if filename == NULL + } + FileName = filename; + Status = TRUE; + + return TRUE; +} + + +// +// File Close +// + +BOOL CloseFile(void) +{ + if (OutFile) + { + if (FileName) + { + if (fclose(OutFile) == -1) + { + error("Can't close '%s'", FileName); + Status = FALSE; + } + } + else + { + setmode(1, O_TEXT); + } + } + OutFile = NULL; + + return Status; +} + + +// +// File Seek +// + +void SeekFile(long pos) +{ + if (OutFile && fseek(OutFile, pos, SEEK_SET)) + { + error("Can't seek '%s'", FileName ? FileName : ""); + CloseFile(); + Status = FALSE; + } +} + + +// +// Error Check +// + +BOOL CheckResult(void) +{ + return Status; +} + + +// +// Delete outfile +// + +void DeleteOutFile(void) +{ + // Delete outfile + if (FileName) + { + debug_printf("Delete '%s'\n", FileName); + (void)unlink(FileName); + FileName = NULL; + } + return; +} + + +// +// Buffer Output +// + +void PutBuffer(const void *ptr, int len) /* If error, close file */ +{ + if (OutFile && len != fwrite(ptr, 1, len, OutFile)) + { + error("Can't write buffer to '%s'", FileName ? FileName : ""); + CloseFile(); + Status = FALSE; + } +} + +// +// Buffer Input +// + +void GetBuffer(void *ptr, int len) /* If error, close file */ +{ + if (OutFile && len != fread(ptr, 1, len, OutFile)) + { + error("Can't read '%s'", FileName ? FileName : ""); + CloseFile(); + Status = FALSE; + } +} + +// +// Byte/Half/Word Output +// + +void PutByte(u8 c) +{ + PutBuffer(&c, 1); +} +void PutHalf(u16 c) +{ + PutBuffer(&c, 2); +} +void PutWord(u32 c) +{ + PutBuffer(&c, 4); +} +void PutString(const char *str) +{ + PutBuffer(str, strlen(str)); +} + + +// +// Printf +// + +void PrintString(const char *fmt, ...) +{ + char *buffer; + va_list va; + int nchars; + int bufsize = FILENAME_MAX; + + while (1) + { + buffer = Alloc(bufsize); + va_start(va, fmt); + nchars = vsnprintf(buffer, bufsize, fmt, va); + va_end(va); + + if (0 <= nchars && nchars < bufsize) + { + break; + } + + Free(&buffer); + bufsize *= 2; + } + + if (nchars > 0) + { + PutBuffer(buffer, nchars); + } + Free(&buffer); +} + + +/*---------------------------------------------------------------------------* + * File Read/Write Utilities + * + * int ReadFile( const char* filename, void** buffer ) + * + * Read a file to buffer allocated internally + * Return read size + * Add '\0' at tail of file for help to handle text file + * + * BOOL WriteFile( const char* filename, void** buffer, int size ) + *---------------------------------------------------------------------------*/ + +int ReadFile(const char *filename, void *filebuffer, int size) +{ + FILE *fp; + struct stat fileStat; + int fileSize, readSize; + void **buffer = (void **)filebuffer; + + /* Check file */ + if (stat(filename, &fileStat) || !S_ISREG(fileStat.st_mode)) + { + goto error; + } + fileSize = fileStat.st_size; + if (fileSize < 0) + { + goto error; + } + + /* Open file */ + fp = fopen(filename, "rb"); + if (!fp) + { + goto error; + } + + if (size && (size < fileSize)) + { + readSize = size; + } + else + { + readSize = fileSize; + } + + /* Read file */ + *buffer = Alloc(readSize + 1); /* error handle is done in Alloc() */ + /* size+1 for '\0' to handle text file */ + if (readSize != fread(*buffer, sizeof(char), readSize, fp)) + { + goto error_free_close; + } + + (*(char **)buffer)[readSize] = '\0'; /* Works as terminater if file is text file */ + + /* Close file */ + fclose(fp); + return readSize; + + error_free_close: + Free(buffer); + fclose(fp); + error: + error("Can't read '%s'", filename); + return -1; +} + + +int ReadFileWithPadding(const char *filename, void *filebuffer, int size, int boundary) +{ + FILE *fp; + struct stat fileStat; + int fileSize, readSize, padSize; + void **buffer = (void **)filebuffer; + + if (!boundary) + { + return ReadFile(filename, buffer, size); + } + + /* Check file */ + if (stat(filename, &fileStat) || !S_ISREG(fileStat.st_mode)) + { + goto error; + } + fileSize = fileStat.st_size; + if (fileSize < 0) + { + goto error; + } + + /* Open file */ + fp = fopen(filename, "rb"); + if (!fp) + { + goto error; + } + + if (size && (size < fileSize)) + { + readSize = size; + } + else + { + readSize = fileSize; + } + + padSize = (boundary - (readSize & (boundary-1))) & (boundary-1); + + /* Read file */ + *buffer = Alloc(readSize + padSize); /* error handle is done in Alloc() */ + /* size+1 for '\0' to handle text file */ + if (readSize != fread(*buffer, sizeof(char), readSize, fp)) + { + goto error_free_close; + } + + memset((char*)*buffer + readSize, 0, padSize); + + /* Close file */ + fclose(fp); + return readSize + padSize; + + error_free_close: + Free(buffer); + fclose(fp); + error: + error("Can't read '%s'", filename); + return -1; +} + + +BOOL WriteFile(const char *filename, void *buffer, int size) +{ + debug_printf("WriteFile %s\n", filename); + + (void)OpenFile(filename); + PutBuffer(buffer, size); + CloseFile(); + Free(&buffer); + return CheckResult(); +} + + +/*---------------------------------------------------------------------------* + * Time Format Utilities + * + * char* GetGMTime( const time_t time ) Show GMT + * char* GetTime( const time_t time ) Show local Time + *---------------------------------------------------------------------------*/ + +char *GetGMTime(const time_t time) +{ + static char timebuffer[32]; + strftime(timebuffer, sizeof(timebuffer), "%y/%m/%d-%H:%M:%S", gmtime(&time)); + return timebuffer; +} + + +char *GetTime(const time_t time) +{ + static char timebuffer[32]; + strftime(timebuffer, sizeof(timebuffer), "%y/%m/%d-%H:%M:%S", localtime(&time)); + return timebuffer; +} + + +/*---------------------------------------------------------------------------* + * Memory Allocation Utilities + * + * void* Alloc( size_t size ) + *---------------------------------------------------------------------------*/ + +void *Alloc(size_t size) +{ + void *t = calloc(1, size); + + if (t == NULL) + { + error("Can't allocate memory."); + exit(10); + } + return t; +} + + +void Free(void *p) +{ + void **ptr = (void **)p; + + if (*ptr) + { + free(*ptr); + (*ptr) = NULL; + } +} + + +/*---------------------------------------------------------------------------* + * VBuffer + * + * void PutVBuffer( VBuffer* vbuf, char c ) + *---------------------------------------------------------------------------*/ + +void PutVBuffer(VBuffer * vbuf, char c) +{ + int size; + char *tmp_buffer; + + if (vbuf->buffer == 0) + { + vbuf->size = VBUFFER_INITIAL_SIZE; + vbuf->buffer = Alloc(vbuf->size); // buffer is CALLOCed + } + size = strlen(vbuf->buffer); + + if (size >= vbuf->size - 1) + { + // Need buffer expansion + vbuf->size *= 2; + tmp_buffer = Alloc(vbuf->size); // buffer is CALLOCed + strcpy(tmp_buffer, vbuf->buffer); + Free(&vbuf->buffer); + vbuf->buffer = tmp_buffer; + } + vbuf->buffer[size] = c; + return; +} + +char *GetVBuffer(VBuffer * vbuf) +{ + return vbuf->buffer; +} + +void InitVBuffer(VBuffer * vbuf) +{ + vbuf->buffer = 0; + vbuf->size = 0; +} + +void FreeVBuffer(VBuffer * vbuf) +{ + Free(&vbuf->buffer); +} + + +/*---------------------------------------------------------------------------* + * File Path Utilities + * + * char* ChangeBackSlash( char* path ) + *---------------------------------------------------------------------------*/ + +char *ChangeBackSlash(char *path) +{ + char *p = path; + + while (*p) + { + if (*p == '\\') + { + *p = '/'; + } + p++; + } + return path; +} + + +/*---------------------------------------------------------------------------* + * Math + * + * u16 CalcCRC16( u16 start, u8 *data, int size ) + *---------------------------------------------------------------------------*/ + +static u16 crc16_table[16] = { + 0x0000, 0xCC01, 0xD801, 0x1400, + 0xF001, 0x3C00, 0x2800, 0xE401, + 0xA001, 0x6C00, 0x7800, 0xB401, + 0x5000, 0x9C01, 0x8801, 0x4400 +}; + +u16 CalcCRC16(u16 start, u8 *data, int size) +{ + u16 r1; + u16 total = start; + + while (size-- > 0) + { + // 下位4bit + r1 = crc16_table[total & 0xf]; + total = (total >> 4) & 0x0fff; + total = total ^ r1 ^ crc16_table[*data & 0xf]; + + // 上位4bit + r1 = crc16_table[total & 0xf]; + total = (total >> 4) & 0x0fff; + total = total ^ r1 ^ crc16_table[(*data >> 4) & 0xf]; + + data++; + } + return total; +} + + +/*---------------------------------------------------------------------------* + * for firm header + * + *---------------------------------------------------------------------------*/ + +static u8 ConvertAlign( u32 ofs ) +{ + u8 i; + + ofs /= 4; + for (i=0; i<7; i++) + { + if ( ofs & 1 ) + { + break; + } + ofs >>= 1; + } + + return i; +} + +tROMAddrConvType ConvertHeaderRamAddr( s32 p ) +{ + tROMAddrConvType retval; + + retval.align = ConvertAlign( p ); + retval.address = (u16)((p - HW_WRAM)/(4< + +typedef enum +{ + FALSE = 0, + TRUE = 1 +} +BOOL; + +typedef unsigned char u8; +typedef unsigned short int u16; +typedef unsigned long int u32; +typedef unsigned long long u64; +typedef signed char s8; +typedef signed short int s16; +typedef signed long int s32; +typedef signed long long s64; + +#define error(...) do { fprintf(stderr, "Error: "); \ + fprintf(stderr, __VA_ARGS__); \ + fprintf(stderr, "\n"); } while(0) + +#define warning(...) do { fprintf(stderr, "Warning: "); \ + fprintf(stderr, __VA_ARGS__); \ + fprintf(stderr, "\n"); } while(0) + +BOOL OpenFile(const char *filename); +BOOL CloseFile(void); +void SeekFile(long pos); +BOOL CheckResult(void); +void DeleteOutFile(void); +void PutBuffer(const void *ptr, int len); +void GetBuffer(void *ptr, int len); +void PutByte(u8 c); +void PutHalf(u16 c); +void PutWord(u32 c); +void PutString(const char *str); +void PrintString(const char *fmt, ...); + +#define READ_ALL 0 +int ReadFile(const char *filename, void *filebuffer, int size); +int ReadFileWithPadding(const char *filename, void *filebuffer, int size, int boundary); +BOOL WriteFile(const char *filename, void *buffer, int size); + +char *GetGMTime(const time_t time); +char *GetTime(const time_t time); + +void *Alloc(size_t size); +void Free(void *p); + +typedef struct +{ + char *buffer; + int size; +} +VBuffer; + +#define VBUFFER_INITIAL_SIZE 1024 +void InitVBuffer(VBuffer * vbuf); +void FreeVBuffer(VBuffer * vbuf); +void PutVBuffer(VBuffer * vbuf, char c); +char *GetVBuffer(VBuffer * vbuf); + +char *ChangeBackSlash(char *path); + +u16 CalcCRC16(u16 start, u8 *data, int size); +const char *WrapNull(const char *str); + +typedef struct +{ + u16 address; + u8 align; +} +tROMAddrConvType; + +tROMAddrConvType ConvertHeaderRamAddr( s32 p ); +tROMAddrConvType ConvertHeaderRomOffset( s32 p ); +u16 ConvertHeaderRomOffsetAlign( s32 p, u32 align ); + +typedef union +{ + struct + { + u32 sign:1; + u32 header_hash:1; + u32 arm9_hash:1; + u32 arm7_hash:1; + u32 hash_table_hash:1; + u32 final_hash:1; + u32 header_footer:1; + } + e; + u32 raw; +} +tErrorFlags; + +extern BOOL DebugMode; +extern BOOL PrintMode; +extern char *PubkeyFileName; +void debug_printf(const char *str, ...); +void debug_printf2(const char *str, ...); + +#endif //MISC_H_ diff --git a/build/tools/makenandfirm/out_nandfirm.c b/build/tools/makenandfirm/out_nandfirm.c new file mode 100644 index 00000000..0cdfa4da --- /dev/null +++ b/build/tools/makenandfirm/out_nandfirm.c @@ -0,0 +1,979 @@ +/*---------------------------------------------------------------------------* + Project: TwlFirm - tools - makenandfirm + File: out_nandfirm.c + + Copyright 2007 Nintendo. All rights reserved. + + These coded instructions, statements, and computer programs contain + proprietary information of Nintendo of America Inc. and/or Nintendo + Company Ltd., and are protected by Federal copyright law. They may + not be disclosed to third parties or copied or duplicated in any form, + in whole or in part, without the prior written consent of Nintendo. + + $Log: $ + $NoKeywords: $ + *---------------------------------------------------------------------------*/ +#include +#include // atoi() +#include // strcmp() +#include // isprint() +#include // chdir() +#include +#include // UCHAR_MAX +#include +#include // stat() +#include "elf.h" +#include "misc.h" +#include "defval.h" +#include "format_rom.h" +#include "format_nlist.h" +#include "makenandfirm.h" +#include "format_sign.h" +#include "acsign_nand.h" +#include "compress.h" + +#define SDK_ASM +#include + +#include +#include "../acsign/aes2.h" + +#define SBIN9CMD "ARM9_SBIN" +#define SBIN7CMD "ARM7_SBIN" +#define ELF9CMD "ARM9_ELF" +#define ELF7CMD "ARM7_ELF" +#define COMP9CMD "ARM9_COMP" +#define COMP7CMD "ARM7_COMP" +#define DECOMPCMD "DECOMP_PROC" +#define ARM9X2CMD "ARM9_X2" +#define VERCMD "VERSION" +#define RSAKEYCMD "RSA_KEY" +#define OUTKEYCMD "OUT_KEY" +#define WREGCMD "WRAM_RBIN" +#define ENDKEYCMD "ENC_KEYINFO" +#define NCDCMD "NCD_ROMOFS" +#define MIRRORCMD "MIRROR_OFS" +#define ERRCMD "ERROR" + +static BOOL ConstructNandfirmFile(char * specFile); +static BOOL ReadSbinFile(const char *fileName, void* minfo, void* minfo2, void* hash, BOOL comp); +static BOOL ReadKeyFile(const char *fileName); +static BOOL ReadWramRegFile(const char *fileName); +static s32 GetRamAddr(const char *fileName); + +static BOOL EncryptBuffer(char *buffer, int length); + +static BOOL Sbin9_Command(char * line, int num); +static BOOL Sbin7_Command(char * line, int num); +static BOOL Elf9_Command(char * line, int num); +static BOOL Elf7_Command(char * line, int num); +static BOOL Comp9_Command(char * line, int num); +static BOOL Comp7_Command(char * line, int num); +static BOOL Decomp_Command(char * line, int num); +static BOOL ARM9X2_Command(char * line, int num); +static BOOL VERSION_Command(char * line, int num); +static BOOL RSAKEY_Command(char * line, int num); +static BOOL OUTKEY_Command(char * line, int num); +static BOOL WramRegs_Command(char * line, int num); +static BOOL ENCKEY_Command(char * line, int num); +static BOOL NcdOffset_Command(char * line, int num); +static BOOL MirrorOffset_Command(char * line, int num); +static BOOL ERROR_Command(char * line, int num); + +static BOOL InitializeAesKey(void); +static BOOL InitializeNandfirmFile(void); +static BOOL FinalizeNandfirmFile(const char *nandFile); + +static s32 Offset; // Current offset +static int LineNum; // Line number for error message +static const char *specFileName; // specFile name for error message + +NANDHeaderEx nandHeader; // Nandfirm Header Shadow +FIRMSignedContext signedContext; +u8 *keyFileBuf; +BOOL compArm9 = TRUE; +BOOL compArm7 = TRUE; +u32 mirrorOfs = 0; +tErrorFlags errFlags; + +//--------------------------------------------------------------------------- +// Output - nandfirm File +//--------------------------------------------------------------------------- + +BOOL OutputNandfirmFile(const char *specFile, const char *nandFile) +{ + char *buffer; + BOOL state; + + if (ReadFile(specFile, &buffer, READ_ALL) <= 0) + { + return FALSE; + } + + if (!OpenFile(nandFile)) + { + return FALSE; + } + + specFileName = specFile; + + state = InitializeNandfirmFile() && ConstructNandfirmFile(buffer) && + FinalizeNandfirmFile(nandFile) && CloseFile(); + + if (!state) + { + DeleteOutFile(); + } + + return state; +} + + +//--------------------------------------------------------------------------- +// Output - Nandfirm File +//--------------------------------------------------------------------------- + +static const tCommandDesc command[] = { + {SBIN9CMD, Sbin9_Command}, {SBIN7CMD, Sbin7_Command}, + {ELF9CMD, Elf9_Command},{ELF7CMD, Elf7_Command}, + {COMP9CMD, Comp9_Command},{COMP7CMD, Comp7_Command}, + {DECOMPCMD, Decomp_Command}, + {VERCMD, VERSION_Command}, + {ARM9X2CMD, ARM9X2_Command}, + {RSAKEYCMD, RSAKEY_Command}, + {OUTKEYCMD, OUTKEY_Command}, + {WREGCMD, WramRegs_Command}, + {ENDKEYCMD, ENCKEY_Command}, + {NCDCMD, NcdOffset_Command}, + {MIRRORCMD, MirrorOffset_Command}, + {ERRCMD, ERROR_Command}, +}; + +BOOL ConstructNandfirmFile(char * specFile) +{ + char *line; + char *line_top; + char *p; + int i; + + LineNum = 0; + Offset = 0x00000000; + + line = specFile++; + + while (*line != '\0') + { + LineNum++; + + // Get command line + line_top = line; + while (*line != '\0') + { + if (*line++ == '\n') + { + break; + } + } + + // Print for debug + debug_printf("NANDSF Line%4d [", LineNum, line); + for (p = line_top; p != line; p++) + { + if (isprint(*p)) + { + debug_printf("%c", *p); + } + } + debug_printf("]\n"); + + if (*line_top == '#') + { + } + else + { + for (i = 0; i < (sizeof(command) / sizeof(command[0])); i++) + { + if (!strncmp(line_top, command[i].string, strlen(command[i].string))) + { + if (command[i].funcp != NULL) + { + char line_cmd[FILENAME_MAX]; + char line_scan[FILENAME_MAX]; + char* line_conv; + int num; + + num = sscanf( line_top, + "%1024[^ :] : %1024[^ \x0d:#\n]", + line_cmd, line_scan + ); + line_conv = ResolveDefVal(line_scan); + + debug_printf("line_cmd = %s, line_conv = %s\n", line_cmd, line_conv); + + if (!command[i].funcp(line_conv, num - 1)) + return FALSE; + } + } + } + } + + } + return TRUE; +} + + +//--------------------------------------------------------------------------- +// Output - 'WramRegs' Command +//--------------------------------------------------------------------------- + +extern MIHeader_WramRegs wram_regs_init; +MIHeader_WramRegs* wram_regs; + +static BOOL WramRegs_Command(char * line, int num) +{ + if (num != 1) + { + error("Wrong format spec file '%s' line:%d", specFileName, LineNum); + return FALSE; + } + + debug_printf2("wram regs file = %s\n", line); + + return ReadWramRegFile(line); +} + +static BOOL ReadWramRegFile(const char *fileName) +{ + int file_size; + struct stat st; + + if (FILESTATUS_FILE != GetFileStatus(&st, fileName)) + { + error("'%s' is not regular file.", fileName); + return FALSE; + } + + if ((file_size = ReadFile(fileName, &wram_regs, READ_ALL)) < 0) + return FALSE; + + nandHeader.o.h.w = *wram_regs; + nandHeader.m.h.w = *wram_regs; + + return CheckResult(); +} + +//--------------------------------------------------------------------------- +// Output - 'RSAKEY' Command +//--------------------------------------------------------------------------- + +static BOOL RSAKEY_Command(char * line, int num) +{ + if (num != 1) + { + error("Wrong format spec file '%s' line:%d", specFileName, LineNum); + return FALSE; + } + + debug_printf2("rsa key = %s\n", line); + + return ReadKeyFile(line); +} + +static BOOL ReadKeyFile(const char *fileName) +{ + int file_size; + struct stat st; + + if (FILESTATUS_FILE != GetFileStatus(&st, fileName)) + { + error("'%s' is not regular file.", fileName); + return FALSE; + } + + if ((file_size = ReadFile(fileName, &keyFileBuf, READ_ALL)) < 0) + return FALSE; + + return CheckResult(); +} + + +//--------------------------------------------------------------------------- +// Output - 'OUTKEY' Command +//--------------------------------------------------------------------------- + +static BOOL OUTKEY_Command(char * line, int num) +{ + if (num != 1) + { + error("Wrong format spec file '%s' line:%d", specFileName, LineNum); + return FALSE; + } + + debug_printf2("out key = %s\n", line); + + PubkeyFileName = line; + + return CheckResult(); +} + + +//--------------------------------------------------------------------------- +// Output - 'VERSION' Command +//--------------------------------------------------------------------------- + +static u8 ConvToBCD8(int x) +{ + u8 bcd = 0; + + x %= 100; + bcd |= (x / 10)<<4; + bcd |= (x % 10); + + return bcd; +} + +static BOOL VERSION_Command(char * line, int num) +{ + char scan[FILENAME_MAX]; + u64 version = -1; + + // rescan + num = sscanf( line, + "0x%1024[^\x0d\n]", + scan + ); + + if (num == 1) + { + // convert version info + version = strtoull(scan, NULL, 16); + } + else if (num == 0) + { + // generate version info + u8* ver8 = (u8*)&version; + time_t timer; + struct tm *t_st; + + time(&timer); + t_st = localtime(&timer); + + ver8[0] = ConvToBCD8(t_st->tm_min); + ver8[1] = ConvToBCD8(t_st->tm_hour); + ver8[2] = ConvToBCD8(t_st->tm_mday); + ver8[3] = ConvToBCD8(t_st->tm_mon+1); + ver8[4] = ConvToBCD8(t_st->tm_year); + ver8[5] = 0xff; + ver8[6] = 0xff; + ver8[7] = 0xff; + } + + debug_printf2("version = %08llx\n", version); + + return CheckResult(); +} + + +//--------------------------------------------------------------------------- +// Output - 'Decomp' Command +//--------------------------------------------------------------------------- + +static BOOL Decomp_Command(char * line, int num) +{ + char* dbg_str = "ARM7"; + + if (num != 1) + { + error("Wrong format spec file '%s' line:%d", specFileName, LineNum); + return FALSE; + } + + if (!strcmp(line, "ARM9")) + { + nandHeader.o.l.arm9_decomp = TRUE; + nandHeader.m.l.arm9_decomp = TRUE; + dbg_str = "ARM9"; + } + + debug_printf2("decompress processor = %s\n", dbg_str); + + return CheckResult(); +} + + +//--------------------------------------------------------------------------- +// Output - 'ARM9_X2' Command +//--------------------------------------------------------------------------- + +static BOOL ARM9X2_Command(char * line, int num) +{ + char* dbg_str = "FALSE"; + + if (num != 1) + { + error("Wrong format spec file '%s' line:%d", specFileName, LineNum); + return FALSE; + } + + if (!strcmp(line, "TRUE")) + { + nandHeader.o.l.arm9_x2 = TRUE; + nandHeader.m.l.arm9_x2 = TRUE; + dbg_str = "TRUE"; + } + + debug_printf2("arm9 x2 = %s\n", dbg_str); + + return CheckResult(); +} + + +//--------------------------------------------------------------------------- +// Output - 'Comp9' Command +//--------------------------------------------------------------------------- + +static BOOL Comp9_Command(char * line, int num) +{ + char* dbg_str = "FALSE"; + + if (num != 1) + { + error("Wrong format spec file '%s' line:%d", specFileName, LineNum); + return FALSE; + } + + compArm9 = FALSE; + + if (!strcmp(line, "TRUE")) + { + nandHeader.o.l.comp_arm9_boot_area = TRUE; + nandHeader.m.l.comp_arm9_boot_area = TRUE; + compArm9 = TRUE; + dbg_str = "TRUE"; + } + + debug_printf2("arm9 compress = %s\n", dbg_str); + + return CheckResult(); +} + + +//--------------------------------------------------------------------------- +// Output - 'Comp7' Command +//--------------------------------------------------------------------------- + +static BOOL Comp7_Command(char * line, int num) +{ + char* dbg_str = "FALSE"; + + if (num != 1) + { + error("Wrong format spec file '%s' line:%d", specFileName, LineNum); + return FALSE; + } + + compArm7 = FALSE; + + if (!strcmp(line, "TRUE")) + { + nandHeader.o.l.comp_arm7_boot_area = TRUE; + nandHeader.m.l.comp_arm7_boot_area = TRUE; + compArm7 = TRUE; + dbg_str = "TRUE"; + } + + debug_printf2("arm7 compress = %s\n", dbg_str); + + return CheckResult(); +} + + +//--------------------------------------------------------------------------- +// Output - 'Elf9' Command +//--------------------------------------------------------------------------- + +static BOOL Elf9_Command(char * line, int num) +{ + if (num != 1) + { + error("Wrong format spec file '%s' line:%d", specFileName, LineNum); + return FALSE; + } + + debug_printf2("arm9 elf = %s\n", line); + + { + s32 ramAddr = GetRamAddr(line); + nandHeader.o.l.main_ram_address = (void*)ramAddr; + nandHeader.m.l.main_ram_address = (void*)ramAddr; + } + + return CheckResult(); +} + + +//--------------------------------------------------------------------------- +// Output - 'Elf7' Command +//--------------------------------------------------------------------------- + +static BOOL Elf7_Command(char * line, int num) +{ + if (num != 1) + { + error("Wrong format spec file '%s' line:%d", specFileName, LineNum); + return FALSE; + } + + debug_printf2("arm7 elf = %s\n", line); + + { + s32 ramAddr = GetRamAddr(line); + nandHeader.o.l.sub_ram_address = (void*)ramAddr; + nandHeader.m.l.sub_ram_address = (void*)ramAddr; + } + + return CheckResult(); +} + + +static s32 GetRamAddr(const char *fileName) +{ + Elf32_Ehdr *ehdr; + s32 ramAddr; + int file_size; + struct stat st; + + if (FILESTATUS_FILE != GetFileStatus(&st, fileName)) + { + error("'%s' is not regular file.", fileName); + return FALSE; + } + + if ((file_size = ReadFile(fileName, &ehdr, sizeof(Elf32_Ehdr))) < 0) + return FALSE; + + ramAddr = ehdr->e_entry; + + Free(&ehdr); + + debug_printf2("ramaddr = 0x%08x\n", ramAddr); + + return ramAddr; +} + + +//--------------------------------------------------------------------------- +// Output - 'Sbin9' Command +//--------------------------------------------------------------------------- + +static BOOL Sbin9_Command(char * line, int num) +{ + FIRMSignedContext* sc = &signedContext; + + if (num != 1) + { + error("Wrong format spec file '%s' line:%d", specFileName, LineNum); + return FALSE; + } + + debug_printf2("arm9 sbin = %s\n", line); + + // Set ARM9 ROM Offset + if (!Offset) + { + Offset = (sizeof(NANDHeader) + FIRM_ALIGN_MASK) & ~FIRM_ALIGN_MASK; + if (mirrorOfs) + { + Offset += sizeof(NANDHeaderCore) * 2; + } + SeekFile(Offset); + } + debug_printf2("romoffset = %#x\n", Offset); + { + nandHeader.o.l.main_rom_offset = Offset; + nandHeader.m.l.main_rom_offset = Offset + mirrorOfs - sizeof(NANDHeaderCore) * 2; + } + + return ReadSbinFile(line, &nandHeader.o.l.main_rom_offset, &nandHeader.m.l.main_rom_offset, &sc->hash[FIRM_SIGNED_HASH_IDX_ARM9], compArm9); +} + +//--------------------------------------------------------------------------- +// Output - 'Sbin7' Command +//--------------------------------------------------------------------------- + +static BOOL Sbin7_Command(char * line, int num) +{ + FIRMSignedContext* sc = &signedContext; + + if (num != 1) + { + error("Wrong format spec file '%s' line:%d", specFileName, LineNum); + return FALSE; + } + + debug_printf2("arm7 sbin = %s\n", line); + + // Set ARM7 ROM Offset + if (!Offset) + { + Offset = (sizeof(NANDHeader) + FIRM_ALIGN_MASK) & ~FIRM_ALIGN_MASK; + if (mirrorOfs) + { + Offset += sizeof(NANDHeaderCore) * 2; + } + SeekFile(Offset); + } + debug_printf2("romoffset = %#x\n", Offset); + { + nandHeader.o.l.sub_rom_offset = Offset; + nandHeader.m.l.sub_rom_offset = Offset + mirrorOfs - sizeof(NANDHeaderCore) * 2; + } + + return ReadSbinFile(line, &nandHeader.o.l.sub_rom_offset, &nandHeader.m.l.sub_rom_offset, &sc->hash[FIRM_SIGNED_HASH_IDX_ARM7], compArm7); +} + + +static BOOL ReadSbinFile(const char *fileName, void* minfo, void* minfo2, void* hash, BOOL comp) +{ + const NANDHeader_ModuleInfo *m = minfo; + u32 *size = (void*)&m->size; + u32 *orig_size = (void*)&m->decomp_size; + const NANDHeader_ModuleInfo *m2 = minfo2; + u32 *size2 = (void*)&m2->size; + u32 *orig_size2 = (void*)&m2->decomp_size; + char *buffer; + char *file; + int file_size; + struct stat st; + + if (FILESTATUS_FILE != GetFileStatus(&st, fileName)) + { + error("'%s' is not regular file.", fileName); + return FALSE; + } + + if ((file_size = ReadFile(fileName, &file, READ_ALL)) < 0) + return FALSE; + + *orig_size = *orig_size2 = file_size; + + // Digest file image + if (hash) + { + ACSign_DigestUnit(hash, file, file_size); + } + + // Compress file image with fitting region + buffer = Alloc(file_size * 2); + if ( comp ) + { + file_size = LZCompWrite(file, file_size, buffer, FIRM_ALIGN); + } + else + { + memcpy(&buffer[0], file, file_size); + { + u32 pad_size = (FIRM_ALIGN - (file_size % FIRM_ALIGN)) % FIRM_ALIGN; + if (pad_size) + memset(&buffer[file_size], 0, pad_size); + file_size += pad_size; + } + } + Free(&file); + file = buffer; + + if (size) + { + *size = *size2 = file_size; + } + Offset += file_size; + + // Encrypt file image + EncryptBuffer(file, file_size); + + // Output file image with fitting region + PutBuffer(file, file_size); + + Free(&file); + + if (mirrorOfs && mirrorOfs < Offset + sizeof(NANDHeaderCore) * 2) + { + error("mirrorOfs is too small (%ld < %ld).\n", mirrorOfs, Offset + sizeof(NANDHeaderCore) * 2); + return FALSE; + } + + return CheckResult(); +} + +typedef struct +{ + unsigned long e[4]; +} u128; + +static BOOL EncryptBuffer(char *buffer, int length) +{ + const u128 id = {{ AES_IDS_ID2_A, AES_IDS_ID2_B, AES_IDS_ID2_C, AES_IDS_ID2_D }}; + u128 iv = {{ length, -length, ~length, 0 }}; + FIRMSignedContext* sc = &signedContext; + char *buffer2 = Alloc(length); + AES_KEY key; + if (!buffer2) + return FALSE; + AES_SetKey(&key, sc->aes_key, (unsigned char*)&id); + AES_Ctr(&key, buffer2, buffer, length, (unsigned char*)&iv); + memcpy(buffer, buffer2, length); + memset(buffer2, 0, length); + Free(buffer2); + return TRUE; +} + + +//--------------------------------------------------------------------------- +// Output - 'ENC_KEYINFO' Command +//--------------------------------------------------------------------------- + +static BOOL ENCKEY_Command(char * line, int num) +{ + u32 key; + + if (num != 1) + { + error("Wrong format spec file '%s' line:%d", specFileName, LineNum); + return FALSE; + } + + key = strtoul(line, NULL, 0); + nandHeader.g.d.ds_key = key; + + debug_printf2("keyinfo = %#x\n", key); + + return CheckResult(); +} + + +//--------------------------------------------------------------------------- +// Output - 'NcdOffset' Command +//--------------------------------------------------------------------------- + +static BOOL NcdOffset_Command(char * line, int num) +{ + u32 ofs; + + if (num != 1) + { + error("Wrong format spec file '%s' line:%d", specFileName, LineNum); + return FALSE; + } + + ofs = strtoul(line, NULL, 0); + { + nandHeader.g.d.ncd_romAdr = ConvertHeaderRomOffsetAlign(ofs, 8); + } + + debug_printf2("ncd romoffset = %#x\n", ofs); + + return CheckResult(); +} + + +//--------------------------------------------------------------------------- +// Output - 'MirrorOffset' Command +//--------------------------------------------------------------------------- + +static BOOL MirrorOffset_Command(char * line, int num) +{ + if (num != 1) + { + error("Wrong format spec file '%s' line:%d", specFileName, LineNum); + return FALSE; + } + + mirrorOfs = strtoul(line, NULL, 0); + + debug_printf2("mirrored image offset = %#x\n", mirrorOfs); + + return CheckResult(); +} + + +//--------------------------------------------------------------------------- +// Output - 'ERROR' Command +//--------------------------------------------------------------------------- +static char* error_type[] = +{ + "SIGN", + "HEADER_HASH", + "ARM9_HASH", + "ARM7_HASH", + "HASH_TABLE_HASH", + "FINAL_HASH", + "HEADER_FOOTER", +}; + +static BOOL ERROR_Command(char * line, int num) +{ + char* dbg_str = "UNKNOWN"; + int i; + + if (num != 1) + { + error("Wrong format spec file '%s' line:%d", specFileName, LineNum); + return FALSE; + } + + for (i=0; ihash[FIRM_SIGNED_HASH_IDX_HEADER][0] ^= 1; + } + if ( errFlags.e.arm9_hash ) + { + sc->hash[FIRM_SIGNED_HASH_IDX_ARM9][0] ^= 1; + } + if ( errFlags.e.arm7_hash ) + { + sc->hash[FIRM_SIGNED_HASH_IDX_ARM7][0] ^= 1; + } + if ( errFlags.e.hash_table_hash ) + { + sc->hash[FIRM_SIGNED_HASH_IDX_HASH_TABLE][0] ^= 1; + } +} + +static void SetFinalHashError(FIRMSignedContext* sc) +{ + if ( errFlags.e.final_hash ) + { + sc->hash[FIRM_SIGNED_HASH_IDX_FINAL][0] ^= 1; + } +} + +static void SetSignError(NANDHeader* nh) +{ + if ( errFlags.e.sign ) + { + nh->sign.raw[0] ^= 1; + } + +} + +static void SetFooterError(NANDHeader* nh) +{ + if ( errFlags.e.header_footer ) + { + nh->h.reserved_footer[sizeof(nh->h.reserved_footer)-1] ^= 1; + } + +} + +//--------------------------------------------------------------------------- +// Output - Initialize AES Key +//--------------------------------------------------------------------------- +static BOOL InitializeAesKey(void) +{ + FIRMSignedContext* sc = &signedContext; + struct stat specstat; + time_t spectime; + if (stat(specFileName, &specstat) != 0) + return FALSE; + time(&spectime); + memcpy(&sc->aes_key[0], &specstat.st_atime, 4); + memcpy(&sc->aes_key[4], &specstat.st_mtime, 4); + memcpy(&sc->aes_key[8], &specstat.st_ctime, 4); + memcpy(&sc->aes_key[12], &spectime, 4); + ACSign_GetKey(sc->aes_key, sizeof(sc->aes_key), sc->aes_key, sizeof(sc->aes_key)); + return TRUE; +} + + +//--------------------------------------------------------------------------- +// Output - Initialize Nandfirm File +//--------------------------------------------------------------------------- + +static BOOL InitializeNandfirmFile(void) +{ + memset(&signedContext.hash[FIRM_SIGNED_HASH_IDX_HASH_TABLE], 0xff, sizeof(signedContext.hash[0])); + nandHeader.o.h.w = wram_regs_init; + nandHeader.m.h.w = wram_regs_init; + InitializeAesKey(); + return TRUE; +} + + +//--------------------------------------------------------------------------- +// Output - Finalize Nandfirm File +//--------------------------------------------------------------------------- + +static void FinalizeNandfirmFileCore(NANDHeaderCore* master) +{ + NANDHeader *nh = &nandHeader.g; + FIRMSignedContext* sc = &signedContext; + u8* key = keyFileBuf; + + memcpy(&nh->l, &master->l, sizeof(NANDHeaderLow)); + memcpy(&nh->h, &master->h, sizeof(NANDHeaderHigh)); + + ACSign_DigestHeader(&sc->hash[FIRM_SIGNED_HASH_IDX_HEADER], nh); + + SetUnitHashErrors(sc); + + ACSign_DigestUnit(&sc->hash[FIRM_SIGNED_HASH_IDX_FINAL], sc, FIRM_HEADER_2ND_HASH_AREA_LEN); + + SetFinalHashError(sc); + + if (key) + { + ACSign_Final(nh, sc, key); + } + + SetFooterError(nh); + + SetSignError(nh); + + memcpy(&master->sign, &nh->sign, sizeof(FIRMPaddedSign)); +} + +static BOOL FinalizeNandfirmFile(const char *nandFile) +{ + // for m header (copy to g) + FinalizeNandfirmFileCore(&nandHeader.m); + + // for o header (copy to g) + FinalizeNandfirmFileCore(&nandHeader.o); + + // Output file image + SeekFile(0L); + if (mirrorOfs) + { + PutBuffer(&nandHeader, sizeof(nandHeader)); + } + else + { + PutBuffer(&nandHeader.g, sizeof(nandHeader.g)); + } + + // Output public key (modulus) + if (PubkeyFileName) + { + WriteFile(PubkeyFileName, &keyFileBuf[ACS_RSA_PRVMOD_OFFSET], ACS_RSA_PRVMOD_LEN); + } + + return TRUE; +} diff --git a/build/tools/makenandfirm/path.c b/build/tools/makenandfirm/path.c new file mode 100644 index 00000000..eeeb9895 --- /dev/null +++ b/build/tools/makenandfirm/path.c @@ -0,0 +1,931 @@ +/*---------------------------------------------------------------------------* + Project: NitroSDK - tools - makebanner + File: path.c + + Copyright 2003-2006 Nintendo. All rights reserved. + + These coded instructions, statements, and computer programs contain + proprietary information of Nintendo of America Inc. and/or Nintendo + Company Ltd., and are protected by Federal copyright law. They may + not be disclosed to third parties or copied or duplicated in any form, + in whole or in part, without the prior written consent of Nintendo. + + $Log: path.c,v $ + Revision 1.3 2006/01/18 02:11:20 kitase_hirotake + do-indent + + Revision 1.2 2005/02/28 05:26:13 yosizaki + do-indent. + + Revision 1.1 2004/08/30 08:41:14 yasu + makebanner moves into CVS tree + + $NoKeywords: $ + *---------------------------------------------------------------------------*/ +#include +#include // free() +#include // strcasecmp() +#include // stat() +#include // opendir()/readdir()/closedir() +#include // getcwd() +#ifdef __CYGWIN__ +#include // cygwin_conv_to_win32_path() +#endif +#include "path.h" + +//--------------------------------------------------------------------------- +// Get File Statue +//--------------------------------------------------------------------------- + +tFileStatus GetFileStatus(struct stat *s, const char *filename) +{ + // Get file status + if (stat(filename, s)) + { + error("Can't get status %s", filename); + return FILESTATUS_ERROR; + } + + if (S_ISREG(s->st_mode)) + { + return FILESTATUS_FILE; + } + else if (S_ISDIR(s->st_mode)) + { + return FILESTATUS_DIR; + } + + error("Unknown file type %s", filename); + return FILESTATUS_ERROR; +} + + +//--------------------------------------------------------------------------- +// File Globbing & Dir Listing +//--------------------------------------------------------------------------- + +typedef struct +{ + tCallBack callBack; + void *param; + tWildCard *accept; + tWildCard *reject; + +} +tForeachEntryParam; + + +static BOOL isAcceptEntryName(char *pathName, tWildCard * accept, tWildCard * reject) +{ + char *p = pathName; + + while (*p) + { + if (*p == '/') + pathName = p + 1; + p++; + } + + if (accept) + { + while (accept) + { + if (WildCardCmp(accept->name, pathName)) + { + goto accepted; + } + accept = accept->next; + } + return FALSE; + } + accepted: + + while (reject) + { + if (WildCardCmp(reject->name, pathName)) + { + return FALSE; + } + reject = reject->next; + } + return TRUE; +} + + +static BOOL ForeachEntry_CallBack(char *pathName, void *param) +{ + tForeachEntryParam *t = (tForeachEntryParam *) param; + struct stat fstat; + + if (!isAcceptEntryName(pathName, t->accept, t->reject)) + { + // Reject!!! ignored + return TRUE; + } + + switch (GetFileStatus(&fstat, pathName)) + { + case FILESTATUS_FILE: + return t->callBack(pathName, t->param); + + case FILESTATUS_DIR: + return ForeachDirList(pathName, ForeachEntry_CallBack, param); + + default: + break; + } + return FALSE; +} + + +BOOL ForeachEntry(const char *pathName, tWildCard * reject, tCallBack callBack, void *param) +{ + tForeachEntryParam t; + + t.callBack = callBack; + t.param = param; + t.accept = NULL; + t.reject = reject; + + return ForeachPathGlobbing(pathName, ForeachEntry_CallBack, &t); +} + + +typedef struct +{ + tCallBack callBack; + void *param; + char *baseName; + +} +tForeachFileParam; + + +static BOOL ForeachFile_CallBack(char *pathName, void *param) +{ + tForeachFileParam *t = (tForeachFileParam *) param; + + int len = strlen(t->baseName); + + debug_printf(" ForeachFile_CallBack path[%s] base[%s]\n", pathName, t->baseName); + + if (strncmp(pathName, t->baseName, len)) + { + error("Wildcard in Root is not supported"); + return FALSE; + } + + return t->callBack(pathName + len, t->param); +} + + +BOOL ForeachFile(const char *baseName, const char *fileName, tWildCard * reject, tCallBack callBack, + void *param) +{ + char *cBaseName; + char *cPathName; + BOOL state; + tForeachFileParam t; + + debug_printf("ForeachFile : baseName[%s] fileName[%s]\n", baseName, fileName); + + cBaseName = GetSrcPath(baseName, ""); + cPathName = GetSrcPath(baseName, fileName); + + debug_printf("ForeachFile : cBaseName[%s] cPathName[%s]\n", cBaseName, cPathName); + + t.callBack = callBack; + t.param = param; + t.baseName = cBaseName; + + state = ForeachEntry(cPathName, reject, ForeachFile_CallBack, &t); + + free(cBaseName); + free(cPathName); + + return state; +} + + +//--------------------------------------------------------------------------- +// FilePath Globbing +//--------------------------------------------------------------------------- + +typedef struct +{ + char *baseName; + char *pathName; + tCallBack callBack; + void *param; + +} +tGlobParam; + + +static int CountFile; +static BOOL ForeachPathGlobbing_Entry(tGlobParam * pg); +static BOOL ForeachPathGlobbing_WildCard(char *pathName, void *param); + +BOOL ForeachPathGlobbing(const char *pathName, tCallBack callBack, void *param) +{ + tGlobParam g; + BOOL ret; + + g.baseName = NULL; + g.pathName = PathNormalize(pathName, TRUE); + g.callBack = callBack; + g.param = param; + CountFile = 0; + + debug_printf("PathGlobbing : Name [%s]->[%s]\n", pathName, g.pathName); + + ret = ForeachPathGlobbing_Entry(&g); + + free(g.pathName); + + if (ret && CountFile == 0) + { + error("No file or directory matched %s", pathName); + return FALSE; + } + return ret; +} + + +static BOOL ForeachPathGlobbing_Entry(tGlobParam * pg) +{ + tGlobParam g; + char *entryName; + struct stat s; + BOOL state; + + if (pg->pathName) + { + entryName = PathDup(pg->pathName); + + g = *pg; + g.pathName = PathGetDirLevelDown(pg->pathName); + + if (pg->baseName) + { + g.baseName = Alloc(strlen(pg->baseName) + strlen(entryName) + 2); + sprintf(g.baseName, "%s/%s", pg->baseName, entryName); + } + else + { + g.baseName = strdup(entryName); + } + + // Check if wildcard ? + if (isPathWildCard(entryName)) + { + state = ForeachDirList(pg->baseName, ForeachPathGlobbing_WildCard, &g); + } + else + { + state = ForeachPathGlobbing_Entry(&g); + } + + Free(&entryName); + Free(&g.baseName); + } + else + { + // Check if file exists + if (!stat(pg->baseName, &s)) + { + debug_printf(" File Found [%s]\n", pg->baseName); + + // Globbing done, exec callback + + state = pg->callBack(pg->baseName, pg->param); + CountFile++; + } + else + { + debug_printf(" File Not Found [%s]\n", pg->baseName); + state = TRUE; // Ignored + } + } + return state; +} + + +static BOOL ForeachPathGlobbing_WildCard(char *pathName, void *param) +{ + tGlobParam g; + tGlobParam *pg = (tGlobParam *) param; + + debug_printf(" WildCardCmp: [%s] [%s]\n", pg->baseName, pathName); + + if (WildCardCmp(pg->baseName, pathName)) + { + g = *pg; + g.baseName = pathName; + return ForeachPathGlobbing_Entry(&g); + } + + return TRUE; // Ignored +} + + +//--------------------------------------------------------------------------- +// Directory Listing +// Listing directory & Exec CallBack +//--------------------------------------------------------------------------- + +BOOL ForeachDirList(const char *dirName, tCallBack callBack, void *param) +{ + DIR *dir; + struct dirent *entry; + char *pathName; + BOOL state = TRUE; + + if (!dirName) + { + dirName = "."; + } + + debug_printf("DirectoryList: Name [%s]\n", dirName); + + // Open directory + if (NULL == (dir = opendir(dirName))) + { + error("Can't read directory %s", dirName); + return FALSE; + } + + // Store new files + while (NULL != (entry = readdir(dir))) + { + pathName = entry->d_name; + + if (!strcmp(pathName, ".") || !strcmp(pathName, "..")) + { + continue; + } + + debug_printf(" :%s\n", pathName); + pathName = GetSrcPath(dirName, pathName); + state = callBack(pathName, param); + free(pathName); + + if (!state) + break; + } + + closedir(dir); + return state; +} + + +//--------------------------------------------------------------------------- +// +// PathName Utilities +// +//--------------------------------------------------------------------------- + +//--------------------------------------------------------------------------- +// StrCmp/StrCpy entry name terminated / or \0 +//--------------------------------------------------------------------------- + +int PathCmp(const char *path, const char *cmp) +{ + char c; + + do + { + c = *path; + if (c == '/') + c = '\0'; // end of string if '/' + if (c != *cmp) + return 1; + path++; + cmp++; + } + while (c); + + return 0; +} + + +char *PathCpy(char *dest, const char *src) +{ + while (*src != '\0' && *src != '/') + { + *dest++ = *src++; + } + return dest; // Don't set '\0' +} + + +int PathLen(const char *path) +{ + int n = 0; + + while (*path != '\0' && *path != '/') + { + n++; + path++; + } + return n; +} + + +char *PathDup(const char *src) +{ + int n = PathLen(src); + char *dest = Alloc(n + 2); + + PathCpy(dest, src); + dest[n] = '\0'; + + return dest; +} + + +BOOL WildCardCmp(const char *wildcard, const char *path) +{ + if (*wildcard == '*') + { + if (*path != '\0' && WildCardCmp(wildcard, path + 1)) + return TRUE; + if (WildCardCmp(wildcard + 1, path)) + return TRUE; + } + + else if (*wildcard == '?') + { + return *path != '\0' && WildCardCmp(wildcard + 1, path + 1); + } + + else if (*wildcard == *path) + { + return *path == '\0' || WildCardCmp(wildcard + 1, path + 1); + } + + return FALSE; +} + + +BOOL isPathWildCard(const char *path) +{ + while (*path != '\0' && *path != '/') + { + if (*path == '*' || *path == '?') + { + return TRUE; + } + path++; + } + return FALSE; +} + + +//--------------------------------------------------------------------------- +// Go up/down directory level +//--------------------------------------------------------------------------- + +char *PathGetDirLevelDown(const char *path) +{ + while (*path) + { + if (*path == '/') + return (char *)path + 1; + path++; + } + return NULL; +} + + +//--------------------------------------------------------------------------- +// Get Basename +//--------------------------------------------------------------------------- + +char *GetBaseName(const char *path) +{ + int i; + char *new_path; + + for (i = strlen(path) - 1; i >= 0; i--) + { + if (path[i] == '/') + { + new_path = strdup(path); + new_path[i] = '\0'; + return new_path; + } + if (path[i] == ':') + { + new_path = Alloc(i + 3); + strncpy(new_path, path, i); + strcpy(new_path + i, ":."); + return new_path; + } + } + + new_path = strdup("."); + return new_path; +} + + +//--------------------------------------------------------------------------- +// Get Filename +//--------------------------------------------------------------------------- + +char *GetFileName(const char *path) +{ + int i; + char *new_file; + + for (i = strlen(path) - 1; i >= 0; i--) + { + if (path[i] == '/' || path[i] == ':') + { + new_file = strdup(path + i + 1); + return new_file; + } + } + new_file = strdup(path); + return new_file; +} + + +//--------------------------------------------------------------------------- +// Reconstruct path name +// +// - Resolve '.' or '..' in path name +// - Work around . and / to translate regular form +// +// Regular form of path: +// Absolute Path [Drive:]/.[/Entry...] +// Relative Path [Drive:].[/Entry]... +// +// ex) +// abc/def -> ./abc/def +// /aaa -> /./aaa +// D:/aaa -> D:/./aaa +// / -> /. +// . -> . +// ../aa -> ./../aa +//--------------------------------------------------------------------------- + +char *PathNormalize(const char *pathName, BOOL isTreatDotDot) +{ + int i, level, level_root, n; + BOOL isAbsolute; + const char *entry[DIRLEVEL_MAX]; + + char *pathNormal = Alloc(strlen(pathName) + 4); + const char *p_org; + char *p_new; + + // + // Check if drive letter C: D: E: + // Check if absolute path + // + p_new = pathNormal; + p_org = SkipDriveName(pathName); + n = (int)p_org - (int)pathName; + + if (n > 0) + { + strncpy(p_new, pathName, n); + p_new += n; + } + isAbsolute = isAbsolutePath(p_org); + + // + // Resolve '.' and '..' + // + // Slice the path at point of / , put them into entry[] + // + + level = level_root = 0; + + for (; p_org; p_org = PathGetDirLevelDown(p_org)) + { + if (!PathCmp(p_org, "") || !PathCmp(p_org, ".")) + { + // skip it + continue; + } + else if (!PathCmp(p_org, "..") && isTreatDotDot) + { + if (level > level_root) + { + // Back to parent dir + level--; + continue; + } + + // if pathname starts with '/', no directory to go up + if (isAbsolute) + { + error("Can't go up directory, '..' Ignored. %s", pathName); + continue; + } + + // keep '..' + level_root = level + 1; + } + + // name entry + entry[level] = p_org; + level++; + } + + // Reconstruct pathname + if (isAbsolute) + { + *p_new++ = '/'; + } + *p_new++ = '.'; + + for (i = 0; i < level; i++) + { + *p_new++ = '/'; + p_new = PathCpy(p_new, entry[i]); + } + *p_new = '\0'; + +#if 0 + if (strcmp(pathNormal, pathName)) + { + debug_printf(" PathNormal: [%s] -> [%s]\n", pathName, pathNormal); + } +#endif + return pathNormal; +} + + +//--------------------------------------------------------------------------- +// Get Src Path +// Normalize BASENAME +// Normalize FILENAME +// Concat both +//--------------------------------------------------------------------------- + +char *GetSrcPath(const char *baseName, const char *fileName) +{ + char *base; + char *file; + char *t; + char *path; + + base = PathNormalize(baseName, TRUE); + file = PathNormalize(fileName, TRUE); + t = Alloc(strlen(base) + strlen(file) + 2); + + // Concat base + '/' + file + sprintf(t, "%s/%s", base, file); + path = PathNormalize(t, FALSE); + + free(base); + free(file); + free(t); + + debug_printf(" GetSrcPath: [%s]\n", path); + return path; +} + + +//--------------------------------------------------------------------------- +// Get Dest Path +// Concat BASENAME + FILENAME +// Normalize it +//--------------------------------------------------------------------------- + +char *GetDestPath(const char *baseName, const char *fileName) +{ + char *t; + char *path; + + t = Alloc(strlen(baseName) + strlen(fileName) + 2); + + // Concat base + '/' + file + sprintf(t, "%s/%s", baseName, fileName); + path = PathNormalize(t, TRUE); + + free(t); + + debug_printf(" GetDestPath: [%s]\n", path); + return path; +} + + +//--------------------------------------------------------------------------- +// Remake the path into familier shape +// Delete ./ +//--------------------------------------------------------------------------- + +char *PathDenormalize(char *path) +{ + char *p; + + p = (char *)SkipDriveName(path); + if (*p == '/') + { + p++; + } + + // Cut './' + if (*p == '.' && *(p + 1) == '/') + { + while ('\0' != (*p = *(p + 2))) + { + p++; + } + + if (p == path) + { + + } + } + + + + return path; +} + + +//--------------------------------------------------------------------------- +// Get PC Path +//--------------------------------------------------------------------------- + +char *GetWin32Path(char *cygpath) +{ + static char buffer[FILENAME_MAX]; + +#ifdef __CYGWIN__ + if (*cygpath == '/') + { + cygwin_conv_to_win32_path(cygpath, buffer); + } + else +#endif + { + strcpy(buffer, cygpath); + } + + return ChangeBackSlash(buffer); +} + +char *ChangeWin32Path(char *cygpath) +{ + char *win32path = strdup(GetWin32Path(cygpath)); + + free(cygpath); + return win32path; +} + +//--------------------------------------------------------------------------- +// Change suffix +//--------------------------------------------------------------------------- +char *ChangeSuffix(const char *file, const char *suffix) +{ + int i, n; + char *path; + + n = strlen(file); + + for (i = n; file[i] != '.'; i--) + { + if (file[i] == '/' || i == 0) + { + i = n; + break; + } + } + + path = Alloc(i + strlen(suffix) + 1); + strncpy(path, file, i); + strcpy(path + i, suffix); + + return path; +} + + +//--------------------------------------------------------------------------- +// Get Current Dir +//--------------------------------------------------------------------------- + +char *GetCurrentDirectory(void) +{ + static char buffer[FILENAME_MAX]; + char *cwd; + + cwd = getcwd(buffer, FILENAME_MAX); + if (!cwd) + { + error("Can't access current directory"); + exit(10); + } + return cwd; +} + + +//--------------------------------------------------------------------------- +// Check if absolute path +// +// Return True in case of ... +// +// /dirA/dirB/fileC +// D:/dirA/dirB/fileC +// +// Return False in case of ... +// +// dirX/dirY/fileZ +// D:dirX/dirY/fileZ +//--------------------------------------------------------------------------- + +BOOL isAbsolutePath(const char *path) +{ + const char *p = path; + + while (*p != '\0') + { + if (*p == '/' || *p == '\\') + { + if (p == path || p[-1] == ':') + { + return TRUE; + } + } + p++; + } + return FALSE; +} + +//--------------------------------------------------------------------------- +// Check if drive name +// +// Return next character of ':' if drive name +// Return head of path if no drive name +//--------------------------------------------------------------------------- + +const char *SkipDriveName(const char *path) +{ + const char *p = path; + + while (*p != '\0' && *p != '/' && *p != '\\') + { + if (*p == ':') + { + return p + 1; + } + p++; + } + return path; +} + + +//--------------------------------------------------------------------------- +// App Name Utilities +//--------------------------------------------------------------------------- +static char *appName; +static char *appBaseName; +static char *appFileName; + +void InitAppName(const char *path) +{ + char *slash_path = ChangeBackSlash(strdup(path)); + + appBaseName = GetBaseName(slash_path); + appFileName = GetFileName(slash_path); + appName = ChangeSuffix(appFileName, ""); + + free(slash_path); +} + +char *GetAppName(void) +{ + return appName; +} + +char *GetAppBaseName(void) +{ + return appBaseName; +} + +char *GetAppFileName(void) +{ + return appFileName; +} + + +#ifdef TEST +int main(int argc, char *argv[]) +{ + int i; + char *s; + + for (i = 1; i < argc; i++) + { + s = PathNormalize(argv[i], TRUE); + printf("[%s] -> [%s]\n", argv[i], s); + free(s); + } + return 0; +} +#endif diff --git a/build/tools/makenandfirm/path.h b/build/tools/makenandfirm/path.h new file mode 100644 index 00000000..e9956dc6 --- /dev/null +++ b/build/tools/makenandfirm/path.h @@ -0,0 +1,93 @@ +/*---------------------------------------------------------------------------* + Project: NitroSDK - tools - makebanner + File: path.h + + Copyright 2003-2006 Nintendo. All rights reserved. + + These coded instructions, statements, and computer programs contain + proprietary information of Nintendo of America Inc. and/or Nintendo + Company Ltd., and are protected by Federal copyright law. They may + not be disclosed to third parties or copied or duplicated in any form, + in whole or in part, without the prior written consent of Nintendo. + + $Log: path.h,v $ + Revision 1.3 2006/01/18 02:11:20 kitase_hirotake + do-indent + + Revision 1.2 2005/02/28 05:26:13 yosizaki + do-indent. + + Revision 1.1 2004/08/30 08:41:14 yasu + makebanner moves into CVS tree + + $NoKeywords: $ + *---------------------------------------------------------------------------*/ +#ifndef PATH_H_ +#define PATH_H_ + +#include // struct tat +#include "misc.h" + +#define DIRLEVEL_MAX 256 +#ifndef FILENAME_MAX +#define FILENAME_MAX 1024 +#endif + +typedef enum +{ + FILESTATUS_ERROR = -1, + FILESTATUS_FILE = 0, + FILESTATUS_DIR = 1 +} +tFileStatus; + + +// Item Reject Control + +typedef struct tWildCard +{ + struct tWildCard *next; + char *name; + +} +tWildCard; + + +// CallBacks + +typedef BOOL (*tCallBack) (char *, void *); + + +// Prototypes + +tFileStatus GetFileStatus(struct stat *s, const char *filename); +BOOL ForeachEntry(const char *pathName, tWildCard * reject, tCallBack callBack, void *param); +BOOL ForeachFile(const char *baseName, const char *fileName, tWildCard * reject, + tCallBack callBack, void *param); +BOOL ForeachPathGlobbing(const char *pathName, tCallBack callBack, void *param); +BOOL ForeachDirList(const char *dirName, tCallBack callBack, void *param); +int PathCmp(const char *path, const char *cmp); +char *PathCpy(char *dest, const char *src); +int PathLen(const char *path); +char *PathDup(const char *src); +char *PathGetDirLevelDown(const char *path); +char *GetBaseName(const char *path); +char *GetFileName(const char *path); +BOOL WildCardCmp(const char *wildcard, const char *path); +BOOL isPathWildCard(const char *path); +char *PathNormalize(const char *pathName, BOOL isTreatDotDot); +char *PathDenormalize(char *path); +char *GetSrcPath(const char *base, const char *file); +char *GetDestPath(const char *base, const char *file); +char *GetWin32Path(char *cygpath); +char *ChangeWin32Path(char *cygpath); +char *ChangeSuffix(const char *file, const char *suffix); +char *GetCurrentDirectory(void); +BOOL isAbsolutePath(const char *path); +const char *SkipDriveName(const char *path); +void InitAppName(const char *path); +char *GetAppName(void); +char *GetAppBaseName(void); +char *GetAppFileName(void); + +#endif //PATH_H_ diff --git a/build/tools/makenandfirm/test/Makefile b/build/tools/makenandfirm/test/Makefile new file mode 100644 index 00000000..6882b9ee --- /dev/null +++ b/build/tools/makenandfirm/test/Makefile @@ -0,0 +1,55 @@ +#! make -f +#--------------------------------------------------------------------------- +# Project: TwlFirm - tools - makenandfirm +# File: Makefile +# +# Copyright 2007 Nintendo. All rights reserved. +# +# These coded instructions, statements, and computer programs contain +# proprietary information of Nintendo of America Inc. and/or Nintendo +# Company Ltd., and are protected by Federal copyright law. They may +# not be disclosed to third parties or copied or duplicated in any form, +# in whole or in part, without the prior written consent of Nintendo. +# +# $Log: $ +# $NoKeywords: $ +#--------------------------------------------------------------------------- + +SUBDIRS = wram_rbin \ + +include $(TWLSDK_ROOT)/build/buildtools/commondefs + +MAKENANDFIRM = ../makenandfirm.exe + +MAKEFIRM_ARM9 = ./twl_nandfirm9_print.axf +MAKEFIRM_ARM7 = ./twl_nandfirm7_print.axf +SDEPENDS_BIN += $(MAKEFIRM_ARM9) $(MAKEFIRM_ARM7) +MAKEFIRM_FLAGS += -d +MAKEFIRM_DEFS += -DFIRM_ROOT='$(FIRM_ROOT)' \ + -DMAKEFIRM_ARM9='$(basename $(MAKEFIRM_ARM9))' \ + -DMAKEFIRM_ARM7='$(basename $(MAKEFIRM_ARM7))' \ + -DMAKEFIRM_RSA_PRVKEY='$(MAKEFIRM_RSA_PRVKEY)' \ + +TARGET = test.srl + +%.srl: %.nandsf $(MAKENANDFIRM) + $(MAKENANDFIRM) $(MAKEFIRM_FLAGS) $(MAKEFIRM_DEFS) $< $@ + +.PHONY: build install do-autotest clean clobber + +define ECHO_CURDIR + echo "==== $(CURDIR)"; +endef + +build: + @$(ECHO_CURDIR) + @$(MAKE) $(TARGET) + +install do-autotest: + @$(ECHO_CURDIR) + +clean clobber super-clobber: + @$(ECHO_CURDIR) + -rm -f $(TARGET) *~ + +test-utf16.bsf: icon.nbfc icon.nbfp diff --git a/build/tools/makenandfirm/test/rsa_private.der b/build/tools/makenandfirm/test/rsa_private.der new file mode 100644 index 0000000000000000000000000000000000000000..06f5f4ccbac2e2f50ebc312c9d601a422754c370 GIT binary patch literal 607 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
U4<#z|> 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 V0e$D4tJkd|WOOI~ka7)h{{^{CZ=wJI literal 0 HcmV?d00001 diff --git a/build/tools/makenandfirm/test/twl_nandfirm9_print.axf b/build/tools/makenandfirm/test/twl_nandfirm9_print.axf new file mode 100644 index 0000000000000000000000000000000000000000..3d064b38a5509e8ea9e4ce4cc5302e3f0684d513 GIT binary patch literal 288636 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 literal 0 HcmV?d00001 diff --git a/build/tools/makenandfirm/test/wram_rbin/Makefile b/build/tools/makenandfirm/test/wram_rbin/Makefile new file mode 100644 index 00000000..562d9811 --- /dev/null +++ b/build/tools/makenandfirm/test/wram_rbin/Makefile @@ -0,0 +1,47 @@ +#! make -f +#---------------------------------------------------------------------------- +# Project: TwlFirm - tools - nandfirm-print +# File: Makefile +# +# Copyright 2007 Nintendo. All rights reserved. +# +# These coded instructions, statements, and computer programs contain +# proprietary information of Nintendo of America Inc. and/or Nintendo +# Company Ltd., and are protected by Federal copyright law. They may +# not be disclosed to third parties or copied or duplicated in any form, +# in whole or in part, without the prior written consent of Nintendo. +# +# $Log: $ +# $NoKeywords: $ +#---------------------------------------------------------------------------- + +SUBDIRS = + +LINCLUDES = ../include + +#---------------------------------------------------------------------------- + +TARGET_BIN = wram_regs.rbin + +SRCS = \ + wram_regs.c \ + +#SRCDIR = # using default +#LCFILE = # using default + + +include $(TWLFIRM_ROOT)/build/buildtools/commondefs + +INSTALL_DIR = . +INSTALL_TARGETS = $(BINDIR)/$(TARGET_BIN) + + +#---------------------------------------------------------------------------- + +do-build: $(TARGETS) + + +include $(TWLFIRM_ROOT)/build/buildtools/modulerules + + +#===== End of Makefile ===== diff --git a/build/tools/makenandfirm/test/wram_rbin/wram_regs.c b/build/tools/makenandfirm/test/wram_rbin/wram_regs.c new file mode 100644 index 00000000..5d0c9b18 --- /dev/null +++ b/build/tools/makenandfirm/test/wram_rbin/wram_regs.c @@ -0,0 +1,88 @@ +/*---------------------------------------------------------------------------* + Project: TwlFirm - tools - makenandfirm + File: wram_regs.c + + Copyright 2007 Nintendo. All rights reserved. + + These coded instructions, statements, and computer programs contain + proprietary information of Nintendo of America Inc. and/or Nintendo + Company Ltd., and are protected by Federal copyright law. They may + not be disclosed to third parties or copied or duplicated in any form, + in whole or in part, without the prior written consent of Nintendo. + + $Log: $ + $NoKeywords: $ + *---------------------------------------------------------------------------*/ +#include +#include + +MIHeader_WramRegs wram_regs = +{ + // ARM9 + { + REG_WRAM_A_BNK_PACK(0, MI_WRAM_A_ARM7, MI_WRAM_A_OFS_0KB, TRUE), + REG_WRAM_A_BNK_PACK(1, MI_WRAM_A_ARM7, MI_WRAM_A_OFS_64KB, TRUE), + REG_WRAM_A_BNK_PACK(2, MI_WRAM_A_ARM7, MI_WRAM_A_OFS_128KB, TRUE), + REG_WRAM_A_BNK_PACK(3, MI_WRAM_A_ARM7, MI_WRAM_A_OFS_192KB, TRUE), + }, + { + REG_WRAM_B_BNK_PACK(0, MI_WRAM_B_ARM7, MI_WRAM_B_OFS_0KB, TRUE), + REG_WRAM_B_BNK_PACK(1, MI_WRAM_B_ARM7, MI_WRAM_B_OFS_32KB, TRUE), + REG_WRAM_B_BNK_PACK(2, MI_WRAM_B_ARM7, MI_WRAM_B_OFS_64KB, TRUE), + REG_WRAM_B_BNK_PACK(3, MI_WRAM_B_ARM7, MI_WRAM_B_OFS_96KB, TRUE), + REG_WRAM_B_BNK_PACK(4, MI_WRAM_B_ARM7, MI_WRAM_B_OFS_128KB, TRUE), + REG_WRAM_B_BNK_PACK(5, MI_WRAM_B_ARM7, MI_WRAM_B_OFS_160KB, TRUE), + REG_WRAM_B_BNK_PACK(6, MI_WRAM_B_ARM7, MI_WRAM_B_OFS_192KB, TRUE), + REG_WRAM_B_BNK_PACK(7, MI_WRAM_B_ARM7, MI_WRAM_B_OFS_224KB, TRUE), + }, + { + REG_WRAM_C_BNK_PACK(0, MI_WRAM_C_ARM9, MI_WRAM_C_OFS_0KB, TRUE), + REG_WRAM_C_BNK_PACK(1, MI_WRAM_C_ARM9, MI_WRAM_C_OFS_32KB, TRUE), + REG_WRAM_C_BNK_PACK(2, MI_WRAM_C_ARM9, MI_WRAM_C_OFS_64KB, TRUE), + REG_WRAM_C_BNK_PACK(3, MI_WRAM_C_ARM9, MI_WRAM_C_OFS_96KB, TRUE), + REG_WRAM_C_BNK_PACK(4, MI_WRAM_C_ARM9, MI_WRAM_C_OFS_128KB, TRUE), + REG_WRAM_C_BNK_PACK(5, MI_WRAM_C_ARM9, MI_WRAM_C_OFS_160KB, TRUE), + REG_WRAM_C_BNK_PACK(6, MI_WRAM_C_ARM9, MI_WRAM_C_OFS_192KB, TRUE), + REG_WRAM_C_BNK_PACK(7, MI_WRAM_C_ARM9, MI_WRAM_C_OFS_224KB, TRUE), + }, + REG_WRAM_A_MAP_PACK(MI_WRAM_MAP_NULL, + MI_WRAM_MAP_NULL, + MI_WRAM_A_IMG_128KB + ), + REG_WRAM_B_MAP_PACK(MI_WRAM_MAP_NULL, + MI_WRAM_MAP_NULL, + MI_WRAM_B_IMG_128KB + ), + REG_WRAM_C_MAP_PACK(HW_WRAM_AREA_HALF, + HW_WRAM_AREA_HALF + 0x00020000, + MI_WRAM_C_IMG_128KB + ), + + // ARM7 + REG_WRAM_A_MAP_PACK(HW_WRAM_AREA_HALF, + HW_WRAM_AREA_HALF + 0x00020000, + MI_WRAM_A_IMG_128KB + ), + REG_WRAM_B_MAP_PACK(HW_WRAM_AREA_HALF + 0x00020000, + HW_WRAM_AREA_HALF + 0x00040000, + MI_WRAM_B_IMG_128KB + ), + REG_WRAM_C_MAP_PACK(MI_WRAM_MAP_NULL, + MI_WRAM_MAP_NULL, + MI_WRAM_C_IMG_128KB + ), + // WRAM Lock + { + 0, + 0, + 0, + }, + + // WRAM-0/1 + MI_WRAM_ARM7_ALL, + + // VRAM-C + 7, + // VRAM-D + 7, +}; diff --git a/build/tools/makenandfirm/wram_regs.c b/build/tools/makenandfirm/wram_regs.c new file mode 100644 index 00000000..1350f138 --- /dev/null +++ b/build/tools/makenandfirm/wram_regs.c @@ -0,0 +1,91 @@ +/*---------------------------------------------------------------------------* + Project: TwlFirm - tools - makenandfirm + File: wram_regs.c + + Copyright 2007 Nintendo. All rights reserved. + + These coded instructions, statements, and computer programs contain + proprietary information of Nintendo of America Inc. and/or Nintendo + Company Ltd., and are protected by Federal copyright law. They may + not be disclosed to third parties or copied or duplicated in any form, + in whole or in part, without the prior written consent of Nintendo. + + $Log: $ + $NoKeywords: $ + *---------------------------------------------------------------------------*/ +#include "format_rom.h" +#define SDK_ASM +#include +#include + +MIHeader_WramRegs wram_regs_init = +{ + // ARM9 + { + REG_WRAM_A_BNK_PACK(0, MI_WRAM_A_ARM7, MI_WRAM_A_OFS_0KB, TRUE), + REG_WRAM_A_BNK_PACK(1, MI_WRAM_A_ARM7, MI_WRAM_A_OFS_64KB, TRUE), + REG_WRAM_A_BNK_PACK(2, MI_WRAM_A_ARM7, MI_WRAM_A_OFS_128KB, TRUE), + REG_WRAM_A_BNK_PACK(3, MI_WRAM_A_ARM7, MI_WRAM_A_OFS_192KB, TRUE), + }, + { + REG_WRAM_B_BNK_PACK(0, MI_WRAM_B_ARM7, MI_WRAM_B_OFS_0KB, TRUE), + REG_WRAM_B_BNK_PACK(1, MI_WRAM_B_ARM7, MI_WRAM_B_OFS_32KB, TRUE), + REG_WRAM_B_BNK_PACK(2, MI_WRAM_B_ARM7, MI_WRAM_B_OFS_64KB, TRUE), + REG_WRAM_B_BNK_PACK(3, MI_WRAM_B_ARM7, MI_WRAM_B_OFS_96KB, TRUE), + REG_WRAM_B_BNK_PACK(4, MI_WRAM_B_ARM7, MI_WRAM_B_OFS_128KB, TRUE), + REG_WRAM_B_BNK_PACK(5, MI_WRAM_B_ARM7, MI_WRAM_B_OFS_160KB, TRUE), + REG_WRAM_B_BNK_PACK(6, MI_WRAM_B_ARM7, MI_WRAM_B_OFS_192KB, TRUE), + REG_WRAM_B_BNK_PACK(7, MI_WRAM_B_ARM7, MI_WRAM_B_OFS_224KB, TRUE), + }, + { + REG_WRAM_C_BNK_PACK(0, MI_WRAM_C_ARM9, MI_WRAM_C_OFS_0KB, TRUE), + REG_WRAM_C_BNK_PACK(1, MI_WRAM_C_ARM9, MI_WRAM_C_OFS_32KB, TRUE), + REG_WRAM_C_BNK_PACK(2, MI_WRAM_C_ARM9, MI_WRAM_C_OFS_64KB, TRUE), + REG_WRAM_C_BNK_PACK(3, MI_WRAM_C_ARM9, MI_WRAM_C_OFS_96KB, TRUE), + REG_WRAM_C_BNK_PACK(4, MI_WRAM_C_ARM9, MI_WRAM_C_OFS_128KB, TRUE), + REG_WRAM_C_BNK_PACK(5, MI_WRAM_C_ARM9, MI_WRAM_C_OFS_160KB, TRUE), + REG_WRAM_C_BNK_PACK(6, MI_WRAM_C_ARM9, MI_WRAM_C_OFS_192KB, TRUE), + REG_WRAM_C_BNK_PACK(7, MI_WRAM_C_ARM9, MI_WRAM_C_OFS_224KB, TRUE), + }, + REG_WRAM_A_MAP_PACK(MI_WRAM_MAP_NULL, + MI_WRAM_MAP_NULL, + MI_WRAM_A_IMG_128KB + ), + REG_WRAM_B_MAP_PACK(MI_WRAM_MAP_NULL, + MI_WRAM_MAP_NULL, + MI_WRAM_B_IMG_128KB + ), + REG_WRAM_C_MAP_PACK(HW_WRAM_AREA_HALF, + HW_WRAM_AREA_HALF + 0x00020000, + MI_WRAM_C_IMG_128KB + ), + + // ARM7 + REG_WRAM_A_MAP_PACK(HW_WRAM_AREA_HALF, + HW_WRAM_AREA_HALF + 0x00020000, + MI_WRAM_A_IMG_128KB + ), + REG_WRAM_B_MAP_PACK(HW_WRAM_AREA_HALF + 0x00020000, + HW_WRAM_AREA_HALF + 0x00040000, + MI_WRAM_B_IMG_128KB + ), + REG_WRAM_C_MAP_PACK(MI_WRAM_MAP_NULL, + MI_WRAM_MAP_NULL, + MI_WRAM_C_IMG_128KB + ), + // WRAM Lock + { + 0, + 0, + 0, + }, + + // WRAM-0/1 + 3, + + // VRAM-C + 7, + // VRAM-D + 7, +}; + diff --git a/build/tools/makenorfirm/Makefile b/build/tools/makenorfirm/Makefile new file mode 100644 index 00000000..990fb2ac --- /dev/null +++ b/build/tools/makenorfirm/Makefile @@ -0,0 +1,148 @@ +#! make -f +#--------------------------------------------------------------------------- +# Project: TwlFirm - tools - makenorfirm +# File: Makefile +# +# Copyright 2007 Nintendo. All rights reserved. +# +# These coded instructions, statements, and computer programs contain +# proprietary information of Nintendo of America Inc. and/or Nintendo +# Company Ltd., and are protected by Federal copyright law. They may +# not be disclosed to third parties or copied or duplicated in any form, +# in whole or in part, without the prior written consent of Nintendo. +# +# $Log: $ +# $NoKeywords: $ +#--------------------------------------------------------------------------- + +SUPPORT_ECC = 0 + +ifeq ($(SUPPORT_ECC),1) +ECC_SRCDIR = ../../libraries/acsign_ecc/common \ + ../../libraries/acsign_ecc/common/algae/common/ecc \ + ../../libraries/acsign_ecc/common/algae/cmp \ + ../../libraries/acsign_ecc/common/algae/ecsource \ + +ECC_INCDIR = ../../libraries/acsign_ecc/include \ + ../../libraries/acsign_ecc/common/algae/include \ + ../../libraries/acsign_ecc/common/algae/common/include \ + +ECC_SRCS = acsign_ecc.c acsign_cryptoc.c \ + \ + cmparith.c cmpbits.c cmpcnv.c cmpdiv.c cmpmem.c \ + cmpmod.c cmpmuldv.c cmpspprt.c cmpsqr.c cmpvectr.c \ + computem.c ecfpatbl.c ecfpsmul.c \ + spcprime.c secfpcom.c \ + \ + p224v1.c p224v1a.c \ + +ECC_DEFS = -DRSA_PROTOTYPES=RSA_ENABLED \ + -DRCOM_BUILD=RSA_ENABLED -DRSA_FAST_INVERSE=RSA_ENABLED \ + -DRSA_STD_MEM_FUNCS=RSA_ENABLED -DRSA_STD_ALLOC_FUNCS=RSA_ENABLED \ +else +ECC_SRCDIR = +ECC_INCDIR = +ECC_SRCS = +ECC_DEFS = +endif + +SRCDIR += ../acsign $(ECC_SRCDIR) +INCDIR += ../acsign/include $(ECC_INCDIR) $(ECC_SRCDIR) + + +include $(TWLFIRM_ROOT)/build/buildtools/commondefs + +TARGETS = makenorfirm.exe + +SOURCES_C = makenorfirm.c \ + out_norfirm.c \ + misc.c \ + path.c \ + defval.c \ + compress.c \ + wram_regs.c \ + acsign.c \ + acsign_nor.c \ + aes2.c \ + $(ECC_SRCS) + +SOURCES = $(SORUCES_C) + +OBJECTS = $(SOURCES_C:.c=.o) + +HEADERS = format_nlist.h \ + makenorfirm.h \ + path.h \ + format_rom.h \ + misc.h \ + defval.h \ + compress.h \ + +MACROS += -DSMALL_CODE_SIZE \ + -DSTANDALONE \ + -DOPT_32_BIT \ + -DNO_SPLIT \ + -DNO_FP_API \ + -DNO_R_DIAG \ + $(ECC_DEFS) + +INSTALL_DIR = $(FIRM_INSTALL_TOOLSDIR)/bin +INSTALL_TARGETS = $(TARGETS) + +LDIRT_CLEAN = $(OBJECTS) $(TARGETS) version.h + + +VPATH = $(SRCDIR) +NITRO_INCDIR := $(FIRM_INCDIR) -I$(TWL_INCDIR) -I$(NITRO_INCDIR) $(addprefix -I,$(INCDIR)) + +include $(TWL_NITROSDK_ROOT)/build/buildtools/modulerules.x86 + +#---------------------------------------------------------------------------- +# build +#---------------------------------------------------------------------------- +do-build: $(TARGETS) + +$(TARGETS): $(OBJECTS) + $(CC_X86) $+ -o $@ + +makenorfirm.o: makenorfirm.c makenorfirm.h format_rom.h path.h version.h +out_norfirm.o: out_norfirm.c misc.h format_rom.h format_nlist.h format_sign.h elf.h compress.h \ + $(FIRM_INCDIR)/firm/format/sign.h \ + $(FIRM_INCDIR)/firm/format/wram_regs.h \ + $(FIRM_INCDIR)/firm/format/norfirm.h \ + +misc.o: misc.c misc.h +path.o: path.c path.h +compress.o: compress.c compress.h +wram_regs.o: wram_regs.c +acsign.o: acsign.c ../acsign/include/acsign.h +acsign_nor.o: acsign_nor.c format_sign.h \ + $(FIRM_INCDIR)/firm/format/sign.h \ + $(FIRM_INCDIR)/firm/format/wram_regs.h \ + $(FIRM_INCDIR)/firm/format/norfirm.h \ + +aes2.o: aes2.c aes2.h + +$(FIRM_INCDIR)/firm/format/sign.h: +$(FIRM_INCDIR)/firm/format/wram_regs.h: +$(FIRM_INCDIR)/firm/format/norfirm.h: +format_nlist.h: +format_rom.h: +makenorfirm.h: +acsign.h: +acsign_nor.h: +path.h: + +# avoid to warning message +misc.o:WARNING += -Wno-format-y2k + +# + +version.h: $(SOURCES) $(HEADERS) + @for i in $^ ; \ + do \ + date -r $$i +'#define SDK_DATE_OF_LATEST_FILE %Y%m%dUL'; \ + done | sort | tail -1 > $@ + +test: path.c misc.c + $(CC_X86) -DTEST $+ -o $@ diff --git a/build/tools/makenorfirm/compress.c b/build/tools/makenorfirm/compress.c new file mode 100644 index 00000000..903adf77 --- /dev/null +++ b/build/tools/makenorfirm/compress.c @@ -0,0 +1,292 @@ +/*---------------------------------------------------------------------------* + Project: TwlFirm - tools - makenorfirm + File: compress.c + + Copyright 2007 Nintendo. All rights reserved. + + These coded instructions, statements, and computer programs contain + proprietary information of Nintendo of America Inc. and/or Nintendo + Company Ltd., and are protected by Federal copyright law. They may + not be disclosed to third parties or copied or duplicated in any form, + in whole or in part, without the prior written consent of Nintendo. + + $Log: $ + $NoKeywords: $ + *---------------------------------------------------------------------------*/ +#include +#include // atoi() +#include // strcmp() +#include // isprint() +#include // chdir() +#include +#include // UCHAR_MAX +#include +#include // stat() +#include "elf.h" +#include "misc.h" +#include "defval.h" +#include "format_rom.h" +#include "format_nlist.h" +#include "makenorfirm.h" + +//#define ADD_HEADER + +#define DIFF_CODE_HEADER (0x80) +#define RL_CODE_HEADER (0x30) +#define LZ_CODE_HEADER (0x10) +#define HUFF_CODE_HEADER (0x20) +#define CODE_HEADER_MASK (0xF0) + +//=========================================================================== +// LZ77圧縮 +//=========================================================================== +static u8 SearchLZ(const u8 *nextp, u32 remainSize, u16 *offset); + +static u16 windowPos; +static u16 windowLen; + +static s16 LZOffsetTable[4096]; +static s16 LZByteTable[256]; +static s16 LZEndTable[256]; + + +static void LZInitTable(void) +{ + u16 i; + + for (i = 0; i < 256; i++) + { + LZByteTable[i] = -1; + LZEndTable[i] = -1; + } + windowPos = 0; + windowLen = 0; +} + +static void SlideByte(const u8 *srcp) +{ + s16 offset; + u8 in_data = *srcp; + u16 insert_offset; + + if (windowLen == 4096) + { + u8 out_data = *(srcp - 4096); + if ((LZByteTable[out_data] = LZOffsetTable[LZByteTable[out_data]]) == -1) + { + LZEndTable[out_data] = -1; + } + insert_offset = windowPos; + } + else + { + insert_offset = windowLen; + } + + offset = LZEndTable[in_data]; + if (offset == -1) + { + LZByteTable[in_data] = insert_offset; + } + else + { + LZOffsetTable[offset] = insert_offset; + } + LZEndTable[in_data] = insert_offset; + LZOffsetTable[insert_offset] = -1; + + if (windowLen == 4096) + { + windowPos = (u16)((windowPos + 1) % 0x1000); + } + else + { + windowLen++; + } +} + +static void LZSlide(const u8 *srcp, u32 n) +{ + u32 i; + + for (i = 0; i < n; i++) + { + SlideByte(srcp++); + } +} + +/*---------------------------------------------------------------------------* + Name: MI_CompressLZ + + Description: LZ77圧縮を行なう関数 + + Arguments: srcp 圧縮元データへのポインタ + size 圧縮元データサイズ + dstp 圧縮先データへのポインタ + 圧縮元データよりも大きいサイズのバッファが必要です。 + + Returns: 圧縮後のデータサイズ。 + 圧縮後のデータが圧縮前よりも大きくなる場合には圧縮を中断し0を返します。 + *---------------------------------------------------------------------------*/ +u32 LZCompWrite(u8 *srcp, u32 size, u8 *dstp, int boundary) +{ + u32 LZDstCount; // 圧縮データのバイト数 + u8 LZCompFlags; // 圧縮の有無を示すフラグ系列 + u8 *LZCompFlagsp; // LZCompFlags を格納するメモリ領域をポイント + u16 lastOffset; // 一致データまでのオフセット (その時点での最長一致データ) + u8 lastLength; // 一致データ長 (その時点での最長一致データ) + u8 i; + u32 dstMax; + +#ifdef ADD_HEADER + *(u32 *)dstp = size << 8 | LZ_CODE_HEADER; // データ・ヘッダ + dstp += 4; +#endif + LZDstCount = 4; + dstMax = size; + LZInitTable(); + + while (size > 0) + { + LZCompFlags = 0; + LZCompFlagsp = dstp++; // フラグ系列の格納先 + LZDstCount++; + + // フラグ系列が8ビットデータとして格納されるため、8回ループ + for (i = 0; i < 8; i++) + { + LZCompFlags <<= 1; // 初回 (i=0) は特に意味はない + if (size <= 0) + { + // 終端に来た場合はフラグを最後までシフトさせてから終了 + continue; + } + + if ((lastLength = SearchLZ(srcp, size, &lastOffset))) + { + // 圧縮可能な場合はフラグを立てる + LZCompFlags |= 0x1; + + // オフセットは上位4ビットと下位8ビットに分けて格納 + *dstp++ = (u8)((lastLength - 3) << 4 | (lastOffset - 1) >> 8); + *dstp++ = (u8)((lastOffset - 1) & 0xff); + LZDstCount += 2; + LZSlide(srcp, lastLength); + srcp += lastLength; + size -= lastLength; + } + else + { + // 圧縮なし + LZSlide(srcp, 1); + *dstp++ = *srcp++; + size--; + LZDstCount++; + } + } // 8回ループ終了 + *LZCompFlagsp = LZCompFlags; // フラグ系列を格納 + } + + // 16バイト境界アラインメント + // アラインメント用データ0 はデータサイズに含める + i = 0; + while (LZDstCount & (boundary - 1)) +// while ((LZDstCount + i) & 0x3) + { + *dstp++ = 0; + LZDstCount++; + i++; + } + + return LZDstCount; +} + +//-------------------------------------------------------- +// LZ77圧縮でスライド窓の中から最長一致列を検索します。 +// Arguments: startp データの開始位置を示すポインタ +// nextp 検索を開始するデータのポインタ +// remainSize 残りデータサイズ +// offset 一致したオフセットを格納する領域へのポインタ +// Return : 一致列が見つかった場合は TRUE +// 見つからなかった場合は FALSE +//-------------------------------------------------------- +static u8 SearchLZ(const u8 *nextp, u32 remainSize, u16 *offset) +{ + const u8 *searchp; + const u8 *headp, *searchHeadp; + u16 maxOffset; + u8 maxLength = 2; + u8 tmpLength; + s32 w_offset; + + if (remainSize < 3) + { + return 0; + } + + w_offset = LZByteTable[*nextp]; + + while (w_offset != -1) + { + if (w_offset < windowPos) + { + searchp = nextp - windowPos + w_offset; + } + else + { + searchp = nextp - windowLen - windowPos + w_offset; + } + + /* 無くても良いが、僅かに高速化する */ + if (*(searchp + 1) != *(nextp + 1) || *(searchp + 2) != *(nextp + 2)) + { + w_offset = LZOffsetTable[w_offset]; + continue; + } + + if (nextp - searchp < 2) + { + // VRAMは2バイトアクセスなので (VRAMからデータを読み出す場合があるため)、 + // 検索対象データは2バイト前からのデータにしなければならない。 + // + // オフセットは12ビットで格納されるため、4096以下 + break; + } + tmpLength = 3; + searchHeadp = searchp + 3; + headp = nextp + 3; + + while (((u32)(headp - nextp) < remainSize) && (*headp == *searchHeadp)) + { + headp++; + searchHeadp++; + tmpLength++; + + // データ長は4ビットで格納されるため、18以下 (3の下駄をはかせる) + if (tmpLength == (0xF + 3)) + { + break; + } + } + if (tmpLength > maxLength) + { + // 最大長オフセットを更新 + maxLength = tmpLength; + maxOffset = (u16)(nextp - searchp); + if (maxLength == (0xF + 3)) + { + // 一致長が最大なので、検索を終了する。 + break; + } + } + w_offset = LZOffsetTable[w_offset]; + } + + if (maxLength < 3) + { + return 0; + } + *offset = maxOffset; + return maxLength; +} + diff --git a/build/tools/makenorfirm/compress.h b/build/tools/makenorfirm/compress.h new file mode 100644 index 00000000..fcdb3ac0 --- /dev/null +++ b/build/tools/makenorfirm/compress.h @@ -0,0 +1,37 @@ +/*---------------------------------------------------------------------------* + Project: TwlFirm - tools - makenorfirm + File: compress.h + + Copyright 2007 Nintendo. All rights reserved. + + These coded instructions, statements, and computer programs contain + proprietary information of Nintendo of America Inc. and/or Nintendo + Company Ltd., and are protected by Federal copyright law. They may + not be disclosed to third parties or copied or duplicated in any form, + in whole or in part, without the prior written consent of Nintendo. + + $Log: $ + $NoKeywords: $ + *---------------------------------------------------------------------------*/ +#ifndef COMPRESS_H_ +#define COMPRESS_H_ + +#include "misc.h" + + +/*---------------------------------------------------------------------------* + Name: MI_CompressLZ + + Description: LZ77圧縮を行なう関数 + + Arguments: srcp 圧縮元データへのポインタ + size 圧縮元データサイズ + dstp 圧縮先データへのポインタ + 圧縮元データよりも大きいサイズのバッファが必要です。 + + Returns: 圧縮後のデータサイズ。 + *---------------------------------------------------------------------------*/ +u32 LZCompWrite(u8 *srcp, u32 size, u8 *dstp, int boundary); + + +#endif //COMPRESS_H_ diff --git a/build/tools/makenorfirm/defval.c b/build/tools/makenorfirm/defval.c new file mode 100644 index 00000000..679fb0e6 --- /dev/null +++ b/build/tools/makenorfirm/defval.c @@ -0,0 +1,315 @@ +/*---------------------------------------------------------------------------* + Project: NitroSDK - tools - makerom + File: defval.c + + Copyright 2003-2006 Nintendo. All rights reserved. + + These coded instructions, statements, and computer programs contain + proprietary information of Nintendo of America Inc. and/or Nintendo + Company Ltd., and are protected by Federal copyright law. They may + not be disclosed to third parties or copied or duplicated in any form, + in whole or in part, without the prior written consent of Nintendo. + + $Log: defval.c,v $ + Revision 1.10 2006/01/18 02:11:19 kitase_hirotake + do-indent + + Revision 1.9 2005/02/28 05:26:03 yosizaki + do-indent. + + Revision 1.8 2004/08/05 13:50:13 yasu + Support -M option + + Revision 1.7 2004/06/29 04:55:40 yasu + Use VBuffer to resolve variables + + Revision 1.6 2004/06/23 07:51:02 yasu + fix a bug as illegal memory freeing at ResolveDefVal() + + Revision 1.5 2004/05/27 00:40:49 yasu + care also about current directory (dot ".") + + Revision 1.4 2004/05/27 00:25:46 yasu + care about double-dots ".." for defvalue option :r, :e + + Revision 1.3 2004/05/27 00:11:19 yasu + fix a error when searching a "dot" of file extension + + Revision 1.2 2004/05/26 12:02:47 yasu + support :h, :t, :r, :e option for variable name + + Revision 1.1 2004/03/26 05:06:45 yasu + support variables like as -DNAME=VALUE + + $NoKeywords: $ + *---------------------------------------------------------------------------*/ +#include +#include // getenv() +#include // strcasecmp() +#include // getopt() +#include "misc.h" +#include "defval.h" + +typedef struct tValdef +{ + struct tValdef *next; + char *name; + char *value; +} +tValdef; + +tValdef *valdef_top = NULL; + + +// +// Add new define value via file +// +// opt : "DEFINE=VALUE" +// +BOOL AddDefValFromFile(char *filename) +{ + char *buffer; + int buffer_size; + int read_size; + FILE *fp; + + if (filename[0] == '-' && filename[1] == '\0') + { + fp = stdin; + } + else if (NULL == (fp = fopen(filename, "rb"))) + { + fprintf(stderr, "Cannot open file \"%s\".\n", filename); + return FALSE; + } + + buffer_size = DEFVAL_DEFAULT_BUFFER_SIZE; + + if (NULL == (buffer = malloc(buffer_size))) + { + fprintf(stderr, "Cannot allocate memory.\n"); + return FALSE; + } + + read_size = 0; + + while (NULL != fgets(buffer + read_size, buffer_size - read_size, fp)) + { + read_size = strlen(buffer); + + if (read_size == buffer_size - 1 && buffer[read_size - 1] != '\n') + { + buffer_size *= 2; + + if (NULL == (buffer = realloc(buffer, buffer_size))) + { + fprintf(stderr, "Cannot allocate memory.\n"); + return FALSE; + } + continue; + } + + AddDefVal(buffer); + read_size = 0; + } + + if (fp != stdin) + { + fclose(fp); + } + free(buffer); + + return TRUE; +} + + +// +// Add new define value +// +// opt : "DEFINE=VALUE" +// +void AddDefVal(char *opt) +{ + int i; + tValdef *t; + + for (i = 0;; i++) + { + if ('=' == opt[i] || '\0' == opt[i]) + { + break; + } + } + + if (i > 0) + { + t = Alloc(sizeof(tValdef)); + t->name = strncpy(Alloc(i + 1), opt, i); + t->name[i] = '\0'; + + if (opt[i] == '=') + { + i++; + } + t->value = strdup(opt + i); + + t->next = valdef_top; + valdef_top = t; + + debug_printf("DEFINE:$(%s)=\"%s\"\n", t->name, t->value); + } + return; +} + +// +// Search define value +// +// Return: value of specified name +// +char *SearchDefVal(char *name) +{ + tValdef *t; + + for (t = valdef_top; t; t = t->next) + { + if (!strcmp(t->name, name)) + { + return t->value; + } + } + + return getenv(name); +} + + +// +// Search define value and Modify it by : option +// +// Return: duplicated value of specified name modified by :x option +// +char *SearchDefValWithOption(char *name) +{ + int len_name = strlen(name); + char *value; + char option = '\0'; + + if (len_name > 2 && name[len_name - 2] == ':') + { + name[len_name - 2] = '\0'; + option = name[len_name - 1]; + } + + value = SearchDefVal(name); + + if (value) + { + int value_len = strlen(value); + int col_dot = value_len; + int col_filename = 0; + int i; + + for (i = 0; i < value_len; i++) + { + switch (value[i]) + { + case '.': + if (col_filename == i && + (value[i + 1] == '\0' || (value[i + 1] == '.' && value[i + 2] == '\0'))) + { + i = value_len; // exit loop if last entry is . or .. + } + else + { + col_dot = i; // Save the last dot column + } + break; + + case '/': + case '\\': + case ':': + col_filename = i + 1; // Save the last filename + col_dot = value_len; // Reset dot position + break; + + default: + ; + } + } + + switch (option) + { + case 'h': // Dirname with the last slash + value = strdup(value); + value[col_filename] = '\0'; + break; + + case 't': // Filename + value = strdup(value + col_filename); + break; + + case 'r': // All without . file extension + value = strdup(value); + value[col_dot] = '\0'; + break; + + case 'e': // File extension + value = strdup(value + col_dot + 1); + break; + + default: + value = strdup(value); + } + } + return value; +} + + +// +// Resolve define value +// +// Return: new string +// +char *ResolveDefVal(char *str) +{ + int i, j; + char *val; + VBuffer buf; + + InitVBuffer(&buf); + + for (i = 0; '\0' != str[i]; i++) + { + // search $(XXX) + if ('$' == str[i] && '(' == str[i + 1]) + { + for (j = i + 2; '\0' != str[j]; j++) + { + if (')' == str[j]) + { + str[j] = '\0'; + + // get value of XXX + val = SearchDefValWithOption(&str[i + 2]); + + // copy value of XXX + if (val) + { + char *s = val; + + while (*s) + { + PutVBuffer(&buf, *s); + s++; + } + free(val); + } + i = j; + goto next; + } + } + } + PutVBuffer(&buf, str[i]); + next:; + } + return GetVBuffer(&buf); // pass allocated buffer, should be freed by caller +} diff --git a/build/tools/makenorfirm/defval.h b/build/tools/makenorfirm/defval.h new file mode 100644 index 00000000..71355101 --- /dev/null +++ b/build/tools/makenorfirm/defval.h @@ -0,0 +1,38 @@ +/*---------------------------------------------------------------------------* + Project: NitroSDK - tools - makerom + File: defval.h + + Copyright 2003-2006 Nintendo. All rights reserved. + + These coded instructions, statements, and computer programs contain + proprietary information of Nintendo of America Inc. and/or Nintendo + Company Ltd., and are protected by Federal copyright law. They may + not be disclosed to third parties or copied or duplicated in any form, + in whole or in part, without the prior written consent of Nintendo. + + $Log: defval.h,v $ + Revision 1.4 2006/01/18 02:11:19 kitase_hirotake + do-indent + + Revision 1.3 2005/02/28 05:26:03 yosizaki + do-indent. + + Revision 1.2 2004/08/05 13:50:13 yasu + Support -M option + + Revision 1.1 2004/03/26 05:06:45 yasu + support variables like as -DNAME=VALUE + + $NoKeywords: $ + *---------------------------------------------------------------------------*/ +#ifndef DEFVAL_H_ +#define DEFVAL_H_ + +#define DEFVAL_DEFAULT_BUFFER_SIZE (1024) + +BOOL AddDefValFromFile(char *filename); +void AddDefVal(char *opt); +char *SearchDefVal(char *name); +char *ResolveDefVal(char *str); + +#endif //DEFVAL_H_ diff --git a/build/tools/makenorfirm/elf.h b/build/tools/makenorfirm/elf.h new file mode 100644 index 00000000..c360cd33 --- /dev/null +++ b/build/tools/makenorfirm/elf.h @@ -0,0 +1,431 @@ +/*---------------------------------------------------------------------------* + Project: TwlFirm - ELF + File: elf.h + + Copyright 2007 Nintendo. All rights reserved. + + These coded instructions, statements, and computer programs contain + proprietary information of Nintendo of America Inc. and/or Nintendo + Company Ltd., and are protected by Federal copyright law. They may + not be disclosed to third parties or copied or duplicated in any form, + in whole or in part, without the prior written consent of Nintendo. + *---------------------------------------------------------------------------*/ + +#ifndef ELF_H_ +#define ELF_H_ + +#include "misc.h" + +/*--------------------------------------------------------- + 型定義 + --------------------------------------------------------*/ +typedef u32 Elf32_Addr; /* size:4, align:4 Unsigned program address */ +typedef u16 Elf32_Half; /* size:2, align:2 Unsigned medium int */ +typedef u32 Elf32_Off; /* size:4, align:4 Unsigned file offset */ +typedef s32 Elf32_Sword; /* size:4, align:4 Signed large int */ +typedef u32 Elf32_Word; /* size:4, align:4 Unsigned large int */ + +/*--------------------------------------------------------- + ELF Header + --------------------------------------------------------*/ +/* e_identのインデックス */ +#define EI_MAG0 0 /* File identification */ +#define EI_MAG1 1 /* File identification */ +#define EI_MAG2 2 /* File identification */ +#define EI_MAG3 3 /* File identification */ +#define EI_CLASS 4 /* File class 0=invalid, 1=32bit, 2=64bit */ +#define EI_DATA 5 /* Data encoding 0=invalid, 1=LSB, 2=MSB */ +#define EI_VERSION 6 /* File version 現在は1 */ +#define EI_PAD 7 /* Start of padding bytes */ +#define EI_NIDENT 16 /* Size of e_ident[] */ + +typedef struct { + unsigned char e_ident[EI_NIDENT]; + Elf32_Half e_type; /* ELFの形式(再配置可能, 実行可能など) */ + Elf32_Half e_machine; /* ファイルで要求されるアーキテクチャ */ + Elf32_Word e_version; /* ELFフォーマットのバージョン(現在は1) */ + Elf32_Addr e_entry; /* プログラムのエントリポイント。指定無しなら0。 */ + Elf32_Off e_phoff; /* プログラムヘッダテーブルのファイル先頭からのオフセット */ + Elf32_Off e_shoff; /* セクションヘッダテーブルのファイル先頭からのオフセット */ + Elf32_Word e_flags; /* プロセッサ固有のフラグ */ + Elf32_Half e_ehsize; /* ELFヘッダのサイズ */ + Elf32_Half e_phentsize; /* 1プログラムヘッダのサイズ */ + Elf32_Half e_phnum; /* プログラムヘッダの数 */ + Elf32_Half e_shentsize; /* 1セクションヘッダのサイズ */ + Elf32_Half e_shnum; /* セクションヘッダの数 */ + Elf32_Half e_shstrndx; /* セクション名文字列テーブルセクションへのインデックス */ +} Elf32_Ehdr; + +/* e_ident[EI_*]の中身定義 */ +#define ELFMAG0 0x7f +#define ELFMAG1 'E' +#define ELFMAG2 'L' +#define ELFMAG3 'F' +#define ELFCLASSNONE 0 /* invalid */ +#define ELFCLASS32 1 /* ARM and Thumb processors use 32-bit ELF. */ +#define ELFCLASS64 2 +#define ELFDATANONE 0 /* invalid */ +#define ELFDATA2LSB 1 /* little-endian */ +#define ELFDATA2MSB 2 /* big-endian */ + + +/* [e_type] */ +#define ET_NONE 0 /* No file type */ +#define ET_REL 1 /* Re-locatable file */ +#define ET_EXEC 2 /* Executable file */ +#define ET_DYN 3 /* Shared object file */ +#define ET_CORE 4 /* Core file */ +#define ET_LOPROC 0xff00 /* Processor-specific */ +#define ET_HIPROC 0xffff /* Processor-specific */ + +/* [e_machine] */ +#define EM_NONE 0 /* No machine */ +#define EM_M32 1 +#define EM_SPARC 2 +#define EM_386 3 +#define EM_68K 4 +#define EM_88K 5 +#define EM_860 7 +#define EM_MIPS 8 +#define EM_MIPS_RS4_BE 10 +#define EM_ARM 40 /* ARM/Thumb Architecture */ + + +/* [e_version] This member identifies the object file version.*/ +#define EV_NONE 0 /* Invalid version */ +#define EV_CURRENT 1 /* Current version */ + + +/* + ARM-specific e_flags + e_flags Field Value Meaning + EF_ARM_HASENTRY (0x02) e_entry contains a program-loader entry point + (see section 4.1.1, Entry points, below). + EF_ARM_SYMSARESORTED (0x04) Each subsection of the symbol table is sorted by symbol value + (see section 4.4.8, Symbol table order, below) + EF_ARM_DYNSYMSUSESEGIDX (0x8) Symbols in dynamic symbol tables that are defined in sections + included in program segment n have st_shndx = n + 1. + (see section 4.4.9, Dynamic symbol table entries, below). + EF_ARM_MAPSYMSFIRST (0x10) Mapping symbols precede other local symbols in the symbol table + (see section 4.4.8, Symbol table order, below). + + EF_ARM_EABIMASK (0xFF000000)(current version is 0x02000000) + This masks an 8-bit version number, the version of the ARM + EABI to which this ELF file conforms. This EABI is version 2. A + value of 0 denotes unknown conformance. +*/ +#define EF_ARM_HASENTRY 0x02 +#define EF_ARM_SYMSARESORTED 0x04 +#define EF_ARM_DYNSYMSUSESEGIDX 0x8 +#define EF_ARM_MAPSYMSFIRST 0x10 +#define EF_ARM_EABIMASK 0xFF000000 + + +/*--------------------------------------------------------- + Program headers + --------------------------------------------------------*/ +typedef struct { + Elf32_Word p_type; + Elf32_Off p_offset; + Elf32_Addr p_vaddr; + Elf32_Addr p_paddr; + Elf32_Word p_filesz; + Elf32_Word p_memsz; + Elf32_Word p_flags; + Elf32_Word p_align; +} Elf32_Phdr; + +/* [p_type] */ +#define PT_NULL 0 /* 使われないエントリで、他のメンバの値の意味は未定義 */ +#define PT_LOAD 1 /* 実行時にロードされるセグメント */ +#define PT_DYNAMIC 2 /* 動的構造体配列を保持するセグメント */ +#define PT_INTERP 3 /* ファイルの解釈に使われるインタプリタのパスを保持するセグメント */ +#define PT_NOTE 4 /* ファイルの解釈には使われない情報を保持するセグメント */ +#define PT_SHLIB 5 /* 予約 */ +#define PT_PHDR 6 /* プログラムヘッダテーブル(プログラムのメモリイメージの一部である場合のみ存在) */ +//#define PT_TLS ? /* スレッド局所記憶領域のテンプレート */ + +#define PT_LOOS 0x60000000 /* OS固有に予約された領域 */ +#define PT_HIOS 0x6fffffff + +#define PT_LOPROC 0x70000000 /* プロセッサ固有に予約された領域 */ +#define PT_HIPROC 0x7fffffff + +/* [p_flags]*/ +#define PF_X 1 /*実行可能*/ +#define PF_W 2 /*書き込み可能*/ +#define PF_R 4 /*読み出し可能*/ +#define PF_ARM_SB 0x10000000 /*The segment contains the location addressed by the static base*/ +#define PF_ARM_PI 0x20000000 /*The segment is position-independent*/ +#define PF_ARM_ENTRY 0x80000000 /*The segment contains the entry point*/ +#define PF_MASKPROC 0xf0000000 + + +/*--------------------------------------------------------- + Section headers + --------------------------------------------------------*/ +typedef struct { + Elf32_Word sh_name; /*セクションヘッダ文字列テーブルセクションのインデックス*/ + Elf32_Word sh_type; /* タイプ(下記定義参照) */ + Elf32_Word sh_flags; + Elf32_Addr sh_addr; /* */ + Elf32_Off sh_offset; /* ファイルの先頭からのオフセット */ + Elf32_Word sh_size; /* バイト単位のサイズ */ + Elf32_Word sh_link; /* sh_typeによって値の意味が変わる */ + Elf32_Word sh_info; /* sh_typeによって値の意味が変わる */ + Elf32_Word sh_addralign; /* アラインメント制限(0or1で制限なし,4で4ByteAlign) */ + Elf32_Word sh_entsize; /* 固定サイズのエントリテーブルがある場合、1要素のサイズ */ +} Elf32_Shdr; + +/* sh_addr mod sh_addralign = 0 でなければならない */ + +/* Section Types, [sh_type] */ +#define SHT_NULL 0 +#define SHT_PROGBITS 1 +#define SHT_SYMTAB 2 +#define SHT_STRTAB 3 +#define SHT_RELA 4 +#define SHT_HASH 5 +#define SHT_DYNAMIC 6 +#define SHT_NOTE 7 +#define SHT_NOBITS 8 +#define SHT_REL 9 +#define SHT_SHLIB 10 +#define SHT_DYNSYM 11 +#define SHT_LOPROC 0x70000000 +#define SHT_HIPROC 0x7fffffff +#define SHT_LOUSER 0x80000000 +#define SHT_HIUSER 0xffffffff + + +/* [sh_flags] */ +#define SHF_WRITE 0x1 +#define SHF_ALLOC 0x2 +#define SHF_EXECINSTR 0x4 +#define SHF_MASKPROC 0xf0000000 +/* ARM-EABI-specific */ +#define SHF_ENTRYSECT 0x10000000 /* The section contains an entry point. */ +#define SHF_COMDEF 0x80000000 /* The section may be multiply defined in the input to a link step. */ +/* others */ +#define SHF_LINK_ORDER 0x80 + +/*セクションインデックス*/ +//Sym->st_shndxなど +#define SHN_UNDEF 0 +#define SHN_LORESERVE 0xff00 +#define SHN_LOPROC 0xff00 +#define SHN_HIPROC 0xff1f +#define SHN_ABS 0xfff1 +#define SHN_COMMON 0xfff2 +#define SHN_HIRESERVE 0xffff + + +//ここからはヘッダでなく実体データ構造 + +/*--------------------------------------------------------- + Symbol Table Entry + --------------------------------------------------------*/ +typedef struct { + Elf32_Word st_name; /* シンボル文字列テーブルのインデックス */ + Elf32_Addr st_value; /* おそらく関連するセクション内でのオフセット値 */ + Elf32_Word st_size; /* サイズがないか、不明な場合は 0 */ + unsigned char st_info; /* バインド と タイプ */ + unsigned char st_other; /* 現在は 0 が入る */ + Elf32_Half st_shndx; /* 関連するセクションヘッダテーブルのインデックス */ +} Elf32_Sym; + + +/* st_info */ +#define ELF32_ST_BIND(i) ((i)>>4) +#define ELF32_ST_TYPE(i) ((i)&0xf) +#define ELF32_ST_INFO(b,t) (((b)<<4)+((t)&0xf)) + +/* st_info の BIND */ +#define STB_LOCAL 0 +#define STB_GLOBAL 1 +#define STB_WEAK 2 +#define STB_LOPROC 13 +#define STB_HIPROC 15 + +/* st_info の TYPE */ +#define STT_NOTYPE 0 /*未定義*/ +#define STT_OBJECT 1 /*データオブジェクト*/ +#define STT_FUNC 2 +#define STT_SECTION 3 +#define STT_FILE 4 +#define STT_LOPROC 13 +#define STT_HIPROC 15 + + +/*--------------------------------------------------------- + Relocation Entry + --------------------------------------------------------*/ +typedef struct { + Elf32_Addr r_offset; + Elf32_Word r_info; +} Elf32_Rel; + +typedef struct { + Elf32_Addr r_offset; + Elf32_Word r_info; + Elf32_Sword r_addend; +} Elf32_Rela; + +#define ELF32_R_SYM(i) ((i)>>8) +#define ELF32_R_TYPE(i) ((unsigned char)(i)) +#define ELF32_R_INFO(s,t) (((s)<<8)+(unsigned char)(t)) + + +/* r_info の TYPE */ +#define R_ARM_NONE 0 /* Any No relocation. Encodes dependencies between sections. */ +#define R_ARM_PC24 1 /* ARM B/BL S . P + A */ +#define R_ARM_ABS32 2 /* 32-bit word S + A */ +#define R_ARM_REL32 3 /* 32-bit word S . P + A */ +#define R_ARM_PC13 4 /* ARM LDR r, [pc,…] S . P + A */ +#define R_ARM_ABS16 5 /* 16-bit half-word S + A */ +#define R_ARM_ABS12 6 /* ARM LDR/STR S + A */ +#define R_ARM_THM_ABS5 7 /* Thumb LDR/STR S + A */ +#define R_ARM_ABS8 8 /* 8-bit byte S + A */ +#define R_ARM_SBREL32 9 /* 32-bit word S . B + A */ +#define R_ARM_THM_PC22 10 /* Thumb BL pair S . P+ A */ +#define R_ARM_THM_PC8 11 /* Thumb LDR r, [pc,…] S . P + A */ +#define R_ARM_AMP_VCALL9 12 /* AMP VCALL Obsolete.SA-1500 only. */ +#define R_ARM_SWI24 13 /* ARM SWI S + A */ +#define R_ARM_THM_SWI8 14 /* Thumb SWI S + A */ +#define R_ARM_XPC25 15 /* ARM BLX S . P+ A */ +#define R_ARM_THM_XPC22 16 /* Thumb BLX pair S . P+ A */ + +/* 17-31, reserved to ARM Linux */ +//17-19 Reserved to ARM LINUX +#define R_ARM_COPY 20 /* 32 bit word Copy symbol at dynamic link time. */ +#define R_ARM_GLOB_DAT 21 /* 32 bit word Create GOT entry. */ +#define R_ARM_JUMP_SLOT 22 /* 32 bit word Create PLT entry. */ +#define R_ARM_RELATIVE 23 /* 32 bit word Adjust by program base. */ +#define R_ARM_GOTOFF 24 /* 32 bit word Offset relative to start of GOT. */ +#define R_ARM_GOTPC 25 /* 32 bit word Insert address of GOT. */ +#define R_ARM_GOT32 26 /* 32 bit word Entry in GOT. */ +#define R_ARM_PLT32 27 /* ARM BL Entry in PLT. */ + +/* 28-31 Reserved to ARM LINUX */ +#define R_ARM_ALU_PCREL_7_0 32 /* ARM ADD/SUB (S . P + A) & 0x000000FF */ +#define R_ARM_ALU_PCREL_15_8 33 /* ARM ADD/SUB (S . P + A) & 0x0000FF00 */ +#define R_ARM_ALU_PCREL_23_15 34 /* ARM ADD/SUB (S . P + A) & 0x00FF0000 */ +#define R_ARM_LDR_SBREL_11_0 35 /* ARM LDR/STR (S . B + A) & 0x00000FFF */ +#define R_ARM_ALU_SBREL_19_12 36 /* ARM ADD/SUB (S . B + A) & 0x000FF000 */ +#define R_ARM_ALU_SBREL_27_20 37 /* ARM ADD/SUB (S . B + A) & 0x0FF00000 */ + +#define R_ARM_TARGET1 38 +#define R_ARM_ROSEGREL32 39 +#define R_ARM_V4BX 40 +#define R_ARM_TARGET2 41 +#define R_ARM_PREL31 42 + +/* 96-111, reserved to ARM g++ */ +#define R_ARM_GNU_VTENTRY 100 /* 32 bit word Record C++ vtable entry. */ +#define R_ARM_GNU_VTINHERIT 101 /* 32 bit word Record C++ member usage. */ +#define R_ARM_THM_PC11 102 /* Thumb B S . P + A */ +#define R_ARM_THM_PC9 103 /* Thumb B S . P + A */ + +/* 112-127, reserved for private experiments */ + +/* 128-248, reserved to ARM */ +#define R_ARM_RXPC25 249 /* ARM BLX (ΔS . ΔP) + A #define For calls between program segments. */ +#define R_ARM_RSBREL32 250 /* Word (ΔS . ΔSB) + A For an offset from SB, the static base. */ +#define R_ARM_THM_RPC22 251 /* Thumb BL/BLX pair (ΔS . ΔP) + A For calls between program segments. */ +#define R_ARM_RREL32 252 /* Word (ΔS . ΔP) + A For on offset between two segments. */ +#define R_ARM_RABS32 253 /* Word ΔS + A For the address of a location in the target segment. */ +#define R_ARM_RPC24 254 /* ARM B/BL (ΔS . ΔP) + A For calls between program segments. */ +#define R_ARM_RBASE 255 /* None None.Identifies the segment being relocated by the following + relocation directives. The ARM EABI poses two problems for relocating + executables and shared objects encoded in */ + + +// shirait +#define R_ARM_LDR_PC_G0 4 //LDR + +#define R_ARM_ABS12 6 //LDR, STR + +#define R_ARM_THM_CALL 10 //R_ARM_THM_PC22と同じ + +#define R_ARM_CALL 28 //BL/BLX +#define R_ARM_JUMP24 29 //B/BL +#define R_ARM_THM_JUMP24 30 + +#define R_ARM_MOVW_ABS_NC 43 //MOVW +#define R_ARM_MOVT_ABS 44 //MOVT +#define R_ARM_MOVW_PREL_NC 45 //MOVW +#define R_ARM_MOVT_PREL 46 //MOVT + +#define R_ARM_ALU_PC_G0_NC 57 //ADD, SUB +#define R_ARM_ALU_PC_G0 58 //ADD, SUB +#define R_ARM_ALU_PC_G1_NC 59 //ADD, SUB +#define R_ARM_ALU_PC_G1 60 //ADD, SUB +#define R_ARM_ALU_PC_G2 61 //ADD, SUB +#define R_ARM_LDR_PC_G1 62 //LDR, STR, LDRB, STRB +#define R_ARM_LDR_PC_G2 63 //LDR, STR, LDRB, STRB +#define R_ARM_LDRS_PC_G0 64 //LDRD, STRD, LDRH, STRH, LDRSH, LDRSB +#define R_ARM_LDRS_PC_G1 65 //LDRD, STRD, LDRH, STRH, LDRSH, LDRSB +#define R_ARM_LDRS_PC_G2 66 //LDRD, STRD, LDRH, STRH, LDRSH, LDRSB +#define R_ARM_LDC_PC_G0 67 //LDC, STC +#define R_ARM_LDC_PC_G1 68 //LDC, STC +#define R_ARM_LDC_PC_G2 69 //LDC, STC +#define R_ARM_ALU_SB_G0_NC 70 //ADD, SUB +#define R_ARM_ALU_SB_G0 71 //ADD, SUB +#define R_ARM_ALU_SB_G1_NC 72 //ADD, SUB +#define R_ARM_ALU_SB_G1 73 //ADD, SUB +#define R_ARM_ALU_SB_G2 74 //ADD, SUB +#define R_ARM_LDR_SB_G0 75 //LDR, STR, LDRB, STRB +#define R_ARM_LDR_SB_G1 76 //LDR, STR, LDRB, STRB +#define R_ARM_LDR_SB_G2 77 //LDR, STR, LDRB, STRB +#define R_ARM_LDRS_SB_G0 78 //LDRD, STRD, LDRH, STRH, LDRSH, LDRSB +#define R_ARM_LDRS_SB_G1 79 //LDRD, STRD, LDRH, STRH, LDRSH, LDRSB +#define R_ARM_LDRS_SB_G2 80 //LDRD, STRD, LDRH, STRH, LDRSH, LDRSB +#define R_ARM_LDC_SB_G0 81 //LDC, STC +#define R_ARM_LDC_SB_G1 82 //LDC, STC +#define R_ARM_LDC_SB_G2 83 //LDC, STC +#define R_ARM_MOVW_BREL_NC 84 //MOVW +#define R_ARM_MOVT_BREL 85 //MOVT +#define R_ARM_MOVW_BREL 86 //MOVW + +#define R_ARM_GOT_BREL12 97 //LDR +#define R_ARM_GOTOFF12 98 //LDR, STR + +#define R_ARM_TLS_LDO12 109 //LDR, STR +#define R_ARM_TLS_LE12 110 //LDR, STR +#define R_ARM_TLS_TE12GP 111 //LDR + + + +/*--------------------------------------------------------- + Dynamic Section elf_v1.2 + --------------------------------------------------------*/ +typedef struct { + Elf32_Sword d_tag; + union { + Elf32_Word d_val; + Elf32_Addr d_ptr; + } d_un; +} Elf32_Dyn; + + +/* Additional symbol types for Thumb. */ +#define STT_ARM_TFUNC STT_LOPROC /* A Thumb function. */ +#define STT_ARM_16BIT STT_HIPROC /* A Thumb label. */ + + + + + + + + + +/*--------------------------------------------------------- + ELFヘッダを読み出す + --------------------------------------------------------*/ +void *ELF_LoadELFHeader(const void *buf, Elf32_Ehdr *ehdr); + + + +#endif /* ELF_H_ */ + diff --git a/build/tools/makenorfirm/format_nlist.h b/build/tools/makenorfirm/format_nlist.h new file mode 100644 index 00000000..06c99d0e --- /dev/null +++ b/build/tools/makenorfirm/format_nlist.h @@ -0,0 +1,53 @@ +/*---------------------------------------------------------------------------* + Project: TwlFirm - tools - makenorfirm + File: format_nlist.h + + Copyright 2007 Nintendo. All rights reserved. + + These coded instructions, statements, and computer programs contain + proprietary information of Nintendo of America Inc. and/or Nintendo + Company Ltd., and are protected by Federal copyright law. They may + not be disclosed to third parties or copied or duplicated in any form, + in whole or in part, without the prior written consent of Nintendo. + + $Log: $ + $NoKeywords: $ + *---------------------------------------------------------------------------*/ +#ifndef FORMAT_NLIST_H_ +#define FORMAT_NLIST_H_ + +#include +#include "misc.h" +#include "path.h" + +#define SPECFILE_MAGIC_ID "#NORSF" + +#define CRC16_INIT_VALUE 0xffff + +//--------------------------------------------------------------------------- +// Banner Spec File +//--------------------------------------------------------------------------- + +// Command List +typedef struct +{ + char *string; + BOOL (*funcp) (char *, int num); + +} +tCommandDesc; + + +// F Command +typedef struct +{ + u32 offsetStart; + u32 offsetEnd; + u32 padding; + char fullPathSrc[FILENAME_MAX]; + +} +tFileDesc; + + +#endif // FORMAT_NLIST_H_ diff --git a/build/tools/makenorfirm/format_rom.h b/build/tools/makenorfirm/format_rom.h new file mode 100644 index 00000000..13cea6b1 --- /dev/null +++ b/build/tools/makenorfirm/format_rom.h @@ -0,0 +1,50 @@ +/*---------------------------------------------------------------------------* + Project: TwlFirm - tools - makenorfirm + File: format_rom.h + + Copyright 2007 Nintendo. All rights reserved. + + These coded instructions, statements, and computer programs contain + proprietary information of Nintendo of America Inc. and/or Nintendo + Company Ltd., and are protected by Federal copyright law. They may + not be disclosed to third parties or copied or duplicated in any form, + in whole or in part, without the prior written consent of Nintendo. + + $Log: $ + $NoKeywords: $ + *---------------------------------------------------------------------------*/ +#ifndef FORMAT_ROM_H_ +#define FORMAT_ROM_H_ + +#include "misc.h" +#include + + +#define DEFAULT_ALIGN 16 +#define FIRM_ALIGN DEFAULT_ALIGN +#define FIRM_ALIGN_MASK (FIRM_ALIGN - 1) + +#define DEFAULT_HOSTROOT "." +#define DEFAULT_ROOT "/" + +#define DEFAULT_REJECT_1 "CVS" +#define DEFAULT_REJECT_2 "vssver.scc" + +#define FORMAT_VERSION "1.0" + +#define ENTRYNAME_MAX 127 + +#define DEFAULT_LISTFILE "default.nlf" + +#define DEFAULT_NORFIRM_SUFFIX ".nor" +#define DEFAULT_SPECFILE_SUFFIX ".norsf" + +/*===========================================================================* + * ROM FORMAT + *===========================================================================*/ + +//--------------------------------------------------------------------------- +// ROM HEADER +//--------------------------------------------------------------------------- + +#endif //FORMAT_ROM_H_ diff --git a/build/tools/makenorfirm/format_sign.h b/build/tools/makenorfirm/format_sign.h new file mode 100644 index 00000000..e5cc43df --- /dev/null +++ b/build/tools/makenorfirm/format_sign.h @@ -0,0 +1,31 @@ +/*---------------------------------------------------------------------------* + Project: TwlFirm - SS + File: format_sign.h + + Copyright 2006 Nintendo. All rights reserved. + + These coded instructions, statements, and computer programs contain + proprietary information of Nintendo of America Inc. and/or Nintendo + Company Ltd., and are protected by Federal copyright law. They may + not be disclosed to third parties or copied or duplicated in any form, + in whole or in part, without the prior written consent of Nintendo. + + $Log: $ + $NoKeywords: $ + *---------------------------------------------------------------------------*/ +#ifndef FIRM_MAKENORFIRM_ACSIGN_FORMAT_H_ +#define FIRM_MAKENORFIRM_ACSIGN_FORMAT_H_ + +#include "format_rom.h" + + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +/* FIRM_MAKENORFIRM_ACSIGN_FORMAT_H_ */ +#endif diff --git a/build/tools/makenorfirm/makenorfirm.c b/build/tools/makenorfirm/makenorfirm.c new file mode 100644 index 00000000..19a8cde0 --- /dev/null +++ b/build/tools/makenorfirm/makenorfirm.c @@ -0,0 +1,105 @@ +/*---------------------------------------------------------------------------* + Project: TwlFirm - tools - makenorfirm + File: makenorfirm.c + + Copyright 2007 Nintendo. All rights reserved. + + These coded instructions, statements, and computer programs contain + proprietary information of Nintendo of America Inc. and/or Nintendo + Company Ltd., and are protected by Federal copyright law. They may + not be disclosed to third parties or copied or duplicated in any form, + in whole or in part, without the prior written consent of Nintendo. + + $Log: $ + $NoKeywords: $ + *---------------------------------------------------------------------------*/ +#include +#include +#include // strcasecmp() +#include // getopt() +#include "makenorfirm.h" +#include "format_rom.h" +#include "path.h" +#include "defval.h" +#include "version.h" + +static int makenorfirm(const char *specFile, const char *norFile); + +//--------------------------------------------------------------------------- +// Main +//--------------------------------------------------------------------------- + +int main(int argc, char *argv[]) +{ + int n; + int narg; + char *norfirmFile; + + InitAppName(argv[0]); + + while ((n = getopt(argc, argv, "D:hvpd")) != -1) + { + switch (n) + { + case 'h': + case 'v': + goto usage; + + case 'D': + AddDefVal(optarg); + break; + + case 'p': + PrintMode = TRUE; + break; + + case 'd': + DebugMode = TRUE; + break; + + default: + break; + } + } + + narg = argc - optind; + if (narg > 0) + { + // Make SpecFile->NorfirmFile + norfirmFile = + strdup(narg > + 1 ? argv[optind + 1] : ChangeSuffix(argv[optind], DEFAULT_NORFIRM_SUFFIX)); + return makenorfirm(argv[optind], norfirmFile); + } + + usage: + { + char *makenorfirm = GetAppName(); + + fprintf(stderr, + "NITRO-SDK Development Tool - %s - Make norfirm file \n" + "Build %lu\n\n" + "Usage: %s [-phv] [-DNAME=VALUE ...] SPECFILE [NORFIRMFILE]\n\n", + makenorfirm, SDK_DATE_OF_LATEST_FILE, makenorfirm); + } + return 1; +} + + +//--------------------------------------------------------------------------- +// makenorfirm +//--------------------------------------------------------------------------- + +static int makenorfirm(const char *specFile, const char *norFile) +{ + debug_printf("makenorfirm(): '%s' -> '%s'\n", specFile, norFile); + + // Check identical + if (specFile && norFile && !strcasecmp(specFile, norFile)) + { + error("norfirm spec file is identical '%s'", norFile); + return 1; + } + + return OutputNorfirmFile(specFile, norFile) ? 0 : 1; +} diff --git a/build/tools/makenorfirm/makenorfirm.h b/build/tools/makenorfirm/makenorfirm.h new file mode 100644 index 00000000..79dc4fb7 --- /dev/null +++ b/build/tools/makenorfirm/makenorfirm.h @@ -0,0 +1,23 @@ +/*---------------------------------------------------------------------------* + Project: TwlFirm - tools - makenorfirm + File: makenorfirm.h + + Copyright 2007 Nintendo. All rights reserved. + + These coded instructions, statements, and computer programs contain + proprietary information of Nintendo of America Inc. and/or Nintendo + Company Ltd., and are protected by Federal copyright law. They may + not be disclosed to third parties or copied or duplicated in any form, + in whole or in part, without the prior written consent of Nintendo. + + $Log: $ + $NoKeywords: $ + *---------------------------------------------------------------------------*/ +#ifndef MAKENORFIRM_H_ +#define MAKENORFIRM_H_ + +#include "misc.h" + +BOOL OutputNorfirmFile(const char *specFile, const char *norFile); + +#endif //MAKENORFIRM_H_ diff --git a/build/tools/makenorfirm/misc.c b/build/tools/makenorfirm/misc.c new file mode 100644 index 00000000..e11bd855 --- /dev/null +++ b/build/tools/makenorfirm/misc.c @@ -0,0 +1,627 @@ +/*---------------------------------------------------------------------------* + Project: TwlFirm - tools - makenorfirm + File: misc.c + + Copyright 2007 Nintendo. All rights reserved. + + These coded instructions, statements, and computer programs contain + proprietary information of Nintendo of America Inc. and/or Nintendo + Company Ltd., and are protected by Federal copyright law. They may + not be disclosed to third parties or copied or duplicated in any form, + in whole or in part, without the prior written consent of Nintendo. + + $Log: $ + $NoKeywords: $ + *---------------------------------------------------------------------------*/ +#include +#include // calloc() +#include // free(), exit() +#include // setmode() +#include // stat() +#include // setmode() +#include // strlen() +#include // va_start(),va_end() +#include // localtime() + +#include +#include "misc.h" + +BOOL DebugMode = FALSE; +BOOL PrintMode = FALSE; +char *PubkeyFileName = NULL; + +/*---------------------------------------------------------------------------* + * File Output Utilities + * + * BOOL OpenFile( const char* filename ) + * void CloseFile( void ) + * BOOL CheckResult( void ) + * void PutBuffer( const void* ptr, int len ) + * void PutByte( u8 c ) + * void PutWord( u16 c ) + * void PutWord( u32 c ) + * void PutString( const char *str ) + *---------------------------------------------------------------------------*/ + +static FILE *OutFile = NULL; +static const char *FileName = NULL; +static BOOL Status = FALSE; + + +// +// File Open +// + +BOOL OpenFile(const char *filename) +{ + if (OutFile) + CloseFile(); + + if (filename) + { + if (NULL == (OutFile = fopen(filename, "wb+"))) + { + error("Can't write '%s'", filename); + Status = FALSE; + return FALSE; + } + } + else + { + setmode(1, O_BINARY); + OutFile = stdout; // out to console if filename == NULL + } + FileName = filename; + Status = TRUE; + + return TRUE; +} + + +// +// File Close +// + +BOOL CloseFile(void) +{ + if (OutFile) + { + if (FileName) + { + if (fclose(OutFile) == -1) + { + error("Can't close '%s'", FileName); + Status = FALSE; + } + } + else + { + setmode(1, O_TEXT); + } + } + OutFile = NULL; + + return Status; +} + + +// +// File Seek +// + +void SeekFile(long pos) +{ + if (OutFile && fseek(OutFile, pos, SEEK_SET)) + { + error("Can't seek '%s'", FileName ? FileName : ""); + CloseFile(); + Status = FALSE; + } +} + + +// +// Error Check +// + +BOOL CheckResult(void) +{ + return Status; +} + + +// +// Delete outfile +// + +void DeleteOutFile(void) +{ + // Delete outfile + if (FileName) + { + debug_printf("Delete '%s'\n", FileName); + (void)unlink(FileName); + FileName = NULL; + } + return; +} + + +// +// Buffer Output +// + +void PutBuffer(const void *ptr, int len) /* If error, close file */ +{ + if (OutFile && len != fwrite(ptr, 1, len, OutFile)) + { + error("Can't write buffer to '%s'", FileName ? FileName : ""); + CloseFile(); + Status = FALSE; + } +} + +// +// Buffer Input +// + +void GetBuffer(void *ptr, int len) /* If error, close file */ +{ + if (OutFile && len != fread(ptr, 1, len, OutFile)) + { + error("Can't read '%s'", FileName ? FileName : ""); + CloseFile(); + Status = FALSE; + } +} + +// +// Byte/Half/Word Output +// + +void PutByte(u8 c) +{ + PutBuffer(&c, 1); +} +void PutHalf(u16 c) +{ + PutBuffer(&c, 2); +} +void PutWord(u32 c) +{ + PutBuffer(&c, 4); +} +void PutString(const char *str) +{ + PutBuffer(str, strlen(str)); +} + + +// +// Printf +// + +void PrintString(const char *fmt, ...) +{ + char *buffer; + va_list va; + int nchars; + int bufsize = FILENAME_MAX; + + while (1) + { + buffer = Alloc(bufsize); + va_start(va, fmt); + nchars = vsnprintf(buffer, bufsize, fmt, va); + va_end(va); + + if (0 <= nchars && nchars < bufsize) + { + break; + } + + Free(&buffer); + bufsize *= 2; + } + + if (nchars > 0) + { + PutBuffer(buffer, nchars); + } + Free(&buffer); +} + + +/*---------------------------------------------------------------------------* + * File Read/Write Utilities + * + * int ReadFile( const char* filename, void** buffer ) + * + * Read a file to buffer allocated internally + * Return read size + * Add '\0' at tail of file for help to handle text file + * + * BOOL WriteFile( const char* filename, void** buffer, int size ) + *---------------------------------------------------------------------------*/ + +int ReadFile(const char *filename, void *filebuffer, int size) +{ + FILE *fp; + struct stat fileStat; + int fileSize, readSize; + void **buffer = (void **)filebuffer; + + /* Check file */ + if (stat(filename, &fileStat) || !S_ISREG(fileStat.st_mode)) + { + goto error; + } + fileSize = fileStat.st_size; + if (fileSize < 0) + { + goto error; + } + + /* Open file */ + fp = fopen(filename, "rb"); + if (!fp) + { + goto error; + } + + if (size && (size < fileSize)) + { + readSize = size; + } + else + { + readSize = fileSize; + } + + /* Read file */ + *buffer = Alloc(readSize + 1); /* error handle is done in Alloc() */ + /* size+1 for '\0' to handle text file */ + if (readSize != fread(*buffer, sizeof(char), readSize, fp)) + { + goto error_free_close; + } + + (*(char **)buffer)[readSize] = '\0'; /* Works as terminater if file is text file */ + + /* Close file */ + fclose(fp); + return readSize; + + error_free_close: + Free(buffer); + fclose(fp); + error: + error("Can't read '%s'", filename); + return -1; +} + + +int ReadFileWithPadding(const char *filename, void *filebuffer, int size, int boundary) +{ + FILE *fp; + struct stat fileStat; + int fileSize, readSize, padSize; + void **buffer = (void **)filebuffer; + + if (!boundary) + { + return ReadFile(filename, buffer, size); + } + + /* Check file */ + if (stat(filename, &fileStat) || !S_ISREG(fileStat.st_mode)) + { + goto error; + } + fileSize = fileStat.st_size; + if (fileSize < 0) + { + goto error; + } + + /* Open file */ + fp = fopen(filename, "rb"); + if (!fp) + { + goto error; + } + + if (size && (size < fileSize)) + { + readSize = size; + } + else + { + readSize = fileSize; + } + + padSize = (boundary - (readSize & (boundary-1))) & (boundary-1); + + /* Read file */ + *buffer = Alloc(readSize + padSize); /* error handle is done in Alloc() */ + /* size+1 for '\0' to handle text file */ + if (readSize != fread(*buffer, sizeof(char), readSize, fp)) + { + goto error_free_close; + } + + memset((char*)*buffer + readSize, 0, padSize); + + /* Close file */ + fclose(fp); + return readSize + padSize; + + error_free_close: + Free(buffer); + fclose(fp); + error: + error("Can't read '%s'", filename); + return -1; +} + + +BOOL WriteFile(const char *filename, void *buffer, int size) +{ + debug_printf("WriteFile %s\n", filename); + + (void)OpenFile(filename); + PutBuffer(buffer, size); + CloseFile(); + Free(&buffer); + return CheckResult(); +} + + +/*---------------------------------------------------------------------------* + * Time Format Utilities + * + * char* GetGMTime( const time_t time ) Show GMT + * char* GetTime( const time_t time ) Show local Time + *---------------------------------------------------------------------------*/ + +char *GetGMTime(const time_t time) +{ + static char timebuffer[32]; + strftime(timebuffer, sizeof(timebuffer), "%y/%m/%d-%H:%M:%S", gmtime(&time)); + return timebuffer; +} + + +char *GetTime(const time_t time) +{ + static char timebuffer[32]; + strftime(timebuffer, sizeof(timebuffer), "%y/%m/%d-%H:%M:%S", localtime(&time)); + return timebuffer; +} + + +/*---------------------------------------------------------------------------* + * Memory Allocation Utilities + * + * void* Alloc( size_t size ) + *---------------------------------------------------------------------------*/ + +void *Alloc(size_t size) +{ + void *t = calloc(1, size); + + if (t == NULL) + { + error("Can't allocate memory."); + exit(10); + } + return t; +} + + +void Free(void *p) +{ + void **ptr = (void **)p; + + if (*ptr) + { + free(*ptr); + (*ptr) = NULL; + } +} + + +/*---------------------------------------------------------------------------* + * VBuffer + * + * void PutVBuffer( VBuffer* vbuf, char c ) + *---------------------------------------------------------------------------*/ + +void PutVBuffer(VBuffer * vbuf, char c) +{ + int size; + char *tmp_buffer; + + if (vbuf->buffer == 0) + { + vbuf->size = VBUFFER_INITIAL_SIZE; + vbuf->buffer = Alloc(vbuf->size); // buffer is CALLOCed + } + size = strlen(vbuf->buffer); + + if (size >= vbuf->size - 1) + { + // Need buffer expansion + vbuf->size *= 2; + tmp_buffer = Alloc(vbuf->size); // buffer is CALLOCed + strcpy(tmp_buffer, vbuf->buffer); + Free(&vbuf->buffer); + vbuf->buffer = tmp_buffer; + } + vbuf->buffer[size] = c; + return; +} + +char *GetVBuffer(VBuffer * vbuf) +{ + return vbuf->buffer; +} + +void InitVBuffer(VBuffer * vbuf) +{ + vbuf->buffer = 0; + vbuf->size = 0; +} + +void FreeVBuffer(VBuffer * vbuf) +{ + Free(&vbuf->buffer); +} + + +/*---------------------------------------------------------------------------* + * File Path Utilities + * + * char* ChangeBackSlash( char* path ) + *---------------------------------------------------------------------------*/ + +char *ChangeBackSlash(char *path) +{ + char *p = path; + + while (*p) + { + if (*p == '\\') + { + *p = '/'; + } + p++; + } + return path; +} + + +/*---------------------------------------------------------------------------* + * Math + * + * u16 CalcCRC16( u16 start, u8 *data, int size ) + *---------------------------------------------------------------------------*/ + +static u16 crc16_table[16] = { + 0x0000, 0xCC01, 0xD801, 0x1400, + 0xF001, 0x3C00, 0x2800, 0xE401, + 0xA001, 0x6C00, 0x7800, 0xB401, + 0x5000, 0x9C01, 0x8801, 0x4400 +}; + +u16 CalcCRC16(u16 start, u8 *data, int size) +{ + u16 r1; + u16 total = start; + + while (size-- > 0) + { + // 下位4bit + r1 = crc16_table[total & 0xf]; + total = (total >> 4) & 0x0fff; + total = total ^ r1 ^ crc16_table[*data & 0xf]; + + // 上位4bit + r1 = crc16_table[total & 0xf]; + total = (total >> 4) & 0x0fff; + total = total ^ r1 ^ crc16_table[(*data >> 4) & 0xf]; + + data++; + } + return total; +} + + +/*---------------------------------------------------------------------------* + * for firm header + * + *---------------------------------------------------------------------------*/ + +static u8 ConvertAlign( u32 ofs ) +{ + u8 i; + + ofs /= 4; + for (i=0; i<7; i++) + { + if ( ofs & 1 ) + { + break; + } + ofs >>= 1; + } + + return i; +} + +tROMAddrConvType ConvertHeaderRamAddr( s32 p ) +{ + tROMAddrConvType retval; + + retval.align = ConvertAlign( p ); + retval.address = (u16)((p - HW_WRAM)/(4< + +typedef enum +{ + FALSE = 0, + TRUE = 1 +} +BOOL; + +typedef unsigned char u8; +typedef unsigned short int u16; +typedef unsigned long int u32; +typedef unsigned long long u64; +typedef signed char s8; +typedef signed short int s16; +typedef signed long int s32; +typedef signed long long s64; + +#define error(...) do { fprintf(stderr, "Error: "); \ + fprintf(stderr, __VA_ARGS__); \ + fprintf(stderr, "\n"); } while(0) + +#define warning(...) do { fprintf(stderr, "Warning: "); \ + fprintf(stderr, __VA_ARGS__); \ + fprintf(stderr, "\n"); } while(0) + +BOOL OpenFile(const char *filename); +BOOL CloseFile(void); +void SeekFile(long pos); +BOOL CheckResult(void); +void DeleteOutFile(void); +void PutBuffer(const void *ptr, int len); +void GetBuffer(void *ptr, int len); +void PutByte(u8 c); +void PutHalf(u16 c); +void PutWord(u32 c); +void PutString(const char *str); +void PrintString(const char *fmt, ...); + +#define READ_ALL 0 +int ReadFile(const char *filename, void *filebuffer, int size); +int ReadFileWithPadding(const char *filename, void *filebuffer, int size, int boundary); +BOOL WriteFile(const char *filename, void *buffer, int size); + +char *GetGMTime(const time_t time); +char *GetTime(const time_t time); + +void *Alloc(size_t size); +void Free(void *p); + +typedef struct +{ + char *buffer; + int size; +} +VBuffer; + +#define VBUFFER_INITIAL_SIZE 1024 +void InitVBuffer(VBuffer * vbuf); +void FreeVBuffer(VBuffer * vbuf); +void PutVBuffer(VBuffer * vbuf, char c); +char *GetVBuffer(VBuffer * vbuf); + +char *ChangeBackSlash(char *path); + +u16 CalcCRC16(u16 start, u8 *data, int size); +const char *WrapNull(const char *str); + +typedef struct +{ + u16 address; + u8 align; +} +tROMAddrConvType; + +tROMAddrConvType ConvertHeaderRamAddr( s32 p ); +tROMAddrConvType ConvertHeaderRomOffset( s32 p ); +u16 ConvertHeaderRomOffsetAlign( s32 p, u32 align ); + +typedef union +{ + struct + { + u32 sign:1; + u32 header_hash:1; + u32 arm9_hash:1; + u32 arm7_hash:1; + u32 hash_table_hash:1; + u32 final_hash:1; + u32 header_footer:1; + u32 wl_check:1; + } + e; + u32 raw; +} +tErrorFlags; + +extern BOOL DebugMode; +extern BOOL PrintMode; +extern char *PubkeyFileName; +void debug_printf(const char *str, ...); +void debug_printf2(const char *str, ...); + +#endif //MISC_H_ diff --git a/build/tools/makenorfirm/out_norfirm.c b/build/tools/makenorfirm/out_norfirm.c new file mode 100644 index 00000000..42a287fa --- /dev/null +++ b/build/tools/makenorfirm/out_norfirm.c @@ -0,0 +1,984 @@ +/*---------------------------------------------------------------------------* + Project: TwlFirm - tools - makenorfirm + File: out_norfirm.c + + Copyright 2007 Nintendo. All rights reserved. + + These coded instructions, statements, and computer programs contain + proprietary information of Nintendo of America Inc. and/or Nintendo + Company Ltd., and are protected by Federal copyright law. They may + not be disclosed to third parties or copied or duplicated in any form, + in whole or in part, without the prior written consent of Nintendo. + + $Log: $ + $NoKeywords: $ + *---------------------------------------------------------------------------*/ +#include +#include // atoi() +#include // strcmp() +#include // isprint() +#include // chdir() +#include +#include // UCHAR_MAX +#include +#include // stat() +#include "elf.h" +#include "misc.h" +#include "defval.h" +#include "format_rom.h" +#include "format_nlist.h" +#include "makenorfirm.h" +#include "format_sign.h" +#include "acsign_nor.h" +#include "compress.h" + +#define SDK_ASM +#include + +#include +#include "../acsign/aes2.h" + +#define NANDCMD "BOOT_NAND" +#define SBIN9CMD "ARM9_SBIN" +#define SBIN7CMD "ARM7_SBIN" +#define ELF9CMD "ARM9_ELF" +#define ELF7CMD "ARM7_ELF" +#define COMP9CMD "ARM9_COMP" +#define COMP7CMD "ARM7_COMP" +#define DECOMPCMD "DECOMP_PROC" +#define BAUDCMD "BAUDRATE" +#define ARM9X2CMD "ARM9_X2" +#define VERCMD "VERSION" +#define RSAKEYCMD "RSA_KEY" +#define OUTKEYCMD "OUT_KEY" +#define WREGCMD "WRAM_RBIN" +#define ENDKEYCMD "ENC_KEYINFO" +#define NCDCMD "NCD_ROMOFS" +#define ERRCMD "ERROR" + +static BOOL ConstructNorfirmFile(char * specFile); +static BOOL ReadSbinFile(const char *fileName, void* minfo, void* hash, BOOL comp); +static BOOL ReadKeyFile(const char *fileName); +static BOOL ReadWramRegFile(const char *fileName); +static s32 GetRamAddr(const char *fileName); + +static BOOL EncryptBuffer(char *buffer, int length); + +static BOOL BTNAND_Command(char * line, int num); +static BOOL Sbin9_Command(char * line, int num); +static BOOL Sbin7_Command(char * line, int num); +static BOOL Elf9_Command(char * line, int num); +static BOOL Elf7_Command(char * line, int num); +static BOOL Comp9_Command(char * line, int num); +static BOOL Comp7_Command(char * line, int num); +static BOOL Decomp_Command(char * line, int num); +static BOOL BAUDRATE_Command(char * line, int num); +static BOOL ARM9X2_Command(char * line, int num); +static BOOL VERSION_Command(char * line, int num); +static BOOL RSAKEY_Command(char * line, int num); +static BOOL OUTKEY_Command(char * line, int num); +static BOOL WramRegs_Command(char * line, int num); +static BOOL ENCKEY_Command(char * line, int num); +static BOOL NcdOffset_Command(char * line, int num); +static BOOL ERROR_Command(char * line, int num); + +static BOOL InitializeAesKey(void); +static BOOL InitializeNorfirmFile(void); +static BOOL FinalizeNorfirmFile(const char *norFile); + +static s32 Offset; // Current offset +static int LineNum; // Line number for error message +static const char *specFileName; // specFile name for error message + +NORHeader norHeader; // Norfirm Header Shadow +FIRMSignedContext signedContext; +u8 *keyFileBuf; +BOOL compArm9 = TRUE; +BOOL compArm7 = TRUE; +tErrorFlags errFlags; + +//--------------------------------------------------------------------------- +// Output - norfirm File +//--------------------------------------------------------------------------- + +BOOL OutputNorfirmFile(const char *specFile, const char *norFile) +{ + char *buffer; + BOOL state; + + if (ReadFile(specFile, &buffer, READ_ALL) <= 0) + { + return FALSE; + } + + if (!OpenFile(norFile)) + { + return FALSE; + } + + specFileName = specFile; + + state = InitializeNorfirmFile() && ConstructNorfirmFile(buffer) && + FinalizeNorfirmFile(norFile) && CloseFile(); + + if (!state) + { + DeleteOutFile(); + } + + return state; +} + + +//--------------------------------------------------------------------------- +// Output - Norfirm File +//--------------------------------------------------------------------------- + +static const tCommandDesc command[] = { + {SBIN9CMD, Sbin9_Command}, {SBIN7CMD, Sbin7_Command}, + {ELF9CMD, Elf9_Command},{ELF7CMD, Elf7_Command}, + {COMP9CMD, Comp9_Command},{COMP7CMD, Comp7_Command}, + {DECOMPCMD, Decomp_Command}, + {NANDCMD, BTNAND_Command}, + {VERCMD, VERSION_Command}, + {BAUDCMD, BAUDRATE_Command}, + {ARM9X2CMD, ARM9X2_Command}, + {RSAKEYCMD, RSAKEY_Command}, + {OUTKEYCMD, OUTKEY_Command}, + {WREGCMD, WramRegs_Command}, + {ENDKEYCMD, ENCKEY_Command}, + {NCDCMD, NcdOffset_Command}, + {ERRCMD, ERROR_Command}, +}; + +BOOL ConstructNorfirmFile(char * specFile) +{ + char *line; + char *line_top; + char *p; + int i; + + LineNum = 0; + Offset = 0x00000000; + + line = specFile++; + + while (*line != '\0') + { + LineNum++; + + // Get command line + line_top = line; + while (*line != '\0') + { + if (*line++ == '\n') + { + break; + } + } + + // Print for debug + debug_printf("NORSF Line%4d [", LineNum, line); + for (p = line_top; p != line; p++) + { + if (isprint(*p)) + { + debug_printf("%c", *p); + } + } + debug_printf("]\n"); + + if (*line_top == '#') + { + } + else + { + for (i = 0; i < (sizeof(command) / sizeof(command[0])); i++) + { + if (!strncmp(line_top, command[i].string, strlen(command[i].string))) + { + if (command[i].funcp != NULL) + { + char line_cmd[FILENAME_MAX]; + char line_scan[FILENAME_MAX]; + char* line_conv; + int num; + + num = sscanf( line_top, + "%1024[^ :] : %1024[^ \x0d:#\n]", + line_cmd, line_scan + ); + line_conv = ResolveDefVal(line_scan); + + debug_printf("line_cmd = %s, line_conv = %s\n", line_cmd, line_conv); + + if (!command[i].funcp(line_conv, num - 1)) + return FALSE; + } + } + } + } + + } + return TRUE; +} + + +//--------------------------------------------------------------------------- +// Output - 'WramRegs' Command +//--------------------------------------------------------------------------- + +extern MIHeader_WramRegs wram_regs_init; +MIHeader_WramRegs* wram_regs; + +static BOOL WramRegs_Command(char * line, int num) +{ + if (num != 1) + { + error("Wrong format spec file '%s' line:%d", specFileName, LineNum); + return FALSE; + } + + debug_printf2("wram regs file = %s\n", line); + + return ReadWramRegFile(line); +} + +static BOOL ReadWramRegFile(const char *fileName) +{ + int file_size; + struct stat st; + + if (FILESTATUS_FILE != GetFileStatus(&st, fileName)) + { + error("'%s' is not regular file.", fileName); + return FALSE; + } + + if ((file_size = ReadFile(fileName, &wram_regs, READ_ALL)) < 0) + return FALSE; + + norHeader.h.w = *wram_regs; + + return CheckResult(); +} + +//--------------------------------------------------------------------------- +// Output - 'RSAKEY' Command +//--------------------------------------------------------------------------- + +static BOOL RSAKEY_Command(char * line, int num) +{ + if (num != 1) + { + error("Wrong format spec file '%s' line:%d", specFileName, LineNum); + return FALSE; + } + + debug_printf2("rsa key = %s\n", line); + + return ReadKeyFile(line); +} + +static BOOL ReadKeyFile(const char *fileName) +{ + int file_size; + struct stat st; + + if (FILESTATUS_FILE != GetFileStatus(&st, fileName)) + { + error("'%s' is not regular file.", fileName); + return FALSE; + } + + if ((file_size = ReadFile(fileName, &keyFileBuf, READ_ALL)) < 0) + return FALSE; + + return CheckResult(); +} + + +//--------------------------------------------------------------------------- +// Output - 'OUTKEY' Command +//--------------------------------------------------------------------------- + +static BOOL OUTKEY_Command(char * line, int num) +{ + if (num != 1) + { + error("Wrong format spec file '%s' line:%d", specFileName, LineNum); + return FALSE; + } + + debug_printf2("out key = %s\n", line); + + PubkeyFileName = line; + + return CheckResult(); +} + + +//--------------------------------------------------------------------------- +// Output - 'VERSION' Command +//--------------------------------------------------------------------------- + +static u8 ConvToBCD8(int x) +{ + u8 bcd = 0; + + x %= 100; + bcd |= (x / 10)<<4; + bcd |= (x % 10); + + return bcd; +} + +static BOOL VERSION_Command(char * line, int num) +{ + char scan[FILENAME_MAX]; + u64 version = -1; + + // rescan + num = sscanf( line, + "0x%1024[^\x0d\n]", + scan + ); + + if (num == 1) + { + // convert version info + version = strtoull(scan, NULL, 16); + } + else if (num == 0) + { + // generate version info + u8* ver8 = (u8*)&version; + time_t timer; + struct tm *t_st; + + time(&timer); + t_st = localtime(&timer); + + ver8[0] = ConvToBCD8(t_st->tm_min); + ver8[1] = ConvToBCD8(t_st->tm_hour); + ver8[2] = ConvToBCD8(t_st->tm_mday); + ver8[3] = ConvToBCD8(t_st->tm_mon+1); + ver8[4] = ConvToBCD8(t_st->tm_year); + ver8[5] = 0xff; + ver8[6] = 0xff; + ver8[7] = 0xff; + } + + norHeader.d.card_key = version; + + debug_printf2("version = %08llx\n", version); + + return CheckResult(); +} + + +//--------------------------------------------------------------------------- +// Output - 'BOOT_NAND' Command +//--------------------------------------------------------------------------- + +static BOOL BTNAND_Command(char * line, int num) +{ + char* dbg_str = "FALSE"; + + norHeader.l.boot_nandfirm = FALSE; + + if (num != 1) + { + error("Wrong format spec file '%s' line:%d", specFileName, LineNum); + return FALSE; + } + + if (!strcmp(line, "TRUE")) + { + norHeader.l.boot_nandfirm = TRUE; + dbg_str = "TRUE"; + } + + debug_printf2("boot nandfirm = %s\n", dbg_str); + + return CheckResult(); +} + + +//--------------------------------------------------------------------------- +// Output - 'Decomp' Command +//--------------------------------------------------------------------------- + +static BOOL Decomp_Command(char * line, int num) +{ + char* dbg_str = "ARM7"; + + if (num != 1) + { + error("Wrong format spec file '%s' line:%d", specFileName, LineNum); + return FALSE; + } + + if (!strcmp(line, "ARM9")) + { + norHeader.l.arm9_decomp = TRUE; + dbg_str = "ARM9"; + } + + debug_printf2("decompress processor = %s\n", dbg_str); + + return CheckResult(); +} + + +//--------------------------------------------------------------------------- +// Output - 'ARM9_X2' Command +//--------------------------------------------------------------------------- + +static BOOL ARM9X2_Command(char * line, int num) +{ + char* dbg_str = "FALSE"; + + if (num != 1) + { + error("Wrong format spec file '%s' line:%d", specFileName, LineNum); + return FALSE; + } + + if (!strcmp(line, "TRUE")) + { + norHeader.l.arm9_x2 = TRUE; + dbg_str = "TRUE"; + } + + debug_printf2("arm9 x2 = %s\n", dbg_str); + + return CheckResult(); +} + + +//--------------------------------------------------------------------------- +// Output - 'BAUDRATE' Command +//--------------------------------------------------------------------------- + +static BOOL BAUDRATE_Command(char * line, int num) +{ + char* dbg_str = "4M"; + + if (num != 1) + { + error("Wrong format spec file '%s' line:%d", specFileName, LineNum); + return FALSE; + } + + if (!strcmp(line, "8M")) + { + norHeader.l.baudrate = TRUE; + dbg_str = "8M"; + } + + debug_printf2("spi speed = %s\n", dbg_str); + + return CheckResult(); +} + + +//--------------------------------------------------------------------------- +// Output - 'Comp9' Command +//--------------------------------------------------------------------------- + +static BOOL Comp9_Command(char * line, int num) +{ + char* dbg_str = "FALSE"; + + if (num != 1) + { + error("Wrong format spec file '%s' line:%d", specFileName, LineNum); + return FALSE; + } + + compArm9 = FALSE; + + if (!strcmp(line, "TRUE")) + { + norHeader.l.comp_arm9_boot_area = TRUE; + compArm9 = TRUE; + dbg_str = "TRUE"; + } + + debug_printf2("arm9 compress = %s\n", dbg_str); + + return CheckResult(); +} + + +//--------------------------------------------------------------------------- +// Output - 'Comp7' Command +//--------------------------------------------------------------------------- + +static BOOL Comp7_Command(char * line, int num) +{ + char* dbg_str = "FALSE"; + + if (num != 1) + { + error("Wrong format spec file '%s' line:%d", specFileName, LineNum); + return FALSE; + } + + compArm7 = FALSE; + + if (!strcmp(line, "TRUE")) + { + norHeader.l.comp_arm7_boot_area = TRUE; + compArm7 = TRUE; + dbg_str = "TRUE"; + } + + debug_printf2("arm7 compress = %s\n", dbg_str); + + return CheckResult(); +} + + +//--------------------------------------------------------------------------- +// Output - 'Elf9' Command +//--------------------------------------------------------------------------- + +static BOOL Elf9_Command(char * line, int num) +{ + if (num != 1) + { + error("Wrong format spec file '%s' line:%d", specFileName, LineNum); + return FALSE; + } + + debug_printf2("arm9 elf = %s\n", line); + + { + s32 ramAddr = GetRamAddr(line); + norHeader.l.main_ram_address = (void*)ramAddr; + } + + return CheckResult(); +} + + +//--------------------------------------------------------------------------- +// Output - 'Elf7' Command +//--------------------------------------------------------------------------- + +static BOOL Elf7_Command(char * line, int num) +{ + if (num != 1) + { + error("Wrong format spec file '%s' line:%d", specFileName, LineNum); + return FALSE; + } + + debug_printf2("arm7 elf = %s\n", line); + + { + s32 ramAddr = GetRamAddr(line); + norHeader.l.sub_ram_address = (void*)ramAddr; + } + + return CheckResult(); +} + + +static s32 GetRamAddr(const char *fileName) +{ + Elf32_Ehdr *ehdr; + s32 ramAddr; + int file_size; + struct stat st; + + if (FILESTATUS_FILE != GetFileStatus(&st, fileName)) + { + error("'%s' is not regular file.", fileName); + return FALSE; + } + + if ((file_size = ReadFile(fileName, &ehdr, sizeof(Elf32_Ehdr))) < 0) + return FALSE; + + ramAddr = ehdr->e_entry; + + Free(&ehdr); + + debug_printf2("ramaddr = 0x%08x\n", ramAddr); + + return ramAddr; +} + + +//--------------------------------------------------------------------------- +// Output - 'Sbin9' Command +//--------------------------------------------------------------------------- + +static BOOL Sbin9_Command(char * line, int num) +{ + FIRMSignedContext* sc = &signedContext; + + if (num != 1) + { + error("Wrong format spec file '%s' line:%d", specFileName, LineNum); + return FALSE; + } + + debug_printf2("arm9 sbin = %s\n", line); + + // Set ARM9 ROM Offset + if (!Offset) + { + Offset = (sizeof(NORHeader) + FIRM_ALIGN_MASK) & ~FIRM_ALIGN_MASK; + SeekFile(Offset); + } + debug_printf2("romoffset = %#x\n", Offset); + { + norHeader.l.main_rom_offset = Offset; + } + + return ReadSbinFile(line, &norHeader.l.main_rom_offset, &sc->hash[FIRM_SIGNED_HASH_IDX_ARM9], compArm9); +} + +//--------------------------------------------------------------------------- +// Output - 'Sbin7' Command +//--------------------------------------------------------------------------- + +static BOOL Sbin7_Command(char * line, int num) +{ + FIRMSignedContext* sc = &signedContext; + + if (num != 1) + { + error("Wrong format spec file '%s' line:%d", specFileName, LineNum); + return FALSE; + } + + debug_printf2("arm7 sbin = %s\n", line); + + // Set ARM7 ROM Offset + if (!Offset) + { + Offset = (sizeof(NORHeader) + FIRM_ALIGN_MASK) & ~FIRM_ALIGN_MASK; + SeekFile(Offset); + } + debug_printf2("romoffset = %#x\n", Offset); + { + norHeader.l.sub_rom_offset = Offset; + } + + return ReadSbinFile(line, &norHeader.l.sub_rom_offset, &sc->hash[FIRM_SIGNED_HASH_IDX_ARM7], compArm7); +} + + +static BOOL ReadSbinFile(const char *fileName, void* minfo, void* hash, BOOL comp) +{ + const NORHeader_ModuleInfo *m = minfo; + u32 *size = (void*)&m->size; + u32 *orig_size = (void*)&m->decomp_size; + char *buffer; + char *file; + int file_size; + struct stat st; + + if (FILESTATUS_FILE != GetFileStatus(&st, fileName)) + { + error("'%s' is not regular file.", fileName); + return FALSE; + } + + if ((file_size = ReadFile(fileName, &file, READ_ALL)) < 0) + return FALSE; + + *orig_size = file_size; + + // Digest file image + if (hash) + { + ACSign_DigestUnit(hash, file, file_size); + } + + // Compress file image with fitting region + buffer = Alloc(file_size * 2); + if ( comp ) + { + file_size = LZCompWrite(file, file_size, buffer, FIRM_ALIGN); + } + else + { + memcpy(&buffer[0], file, file_size); + { + u32 pad_size = (FIRM_ALIGN - (file_size % FIRM_ALIGN)) % FIRM_ALIGN; + if (pad_size) + memset(&buffer[file_size], 0, pad_size); + file_size += pad_size; + } + } + Free(&file); + file = buffer; + + if (size) + { + *size = file_size; + } + Offset += file_size; + + // Encrypt file image + EncryptBuffer(file, file_size); + + // Output file image with fitting region + PutBuffer(file, file_size); + + Free(&file); + + return CheckResult(); +} + +typedef struct +{ + unsigned long e[4]; +} u128; + +static BOOL EncryptBuffer(char *buffer, int length) +{ + const u128 id = {{ AES_IDS_ID2_A, AES_IDS_ID2_B, AES_IDS_ID2_C, AES_IDS_ID2_D }}; + u128 iv = {{ length, -length, ~length, 0 }}; + FIRMSignedContext* sc = &signedContext; + char *buffer2 = Alloc(length); + AES_KEY key; + if (!buffer2) + return FALSE; + AES_SetKey(&key, sc->aes_key, (unsigned char*)&id); + AES_Ctr(&key, buffer2, buffer, length, (unsigned char*)&iv); + memcpy(buffer, buffer2, length); + memset(buffer2, 0, length); + Free(buffer2); + return TRUE; +} + + +//--------------------------------------------------------------------------- +// Output - 'ENC_KEYINFO' Command +//--------------------------------------------------------------------------- + +static BOOL ENCKEY_Command(char * line, int num) +{ + u32 key; + + if (num != 1) + { + error("Wrong format spec file '%s' line:%d", specFileName, LineNum); + return FALSE; + } + + key = strtoul(line, NULL, 0); + norHeader.d.ds_key = key; + + debug_printf2("keyinfo = %#x\n", key); + + return CheckResult(); +} + + +//--------------------------------------------------------------------------- +// Output - 'NcdOffset' Command +//--------------------------------------------------------------------------- + +static BOOL NcdOffset_Command(char * line, int num) +{ + u32 ofs; + + if (num != 1) + { + error("Wrong format spec file '%s' line:%d", specFileName, LineNum); + return FALSE; + } + + ofs = strtoul(line, NULL, 0); + { + norHeader.d.ncd_romAdr = ConvertHeaderRomOffsetAlign(ofs, 8); + } + + debug_printf2("ncd romoffset = %#x\n", ofs); + + return CheckResult(); +} + + +//--------------------------------------------------------------------------- +// Output - 'ERROR' Command +//--------------------------------------------------------------------------- +static char* error_type[] = +{ + "SIGN", + "HEADER_HASH", + "ARM9_HASH", + "ARM7_HASH", + "HASH_TABLE_HASH", + "FINAL_HASH", + "HEADER_FOOTER", + "WL_CHECK", +}; + +static BOOL ERROR_Command(char * line, int num) +{ + char* dbg_str = "UNKNOWN"; + int i; + + if (num != 1) + { + error("Wrong format spec file '%s' line:%d", specFileName, LineNum); + return FALSE; + } + + for (i=0; ihash[FIRM_SIGNED_HASH_IDX_HEADER][0] ^= 1; + } + if ( errFlags.e.arm9_hash ) + { + sc->hash[FIRM_SIGNED_HASH_IDX_ARM9][0] ^= 1; + } + if ( errFlags.e.arm7_hash ) + { + sc->hash[FIRM_SIGNED_HASH_IDX_ARM7][0] ^= 1; + } + if ( errFlags.e.hash_table_hash ) + { + sc->hash[FIRM_SIGNED_HASH_IDX_HASH_TABLE][0] ^= 1; + } +} + +static void SetFinalHashError(FIRMSignedContext* sc) +{ + if ( errFlags.e.final_hash ) + { + sc->hash[FIRM_SIGNED_HASH_IDX_FINAL][0] ^= 1; + } +} + +static void SetSignError(NORHeader* nh) +{ + if ( errFlags.e.sign ) + { + nh->sign.raw[0] ^= 1; + } + +} + +static void SetFooterError(NORHeader* nh) +{ + if ( errFlags.e.header_footer ) + { + nh->h.reserved_footer[sizeof(nh->h.reserved_footer)-1] ^= 1; + } + +} + +static void SetWirelessCheck(NORHeader* nh) +{ + if ( errFlags.e.wl_check ) + { + nh->wl_params[0] ^= 0x80; + nh->wl_params[sizeof(nh->wl_params)-1] ^= 0x80; + } + +} + + +//--------------------------------------------------------------------------- +// Output - Initialize AES Key +//--------------------------------------------------------------------------- +static BOOL InitializeAesKey(void) +{ + FIRMSignedContext* sc = &signedContext; + struct stat specstat; + time_t spectime; + if (stat(specFileName, &specstat) != 0) + return FALSE; + time(&spectime); + memcpy(&sc->aes_key[0], &specstat.st_atime, 4); + memcpy(&sc->aes_key[4], &specstat.st_mtime, 4); + memcpy(&sc->aes_key[8], &specstat.st_ctime, 4); + memcpy(&sc->aes_key[12], &spectime, 4); + ACSign_GetKey(sc->aes_key, sizeof(sc->aes_key), sc->aes_key, sizeof(sc->aes_key)); + return TRUE; +} + + +//--------------------------------------------------------------------------- +// Output - Initialize Norfirm File +//--------------------------------------------------------------------------- + +static BOOL InitializeNorfirmFile(void) +{ + memset(norHeader.wl_params, 0xff, sizeof(norHeader.wl_params)); + memset(&signedContext.hash[FIRM_SIGNED_HASH_IDX_HASH_TABLE], 0xff, sizeof(signedContext.hash[0])); + norHeader.l.boot_nandfirm = TRUE; + InitializeAesKey(); + return TRUE; +} + + +//--------------------------------------------------------------------------- +// Output - Finalize Norfirm File +//--------------------------------------------------------------------------- + +static BOOL FinalizeNorfirmFile(const char *norFile) +{ + NORHeader* nh = &norHeader; + FIRMSignedContext* sc = &signedContext; + u8* key = keyFileBuf; + + ACSign_DigestHeader(&sc->hash[FIRM_SIGNED_HASH_IDX_HEADER], nh); + + SetUnitHashErrors(sc); + + ACSign_DigestUnit(&sc->hash[FIRM_SIGNED_HASH_IDX_FINAL], sc, FIRM_HEADER_2ND_HASH_AREA_LEN); + + SetFinalHashError(sc); + + if (key) + { + ACSign_Final(nh, sc, key); + } + + SetFooterError(nh); + + SetSignError(nh); + + SetWirelessCheck(nh); + + // Output file image + SeekFile(0L); + PutBuffer(&norHeader, sizeof(norHeader)); + + // Output public key (modulus) + if (PubkeyFileName) + { + WriteFile(PubkeyFileName, &keyFileBuf[ACS_RSA_PRVMOD_OFFSET], ACS_RSA_PRVMOD_LEN); + } + + return TRUE; +} diff --git a/build/tools/makenorfirm/path.c b/build/tools/makenorfirm/path.c new file mode 100644 index 00000000..eeeb9895 --- /dev/null +++ b/build/tools/makenorfirm/path.c @@ -0,0 +1,931 @@ +/*---------------------------------------------------------------------------* + Project: NitroSDK - tools - makebanner + File: path.c + + Copyright 2003-2006 Nintendo. All rights reserved. + + These coded instructions, statements, and computer programs contain + proprietary information of Nintendo of America Inc. and/or Nintendo + Company Ltd., and are protected by Federal copyright law. They may + not be disclosed to third parties or copied or duplicated in any form, + in whole or in part, without the prior written consent of Nintendo. + + $Log: path.c,v $ + Revision 1.3 2006/01/18 02:11:20 kitase_hirotake + do-indent + + Revision 1.2 2005/02/28 05:26:13 yosizaki + do-indent. + + Revision 1.1 2004/08/30 08:41:14 yasu + makebanner moves into CVS tree + + $NoKeywords: $ + *---------------------------------------------------------------------------*/ +#include +#include // free() +#include // strcasecmp() +#include // stat() +#include // opendir()/readdir()/closedir() +#include // getcwd() +#ifdef __CYGWIN__ +#include // cygwin_conv_to_win32_path() +#endif +#include "path.h" + +//--------------------------------------------------------------------------- +// Get File Statue +//--------------------------------------------------------------------------- + +tFileStatus GetFileStatus(struct stat *s, const char *filename) +{ + // Get file status + if (stat(filename, s)) + { + error("Can't get status %s", filename); + return FILESTATUS_ERROR; + } + + if (S_ISREG(s->st_mode)) + { + return FILESTATUS_FILE; + } + else if (S_ISDIR(s->st_mode)) + { + return FILESTATUS_DIR; + } + + error("Unknown file type %s", filename); + return FILESTATUS_ERROR; +} + + +//--------------------------------------------------------------------------- +// File Globbing & Dir Listing +//--------------------------------------------------------------------------- + +typedef struct +{ + tCallBack callBack; + void *param; + tWildCard *accept; + tWildCard *reject; + +} +tForeachEntryParam; + + +static BOOL isAcceptEntryName(char *pathName, tWildCard * accept, tWildCard * reject) +{ + char *p = pathName; + + while (*p) + { + if (*p == '/') + pathName = p + 1; + p++; + } + + if (accept) + { + while (accept) + { + if (WildCardCmp(accept->name, pathName)) + { + goto accepted; + } + accept = accept->next; + } + return FALSE; + } + accepted: + + while (reject) + { + if (WildCardCmp(reject->name, pathName)) + { + return FALSE; + } + reject = reject->next; + } + return TRUE; +} + + +static BOOL ForeachEntry_CallBack(char *pathName, void *param) +{ + tForeachEntryParam *t = (tForeachEntryParam *) param; + struct stat fstat; + + if (!isAcceptEntryName(pathName, t->accept, t->reject)) + { + // Reject!!! ignored + return TRUE; + } + + switch (GetFileStatus(&fstat, pathName)) + { + case FILESTATUS_FILE: + return t->callBack(pathName, t->param); + + case FILESTATUS_DIR: + return ForeachDirList(pathName, ForeachEntry_CallBack, param); + + default: + break; + } + return FALSE; +} + + +BOOL ForeachEntry(const char *pathName, tWildCard * reject, tCallBack callBack, void *param) +{ + tForeachEntryParam t; + + t.callBack = callBack; + t.param = param; + t.accept = NULL; + t.reject = reject; + + return ForeachPathGlobbing(pathName, ForeachEntry_CallBack, &t); +} + + +typedef struct +{ + tCallBack callBack; + void *param; + char *baseName; + +} +tForeachFileParam; + + +static BOOL ForeachFile_CallBack(char *pathName, void *param) +{ + tForeachFileParam *t = (tForeachFileParam *) param; + + int len = strlen(t->baseName); + + debug_printf(" ForeachFile_CallBack path[%s] base[%s]\n", pathName, t->baseName); + + if (strncmp(pathName, t->baseName, len)) + { + error("Wildcard in Root is not supported"); + return FALSE; + } + + return t->callBack(pathName + len, t->param); +} + + +BOOL ForeachFile(const char *baseName, const char *fileName, tWildCard * reject, tCallBack callBack, + void *param) +{ + char *cBaseName; + char *cPathName; + BOOL state; + tForeachFileParam t; + + debug_printf("ForeachFile : baseName[%s] fileName[%s]\n", baseName, fileName); + + cBaseName = GetSrcPath(baseName, ""); + cPathName = GetSrcPath(baseName, fileName); + + debug_printf("ForeachFile : cBaseName[%s] cPathName[%s]\n", cBaseName, cPathName); + + t.callBack = callBack; + t.param = param; + t.baseName = cBaseName; + + state = ForeachEntry(cPathName, reject, ForeachFile_CallBack, &t); + + free(cBaseName); + free(cPathName); + + return state; +} + + +//--------------------------------------------------------------------------- +// FilePath Globbing +//--------------------------------------------------------------------------- + +typedef struct +{ + char *baseName; + char *pathName; + tCallBack callBack; + void *param; + +} +tGlobParam; + + +static int CountFile; +static BOOL ForeachPathGlobbing_Entry(tGlobParam * pg); +static BOOL ForeachPathGlobbing_WildCard(char *pathName, void *param); + +BOOL ForeachPathGlobbing(const char *pathName, tCallBack callBack, void *param) +{ + tGlobParam g; + BOOL ret; + + g.baseName = NULL; + g.pathName = PathNormalize(pathName, TRUE); + g.callBack = callBack; + g.param = param; + CountFile = 0; + + debug_printf("PathGlobbing : Name [%s]->[%s]\n", pathName, g.pathName); + + ret = ForeachPathGlobbing_Entry(&g); + + free(g.pathName); + + if (ret && CountFile == 0) + { + error("No file or directory matched %s", pathName); + return FALSE; + } + return ret; +} + + +static BOOL ForeachPathGlobbing_Entry(tGlobParam * pg) +{ + tGlobParam g; + char *entryName; + struct stat s; + BOOL state; + + if (pg->pathName) + { + entryName = PathDup(pg->pathName); + + g = *pg; + g.pathName = PathGetDirLevelDown(pg->pathName); + + if (pg->baseName) + { + g.baseName = Alloc(strlen(pg->baseName) + strlen(entryName) + 2); + sprintf(g.baseName, "%s/%s", pg->baseName, entryName); + } + else + { + g.baseName = strdup(entryName); + } + + // Check if wildcard ? + if (isPathWildCard(entryName)) + { + state = ForeachDirList(pg->baseName, ForeachPathGlobbing_WildCard, &g); + } + else + { + state = ForeachPathGlobbing_Entry(&g); + } + + Free(&entryName); + Free(&g.baseName); + } + else + { + // Check if file exists + if (!stat(pg->baseName, &s)) + { + debug_printf(" File Found [%s]\n", pg->baseName); + + // Globbing done, exec callback + + state = pg->callBack(pg->baseName, pg->param); + CountFile++; + } + else + { + debug_printf(" File Not Found [%s]\n", pg->baseName); + state = TRUE; // Ignored + } + } + return state; +} + + +static BOOL ForeachPathGlobbing_WildCard(char *pathName, void *param) +{ + tGlobParam g; + tGlobParam *pg = (tGlobParam *) param; + + debug_printf(" WildCardCmp: [%s] [%s]\n", pg->baseName, pathName); + + if (WildCardCmp(pg->baseName, pathName)) + { + g = *pg; + g.baseName = pathName; + return ForeachPathGlobbing_Entry(&g); + } + + return TRUE; // Ignored +} + + +//--------------------------------------------------------------------------- +// Directory Listing +// Listing directory & Exec CallBack +//--------------------------------------------------------------------------- + +BOOL ForeachDirList(const char *dirName, tCallBack callBack, void *param) +{ + DIR *dir; + struct dirent *entry; + char *pathName; + BOOL state = TRUE; + + if (!dirName) + { + dirName = "."; + } + + debug_printf("DirectoryList: Name [%s]\n", dirName); + + // Open directory + if (NULL == (dir = opendir(dirName))) + { + error("Can't read directory %s", dirName); + return FALSE; + } + + // Store new files + while (NULL != (entry = readdir(dir))) + { + pathName = entry->d_name; + + if (!strcmp(pathName, ".") || !strcmp(pathName, "..")) + { + continue; + } + + debug_printf(" :%s\n", pathName); + pathName = GetSrcPath(dirName, pathName); + state = callBack(pathName, param); + free(pathName); + + if (!state) + break; + } + + closedir(dir); + return state; +} + + +//--------------------------------------------------------------------------- +// +// PathName Utilities +// +//--------------------------------------------------------------------------- + +//--------------------------------------------------------------------------- +// StrCmp/StrCpy entry name terminated / or \0 +//--------------------------------------------------------------------------- + +int PathCmp(const char *path, const char *cmp) +{ + char c; + + do + { + c = *path; + if (c == '/') + c = '\0'; // end of string if '/' + if (c != *cmp) + return 1; + path++; + cmp++; + } + while (c); + + return 0; +} + + +char *PathCpy(char *dest, const char *src) +{ + while (*src != '\0' && *src != '/') + { + *dest++ = *src++; + } + return dest; // Don't set '\0' +} + + +int PathLen(const char *path) +{ + int n = 0; + + while (*path != '\0' && *path != '/') + { + n++; + path++; + } + return n; +} + + +char *PathDup(const char *src) +{ + int n = PathLen(src); + char *dest = Alloc(n + 2); + + PathCpy(dest, src); + dest[n] = '\0'; + + return dest; +} + + +BOOL WildCardCmp(const char *wildcard, const char *path) +{ + if (*wildcard == '*') + { + if (*path != '\0' && WildCardCmp(wildcard, path + 1)) + return TRUE; + if (WildCardCmp(wildcard + 1, path)) + return TRUE; + } + + else if (*wildcard == '?') + { + return *path != '\0' && WildCardCmp(wildcard + 1, path + 1); + } + + else if (*wildcard == *path) + { + return *path == '\0' || WildCardCmp(wildcard + 1, path + 1); + } + + return FALSE; +} + + +BOOL isPathWildCard(const char *path) +{ + while (*path != '\0' && *path != '/') + { + if (*path == '*' || *path == '?') + { + return TRUE; + } + path++; + } + return FALSE; +} + + +//--------------------------------------------------------------------------- +// Go up/down directory level +//--------------------------------------------------------------------------- + +char *PathGetDirLevelDown(const char *path) +{ + while (*path) + { + if (*path == '/') + return (char *)path + 1; + path++; + } + return NULL; +} + + +//--------------------------------------------------------------------------- +// Get Basename +//--------------------------------------------------------------------------- + +char *GetBaseName(const char *path) +{ + int i; + char *new_path; + + for (i = strlen(path) - 1; i >= 0; i--) + { + if (path[i] == '/') + { + new_path = strdup(path); + new_path[i] = '\0'; + return new_path; + } + if (path[i] == ':') + { + new_path = Alloc(i + 3); + strncpy(new_path, path, i); + strcpy(new_path + i, ":."); + return new_path; + } + } + + new_path = strdup("."); + return new_path; +} + + +//--------------------------------------------------------------------------- +// Get Filename +//--------------------------------------------------------------------------- + +char *GetFileName(const char *path) +{ + int i; + char *new_file; + + for (i = strlen(path) - 1; i >= 0; i--) + { + if (path[i] == '/' || path[i] == ':') + { + new_file = strdup(path + i + 1); + return new_file; + } + } + new_file = strdup(path); + return new_file; +} + + +//--------------------------------------------------------------------------- +// Reconstruct path name +// +// - Resolve '.' or '..' in path name +// - Work around . and / to translate regular form +// +// Regular form of path: +// Absolute Path [Drive:]/.[/Entry...] +// Relative Path [Drive:].[/Entry]... +// +// ex) +// abc/def -> ./abc/def +// /aaa -> /./aaa +// D:/aaa -> D:/./aaa +// / -> /. +// . -> . +// ../aa -> ./../aa +//--------------------------------------------------------------------------- + +char *PathNormalize(const char *pathName, BOOL isTreatDotDot) +{ + int i, level, level_root, n; + BOOL isAbsolute; + const char *entry[DIRLEVEL_MAX]; + + char *pathNormal = Alloc(strlen(pathName) + 4); + const char *p_org; + char *p_new; + + // + // Check if drive letter C: D: E: + // Check if absolute path + // + p_new = pathNormal; + p_org = SkipDriveName(pathName); + n = (int)p_org - (int)pathName; + + if (n > 0) + { + strncpy(p_new, pathName, n); + p_new += n; + } + isAbsolute = isAbsolutePath(p_org); + + // + // Resolve '.' and '..' + // + // Slice the path at point of / , put them into entry[] + // + + level = level_root = 0; + + for (; p_org; p_org = PathGetDirLevelDown(p_org)) + { + if (!PathCmp(p_org, "") || !PathCmp(p_org, ".")) + { + // skip it + continue; + } + else if (!PathCmp(p_org, "..") && isTreatDotDot) + { + if (level > level_root) + { + // Back to parent dir + level--; + continue; + } + + // if pathname starts with '/', no directory to go up + if (isAbsolute) + { + error("Can't go up directory, '..' Ignored. %s", pathName); + continue; + } + + // keep '..' + level_root = level + 1; + } + + // name entry + entry[level] = p_org; + level++; + } + + // Reconstruct pathname + if (isAbsolute) + { + *p_new++ = '/'; + } + *p_new++ = '.'; + + for (i = 0; i < level; i++) + { + *p_new++ = '/'; + p_new = PathCpy(p_new, entry[i]); + } + *p_new = '\0'; + +#if 0 + if (strcmp(pathNormal, pathName)) + { + debug_printf(" PathNormal: [%s] -> [%s]\n", pathName, pathNormal); + } +#endif + return pathNormal; +} + + +//--------------------------------------------------------------------------- +// Get Src Path +// Normalize BASENAME +// Normalize FILENAME +// Concat both +//--------------------------------------------------------------------------- + +char *GetSrcPath(const char *baseName, const char *fileName) +{ + char *base; + char *file; + char *t; + char *path; + + base = PathNormalize(baseName, TRUE); + file = PathNormalize(fileName, TRUE); + t = Alloc(strlen(base) + strlen(file) + 2); + + // Concat base + '/' + file + sprintf(t, "%s/%s", base, file); + path = PathNormalize(t, FALSE); + + free(base); + free(file); + free(t); + + debug_printf(" GetSrcPath: [%s]\n", path); + return path; +} + + +//--------------------------------------------------------------------------- +// Get Dest Path +// Concat BASENAME + FILENAME +// Normalize it +//--------------------------------------------------------------------------- + +char *GetDestPath(const char *baseName, const char *fileName) +{ + char *t; + char *path; + + t = Alloc(strlen(baseName) + strlen(fileName) + 2); + + // Concat base + '/' + file + sprintf(t, "%s/%s", baseName, fileName); + path = PathNormalize(t, TRUE); + + free(t); + + debug_printf(" GetDestPath: [%s]\n", path); + return path; +} + + +//--------------------------------------------------------------------------- +// Remake the path into familier shape +// Delete ./ +//--------------------------------------------------------------------------- + +char *PathDenormalize(char *path) +{ + char *p; + + p = (char *)SkipDriveName(path); + if (*p == '/') + { + p++; + } + + // Cut './' + if (*p == '.' && *(p + 1) == '/') + { + while ('\0' != (*p = *(p + 2))) + { + p++; + } + + if (p == path) + { + + } + } + + + + return path; +} + + +//--------------------------------------------------------------------------- +// Get PC Path +//--------------------------------------------------------------------------- + +char *GetWin32Path(char *cygpath) +{ + static char buffer[FILENAME_MAX]; + +#ifdef __CYGWIN__ + if (*cygpath == '/') + { + cygwin_conv_to_win32_path(cygpath, buffer); + } + else +#endif + { + strcpy(buffer, cygpath); + } + + return ChangeBackSlash(buffer); +} + +char *ChangeWin32Path(char *cygpath) +{ + char *win32path = strdup(GetWin32Path(cygpath)); + + free(cygpath); + return win32path; +} + +//--------------------------------------------------------------------------- +// Change suffix +//--------------------------------------------------------------------------- +char *ChangeSuffix(const char *file, const char *suffix) +{ + int i, n; + char *path; + + n = strlen(file); + + for (i = n; file[i] != '.'; i--) + { + if (file[i] == '/' || i == 0) + { + i = n; + break; + } + } + + path = Alloc(i + strlen(suffix) + 1); + strncpy(path, file, i); + strcpy(path + i, suffix); + + return path; +} + + +//--------------------------------------------------------------------------- +// Get Current Dir +//--------------------------------------------------------------------------- + +char *GetCurrentDirectory(void) +{ + static char buffer[FILENAME_MAX]; + char *cwd; + + cwd = getcwd(buffer, FILENAME_MAX); + if (!cwd) + { + error("Can't access current directory"); + exit(10); + } + return cwd; +} + + +//--------------------------------------------------------------------------- +// Check if absolute path +// +// Return True in case of ... +// +// /dirA/dirB/fileC +// D:/dirA/dirB/fileC +// +// Return False in case of ... +// +// dirX/dirY/fileZ +// D:dirX/dirY/fileZ +//--------------------------------------------------------------------------- + +BOOL isAbsolutePath(const char *path) +{ + const char *p = path; + + while (*p != '\0') + { + if (*p == '/' || *p == '\\') + { + if (p == path || p[-1] == ':') + { + return TRUE; + } + } + p++; + } + return FALSE; +} + +//--------------------------------------------------------------------------- +// Check if drive name +// +// Return next character of ':' if drive name +// Return head of path if no drive name +//--------------------------------------------------------------------------- + +const char *SkipDriveName(const char *path) +{ + const char *p = path; + + while (*p != '\0' && *p != '/' && *p != '\\') + { + if (*p == ':') + { + return p + 1; + } + p++; + } + return path; +} + + +//--------------------------------------------------------------------------- +// App Name Utilities +//--------------------------------------------------------------------------- +static char *appName; +static char *appBaseName; +static char *appFileName; + +void InitAppName(const char *path) +{ + char *slash_path = ChangeBackSlash(strdup(path)); + + appBaseName = GetBaseName(slash_path); + appFileName = GetFileName(slash_path); + appName = ChangeSuffix(appFileName, ""); + + free(slash_path); +} + +char *GetAppName(void) +{ + return appName; +} + +char *GetAppBaseName(void) +{ + return appBaseName; +} + +char *GetAppFileName(void) +{ + return appFileName; +} + + +#ifdef TEST +int main(int argc, char *argv[]) +{ + int i; + char *s; + + for (i = 1; i < argc; i++) + { + s = PathNormalize(argv[i], TRUE); + printf("[%s] -> [%s]\n", argv[i], s); + free(s); + } + return 0; +} +#endif diff --git a/build/tools/makenorfirm/path.h b/build/tools/makenorfirm/path.h new file mode 100644 index 00000000..e9956dc6 --- /dev/null +++ b/build/tools/makenorfirm/path.h @@ -0,0 +1,93 @@ +/*---------------------------------------------------------------------------* + Project: NitroSDK - tools - makebanner + File: path.h + + Copyright 2003-2006 Nintendo. All rights reserved. + + These coded instructions, statements, and computer programs contain + proprietary information of Nintendo of America Inc. and/or Nintendo + Company Ltd., and are protected by Federal copyright law. They may + not be disclosed to third parties or copied or duplicated in any form, + in whole or in part, without the prior written consent of Nintendo. + + $Log: path.h,v $ + Revision 1.3 2006/01/18 02:11:20 kitase_hirotake + do-indent + + Revision 1.2 2005/02/28 05:26:13 yosizaki + do-indent. + + Revision 1.1 2004/08/30 08:41:14 yasu + makebanner moves into CVS tree + + $NoKeywords: $ + *---------------------------------------------------------------------------*/ +#ifndef PATH_H_ +#define PATH_H_ + +#include // struct tat +#include "misc.h" + +#define DIRLEVEL_MAX 256 +#ifndef FILENAME_MAX +#define FILENAME_MAX 1024 +#endif + +typedef enum +{ + FILESTATUS_ERROR = -1, + FILESTATUS_FILE = 0, + FILESTATUS_DIR = 1 +} +tFileStatus; + + +// Item Reject Control + +typedef struct tWildCard +{ + struct tWildCard *next; + char *name; + +} +tWildCard; + + +// CallBacks + +typedef BOOL (*tCallBack) (char *, void *); + + +// Prototypes + +tFileStatus GetFileStatus(struct stat *s, const char *filename); +BOOL ForeachEntry(const char *pathName, tWildCard * reject, tCallBack callBack, void *param); +BOOL ForeachFile(const char *baseName, const char *fileName, tWildCard * reject, + tCallBack callBack, void *param); +BOOL ForeachPathGlobbing(const char *pathName, tCallBack callBack, void *param); +BOOL ForeachDirList(const char *dirName, tCallBack callBack, void *param); +int PathCmp(const char *path, const char *cmp); +char *PathCpy(char *dest, const char *src); +int PathLen(const char *path); +char *PathDup(const char *src); +char *PathGetDirLevelDown(const char *path); +char *GetBaseName(const char *path); +char *GetFileName(const char *path); +BOOL WildCardCmp(const char *wildcard, const char *path); +BOOL isPathWildCard(const char *path); +char *PathNormalize(const char *pathName, BOOL isTreatDotDot); +char *PathDenormalize(char *path); +char *GetSrcPath(const char *base, const char *file); +char *GetDestPath(const char *base, const char *file); +char *GetWin32Path(char *cygpath); +char *ChangeWin32Path(char *cygpath); +char *ChangeSuffix(const char *file, const char *suffix); +char *GetCurrentDirectory(void); +BOOL isAbsolutePath(const char *path); +const char *SkipDriveName(const char *path); +void InitAppName(const char *path); +char *GetAppName(void); +char *GetAppBaseName(void); +char *GetAppFileName(void); + +#endif //PATH_H_ diff --git a/build/tools/makenorfirm/test/Makefile b/build/tools/makenorfirm/test/Makefile new file mode 100644 index 00000000..b2673ecf --- /dev/null +++ b/build/tools/makenorfirm/test/Makefile @@ -0,0 +1,57 @@ +#! make -f +#--------------------------------------------------------------------------- +# Project: TwlFirm - tools - makenorfirm +# File: Makefile +# +# Copyright 2007 Nintendo. All rights reserved. +# +# These coded instructions, statements, and computer programs contain +# proprietary information of Nintendo of America Inc. and/or Nintendo +# Company Ltd., and are protected by Federal copyright law. They may +# not be disclosed to third parties or copied or duplicated in any form, +# in whole or in part, without the prior written consent of Nintendo. +# +# $Log: $ +# $NoKeywords: $ +#--------------------------------------------------------------------------- + +SUBDIRS = wram_rbin \ + +include $(TWLSDK_ROOT)/build/buildtools/commondefs + +MAKENORFIRM = ../makenorfirm.exe + +MAKEFIRM_RSA_PRVKEY = ./rsa_private.der + +MAKEFIRM_ARM9 = ./twl_norfirm9_print.axf +MAKEFIRM_ARM7 = ./twl_norfirm7_print.axf +SDEPENDS_BIN += $(MAKEFIRM_ARM9) $(MAKEFIRM_ARM7) +MAKEFIRM_FLAGS += -d +MAKEFIRM_DEFS += -DFIRM_ROOT='$(FIRM_ROOT)' \ + -DMAKEFIRM_ARM9='$(basename $(MAKEFIRM_ARM9))' \ + -DMAKEFIRM_ARM7='$(basename $(MAKEFIRM_ARM7))' \ + -DMAKEFIRM_RSA_PRVKEY='$(MAKEFIRM_RSA_PRVKEY)' \ + +TARGET = test.srl + +%.srl: %.norsf $(MAKENORFIRM) $(MAKEFIRM_RSA_PRVKEY) + $(MAKENORFIRM) $(MAKEFIRM_FLAGS) $(MAKEFIRM_DEFS) $< $@ + +.PHONY: build install do-autotest clean clobber + +define ECHO_CURDIR + echo "==== $(CURDIR)"; +endef + +build: + @$(ECHO_CURDIR) + @$(MAKE) $(TARGET) + +install do-autotest: + @$(ECHO_CURDIR) + +clean clobber super-clobber: + @$(ECHO_CURDIR) + -rm -f $(TARGET) *~ + +test-utf16.bsf: icon.nbfc icon.nbfp diff --git a/build/tools/makenorfirm/test/rsa_private.der b/build/tools/makenorfirm/test/rsa_private.der new file mode 100644 index 0000000000000000000000000000000000000000..ccf67c186ba0783774a391fa190a1169155f281d GIT binary patch literal 608 zcmV-m0-yabf&yFu0RRGlfdI^ydqI7je1HOl+Yp(I)Tf4e@7hDDZ3|Vf5OZgl!rAb0 z*83f%7n*m8hv(?Xz=k%zT`bpHAL`&wX2*l5GxU>lo;sIsQeHis4((7+)?dUnagoFp zfy&R4POTg=aNJnsiHW0X2}6+c@s2=GxAACyF!yZ$ik#GkB}$mdW-kH*0RRC4fq+hQ zZdX^o3titC%RXCESiFGZC6%%IhdCg7s$)i{{KG5cEwT^QfEk#ZA%%I1dEh;uHZPjV z5t1~&-R{NuyD0C>s3WN#p=?Z z*(>6%jD;YYND`!^I52ZSVS9((vB6RD<0CRlzixl)1+a8Tu<UUPp7kh#$!@g$VcL08j(Mm6qlecx;X z-nFqs_-g#V-(^EJniWC02~+xnu{a#-yR)Yugs_$y8t~d@XK(+_sv%N?oT9CknwCzKqm_T literal 0 HcmV?d00001 diff --git a/build/tools/makenorfirm/test/test.norsf b/build/tools/makenorfirm/test/test.norsf new file mode 100644 index 00000000..7c3093af --- /dev/null +++ b/build/tools/makenorfirm/test/test.norsf @@ -0,0 +1,30 @@ +#NORSF --- Norfirm Spec File + +VERSION : 0x0 # GENERATE + +BOOT_NAND : TRUE # TRUE or FALSE + +RSA_KEY : rsa_private.der +OUT_KEY : rsa_public.sbin + +WRAM_RBIN: ./wram_rbin/wram_regs.rbin + +DECOMP_PROC : ARM9 # ARM9 or ARM7 + +ARM9_COMP : TRUE # TRUE or FALSE, should be before ARM9_SBIN +ARM9_SBIN : $(MAKEFIRM_ARM9).sbin +ARM9_ELF : $(MAKEFIRM_ARM9).axf + +ARM7_COMP : FALSE # TRUE or FALSE, should be before ARM7_SBIN +ARM7_SBIN : $(MAKEFIRM_ARM7).sbin +ARM7_ELF : $(MAKEFIRM_ARM7).axf + +BAUDRATE : 8M # 4M or 8M +ARM9_X2 : TRUE # TRUE or FALSE + +ENC_KEYINFO : 0x5043414d # 'MACP' +NCD_ROMOFS : 0x07fe00 + +ERROR : ARM7_HASH # SIGN, HEADER_HASH, ARM9_HASH, ARM7_HASH, HASH_TABLE_HASH or FINAL_HASH +ERROR : HEADER_FOOTER # for debug +ERROR : WL_CHECK # for debug diff --git a/build/tools/makenorfirm/test/twl_norfirm7_print.axf b/build/tools/makenorfirm/test/twl_norfirm7_print.axf new file mode 100644 index 0000000000000000000000000000000000000000..b255bd21a2a20901a9313cd8ff4c795d71cefd3b GIT binary patch literal 175676 zcmeFa4|tqanKyi%$s|ofpwl+hTDmZmAYFu#mI4J?O`4=_AZ@0ZNn5ZwO_NC*nm@y2 z(m%UMlQwM=D9}QyT}2qQDkA%CKvX`&-5|w97PTNKD(Fn&2(;>gi|+!WeSg36=XuUd zQo&vPzU#ZLH&>_6bKmDa_qoq~?sK32=hgQ_JV~#KKvD*@Zq^o z1jQtT1rW;cGX4h|{(d=M2>vDk;|H2SuP*@ZK7`>1P(%=baAAiNZ~x_oFZ%lS0{%!7 zz^@2DJH8!Gn!hyM$j`WoF1O@+G&NTI?Y-Ft19xUmD@=_!B9sw<)XIBl1jrqjN zj3e%P@U0Uk9-0nVyfP_vQ(oAJ6*X?FHabaC9g1+^_WPP}V?d zY;F*;U$l61IPmSv@W>A{rI67lQq#d=mJ{e7h0J6Bb>hUVVe#$E8A$8R6DNGdmFK^V zGCUrDEHixKcqyKUaJ;zkTQ8BO73n4MBi(g{;&^nDINsuj14Dc06ucMrt$4b4$Ihe0Lj1G< zF!4orI(;`wJaq6=0r>L1I}1;nbK`r3STpAL56&#!H#nm-H8|rg@JfkPrhiIbrZ|ww zIO&^5F97{7U@xyXC|w+a9)1n^JMQ0J5P&`^A+xAVW&G22zPJeQ1!7>-K{-qXAC`yz z{X1Vo9XK9@K8W{E>wD4f>r2b9KwmbXb$lN9Rp7S}ztSLhf_7LuonbjrXT?6@Uxf4o z^x`1B-=8>fd|?`N6?bHdgDXZ013Qu4&Dr9SJAm6c<`3*e9&XMK2c8CgFL-?$dP-#$ z>GT45{4(HoIrtR@(-{FC{y;k83znuu5Il7nPM{#2&l7eB{yWD`#`oXB_dLoOxFcH% zIYr3G*gsZWd2;#6AfG^f9n|SL-`_i09GH!IvT{uHeXF=g-2Rd;Fg4A0;$Vj*P6g~A zbzpjMYT6NfKGYA`hd5R|vUk+bPecJ==<>7=bPj&ieKI2+L+~){oyacxTxU2cFD4 zj`!if^PpQf=6~h0ep$}IKaDgB;E#%fdq+j!R+-la{ZCDW51U>e*gpgHbQa^V9^a~I z5&xHdqz!!L4dl19g70D4eMv#!{FkSsQ<>?=OF`gUFToyA{~9u=li5=7t(Qwh>gCeV zw_bj0d-`SA@5?{bJQ4nK&C&f?BaaB)m-W5Uo~cal&z{)+>dVk^W=iP%m!SKXSdR~X zb$bErleRg?x&Zqe2;Q09Rf#$~?ba7rCJ$8HMw=Zg^z8t=a;&Uc*4dJE0`}(H_afz_ z--{q#gykS^%`U1~Jn9#H(9;gceH-ZdVT(J)L~v);FZN{%r|k!?5XyXI0d?YsT-0S9 z?9JHXfUc{rK+f0UFJHsc5B*c7*+}oYT?&Q`=fLonIWX!?4ve&N;KDq(C=Z@w!Stau z$Y%}m&T{Un*q3!g|ESC-%FaRAnQ|)!ozz8O@hEH>d4(>-e*F5g#?R7^(jPH@EQ7U@ zFWZ}yV`yV$g(_itLD(>KLm&M0z>$peQMQfZ805~J2H*66cqX$Qx}=SjOZ}&jR)1C$ z!>{4_uaN)i;)v@zu7i(y1?|Rz_=%A_v()9YUxo~$vOiY5&wqgDmVY{0Z-zE*n_+@jj?q}*Pkc0FaHT_5Vge;T6hrT_6HcM0vBY%`@wVUpHk?sTg9+$FE zKjg7i^Ju`6a=c!x+5-B2%HLBgj+a4iWhgHxC+gBSVTZ=IzXUx!H>lc+An1aiqc3_2 zG?W3lnAHLvjBCev260%o_|@NIg3UuG z&T!3W3F^~o+Fj_4OigKTu!0;DOd=O9cS018WoR9a(cruJnBy{Yl;8lp* zI5r73>#H0XJAUHCF$d3=@hrykMLYv|X7M~1@z4g(qRlXG{@{*Le`*@~D>p&ESC5I* zHTNUVSHb&ONs#?>@DKeY{prTD(-o=O(W9ZCWSBPU;6s<O@zwLCfL44ZU5IwpqGH z44@oRV^8aM^11|eKpuXSRWWo(9zuK+eqP*DEIykp7G;o`eWL*0Z^}a7C62fPHVIn| z!WLnpv`^aUIp|kUf{h`JZE!!^9)a>io*Z}k=MS_qrv?WhRn05Q{qlRn+Cf8tqMcG zYZi4SQdgwUKs@Ti$Sn0?WUf^Dcn0zOLC9S>0J$R^e_iIueD&A`T9+lrZ^=8+CGuMW zJrnEvhLDvGhc1oceu`JE{ zS_wTtk0r$cv>_GC#RSW_;oj(p1(VZ;`QT2X=*a!Vf*8z36AT3)BhP><`6$jFnJE6+1Cr z4$Cs~K`(yjGu)J|>Xj`{$kE)K^Q9FQ=qzYytAZ{M&i& z0sjYj`rUk;JP=xm{)q~Q9Z8*-_{CZu#i6NEZ?-=Cz?pajl}C_|vatrFAAmi=zd|3V zlcK5M3gG4hca8>v-^*46F;2zPhi55#ay9h}`YO;fj5Ll8!!*mW{JcD)Wqs_-RA!>Q zyk>>}ck)8M0_lr&KKO%GW4>mG&jQrdlEP3LegePH!BJn}uC$T!d@VQHuw$H`@rfn} zO)=9fS8xUfwAX5z;6^8p{Ar z3*LmWW-2o&of<0|LEpNfFkJx~DysPQi+9aueGF&5uoCkNAK!&B8GT!mhfZZ2~M_(kb z{|#PeTXMZ0a9ZSCnK?etB0nXpKgd&s*l~Y>*pVs{Pl6xPgnp0@na@|juT=Wq5dG~> zk2)`aKE(QgbZ#20K%FQGjL3MUXmcn7+lC@>Wt!zy3cpYkyaM@NoaWfV5vlu#qpyPh z9VrU*rycm=;y~G`(%;ptFV6RUtmiE2lli~WAo(`R_yOqNSGhB-a2P9~9e%1JnPHs~ z6_3v#UbTm4%hA@LJfC?yL;~fp1h$X1m;FH5_F}Ar zw9qF)S}Vtp-xB(J+4i6u{?hh?K9qe=${qP5=Ngz+3cl^}8Su#%Un@NqFdeD$emRGQ zc~alTSV=#FR{5pl{5O@MncfK6jYy{+^uCJ9bZCBmw*C=}sUe@7=WzP7 z{$N42e$M{vvXMKp%PKJD7}=3s3;Y`V9`HSqSp*(m$5T}7z?_XYx7vp}m%H&3Fyb{2 z;K})XpAeiY{;xlN=*Qo@LF;~Wa*Jq>gHd{SSF zfaBQC7pzPZe_G+nG053JSIWMUbPmdB*b$3UjAQaFUfC`eOsGRS|Bgf^N*j5VV}bK@ z+8i^URv1D$sh#9E6Y1FL=A+e z(o)Xh;3Jv;K)EeLn(RLm!~QBqxW>?jImIVs{_hXeXC8swvK)php3I#6<_TFQr0>Ez z=l1#TM;tSUAPJar86%<{tz zvwujLDX$;?ne-W?VZw&R3mIo-D)auC{h5(7`!l}czDz0nmeBJ^D0Dw%J#hkSOY|p| zsmu?d^NtFc7nH%H@IME#frs*_5Dj!XA&XRTE%{SzUEYz@1-%89n3_ zH(~7aOuA5{@57wheZiTj`{sh*BSGjc@Mwm8NyfP!ybDoArcM??mbIVVUhpT#;>SDd z;6mz2+XnJ4>mco?9Wu^@?o`?C$jbg{U`LkxOgWb!U$0alPkpIOP#nt6t~i8p!Wb@& z`LcDge^LO4U-BvNn!D~$wmg0a`C0`#TUpJVAJ&gH7`?5axZ}u5W zD-J@|gV}=0)oD@j9klC_v`_pn4S#n}8SrIdFk9BQD?2lUCuF9)GyD2xAnq*c9d@4a z_w{FQgUslk9Ge+B2%4rb%(=~?u4sod!J`=Srp2HmKf?nKZx(Te#}4p#rAp21QCF|C z{4uBKgrJ{F%vn=U(2o$Glspi>g!Lr^y?^Y?)Yw&jIB~oRw#G7;9oUUFW_MP^cV}m^ zy+m9;;7X(+Dh{Td;9;y|fhY9vkHEoyW-dY8*R>tSF}HLbe#U=)Q1V6IAP3tm2eK1~ z{^+rp&=d1i7Ccyp_NK5n*k4!{I5?v$06hg5wh}t%p8;E(rF=MjDBB`YQcHPguYp}? z`*ulx9K^E>yp0`F7Y=lRG>=kGCR9mt!rc97P?2=`ZV zeIqh97wNMeu|9DOUn)Wl^m{Jbgfl@KWZnX(XW;LLEi)g*Vn<;a@)8sWATPtv_X{Z+ zgri6X3R(UvCqHPWq*Iw0@XI;$q(h!reoV^=LZ_jD>?KH(bAMc4kbDvTM@tW^kDOwr5MNfQ3o-<&hZ)61`C})s^I>> zh(nn;m&&>17a-#itNxP5?XZcv@iTpz*TIu@`tyH0ar`Lly)uRNM715~L8cM#LjGji z!MeBiEVLECvHtm{_Pxk?nIQTw82dkTd&QmE+mWs_Ny%S|^4zCk>g(3mPaJ0$-+zYp z-yu!rJudE?fw|!6!-0{hsDrbLL#eT~eVB_~m&&}koox~3q>$fX@py)Iely|?i{~<+ z9WCT|CzP7%glb1wZsty$u&=`Utn~PF#+e*| zZ9k1E-<}EwFl#B|vi;-wst;`u?Tt9Bb?_bYYCE#kfN8s?FGIOsUwZz_ zezbdH-Dk4}ed)20O7vy-LFcK#(W=eREp#``IbfzMelpbw3{ESU+&6t#d{?IXx8OO9 zd1R+lrFkx3u4lk*j)`@j%8GS2WzPn#5V&I0DUMf5@WlLV$?j9JzJ2O&=2v(Z^AA8j zVYZtkMdzn7ADEuXd=7IitS?+Md;$IDBGOM8&ioX|9|Lo(y*NmEW%k&?S zhvPmG7(<_4uJ>WC*&xE zdCa2u4&IHt3_HRwM~vEWOxoW%)s0j=TzjHH0BsM z-?uA^x^Zk+JSKhH{dl6k4E$r+U>W!X@I>Fs$0zhz0K7l6Yt&y^gSFns80_*~whNQM z7d{dF0&fk*5s&>c2p{59Mn>VQX2o?o^dNj8+v&S4S<%*V4*ghxFfxW@{gg7s4@w#D z4?GKh^lZl2+9zc!2tJ$jiD$D;0MGJ1_;ftW`ZG?lFH;uyB>Ft6Zg6eOmKp0DQfBzL z63XTm&%QVbaVTRM{4wK|S?gk^eV1}$?WlYXbeu|MD$r&+``EUlKL%b#&q7>q@T`JQD{~?A`W96Xh!#qM^JoQLXJoOa!x?s#y zG8b*8BbG|LWE#v5)1=N1LFZie%9mjf_`d}Qz#Q!cjs zi$D{!@F&xY5ABMntJF*CbK46xeo5))2;Kvq$37)IEASk|^SQgQXAHk(crVAV1HU7H z>k zls-e@u{ST2+Q~Nha&ZvNRA@SEcGk7%Lm#Pv9ilF<|K@{U-UmGD$k_0;Xm3~EnJrld zKL8s?qkN3@>k{-u`uYM`PZ8MH_DrT6F!c$Wp5+HFN654 zu%T;^Cg;wumRYg}@Eo+Cw7W3y^`N1QS3vFxgw1AJl>yXqF>5y9VXVs+>-0VZoIpPP zQU@!!rZMYvlqdULuT!TecaGsjh~v_O*X?gkLd!&TJ>Bo#k1o}Pr+L$ey8Gh8h%so3*a{uziIeQ$FB^(8Tie@?{xfT<461E@0B$e_f)do z8DRZ9M!2AIPWlLN1Niwu*xLlXys}31gHln@hIXYt*NygV-R^YJx`S+AX8BM~opStC zhPi;$)Df(0m^jQgc-$jI9CHVmQTEFu988Rgim_1<+Mij%x&`0vtfS7twBsv~&ZsYV zYr3G~t7+)%>O!PPUMQD`Fm9LrM9mxT!<_kjX{=RV&9+gD;D`Fd@^eP^g6FDHtiz)2 zD!NJWZ(s}_N*4s4N{jf9M@vU;PoFaU74Tj?_B-^?Ne^O+{!5_W2m1R!|D_yywCVRC z&M_5^b((nDD8@(5DS?y&dnyUWn?~nPO5Ft;y(butjaC$*O`6n~nLh)1gnS6gaJ)hL z1N@MuGo>pJ292h}oVZKJ@)DJ5zmqP@vHUVCfPTL~yF(cbp^Q{osj1{=_&X!PwC|Bp zlnwWst-Bw3yPvuif#&;rkq-ys7|_p09lr2dAugK@+YT17iS6uJU{*p??#)4y{7!N;ysDq0DcEFz8uem_|@PS z(Kti@|MgRR5_-TsL}XQ*-E-O5t-(-pS9{B4%vX0$w7H`>(K)Y02>B9h>F(`l4X%#` zn>!M*=GJY&-kwoaz$SN;E?9{(*4(KSd+)^VauJ zRn5t~coTx-Ia!#7aC;PTR5y2YtZ#1FRELtQ>uQZ{6*38iR&}7*nv=0=)W}48>xS6k z-X3J4JzCS<)P7cNxNs0>#wT)5z(2~s1zg4zxBCc}`lYXiCDE0R^ByDPfBx2-Lf$f3S+dG$E1 zGFKEavXRxTuC=>r zBNL$ZTSaJ1ol2*BNjuyY|7(o3Y&NVTvd-&*$}JB>CUtc((IHLEFkMw&9gX)ULy_p> z`YVNos+ZTypD%$8F%twLpyAMxQTTBa7>-m$M0<2IJm;q9HOb};(Uy2`d#li7D!{M} zMQpBk&yCpKNlAcKb%(O387(~31GyBj(Mfktth3iBFqE%8rG<&}p!J}&;;A)SuZ(0OwRlwK4aF#QX|~hr z^lWO6SF>>_zwn|eD677jI^1Nhht6MUz27yV6)>tyul8kmXw|7N5W1ZcJZP6b1STv#Cc3Yw% z$%0hOY?ki3`x36XGHRMs*(}aO5j94HjgQvXg(K106*Z{5(MX65cUvw&xbEuOT+C{; zsZqts6avg5k&L!=wMa@yP}NwwqAFV7Aictp-maENEYXQ}J{gl3?YP+?i9%`7&Te#v zWZi7*Xztlaao`z}-HBLTS2C7J^v07t!WaVrS+Nxl5suVEm&SD4QXlKuklYBK7cPjl zwr`FiH>;`{T-{hb|6-XmnpNq$7UVz|=0I$)WCJWI=L7PZFPX>iRblF&`bz328m?Qq zLX@wHb(NPhxz)|>$;D_nyW=u-g2+WeMC-ye>o?T5ua^yPZD%h!NZkoZ)6^AbqtuRE zGIxnUCD5unS~78dWl&#?@6vYa>;Q>uY5pvS?Wkfn6My0;0T?pEU}1 z8}QshWw#00$S-S}hn<$BKrt{8iLYy1>FGg4qp__mv3Rl_O}XrpMKM62v`|X8yDh2Q zzb-zOgLLAS)HPL+Ds5ZR|Lca1j-M`lMR&5cGoDm>VY?`sNVILn5Sbj!(9^W(CPCZH z9n^MITbq??xNb#VL?^3^kPU=I5UE{b!XcJ4KC8Hwfw3+`bYtR)SWi!5{USkUiCVF| zyS29?7HUREyeCKJs!p=*$uzZfGi8>T>Z+L5uC6P*r9IiQQQLtwPA?SIC=Vy9BaJmn zqpRB2!zuN|YT?kD;_xP{er`wCty;7=^jVg)U&~+sriVc#7$*{y6`O`dp>sl3G%a6T z+n9sXE+^ll=8?&~aSWvW8P?Jxp!QOh>gKMNScmD2L{-<;b+~pR>3X0%(|Ai%r4bdf zr_q&+Zf;*M3r&`usw0iPT@76|98_(HB_gZqWq3{d=EhhjO*^?jH~u{v+uM@Sj-2dj z^BGU3>Ua3sgtC$9crQ9nx$M|vBdg(fdl!9$=e^jBR=|hRX0<+~<71yO+T*!6tB{o1 zjV76|`eu0FjmVJfE9VHU3d19g>K1@D&?B|sIw_zU(baC)(iKZIv~g4*M;CQH%h6)h zZjGZyivHWUI>X|p^gT*^?>w+3q&NU~2`m5P#W@p;2-KJLSAlLNG=+v#Wx@VJPOD&zP81v<5)va1m z0@Xij#S{iw)vj)tWg6Y8#U0IE7-yoHQX(O%Y_{pkbNG2SGfvM)d`m(%)8r!EePis} zUZ@)EmN)lo(&*fV-SR%m>!RT;&2ed4(wOXaHb)NSjda&#Rd|KfdC^UvYD{QOUZ&w{ zQS2CmMq9eOdb%-fAloLC07j6li`$!3`-?_awN+Avrp2Mgx>ZrPOCyo0nU!f!2<7dW zJv51`UE@0#t!RK_M7g6AvZ}kIw=-r!+oQECOWDP+3cWolXPWZuXJVIWusX}CCDTel zf^zK0#H%-QTFA>}j)k~>-L+BWrIs}JNXG<5%!nM@Vpyy5MIRtD1`kP`O|`7uH7wl` z(vc(&x4D*`88t>Q?W!E?X?2BUlPCKKCOXHWBC@3;9AA#!vJ}N_tn_Svs!K9`uP!gc za36DSdDR(Z4A02+Sxq2V1#1-Bu&TDPzUr!|)xw2qt0Q#{E3`eR_J*EiMJ(1Ttws%v zEoQR+ph`u0Kvmf&z1=Hzn`N)6)OP6!DOq<^--H7(L{)?1oT&vOt2vPF(tYC)YBLoJ z50gKB*4+)sjOzAOReyDk+f#|6Ewmb$sOm1N;e;~naC?{T%>fQ~#A4|CS}~N@Q1%71 zG1qYe5>4lFmiqL}Hg7HuAlAmj-7fmo6IdSN43cz<; z>8%9J`#=zsHau{>{ zgsIO>cyc|!z+HIZ8i9earYMpYoCMrz!CL@x?ZD9gJzlt;VBk;SWuXQ40WP=Toq%~C z1|CwL-FR~C!N6D-6|Y(JdjY2`_!hwXEcjNyeHMHWaNL3q0bXapp8*`Q;5z_UTJT+f zD=hdh;D7~x2{84G^J>z*NATpm7qrK^7BKHm@Bu;pv_*$k0w(`Kz(^cMg9fDc*l0l>2@_)~y+Zv)GP`uGB#l#hOi>3t3GOICUh0zPWN-vs=y z1^)}+!xsDq;C&W+1n^rH{BMA{mWKajdQSj8fHPO7e4Ynf_9-v?7l6wxdTbgIms{c3 zSS4Pv!e0Ws*W&jE;C&W61&UT@yj1z)ekBQ;^v?#o%;G;6aHR!*5bzoco(Gs`yG;BK z0}dVXcuJ1-#aRuK=8~;x7k$xdn#+AF$}dfSEq^Nqb%cnEcsJusk~e z-)+V31}wg2?5#-j10J!$2XwgMHw0LoSptIgauD#nJhy~D@;?Mvo@3JRX8<3u_}#(q zPkZ!t0lwXee;Dv03;q)60kd5o|8D?h`b>}cdl)d!0AnCC^X zPgBA_0bKr>?FHw-4oLqp;10w`poHHvu;%wiz~m25h9gn&%8H zJFNUA0rR|uk!JvK+6vzT_;w4v0r2}R`a5)d@~6C?2mEV`{)>QLv*0fSuECjzb)ctw z4+7?%{64_c$M*sA%mhFgjtxkX-oVcQe(g>#{KtTKw#0YJiynO) z;Ivgf^?>iS%BKPFYnHwn0aJfwytNMSgI0P;z|>zk1Y-GL518^Ad%g`Y^Jn0%0H*$p zeSQNl^=;xm3Yh!InLpOY#{p9xCjJY6seeQNB4C!!T%G>!0Mj1N)9{}F|JsuGRBVNM z#e#!?xfhu9^iT5vbFZ-R50?VwK4ApQ{9O+Cz?VJvBY=Nt@oNP9k_BG_nEV)@`RNAy z8er;^>1_r)Z1LL$_$XkG2UwrC1DCC?Cu30N}U!y#C9*fO*d=!zs@LI^58I1F$^zj|b!9qEoT3-{bc!z>kBU zp&tPpy2pbb1zc~z-vJx}Jc9lK(|Zzd*}ba0BmA@uH~gN};ReQ?abhj#OHqKN|G9=u zI5$CW{9o8hruo09VH1vvRPeWm#gDDxMZ!198$SocBry{(#~bws9|ixj0GmICUqo2v zm+_YaeoouNRKU2iR+v3}0qoJc7BI&@aC2fR`0WJDI}_O6lm1q~LF9*N1Q7li!1VWq z{!64ksO)JP!XF0wxD|c`F#GSXAU?yN226VkYWP=xsSkjX|Eqw5x;&WPp8#|GPk9)> z6czD+1>@2w!STNdp9?r?!50HwW64tu7^;_l%>UJ-hrO0+{@s9|)Aq>puLDf`V1AkY zZGh)m@_i04=Lbyq{{s8~^miEa)E{qVU8n7r{QeEFv1h_B056&Y*8paI+5e$^Uk})>Uk3m`Z>9HXz(*|jFyKR0 zc{~P~^C|lzzasGyz??6-8!+?tGGO{^)?mUubfh_6X8MHZ0OtJJfQBz%N875eb%gaF z%ll(_a59hn1{v=8i^G8FugHSs^-q8~ei;PJ^dAPy`I!n0{{XOU4=?7y4iuZV%JWpf zzqRVedjNC1Z1Q(L;L|MqF9LkjieC@dvtRflz)xD?J%DFh@vj41X2E*^KWxz-0?hde zwr9-WVZd?ApL`8)s}=uSfH^+x)AZj3On(hf>gyT6wtf5-Fz=Q*3@q)L7eLtMbvj_q z_tXPTe|$Dz&KIygP@YAAIiF_W2EgT(JRb+#%EG`O%d3a56@DFH>WlH2{w;vbAMN9d zfSgKgxeS;PWj0`v7zP=RAZ{{;vS$c-z#MZvf`}lCifZ0jI6{ z`a{4YR{nT!(flzzfrdgZvr*mss?x0du~N{a=Q+6SmUZ z3YhbCm+SCdfH~d_Y5gAp%=rT9kMX|%xW?lD4Zxfqs?p&`0CW9mu7M)87l2 z^R={J+QUBr=K7Y==T`u8eJiBt9|nBXst^ALnDdXbb@=muZ@1)s6)@*pO#I1MfP2P@ zUk13s%Kv=8Pg>!Z0=DgECE!Ynz8UcEtn%9==^01)pB;qV`iA~p4qPO@0@!Eq>vPJ* zDMcSXg%!BpnTPr|E%2ej%L?lPA1SRYC@6MdpF5nX&h#k^2^3Wq6%?Ocj9aC}PUk(M zF2HyL&Kz+8T+uFkxa5?gI>gxR%yTBe478KoY`W*DNLar-b(#_0T0#v^zU$dv~;tY%5ZLR#Yvduv1Foq zODwUeCs=(!^#?x~Z0k-0aiB<4U$z$KL->pGEXfv}>4>gx-n4doZ+l1UT7AOAJkOU0 zP1f=t(puvqH+N&JZogyFoi4sBwjy1}5g+q*4ctvpxbWr)N+H<|{yof-f7l-*Hz!bv z|GvN-?ducGiS}5}+U}mU>WITyNM{n8ZAqs$IB>JbQo8$Z;6G11DUbr$otyP^cxZWj_lEWs4J`>bsN+g?H5#)j*0N;l{0bNgRb1?5vSnj4S$qKs*J&=w z11SN6drg4Wz#>lp4Bm`z@e@n|c|}L7;s9XA2UPh<%hFq#-(lfxNi@fMS~poWVM4=F zWw5-ib}ROuRVCK9Clk$b`Oax20s;!ez;QbH4-yqYu#i{J-j`Gx6rMQ(G_vqv)M4v-UVLJ{B?R4(I z`{IU%di~lEUKVSPtBtQ3)wv$KMw2wxJzHjs6AH+ytXS@zNl^VyXbDE8`?jh$Q#CV@VuaT!0*;qa*SiYeAcsnIv z>fFb3UycM5Zo!*`DX2k*g!Fpg5J_^I)rxM7{jrvyMVp{Tb0vX-(Rz^}6Jtl|X>r&{ z@-ZmK?Bc{3!)l~6Jzp4c1&YdZ?v#qN zfj4v87XIkm-TjE~u6G}D&Jh=j>oL8?o*ah&-0sJEW^Je5FTH!TA#5XEZ~G)7Z^R~U z&)YtM*??qodk0-BS?5OF<2()Aeg_6BT%Mlu{tP;O*hMSbf0@QI&~g4pI!#4a4^#@M zE_M!iv1}+@?BecZvb$3STxSQY zZccPzA8|ulTMzd0%ZPmlrxvg2Ztt>E83cMWQtye!TH4#tJ(0u*fV~OWW^QT4esr~G zQU)DD&|L^>^|q)>{O!OW0Un1E6Y@X_&W&l!q!7ef(<4u~*2=BjC{vc7DN__A3#cJ% zS|7<17@66Yc#md9H+Bv*s=-B?0H@ewgv&MDp~EXR9B+xnJ9>K-DykYcR07(1^+ow zSfsXbdEE-|s8Q8c*|g~su(a~(OucJJOzck(4jyZ>$`w`PM*&D{MxJPRM!hng;ZD8p zaV`lmFSSO4#lO56jI0Y`c#lfmCg z5%_;YSe+!;<9rEZH)wY|q~Of$;T$U2q!=}b z_oBBx2>-v_G|5fv@)m+2=S#qaDFC`HZbxXy`6AF>=M6oQ_D-JqfO#+}P7Hr%d~)*W z%-cSv&>zc)@ohl53++wG8s6PTwuc46Ab z2yOx0kTV70oAV0UrUy@>2 zA(7~V#4xXc15HRMjD#$ndn96ya|CEp?MSl6`B5IG-;wjl&}0@_wCfkn$jC!6#h63f z?eu|yJCm<>zABh2)D(2$$KB7d55$8GiOeW~IfCd4`Jjrqn#j1u{fok_YQYT$JflVY zb}-Q0=B8G4kI9m{m6U@ra0T#Z ztAsoAqKvzcB@FW24oO*U=M*T>+yT(|1DXGb2!jp@IrA>HAuR{1xH%&ivg5j1j0ZUI z2TvfUQ`_EcKkya*cSHiGYS?^o;|Z$(jn+H)G~|%xdQHR48)!U3=C&BPHU@tN!6+5^ z4Z_^ZV$jt=n5nY^csETYPWp+1jJL-*fR4s4`6Y0^4PdzHqE+C@{=ZfBr(BOi!{||7 zg&c};-LCB29z`+@VR;5|HO|^4kQlwLN{l^I%zPEYIk}8RI0WkQx|M{xd-Xt~81&s> zt*^s@w|XBS%|Fb0WD@%R8srIFe~;9WC#G|GcKH8eG-Pe=O-fhk>Y;EFG7BZ z%!u3tby!E%6+r5{P3)D?81^8e`NaO>w$3EuF%DNF|0y?~x#{9D8Ighik{6MYxD0zd zC!#r;c@|EE4LS@vqVvr`szzz&7r}_spq|j&XKJy?nU(JX^oB^Tv*Vp&z43G=z~DZX z2XZ7@aTZgdq-KHgcf`6hYB#AIaTWP1qP;p^G0wgs=8$tUDv=f~)D?|m7GTJ^-og>? z&4ZJGz1|$A;#jzw@`$bhYzmv9p^%#e!tut(L{0*bYO54oH${>LQkkn>S1-p1J0 zsGKoJ4!p>rM00EVR*+h_k79t>(k}ZIIryu$w723ek{nzF*PCnr>SD4~>4%utg(eA< zYblB+5o?plTnPMOU6cVAyTDLGS0>N%f%}@KoJQ3M@g-D2ukS~LAjL2dEHQRF@*ovdywGW|JBIGX4j1E; z%Amv05$zpYwzY6!L!|KYib^iPgHL!1(D*}9VWvl2pMwtj+Y}f)bE5lvEEug_!8aVk z)$6A@t44C!v?tNR1xb_MTydW*x7S4fl*ellIUNXY^5n*+$M#4OrHpbW@?DWmFP2LA zhMdUcR;4j^0I->0+=Xq5qSm>G<)7{zjGd_kxw12NO~jFcNY>QRBfxmV!WYR{@ffDh z^^AJC#;H~f`9FjxrcdlGj{Y;S2vjAZ{{L-|82-OsuG5;iOaOs)8?LgHV zOoXd&>BAU~i&AhA)JCs^yW#yPBXv1}Lb5zMft39dw5+FG8O!HjopzZcOzuRh zl72g)nryb=-WKs`AZ~Gz-6Dk-Z3iYIiT61CA5&9sW!!@pDQLCbAG^4!qKKb@Znr}k zy1a$OtzsK6x?wxn{Vk!hq_+q07eS1MqRE;ly98*{2&m<4cEBzM_FhFCQT8XEMpPeu zn0dyA8Ya)}X#V{YMX!q+_T)WcZ{v5(zM6NqnV{ccQrzj=r56TuZu2h&`aN>l?~ssk zouL=Z6fs@RxJ#8jK>NJZv2&&J;{A_Y;nLZj<_)p8$<#Y*>g>j3zY|0{sLzUj-W0s+ zZ^F1q%rjFLjf==)?VEM|q?=iNHjKDu125XtD_5+w^hdy|OGVt8ODt+P?!u9Cs1ncm zOFWiz^d+XS_7pUfCn0uEASG2>#gAYQo=H&c+;KPW^WcR)C=c7Fa24)5+h_F-?P19I z89E219pedP>He)21pCgAy0wC%?#tm-AyO{6s^j6JVRfwz>V;mn@xp4?$oyF`>LS(4 z1-zqL{ZW)P#4$~aF1!q3HS|z6R$g!;1>sbnMXqj~YFaF?mOc@l_x|8&>zuIVRco+s z7k{?z>~~lUJcvCTI|t~GOm%^V)TK9PoI@W%67Dyx@g*f0vd^V``Wi`Q?vDL9s0STV zy@`ScRRjyBh|jp(-})6KbNmBtGI^Kpd|$>UjgB2c>>=kxAdJ&N-(U8RaG}qT^D8f) z5w6<=AU&o<>niizCtjU6J#hHDxs;YPIq7{E2$LRWmo~4)t~Ht7MOFX|uru7d9(W<1 zdONp(Ef?Kyx99D@;}qEXR>wLzRGkMPe1B`Vyfm|7V7!)>R>7JpZ|X#){TfstIqZ?I z>i_+6U5@*TaNU?*_$6~UqI3dez7fpZa2>Pfbh?0%*~Jbd6#Y);G$347b}D-vIvvT1 z%vniwOG|PUz%&?u^;IJE92PSn&Skd*4g;l*QLtn~brczVt#O5Pc%{H@1Ow&qAXO4l zsjXA8mta?lJyK*EujQ_oqW#Af%v>7z?z*hK4%FtUWy0+Ww9c0J{EHM>J?M$)aTmiw z8iW47+XspOSNK6L&*P>*7R-&%3A|zLRC&>@*@ z2rvRyJEO2>&lP+-i_9MH8Mxt_Ruw%q`tA?GLpX+&Vdtzn!@`@W`!G41!uAc-&1V2a>~EHM=LEx^eFcf?9r1yxrQv%CnPJ&7W{c``|x8***}p0d06)>sF=QUIH! z1iaAXAyDL5wkoh6K?+RYgM{a@&d^g^Mp*mG`Arp+mlQ85dDkd;6;6w_>NfxC+x&8r zJLG%-v=fOt2l(G89!;{z9dd#|{ii~y%0LZ5ARczS24R-$P@oe1k5V9pzM*2SG6E5& z6IRI;!h~r)n)*%u!T?qYu!u^;fI_K5RT}&X~u7mh|=&nnMV+8No3z*+d7|G zBEczSiD97ra&8Zz5vTh8ZYLL_;wmVK+< zo64y41*f7W+Lo8|y@0?1`k-kb`5P^#QOJ-p71*5K95M=bPf7~ z5`&s$j|Go{D0c?uN5FXfNR9y6K-n`mL5*v}85CTGE)y)*xE6UKvg#2Ip@ycR6eR65C(CkcSE;jeb4dOCGTisV8_c%;M{V#Y(&1+F;LBGcD$4`PO z{!o?){i&NjcfjYr)5d>7Jmj`0yj_S*MxFIV3GTo0D0rWcqF^T{>3&_J-=WC7&1eYJ zR`ulQOOlF57t-00ggJXqlbPyOEx%IYPE7ePVofFyjRQm>d0oE{|D2;IsT}J+=t*r) zL67pUVD{{uHiYFx&AIb-JfMP0emC@DUPajyr6#tmc{BIk;uh2n+>kEKjm5m;Vk|+o zCz2amjX|@)JqRqmgtTb|zbqtaX93IB$YAv){4$K@{YAv!91QLyRS_C>1dY`6XkPoE zQCs%?(o`G5i*ux4Y>JJ0oy`oR$q6L|A4+b;+J&Qd_=AW8ypMoe0YIP;QhIR+AoHxI z*;x2I)wz_{CE_o>wOyVE`=1vVIGK}Rdhx}iwNSLVHfe6d72!*e`gb9?CmZGBiQ*5x zC`FU9wc&F7cVD(4=QEfWko}B8QNUZQc#Ew2T@@8PeG$Y!@LU~?DYCqewCr;FKvE96 z0q0E0yvI2gfbmqE0S^Jb#gVm#Rp}N7ms6`TM((T<^|GXHNkcSJwKR%ZBL3}ja-x#Rfij)20QdVK1Wpy}4J}$7THjLB82EDv$ja#ZB2(-44 zTjh+RbR$F#UWSuk%7GB1p(L8ILb}tbM+h`W>(ukQ*lxo@yA30{QbSBbBeL-H95hXh zXlGUqS{ftq@DC?3!f;b{b!|9I{K{)UxI7fOO5oGxHs=)$(dw%D`o;Jt2|myeX}n6{+o&hdn$cnK$n~|>>Mre! ztsYe-e(@pp^pXgsPEmEN-e-@Q^~ENPcD9Y=#0&SH$>WAjH<{VhUo>6L29lI|z97;D z5|)F)Ek@l@}MrkR$qv^TP8*VlM7jd*jwSD zY?fq4KiY%ft&{^paLH2lpP3Wp$o=R@@b9RmEg+}6g4`kIWXRbJjFu^sh$V5eJdU$s zGY!x^6qSQ=<{K*lfQBUo{|T<=@M-^bwntk#-)`#u9rU0&b6c6bnnVSy$Ie=}YSERg zM{f^x>66Ii^162H$*%5*H7CM}7Hmuhz1INQ^70)r$9UxQZ5p6=vH<0YjS9@JgDc_m zk6_>pStNqh@_j(?fQ-U$B9pmF!n!)tU|3SJ@)N%WpTe`y^jA^|a{nlIq;M0Gj8Ue8 zu%oM^hF|iN*qIv3ovzeU-G`A@RG+oQhQF@H*oGdB&&QgvKaEU0fuE;J+8=M%`@-uC zld9~&=MUaSlYe>n?~w<%cRMD%kBHM{{%P~}Cl`JLr4BkKRKUd1f0kNM*iRxH*p*a# z9IxlRonc_3r~V|)yPhX(sDuQvbnukj6d1?~%tcB+E|mnY0jI93#Eo`>B?dm2AucVnNx;=i>PExX?N+fnlwz?ZRSnR(; ze$X00RP_~7TKDz9p?Sb}nG&taviCTz17~{k-n8qK8n961A|epW$7BQ>J$gLenfA*+ zNE9Xxu!rGobn#AF5BZrFGrB?x{8m9&T)!0662p_bF+9otN|>TBs{_0x%KK0ZdWHBp zj-t2$JfNTg-do$IzPqRb;>%P3FGN*Qy{{zZQt%2>MUneyL`5|KJ`|D{EvoNrV4El= zepKi_9m;F|Ht27!sgT=s2rtl6@%Z^L74`!ic0|R}U%gaezB-j2FKz@!gw^Xji=)7XPb)GyV+Fw16YdaUM)QD_%!y6{3q1s7xy>= zuvK#wVjtf3J3)9NX|~YHQK0ZaA!u$yXDJo*FkX)49S6lCGe=gEa8Az9AXGn8YyzKw)mweeV zHQ806RIoZRlGhEV;M*`PP-Vl812M2rg(%htcfyd(5h*ry_v$$87RWf}^b5NuMD)aT z@17PXDTqOna}+|Z;$F@uKU*ek5OVJm5Q zuQNW!=Pr4SqldQ<&w+f#lf zGA_e&)W=A?Hvd;1QFe1msEL=nP^Cad;qCjcc>KI*GQB1l@poRhipWU3#(%1l zs~XkWV6W$Eh&YIg_`>2r_zT!v6Ar=fWGqpx?Cd8@_Yiu)26JTKt*+I~B;>n;e#?YQXf&NT@3=HZ{j0= zSIHK3$k_#=Clt}jII`ELlK{yY*)srcyN6rdLLFT1Ay)Ai8LX7z@h1Dn(P1$n8vn)- zV^n>pFAK?bJ0#?18U8~PvyLMd{A@aD#q1EZ#ZY5%vL-MJzdrH%@zHd@SkxkBze6Gp z5#F>2P0v~CO>}}z{PfR`veYek@5Xr<9FkY} z7uJ~Bd>wXie>^y6W0!Nz@INzYHqBl1XG^y^#*r2T;P|SUj)!?+8wp6NE`L#qQ-kauyshGRZ&RYX<6Tu6EE&mTX(zWja5p=qzE>)n z4Wf*nz!G)truhnBd9=tr*z*i<8BoH9VGEN~)icP~?||a;1$ID4+;hOu$8ar~HBKUb zVvzQM6FLi0$~j_1_cNfDG+~y56TMLBAD~i`0cDRrLvq?4)m(-efpDjbUd>cB4YBGW z6)FYZqD?F68>+9wMs)rultM%oUxn|6qB0=ii2^QF(p$wEH`)F!DS~Fz-L+P#q7T#M z*g>anMwN$ycRJ6nf{D^0@MSaJ#OaXo&leRoJpXkHYrg|?6;MC+BC8aXol`xurp|4+ z#ZcA|=0sKow~3+1#KgKb%YhRvtB~{EEG*)i;bu3-qVfPUV>^>_g7qW>k2lNEqMXp? z-eh;QXA3_ID?)__TO5!LJu9G_8VYY*^XQ-qqX@-4xTUCXM6s z7pm*gqOGx(=52a~i=$FTaVgFT1xfG>>X66K;kG8?=b>S?FERdw<8F@aJ zxSn2=gBe+$0VHdYm0(b#5-}|9^=Os8A4o>V5e;n}XBmviFMPOUwOM|_T1of;jguQt z6>2U}&tDA(ZO#iW0D^i_8MG9#5CK$(oiTcpfBh&H>zd6GD>~5TwU|DNHg}mh2IBSt zCw;cGWn!M97wcV}bZ8Gb;^Zx5=UM{Ob}a!jSX%+( z{3{Ap&A}aTSjY#QZ^-`{7r`uN-CedEr&*K=V{#d;P&GugYogW5s#aiokGI#Nrm=z6 zW7?{TNS4&Cs0%NXo=*sVp+_p|q%C<*?Ao{KnEl1AxE^J!s0qiet!~DlT~&k>`?Zf@ z?cenUw2(J}!TCpha!6Y3$3cTnsKYtIXg-B-l!?nqtuCwo$Mxd(JLO;ra$L)_4e_Pp zXS&jbP28rKVCO?(9{|%eJzM8B2#OC zx=^F2lU)A@YnB+>9nz-h>fk|^G`AWzPVLrvNE+3W20{H#NNs&HZbL;$O+~qRs#j&hoanLV9_Ond-tSzFXdqjR zST*jJ2PV979VY&9wGEDpxjAA}7Hmcu?1kF_%+^03fbP30(c+!4r(b8a=4j2@guzrF zTCdQ0nH?fiB3l-95}g>d_K9sv@}vO|F$IaTQ-+Fa&dwW0w;n6j4GC9v<9Bd@lDw}a z$LlboVcbry6wel(g~Zb)v4n?b3r{@Tj>S|#4;4*CO(f$kxeZONSJ1McY+8g%DmmCn zBn@@72^TFYL6{X{mWS87dL_H43b`}T7*X?5Z% z*pvFKWV;>a&+(R$t2z^4F)zrWiDMT=v&G$)6WY=lx5CtZ76>SPnI4ivd10qTqzaeW zkS;K{Gwx$#REBtBgBe&DXKA}eP+0C#_sUrp+1-|%!svSU!2KjmOpM*}XT6d1rccUV zJP~lXUdNoB`0-xYbk`-R*DGgTmd%jWljfuxaaO099*?M;&h=D{%;`x_(@3u;JslVd z3IE^KgL|#CcVWk0>Qfe%J207%gZ7qN-vj)F-Dvt;_Tf6sVj0{jkoY^<#7Jj*YkORF zKHmLa)gX)z9%4C?(ES^!S&Fk|gKmcG^ZXhN!yFmox z)%zQOajUaen{Nc#Gjz|Q_iDVI@0$z@Q^Rl8M6s>t3HEp`_AQz)uTt&P)n0*)GW!oo zSEltEKf7e~Y8wGHO93q-n50oTM!?b}oiPHLyU7~KIVH%<3}~5^gsSVvf*X+Yj$YUC z%GkDWyaSyJS4&?;CR__(Y7YUy?zYz}`Y15q@5bFFJED|oz@gB(|3lT`Dp7Wb)Zr_{ zsY Vtc=r9An`&I)yN0(%G!Pwtzq}wJs=?!ji#j+pGn?@+wls7g$wC8-p_ z4?r`ACvR`C*Zi+iNBWQ~@)^eUva#0OH5TeabquT3GDrl|uu_3EXsJNi0P4vM8A5f_ zx^!b~2bGwh<~WB`b`W)T5eydY#oH_z1ab+HYKSk{uYj!c0I zJ1wp>%q7R@E`rCv*c;MoOE5oBM8Q#4Qg=*}cc_^bs*cO#R!XzS7r~|t@QRJL@eAC78D`%4INla^AHlV)RjF)Woz>ZVm4K!FBBJ4Jt@NP&2G4 zN?w-27vfYpj~6Q32ykBqj#O2xt?U7)fFlTCS#$J_YqL~mQinZ`u!j-J*eA-M6d*ES z34xM;1j^o~P?$ZkpwuS2s7>e0oKOnrg`FXl zL^4)8yW?Zp5qEjPv?9obZsS;6`>tgnZ`RcjIs2ZK3R`FaYninC*@a5on=Ce^K=@mZ9Owe_`C;aa(T=jlfEd;e0^#5r?!GTq1; zg>xD5`%+SCrVk_M$CMRNBQ$$kkzSi>^k!TcG>I8L=|B-Wb7o&6P}VTv$U$2Ep6p1%(=G zL%3^BhEi>+mgO?lG)M#7X(zcvp(-3!mSHrP+_3PKbt+|=Om0x9zUnHRaYpJmnoQHl z4URPO1gHw7AIS|}-h|I7NfLeH7z&FTsd0R?p}HzkwYa|4taIoC$TpQupQQECRaK4p zH1Y%_OY0jJYnoHWNuu-~sasy#sLv?d9KsQ3UIm^yK5A{GDP$y!hHwqwX>UgpUaAi; z=OmKDXGKFDoLk^zoT@^Rrp8)<_9aiHVacVc9{W1Vuk>x$_3|#azn#Sjg352uH85%igF2-SK(}XV^p($@5#c+os}>*azj%j)WphB zUt1l)r)l3ej)L1-OiA#lbRGk>5ymAreH?)-XR}?#rQ1=_x74@ZX!&3xx{;Hwv7+l3Whl zihodxJW*b|4>x)puF&wloy$(!UWajA#>}j7!Sq~DfGGx5Q49&ZP8xkP2^k=lE@X7} zr-)tOgWKobwH9jdD46uftrv0wsibCQ$i+w9rj*7?PF%I&Q8wA}qTLXQmP*bTVRf7W z&Qzy9O=z3xu0h6~&a?U1)ypyFM8uCE*dFJ3{I5=wDf?Y1Fr#SXync=wOebS zlaE(BzMx6!B}bFp))xdREalg`A|TzFC>-9((AeFHBgXu8k?NE#U)^4Vb#v@2D9x185PH`hiP?M>v9|dP!MOhe@^>-(BSP_97F)k2J$NofW-cv_TB}+?&_}h{qLFZXhj+)G(kW}fdVZL38X+P zM+wOQp-D(HfzY0KcPBHGWMp2Q2MM(gT1tV6EqGe`kXBmJQbnZ?D^*nVqE=7agNj_*8uroSGnYZ+NMCcagwBHezdMi(Y0gZtQOP`4=| zI^G408uF6BuC#|2==GY$O^siFQ+-(6()e4(!<~)SlY1I2jD&Tuec-DPzE!<6w=fYt z1btcEbR&&Res^)m#1%Czk?CV{TBqf<7R%*QSYkLv^LZZ}WWnCt zT>I)vH*O!Q8c0x~3LS_ph*a>P2c~+(n)5#Vfe41N6A_X_^SoDF;I-~Y1=4|N%T?Q($R~6*KeyZ4u|9POYV?^K zb+L(SWDC!!z4-zihquk7yyeGHE}o(359L_yajEp$QIfxy8{XWA z9=6uzHN;J^t_Ph}Fx#vRbr?yvHEY#X?@WVvndHL%Vbw_Cv>KjEa6Qf&&< z#tmFzwDGdnT3!X-xNwB{+N$yzRtFvnR-H5q3-TS?HX;-#qPD1n1fjaM73DQePZQ!? zgbr)bYsBnbuGte})t5zShz3+SMoKwTL=J94dB%!)`CufFqNa^t$V9D3;&O8;-t$<0 z1VR$MaJ}d0zQSu#jlOz1dzL*V&LXToq^=#Wy!rlSVTv ztIiowj`%uJRrO{5X{YL};4-F?b6(#Bj<#7B{d6o%Z~=Y90z6ou+v$2|TH9nD0q?z``TD^SZt!gJ4?A?jAi+hA! zqhxEJ8v5f9jwE)=Gf;a!0ls9=K>cDO4NTcrLr28YZ)v;}l6HCJ!(bUwB99IxaYIQq zC-q<$FmxxUuUKOa(y2Wiq3Xd?cXocHIB#=j<1-Y?^@4SaRQoBaE$_kzTYGv$drYTO zR81)7vg?9QQE4cNL-x+r87i%hHBm;#rA+-@TKXObsUl{kCpeKG`1l*?ByA`G8BTiL z3U-E)1D3H7ori9vfif|L7`H4jsMp<=8y*i^i66LvT!Kp@1~F=I4;j>^d4X`NHT}13 zc%NHJ&daWQYL6bIY?{!bZ@cWMWU*1C4YzactM|RI7Ncb6<#de~)MDqE&z7p;ss?hl z&a?dd&dDP+Z{J2!>UC?c@0xmjSt_u-tFDa~)yfRM_!e_?Ev9Tmy?tthSEuo9wItR& zFQwk@T@#~@t~YpQdZd^3uKA&Qdtr5+q?=Xe&dacz7J56;`7CCZmLKnZA9JY(wDxj1 z={n;c`oF7&51pyB%f7P~vv17k8^_kLcGZQb4}PT zSbQw-6}6Zna`&sUT%%(2d9(>@RXxvQY_$F8?e+G)&v-xYfx2hJs&~WC=cW69z6WtT z2mkO+v%-!RP8t6L)ZXEV-397vpyY3DO2iNUa4gyiYSDXLN^v+)7fv@TDAByb5UAC% zRLQYC8`MO*d_)9C)_Wa24SE4Z9JZp?hsEdXulKGlDN%yM^MFsCxW;1o>fM`1NIqwf zQh2vBwr^^vbw&Nl25r|3I<@-c;ZnbUv}L%|M_$pWmeE%Znfeg3G~TX8y(5rfrtvmc zU$hLfVnDIJ-_+1o@|IacTE40#JNpdBWr7H!X|5H|k%ejWEqBz+5$fnF$?}^ukoAp!o zT-AQh)v|DQTVp-=qB%l4X4;rpaRT=wk%{wnP#<`mhtobJFPWyB8G`QiZra9W26g61 z+)jFAD36=^6$`3=EBACixZVG$#y#Cnn~WL59VO`|z5sF`bns)QEi~5lD7ch&gXG%t zs5iZxil`S2RXhWC2Y7K%(gS<_J`gB{**K~X{gxleEpxvh=X$xFm3soM@{uf#3)df(ga6MO;OmBP7q1iiSD>_a_nZmcHS$MA~;xx!-mR%aU# zjJIQg=YpQAhfKZjTG7<7>#p8@mHQ7=baRKOb(^+v!>OSog*#_f39xGIA$ zcn?|Vv4z@+nqWjX9yVf%5H}KKYNN z&)IgkIeWjYV@DlRTcLdOJgUA$&QC9JeS z;<-fW5-kk}4!QW*a(-2To1W#cotB-)87Z;J2beKLrzr#6Y0ebrwn@_#SD!Vl(Q(Ge z#+u5~ZR?s(Hj09FsXCvIJMdbkal%`N-?&aBZ9&CASx#3{y^SpWb%!#2%#rM>rP(0V zTu@7E6SD6eKH*UzjE^HPEzjzM9QMI_)^2zWg>6ko_(>RY!jaCuZ!BzHp18jIkl$?1 z&r)+c)WN!K26O%)xS%?9+RS8}TpH_JH`nLZ$3N6zZaoOHb#AHC3fFE;B%x-<_Hgy{ zF7LTlk}ec*fRVg)a~jB7H+LGx85`WjHepVxD2T;B$6dXBj18*l7_G0xOVs>3@EF#|7@OFADY}?L;c^~txr*PU<(4Ax8#A1LK#cVPrO~QI zBp`>9L&pp25!=S*xHm$#G-&{T@kjL;#;wv@Jv-aas*?`7uA?&?-SdslR?DWv;IFQ> z_bTdxPfeR!*fq1!Z8o$nnEvg95NXWW2DRzacb7T%l(ZbtORm3RWTM!fw|tl(9Rws5 z{M!n%?K881>WS(m6nskjRSnD;>tDNm)8Lxs8tn!eWF6w-Z9E;KE(k-jL)03-o~Olr z&_;M(({`6mC7;(^Ycm2#UIWCcya_h;(i^sNds${n#FeWDW3Q?+G-p|q)f-2Bq&;nQ zJiVT$gKmg>;1h4{A3j_&kZ#lvIV8)Ij}qO~!At_E=#g(LW zqNgC5yKa1D{oXNzg?ijq4^Y6@sGchDH1Q+z`{<&O@NZUo#8IEsUCRjH%`pmYy3k!8 zeb;Qfloc`Eb>YEv+qV1n@CzpFTWb;xh5d}&tM}Yf7xsmF2ppa?|GZIi>-jm>5>!`$Tf@j0zs ziTcw4J&B=ae5%OiGa))RIT7Dk<&r+AM{hQ{-G1yw89t8QTAf?pS@&4vI$3GM@ znVa4{e@%Hg!!L&j7;MgHc{NTZNbAwem=p@*4Bua>% z^4l0>SOp;w!-b7LN2qfP>BHsdcQ)cuv!(GVyPH)nXZQ!cW_*{abOxC?SI?MM^UXi5 z+b>1>C~d9u!i}v=WBdKeq1zf$#M!FQs9xs>x9E#GeB_$(hR53)6K`v5 z&8e>CUVmRao4amkPS5WZi&O1;Nx3|NsQv7_e)f?upi?(aa6|p7kxL z;ppU~ULN=fjlGd|qg=XfgClYcvmEtxj6RpL!UtkF^U+5KESHMy9ZEMK#i=^CFtd$< z(}Bf-Ks`Hmy=NL%5w6&u*Bv(Lz>(Gxm1fU87UwC@vl)J3d^gVuw_btHxyMa0LMss8 ze#Zo>!(bS$H~W1Pb)KjR%1>Wyozo~HwUo6!7_QqOhY75mq)_rV(o5v>e zC)IcUJ9>n%5H(w|0fx##Y^?oE&6`hEKj!14XPw*3C6Wtc3(==ibyw6Zu9huPvHJ99 zI2+|PJ%J=@7WmD>`!l<`I^g&6uz6m4jf|x#_EAu758w`a;CI>U zNAI=Y=mtJsp!6jJpSol7`kLRW%2JDlXU1dy$7PJiLF|z)`$#HLbo(aOPmacYP7QV> zvX7iU84;hM+@g>>8+W`|h@lp(sK(0t^o~vFl7l6EHF@p->4f#MaRx|{?w*~VEQzlG z{~m}F-pQ{G{H*@VB>Qr3l8;%Rf;JA2-O{M%#mh zCK>>=g5G0j&EudNb58dc+R-;c@+Cu`R8s3yl)8nR0h9~ohh+)jbkC6J&c>t6kR6-X z%}!o;Y1CjU`g8Z!yPw3>&#L<)7Z_~KM}oiP7ky-0I6wVL|5(}?ezl|yFO#Tc=fDRt zc&G5t+?pALM~QjVQrHMqUFa`WamQw?iB`E7j+MQo@vGGG&c?$>Sg-o6fA;ao%;qv1 zRx3YqDK0eY^7^25X;`vkD8{R=MhS`bbqbfSDUMo41+7}9sCX-r6{ltiIXc3ND>|uS zSnYPgUN=`Dwa-OT?T~h!*2uNfDdw7?8>U7wdNt@}4{EKdG1+H;)@lVeR>QwM;AeN& z&}V^uF}dv8HB-Z%8?%9n)pVWDJM4Km3h`lLG{>yXF`lTlGn1vS|<)mtT z&hu3{Lfk=)xHw0^whh%1eQzyJuRrE=m*hCFX=aK^-7F#n(ErU#PZ7u@e0Ia!h@RYC1c=pPG+?kQ|PbU7qGX8p9 z)@xVljTPhV^(_Q>$n7FVH!$c6X1(`l#!ie+j+fU0GFN>t&gg-ng-JgpZlii{j^cRN z^;>a5L%BUU9Fz?|P>IcyZYpluoypM}ORObBW8s1V zNnAENMsE%?@$i?Qa6FB@VpCY+5F71RwGW_&oaFp5xeGvjp;vjMt@J z>%wIN*4%gIIQd}SQ1;WUIc{of-(|Sj7k&J$9EsnVdLw_!+snAAmdJPK$X#|-=-}kc z{0%wr9XWn{PM@wEKR=Qq8Kw4NCucS%<->b(G-m|&OmHf? zyq!p`G8Wm~Rji%zu0Mlh7&~@Pr&a0JK%ewB?*j2|KAG=Ftu?$OkUOj6_p*3D$QkIz zNS8Ri(3y9I6nx!0g+2gM%nQhZ*|{6+17zU?!LxlTB+r*k|4qQKln+m0=5)HR2JAdb zQqZ~lT}-OAV3YPBCw)5L{WhJN`rsczl*G+_>Eanr_BG;uO&z0GP{-a7rI)YuRn>n7 ztr=>{i#&n9b~pk*qdP^h-HvTS81W3BA^Hc)Xh$CfdwM%)ThuK_$DaURLRHFbGoGX2 zLaw#MsXtTP#;L2Lo83jkeVF{%kE9RV#y*yLhX;ua3`o>{StQJ~m}O*FXOm-495N!7 z%Ob8sB?rk1lx_pB=00{Ii%ybO{sVzK8{+qNY~DPhdZbaPv=4rcHqTwZ##>9@Sa#u@ zv@M87g8FTz3Z8~hpBOexD$C&sqdh95pGTWlppOn{4X377DyLo!`cFLP^Br>xn7?++ zTE`p<<{2VWf}#$4VSNoSIi_*i8*bu}{agcK9%HexzR3 z5$)X(-O7V9PWggy&QHl zi}JD1GNxqDV@t!?NuN)ux_0e7iB(AxK4IebG*`^bZfLHnqY!@vv)|@G#KJs|UphTK zpA%>)P=Z$vH8XN$K&~&58W^HFitfOj97n|OW9iCc@?*6m7KU;hL;E&s`U6#KpXtHsVAWfv*GX6|`=kjaPSMdWRtWA{u+`>KpawXtD= zqU9xHANS%AC#$U{MA@Y84RL1p3znurpt*$T7d-3+Uzzi00ecBsIzhiTKI)k!qm>k|SJ`y~$tg&(FxIS{n zT2fRxH!jZWq?7lFzIuIT$B~!C5)A{TJkXNyZN$Ip*XlkA!YPgVT6S~m%kU1ej^J1w zBN0AJJdd2vac$`^3F9IYnDJtIY6g9=r4qs|`}KOoy=29_n3(B_QNNsn@2T%wbZ+EplK+x z%0sfar8N%5xg^)ehQW76fhF#AcG*j@qG+2lMT8g=Ylgng>2yNPfRv@uY9iLlkO(mnL@N_&)l=daQvWb2ui zd&exF=^OW8B(IwPqCeFA&PFWr52{a^xF;8tq3s)*mygX4SQ z$Js#&_XgKF)iax(_2fx_MjUN2^Dw;oo#mx=jzfwO_Yn@UlIc0epOnU6gRC zp*RawIJfPFuD3N*!Dsk)0h#PqyJ)^=P<;^Uw>1>07JM}j(9Z849h=v6C3Xe>+?1T*Il}?Ipoa2&dIrnotuX? zH3uzo1J~PCTRVqlM|Lv3HQ(?jYt9FFVVr$0z;N&h9-o>{u+(BH!1F$D%%Kr;)>H;k zgJ8W6$+QaZ!!?ViIY?RkHQzS%gw}!D50ixd(JGxYGBZ+RUMrlXr1$Um zZLNf{625NT)Q~?OQVp7A`3^evK)?Muo6&JSMK(4y!Y5W5$I{>#j`r&_>%f@$Onq|x zc?m_dlBRI#)`G_CXM3dQBOOthBG77x`XKWfD7-+1MYMut6(UKjWeoeL4GFcUq=f#3 zg`!&0uBokwq8Jeorv&~?>5;pprzfDk=|9`ytA*56LWEw^Y``0JJ8rl>HB{ju&PsH| zUFJHuTUSITM(3x_fg`os3ya}niblo`IewXvygQZ5!$7eFMei%)m7l6}!CHtFi2ZlL zTKfP2g=n6Fo}5Y)DXd$HB*exdLk<>MJxJ>hJl)5qS@oX7iGG(Uu_%>oGAd^^Ltphf(m$Lgswl^8@MczSErdugnNXaZ z2xkhy)!;6HaCVecBxHChIL$XS?jvM6Jr(e01iW4D6>w*PyNvX^C+BzL+AtT;eClf3 zqtt2IN1WH{y8||z+=ZOwVK*Iem-)!e437sh8I;vefycEe>d)18uJp#NG^*jKgjlH< z;-+1@rSSlW_M2k?##v(VqgP3l zDDP;DOwZgvlLOAi+%1h)eo?Ja&)w2^6=gUdD`J^ z2h%Z#3JR%fx^08!|8Cnhc!f~KW~Oa$i*WUAa671$ZSZwM6)Ck1zFxSv4ZfeD9u_`z z6hnd78|{-HvCntHfb^*L-_=g!T{V->@pd<^SRnjTXFcbf%3U@s^tc`ThwnSn+MzYk z>HkgCt$EqTO@n#=c$lx|2OAxyFLmr==w6e7Q-ith544MD>$8oAF=ld?^%LEvH%Tqx zJK7`cre(%caGjQHdn>su@R;=chz{zf#99?U7^C*p4nahJ@414!>PVqoD7^bktmt_`lnjP*nf% zwW!c@DY`FTl%kYYoV}%KX;;?LS|nG;vhumw)r!ui+?ByDdv&zQbF7MNte{OAqP2$H z)DUI!YUiYUZwdrHaBHQ~7{^+T@?Aq#U8J)(F0H&1Y-kG#u>`2ByhrINus_!H2J#2`)bY2Y_95Z?~p;VpL>~3j%f2T}Ck$2e( zwl3s^rLTH_gY$S=w5wksWh3>YLQcslsjgc95fk0j&@9Q(ITP*H-uIe)%l`--8ubO@ zh9foUB*)No6UO_h7!`1f<$v%BzMkB21r^FQpn@ww>|1ONKi#Q@`ueGcDrcWpQ6=2f zxP&%@f_?kw*oQ|#Rqw;pwc=&=*r&BF_#C<7&Y+E`OYXS?lzY7c4wU<4EoG>0Ktqo@4PqW7$6s zjYg4}n|RwFvvgZ)t_2FQF0kp?9Qx!rT5H_$`y2gXR{R{K`s&KIg{nD@pG2nopQ5zS zpcMZ?$k=QO^ex{`Uzk2AWXG%B%|c8(?oX&*EJ|?;Ikz3e&7YgYw<#Q)JXG4>Dtul_ zJiHIdFI8nfksLZ2m0aLQ`?PEXo42=YVtQ&1Nzwu=0t6QggyO9xVNGlj{vM$6l!4#^IW0aSMH zG(L$ulRm!XyC?8G2g{YmL-QB??Bite?+TH3X*s4A8!g-bQFQkIQ z4PW{tU_=#K)4?q8_fSBQJ-{d@l%?7pDA8 zjk_UL4pA$np3HC0=PZvVO_H0Zah2+1VztW2KYz;}+*kbpzn3+>zU(NhASRZP5YExzF-FGp`MfDwMvxsrC3unbr}1SUExsH;9$hw zyC#F*;5-w_ssW3I5|AOw;N?oB^LLDEROz_-HP{q_52qza?;`ap6Vm& zMdaROR`onCMZAc-ckY|)J#P^&ChtA_d+To|^Uqa;?%HTxnNdhTdjwr!4$-)8EeZJIpG*1-%sa<6X6S!_ew$&O7H^^>$}_{CYD# z6TNd|*p0pk*{^p?e_6u8+Nv7F_>MeDb;bcn+}VT!UB^7E1*T%Ut)Oci$f`}L7?Fc-Dd=xQIEdAL24U@wVp}8}E z12oTs4p&hK?7ZTJV%ZToB0M`i;eok*mNh~eg5cP!Dg~^Q^A5q{08n|Jz#7{F2>%#C zux#%<%eQNU2nx2k&)M_gAh=Ay5Tfyf7L+RtLF7OS`t#i(etb?jk?-UzMGh$yH!*l)>QW&eroQg>Zf0nGoq!z6V znRsd8mY==VD!xBA{v@(vE$l=)Cd{3Rz;~j{*L5{Ic@%?OWU*;@*OOIdr`}qqB2}utK5;zg$ z%(#a6Nj?;SR<}+UqVjYh5GDq6?6sa_d~9=X^%96#g&jTY&lC~>EroZ0!hj1Iot%n3 zCCn?{@7Vw;(~P?pQ`ZtZqsE;Ysklzpsp4d$ZETC+UTRg|J+8@0GDi}-|AtE!=I3n@ zK;OYONBrp#AfD?J)uU%<{D(6D9;^i>xn$ShsP{-bI~|yu45_7Ha@4M2E{(Jt(S6M? z%&H5>-VQ=KXq9jBnk_+EtP6fTBCQJ0s}p?m69}sZ(40*OD$cqzg-10BUuML9(!hgZ zoh2f=d4nenmeTQIEkiHY7q_(-h>}d1>3D>2!ojJ@QLF*-&$49PW~a;Z2%PD4w5|mn z+rQb?87Jc^AP2Zc#c9(-gH|Vp92@7OG8n_;70_WoQ8`_(yl!L!U90Ek)sybk!(krwkd2u5DEdY@eVNKgl8RB=E?(9|Xr+8zpO6_2r%SJ&}* z9Cof*JP|ywTQlw~&Ac>Cdev~x2lFG>lXy_-$KWZM5xfTBYeA?2rcWUJl61wClX*=s+>P(g zZ6UMEC#H7|PiVlCLYa@CxU%iQQ8dF=bYfxYz+p=Zywn<>q%0(7!!(VpMrX#KxtCSv zg+*ngIv*|9vft()V4u8r`&TpuR;*mvw+t263GB~()gQ9^E-Ag1lwPLJG|p{jT`d*G zh$W>L`#@*yqeLm|y3Wzwt4m6+18Z)VlwK{G>?NgFC;k4C(yMOoSW}&_rXvJRZ zs`SbytcpD_sZ>zeENQ!>$y?HPEor-!v|UTuE-Aj2v|UTut|e_(O{2A>?Lt-c9BaGK zv`IngM#fU>^?GT&PH6OhvFd9y`upp@UQiD%Q&70RKJ;S?rXTxjEMMQCe(XS+lO>0& zn)g;uT9kq>FHGDA>fzq0J88bUs&tkdvX&gO+ME!V9I{$;SxXLC?VhSj4p}%)`t$5jj&grR0<4S5fV) z43;rm6v0^vFu4R^EO%$A1iXk?bCwd2ouvrbBp@rCvD{e-TyT~Giq2Ai!C5LtQ=&Ud zsp#x11;1KzmcqUpVqH*8;pm)^5XpxlVN#1=g)I|rb8?o#F{|b*h2IrnC(6!Jcv_Xt zQWh-KM;3NxDFdUkRHP$21Kn8)u;wf!f34swC08xk+VQ@!zw}FVmcpgVoTWfzXDOVk zg0mFvRT>JU#har(i-^jzh(MSx(7Ce|Ic8@moFjs7ixtELhSw`o(7{VZkSzT~hm7ie zTJlE>&QeU0(pd`cEOVB6Nm99(vs5uh$>4hwf;diN)1Dcr>Z4UiHC?;}F3&QhR~ zvlMW4mO8`hXwFi=g|if7?kol1{!hfGi3S{;r33}%MP(43rGUeLB7MzSig3+Yif~xq zfs(Tnu-0vw43wRvSV>!)rNETVQUs&36ky>jMJPE-DW-ImBAlJ22$-|f%QW-SI>J0` zcb2j-YR*zeGMNfzDe{QUQh>=>3OJ4+z~C%pNOzV33||zebe19*ouvdOXDI^7Sqix3 zEJZjvO95tQDFWF`hkBRJQfx==EX4}r1KgaY4vQgomP$c)mP$cWAEl5vOQn!IOQoPY zOA!ptQh3DGoTc!)%g$0~(QT!(6yfMB1(=+rfNRcDgtN000e6-H2+mSK$yrLUyp@8p z6v4W)R6rzWDZ;7(&Qd_xS&Bfr`iQpFoTWlurLz<$%Z=d<=+093kA>x<#YGB7GE*8w zAGZ;?UscQgZ@Eg{E~U{)js9hQ{f+)NNh>7P(2Bl()Iv`2fGEyu^q;86-TL(s{hHLT z>-Fmd{o18phxNUn(f?*a(mt)~e@kDVkd4Oi4jvA;qF=wWdTM|I`x+-6*LPSdapvum z_(_OVmE)ZB&3(%}(ZFd=d9P~!c~Sm;qcO02V1;S2yvk1q{g*vY=? z(+ulHD&Ab6D32H#=3rhq1-IRc6HiY%s`_!LgZWQ+g%??e9@U1WHN2EJqA@&mmM_k| z9kVI>_TRL;=DkyQ7$|%3U{YJLHBN!qmADw-yrq$CA@4fZf1XwJVW z=-XQg+V+-0w!Nj0JI$w{?{+EZyB$I6(j+PUr5#$cTA| zWZ$mskc8t72^i9uv{~CBiOV}A0pB43!VamlcCjkoSa7jJ60Gl#0g-k{!eNI5$~z>1 z_8pS8)ON^_SGhxiiaR7=VtVxD3ukXJ6dVyc|2!&ulwf1OTlPxA&&5v)g{55pK^dD(dhV*SkMQncu zMQD>-0ipisH-GuR|GPqlzP~tLF;i&@vAdp#=L>&Z_tIS}7)}TfmkAEJZ~D7BFqok{zQ2 z3%&q@Fl$H`W&wjR3sefT1fwu3FbT6F%LhvdrBxod#(NNsqAg(N0|?ak5c>>?gQ0`t zXJEr|2Eh4&*!4}5c2mf?gB&#OAO)Q}NI~NcQpmW26msq$1)V!cLFWz#mdrp524;Xg zuiWSO4w;W)qpNWS>~EPfU}vjw281JL0GK!f;2LK@ICBOBoHGCjoB>ec3{e_|McO_y$iz7t!3e%jfB?ZZ4I z2vdM!yIQuH9ee-H?AYA()7OwpE6kgOxtHyBC6cA_1|~lqCSlb01rt%V{jVT8h`Lem zJgg8wPdUEth?wcA?^TiUgK^T~aOtW2>KQ1w&!hEH~%nS23CFUK% zJY8bmE6hKZn0tl!VOnHTbCPwl)Js$QPzy-lf4NEVzmE#PuX-09*MY}4*v=M&mP01U zF8Mbs`8OO&|Ay{kx705jGM=-}nsv=qXK#Av`<71XES=Qpe)z9<>BQIYMGo1X4+8aG zk7p$%b)VqkW&aI$dps)%Dd|*<+aBdD7QYAp!KM^M3ONTb`z63~`6Vu3vtNQZT)!U% zbn;LD3peLF#pt0xe6qtK`?!Zf0;CBv4+R0)LxGTuNV0IM{+eHhs**hx5SnTp3$W!ikA)PD7Se=BhSY>fEkrhn9t)I~JQk1uY90%4 z{qC^yO331k$C`$gS(K^Nzwi;)Nfp9ARJd4U|W^8%g~!PaaA zMdt<3!FfTDoEL}=8R2{pipKZCMM0PLsD0_YK&&}0;F(bhj8MhqlBfGAU?EP@G|3!E zY~j3sRn>hKaK~_f_~5*NUq*S;3&HHXK%nNlK-irZ6k*N_0Ks_yC^#QAV;`#G=})s;ldhNvWZDQV2D z$+SXo$~WByA_ZLyl!B%PN+DANrI71=Qqc821WUb742Jo38ZFYxi{=Isv^8%3Iiu9_ zyo~NIJtPQ6Ee~KwXE!p{v^>OREe`?L@&E)akJ3i_kKjVfL$I#p35cZSAsn~ESnpZ&DTvO($_c%F9=f0Y2fsgt5$ge^M-gM zs9>})bSKqKQn(ke?elQFE;ZKa4Rp%C(3IK<;uaqJXSLBxG}*Ccyr>7EvOaYY~B>77;LL5py(^ zPFlo5YZ#9tHN&OQ(sj8`(GNVNa8yksMABd+Op@qf(K~4k(R|gkhJH!3f55CYMDj1S zh88Teh8A|Mp@C6r80pB)K-U@q*0hFpsL5}Bw)>%>j+aC0tADsS1g7RUv?@3IT$u z5Gbe$fwHQQK-A>`hPJd^3Qa^(6)JI36%q)l3!o0FLPULt(>W)CK`#V!097IKexPy5 zKb~_U)T$~3l~jemSyhN?AWaqELRAQvs|o>JRY-iAXuv^LC@823l|fV$0tZzg>1(P& z!ZlSP;jq91B~>ACP!$4YRUs>Bi>eSzsVXEGRfT{_=V5-jNmZzrQZq<6YX%9JX7EhS zywo|&!*y*L0JHv&zybAtY)7vDV})4q4|~iQ&7CRBNQn`%NkHo|Ja#U2;8okyK%(50lExRB@|tS3+lJK2%11 z>TOW}Nnc~-@u#>N&am5sy%#l{yqCVuReTn4k0O4iL&Q^x_-l(;))+YPdsm+5u3MIB z{#``=+~JM>Z__cOx1e8Kbv&xS!y3meU%B#R`EU0%PVPsYX2+-+jg?kQ&__3pH#cw8 zg>ti6b@ISU*9Z3Zy->bn{f*@VAcL3LGDjZXSh+&~c_v5nozhsoLcZRX1+?aZrXp9E zic&RcG?pK?eBerhPCRDdvXhh;)!_1DSFAYh#8~9vl(yo^K(&0}(pLusBG{FutvvG- z&-(b2t4J3{BrOvh>obP{dFF`-Gzaf^ozWv4!%r8ZK!dw_FdhY@q) z@yCJEDX~DQtX9$s#~qux=78bqo6greuEzM)n7=V1EcD_6N9bDMh|41j-s~1Z@w!lYj3ovk7hIDQVFmPKy zCAUQ|a$5ouwGdAYjxkg>Xmw_#yJW z5>ET=#sfZB~fGU2uJ45wH10P z#2&n=F?Yme=8k|fcL0I8Q`%^v6rDa%!#f*`iqEBpa_b*c;?=}E;KBu;; z?v)pp(3@H8U*F8-OX4DC`yw6(^0lYVsdxRB&lgYje*+>#hraG`YlY3qx!IAO@Td0l zW+N~b{Ec_;mE1Y#$aMnR_;ITQOumPLJw}{tX^%k$?7c zmynQNQcV^rnfuJoU9{mQK2-}%m|ez&^xzh%1i&}Bg-SrSP!V#o6hJUb0Y$SEU@%MN z=-7Iy4=D4HJ>vMKIUV z!5m)N9LwQ5?JqTrLK)0uv>L&rnbnBc%!b1-2CES!yI{J(=2o{F5hZcOU*45z&#kP` zxz&gqvxB+JZT^&~6%-xJK?kdmAX$xw4jEyO3q@ms3u22sB}(l}2XkW0YJ_2~6ojCP z%_UEE6*9-LmZU_IIg;4IYJ{!LjYU}693Vb8m}7J+twsd1)rdgNYDCzrMv5>8bAVtq z0t!|mplmfF5T#JS&=#{A(U#y~PDHXADRHtI5eV`XkXwzYxU1;-oWTVa+e8qUiS#L& z3^mNj?2nFCpS+WiNlJbuEL{jx7@5c=`J-bv3^Zm>O#bMET8&Jgk}w0DjZBy()BFW4 zj7*TZkqN+!OvIW&n2|8s(+x+4a&|9PqIplfRyj?+sG#~D#? zz*4NmkZ{y+0ETpiu4y=k%Nh;>uHgU(8V;q68VT!x0ck!$CM`IDoQ-gFw56 zgSON(93ih#!vV^Q4~^?+wZe=`LEOzZIX*Q!&(p0lTqS^&xLWo}b40%jRl^zPm@UhM z=`p^Ct+%gw_mLen4&B-Io@D->h6gUdN#8ChTj)L?7 zBIC$&Mh!U&9q~1e1F9OwK{#+6sbZu84jhND;$F!o^BG7#Eqn%;(!N75+IJ)= zCHoGrZC&X?J!B>40>UHc5M-kb0W;dL%obJ}mfm(g1JS30Wd>}?eUM+|J^&N_A0FRr_u>W%M4()%n<0!G9yEW zoqqExlH!PX{#s^`7)w@#?~p}8q{tfI0+*BBZeTTQmKnm4Zvjkv3vi8ZA)NUZ0?xMp z1il3*@hyT2zJ*|&ZwZLRw-63|3sB};2(GueF+3}2$}YB)6B zai`M!EO11jo86&_ zlkAD~ebOhrP+BBCjQ%2YM19p|=)gK?inPw2XnlvB3*1phap zczIT*1xrh})F|O%9l-2%i>9T6+pXE1=SJWxAWm+#Qm-7$?H2WlyWJ)Lmd@R71!T8d zLiU--ACszk;DQStxIobZ7ch9>=4fhkK$m6PN8Ka$2ud_Ztp7XE}t zjsOXhM1S~w%fvedEr6Hh-o9Bwfcexl1k^Qa2vE6{8UhOz8UhQuhQPq+{Tt~h(?IwB z1+01hqOFPEztQU#+h(ixFU(u?{)L-s_x{Bg7`%V+<<)qhP~3CtDmPuhle~Wk1mzlZ z9lU>awN3}`U(mt(S5V#i7gb3pI{EdwvVhu`$^v3dS%7PAX>5lodSYnsn)ffRy~+ER zWR4`Z@cvCszc|shdjH}(TYCQz%-+8QYTm!{vmJW_{iX=>{sjo$zd*tJ7btuG5{RA- zfT1ns{YzVd_b(C2`&WsR_b-9qZU~gTf0x{O?fvy`?!4Kl2F+88Qw^BXsfJ*5ssSvV zY6vB#8sNH9%}XFHFOh*DYYzyR_5f8@p*>(?wma3>pta-9jM~zXhCHGp4PbJlIi+x< z0Uw7KU~r@{q&w082Hg-)=}1E`I?@PCjx-{R66M~CP;#UJt~t^Wj*c{d*^!1ocBG+2 zrSAzoI&@O7t;`q9x{L--M(bEjFsZPj1=5VfXb~>wuRnT zdt1g@F7xWJwAD|(Nafdk%PG}TU9&vYhiR5J(%sXutGDRYi!B?jTD@Ut^)=_6bFNhT z&B1k-Zfp)M%*;&B&hKQ<@7z4Jskz>M6a8FXw!O5G%6z%BkV8rEXA`86 zt5wp#a{dE4qH#LE4{xk^xe3-YXw3Vn$<7;l_f54YYgJiy`MUCJ&_A~xa784&n)bLXeqc#11O zw|I6@{4TnCvmhi zL_>|vJuwn1Tum?PQ-2idk3gn-CZ5M}#44O7QZCgx`9LYUB64>$6(@4sRUe9kptO@o^L8sJWcq&qkTj6F0Tk zm%S!;bvwc7nwi~mz0oZ>iY_^dwppF_O)kQh6XL=`bBOu-UhQ894MiooY7M`k7Z1BP zaj{;DEmpa%)fYYcyt_nquTG7;Yv$kF+h7;lHNEO%x7iuaAluU)%u$)WQ(RtEQE zQ}FY&9X?(++p8Q_njHQ@@WZ-^iAa|cdcdM~jqMo^;q0B4{A?tDgeMxK*dgQVuIBFH z>&BW~;5ng;5m`XhLwklN$Hu32PkVIqJ`84HgvorDs}-PJ3yUkk?3jvV)4?$n?QwEP zH8?ae9#ACtJ3{K_`~zvGSgG?#$G9dCld9dF7Y-Wm%WRz}j- z?rpHFX)`s4fFc>N4P(x{PpEml1Gv z89?wz0ZJYzf(wrnf_0CSfJh!Ggo8&4Q1(b6(C(2!TWTIDA+ORS1(X%fSOZLI@s|sx z|E5M;JWtNFLVehd=-6zv>=$*5?8HaV$lNCFQh6a8%9U_68!`R@qEX)~>IaEE0sYHK zrhxI=)}&=Jeaqj&EA&;5^ZT2oZ)uS~x(iP&2|BQlbsv}Ko50e&h5xDsH_4_Nbl7)# z^A4(uZ(Tn}T`};j?LXQ@($SahN#~e8c&~=EVh*@RYL-6o@ff(7xY##Xg6h&pU@4Jw z4#2{|Nd^`Ms9<3TwX!gv5(@*ISr`NzA4;riz~)UyRSH%Ha%W`#oRxttu`FqT%h#3i43#qCtET9Bg?$hP)%=qDGi4j-qsYE^;g23-I}O(JXycGA>lTW=;8euG0 z7-1~DbSKE(s%{6er#0L_1N&JTXqN5-(Og`*6J%tB+bZY@tWP!hnDEk_AoL67t*G+= zT)Gn^NcKy2f{ah=>A`HQA<%BD!4Oq5)<7gnP-(0I6^%83os|q&ainB0Eo*rv$k(K0 z_}fZO#JIn@o2@&$%HvjkuYHZEYo)p zv(gVS+3g`u{}|;w2FVMESm6%3dP651cglI66bwR^YQsIG`c&|QJ_*<9w@@AP&K)>O zC!q8)VQ>mI`g=wH_mCVdI(wtX9Cvk6t0`CBzQ(Fmt4?-b;KQhiyr1=+&wyE%z6TDg zuR59E9n>t{rMR9%HxYfy8yUI^DT}nhNC@kd&4xC};9cLziySvhc1`!n2hPS^xaOKM zmvA)ZW}U4)pj|WO5|@p+1l*Vl5RAD>8(#wzTo`i+){VIVk&L;7gE1E#{lDC7&{SRIN* zFi-b_M5{wI)#W9jG~n5y5ib}b#}-p2n9`I2iu4ZcdEP$)OlUMQuwb9Jh49kj}_zTRL%hODEu4I>7V& zq0E}tEmQsc?AWmU1ZAL?XwF!OFZFib-mx!E&#@?Q-2vvvYS|E*`|aA?|7YCX4fS53 z{=SB~Td4n2L)|OX*K4Tzg}PaMc0>0XDErR{#gWMZ^^j2cnFLWiBGiYVTIp923ioB< zehgVc{QPl6#50Qcc!P@z2Qu}k9|QR_9jLER#Ak_+NORorImLt`9*z+_0UE1(t0I1f z${d$*_Xzi2I%NNdBL1kQGEWKlryZ!5|2PqU8OvOi^FLP+UyTt(Wi~0|&LA7j4h*bZ zdD6hZDnA~oUizBiA0j?TW_EtDuW`+ZkhKz$%s%cYn(W+{Imf? zEq+!cP(^?`nfe_LMc<0p%NKu~^#AJ!4yvAkEXEi*#>Q$&4
U4<#z|> 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 V0e$D4tJkd|WOOI~ka7)h{{^{CZ=wJI literal 0 HcmV?d00001 diff --git a/build/tools/makenorfirm/test/twl_norfirm9_print.axf b/build/tools/makenorfirm/test/twl_norfirm9_print.axf new file mode 100644 index 0000000000000000000000000000000000000000..3d064b38a5509e8ea9e4ce4cc5302e3f0684d513 GIT binary patch literal 288636 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 literal 0 HcmV?d00001 diff --git a/build/tools/makenorfirm/test/wram_rbin/Makefile b/build/tools/makenorfirm/test/wram_rbin/Makefile new file mode 100644 index 00000000..2d5f2eac --- /dev/null +++ b/build/tools/makenorfirm/test/wram_rbin/Makefile @@ -0,0 +1,47 @@ +#! make -f +#---------------------------------------------------------------------------- +# Project: TwlFirm - tools - norfirm-print +# File: Makefile +# +# Copyright 2007 Nintendo. All rights reserved. +# +# These coded instructions, statements, and computer programs contain +# proprietary information of Nintendo of America Inc. and/or Nintendo +# Company Ltd., and are protected by Federal copyright law. They may +# not be disclosed to third parties or copied or duplicated in any form, +# in whole or in part, without the prior written consent of Nintendo. +# +# $Log: $ +# $NoKeywords: $ +#---------------------------------------------------------------------------- + +SUBDIRS = + +LINCLUDES = ../include + +#---------------------------------------------------------------------------- + +TARGET_BIN = wram_regs.rbin + +SRCS = \ + wram_regs.c \ + +#SRCDIR = # using default +#LCFILE = # using default + + +include $(TWLFIRM_ROOT)/build/buildtools/commondefs + +INSTALL_DIR = . +INSTALL_TARGETS = $(BINDIR)/$(TARGET_BIN) + + +#---------------------------------------------------------------------------- + +do-build: $(TARGETS) + + +include $(TWLFIRM_ROOT)/build/buildtools/modulerules + + +#===== End of Makefile ===== diff --git a/build/tools/makenorfirm/test/wram_rbin/wram_regs.c b/build/tools/makenorfirm/test/wram_rbin/wram_regs.c new file mode 100644 index 00000000..b87e8ba5 --- /dev/null +++ b/build/tools/makenorfirm/test/wram_rbin/wram_regs.c @@ -0,0 +1,88 @@ +/*---------------------------------------------------------------------------* + Project: TwlFirm - tools - makenorfirm + File: wram_regs.c + + Copyright 2007 Nintendo. All rights reserved. + + These coded instructions, statements, and computer programs contain + proprietary information of Nintendo of America Inc. and/or Nintendo + Company Ltd., and are protected by Federal copyright law. They may + not be disclosed to third parties or copied or duplicated in any form, + in whole or in part, without the prior written consent of Nintendo. + + $Log: $ + $NoKeywords: $ + *---------------------------------------------------------------------------*/ +#include +#include + +MIHeader_WramRegs wram_regs = +{ + // ARM9 + { + REG_WRAM_A_BNK_PACK(0, MI_WRAM_A_ARM7, MI_WRAM_A_OFS_0KB, TRUE), + REG_WRAM_A_BNK_PACK(1, MI_WRAM_A_ARM7, MI_WRAM_A_OFS_64KB, TRUE), + REG_WRAM_A_BNK_PACK(2, MI_WRAM_A_ARM7, MI_WRAM_A_OFS_128KB, TRUE), + REG_WRAM_A_BNK_PACK(3, MI_WRAM_A_ARM7, MI_WRAM_A_OFS_192KB, TRUE), + }, + { + REG_WRAM_B_BNK_PACK(0, MI_WRAM_B_ARM7, MI_WRAM_B_OFS_0KB, TRUE), + REG_WRAM_B_BNK_PACK(1, MI_WRAM_B_ARM7, MI_WRAM_B_OFS_32KB, TRUE), + REG_WRAM_B_BNK_PACK(2, MI_WRAM_B_ARM7, MI_WRAM_B_OFS_64KB, TRUE), + REG_WRAM_B_BNK_PACK(3, MI_WRAM_B_ARM7, MI_WRAM_B_OFS_96KB, TRUE), + REG_WRAM_B_BNK_PACK(4, MI_WRAM_B_ARM7, MI_WRAM_B_OFS_128KB, TRUE), + REG_WRAM_B_BNK_PACK(5, MI_WRAM_B_ARM7, MI_WRAM_B_OFS_160KB, TRUE), + REG_WRAM_B_BNK_PACK(6, MI_WRAM_B_ARM7, MI_WRAM_B_OFS_192KB, TRUE), + REG_WRAM_B_BNK_PACK(7, MI_WRAM_B_ARM7, MI_WRAM_B_OFS_224KB, TRUE), + }, + { + REG_WRAM_C_BNK_PACK(0, MI_WRAM_C_ARM9, MI_WRAM_C_OFS_0KB, TRUE), + REG_WRAM_C_BNK_PACK(1, MI_WRAM_C_ARM9, MI_WRAM_C_OFS_32KB, TRUE), + REG_WRAM_C_BNK_PACK(2, MI_WRAM_C_ARM9, MI_WRAM_C_OFS_64KB, TRUE), + REG_WRAM_C_BNK_PACK(3, MI_WRAM_C_ARM9, MI_WRAM_C_OFS_96KB, TRUE), + REG_WRAM_C_BNK_PACK(4, MI_WRAM_C_ARM9, MI_WRAM_C_OFS_128KB, TRUE), + REG_WRAM_C_BNK_PACK(5, MI_WRAM_C_ARM9, MI_WRAM_C_OFS_160KB, TRUE), + REG_WRAM_C_BNK_PACK(6, MI_WRAM_C_ARM9, MI_WRAM_C_OFS_192KB, TRUE), + REG_WRAM_C_BNK_PACK(7, MI_WRAM_C_ARM9, MI_WRAM_C_OFS_224KB, TRUE), + }, + REG_WRAM_A_MAP_PACK(MI_WRAM_MAP_NULL, + MI_WRAM_MAP_NULL, + MI_WRAM_A_IMG_128KB + ), + REG_WRAM_B_MAP_PACK(MI_WRAM_MAP_NULL, + MI_WRAM_MAP_NULL, + MI_WRAM_B_IMG_128KB + ), + REG_WRAM_C_MAP_PACK(HW_WRAM_AREA_HALF, + HW_WRAM_AREA_HALF + 0x00020000, + MI_WRAM_C_IMG_128KB + ), + + // ARM7 + REG_WRAM_A_MAP_PACK(HW_WRAM_AREA_HALF, + HW_WRAM_AREA_HALF + 0x00020000, + MI_WRAM_A_IMG_128KB + ), + REG_WRAM_B_MAP_PACK(HW_WRAM_AREA_HALF + 0x00020000, + HW_WRAM_AREA_HALF + 0x00040000, + MI_WRAM_B_IMG_128KB + ), + REG_WRAM_C_MAP_PACK(MI_WRAM_MAP_NULL, + MI_WRAM_MAP_NULL, + MI_WRAM_C_IMG_128KB + ), + // WRAM Lock + { + 0, + 0, + 0, + }, + + // WRAM-0/1 + MI_WRAM_ARM7_ALL, + + // VRAM-C + 7, + // VRAM-D + 7, +}; diff --git a/build/tools/makenorfirm/wram_regs.c b/build/tools/makenorfirm/wram_regs.c new file mode 100644 index 00000000..b303c52e --- /dev/null +++ b/build/tools/makenorfirm/wram_regs.c @@ -0,0 +1,91 @@ +/*---------------------------------------------------------------------------* + Project: TwlFirm - tools - makenorfirm + File: wram_regs.c + + Copyright 2007 Nintendo. All rights reserved. + + These coded instructions, statements, and computer programs contain + proprietary information of Nintendo of America Inc. and/or Nintendo + Company Ltd., and are protected by Federal copyright law. They may + not be disclosed to third parties or copied or duplicated in any form, + in whole or in part, without the prior written consent of Nintendo. + + $Log: $ + $NoKeywords: $ + *---------------------------------------------------------------------------*/ +#include "format_rom.h" +#define SDK_ASM +#include +#include + +MIHeader_WramRegs wram_regs_init = +{ + // ARM9 + { + REG_WRAM_A_BNK_PACK(0, MI_WRAM_A_ARM7, MI_WRAM_A_OFS_0KB, TRUE), + REG_WRAM_A_BNK_PACK(1, MI_WRAM_A_ARM7, MI_WRAM_A_OFS_64KB, TRUE), + REG_WRAM_A_BNK_PACK(2, MI_WRAM_A_ARM7, MI_WRAM_A_OFS_128KB, TRUE), + REG_WRAM_A_BNK_PACK(3, MI_WRAM_A_ARM7, MI_WRAM_A_OFS_192KB, TRUE), + }, + { + REG_WRAM_B_BNK_PACK(0, MI_WRAM_B_ARM7, MI_WRAM_B_OFS_0KB, TRUE), + REG_WRAM_B_BNK_PACK(1, MI_WRAM_B_ARM7, MI_WRAM_B_OFS_32KB, TRUE), + REG_WRAM_B_BNK_PACK(2, MI_WRAM_B_ARM7, MI_WRAM_B_OFS_64KB, TRUE), + REG_WRAM_B_BNK_PACK(3, MI_WRAM_B_ARM7, MI_WRAM_B_OFS_96KB, TRUE), + REG_WRAM_B_BNK_PACK(4, MI_WRAM_B_ARM7, MI_WRAM_B_OFS_128KB, TRUE), + REG_WRAM_B_BNK_PACK(5, MI_WRAM_B_ARM7, MI_WRAM_B_OFS_160KB, TRUE), + REG_WRAM_B_BNK_PACK(6, MI_WRAM_B_ARM7, MI_WRAM_B_OFS_192KB, TRUE), + REG_WRAM_B_BNK_PACK(7, MI_WRAM_B_ARM7, MI_WRAM_B_OFS_224KB, TRUE), + }, + { + REG_WRAM_C_BNK_PACK(0, MI_WRAM_C_ARM9, MI_WRAM_C_OFS_0KB, TRUE), + REG_WRAM_C_BNK_PACK(1, MI_WRAM_C_ARM9, MI_WRAM_C_OFS_32KB, TRUE), + REG_WRAM_C_BNK_PACK(2, MI_WRAM_C_ARM9, MI_WRAM_C_OFS_64KB, TRUE), + REG_WRAM_C_BNK_PACK(3, MI_WRAM_C_ARM9, MI_WRAM_C_OFS_96KB, TRUE), + REG_WRAM_C_BNK_PACK(4, MI_WRAM_C_ARM9, MI_WRAM_C_OFS_128KB, TRUE), + REG_WRAM_C_BNK_PACK(5, MI_WRAM_C_ARM9, MI_WRAM_C_OFS_160KB, TRUE), + REG_WRAM_C_BNK_PACK(6, MI_WRAM_C_ARM9, MI_WRAM_C_OFS_192KB, TRUE), + REG_WRAM_C_BNK_PACK(7, MI_WRAM_C_ARM9, MI_WRAM_C_OFS_224KB, TRUE), + }, + REG_WRAM_A_MAP_PACK(MI_WRAM_MAP_NULL, + MI_WRAM_MAP_NULL, + MI_WRAM_A_IMG_128KB + ), + REG_WRAM_B_MAP_PACK(MI_WRAM_MAP_NULL, + MI_WRAM_MAP_NULL, + MI_WRAM_B_IMG_128KB + ), + REG_WRAM_C_MAP_PACK(HW_WRAM_AREA_HALF, + HW_WRAM_AREA_HALF + 0x00020000, + MI_WRAM_C_IMG_128KB + ), + + // ARM7 + REG_WRAM_A_MAP_PACK(HW_WRAM_AREA_HALF, + HW_WRAM_AREA_HALF + 0x00020000, + MI_WRAM_A_IMG_128KB + ), + REG_WRAM_B_MAP_PACK(HW_WRAM_AREA_HALF + 0x00020000, + HW_WRAM_AREA_HALF + 0x00040000, + MI_WRAM_B_IMG_128KB + ), + REG_WRAM_C_MAP_PACK(MI_WRAM_MAP_NULL, + MI_WRAM_MAP_NULL, + MI_WRAM_C_IMG_128KB + ), + // WRAM Lock + { + 0, + 0, + 0, + }, + + // WRAM-0/1 + 3, + + // VRAM-C + 7, + // VRAM-D + 7, +}; + diff --git a/include/firm.h b/include/firm.h new file mode 100644 index 00000000..a44dcc38 --- /dev/null +++ b/include/firm.h @@ -0,0 +1,29 @@ +/*---------------------------------------------------------------------------* + Project: TwlFirm + File: firm.h + + Copyright 2007 Nintendo. All rights reserved. + + These coded instructions, statements, and computer programs contain + proprietary information of Nintendo of America Inc. and/or Nintendo + Company Ltd., and are protected by Federal copyright law. They may + not be disclosed to third parties or copied or duplicated in any form, + in whole or in part, without the prior written consent of Nintendo. + + $Log: $ + $NoKeywords: $ + *---------------------------------------------------------------------------*/ +#ifndef FIRM_H_ +#define FIRM_H_ + +#include + +#include +#include +#include +#include +#include +#include + +/* FIRM_H_ */ +#endif diff --git a/include/firm/acsign.h b/include/firm/acsign.h new file mode 100644 index 00000000..2ccfcced --- /dev/null +++ b/include/firm/acsign.h @@ -0,0 +1,32 @@ +/*---------------------------------------------------------------------------* + Project: TwlFirm - ACSIGN + File: acsign.h + + Copyright 2007 Nintendo. All rights reserved. + + These coded instructions, statements, and computer programs contain + proprietary information of Nintendo of America Inc. and/or Nintendo + Company Ltd., and are protected by Federal copyright law. They may + not be disclosed to third parties or copied or duplicated in any form, + in whole or in part, without the prior written consent of Nintendo. + + $Log: $ + $NoKeywords: $ + *---------------------------------------------------------------------------*/ + +#ifndef FIRM_ACSIGN_H_ +#define FIRM_ACSIGN_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +/* FIRM_ACSIGN_H_ */ +#endif diff --git a/include/firm/aes.h b/include/firm/aes.h new file mode 100644 index 00000000..403eec4a --- /dev/null +++ b/include/firm/aes.h @@ -0,0 +1,27 @@ +/*---------------------------------------------------------------------------* + Project: TwlFirm - include - AES + File: aes.h + + Copyright 2007 Nintendo. All rights reserved. + + These coded instructions, statements, and computer programs contain + proprietary information of Nintendo of America Inc. and/or Nintendo + Company Ltd., and are protected by Federal copyright law. They may + not be disclosed to third parties or copied or duplicated in any form, + in whole or in part, without the prior written consent of Nintendo. + + $Log: $ + $NoKeywords: $ + *---------------------------------------------------------------------------*/ +#ifndef FIRM_AES_H_ +#define FIRM_AES_H_ + +#ifdef SDK_ARM7 +#include +#include +#include +#include +#endif // SDK_ARM7 + +/* FIRM_AES_H_ */ +#endif diff --git a/include/firm/aes/ARM7/aes_ids.h b/include/firm/aes/ARM7/aes_ids.h new file mode 100644 index 00000000..53b19963 --- /dev/null +++ b/include/firm/aes/ARM7/aes_ids.h @@ -0,0 +1,41 @@ +/*---------------------------------------------------------------------------* + Project: TwlFirmSDK - AES - include + File: aes_ids.h + + Copyright 2007 Nintendo. All rights reserved. + + These coded instructions, statements, and computer programs contain + proprietary information of Nintendo of America Inc. and/or Nintendo + Company Ltd., and are protected by Federal copyright law. They may + not be disclosed to third parties or copied or duplicated in any form, + in whole or in part, without the prior written consent of Nintendo. + + $Log: $ + $NoKeywords: $ + *---------------------------------------------------------------------------*/ + +#ifndef TWL_AES_AES_IDS_H_ +#define TWL_AES_AES_IDS_H_ + +#define AES_IDS_ID0_A (('N' << 0) | ('i' << 8) | ('n' << 16) | ('t' << 24)) +#define AES_IDS_ID0_B (('e' << 0) | ('n' << 8) | ('d' << 16) | ('o' << 24)) +#define AES_IDS_ID0_C(c) (((unsigned long)c[0] << 0) | ((unsigned long)c[1] << 8) | ((unsigned long)c[2] << 16) | ((unsigned long)c[3] << 24)) +#define AES_IDS_ID0_D(c) (((unsigned long)c[3] << 0) | ((unsigned long)c[2] << 8) | ((unsigned long)c[1] << 16) | ((unsigned long)c[0] << 24)) + +#define AES_IDS_ID1_A(c) (((unsigned long)c[3] << 0) | ((unsigned long)c[1] << 8) | ((unsigned long)c[2] << 16) | ((unsigned long)c[0] << 24)) +#define AES_IDS_ID1_B(c) (((unsigned long)c[0] << 0) | ((unsigned long)c[2] << 8) | ((unsigned long)c[1] << 16) | ((unsigned long)c[3] << 24)) +#define AES_IDS_ID1_C (*(unsigned long*)0x04004d04) +#define AES_IDS_ID1_D (*(unsigned long*)0x04004d00) + +#define AES_IDS_ID2_A (('N' << 0) | ('i' << 8) | ('n' << 16) | ('t' << 24)) +#define AES_IDS_ID2_B (('e' << 0) | ('n' << 8) | ('d' << 16) | ('o' << 24)) +#define AES_IDS_ID2_C ((' ' << 0) | ('D' << 8) | ('S' << 16) | ('\0' << 24)) +#define AES_IDS_ID2_D ((0x01 << 0) | (0x23 << 8) | (0x21 << 16) | (0x00 << 24)) + +#define AES_IDS_ID3_A (*(unsigned long*)0x04004d00) +#define AES_IDS_ID3_B (('N' << 0) | ('I' << 8) | ('N' << 16) | ('T' << 24)) +#define AES_IDS_ID3_C (('E' << 0) | ('N' << 8) | ('D' << 16) | ('O' << 24)) +#define AES_IDS_ID3_D (*(unsigned long*)0x04004d04) + +/* TWL_AES_AES_IDS_H_ */ +#endif diff --git a/include/firm/aes/ARM7/aes_init.h b/include/firm/aes/ARM7/aes_init.h new file mode 100644 index 00000000..cc787258 --- /dev/null +++ b/include/firm/aes/ARM7/aes_init.h @@ -0,0 +1,34 @@ +/*---------------------------------------------------------------------------* + Project: TwlFirm - AES - include + File: aes_init.h + + Copyright 2007 Nintendo. All rights reserved. + + These coded instructions, statements, and computer programs contain + proprietary information of Nintendo of America Inc. and/or Nintendo + Company Ltd., and are protected by Federal copyright law. They may + not be disclosed to third parties or copied or duplicated in any form, + in whole or in part, without the prior written consent of Nintendo. + + $Log: $ + $NoKeywords: $ + *---------------------------------------------------------------------------*/ + +#ifndef TWL_AES_AES_INIT_H_ +#define TWL_AES_AES_INIT_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +/*---------------------------------------------------------------------------* + 関数定義 + *---------------------------------------------------------------------------*/ +void AESi_InitGameKeys( u8 game_code[4] ); + +/* TWL_AES_AES_INIT_H_ */ +#endif diff --git a/include/firm/devices/firm_sdmc/ARM7/sdmc.h b/include/firm/devices/firm_sdmc/ARM7/sdmc.h new file mode 100644 index 00000000..7ddbb092 --- /dev/null +++ b/include/firm/devices/firm_sdmc/ARM7/sdmc.h @@ -0,0 +1,170 @@ + +#ifndef __SDMC_H__ +#define __SDMC_H__ + +//#include +//#include + + +#ifdef __cplusplus +extern "C" { +#endif + + + +/********************************************* + ポート番号 +*********************************************/ +typedef enum { + SDMC_PORT_CARD = 0x400, + SDMC_PORT_NAND = 0x401 +}SDMC_PORT_NO; + + +/********************************************* + DMA番号 +*********************************************/ +typedef enum { + SDMC_USE_DMA_0 = 0, + SDMC_USE_DMA_1 = 1, + SDMC_USE_DMA_2 = 2, + SDMC_USE_DMA_3 = 3, + SDMC_NOUSE_DMA = 0xFF +}SDMC_DMA_NO; + + +/********************************************* + カードエラーコード(カードエラーステータス設定値)アプリケーション固有のSDCARD_ErrStatusに対して +*********************************************/ +typedef enum { + SDMC_NORMAL = 0, /* 正常終了 */ + SDMC_ERR_COMMAND = 0x0001, /* CMDエラー */ + SDMC_ERR_CRC = 0x0002, /* CRCエラー */ + SDMC_ERR_END = 0x0004, /* 実行エラー */ + SDMC_ERR_TIMEOUT = 0x0008, /* コマンドタイムアウト */ + SDMC_ERR_FIFO_OVF = 0x0010, /* FIFO オーバーフローエラー(INFO2のIllegal write access to buffer) */ + SDMC_ERR_FIFO_UDF = 0x0020, /* FIFO アンダーフローエラー(INFO2のIllegal read access to buffer) */ + SDMC_ERR_WP = 0x0040, /* WriteProtectによる書き込みエラー */ + SDMC_ERR_FPGA_TIMEOUT = 0x0100, /* FPGA アクセスタイムアウト */ + SDMC_ERR_PARAM = 0x0200, /* コマンドパラメータエラー */ + SDMC_ERR_R1_STATUS = 0x0800, /* Normal response command カードステータス エラー */ + SDMC_ERR_NUM_WR_SECTORS = 0x1000, /* 書き込み完了セクタ数 エラー */ + SDMC_ERR_RESET = 0x2000, /* 初期化カードリセットコマンド時1.5秒タイムアウトエラー */ + SDMC_ERR_ILA = 0x4000, /* イリーガルアクセスエラー */ + SDMC_ERR_INFO_DETECT = 0x8000 /* カード排出時判別エラービット(IO3) */ +}SDMC_ERR_CODE; + + +/********************************************* + SDドライバ処理結果通知情報構造体 +*********************************************/ +typedef struct { + u16 b_flags; /* 処理内容 */ + u16 result; /* 実行結果 */ + u32 resid; /* 読み(書き)サイズ */ +} SdmcResultInfo; + + + + + +/********************************************* + SDポート状態保存用構造体 +*********************************************/ +typedef struct +{ + u16 SD_CID[8]; /* CID保存用 (Card IDentification register) : ID*/ + u16 SD_CSD[8]; /* CSD保存用 (Card Specific Data register) : spec*/ + u16 SD_OCR[2]; /* OCR保存用 (Operation Condition Register) : voltage and status*/ + u16 SD_SCR[4]; /* SCR保存用 (Sd card Configulation Register) : bus-width, card-ver, etc*/ + u16 SD_RCA; /* RCA保存用 (Relative Card Address register) : address*/ + s16 MMCFlag; + s16 SDHCFlag; + s16 SDFlag; + SDMC_ERR_CODE ErrStatus; /* SDCARD_ErrStatus */ + u32 Status; /* SDCARD_Status */ + u16 SD_CLK_CTRL_VALUE; + u16 SD_OPTION_VALUE; + + s16 OutFlag; + u16 port_no; +} +SDPortContext; + + + + + +/********************************************* + SDスペック構造体 +*********************************************/ +typedef struct { + u32 csd_ver2_flag; //CSDフォーマットバージョン(SDHCのときは1) + u32 memory_capacity; //data areaのサイズ(512Byte単位) + u32 protected_capacity; //protected areaのサイズ(512Byte単位) + u32 card_capacity; //カード全体のサイズ(512Byte単位) + + u32 adjusted_memory_capacity; //memory_capacityをシリンダ(heads*secptrack)の倍数に調整したサイズ(cylinders*heads*secptrackになる) + + u16 heads; + u16 secptrack; + u16 cylinders; + u16 SC; //sectors per cluster + u16 BU; + u16 RDE; //number of root dir entries(512 fix) + u32 SS; //sector size(512 fix) + u32 RSC; //reserved sector count(1 fix) +// u32 TS; //total sectors + u16 FATBITS; //16 or 32 + u16 SF; //sectors per FAT + u32 SSA; //sectors in system area + u32 NOM; //sectors in master boot record +} SdmcSpec; + + +/********************************************* + RTFS用ドライバインタフェース +*********************************************/ +#if 0 +BOOL sdmcRtfsIo( int driveno, dword block, void* buffer, word count, BOOLEAN reading); +int sdmcRtfsCtrl( int driveno, int opcode, void* pargs); +BOOL sdmcRtfsAttach( int driveno); +#endif + +BOOL sdmcCheckMedia( void); + + + +/********************************************* + 基本API +*********************************************/ +void sdmcClearPortContext( SDPortContext* buf_adr); +SDMC_ERR_CODE sdmcCheckPortContext( SDPortContext* buf_adr); + + +SDMC_ERR_CODE sdmcInit( SDMC_DMA_NO dma_no, void (*func1)(void),void (*func2)(void)); /* カードドライバ初期化 */ +SDMC_ERR_CODE sdmcReset( void); /* カードリセット */ + +SDMC_ERR_CODE sdmcGetStatus(u16 *status); /* カードドライバの現在の状態を取得する */ +u32 sdmcGetCardSize(void); /* カード全サイズの取得 */ + +/*SD I/FのFIFOを使ってリードする(高速)*/ +SDMC_ERR_CODE sdmcReadFifo(void* buf,u32 bufsize,u32 offset,void(*func)(void),SdmcResultInfo *info);/* テスト用カードリード */ +/*リードする*/ +SDMC_ERR_CODE sdmcRead(void* buf,u32 bufsize,u32 offset,void(*func)(void),SdmcResultInfo *info); /* テスト用カードリード */ + +/*SD I/FのFIFOを使ってライトする(高速)*/ +SDMC_ERR_CODE sdmcWriteFifo(void* buf,u32 bufsize,u32 offset,void(*func)(void),SdmcResultInfo *info);/* テスト用カードライト */ +/*ライトする*/ +SDMC_ERR_CODE sdmcWrite(void* buf,u32 bufsize,u32 offset,void(*func)(void),SdmcResultInfo *info); /* テスト用カードライト */ + +u16 sdmcSelectedNo(void); +SDMC_ERR_CODE sdmcSelect( u16 select); + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + + +#endif /*__SDMC_H__*/ diff --git a/include/firm/format/firm_common.h b/include/firm/format/firm_common.h new file mode 100644 index 00000000..21816c18 --- /dev/null +++ b/include/firm/format/firm_common.h @@ -0,0 +1,66 @@ +/*---------------------------------------------------------------------------* + Project: TwlFirm - format - firm + File: firm_common.h + + Copyright 2007 Nintendo. All rights reserved. + + These coded instructions, statements, and computer programs contain + proprietary information of Nintendo of America Inc. and/or Nintendo + Company Ltd., and are protected by Federal copyright law. They may + not be disclosed to third parties or copied or duplicated in any form, + in whole or in part, without the prior written consent of Nintendo. + + $Log: $ + $NoKeywords: $ + *---------------------------------------------------------------------------*/ +#ifndef FIRM_FORMAT_FIRM_COMMON_H_ +#define FIRM_FORMAT_FIRM_COMMON_H_ + +#include +#include + + +#ifdef __cplusplus +extern "C" { +#endif + + +// 常駐モジュール情報 +typedef struct +{ + u32 rom_offset; /* 転送元 ROM オフセット */ + u32 decomp_size; /* 展開サイズ */ + void *ram_address; /* 転送先 RAM オフセット */ + u32 size; /* 転送サイズ */ +} +FIRMHeader_ModuleInfo; + +// DS-IPL2ヘッダ +typedef struct +{ + u16 reserved_0h[4]; + u32 ds_key; + u16 ds_arm9_romAdr; + u16 ds_arm9_ramAdr; + u16 ds_arm7_romAdr; + u16 ds_arm7_ramAdr; + u16 ds_arm9_romOffsetUnit:3; + u16 ds_arm9_ramOffsetUnit:3; + u16 ds_arm7_romOffsetUnit:3; + u16 ds_arm7_ramOffsetUnit:3; + u16 :2; + u16 ds_header_ver:2; + u16 ds_data_romAdr; + u64 card_key; + u16 ncd_romAdr; + u16 reserved_24h[2]; + u16 ds_data_crc16; +} +NORHeaderDS; + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif // FIRM_FORMAT_FIRM_COMMON_H_ diff --git a/include/firm/format/from_brom.h b/include/firm/format/from_brom.h new file mode 100644 index 00000000..de165174 --- /dev/null +++ b/include/firm/format/from_brom.h @@ -0,0 +1,92 @@ +/*---------------------------------------------------------------------------* + Project: TwlFirm - format - from_brom + File: from_brom.h + + Copyright 2007 Nintendo. All rights reserved. + + These coded instructions, statements, and computer programs contain + proprietary information of Nintendo of America Inc. and/or Nintendo + Company Ltd., and are protected by Federal copyright law. They may + not be disclosed to third parties or copied or duplicated in any form, + in whole or in part, without the prior written consent of Nintendo. + + $Log: $ + $NoKeywords: $ + *---------------------------------------------------------------------------*/ +#ifndef FIRM_FORMAT_FROM_BROM_H_ +#define FIRM_FORMAT_FROM_BROM_H_ + +#include +#include +#include +#include +#include + + + + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef SDK_ARM9 +#define RSA_PUBKEY_NUM_FROM_BROM 8 +#define AESKEY_NUM_FROM_BROM 8 +#else // SDK_ARM7 +#define RSA_PUBKEY_NUM_FROM_BROM 4 +#define AESKEY_NUM_FROM_BROM 4 +#endif // SDK_ARM7 + +typedef struct +{ + union + { + NANDHeader nand; + NORHeader nor; + GCDHeader gcd; + u8 max[0x400]; + } + header; // 1KB + + u8 rsa_pubkey[RSA_PUBKEY_NUM_FROM_BROM][ACS_PUBKEY_LEN]; // 1KB + u8 aes_key[AESKEY_NUM_FROM_BROM][ACS_AES_LEN]; // 128B + u8 hash_table_hash[ACS_HASH_LEN]; // 20B + + BLOWFISH_CTX ds_blowfish; // 4KB + α + BLOWFISH_CTX twl_blowfish; // 4KB + α +} +OSFromBrom9Buf; + +typedef struct +{ + union + { + NANDHeader nand; + NORHeader nor; + GCDHeader gcd; + u8 max[0x400]; + } + header; // 1KB + + u8 rsa_pubkey[RSA_PUBKEY_NUM_FROM_BROM][ACS_PUBKEY_LEN]; // 512B + u8 aes_key[AESKEY_NUM_FROM_BROM][ACS_AES_LEN]; // 64B + u8 hash_table_hash[ACS_HASH_LEN]; // 20B + + BLOWFISH_CTX twl_blowfish[2]; // (4KB + α) * 2 + + SDPortContext SDNandContext; +} +OSFromBrom7Buf; + +#ifdef SDK_ARM9 +typedef OSFromBrom9Buf OSFromBromBuf; +#else // SDK_ARM7 +typedef OSFromBrom7Buf OSFromBromBuf; +#endif // SDK_ARM7 + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif // FIRM_FORMAT_FROM_BROM_H_ diff --git a/include/firm/format/gcdfirm.h b/include/firm/format/gcdfirm.h new file mode 100644 index 00000000..1c8b771b --- /dev/null +++ b/include/firm/format/gcdfirm.h @@ -0,0 +1,113 @@ +/*---------------------------------------------------------------------------* + Project: TwlFirm - format - gcdfirm + File: gcdfirm.h + + Copyright 2007 Nintendo. All rights reserved. + + These coded instructions, statements, and computer programs contain + proprietary information of Nintendo of America Inc. and/or Nintendo + Company Ltd., and are protected by Federal copyright law. They may + not be disclosed to third parties or copied or duplicated in any form, + in whole or in part, without the prior written consent of Nintendo. + + $Log: $ + $NoKeywords: $ + *---------------------------------------------------------------------------*/ +#ifndef FIRM_FORMAT_GCDFIRM_H_ +#define FIRM_FORMAT_GCDFIRM_H_ + +#include +#include +#include + + +#ifdef __cplusplus +extern "C" { +#endif + + +/*===========================================================================* + * NOR FORMAT + *===========================================================================*/ + +//--------------------------------------------------------------------------- +// Section A NOR HEADER +//--------------------------------------------------------------------------- + +// 常駐モジュール情報 +typedef FIRMHeader_ModuleInfo GCDHeader_ModuleInfo; + +// TWL-GCDファームヘッダ +typedef struct +{ + /* 0x000-0x020 [システム予約領域] */ + u8 reserved_0h[0x14]; + u8 rom_size; // Rom size (2のrom_size乗 Mbit: ex. 128Mbitのときrom_size = 7) + u8 reserved_16h[0xb]; + + /* 0x020-0x040 [常駐モジュール用パラメータ] */ + u32 main_rom_offset; /* ARM9 転送元 ROM オフセット */ + u32 main_decomp_size; /* ARM9 展開サイズ */ + void *main_ram_address; /* ARM9 転送先 RAM オフセット */ + u32 main_size; /* ARM9 転送サイズ */ + u32 sub_rom_offset; /* ARM7 転送元 ROM オフセット */ + u32 sub_decomp_size; /* ARM9 展開サイズ */ + void *sub_ram_address; /* ARM7 転送先 RAM オフセット */ + u32 sub_size; /* ARM7 転送サイズ */ + + /* 0x040-0x080 [システム予約領域] */ + u8 reserved_40h[0x40]; + + /* 0x080-0x090 [ファームバイナリ情報] */ + u32 nandfirm_offset; // address of rom_valid_size + u32 nandfirm_size; // address of rom_header_size + u32 norfirm_offset; // address of main_module_param + u32 norfirm_size; // address of sub_module_param + + /* 0x090-0x094 [TWL-ROMコントロール] */ + u16 normal_area_offset; + u16 twl_area_offset; + + /* 0x094-0x0c0 [システム予約領域] */ + u8 reserved_98h[0x2c]; + + /* 0x0c0-0x100 [DSカードNINTENDOロゴ重複領域] */ + u8 reserved_C0h[0x3f]; + + u8 comp_arm9_boot_area:1; // Compress arm9 boot area + u8 comp_arm7_boot_area:1; // Compress arm7 boot area + u8 arm9_x2:1; + u8 :0; +} +GCDHeaderLow; + +typedef struct +{ + /* 0x180-0x1b0 [WRAMレジスタパラメータ] */ + MIHeader_WramRegs w; + + /* 0x1b0-0x200 [システム予約領域] */ + u8 reserved_footer[0x50]; +} +GCDHeaderHigh; + +// GCDヘッダ +typedef struct +{ + /* 0x000-0x100 */ + GCDHeaderLow l; + + /* 0x100-0x180 */ + FIRMPaddedSign sign; + + /* 0x180-0x200 */ + GCDHeaderHigh h; +} +GCDHeader; + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif //FIRM_FORMAT_GCDFIRM_H_ diff --git a/include/firm/format/nandfirm.h b/include/firm/format/nandfirm.h new file mode 100644 index 00000000..1ec292c0 --- /dev/null +++ b/include/firm/format/nandfirm.h @@ -0,0 +1,130 @@ +/*---------------------------------------------------------------------------* + Project: TwlFirm - format - nandfirm + File: nandfirm.h + + Copyright 2007 Nintendo. All rights reserved. + + These coded instructions, statements, and computer programs contain + proprietary information of Nintendo of America Inc. and/or Nintendo + Company Ltd., and are protected by Federal copyright law. They may + not be disclosed to third parties or copied or duplicated in any form, + in whole or in part, without the prior written consent of Nintendo. + + $Log: $ + $NoKeywords: $ + *---------------------------------------------------------------------------*/ +#ifndef FIRM_FORMAT_NAND_H_ +#define FIRM_FORMAT_NAND_H_ + +#include +#include +#include + + +#ifdef __cplusplus +extern "C" { +#endif + + +/*===========================================================================* + * NAND FORMAT + *===========================================================================*/ + +//--------------------------------------------------------------------------- +// Section A NAND HEADER +//--------------------------------------------------------------------------- + +// 常駐モジュール情報 +typedef FIRMHeader_ModuleInfo NANDHeader_ModuleInfo; + +// TWL-NANDファームヘッダ +typedef struct +{ + /* 0x000-0x020 [システム予約領域] */ + u8 reserved_0h[0x20]; /* システム予約 A */ + + /* 0x020-0x040 [常駐モジュール用パラメータ] */ + u32 main_rom_offset; /* ARM9 転送元 ROM オフセット */ + u32 main_decomp_size; /* ARM9 展開サイズ */ + void *main_ram_address; /* ARM9 転送先 RAM オフセット */ + u32 main_size; /* ARM9 転送サイズ */ + u32 sub_rom_offset; /* ARM7 転送元 ROM オフセット */ + u32 sub_decomp_size; /* ARM9 展開サイズ */ + void *sub_ram_address; /* ARM7 転送先 RAM オフセット */ + u32 sub_size; /* ARM7 転送サイズ */ + + /* 0x040-0x0c0 [システム予約領域] */ + u8 reserved_40h[0x80]; + + /* 0x0c0-0x100 [DSカードNINTENDOロゴ重複領域] */ + u8 reserved_C0h[0x3f]; + + u8 comp_arm9_boot_area:1; // Compress arm9 boot area + u8 comp_arm7_boot_area:1; // Compress arm7 boot area + u8 arm9_x2:1; + u8 arm9_decomp:1; + u8 :0; +} +NANDHeaderLow; + +typedef struct +{ + /* 0x180-0x1b0 [WRAMレジスタパラメータ] */ + MIHeader_WramRegs w; + + /* 0x1b0-0x200 [システム予約領域] */ + u8 reserved_footer[0x50]; +} +NANDHeaderHigh; + +// NANDヘッダ +typedef struct +{ + /* 0x000-0x028 [DS-NORヘッダ] */ + NORHeaderDS d; + + /* 0x028-0x200 [システム予約領域(署名外)] */ + u8 reserved_mbr[512 - sizeof(NORHeaderDS)]; + + /* 0x200-0x300 */ + NANDHeaderLow l; + + /* 0x300-0x380 */ + FIRMPaddedSign sign; + + /* 0x380-0x400 */ + NANDHeaderHigh h; +} +NANDHeader; + +// 冗長化ヘッダ (冗長部分) +typedef struct +{ + /* 0x400-0x500 *//* 0x600-0x700 */ + NANDHeaderLow l; + + /* 0x500-0x580 *//* 0x700-0x780 */ + FIRMPaddedSign sign; + + /* 0x580-0x600 *//* 0x780-0x800 */ + NANDHeaderHigh h; +} +NANDHeaderCore; + +// 冗長化ヘッダ (全体) +typedef struct +{ + /* 0x000-0x400 [実際に使用されるヘッダ] */ + NANDHeader g; + /* 0x400-0x600 [デフォルト位置イメージに対するヘッダ] */ + NANDHeaderCore o; + /* 0x600-0x800 [一時待避位置イメージに対するヘッダ] */ + NANDHeaderCore m; +} +NANDHeaderEx; + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif //FIRM_FORMAT_NAND_H_ diff --git a/include/firm/format/norfirm.h b/include/firm/format/norfirm.h new file mode 100644 index 00000000..6eafc329 --- /dev/null +++ b/include/firm/format/norfirm.h @@ -0,0 +1,107 @@ +/*---------------------------------------------------------------------------* + Project: TwlFirm - format - norfirm + File: norfirm.h + + Copyright 2007 Nintendo. All rights reserved. + + These coded instructions, statements, and computer programs contain + proprietary information of Nintendo of America Inc. and/or Nintendo + Company Ltd., and are protected by Federal copyright law. They may + not be disclosed to third parties or copied or duplicated in any form, + in whole or in part, without the prior written consent of Nintendo. + + $Log: $ + $NoKeywords: $ + *---------------------------------------------------------------------------*/ +#ifndef FIRM_FORMAT_NOR_H_ +#define FIRM_FORMAT_NOR_H_ + +#include +#include +#include + + +#ifdef __cplusplus +extern "C" { +#endif + + +/*===========================================================================* + * NOR FORMAT + *===========================================================================*/ + +//--------------------------------------------------------------------------- +// Section A NOR HEADER +//--------------------------------------------------------------------------- + +// 常駐モジュール情報 +typedef FIRMHeader_ModuleInfo NORHeader_ModuleInfo; + +// TWL-NORファームヘッダ +typedef struct +{ + /* 0x000-0x020 [システム予約領域] */ + u8 reserved_0h[0x20]; /* システム予約 A */ + + /* 0x020-0x040 [常駐モジュール用パラメータ] */ + u32 main_rom_offset; /* ARM9 転送元 ROM オフセット */ + u32 main_decomp_size; /* ARM9 展開サイズ */ + void *main_ram_address; /* ARM9 転送先 RAM オフセット */ + u32 main_size; /* ARM9 転送サイズ */ + u32 sub_rom_offset; /* ARM7 転送元 ROM オフセット */ + u32 sub_decomp_size; /* ARM9 展開サイズ */ + void *sub_ram_address; /* ARM7 転送先 RAM オフセット */ + u32 sub_size; /* ARM7 転送サイズ */ + + /* 0x040-0x0c0 [システム予約領域] */ + u8 reserved_40h[0x80]; + + /* 0x0c0-0x100 [DSカードNINTENDOロゴ重複領域] */ + u8 reserved_C0h[0x3f]; + + u8 comp_arm9_boot_area:1; // Compress arm9 boot area + u8 comp_arm7_boot_area:1; // Compress arm7 boot area + u8 arm9_x2:1; + u8 arm9_decomp:1; + u8 :2; + u8 baudrate:1; + u8 boot_nandfirm:1; +} +NORHeaderLow; + +typedef struct +{ + /* 0x180-0x1b0 [WRAMレジスタパラメータ] */ + MIHeader_WramRegs w; + + /* 0x1b0-0x200 [システム予約領域] */ + u8 reserved_footer[0x50]; +} +NORHeaderHigh; + +// NORヘッダ +typedef struct +{ + /* 0x000-0x028 */ + NORHeaderDS d; + + /* 0x028-0x200 */ + u8 wl_params[472]; + + /* 0x200-0x300 */ + NORHeaderLow l; + + /* 0x300-0x380 */ + FIRMPaddedSign sign; + + /* 0x380-0x400 */ + NORHeaderHigh h; +} +NORHeader; + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif //FIRM_FORMAT_NOR_H_ diff --git a/include/firm/format/sign.h b/include/firm/format/sign.h new file mode 100644 index 00000000..7dc5d7e9 --- /dev/null +++ b/include/firm/format/sign.h @@ -0,0 +1,94 @@ +/*---------------------------------------------------------------------------* + Project: TwlFirm - format - sign + File: sign.h + + Copyright 2007 Nintendo. All rights reserved. + + These coded instructions, statements, and computer programs contain + proprietary information of Nintendo of America Inc. and/or Nintendo + Company Ltd., and are protected by Federal copyright law. They may + not be disclosed to third parties or copied or duplicated in any form, + in whole or in part, without the prior written consent of Nintendo. + + $Log: $ + $NoKeywords: $ + *---------------------------------------------------------------------------*/ +#ifndef FORMAT_SIGN_H_ +#define FORMAT_SIGN_H_ + + +#ifdef __cplusplus +extern "C" { +#endif + +// signed hash index +typedef enum +{ + FIRM_SIGNED_HASH_IDX_HEADER = 0, + FIRM_SIGNED_HASH_IDX_ARM9 = 1, + FIRM_SIGNED_HASH_IDX_ARM7 = 2, + FIRM_SIGNED_HASH_IDX_HASH_TABLE = 3, + FIRM_SIGNED_HASH_IDX_FINAL = 4, + + FIRM_SIGNED_HASH_NUM = 5 +} +FIRMSignedHashIndex; + + +#define ACS_BASE_BLOCK_SIZE 4096 // should be the multiple of 16 +#define ACS_META_BLOCK_SIZE 5120 // should be the multiple of 16 and the multiple of 20 + // would be the multiple of 512, then the mutilple of 2560 + +#define ACS_PUBKEY_LEN 128 +#define ACS_HASH_LEN 20 +#define ACS_AES_LEN 16 + +#define ACS_ENCRYPTED_HASH_LEN ACS_PUBKEY_LEN +#define ACS_DECRYPTED_HASH_LEN ACS_HASH_LEN + +#define ACS_RSA_EXP 0x00010001 +#define ACS_RSA_EXP_LEN 3 + +// DER format of RSA keys + +#define ACS_RSA_PRVMOD_OFFSET 0x0B +#define ACS_RSA_PRVEXP_OFFSET 0x93 +#define ACS_RSA_PRVMOD_LEN 128 +#define ACS_RSA_PRVEXP_LEN 128 + +#define ACS_RSA_PUBMOD_OFFSET 0x1D +#define ACS_RSA_PUBEXP_OFFSET 0x93 +#define ACS_RSA_PUBMOD_LEN 128 +#define ACS_RSA_PUBEXP_LEN ACS_RSA_EXP_LEN + +#define FIRM_HEADER_2ND_HASH_AREA_LEN (sizeof(FIRMSignedContext) - ACS_HASH_LEN) + + +// 署名コンテキスト +typedef struct +{ + unsigned char aes_key[ACS_AES_LEN]; + unsigned char hash[FIRM_SIGNED_HASH_NUM][ACS_HASH_LEN]; +} +FIRMSignedContext; + +// 署名 +typedef union +{ + struct + { + unsigned char prePad[(ACS_ENCRYPTED_HASH_LEN - sizeof(FIRMSignedContext))-1]; + FIRMSignedContext c; + unsigned char postPad[1]; + } + e; + unsigned int raw[ACS_ENCRYPTED_HASH_LEN/4]; +} +FIRMPaddedSign; + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif //FORMAT_SIGN_H_ diff --git a/include/firm/format/wram_regs.h b/include/firm/format/wram_regs.h new file mode 100644 index 00000000..10e8347c --- /dev/null +++ b/include/firm/format/wram_regs.h @@ -0,0 +1,52 @@ +/*---------------------------------------------------------------------------* + Project: TwlFirm - MI - include + File: wram_regs.h + + Copyright 2007 Nintendo. All rights reserved. + + These coded instructions, statements, and computer programs contain + proprietary information of Nintendo of America Inc. and/or Nintendo + Company Ltd., and are protected by Federal copyright law. They may + not be disclosed to third parties or copied or duplicated in any form, + in whole or in part, without the prior written consent of Nintendo. + + $Log: $ + $NoKeywords: $ + *---------------------------------------------------------------------------*/ +#ifndef FIRM_FORMAT_WRAM_REGS_H_ +#define FIRM_FORMAT_WRAM_REGS_H_ + + +#ifdef __cplusplus +extern "C" { +#endif + +// WRAMマッピングレジスタ +typedef struct +{ + u8 main_wrambnk_a[4]; // ARM9 SCFG_MBK1 + u8 main_wrambnk_b[8]; // ARM9 SCFG_MBK2-3 + u8 main_wrambnk_c[8]; // ARM9 SCFG_MBK4-5 + + u32 main_wrammap_a; // ARM9 SCFG_MBK6 + u32 main_wrammap_b; // ARM9 SCFG_MBK7 + u32 main_wrammap_c; // ARM9 SCFG_MBK8 + + u32 sub_wrammap_a; // ARM7 SCFG_MBK6 + u32 sub_wrammap_b; // ARM7 SCFG_MBK7 + u32 sub_wrammap_c; // ARM7 SCFG_MBK8 + + u8 sub_wramlock[3]; // ARM7 SCFG_MBK9 + + u8 main_wrambnk_01:2; // ARM9 RBKCNT1_H + u8 main_vrambnk_c:3; // ARM9 RBKCNT0_H + u8 main_vrambnk_d:3; // ARM9 RBKCNT0_H +} +MIHeader_WramRegs; + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +/* FIRM_FORMAT_WRAM_REGS_H_ */ +#endif diff --git a/include/firm/gcd.h b/include/firm/gcd.h new file mode 100644 index 00000000..8f2404c6 --- /dev/null +++ b/include/firm/gcd.h @@ -0,0 +1,25 @@ +/*---------------------------------------------------------------------------* + Project: TwlFirm - GCD + File: gcd.h + + Copyright 2007 Nintendo. All rights reserved. + + These coded instructions, statements, and computer programs contain + proprietary information of Nintendo of America Inc. and/or Nintendo + Company Ltd., and are protected by Federal copyright law. They may + not be disclosed to third parties or copied or duplicated in any form, + in whole or in part, without the prior written consent of Nintendo. + + $Log: $ + $NoKeywords: $ + *---------------------------------------------------------------------------*/ + +#ifndef FIRM_GCD_H_ +#define FIRM_GCD_H_ + +//#include +#include +#include + +/* FIRM_GCD_H_ */ +#endif diff --git a/include/firm/gcd/blowfish.h b/include/firm/gcd/blowfish.h new file mode 100644 index 00000000..6d33d599 --- /dev/null +++ b/include/firm/gcd/blowfish.h @@ -0,0 +1,41 @@ +/*---------------------------------------------------------------------------* + Project: TwlFirm - GCD - include + File: blowfish.h + + Copyright 2007 Nintendo. All rights reserved. + + These coded instructions, statements, and computer programs contain + proprietary information of Nintendo of America Inc. and/or Nintendo + Company Ltd., and are protected by Federal copyright law. They may + not be disclosed to third parties or copied or duplicated in any form, + in whole or in part, without the prior written consent of Nintendo. + + $Log: $ + $NoKeywords: $ + *---------------------------------------------------------------------------*/ +#ifndef FIRM_GCD_BLOWFISH_H +#define FIRM_GCD_BLOWFISH_H + +#include + + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + u32 P[16 + 2]; + u32 S[4][256]; +} BLOWFISH_CTX; + + +void InitBlowfish(BLOWFISH_CTX *ctx, const unsigned char *key, int keyLen); +void EncryptByBlowfish(const BLOWFISH_CTX *ctx, u32 *xl, u32 *xr); +void DecryptByBlowfish(const BLOWFISH_CTX *ctx, u32 *xl, u32 *xr); + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif // FIRM_GCD_BLOWFISH_H diff --git a/include/firm/gcd/gcd.h b/include/firm/gcd/gcd.h new file mode 100644 index 00000000..347d84c3 --- /dev/null +++ b/include/firm/gcd/gcd.h @@ -0,0 +1,773 @@ +/*---------------------------------------------------------------------------* + Project: TwlFirm - GCD - include + File: gcd.h + + Copyright 2007 Nintendo. All rights reserved. + + These coded instructions, statements, and computer programs contain + proprietary information of Nintendo of America Inc. and/or Nintendo + Company Ltd., and are protected by Federal copyright law. They may + not be disclosed to third parties or copied or duplicated in any form, + in whole or in part, without the prior written consent of Nintendo. + + $Log: $ + $NoKeywords: $ + *---------------------------------------------------------------------------*/ + +#ifndef FIRM_GCD_GCD_H_ +#define FIRM_GCD_GCD_H_ + +#include +#include +#include +#include +#include +#include + + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum +{ + GCD_PRIMARY_SLOT = 0, + GCD_SECONDARY_SLOT = 1, + + GCD_NO_SLOT = -1 +} +GCDSlot; + + +/*****************/ + +/* ROMヘッダ・ブートパラメータ構造体 */ +typedef struct { + u8 *romAddr; // ROMアドレス + u8 *entryAddr; // エントリアドレス + u8 *ramAddr; // RAMアドレス + s32 romSize; // ROMサイズ +} GCDBootUsrParam; + +// カードアクセス・コントロールレジスタ構造体 +typedef struct { + u16 latency1:13; // レイテンシ1のサイクル数 + u16 dataScramble_on:1; // データスクランブル ON + u16 scrambleUnit_on:1; // スクランブル回路 ON + u16 initScramblePN:1; // スクランブルPNデータ初期化 + u16 latency2:6; // レイテンシ2のサイクル数 + u16 cmdScramble_on:1; // コマンドスクランブル ON + u16 dataReady:1; // データ レディ + u16 pages:3; // ページ数 + u16 clockType:1; // クロックタイプ(150ns/240ns) + u16 clockInLatency:1; // レイテンシ期間にクロック供給 + u16 reset:1; // リセット信号 + u16 accessMode:1; // アクセスモード + u16 start:1; // スタート +} GCDCardCnt; + +// カードコントロール・パラメータ構造体 +typedef struct { + // u32 dmaNo; // DMA番号 + u32 cardCnt; // カードコントロール + u32 op[2]; // コマンド +} GCDCardCtrlParam; + + +/* ROMヘッダ構造体 */ +typedef struct { + s8 titleName[12]; // ソフトタイトル名 + u32 initialCode; // イニシャルコード + + u16 makerCode; // メーカーコード + u8 machineCode; // 本体コード + u8 deviceType; // デバイスタイプ + + u8 nonVerReserved[4]; // 予約(4バイト) + u8 verDepReserved[4]; // 予約(バージョン依存、4バイト) + u16 wirelessSerialNo; + u16 softVersion:8; // ソフトバージョン + u16 compArm9BootArea:1; // ARM9ブートエリア圧縮フラグ + u16 compArm7BootArea:1; // ARM7ブートエリア圧縮フラグ + u16 :0; + + GCDBootUsrParam arm9; // ARM9ブート領域パラメータ + GCDBootUsrParam arm7; // ARM7ブート領域パラメータ + + u32 fileSysReserved[8]; // ファイルシステム予約(32バイト) + + GCDCardCnt romCtrl4Game; // ROMコントロール情報(NORMAL & GAMEモード) + GCDCardCnt romCtrl4Secure; // (SECUREモード) + u8 romCtrlReserved_l[4]; // 予約 + u16 secureCRC16; // セキュア領域CRC16bit + u16 romTimerLatency; // タイマーレイテンシ((サイクル数/256) - 2、3Dメモリ用) + u8 romReserved_h[8]; // 予約 + u32 romNormalModeKey[2]; // NORMALモード判定キー + + u8 reserved_80h[0x10]; // 予約領域(16バイト) + + u16 normalAreaOffset; // NORMAL領域 + u16 twlAreaOffset; // TWL専用領域 + + u8 reserved_94h[0x2c]; // 予約領域(44バイト) + + u16 nintendoLogo[0x9c/2]; // NINTENDOロゴ(156バイト) + u16 ninLogoCRC16; // NINTENDOロゴCRC16 + u16 headerCRC16; // ヘッダCRC16 + + u8 *dbgRomAddr; // デバッガモニタROMアドレス + s32 dbgRomSize; // デバッガモニタROMサイズ + u8 *dbgArm9RamAddr; // デバッガモニタARM9-RAMアドレス + u8 *dbgArm7RamAddr; // デバッガモニタARM7-RAMアドレス + + u8 reserved_170h[0x10]; // 予約領域(16バイト) +} +GCDRomHeaderDS; + +typedef struct { + // DS互換 + GCDRomHeaderDS l; + + // TWL拡張 + MIHeader_WramRegs w; + + u8 reserved_1b0h[0x10]; // 予約領域(16バイト) + + GCDBootUsrParam arm9ex; // ARM9拡張ブート領域パラメータ + GCDBootUsrParam arm7ex; // ARM7拡張ブート領域パラメータ + + u8 reserved_1e0h[0x1000 - 0x1e0 - 0x200]; // 予約領域 + + u8 acsign_contents[0x180]; // コンテンツ証明書 + + u8 acsign_header[0x80]; // ROMヘッダ電子署名 +} +GCDRomHeader; + + +/* セキュアワーク構造体 */ +typedef struct { + u8 recvRtcBuf[8]; // RTCデータ受信バッファ(要4バイト境界) +#if 0 + u16 flashCrc16; // フラッシュメモリCRC16 + u16 flashCount; // フラッシュメモリ 8バイトカウント + u32 flashBuf[2]; // フラッシュメモリ受信バッファ(要4バイト境界) + u32 flashKeyBuf[3]; // フラッシュメモリキー・テンポラリバッファ +#endif + u32 unScrambleKey[2]; // スクランブル解除キー(8バイト、MakeBlowfishTable() で算出) + s16 isGenUnScrambleKey; // スクランブル解除キー生成完了(MakeBlowfishTable() で設定) + + u32 va; // 下位24bit(24bit送信) + u32 vb; // 中間20bit(+未定義4bit、VBI: 32bit送信) + u32 vc_dummy; // 下位16bit(16bit送信) + u32 vd; // 下位24bit(24bit送信) + + GCDCardCnt cardCntBak4Secure; // SECUREコマンドパラメータ・バックアップ + s16 enableReadSecure; // SECURE領域リード・イネーブル(LoadSecure4Card() で設定) + u32 cardNormalModeKey[2]; // NORMALモード判定キー(MakeBlowfishTable() で算出) + u32 cardKeyBuf[3]; // カードキー・テンポラリバッファ + s32 secureSize; // SECURE領域サイズ + s16 sequenceNo4Secure; // シーケンス番号(SECURE用) + s16 segmentTblShift; // セグメント番号テーブルシフト値(SECURE用) + u8 *segmentTblp; // セグメントテーブルポインタ(SECURE用) + s16 numSecureSegment; // SECUREセグメント数(SECURE用) + s16 segmentOffset; // セグメントオフセット(SECURE用) + s16 blockOffset; // ブロックオフセット(SECURE用) + + // CardCtrlParam paramBak; // カードパラメータ 暗号化前バックアップ(SECURE用) + + BLOWFISH_CTX blowfishCardTable; // カード用Blowfishテーブル + BLOWFISH_CTX blowfishFlashTable; + + u8 bufEnd[4]; // セキュアワーク最終データ +} GCDSecureWork; + + +/* 共有ワーク構造体 */ +typedef struct { + u32 nCardID; // NORMALカードID(LoadCardHeader() で取得) + u32 sCardID; // SECUREカードID(CardTimerIntr4Secure() で取得) + u16 cardHeaderCrc16; // カードヘッダCRC16(LoadCardHeader() で算出) + u16 cardSecureCrc16; // カードSECURE領域CRC16(LoadSecure4Card() で算出) + s16 cardHeaderError; // カードヘッダエラー(CheckCardHeader() で設定) + s16 disableEncryptedCardData;// カードSECURE領域暗号化データ無効(DecryptObjectFile() で設定) + s16 cardSequenceNo; // カード読み込みシーケンス番号 + s16 enableCardNormalOnly; // カードNORMALモードのみ有効(LoadFlashDemo() で設定) + + // s16 isOnDebugger; // デバッガ上で動作中か + // s16 rtcError; // RTCエラー + + u32 recvRtc[2]; // RTCステータス1&タイマーデータ(要4バイト境界) + +} GCDSharedWork; + + +typedef union +{ + u64 dw; + u8 b[8]; +} +GCDCmd64; + +typedef struct +{ + u32 ctrl; + s32 cmdcount; + u32 latency; + u32 scramble; + u8 master; + u8 spi; + GCDSlot slot; +} +GCDCtrlRegs; + +typedef struct +{ + GCDCmd64 gcdOp; + u32 dmaNo; + u32 lastDmaNo; + BOOL ltckReq; + GCDCtrlRegs *gcdRegs; + GCDCtrlRegs nLoadTableRegs; + GCDCtrlRegs nSendOnlyOpRegs; + GCDCtrlRegs sSendOnlyOpRegs; + GCDCtrlRegs nIDRegs; + GCDCtrlRegs sIDRegs; + GCDCtrlRegs gIDRegs; + GCDCtrlRegs nReadShortHeaderRegs; + GCDCtrlRegs nReadRomRegs; + GCDCtrlRegs sReadRomRegs; + GCDCtrlRegs gReadRomRegs; + GCDCtrlRegs gWriteRomRegs; +} +GCDRomCtrls; + +typedef void (* GCDRomFuncp)( u32 romp, void *ramp, s32 size ); +typedef void (* GCDAsyncCoreFuncp)( GCDSlot slot, u32 romp, void *ramp, s32 size, GCDRomCtrls *ctrls ); + + +typedef struct +{ + u32 romp; // ROM offset + u8 *ramp; // RAM address + s32 restSize; // rest size + s32 oneShotSize; // one shot size + vs8 intrDone; + s8 isAsync; + volatile BOOL isPxiDone; + GCDRomCtrls ctrls; + GCDAsyncCoreFuncp funcp; +} +GCDWork; + + +#define GCD_DEFAULT_DMA_A_NO 7 +#define GCD_DEFAULT_DMA_B_NO 6 + +#define GCD_CHATTERING_CYCLES 0x200U + +#define GCD_ROM_PAGE_SIZE 0x200 // 512B +#define GCD_SECURE_SEGMENT_SIZE 0x1000 // 4KB +#define GCD_LOAD_TABLE_SIZE 0x2000 // 8KB + +#define GCD_NML_AREA_ALIGN 0x80000 // 512KB +#define GCD_TWL_AREA_ALIGN 0x80000 // 512KB +#define GCD_SECURE2_AREA_SIZE 0x4000 // 16KB +#define GCD_SECURE2_AREA_OFFSET 0x3000 // 12KB +#define GCD_GAME2_AREA_OFFSET 0x7000 // 28KB + + +// ROM area + +#define GCD_LOAD_TABLE_SIZE 0x2000 // 8KB +#define GCD_DS_ROM_HEADER_SIZE 0x200 // 512B +#define GCD_ROM_HEADER_SIZE 0x1000 // 4KB +#define GCD_SECURE_AREA_SIZE 0x4000 // 16KB + +#define GCD_SECURE_AREA_OFFSET 0x4000 +#define GCD_GAME_AREA_OFFSET 0x8000 + + +// ROM command + +#define GCDOP_BIT_NUM 64 + +// NORMAL mode + +#define GCDOP_N_OP_MASK 0xff00000000000000ULL +#define GCDOP_N_OP_SIZE 8 + +#define GCDOP_N_OP_RD_ROM_ID 0x9000000000000000ULL +#define GCDOP_N_OP_RD_PAGE 0x0000000000000000ULL +#define GCDOP_N_OP_WR_PAGE 0x8000000000000000ULL +#define GCDOP_N_OP_LD_TABLE 0x9f00000000000000ULL +#define GCDOP_N_OP_CHG_MODE 0x3c00000000000000ULL +#define GCDOP_N_OP_CHG2_MODE 0x3d00000000000000ULL + +#define GCDOP_N_RD_ROM_ID_PAD 0x00ffffffffffffffULL +#define GCDOP_N_RD_PAGE_PAD 0x00fffffeff00ffffULL +#define GCDOP_N_CHG_MODE_PAD 0x00000000f00000ffULL +#define GCDOP_N_CHG2_MODE_PAD GCDOP_N_CHG_MODE_PAD + +#define GCDOP_N_RD_PAGE_ADDR_SHIFT 33 +#define GCDOP_N_RD_PAGE_ADDR_SIZE 23 +#define GCDOP_N_RD_PAGE_ADDR_MASK 0x00fffffe00000000ULL + +#define GCDOP_N_VAE_SHIFT 32 +#define GCDOP_N_VAE_SIZE 24 +#define GCDOP_N_VAE_MASK 0x00ffffff00000000ULL + +#define GCDOP_N_VBI_SHIFT 8 +#define GCDOP_N_VBI_SIZE 20 +#define GCDOP_N_VBI_MASK 0x000000000fffff00ULL + + +// SECURE mode + +#define GCDOP_S_OP_MASK 0xf000000000000000ULL +#define GCDOP_S_OP_SIZE 4 + +#define GCDOP_S_OP_RD_ROM_ID 0x1000000000000000ULL +#define GCDOP_S_OP_RD_SEGMENT 0x2000000000000000ULL +#define GCDOP_S_OP_PNG_ON 0x4000000000000000ULL +#define GCDOP_S_OP_PNG_OFF 0x6000000000000000ULL +#define GCDOP_S_OP_CHG_MODE 0xa000000000000000ULL + +#define GCDOP_S_VA_SHIFT GCDOP_S_VB_SIZE +#define GCDOP_S_VA_SIZE 24 +#define GCDOP_S_VA_MASK 0x00000ffffff00000ULL + +#define GCDOP_S_VB_SHIFT 0 +#define GCDOP_S_VB_SIZE 20 +#define GCDOP_S_VB_MASK 0x00000000000fffffULL + +#define GCDOP_S_VC_SHIFT (GCDOP_S_VA_SIZE + GCDOP_S_VB_SIZE) +#define GCDOP_S_VC_SIZE 16 +#define GCDOP_S_VC_MASK 0x0ffff00000000000ULL + +#define GCDOP_S_VD_SHIFT GCDOP_S_VA_SHIFT +#define GCDOP_S_VD_SIZE GCDOP_S_VA_SIZE +#define GCDOP_S_VD_MASK GCDOP_S_VA_MASK + + +// GAME mode + +#define GCDOP_G_OP_MASK 0xff00000000000000ULL +#define GCDOP_G_OP_SIZE 8 + +#define GCDOP_G_OP_RD_ROM_ID 0xb800000000000000ULL +#define GCDOP_G_OP_RD_ROM_UID 0xb900000000000000ULL +#define GCDOP_G_OP_RD_PAGE 0xb700000000000000ULL +#define GCDOP_G_OP_RD_CACHE_START 0x5800000000000000ULL +#define GCDOP_G_OP_RD_CACHE 0x6000000000000000ULL +#define GCDOP_G_OP_RD_CACHE_LAST 0x6800000000000000ULL +#define GCDOP_G_OP_WR_PAGE 0x8000000000000000ULL + +#define GCDOP_G_RD_ROM_ID_PAD 0x00ffffffffffffffULL +#define GCDOP_G_RD_ROM_UID_PAD 0x00ffffffffffffffULL +#define GCDOP_G_RD_PAGE_PAD 0x00f0000000ffffffULL + +#define GCDOP_G_RD_PAGE_ADDR_SHIFT 33 +#define GCDOP_G_RD_PAGE_ADDR_SIZE 23 +#define GCDOP_G_RD_PAGE_ADDR_MASK 0x000ffffe00000000ULL + + +// ROM ID + +#define GCD_ROMID_1TROM_MASK 0x80000000UL +#define GCD_ROMID_TWLROM_MASK 0x40000000UL +#define GCD_ROMID_BADBLK_MASK 0x20000000UL +#define GCD_ROMID_SIZE_MASK 0x0000ff00UL + + + +/*---------------------------------------------------------------------------* + Name: GCDi_ReadRomID + + Description: read rom ID + sync version + + Arguments: None + + Returns: rom ID + *---------------------------------------------------------------------------*/ +u32 GCDi_ReadRomID( GCDSlot slot ); + +/*---------------------------------------------------------------------------* + Name: GCDi_ReadRom + + Description: read rom data + sync version + + Arguments: romp : rom offset + ramp : ram destination address + size : size (byte) + + Returns: None + *---------------------------------------------------------------------------*/ +void GCDi_ReadRom( GCDSlot slot, u32 romp, void *ramp, s32 size ); + +/*---------------------------------------------------------------------------* + Name: GCDi_ReadRomAsync + + Description: read rom data + async version + + Arguments: romp : rom offset + ramp : ram destination address + size : size (byte) + + Returns: None + *---------------------------------------------------------------------------*/ +void GCDi_ReadRomAsync( GCDSlot slot, u32 romp, void *ramp, s32 size ); + +/*---------------------------------------------------------------------------* + Name: GCDi_ReadNormalModeRom + + Description: read rom data on normal mode + sync version + + Arguments: romp : rom offset + ramp : ram destination address + size : size (byte) + + Returns: None + *---------------------------------------------------------------------------*/ +void GCDi_ReadNormalModeRom( GCDSlot slot, u32 romp, void *ramp, s32 size ); + +/*---------------------------------------------------------------------------* + Name: GCD_ReadNormalModeRomAsync + + Description: read rom data on normal mode + async version + + Arguments: romp : rom offset + ramp : ram destination address + size : size (byte) + + Returns: None + *---------------------------------------------------------------------------*/ +void GCD_ReadNormalModeRomAsync( GCDSlot slot, u32 romp, void *ramp, s32 size ); + +/*---------------------------------------------------------------------------* + Name: GCDi_ReadGameModeRom + + Description: read rom data on game mode + sync version + + Arguments: romp : rom offset + ramp : ram destination address + size : size (byte) + + Returns: None + *---------------------------------------------------------------------------*/ +void GCDi_ReadGameModeRom( GCDSlot slot, u32 romp, void *ramp, s32 size ); + +/*---------------------------------------------------------------------------* + Name: GCDi_ReadGameModeRomAsync + + Description: read rom data on game mode + async version + + Arguments: romp : rom offset + ramp : ram destination address + size : size (byte) + + Returns: None + *---------------------------------------------------------------------------*/ +void GCDi_ReadGameModeRomAsync( GCDSlot slot, u32 romp, void *ramp, s32 size ); + +/*---------------------------------------------------------------------------* + Name: GCDi_WriteGameModeRom + + Description: write rom data on game mode + sync version + + Arguments: romp : rom offset + ramp : ram destination address + size : size (byte) + + Returns: None + *---------------------------------------------------------------------------*/ +void GCDi_WriteGameModeRom( GCDSlot slot, u32 romp, void *ramp, s32 size ); + +/*---------------------------------------------------------------------------* + Name: GCDi_WriteGameModeRomAsync + + Description: write rom data on game mode + async version + + Arguments: romp : rom offset + ramp : ram destination address + size : size (byte) + + Returns: None + *---------------------------------------------------------------------------*/ +void GCDi_WriteGameModeRomAsync( GCDSlot slot, u32 romp, void *ramp, s32 size ); + +/*---------------------------------------------------------------------------* + Name: GCDi_Enable + + Description: Enable game card master control + + Arguments: None + + Returns: None + *---------------------------------------------------------------------------*/ +void GCDi_Enable( GCDSlot slot ); + +/*---------------------------------------------------------------------------* + Name: GCDi_Disable + + Description: disable game card master control + + Arguments: None + + Returns: None + *---------------------------------------------------------------------------*/ +void GCDi_Disable( GCDSlot slot ); + +/*---------------------------------------------------------------------------* + Name: GCD_SetPrimarySlot + + Description: set primary game card slot + + Arguments: primary slot + + Returns: None + *---------------------------------------------------------------------------*/ +void GCD_SetPrimarySlot( u8 slot ); + +/*---------------------------------------------------------------------------* + Name: GCD_GetPrimarySlot + + Description: get primary game card slot + + Arguments: None + + Returns: primary slot + *---------------------------------------------------------------------------*/ +u8 GCD_GetPrimarySlot(void ); + +/*---------------------------------------------------------------------------* + Name: GCD_Reset + + Description: reset game card + + Arguments: None + + Returns: None + *---------------------------------------------------------------------------*/ +void GCD_Reset( GCDSlot slot, u32 chat_cycles ); + +/*---------------------------------------------------------------------------* + Name: GCD_Reset + + Description: reset game card + + Arguments: None + + Returns: None + *---------------------------------------------------------------------------*/ +void GCD_ResetAll( u32 chat_cycles ); + +/*---------------------------------------------------------------------------* + Name: GCDi_SelectRom + + Description: select game card spi + + Arguments: None + + Returns: None + *---------------------------------------------------------------------------*/ +void GCDi_SelectRom( GCDSlot slot ); + +/*---------------------------------------------------------------------------* + Name: GCDi_SelectSpi + + Description: select game card spi + + Arguments: None + + Returns: None + *---------------------------------------------------------------------------*/ +void GCDi_SelectSpi( GCDSlot slot ); + +/*---------------------------------------------------------------------------* + Name: GCDi_SetDmaNo + + Description: set card dma number + + Arguments: dmaNo : dma channel number + + Returns: None + *---------------------------------------------------------------------------*/ +void GCDi_SetDmaNo( GCDSlot slot, u32 dmaNo ); + +/*---------------------------------------------------------------------------* + Name: GCDi_GetDmaNo + + Description: get card dma number + + Arguments: None + + Returns: dmaNo : dma channel number + *---------------------------------------------------------------------------*/ +u32 GCDi_GetDmaNo( GCDSlot slot ); + +/*---------------------------------------------------------------------------* + Name: GCDi_InterruptHandler + + Description: interrupt handler + + Arguments: None + + Returns: None + *---------------------------------------------------------------------------*/ +void GCDi_InterruptHandlerPRIME( void ); +void GCDi_InterruptHandlerSECOND( void ); +void GCDi_InterruptHandlerCommon( GCDSlot slot ); + +//================================================================================ +// WAIT/STOP +//================================================================================ +/*---------------------------------------------------------------------------* + Name: GCDi_IsBusy + + Description: check whether game card is busy or not + + Arguments: None + + Returns: TRUE if game card is busy, FALSE if not + *---------------------------------------------------------------------------*/ +BOOL GCDi_IsBusy( GCDSlot slot ); + +/*---------------------------------------------------------------------------* + Name: GCDi_IsDataReady + + Description: check whether data is ready or not + + Arguments: None + + Returns: TRUE if game card is busy, FALSE if not + *---------------------------------------------------------------------------*/ +BOOL GCDi_IsDataReady( GCDSlot slot ); + +/*---------------------------------------------------------------------------* + Name: GCDi_Stop + + Description: stop game card access + + Arguments: None + + Returns: None + *---------------------------------------------------------------------------*/ +void GCDi_Stop( GCDSlot slot ); + +/*---------------------------------------------------------------------------* + Name: GCDi_WaitCtrl + + Description: wait while game card is busy + + Arguments: None + + Returns: None + *---------------------------------------------------------------------------*/ +void GCDi_WaitCtrl( GCDSlot slot ); + +/*---------------------------------------------------------------------------* + Name: GCDi_WaitData + + Description: wait until data is ready + + Arguments: None + + Returns: None + *---------------------------------------------------------------------------*/ +void GCDi_WaitData( GCDSlot slot ); + +/*---------------------------------------------------------------------------* + Name: GCDi_WaitDma + + Description: wait for stopping game card DMA + + Arguments: dmaNo : DMA channel No. + + Returns: None + *---------------------------------------------------------------------------*/ +void GCDi_WaitDma( GCDSlot slot, u32 dmaNo ); + +/*---------------------------------------------------------------------------* + Name: GCDi_WaitInterrupt + + Description: wait for game card interrupt + + Arguments: None + + Returns: None + *---------------------------------------------------------------------------*/ +void GCDi_WaitInterrupt( GCDSlot slot ); + + +// internal + +BOOL GCDi_IsSecureInitialized( GCDSlot slot ); +BOOL GCDi_IsCtrApp( GCDSlot slot ); +BOOL GCDi_IsDsApp( GCDSlot slot ); + +u32 GCDi_ReadNormalModeID( GCDSlot slot ); +u32 GCDi_ReadSecureModeID( GCDSlot slot ); +u32 GCDi_ReadGameModeID( GCDSlot slot ); + +u32 GCDi_ReadRomIDCore( GCDSlot slot, GCDRomCtrls *ctrls ); +void GCDi_ReadRomCore( GCDSlot slot, void *ramp, s32 size, GCDRomCtrls *ctrls ); +BOOL GCDi_ReadRomCommonPreCore( GCDSlot slot, u32 romp, void *ram, s32 size ); +void GCDi_WriteRomCore( GCDSlot slot, void *ramp, s32 size, GCDRomCtrls *ctrls ); + +void GCDi_ReadNormalModeRomCore( GCDSlot slot, u32 romp, void *ramp, s32 size, GCDRomCtrls *ctrls ); + +void GCDi_InitSecureParam( GCDSlot slot, BOOL twl_ex ); +void GCDi_InitPngIntf( GCDSlot slot ); +void GCDi_SetInitCardOpBlowfish( void (*p)( void ) ); +void GCDi_SetDecryptObjectFilep( void (*p)( void ) ); +void GCDi_SetSetPngIntf( void (*p)( u32 pnA_l, u8 pnA_h, u32 pnB_l, u8 pnB_h ) ); +void GCDi_SetReadSecureModeIDCorep( u32 (p)( GCDRomCtrls *ctrls ) ); +void GCDi_SetReadSecureModeRomCorep( void (p)( u32 romp, void *ramp, s32 size, GCDRomCtrls *ctrls ) ); +void GCDi_SetGetOneShotSizeFromCtrlp( s32 (p)( u32 ctrl ) ); + +void GCDi_SetSendOnlyCardOpCorep( void (*p)( GCDRomCtrls *ctrls ) ); +void GCDi_ChangeIntoSecureMode( GCDSlot slot ); +void GCDi_ChangeIntoSecure2Mode( GCDSlot slot ); +void GCDi_SendPngON( GCDSlot slot ); +void GCDi_SendPngOFF( GCDSlot slot ); +void GCDi_ChangeIntoGameMode( GCDSlot slot ); +void GCDi_ReadCardSegmentAsync4Secure( GCDSlot slot, BOOL twl_ex ); + +void GCDi_InitCardOpBlowfish( GCDSlot slot ); +void GCDi_InitCardOpBlowfishDS( GCDSlot slot ); +void GCDi_DecryptObjectFile( GCDSlot slot ); +void GCDi_DecryptObjectFileDS( GCDSlot slot ); + +void GCD_SetInterrupt( GCDSlot slot, u32 romp, void *ramp, s32 size, GCDWork *wp ); + +void* GCDi_GetRomHeaderAddr( GCDSlot slot ); + + +extern u32 GCDi_HeaderBuf[2][ GCD_ROM_HEADER_SIZE/sizeof(u32) ]; +extern u32 GCDi_SecureAreaBuf[2][ GCD_SECURE_AREA_SIZE/sizeof(u32) ]; +extern u32 GCDi_Secure2AreaBuf[2][ GCD_SECURE_AREA_SIZE/sizeof(u32) ]; + +extern GCDSharedWork GCDi_SharedWork[2]; +extern GCDWork GCDi_Work[2]; + +// 初期化テーブル +extern const BLOWFISH_CTX GCDi_BlowfishInitTableDS; +extern const BLOWFISH_CTX GCDi_BlowfishInitTableGCDFIRM; + +#ifdef __cplusplus +} /* extern "C" */ + +#endif + +/* FIRM_GCD_GCD_H_ */ +#endif diff --git a/include/firm/gcd/gcd_misc.h b/include/firm/gcd/gcd_misc.h new file mode 100644 index 00000000..cf136ea3 --- /dev/null +++ b/include/firm/gcd/gcd_misc.h @@ -0,0 +1,468 @@ +/*---------------------------------------------------------------------------* + Project: TwlFirm - GCD - include + File: ngcd.h + + Copyright 2007 Nintendo. All rights reserved. + + These coded instructions, statements, and computer programs contain + proprietary information of Nintendo of America Inc. and/or Nintendo + Company Ltd., and are protected by Federal copyright law. They may + not be disclosed to third parties or copied or duplicated in any form, + in whole or in part, without the prior written consent of Nintendo. + + $Log: $ + $NoKeywords: $ + *---------------------------------------------------------------------------*/ + +#ifndef FIRM_GCD_GCD_MISC_H_ +#define FIRM_GCD_GCD_MISC_H_ + +#include +#include "./gcd.h" + + +#ifdef __cplusplus +extern "C" { +#endif + + +typedef enum +{ + GCD_PAGE_0 = 0x0UL << REG_MI_MCCNT1_PC_SHIFT, + GCD_PAGE_1 = 0x1UL << REG_MI_MCCNT1_PC_SHIFT, + GCD_PAGE_2 = 0x2UL << REG_MI_MCCNT1_PC_SHIFT, + GCD_PAGE_4 = 0x3UL << REG_MI_MCCNT1_PC_SHIFT, + GCD_PAGE_8 = 0x4UL << REG_MI_MCCNT1_PC_SHIFT, + GCD_PAGE_16 = 0x5UL << REG_MI_MCCNT1_PC_SHIFT, + GCD_PAGE_32 = 0x6UL << REG_MI_MCCNT1_PC_SHIFT, + GCD_PAGE_STAT = 0x7UL << REG_MI_MCCNT1_PC_SHIFT +} +GCDPageCount; + +typedef enum +{ + GCD_CKT_150NS = 0x0UL << REG_MI_MCCNT1_CT_SHIFT, + GCD_CKT_240NS = 0x1UL << REG_MI_MCCNT1_CT_SHIFT +} +GCDClockType; + +typedef enum +{ + GCD_RW_READ = 0x0UL << REG_MI_MCCNT1_WR_SHIFT, + GCD_RW_WRITE = 0x1UL << REG_MI_MCCNT1_WR_SHIFT +} +GCDRw; + +typedef enum +{ + GCD_RESET_LO = 0x0UL << REG_MI_MCCNT1_RESB_SHIFT, + GCD_RESET_HI = 0x1UL << REG_MI_MCCNT1_RESB_SHIFT +} +GCDReset; + +typedef enum +{ + GCD_LTCK_DISABLE = 0x0UL << REG_MI_MCCNT1_TRM_SHIFT, + GCD_LTCK_ENABLE = 0x1UL << REG_MI_MCCNT1_TRM_SHIFT +} +GCDLtClkEnable; + +typedef struct +{ + u32 ctrl; + u8 master; + u8 spi; +} +NGCDCtrlRegs; + + +// PXIでの通信プロトコル関連定義 +#define GCD_PXI_COMMAND_MASK 0x0000003f // 開始ワードのコマンド部 +#define GCD_PXI_COMMAND_SHIFT 0 +#define GCD_PXI_COMMAND_SIZE 6 +#define GCD_PXI_COMMAND_PARAM_MASK 0x03ffffc0 // 開始ワードのパラメータ部 +#define GCD_PXI_COMMAND_PARAM_SHIFT 6 +#define GCD_PXI_COMMAND_PARAM_SIZE 20 + +typedef union +{ + struct + { + u32 cmd:GCD_PXI_COMMAND_SIZE; + u32 param:GCD_PXI_COMMAND_PARAM_SIZE-1; + u32 slot:1; + } + e; + u32 raw; +} +GCDPxiCmd; + +// PXI経由で発行される命令 +#define GCD_PXI_COMMAND_RESET 0x01 +#define GCD_PXI_COMMAND_LOADED 0x02 + + +/*---------------------------------------------------------------------------* + Name: GCD_Init + + Description: initialize for game card access + + Arguments: None + + Returns: None + *---------------------------------------------------------------------------*/ +void GCD_Init( void ); + +/*---------------------------------------------------------------------------* + Name: GCDi_Init + + Description: initialize for game card access + + Arguments: None + + Returns: None + *---------------------------------------------------------------------------*/ +void GCDi_Init( BOOL reset ); + +/*---------------------------------------------------------------------------* + Name: GCD_ReadRomID + + Description: read rom ID + sync version + + Arguments: None + + Returns: rom ID + *---------------------------------------------------------------------------*/ +u32 GCD_ReadRomID( GCDSlot slot ); + +/*---------------------------------------------------------------------------* + Name: GCD_ReadRom + + Description: read rom data + sync version + + Arguments: romp : rom offset + ramp : ram destination address + size : size (byte) + + Returns: None + *---------------------------------------------------------------------------*/ +void GCD_ReadRom( GCDSlot slot, u32 romp, void *ramp, s32 size ); + +/*---------------------------------------------------------------------------* + Name: GCD_ReadRomAsync + + Description: read rom data + async version + + Arguments: romp : rom offset + ramp : ram destination address + size : size (byte) + + Returns: None + *---------------------------------------------------------------------------*/ +void GCD_ReadRomAsync( GCDSlot slot, u32 romp, void *ramp, s32 size ); + +/*---------------------------------------------------------------------------* + Name: GCD_ReadNormalModeRom + + Description: read rom data on normal mode + sync version + + Arguments: romp : rom offset + ramp : ram destination address + size : size (byte) + + Returns: None + *---------------------------------------------------------------------------*/ +void GCD_ReadNormalModeRom( GCDSlot slot, u32 romp, void *ramp, s32 size ); + +/*---------------------------------------------------------------------------* + Name: GCD_ReadGameModeRom + + Description: read rom data on game mode + sync version + + Arguments: romp : rom offset + ramp : ram destination address + size : size (byte) + + Returns: None + *---------------------------------------------------------------------------*/ +void GCD_ReadGameModeRom( GCDSlot slot, u32 romp, void *ramp, s32 size ); + +/*---------------------------------------------------------------------------* + Name: GCD_ReadRomPreCore + + Description: read rom data + sync version + + Arguments: romp : rom offset + ramp : ram destination address + size : size (byte) + + Returns: None + *---------------------------------------------------------------------------*/ +BOOL GCD_ReadRomPreCore( GCDSlot slot, u32 romp, void *ramp, s32 size ); + +/*---------------------------------------------------------------------------* + Name: GCD_ReadGameModeRomAsync + + Description: read rom data on game mode + async version + + Arguments: romp : rom offset + ramp : ram destination address + size : size (byte) + + Returns: None + *---------------------------------------------------------------------------*/ +void GCD_ReadGameModeRomAsync( GCDSlot slot, u32 romp, void *ramp, s32 size ); + +/*---------------------------------------------------------------------------* + Name: GCD_WriteGameModeRom + + Description: write rom data on game mode + sync version + + Arguments: romp : rom offset + ramp : ram destination address + size : size (byte) + + Returns: None + *---------------------------------------------------------------------------*/ +void GCD_WriteGameModeRom( GCDSlot slot, u32 romp, void *ramp, s32 size ); + +/*---------------------------------------------------------------------------* + Name: GCD_WriteGameModeRomAsync + + Description: write rom data on game mode + async version + + Arguments: romp : rom offset + ramp : ram destination address + size : size (byte) + + Returns: None + *---------------------------------------------------------------------------*/ +void GCD_WriteGameModeRomAsync( GCDSlot slot, u32 romp, void *ramp, s32 size ); + +/*---------------------------------------------------------------------------* + Name: GCDi_Enable + + Description: Enable game card master control + + Arguments: None + + Returns: None + *---------------------------------------------------------------------------*/ +void GCDi_Enable( GCDSlot slot ); + +/*---------------------------------------------------------------------------* + Name: GCDi_Disable + + Description: disable game card master control + + Arguments: None + + Returns: None + *---------------------------------------------------------------------------*/ +void GCDi_Disable( GCDSlot slot ); + +//================================================================================ +// WAIT/STOP +//================================================================================ +/*---------------------------------------------------------------------------* + Name: GCD_IsExisting + + Description: get whether cartridge exists + + Arguments: None + + Returns: TRUE if game card is busy, FALSE if not + *---------------------------------------------------------------------------*/ +static inline BOOL GCD_IsExisting( GCDSlot slot ) +{ + s32 ofs = (GCD_GetPrimarySlot() ^ slot) * 4; + s32 r = ~reg_MI_MC_DET & (REG_MI_MC_DET_DET1_MASK << ofs); + return r >> (REG_MI_MC_DET_DET1_SHIFT + ofs); +} + +/*---------------------------------------------------------------------------* + Name: GCD_SetDetectMode + + Description: + + Arguments: None + + Returns: None + *---------------------------------------------------------------------------*/ +static inline void GCD_SetDetectMode( GCDSlot slot, u32 mode ) +{ + s32 ofs = (GCD_GetPrimarySlot() ^ slot) * 4; + s32 others = reg_MI_MC_DET & ~(REG_MI_MC_DET_MODE1_MASK << ofs); + reg_MI_MC_DET = (u8)((mode << (REG_MI_MC_DET_MODE1_SHIFT + ofs)) | others); +} + +/*---------------------------------------------------------------------------* + Name: GCD_GetDetectMode + + Description: + + Arguments: None + + Returns: None + *---------------------------------------------------------------------------*/ +static inline u8 GCD_GetDetectMode( GCDSlot slot ) +{ + s32 ofs = (GCD_GetPrimarySlot() ^ slot) * 4; + return (u8)((reg_MI_MC_DET & (REG_MI_MC_DET_MODE1_MASK << ofs)) + >> (REG_MI_MC_DET_MODE1_SHIFT + ofs)); +} + +/*---------------------------------------------------------------------------* + Name: GCD_SetChatCounter + + Description: + + Arguments: None + + Returns: None + *---------------------------------------------------------------------------*/ +static inline void GCD_SetChatCounter( u16 value ) +{ + reg_MI_MC_CHT = value; +} + +/*---------------------------------------------------------------------------* + Name: GCD_IsBusy + + Description: check whether game card is busy or not + + Arguments: None + + Returns: TRUE if game card is busy, FALSE if not + *---------------------------------------------------------------------------*/ +BOOL GCD_IsBusy( GCDSlot slot ); + +/*---------------------------------------------------------------------------* + Name: GCD_IsDataReady + + Description: check whether data is ready or not + + Arguments: None + + Returns: TRUE if game card is busy, FALSE if not + *---------------------------------------------------------------------------*/ +BOOL GCD_IsDataReady( GCDSlot slot ); + +/*---------------------------------------------------------------------------* + Name: GCD_Stop + + Description: stop game card access + + Arguments: None + + Returns: None + *---------------------------------------------------------------------------*/ +void GCD_Stop( GCDSlot slot ); + +/*---------------------------------------------------------------------------* + Name: GCD_WaitRomAsync + + Description: wait for game card async access + + Arguments: None + + Returns: None + *---------------------------------------------------------------------------*/ +void GCD_WaitRomAsync( GCDSlot slot ); + +/*---------------------------------------------------------------------------* + Name: GCDi_SelectRegAddr + + Description: + + Arguments: None + + Returns: None + *---------------------------------------------------------------------------*/ +static inline void* GCDi_SelectRegAddr( GCDSlot slot, u32 base ) +{ + u32 addr = base; + + if ( slot == GCD_SECONDARY_SLOT ) + { + addr += REG_MCCNT0_B_ADDR - REG_MCCNT0_ADDR; + } + + return (void*)addr; +} + +/*---------------------------------------------------------------------------* + Name: GCDi_SelectIrqMask + + Description: + + Arguments: None + + Returns: None + *---------------------------------------------------------------------------*/ +static inline u32 GCDi_SelectIrqMask( GCDSlot slot, u32 base_mask ) +{ + u32 mask = base_mask; + + if ( slot == GCD_SECONDARY_SLOT ) + { + switch ( mask ) + { + case OS_IE_CARD_DATA: + mask = OS_IE_CARD_B_DATA; + break; + case OS_IE_CARD_IREQ: + mask = OS_IE_CARD_B_IREQ; + break; + case OS_IE_CARD_DET: + mask = OS_IE_CARD_B_DET; + break; + } + } + + return mask; +} + + +// internal + +u32 GCD_ReadNormalModeID( GCDSlot slot ); +u32 GCD_ReadSecureModeID( GCDSlot slot ); +u32 GCD_ReadGameModeID( GCDSlot slot ); + +void GCDi_SetOp( GCDSlot slot, GCDCmd64* op ); +void GCDi_SetCtrl( GCDSlot slot, GCDCtrlRegs* regs ); +void GCDi_GenCtrl( GCDCtrlRegs* regs, + GCDRw rw, + GCDPageCount pcount, + GCDClockType ckt, u32 lt1, u32 lt2, + BOOL cpn, BOOL dpn, BOOL csc, BOOL dsc ); +s32 GCDi_GetOneShotSizeFromCtrl( u32 ctrl ); +void GCD_SendOnlyCardOpCore( GCDSlot slot, GCDRomCtrls *ctrls ); +u32 GCDi_ReadRomIDCore( GCDSlot slot, GCDRomCtrls *ctrls ); +void GCD_ReadRomCore( GCDSlot slot, u32 romp, void *ramp, s32 size, GCDRomCtrls *ctrls ); +void GCD_WaitRomAsyncCore( GCDSlot slot ); + +void GCDi_WaitCtrl( GCDSlot slot ); +void GCDi_WaitData( GCDSlot slot ); +void GCDi_WaitDma( GCDSlot slot, u32 dmaNo ); +void GCDi_WaitInterrupt( GCDSlot slot ); + +void GCDi_SendtoPxi(u32 data); + +#ifdef __cplusplus +} /* extern "C" */ + +#endif + +/* FIRM_GCD_GCD_MISC_H_ */ +#endif diff --git a/include/firm/hw/ARM7/mmap_firm.h b/include/firm/hw/ARM7/mmap_firm.h new file mode 100644 index 00000000..dfec8855 --- /dev/null +++ b/include/firm/hw/ARM7/mmap_firm.h @@ -0,0 +1,44 @@ +/*---------------------------------------------------------------------------* + Project: TwlFirm - HW - include + File: mmap_firm.h + + Copyright 2007 Nintendo. All rights reserved. + + These coded instructions, statements, and computer programs contain + proprietary information of Nintendo of America Inc. and/or Nintendo + Company Ltd., and are protected by Federal copyright law. They may + not be disclosed to third parties or copied or duplicated in any form, + in whole or in part, without the prior written consent of Nintendo. + + $Log: $ + $NoKeywords: $ + *---------------------------------------------------------------------------*/ +#ifndef FIRM_HW_MMAP_FIRM_H_ +#define FIRM_HW_MMAP_FIRM_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +//------------------------------------- NORFIRM +#define HW_NORFIRM HW_WRAM +#define HW_NORFIRM_END (HW_NORFIRM + HW_NORFIRM_SIZE) +#define HW_NORFIRM_SIZE (HW_WRAM_0_SIZE + HW_WRAM_1_SIZE + HW_WRAM_A_SIZE_MAX + HW_WRAM_B_SIZE_MAX) + +//------------------------------------- NORFIRM_WRAM_ABC +#define HW_NORFIRM_WRAM_A_MAP_END (HW_WRAM_AREA_END - HW_PRV_WRAM_SIZE) +#define HW_NORFIRM_WRAM_B_MAP_END HW_NORFIRM_WRAM_A_MAP_END +#define HW_NORFIRM_WRAM_C_MAP_END HW_NORFIRM_WRAM_A_MAP_END + +//------------------------------------- HW_NORFIRM_FROM_BROM_BUF +#define HW_NORFIRM_FROM_BROM_BUF (HW_NORFIRM_FROM_BROM_BUF_END - HW_NORFIRM_FROM_BROM_BUF_SIZE) +#define HW_NORFIRM_FROM_BROM_BUF_END (HW_WRAM_AREA_END - 0x1000) // END - 4KB +#define HW_NORFIRM_FROM_BROM_BUF_SIZE 0x3000 // 12KB + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +/* FIRM_HW_MMAP_FIRM_H_ */ +#endif diff --git a/include/firm/hw/ARM9/mmap_firm.h b/include/firm/hw/ARM9/mmap_firm.h new file mode 100644 index 00000000..cecda941 --- /dev/null +++ b/include/firm/hw/ARM9/mmap_firm.h @@ -0,0 +1,44 @@ +/*---------------------------------------------------------------------------* + Project: TwlFirm - HW - include + File: mmap_firm.h + + Copyright 2007 Nintendo. All rights reserved. + + These coded instructions, statements, and computer programs contain + proprietary information of Nintendo of America Inc. and/or Nintendo + Company Ltd., and are protected by Federal copyright law. They may + not be disclosed to third parties or copied or duplicated in any form, + in whole or in part, without the prior written consent of Nintendo. + + $Log: $ + $NoKeywords: $ + *---------------------------------------------------------------------------*/ +#ifndef FIRM_HW_MMAP_FIRM_H_ +#define FIRM_HW_MMAP_FIRM_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +//------------------------------------- NORFIRM +#define HW_NORFIRM HW_WRAM_EX +#define HW_NORFIRM_END (HW_NORFIRM + HW_NORFIRM_SIZE) +#define HW_NORFIRM_SIZE HW_WRAM_C_SIZE_MAX + +//------------------------------------- NORFIRM_WRAM_ABC +#define HW_NORFIRM_WRAM_A_MAP_END HW_WRAM_AREA_END +#define HW_NORFIRM_WRAM_B_MAP_END HW_NORFIRM_WRAM_A_MAP_END +#define HW_NORFIRM_WRAM_C_MAP_END HW_NORFIRM_WRAM_A_MAP_END + +//------------------------------------- HW_NORFIRM_FROM_BROM_BUF +#define HW_NORFIRM_FROM_BROM_BUF (HW_NORFIRM_FROM_BROM_BUF_END - HW_NORFIRM_FROM_BROM_BUF_SIZE) +#define HW_NORFIRM_FROM_BROM_BUF_END (HW_ITCM_END - 0x1000) // END - 4KB +#define HW_NORFIRM_FROM_BROM_BUF_SIZE 0x3000 // 12KB + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +/* FIRM_HW_MMAP_FIRM_H_ */ +#endif diff --git a/include/firm/memorymap.h b/include/firm/memorymap.h new file mode 100644 index 00000000..a6150eeb --- /dev/null +++ b/include/firm/memorymap.h @@ -0,0 +1,28 @@ +/*---------------------------------------------------------------------------* + Project: TwlFirm - include - HW + File: memorymap.h + + Copyright 2007 Nintendo. All rights reserved. + + These coded instructions, statements, and computer programs contain + proprietary information of Nintendo of America Inc. and/or Nintendo + Company Ltd., and are protected by Federal copyright law. They may + not be disclosed to third parties or copied or duplicated in any form, + in whole or in part, without the prior written consent of Nintendo. + + $Log: $ + $NoKeywords: $ + *---------------------------------------------------------------------------*/ +#ifndef FIRM_MEMORYMAP_H_ +#define FIRM_MEMORYMAP_H_ + +#include + +#ifdef SDK_ARM9 +#include +#else //SDK_ARM7 +#include +#endif + +/* FIRM_MEMORYMAP_H_ */ +#endif diff --git a/include/firm/mi.h b/include/firm/mi.h new file mode 100644 index 00000000..e644867d --- /dev/null +++ b/include/firm/mi.h @@ -0,0 +1,25 @@ +/*---------------------------------------------------------------------------* + Project: TwlFirm - include - MI + File: mi.h + + Copyright 2007 Nintendo. All rights reserved. + + These coded instructions, statements, and computer programs contain + proprietary information of Nintendo of America Inc. and/or Nintendo + Company Ltd., and are protected by Federal copyright law. They may + not be disclosed to third parties or copied or duplicated in any form, + in whole or in part, without the prior written consent of Nintendo. + + $Log: $ + $NoKeywords: $ + *---------------------------------------------------------------------------*/ + +#ifndef FIRM_MI_H_ +#define FIRM_MI_H_ + +#include + +#include + +/* FIRM_MI_H_ */ +#endif diff --git a/include/firm/mi/mainMemory.h b/include/firm/mi/mainMemory.h new file mode 100644 index 00000000..29b47e62 --- /dev/null +++ b/include/firm/mi/mainMemory.h @@ -0,0 +1,79 @@ +/*---------------------------------------------------------------------------* + Project: TwlFirm - include - MI + File: mainMemory.h + + Copyright 2007 Nintendo. All rights reserved. + + These coded instructions, statements, and computer programs contain + proprietary information of Nintendo of America Inc. and/or Nintendo + Company Ltd., and are protected by Federal copyright law. They may + not be disclosed to third parties or copied or duplicated in any form, + in whole or in part, without the prior written consent of Nintendo. + + $Log: $ + $NoKeywords: $ + *---------------------------------------------------------------------------*/ + +#ifndef FIRM_MI_MAINMEMORY_H_ +#define FIRM_MI_MAINMEMORY_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +//---------------------------------------------------------------------- +// メインメモリ CRコマンド +//---------------------------------------------------------------------- + +// for DS-PSRAM + +// CR0 +#define MMEM_DCR0_PARTIAL_REFRESH_NONE 0x0003 // パーシャルリフレッシュ無し +#define MMEM_DCR0_BURST_CONTINUOUS 0x001c // 連続バースト(224バイト) +#define MMEM_DCR0_BURST_MODE 0x0000 // バーストモード +#define MMEM_DCR0_PAGE_MODE 0x0020 // ページモード +#define MMEM_DCR0_SB1 0xffc0 // 1固定 + +// CR1 +#define MMEM_DCR1_1ST_R4_W3 0x0002 // 1stR/W = 4/3 +#define MMEM_DCR1_BURST_WRITE 0x0000 // バーストライト +#define MMEM_DCR1_CLOCK_TRIGGER_UP 0x0100 // クロック立上り +#define MMEM_DCR1_BURST_LINER 0x0200 // バーストリニア・シーケンス +#define MMEM_DCR1_SB1 0xe430 // 1固定 + +// CR2 +#define MMEM_DCR2_SB1 0x004003fe // 1固定 +#define MMEM_DCR2_CLOCK_TRIGGER_UP 0x00000400 // クロック立上り +#define MMEM_DCR2_BURST_WRITE 0x00000000 // バーストライト +#define MMEM_DCR2_BURST_LINER 0x00001000 // バーストリニア・シーケンス +#define MMEM_DCR2_1ST_R4_W3 0x00004000 // 1stR/W = 4/3 +#define MMEM_DCR2_BURST_MODE 0x00000000 // バーストモード +#define MMEM_DCR2_PAGE_MODE 0x00010000 // ページモード +#define MMEM_DCR2_BURST_CONTINUOUS 0x000e0000 // 連続バースト(224バイト) +#define MMEM_DCR2_PARTIAL_REFRESH_NONE 0x00300000 // パーシャルリフレッシュ無し + +// for TWL-PSRAM + +#define MMEM_TCR0 0xFFFF +#define MMEM_TCR1 0xFFDF +#define MMEM_TCR2 0xFFEA + + +/*---------------------------------------------------------------------------* + Name: MIi_InitMainMemCR + + Description: change main memory into the burst mode + + Arguments: None + + Returns: None + *---------------------------------------------------------------------------*/ +void MIi_InitMainMemCR( void ); + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +/* FIRM_MI_MAINMEMORY_H_ */ +#endif diff --git a/include/firm/misc.h b/include/firm/misc.h new file mode 100644 index 00000000..21ff27e0 --- /dev/null +++ b/include/firm/misc.h @@ -0,0 +1,32 @@ +/*---------------------------------------------------------------------------* + Project: TwlFirm - include - + File: misc.h + + Copyright 2007 Nintendo. All rights reserved. + + These coded instructions, statements, and computer programs contain + proprietary information of Nintendo of America Inc. and/or Nintendo + Company Ltd., and are protected by Federal copyright law. They may + not be disclosed to third parties or copied or duplicated in any form, + in whole or in part, without the prior written consent of Nintendo. + + $Log: $ + $NoKeywords: $ + *---------------------------------------------------------------------------*/ +#ifndef FIRM_MISC_H_ +#define FIRM_MISC_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define offsetof(t, memb) ((size_t)(&(((t *)0)->memb))) + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +/* FIRM_MISC_H_ */ +#endif diff --git a/include/firm/nvram.h b/include/firm/nvram.h new file mode 100644 index 00000000..c9b8ffd4 --- /dev/null +++ b/include/firm/nvram.h @@ -0,0 +1,42 @@ +/*---------------------------------------------------------------------------* + Project: TwlFirm - NVRAM + File: nvram.h + + Copyright 2007 Nintendo. All rights reserved. + + These coded instructions, statements, and computer programs contain + proprietary information of Nintendo of America Inc. and/or Nintendo + Company Ltd., and are protected by Federal copyright law. They may + not be disclosed to third parties or copied or duplicated in any form, + in whole or in part, without the prior written consent of Nintendo. + + $Log: $ + $NoKeywords: $ + *---------------------------------------------------------------------------*/ + +#ifndef FIRM_NVRAM_H_ +#define FIRM_NVRAM_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef SDK_ARM9 +#else // SDK_ARM7 + +/*---------------------------------------------------------------------------* + 関数定義 + *---------------------------------------------------------------------------*/ +void NVRAMi_Read(u32 address, void *buf, u32 size); +void NVRAMi_Write(u32 address, void *buf, u32 size); + +#endif + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +/* FIRM_NVRAM_H_ */ +#endif diff --git a/include/firm/os.h b/include/firm/os.h new file mode 100644 index 00000000..3d1afa41 --- /dev/null +++ b/include/firm/os.h @@ -0,0 +1,37 @@ +/*---------------------------------------------------------------------------* + Project: TwlFirm - OS + File: os.h + + Copyright 2007 Nintendo. All rights reserved. + + These coded instructions, statements, and computer programs contain + proprietary information of Nintendo of America Inc. and/or Nintendo + Company Ltd., and are protected by Federal copyright law. They may + not be disclosed to third parties or copied or duplicated in any form, + in whole or in part, without the prior written consent of Nintendo. + + $Log: $ + $NoKeywords: $ + *---------------------------------------------------------------------------*/ + +#ifndef FIRM_OS_H_ +#define FIRM_OS_H_ + +#include + +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +/* FIRM_OS_H_ */ +#endif diff --git a/include/firm/os/common/boot.h b/include/firm/os/common/boot.h new file mode 100644 index 00000000..6f8c59f6 --- /dev/null +++ b/include/firm/os/common/boot.h @@ -0,0 +1,74 @@ +/*---------------------------------------------------------------------------* + Project: TwlFirm - OS - include + File: boot.h + + Copyright 2007 Nintendo. All rights reserved. + + These coded instructions, statements, and computer programs contain + proprietary information of Nintendo of America Inc. and/or Nintendo + Company Ltd., and are protected by Federal copyright law. They may + not be disclosed to third parties or copied or duplicated in any form, + in whole or in part, without the prior written consent of Nintendo. + + $Log: $ + $NoKeywords: $ + *---------------------------------------------------------------------------*/ +#ifndef FIRM_OS_BOOT_H_ +#define FIRM_OS_BOOT_H_ + +#include +#include +#include +#include //from_brom.h用 +#include + +#include + +#ifdef __cplusplus +extern "C" { +#endif + + +/*---------------------------------------------------------------------------* + Name: OSi_Boot + + Description: boot firm + + Arguments: entry : entry point + + Returns: None + *---------------------------------------------------------------------------*/ +void OSi_Boot( void* entry, MIHeader_WramRegs* w, BOOL to_firm ); + +/*---------------------------------------------------------------------------* + Name: OSi_Finalize + + Description: finalize + + Arguments: None + + Returns: None + *---------------------------------------------------------------------------*/ +void OSi_Finalize(void); + +/*---------------------------------------------------------------------------* + Name: OSi_GetFromBromAddr + + Description: data address from bootrom to norfirm + + Arguments: None + + Returns: address + *---------------------------------------------------------------------------*/ +static inline OSFromBromBuf* OSi_GetFromBromAddr( void ) +{ + return (OSFromBromBuf*)HW_NORFIRM_FROM_BROM_BUF; +} + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +/* FIRM_OS_BOOT_H_ */ +#endif diff --git a/include/firm/os/common/init.h b/include/firm/os/common/init.h new file mode 100644 index 00000000..b6bf0f87 --- /dev/null +++ b/include/firm/os/common/init.h @@ -0,0 +1,42 @@ +/*---------------------------------------------------------------------------* + Project: TwlFirm - OS - include + File: init.h + + Copyright 2007 Nintendo. All rights reserved. + + These coded instructions, statements, and computer programs contain + proprietary information of Nintendo of America Inc. and/or Nintendo + Company Ltd., and are protected by Federal copyright law. They may + not be disclosed to third parties or copied or duplicated in any form, + in whole or in part, without the prior written consent of Nintendo. + + $Log: $ + $NoKeywords: $ + *---------------------------------------------------------------------------*/ +#ifndef FIRM_OS_INIT_H_ +#define FIRM_OS_INIT_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/*---------------------------------------------------------------------------* + Name: OS_InitNOR + + Description: initialize sdk os for norfirm + + Arguments: None + + Returns: None + *---------------------------------------------------------------------------*/ +void OS_InitNOR(void); + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +/* FIRM_OS_INIT_H_ */ +#endif diff --git a/include/firm/os/common/systemCall.h b/include/firm/os/common/systemCall.h new file mode 100644 index 00000000..b81d50bd --- /dev/null +++ b/include/firm/os/common/systemCall.h @@ -0,0 +1,94 @@ +/*---------------------------------------------------------------------------* + Project: TwlFirm - OS - include + File: systemCall.h + + Copyright 2007 Nintendo. All rights reserved. + + These coded instructions, statements, and computer programs contain + proprietary information of Nintendo of America Inc. and/or Nintendo + Company Ltd., and are protected by Federal copyright law. They may + not be disclosed to third parties or copied or duplicated in any form, + in whole or in part, without the prior written consent of Nintendo. + + $Log: $ + $NoKeywords: $ + *---------------------------------------------------------------------------*/ + +#ifndef FIRM_OS_SYSTEMCALL_H_ +#define FIRM_OS_SYSTEMCALL_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + + +#define SVC_ID_PREMASK 0x3f +#define SVC_ID_SHIFT 1 + +#define SVC_ID_SEMIHOST_ARM 0x12 +#define SVC_ID_SEMIHOST_THUMB 0xab + + +typedef enum +{ + // TWL + SVC_ID_INIT_SIGN_HEAP = 32, + SVC_ID_DEC_RSA = 33, + SVC_ID_DEC_SIGN = 34, + SVC_ID_DEC_SIGN_DER = 35, + SVC_ID_SHA1_INIT = 36, + SVC_ID_SHA1_UPDATE = 37, + SVC_ID_SHA1_FINAL = 38, + SVC_ID_CALC_SHA1 = 39, + SVC_ID_CMP_SHA1 = 40, + SVC_ID_RAND_SHA1 = 41, + SVC_ID_LZ8_DEV = 1, + SVC_ID_LZ16_DEV_IMG = 2, + + // reserve 0x2b for semihosting (0xab & 0x3f == 0x2b) + + // DS compatible + SVC_ID_SOFT_RESET = 0, + + SVC_ID_WAIT_BY_LOOP = 3, + SVC_ID_WAIT_INTR = 4, + SVC_ID_WAIT_VB_INTR = 5, + SVC_ID_HALT = 6, + SVC_ID_SLEEP = 7, + SVC_ID_SND_BIAS = 8, + SVC_ID_DIV = 9, + + SVC_ID_CPU_SET = 11, + SVC_ID_CPU_SET_FAST = 12, + SVC_ID_SQRT = 13, + SVC_ID_CRC16 = 14, + SVC_ID_IS_MEMEX = 15, + SVC_ID_UNPACKBITS_DEV = 16, + SVC_ID_LZ8 = 17, + // overlap semihosting ((0x123456>>16) & 0x3f == 0x12) + SVC_ID_LZ16_DEV = 18, + SVC_ID_HUFF_DEV = 19, + SVC_ID_RL8 = 20, + SVC_ID_RL16_DEV = 21, + SVC_ID_DF8 = 22, + + SVC_ID_DF16 = 24, + + SVC_ID_SND_SIN = 26, + SVC_ID_SND_PITCH = 27, + SVC_ID_SND_VOL = 28, + SVC_ID_DS_IPL2 = 29, + + SVC_ID_PAUSE_HI = 31 +} +OSSvcID; + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +/* FIRM_OS_SYSTEMCALL_H_ */ +#endif diff --git a/include/firm/os/common/tick_brom.h b/include/firm/os/common/tick_brom.h new file mode 100644 index 00000000..c9b57557 --- /dev/null +++ b/include/firm/os/common/tick_brom.h @@ -0,0 +1,50 @@ +/*---------------------------------------------------------------------------* + Project: TwlBromSDK - OS - include + File: tick_addin.h + + Copyright 2007 Nintendo. All rights reserved. + + These coded instructions, statements, and computer programs contain + proprietary information of Nintendo of America Inc. and/or Nintendo + Company Ltd., and are protected by Federal copyright law. They may + not be disclosed to third parties or copied or duplicated in any form, + in whole or in part, without the prior written consent of Nintendo. + + $Log: $ + $NoKeywords: $ + *---------------------------------------------------------------------------*/ + +#ifndef FRIM_OS_TICK_BROM_H_ +#define FRIM_OS_TICK_BROM_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +//---- sec to tick +#define OS_MicroSecondsToTicksBROM( usec ) ((OSTick)( ((OS_SYSTEM_CLOCK/1000) * (u64)(usec)) / 1024 / 1000 )) +#define OS_MicroSecondsToTicksBROM32( usec ) ((OSTick)( ((OS_SYSTEM_CLOCK/1000) * (u32)(usec)) / 1024 / 1000 )) + +#define OS_MilliSecondsToTicksBROM( msec ) ((OSTick)( ((OS_SYSTEM_CLOCK/1000) * (u64)(msec)) / 1024 )) +#define OS_MilliSecondsToTicksBROM32( msec ) ((OSTick)( ((OS_SYSTEM_CLOCK/1000) * (u32)(msec)) / 1024 )) + +#define OS_SecondsToTicksBROM( sec ) ((OSTick)( (OS_SYSTEM_CLOCK * (u64)(sec)) / 1024 )) +#define OS_SecondsToTicksBROM32( sec ) ((OSTick)( (OS_SYSTEM_CLOCK * (u32)(sec)) / 1024 )) + +//---- tick to sec +#define OS_TicksToMicroSecondsBROM( tick ) ( ((u64)(tick) * 1024 * 1000) / (OS_SYSTEM_CLOCK/1000) ) +#define OS_TicksToMicroSecondsBROM32( tick ) ( ((u32)(tick) * 1024 * 1000) / (OS_SYSTEM_CLOCK/1000) ) + +#define OS_TicksToMilliSecondsBROM( tick ) ( ((u64)(tick) * 1024) / (OS_SYSTEM_CLOCK/1000) ) +#define OS_TicksToMilliSecondsBROM32( tick ) ( ((u32)(tick) * 1024) / (OS_SYSTEM_CLOCK/1000) ) + +#define OS_TicksToSecondsBROM( tick ) ( ((u64)(tick) * 1024) / OS_SYSTEM_CLOCK ) +#define OS_TicksToSecondsBROM32( tick ) ( ((u32)(tick) * 1024) / OS_SYSTEM_CLOCK ) + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +/* FRIM_OS_TICK_BROM_H_ */ +#endif diff --git a/include/firm/pm.h b/include/firm/pm.h new file mode 100644 index 00000000..b471de66 --- /dev/null +++ b/include/firm/pm.h @@ -0,0 +1,29 @@ +/*---------------------------------------------------------------------------* + Project: TwlFirm - include - PM + File: pm.h + + Copyright 2007 Nintendo. All rights reserved. + + These coded instructions, statements, and computer programs contain + proprietary information of Nintendo of America Inc. and/or Nintendo + Company Ltd., and are protected by Federal copyright law. They may + not be disclosed to third parties or copied or duplicated in any form, + in whole or in part, without the prior written consent of Nintendo. + + $Log: $ + $NoKeywords: $ + *---------------------------------------------------------------------------*/ + +#ifndef FIRM_PM_H_ +#define FIRM_PM_H_ + +#include + +#ifdef SDK_ARM7 +#include <../build/libraries/spi/ARM7/pm/include/pm_pmic.h> +#include <../build/libraries/spi/ARM7/pm/include/pm_pmic_ex.h> +#endif // SDK_ARM7 + + +/* FIRM_PM_H_ */ +#endif diff --git a/include/firm/specfiles/ARM7-BB-GCDFIRM.lcf.template b/include/firm/specfiles/ARM7-BB-GCDFIRM.lcf.template new file mode 100644 index 00000000..2b60c555 --- /dev/null +++ b/include/firm/specfiles/ARM7-BB-GCDFIRM.lcf.template @@ -0,0 +1,494 @@ +#--------------------------------------------------------------------------- +# Project: TwlFirm - tools - makelcf +# File: ARM7-BB-NORFIRM.lcf.template +# +# Copyright 2007 Nintendo. All rights reserved. +# +# These coded instructions, statements, and computer programs contain +# proprietary information of Nintendo of America Inc. and/or Nintendo +# Company Ltd., and are protected by Federal copyright law. They may +# not be disclosed to third parties or copied or duplicated in any form, +# in whole or in part, without the prior written consent of Nintendo. +# +# $Log: $ +# $NoKeywords: $ +#--------------------------------------------------------------------------- +MEMORY +{ + (RWX) : ORIGIN = , LENGTH = 0x0 # > + + (RWX) : ORIGIN = , LENGTH = 0x0 > + +# binary.AUTOLOAD_INFO (RWX) : ORIGIN = 0, LENGTH = 0x0 > + + (RW) : ORIGIN = AFTER(), LENGTH = 0x0 > + (RW) : ORIGIN = AFTER(), LENGTH = 0x0 > + + (RWXO): ORIGIN = , LENGTH = 0x0 > + + arena.MAIN (RW) : ORIGIN = AFTER(,), LENGTH = 0x0 + check.WORKRAM (RWX) : ORIGIN = , LENGTH = 0x48000 > workram.check +} + +KEEP_SECTION +{ + .ctor +} + +SECTIONS +{ + ############################ STATIC ################################# + .: + { + ALIGNALL(4); . = ALIGN(4); # Fit to cache line + + + + # + # TEXT BLOCK: READ ONLY + # + SDK_STATIC_START =.; + SDK_STATIC_TEXT_START =.; + #:::::::::: text/rodata + OBJECT(_start,*) + crt0.o (.text) + + + + + + + . = ALIGN(4); + * (.exception) + . = ALIGN(4); + SDK_STATIC_ETABLE_START =.; + EXCEPTION + SDK_STATIC_ETABLE_END =.; + . = ALIGN(4); + + + + + + + . = ALIGN(4); + + + + + + + . = ALIGN(4); + + SDK_STATIC_SINIT_START =.; + #:::::::::: ctor + + + + + + + WRITEW 0; + #:::::::::: ctor + SDK_STATIC_SINIT_END =.; + + #:::::::::: text/rodata + . = ALIGN(4); + SDK_STATIC_TEXT_END =.; + + # + # DATA BLOCK: READ WRITE + # + SDK_STATIC_DATA_START =.; + #:::::::::: data + + + + + + + . = ALIGN(4); + + + + + + + . = ALIGN(4); + + + + + + + . = ALIGN(4); + #:::::::::: data + . = ALIGN(4); + SDK_STATIC_DATA_END =.; + SDK_STATIC_END =.; + + SDK_STATIC_TEXT_SIZE = SDK_STATIC_TEXT_END - SDK_STATIC_TEXT_START; + SDK_STATIC_DATA_SIZE = SDK_STATIC_DATA_END - SDK_STATIC_DATA_START; + SDK_STATIC_SIZE = SDK_STATIC_END - SDK_STATIC_START; + __sinit__ = SDK_STATIC_SINIT_START; # for static initializer + __exception_table_start__ = SDK_STATIC_ETABLE_START; # for exception table + __exception_table_end__ = SDK_STATIC_ETABLE_END; # for exception table + } > + + ..bss: + { + ALIGNALL(4); . = ALIGN(4); + + + + # + # BSS BLOCK + # + SDK_STATIC_BSS_START =.; + #:::::::::: bss + + + + + + + . = ALIGN(4); + + + + + + + . = ALIGN(4); + #:::::::::: bss + . = ALIGN(4); + SDK_STATIC_BSS_END = .; + SDK_STATIC_BSS_SIZE = SDK_STATIC_BSS_END - SDK_STATIC_BSS_START; + + } >> + + + ############################ AUTOLOADS ############################## + SDK_AUTOLOAD.MAIN.START = 0x02380000; + SDK_AUTOLOAD.MAIN.END = SDK_AUTOLOAD.MAIN.START; + SDK_AUTOLOAD.MAIN.BSS_END = SDK_AUTOLOAD.MAIN.START; + SDK_AUTOLOAD.MAIN.SIZE = 0; + SDK_AUTOLOAD.MAIN.BSS_SIZE = 0; + SDK_AUTOLOAD.WRAM.START = 0x037f8000; + SDK_AUTOLOAD.WRAM.END = SDK_AUTOLOAD.WRAM.START; + SDK_AUTOLOAD.WRAM.BSS_END = SDK_AUTOLOAD.WRAM.START; + SDK_AUTOLOAD.WRAM.SIZE = 0; + SDK_AUTOLOAD.WRAM.BSS_SIZE = 0; + SDK_AUTOLOAD_START = SDK_STATIC_END; + SDK_AUTOLOAD_SIZE = 0; + SDK_AUTOLOAD_NUMBER = ; + + + .: + { + ALIGNALL(4); . = ALIGN(4); + + + + # + # TEXT BLOCK: READ ONLY + # + SDK_AUTOLOAD__ID =; + SDK_AUTOLOAD..ID =; + SDK_AUTOLOAD..START =.; + SDK_AUTOLOAD..TEXT_START =.; + #:::::::::: text/rodata + + + + + + + . = ALIGN(4); + + + + + + + . = ALIGN(4); + #:::::::::: text/rodata + SDK_AUTOLOAD..TEXT_END =.; + + # + # DATA BLOCK: READ WRITE BLOCK + # + SDK_AUTOLOAD..DATA_START =.; + #:::::::::: data + + + + + + + . = ALIGN(4); + + + + + + + . = ALIGN(4); + + + + + + + . = ALIGN(4); + #:::::::::: data + SDK_AUTOLOAD..DATA_END =.; + SDK_AUTOLOAD..END =.; + + SDK_AUTOLOAD..TEXT_SIZE = SDK_AUTOLOAD..TEXT_END - SDK_AUTOLOAD..TEXT_START; + SDK_AUTOLOAD..DATA_SIZE = SDK_AUTOLOAD..DATA_END - SDK_AUTOLOAD..DATA_START; + SDK_AUTOLOAD..SIZE = SDK_AUTOLOAD..END - SDK_AUTOLOAD..START; + SDK_AUTOLOAD_SIZE = SDK_AUTOLOAD_SIZE + SDK_AUTOLOAD..SIZE; + + } > + + ..bss: + { + ALIGNALL(4); . = ALIGN(4); + + + + # + # BSS BLOCK + # + SDK_AUTOLOAD..BSS_START = .; + #:::::::::: bss + + + + + + + . = ALIGN(4); + + + + + + + . = ALIGN(4); + #:::::::::: bss + . = ALIGN(4); + SDK_AUTOLOAD..BSS_END = .; + + SDK_AUTOLOAD..BSS_SIZE = SDK_AUTOLOAD..BSS_END - SDK_AUTOLOAD..BSS_START; + + } >> + + + + SDK_AUTOLOAD_MAIN_START = SDK_AUTOLOAD.MAIN.START; + SDK_AUTOLOAD_MAIN_END = SDK_AUTOLOAD.MAIN.END; + SDK_AUTOLOAD_MAIN_BSS_END = SDK_AUTOLOAD.MAIN.BSS_END; + SDK_AUTOLOAD_MAIN_SIZE = SDK_AUTOLOAD.MAIN.SIZE; + SDK_AUTOLOAD_MAIN_BSS_SIZE = SDK_AUTOLOAD.MAIN.BSS_SIZE; + SDK_AUTOLOAD_WRAM_START = SDK_AUTOLOAD.WRAM.START; + SDK_AUTOLOAD_WRAM_END = SDK_AUTOLOAD.WRAM.END; + SDK_AUTOLOAD_WRAM_BSS_END = SDK_AUTOLOAD.WRAM.BSS_END; + SDK_AUTOLOAD_WRAM_SIZE = SDK_AUTOLOAD.WRAM.SIZE; + SDK_AUTOLOAD_WRAM_BSS_SIZE = SDK_AUTOLOAD.WRAM.BSS_SIZE; + + ############################ AUTOLOAD_INFO ########################## + .binary.AUTOLOAD_INFO: + { + SDK_AUTOLOAD_LIST = .; + + WRITEW ADDR(.); + WRITEW SDK_AUTOLOAD..SIZE; + WRITEW SDK_AUTOLOAD..BSS_SIZE; + + SDK_AUTOLOAD_LIST_END = .; + } >> # > binary.AUTOLOAD_INFO + +# SDK_AUTOLOAD_LIST = SDK_AUTOLOAD_START + SDK_AUTOLOAD_SIZE; +# SDK_AUTOLOAD_LIST_END = SDK_AUTOLOAD_START + SDK_AUTOLOAD_SIZE + SIZEOF(.binary.AUTOLOAD_INFO); + SDK_AUTOLOAD_SIZE = SDK_AUTOLOAD_SIZE + SIZEOF(.binary.AUTOLOAD_INFO); + + ############################ OVERLAYS ############################### + SDK_OVERLAY_NUMBER = ; + + + .: + { + ALIGNALL(4); . = ALIGN(4); + + + + # + # TEXT BLOCK: READ ONLY + # + SDK_OVERLAY__ID =; ### SEGMENT OVERLAY ID + SDK_OVERLAY..ID =; + SDK_OVERLAY..START =.; + SDK_OVERLAY..TEXT_START =.; + #:::::::::: text/rodata + + + + + + + . = ALIGN(4); + + + + + + + . = ALIGN(4); + + + + + + + . = ALIGN(4); + SDK_OVERLAY..SINIT_START =.; + #:::::::::: ctor + + + + + + + WRITEW 0; + #:::::::::: ctor + SDK_OVERLAY..SINIT_END =.; + + #:::::::::: text/rodata + . = ALIGN(4); + SDK_OVERLAY..TEXT_END =.; + + # + # DATA BLOCK: READ WRITE + # + SDK_OVERLAY..DATA_START =.; + #:::::::::: data + + + + + + + . = ALIGN(4); + + + + + + + . = ALIGN(4); + #:::::::::: data + . = ALIGN(4); + SDK_OVERLAY..DATA_END =.; + SDK_OVERLAY..END =.; + + SDK_OVERLAY..TEXT_SIZE = SDK_OVERLAY..TEXT_END - SDK_OVERLAY..TEXT_START; + SDK_OVERLAY..DATA_SIZE = SDK_OVERLAY..DATA_END - SDK_OVERLAY..DATA_START; + SDK_OVERLAY..SIZE = SDK_OVERLAY..END - SDK_OVERLAY..START; + + } > + + ..bss: + { + ALIGNALL(4); . = ALIGN(4); + + + + # + # BSS BLOCK + # + SDK_OVERLAY..BSS_START = .; + #:::::::::: bss + + + + + + + . = ALIGN(4); + + + + + + + . = ALIGN(4); + #:::::::::: bss + . = ALIGN(4); + SDK_OVERLAY..BSS_END = .; + + SDK_OVERLAY..BSS_SIZE = SDK_OVERLAY..BSS_END - SDK_OVERLAY..BSS_START; + + } >> + + + + + ############################ ARENA ################################## + .arena.MAIN: + { + . = ALIGN(4); + SDK_SECTION_ARENA_START =.; + } > arena.MAIN + + + ############################ OVERLAYDEFS ############################ + .: + { + ### module information + WRITEW ADDR(.); # load address + WRITEW _start; # entry address + WRITEW SDK_STATIC_SIZE + SDK_AUTOLOAD_SIZE; # size of module + WRITEW _start_AutoloadDoneCallback; # callback autoload done + + ### overlay filename + + WRITES (""); # Overlay + + + } > + + + ############################ OVERLAYTABLE ########################### + .: + { + + # Overlay + WRITEW ; # overlay ID + WRITEW ADDR(.); # load address + WRITEW SDK_OVERLAY..SIZE; # size of module + WRITEW SDK_OVERLAY..BSS_SIZE; # size of bss + WRITEW SDK_OVERLAY..SINIT_START; # start address of static init + WRITEW SDK_OVERLAY..SINIT_END; # end address of static init + WRITEW ; # ROM file ID + WRITEW 0; # Reserved + + + + } > + + + ############################ OTHERS ################################# + SDK_SUBPRIV_ARENA_LO = SDK_AUTOLOAD.MAIN.BSS_END; + SDK_WRAM_ARENA_LO = SDK_SECTION_ARENA_START; + SDK_IRQ_STACKSIZE = ; # allocated in WRAM + SDK_SYS_STACKSIZE = ; # allocated in WRAM + + # work ram size checker + SDK_SYS_STACKSIZE_SIGN = (SDK_SYS_STACKSIZE < 0x80000000) * 2 - 1; + .check.WORKRAM: + { + . = . + SDK_SECTION_ARENA_START - + SDK_IRQ_STACKSIZE + SDK_SYS_STACKSIZE * SDK_SYS_STACKSIZE_SIGN; + } > check.WORKRAM +} diff --git a/include/firm/specfiles/ARM7-BB-GCDFIRM.lsf b/include/firm/specfiles/ARM7-BB-GCDFIRM.lsf new file mode 100644 index 00000000..6c472e66 --- /dev/null +++ b/include/firm/specfiles/ARM7-BB-GCDFIRM.lsf @@ -0,0 +1,27 @@ +#---------------------------------------------------------------------------- +# Project: TwlFirm - include +# File: ARM7-BB-NORFIRM.lsf +# +# Copyright 2007 Nintendo. All rights reserved. +# +# These coded insructions, statements, and computer programs contain +# proprietary information of Nintendo of America Inc. and/or Nintendo +# Company Ltd., and are protected by Federal copyright law. They may +# not be disclosed to third parties or copied or duplicated in any form, +# in whole or in part, without the prior written consent of Nintendo. +# +# $Log: $ +# $NoKeywords: $ +#---------------------------------------------------------------------------- +# +# TwlFirm LCF SPEC FILE +# + +Static $(TARGET_NAME) +{ + Address 0x037f8000 + Object $(OBJS_STATIC) + Library $(LLIBS) $(GLIBS) $(CW_LIBS) + Object * (.wram) + StackSize 1024 512 +} diff --git a/include/firm/specfiles/ARM7-BB-NORFIRM.lcf.template b/include/firm/specfiles/ARM7-BB-NORFIRM.lcf.template new file mode 100644 index 00000000..2b60c555 --- /dev/null +++ b/include/firm/specfiles/ARM7-BB-NORFIRM.lcf.template @@ -0,0 +1,494 @@ +#--------------------------------------------------------------------------- +# Project: TwlFirm - tools - makelcf +# File: ARM7-BB-NORFIRM.lcf.template +# +# Copyright 2007 Nintendo. All rights reserved. +# +# These coded instructions, statements, and computer programs contain +# proprietary information of Nintendo of America Inc. and/or Nintendo +# Company Ltd., and are protected by Federal copyright law. They may +# not be disclosed to third parties or copied or duplicated in any form, +# in whole or in part, without the prior written consent of Nintendo. +# +# $Log: $ +# $NoKeywords: $ +#--------------------------------------------------------------------------- +MEMORY +{ + (RWX) : ORIGIN = , LENGTH = 0x0 # > + + (RWX) : ORIGIN = , LENGTH = 0x0 > + +# binary.AUTOLOAD_INFO (RWX) : ORIGIN = 0, LENGTH = 0x0 > + + (RW) : ORIGIN = AFTER(), LENGTH = 0x0 > + (RW) : ORIGIN = AFTER(), LENGTH = 0x0 > + + (RWXO): ORIGIN = , LENGTH = 0x0 > + + arena.MAIN (RW) : ORIGIN = AFTER(,), LENGTH = 0x0 + check.WORKRAM (RWX) : ORIGIN = , LENGTH = 0x48000 > workram.check +} + +KEEP_SECTION +{ + .ctor +} + +SECTIONS +{ + ############################ STATIC ################################# + .: + { + ALIGNALL(4); . = ALIGN(4); # Fit to cache line + + + + # + # TEXT BLOCK: READ ONLY + # + SDK_STATIC_START =.; + SDK_STATIC_TEXT_START =.; + #:::::::::: text/rodata + OBJECT(_start,*) + crt0.o (.text) + + + + + + + . = ALIGN(4); + * (.exception) + . = ALIGN(4); + SDK_STATIC_ETABLE_START =.; + EXCEPTION + SDK_STATIC_ETABLE_END =.; + . = ALIGN(4); + + + + + + + . = ALIGN(4); + + + + + + + . = ALIGN(4); + + SDK_STATIC_SINIT_START =.; + #:::::::::: ctor + + + + + + + WRITEW 0; + #:::::::::: ctor + SDK_STATIC_SINIT_END =.; + + #:::::::::: text/rodata + . = ALIGN(4); + SDK_STATIC_TEXT_END =.; + + # + # DATA BLOCK: READ WRITE + # + SDK_STATIC_DATA_START =.; + #:::::::::: data + + + + + + + . = ALIGN(4); + + + + + + + . = ALIGN(4); + + + + + + + . = ALIGN(4); + #:::::::::: data + . = ALIGN(4); + SDK_STATIC_DATA_END =.; + SDK_STATIC_END =.; + + SDK_STATIC_TEXT_SIZE = SDK_STATIC_TEXT_END - SDK_STATIC_TEXT_START; + SDK_STATIC_DATA_SIZE = SDK_STATIC_DATA_END - SDK_STATIC_DATA_START; + SDK_STATIC_SIZE = SDK_STATIC_END - SDK_STATIC_START; + __sinit__ = SDK_STATIC_SINIT_START; # for static initializer + __exception_table_start__ = SDK_STATIC_ETABLE_START; # for exception table + __exception_table_end__ = SDK_STATIC_ETABLE_END; # for exception table + } > + + ..bss: + { + ALIGNALL(4); . = ALIGN(4); + + + + # + # BSS BLOCK + # + SDK_STATIC_BSS_START =.; + #:::::::::: bss + + + + + + + . = ALIGN(4); + + + + + + + . = ALIGN(4); + #:::::::::: bss + . = ALIGN(4); + SDK_STATIC_BSS_END = .; + SDK_STATIC_BSS_SIZE = SDK_STATIC_BSS_END - SDK_STATIC_BSS_START; + + } >> + + + ############################ AUTOLOADS ############################## + SDK_AUTOLOAD.MAIN.START = 0x02380000; + SDK_AUTOLOAD.MAIN.END = SDK_AUTOLOAD.MAIN.START; + SDK_AUTOLOAD.MAIN.BSS_END = SDK_AUTOLOAD.MAIN.START; + SDK_AUTOLOAD.MAIN.SIZE = 0; + SDK_AUTOLOAD.MAIN.BSS_SIZE = 0; + SDK_AUTOLOAD.WRAM.START = 0x037f8000; + SDK_AUTOLOAD.WRAM.END = SDK_AUTOLOAD.WRAM.START; + SDK_AUTOLOAD.WRAM.BSS_END = SDK_AUTOLOAD.WRAM.START; + SDK_AUTOLOAD.WRAM.SIZE = 0; + SDK_AUTOLOAD.WRAM.BSS_SIZE = 0; + SDK_AUTOLOAD_START = SDK_STATIC_END; + SDK_AUTOLOAD_SIZE = 0; + SDK_AUTOLOAD_NUMBER = ; + + + .: + { + ALIGNALL(4); . = ALIGN(4); + + + + # + # TEXT BLOCK: READ ONLY + # + SDK_AUTOLOAD__ID =; + SDK_AUTOLOAD..ID =; + SDK_AUTOLOAD..START =.; + SDK_AUTOLOAD..TEXT_START =.; + #:::::::::: text/rodata + + + + + + + . = ALIGN(4); + + + + + + + . = ALIGN(4); + #:::::::::: text/rodata + SDK_AUTOLOAD..TEXT_END =.; + + # + # DATA BLOCK: READ WRITE BLOCK + # + SDK_AUTOLOAD..DATA_START =.; + #:::::::::: data + + + + + + + . = ALIGN(4); + + + + + + + . = ALIGN(4); + + + + + + + . = ALIGN(4); + #:::::::::: data + SDK_AUTOLOAD..DATA_END =.; + SDK_AUTOLOAD..END =.; + + SDK_AUTOLOAD..TEXT_SIZE = SDK_AUTOLOAD..TEXT_END - SDK_AUTOLOAD..TEXT_START; + SDK_AUTOLOAD..DATA_SIZE = SDK_AUTOLOAD..DATA_END - SDK_AUTOLOAD..DATA_START; + SDK_AUTOLOAD..SIZE = SDK_AUTOLOAD..END - SDK_AUTOLOAD..START; + SDK_AUTOLOAD_SIZE = SDK_AUTOLOAD_SIZE + SDK_AUTOLOAD..SIZE; + + } > + + ..bss: + { + ALIGNALL(4); . = ALIGN(4); + + + + # + # BSS BLOCK + # + SDK_AUTOLOAD..BSS_START = .; + #:::::::::: bss + + + + + + + . = ALIGN(4); + + + + + + + . = ALIGN(4); + #:::::::::: bss + . = ALIGN(4); + SDK_AUTOLOAD..BSS_END = .; + + SDK_AUTOLOAD..BSS_SIZE = SDK_AUTOLOAD..BSS_END - SDK_AUTOLOAD..BSS_START; + + } >> + + + + SDK_AUTOLOAD_MAIN_START = SDK_AUTOLOAD.MAIN.START; + SDK_AUTOLOAD_MAIN_END = SDK_AUTOLOAD.MAIN.END; + SDK_AUTOLOAD_MAIN_BSS_END = SDK_AUTOLOAD.MAIN.BSS_END; + SDK_AUTOLOAD_MAIN_SIZE = SDK_AUTOLOAD.MAIN.SIZE; + SDK_AUTOLOAD_MAIN_BSS_SIZE = SDK_AUTOLOAD.MAIN.BSS_SIZE; + SDK_AUTOLOAD_WRAM_START = SDK_AUTOLOAD.WRAM.START; + SDK_AUTOLOAD_WRAM_END = SDK_AUTOLOAD.WRAM.END; + SDK_AUTOLOAD_WRAM_BSS_END = SDK_AUTOLOAD.WRAM.BSS_END; + SDK_AUTOLOAD_WRAM_SIZE = SDK_AUTOLOAD.WRAM.SIZE; + SDK_AUTOLOAD_WRAM_BSS_SIZE = SDK_AUTOLOAD.WRAM.BSS_SIZE; + + ############################ AUTOLOAD_INFO ########################## + .binary.AUTOLOAD_INFO: + { + SDK_AUTOLOAD_LIST = .; + + WRITEW ADDR(.); + WRITEW SDK_AUTOLOAD..SIZE; + WRITEW SDK_AUTOLOAD..BSS_SIZE; + + SDK_AUTOLOAD_LIST_END = .; + } >> # > binary.AUTOLOAD_INFO + +# SDK_AUTOLOAD_LIST = SDK_AUTOLOAD_START + SDK_AUTOLOAD_SIZE; +# SDK_AUTOLOAD_LIST_END = SDK_AUTOLOAD_START + SDK_AUTOLOAD_SIZE + SIZEOF(.binary.AUTOLOAD_INFO); + SDK_AUTOLOAD_SIZE = SDK_AUTOLOAD_SIZE + SIZEOF(.binary.AUTOLOAD_INFO); + + ############################ OVERLAYS ############################### + SDK_OVERLAY_NUMBER = ; + + + .: + { + ALIGNALL(4); . = ALIGN(4); + + + + # + # TEXT BLOCK: READ ONLY + # + SDK_OVERLAY__ID =; ### SEGMENT OVERLAY ID + SDK_OVERLAY..ID =; + SDK_OVERLAY..START =.; + SDK_OVERLAY..TEXT_START =.; + #:::::::::: text/rodata + + + + + + + . = ALIGN(4); + + + + + + + . = ALIGN(4); + + + + + + + . = ALIGN(4); + SDK_OVERLAY..SINIT_START =.; + #:::::::::: ctor + + + + + + + WRITEW 0; + #:::::::::: ctor + SDK_OVERLAY..SINIT_END =.; + + #:::::::::: text/rodata + . = ALIGN(4); + SDK_OVERLAY..TEXT_END =.; + + # + # DATA BLOCK: READ WRITE + # + SDK_OVERLAY..DATA_START =.; + #:::::::::: data + + + + + + + . = ALIGN(4); + + + + + + + . = ALIGN(4); + #:::::::::: data + . = ALIGN(4); + SDK_OVERLAY..DATA_END =.; + SDK_OVERLAY..END =.; + + SDK_OVERLAY..TEXT_SIZE = SDK_OVERLAY..TEXT_END - SDK_OVERLAY..TEXT_START; + SDK_OVERLAY..DATA_SIZE = SDK_OVERLAY..DATA_END - SDK_OVERLAY..DATA_START; + SDK_OVERLAY..SIZE = SDK_OVERLAY..END - SDK_OVERLAY..START; + + } > + + ..bss: + { + ALIGNALL(4); . = ALIGN(4); + + + + # + # BSS BLOCK + # + SDK_OVERLAY..BSS_START = .; + #:::::::::: bss + + + + + + + . = ALIGN(4); + + + + + + + . = ALIGN(4); + #:::::::::: bss + . = ALIGN(4); + SDK_OVERLAY..BSS_END = .; + + SDK_OVERLAY..BSS_SIZE = SDK_OVERLAY..BSS_END - SDK_OVERLAY..BSS_START; + + } >> + + + + + ############################ ARENA ################################## + .arena.MAIN: + { + . = ALIGN(4); + SDK_SECTION_ARENA_START =.; + } > arena.MAIN + + + ############################ OVERLAYDEFS ############################ + .: + { + ### module information + WRITEW ADDR(.); # load address + WRITEW _start; # entry address + WRITEW SDK_STATIC_SIZE + SDK_AUTOLOAD_SIZE; # size of module + WRITEW _start_AutoloadDoneCallback; # callback autoload done + + ### overlay filename + + WRITES (""); # Overlay + + + } > + + + ############################ OVERLAYTABLE ########################### + .: + { + + # Overlay + WRITEW ; # overlay ID + WRITEW ADDR(.); # load address + WRITEW SDK_OVERLAY..SIZE; # size of module + WRITEW SDK_OVERLAY..BSS_SIZE; # size of bss + WRITEW SDK_OVERLAY..SINIT_START; # start address of static init + WRITEW SDK_OVERLAY..SINIT_END; # end address of static init + WRITEW ; # ROM file ID + WRITEW 0; # Reserved + + + + } > + + + ############################ OTHERS ################################# + SDK_SUBPRIV_ARENA_LO = SDK_AUTOLOAD.MAIN.BSS_END; + SDK_WRAM_ARENA_LO = SDK_SECTION_ARENA_START; + SDK_IRQ_STACKSIZE = ; # allocated in WRAM + SDK_SYS_STACKSIZE = ; # allocated in WRAM + + # work ram size checker + SDK_SYS_STACKSIZE_SIGN = (SDK_SYS_STACKSIZE < 0x80000000) * 2 - 1; + .check.WORKRAM: + { + . = . + SDK_SECTION_ARENA_START - + SDK_IRQ_STACKSIZE + SDK_SYS_STACKSIZE * SDK_SYS_STACKSIZE_SIGN; + } > check.WORKRAM +} diff --git a/include/firm/specfiles/ARM7-BB-NORFIRM.lsf b/include/firm/specfiles/ARM7-BB-NORFIRM.lsf new file mode 100644 index 00000000..6c472e66 --- /dev/null +++ b/include/firm/specfiles/ARM7-BB-NORFIRM.lsf @@ -0,0 +1,27 @@ +#---------------------------------------------------------------------------- +# Project: TwlFirm - include +# File: ARM7-BB-NORFIRM.lsf +# +# Copyright 2007 Nintendo. All rights reserved. +# +# These coded insructions, statements, and computer programs contain +# proprietary information of Nintendo of America Inc. and/or Nintendo +# Company Ltd., and are protected by Federal copyright law. They may +# not be disclosed to third parties or copied or duplicated in any form, +# in whole or in part, without the prior written consent of Nintendo. +# +# $Log: $ +# $NoKeywords: $ +#---------------------------------------------------------------------------- +# +# TwlFirm LCF SPEC FILE +# + +Static $(TARGET_NAME) +{ + Address 0x037f8000 + Object $(OBJS_STATIC) + Library $(LLIBS) $(GLIBS) $(CW_LIBS) + Object * (.wram) + StackSize 1024 512 +} diff --git a/include/firm/specfiles/ARM7-TS-GCDFIRM.lcf.template b/include/firm/specfiles/ARM7-TS-GCDFIRM.lcf.template new file mode 100644 index 00000000..83064d35 --- /dev/null +++ b/include/firm/specfiles/ARM7-TS-GCDFIRM.lcf.template @@ -0,0 +1,494 @@ +#--------------------------------------------------------------------------- +# Project: TwlFirm - tools - makelcf +# File: ARM7-TS-NORFIRM.lcf.template +# +# Copyright 2007 Nintendo. All rights reserved. +# +# These coded instructions, statements, and computer programs contain +# proprietary information of Nintendo of America Inc. and/or Nintendo +# Company Ltd., and are protected by Federal copyright law. They may +# not be disclosed to third parties or copied or duplicated in any form, +# in whole or in part, without the prior written consent of Nintendo. +# +# $Log: $ +# $NoKeywords: $ +#--------------------------------------------------------------------------- +MEMORY +{ + (RWX) : ORIGIN = , LENGTH = 0x0 # > + + (RWX) : ORIGIN = , LENGTH = 0x0 > + +# binary.AUTOLOAD_INFO (RWX) : ORIGIN = 0, LENGTH = 0x0 > + + (RW) : ORIGIN = AFTER(), LENGTH = 0x0 > + (RW) : ORIGIN = AFTER(), LENGTH = 0x0 > + + (RWXO): ORIGIN = , LENGTH = 0x0 > + + arena.MAIN (RW) : ORIGIN = AFTER(,), LENGTH = 0x0 + check.WORKRAM (RWX) : ORIGIN = , LENGTH = 0x48000 > workram.check +} + +KEEP_SECTION +{ + .ctor +} + +SECTIONS +{ + ############################ STATIC ################################# + .: + { + ALIGNALL(4); . = ALIGN(4); # Fit to cache line + + + + # + # TEXT BLOCK: READ ONLY + # + SDK_STATIC_START =.; + SDK_STATIC_TEXT_START =.; + #:::::::::: text/rodata + OBJECT(_start,*) + crt0.o (.text) + + + + + + + . = ALIGN(4); + * (.exception) + . = ALIGN(4); + SDK_STATIC_ETABLE_START =.; + EXCEPTION + SDK_STATIC_ETABLE_END =.; + . = ALIGN(4); + + + + + + + . = ALIGN(4); + + + + + + + . = ALIGN(4); + + SDK_STATIC_SINIT_START =.; + #:::::::::: ctor + + + + + + + WRITEW 0; + #:::::::::: ctor + SDK_STATIC_SINIT_END =.; + + #:::::::::: text/rodata + . = ALIGN(4); + SDK_STATIC_TEXT_END =.; + + # + # DATA BLOCK: READ WRITE + # + SDK_STATIC_DATA_START =.; + #:::::::::: data + + + + + + + . = ALIGN(4); + + + + + + + . = ALIGN(4); + + + + + + + . = ALIGN(4); + #:::::::::: data + . = ALIGN(4); + SDK_STATIC_DATA_END =.; + SDK_STATIC_END =.; + + SDK_STATIC_TEXT_SIZE = SDK_STATIC_TEXT_END - SDK_STATIC_TEXT_START; + SDK_STATIC_DATA_SIZE = SDK_STATIC_DATA_END - SDK_STATIC_DATA_START; + SDK_STATIC_SIZE = SDK_STATIC_END - SDK_STATIC_START; + __sinit__ = SDK_STATIC_SINIT_START; # for static initializer + __exception_table_start__ = SDK_STATIC_ETABLE_START; # for exception table + __exception_table_end__ = SDK_STATIC_ETABLE_END; # for exception table + } > + + ..bss: + { + ALIGNALL(4); . = ALIGN(4); + + + + # + # BSS BLOCK + # + SDK_STATIC_BSS_START =.; + #:::::::::: bss + + + + + + + . = ALIGN(4); + + + + + + + . = ALIGN(4); + #:::::::::: bss + . = ALIGN(4); + SDK_STATIC_BSS_END = .; + SDK_STATIC_BSS_SIZE = SDK_STATIC_BSS_END - SDK_STATIC_BSS_START; + + } >> + + + ############################ AUTOLOADS ############################## + SDK_AUTOLOAD.MAIN.START = 0x02380000; + SDK_AUTOLOAD.MAIN.END = SDK_AUTOLOAD.MAIN.START; + SDK_AUTOLOAD.MAIN.BSS_END = SDK_AUTOLOAD.MAIN.START; + SDK_AUTOLOAD.MAIN.SIZE = 0; + SDK_AUTOLOAD.MAIN.BSS_SIZE = 0; + SDK_AUTOLOAD.WRAM.START = 0x037f8000; + SDK_AUTOLOAD.WRAM.END = SDK_AUTOLOAD.WRAM.START; + SDK_AUTOLOAD.WRAM.BSS_END = SDK_AUTOLOAD.WRAM.START; + SDK_AUTOLOAD.WRAM.SIZE = 0; + SDK_AUTOLOAD.WRAM.BSS_SIZE = 0; + SDK_AUTOLOAD_START = SDK_STATIC_END; + SDK_AUTOLOAD_SIZE = 0; + SDK_AUTOLOAD_NUMBER = ; + + + .: + { + ALIGNALL(4); . = ALIGN(4); + + + + # + # TEXT BLOCK: READ ONLY + # + SDK_AUTOLOAD__ID =; + SDK_AUTOLOAD..ID =; + SDK_AUTOLOAD..START =.; + SDK_AUTOLOAD..TEXT_START =.; + #:::::::::: text/rodata + + + + + + + . = ALIGN(4); + + + + + + + . = ALIGN(4); + #:::::::::: text/rodata + SDK_AUTOLOAD..TEXT_END =.; + + # + # DATA BLOCK: READ WRITE BLOCK + # + SDK_AUTOLOAD..DATA_START =.; + #:::::::::: data + + + + + + + . = ALIGN(4); + + + + + + + . = ALIGN(4); + + + + + + + . = ALIGN(4); + #:::::::::: data + SDK_AUTOLOAD..DATA_END =.; + SDK_AUTOLOAD..END =.; + + SDK_AUTOLOAD..TEXT_SIZE = SDK_AUTOLOAD..TEXT_END - SDK_AUTOLOAD..TEXT_START; + SDK_AUTOLOAD..DATA_SIZE = SDK_AUTOLOAD..DATA_END - SDK_AUTOLOAD..DATA_START; + SDK_AUTOLOAD..SIZE = SDK_AUTOLOAD..END - SDK_AUTOLOAD..START; + SDK_AUTOLOAD_SIZE = SDK_AUTOLOAD_SIZE + SDK_AUTOLOAD..SIZE; + + } > + + ..bss: + { + ALIGNALL(4); . = ALIGN(4); + + + + # + # BSS BLOCK + # + SDK_AUTOLOAD..BSS_START = .; + #:::::::::: bss + + + + + + + . = ALIGN(4); + + + + + + + . = ALIGN(4); + #:::::::::: bss + . = ALIGN(4); + SDK_AUTOLOAD..BSS_END = .; + + SDK_AUTOLOAD..BSS_SIZE = SDK_AUTOLOAD..BSS_END - SDK_AUTOLOAD..BSS_START; + + } >> + + + + SDK_AUTOLOAD_MAIN_START = SDK_AUTOLOAD.MAIN.START; + SDK_AUTOLOAD_MAIN_END = SDK_AUTOLOAD.MAIN.END; + SDK_AUTOLOAD_MAIN_BSS_END = SDK_AUTOLOAD.MAIN.BSS_END; + SDK_AUTOLOAD_MAIN_SIZE = SDK_AUTOLOAD.MAIN.SIZE; + SDK_AUTOLOAD_MAIN_BSS_SIZE = SDK_AUTOLOAD.MAIN.BSS_SIZE; + SDK_AUTOLOAD_WRAM_START = SDK_AUTOLOAD.WRAM.START; + SDK_AUTOLOAD_WRAM_END = SDK_AUTOLOAD.WRAM.END; + SDK_AUTOLOAD_WRAM_BSS_END = SDK_AUTOLOAD.WRAM.BSS_END; + SDK_AUTOLOAD_WRAM_SIZE = SDK_AUTOLOAD.WRAM.SIZE; + SDK_AUTOLOAD_WRAM_BSS_SIZE = SDK_AUTOLOAD.WRAM.BSS_SIZE; + + ############################ AUTOLOAD_INFO ########################## + .binary.AUTOLOAD_INFO: + { + SDK_AUTOLOAD_LIST = .; + + WRITEW ADDR(.); + WRITEW SDK_AUTOLOAD..SIZE; + WRITEW SDK_AUTOLOAD..BSS_SIZE; + + SDK_AUTOLOAD_LIST_END = .; + } >> # > binary.AUTOLOAD_INFO + +# SDK_AUTOLOAD_LIST = SDK_AUTOLOAD_START + SDK_AUTOLOAD_SIZE; +# SDK_AUTOLOAD_LIST_END = SDK_AUTOLOAD_START + SDK_AUTOLOAD_SIZE + SIZEOF(.binary.AUTOLOAD_INFO); + SDK_AUTOLOAD_SIZE = SDK_AUTOLOAD_SIZE + SIZEOF(.binary.AUTOLOAD_INFO); + + ############################ OVERLAYS ############################### + SDK_OVERLAY_NUMBER = ; + + + .: + { + ALIGNALL(4); . = ALIGN(4); + + + + # + # TEXT BLOCK: READ ONLY + # + SDK_OVERLAY__ID =; ### SEGMENT OVERLAY ID + SDK_OVERLAY..ID =; + SDK_OVERLAY..START =.; + SDK_OVERLAY..TEXT_START =.; + #:::::::::: text/rodata + + + + + + + . = ALIGN(4); + + + + + + + . = ALIGN(4); + + + + + + + . = ALIGN(4); + SDK_OVERLAY..SINIT_START =.; + #:::::::::: ctor + + + + + + + WRITEW 0; + #:::::::::: ctor + SDK_OVERLAY..SINIT_END =.; + + #:::::::::: text/rodata + . = ALIGN(4); + SDK_OVERLAY..TEXT_END =.; + + # + # DATA BLOCK: READ WRITE + # + SDK_OVERLAY..DATA_START =.; + #:::::::::: data + + + + + + + . = ALIGN(4); + + + + + + + . = ALIGN(4); + #:::::::::: data + . = ALIGN(4); + SDK_OVERLAY..DATA_END =.; + SDK_OVERLAY..END =.; + + SDK_OVERLAY..TEXT_SIZE = SDK_OVERLAY..TEXT_END - SDK_OVERLAY..TEXT_START; + SDK_OVERLAY..DATA_SIZE = SDK_OVERLAY..DATA_END - SDK_OVERLAY..DATA_START; + SDK_OVERLAY..SIZE = SDK_OVERLAY..END - SDK_OVERLAY..START; + + } > + + ..bss: + { + ALIGNALL(4); . = ALIGN(4); + + + + # + # BSS BLOCK + # + SDK_OVERLAY..BSS_START = .; + #:::::::::: bss + + + + + + + . = ALIGN(4); + + + + + + + . = ALIGN(4); + #:::::::::: bss + . = ALIGN(4); + SDK_OVERLAY..BSS_END = .; + + SDK_OVERLAY..BSS_SIZE = SDK_OVERLAY..BSS_END - SDK_OVERLAY..BSS_START; + + } >> + + + + + ############################ ARENA ################################## + .arena.MAIN: + { + . = ALIGN(4); + SDK_SECTION_ARENA_START =.; + } > arena.MAIN + + + ############################ OVERLAYDEFS ############################ + .: + { + ### module information + WRITEW ADDR(.); # load address + WRITEW _start; # entry address + WRITEW SDK_STATIC_SIZE + SDK_AUTOLOAD_SIZE; # size of module + WRITEW _start_AutoloadDoneCallback; # callback autoload done + + ### overlay filename + + WRITES (""); # Overlay + + + } > + + + ############################ OVERLAYTABLE ########################### + .: + { + + # Overlay + WRITEW ; # overlay ID + WRITEW ADDR(.); # load address + WRITEW SDK_OVERLAY..SIZE; # size of module + WRITEW SDK_OVERLAY..BSS_SIZE; # size of bss + WRITEW SDK_OVERLAY..SINIT_START; # start address of static init + WRITEW SDK_OVERLAY..SINIT_END; # end address of static init + WRITEW ; # ROM file ID + WRITEW 0; # Reserved + + + + } > + + + ############################ OTHERS ################################# + SDK_SUBPRIV_ARENA_LO = SDK_AUTOLOAD.MAIN.BSS_END; + SDK_WRAM_ARENA_LO = SDK_SECTION_ARENA_START; + SDK_IRQ_STACKSIZE = ; # allocated in WRAM + SDK_SYS_STACKSIZE = ; # allocated in WRAM + + # work ram size checker + SDK_SYS_STACKSIZE_SIGN = (SDK_SYS_STACKSIZE < 0x80000000) * 2 - 1; + .check.WORKRAM: + { + . = . + SDK_SECTION_ARENA_START - + SDK_IRQ_STACKSIZE + SDK_SYS_STACKSIZE * SDK_SYS_STACKSIZE_SIGN; + } > check.WORKRAM +} diff --git a/include/firm/specfiles/ARM7-TS-GCDFIRM.lsf b/include/firm/specfiles/ARM7-TS-GCDFIRM.lsf new file mode 100644 index 00000000..ef7f7d42 --- /dev/null +++ b/include/firm/specfiles/ARM7-TS-GCDFIRM.lsf @@ -0,0 +1,27 @@ +#---------------------------------------------------------------------------- +# Project: TwlFirm - include +# File: ARM7-TS-NORFIRM.lsf +# +# Copyright 2007 Nintendo. All rights reserved. +# +# These coded insructions, statements, and computer programs contain +# proprietary information of Nintendo of America Inc. and/or Nintendo +# Company Ltd., and are protected by Federal copyright law. They may +# not be disclosed to third parties or copied or duplicated in any form, +# in whole or in part, without the prior written consent of Nintendo. +# +# $Log: $ +# $NoKeywords: $ +#---------------------------------------------------------------------------- +# +# TwlFirm LCF SPEC FILE +# + +Static $(TARGET_NAME) +{ + Address 0x037f8000 + Object $(OBJS_STATIC) + Library $(LLIBS) $(GLIBS) $(CW_LIBS) + Object * (.wram) + StackSize 1024 512 +} diff --git a/include/firm/specfiles/ARM7-TS-NORFIRM.lcf.template b/include/firm/specfiles/ARM7-TS-NORFIRM.lcf.template new file mode 100644 index 00000000..83064d35 --- /dev/null +++ b/include/firm/specfiles/ARM7-TS-NORFIRM.lcf.template @@ -0,0 +1,494 @@ +#--------------------------------------------------------------------------- +# Project: TwlFirm - tools - makelcf +# File: ARM7-TS-NORFIRM.lcf.template +# +# Copyright 2007 Nintendo. All rights reserved. +# +# These coded instructions, statements, and computer programs contain +# proprietary information of Nintendo of America Inc. and/or Nintendo +# Company Ltd., and are protected by Federal copyright law. They may +# not be disclosed to third parties or copied or duplicated in any form, +# in whole or in part, without the prior written consent of Nintendo. +# +# $Log: $ +# $NoKeywords: $ +#--------------------------------------------------------------------------- +MEMORY +{ + (RWX) : ORIGIN = , LENGTH = 0x0 # > + + (RWX) : ORIGIN = , LENGTH = 0x0 > + +# binary.AUTOLOAD_INFO (RWX) : ORIGIN = 0, LENGTH = 0x0 > + + (RW) : ORIGIN = AFTER(), LENGTH = 0x0 > + (RW) : ORIGIN = AFTER(), LENGTH = 0x0 > + + (RWXO): ORIGIN = , LENGTH = 0x0 > + + arena.MAIN (RW) : ORIGIN = AFTER(,), LENGTH = 0x0 + check.WORKRAM (RWX) : ORIGIN = , LENGTH = 0x48000 > workram.check +} + +KEEP_SECTION +{ + .ctor +} + +SECTIONS +{ + ############################ STATIC ################################# + .: + { + ALIGNALL(4); . = ALIGN(4); # Fit to cache line + + + + # + # TEXT BLOCK: READ ONLY + # + SDK_STATIC_START =.; + SDK_STATIC_TEXT_START =.; + #:::::::::: text/rodata + OBJECT(_start,*) + crt0.o (.text) + + + + + + + . = ALIGN(4); + * (.exception) + . = ALIGN(4); + SDK_STATIC_ETABLE_START =.; + EXCEPTION + SDK_STATIC_ETABLE_END =.; + . = ALIGN(4); + + + + + + + . = ALIGN(4); + + + + + + + . = ALIGN(4); + + SDK_STATIC_SINIT_START =.; + #:::::::::: ctor + + + + + + + WRITEW 0; + #:::::::::: ctor + SDK_STATIC_SINIT_END =.; + + #:::::::::: text/rodata + . = ALIGN(4); + SDK_STATIC_TEXT_END =.; + + # + # DATA BLOCK: READ WRITE + # + SDK_STATIC_DATA_START =.; + #:::::::::: data + + + + + + + . = ALIGN(4); + + + + + + + . = ALIGN(4); + + + + + + + . = ALIGN(4); + #:::::::::: data + . = ALIGN(4); + SDK_STATIC_DATA_END =.; + SDK_STATIC_END =.; + + SDK_STATIC_TEXT_SIZE = SDK_STATIC_TEXT_END - SDK_STATIC_TEXT_START; + SDK_STATIC_DATA_SIZE = SDK_STATIC_DATA_END - SDK_STATIC_DATA_START; + SDK_STATIC_SIZE = SDK_STATIC_END - SDK_STATIC_START; + __sinit__ = SDK_STATIC_SINIT_START; # for static initializer + __exception_table_start__ = SDK_STATIC_ETABLE_START; # for exception table + __exception_table_end__ = SDK_STATIC_ETABLE_END; # for exception table + } > + + ..bss: + { + ALIGNALL(4); . = ALIGN(4); + + + + # + # BSS BLOCK + # + SDK_STATIC_BSS_START =.; + #:::::::::: bss + + + + + + + . = ALIGN(4); + + + + + + + . = ALIGN(4); + #:::::::::: bss + . = ALIGN(4); + SDK_STATIC_BSS_END = .; + SDK_STATIC_BSS_SIZE = SDK_STATIC_BSS_END - SDK_STATIC_BSS_START; + + } >> + + + ############################ AUTOLOADS ############################## + SDK_AUTOLOAD.MAIN.START = 0x02380000; + SDK_AUTOLOAD.MAIN.END = SDK_AUTOLOAD.MAIN.START; + SDK_AUTOLOAD.MAIN.BSS_END = SDK_AUTOLOAD.MAIN.START; + SDK_AUTOLOAD.MAIN.SIZE = 0; + SDK_AUTOLOAD.MAIN.BSS_SIZE = 0; + SDK_AUTOLOAD.WRAM.START = 0x037f8000; + SDK_AUTOLOAD.WRAM.END = SDK_AUTOLOAD.WRAM.START; + SDK_AUTOLOAD.WRAM.BSS_END = SDK_AUTOLOAD.WRAM.START; + SDK_AUTOLOAD.WRAM.SIZE = 0; + SDK_AUTOLOAD.WRAM.BSS_SIZE = 0; + SDK_AUTOLOAD_START = SDK_STATIC_END; + SDK_AUTOLOAD_SIZE = 0; + SDK_AUTOLOAD_NUMBER = ; + + + .: + { + ALIGNALL(4); . = ALIGN(4); + + + + # + # TEXT BLOCK: READ ONLY + # + SDK_AUTOLOAD__ID =; + SDK_AUTOLOAD..ID =; + SDK_AUTOLOAD..START =.; + SDK_AUTOLOAD..TEXT_START =.; + #:::::::::: text/rodata + + + + + + + . = ALIGN(4); + + + + + + + . = ALIGN(4); + #:::::::::: text/rodata + SDK_AUTOLOAD..TEXT_END =.; + + # + # DATA BLOCK: READ WRITE BLOCK + # + SDK_AUTOLOAD..DATA_START =.; + #:::::::::: data + + + + + + + . = ALIGN(4); + + + + + + + . = ALIGN(4); + + + + + + + . = ALIGN(4); + #:::::::::: data + SDK_AUTOLOAD..DATA_END =.; + SDK_AUTOLOAD..END =.; + + SDK_AUTOLOAD..TEXT_SIZE = SDK_AUTOLOAD..TEXT_END - SDK_AUTOLOAD..TEXT_START; + SDK_AUTOLOAD..DATA_SIZE = SDK_AUTOLOAD..DATA_END - SDK_AUTOLOAD..DATA_START; + SDK_AUTOLOAD..SIZE = SDK_AUTOLOAD..END - SDK_AUTOLOAD..START; + SDK_AUTOLOAD_SIZE = SDK_AUTOLOAD_SIZE + SDK_AUTOLOAD..SIZE; + + } > + + ..bss: + { + ALIGNALL(4); . = ALIGN(4); + + + + # + # BSS BLOCK + # + SDK_AUTOLOAD..BSS_START = .; + #:::::::::: bss + + + + + + + . = ALIGN(4); + + + + + + + . = ALIGN(4); + #:::::::::: bss + . = ALIGN(4); + SDK_AUTOLOAD..BSS_END = .; + + SDK_AUTOLOAD..BSS_SIZE = SDK_AUTOLOAD..BSS_END - SDK_AUTOLOAD..BSS_START; + + } >> + + + + SDK_AUTOLOAD_MAIN_START = SDK_AUTOLOAD.MAIN.START; + SDK_AUTOLOAD_MAIN_END = SDK_AUTOLOAD.MAIN.END; + SDK_AUTOLOAD_MAIN_BSS_END = SDK_AUTOLOAD.MAIN.BSS_END; + SDK_AUTOLOAD_MAIN_SIZE = SDK_AUTOLOAD.MAIN.SIZE; + SDK_AUTOLOAD_MAIN_BSS_SIZE = SDK_AUTOLOAD.MAIN.BSS_SIZE; + SDK_AUTOLOAD_WRAM_START = SDK_AUTOLOAD.WRAM.START; + SDK_AUTOLOAD_WRAM_END = SDK_AUTOLOAD.WRAM.END; + SDK_AUTOLOAD_WRAM_BSS_END = SDK_AUTOLOAD.WRAM.BSS_END; + SDK_AUTOLOAD_WRAM_SIZE = SDK_AUTOLOAD.WRAM.SIZE; + SDK_AUTOLOAD_WRAM_BSS_SIZE = SDK_AUTOLOAD.WRAM.BSS_SIZE; + + ############################ AUTOLOAD_INFO ########################## + .binary.AUTOLOAD_INFO: + { + SDK_AUTOLOAD_LIST = .; + + WRITEW ADDR(.); + WRITEW SDK_AUTOLOAD..SIZE; + WRITEW SDK_AUTOLOAD..BSS_SIZE; + + SDK_AUTOLOAD_LIST_END = .; + } >> # > binary.AUTOLOAD_INFO + +# SDK_AUTOLOAD_LIST = SDK_AUTOLOAD_START + SDK_AUTOLOAD_SIZE; +# SDK_AUTOLOAD_LIST_END = SDK_AUTOLOAD_START + SDK_AUTOLOAD_SIZE + SIZEOF(.binary.AUTOLOAD_INFO); + SDK_AUTOLOAD_SIZE = SDK_AUTOLOAD_SIZE + SIZEOF(.binary.AUTOLOAD_INFO); + + ############################ OVERLAYS ############################### + SDK_OVERLAY_NUMBER = ; + + + .: + { + ALIGNALL(4); . = ALIGN(4); + + + + # + # TEXT BLOCK: READ ONLY + # + SDK_OVERLAY__ID =; ### SEGMENT OVERLAY ID + SDK_OVERLAY..ID =; + SDK_OVERLAY..START =.; + SDK_OVERLAY..TEXT_START =.; + #:::::::::: text/rodata + + + + + + + . = ALIGN(4); + + + + + + + . = ALIGN(4); + + + + + + + . = ALIGN(4); + SDK_OVERLAY..SINIT_START =.; + #:::::::::: ctor + + + + + + + WRITEW 0; + #:::::::::: ctor + SDK_OVERLAY..SINIT_END =.; + + #:::::::::: text/rodata + . = ALIGN(4); + SDK_OVERLAY..TEXT_END =.; + + # + # DATA BLOCK: READ WRITE + # + SDK_OVERLAY..DATA_START =.; + #:::::::::: data + + + + + + + . = ALIGN(4); + + + + + + + . = ALIGN(4); + #:::::::::: data + . = ALIGN(4); + SDK_OVERLAY..DATA_END =.; + SDK_OVERLAY..END =.; + + SDK_OVERLAY..TEXT_SIZE = SDK_OVERLAY..TEXT_END - SDK_OVERLAY..TEXT_START; + SDK_OVERLAY..DATA_SIZE = SDK_OVERLAY..DATA_END - SDK_OVERLAY..DATA_START; + SDK_OVERLAY..SIZE = SDK_OVERLAY..END - SDK_OVERLAY..START; + + } > + + ..bss: + { + ALIGNALL(4); . = ALIGN(4); + + + + # + # BSS BLOCK + # + SDK_OVERLAY..BSS_START = .; + #:::::::::: bss + + + + + + + . = ALIGN(4); + + + + + + + . = ALIGN(4); + #:::::::::: bss + . = ALIGN(4); + SDK_OVERLAY..BSS_END = .; + + SDK_OVERLAY..BSS_SIZE = SDK_OVERLAY..BSS_END - SDK_OVERLAY..BSS_START; + + } >> + + + + + ############################ ARENA ################################## + .arena.MAIN: + { + . = ALIGN(4); + SDK_SECTION_ARENA_START =.; + } > arena.MAIN + + + ############################ OVERLAYDEFS ############################ + .: + { + ### module information + WRITEW ADDR(.); # load address + WRITEW _start; # entry address + WRITEW SDK_STATIC_SIZE + SDK_AUTOLOAD_SIZE; # size of module + WRITEW _start_AutoloadDoneCallback; # callback autoload done + + ### overlay filename + + WRITES (""); # Overlay + + + } > + + + ############################ OVERLAYTABLE ########################### + .: + { + + # Overlay + WRITEW ; # overlay ID + WRITEW ADDR(.); # load address + WRITEW SDK_OVERLAY..SIZE; # size of module + WRITEW SDK_OVERLAY..BSS_SIZE; # size of bss + WRITEW SDK_OVERLAY..SINIT_START; # start address of static init + WRITEW SDK_OVERLAY..SINIT_END; # end address of static init + WRITEW ; # ROM file ID + WRITEW 0; # Reserved + + + + } > + + + ############################ OTHERS ################################# + SDK_SUBPRIV_ARENA_LO = SDK_AUTOLOAD.MAIN.BSS_END; + SDK_WRAM_ARENA_LO = SDK_SECTION_ARENA_START; + SDK_IRQ_STACKSIZE = ; # allocated in WRAM + SDK_SYS_STACKSIZE = ; # allocated in WRAM + + # work ram size checker + SDK_SYS_STACKSIZE_SIGN = (SDK_SYS_STACKSIZE < 0x80000000) * 2 - 1; + .check.WORKRAM: + { + . = . + SDK_SECTION_ARENA_START - + SDK_IRQ_STACKSIZE + SDK_SYS_STACKSIZE * SDK_SYS_STACKSIZE_SIGN; + } > check.WORKRAM +} diff --git a/include/firm/specfiles/ARM7-TS-NORFIRM.lsf b/include/firm/specfiles/ARM7-TS-NORFIRM.lsf new file mode 100644 index 00000000..ef7f7d42 --- /dev/null +++ b/include/firm/specfiles/ARM7-TS-NORFIRM.lsf @@ -0,0 +1,27 @@ +#---------------------------------------------------------------------------- +# Project: TwlFirm - include +# File: ARM7-TS-NORFIRM.lsf +# +# Copyright 2007 Nintendo. All rights reserved. +# +# These coded insructions, statements, and computer programs contain +# proprietary information of Nintendo of America Inc. and/or Nintendo +# Company Ltd., and are protected by Federal copyright law. They may +# not be disclosed to third parties or copied or duplicated in any form, +# in whole or in part, without the prior written consent of Nintendo. +# +# $Log: $ +# $NoKeywords: $ +#---------------------------------------------------------------------------- +# +# TwlFirm LCF SPEC FILE +# + +Static $(TARGET_NAME) +{ + Address 0x037f8000 + Object $(OBJS_STATIC) + Library $(LLIBS) $(GLIBS) $(CW_LIBS) + Object * (.wram) + StackSize 1024 512 +} diff --git a/include/firm/specfiles/ARM9-BB-GCDFIRM.lcf.template b/include/firm/specfiles/ARM9-BB-GCDFIRM.lcf.template new file mode 100644 index 00000000..fcc55e5a --- /dev/null +++ b/include/firm/specfiles/ARM9-BB-GCDFIRM.lcf.template @@ -0,0 +1,589 @@ +#--------------------------------------------------------------------------- +# Project: TwlFirm - tools - makelcf +# File: ARM9-BB-NORFIRM.lcf.template +# +# Copyright 2007 Nintendo. All rights reserved. +# +# These coded instructions, statements, and computer programs contain +# proprietary information of Nintendo of America Inc. and/or Nintendo +# Company Ltd., and are protected by Federal copyright law. They may +# not be disclosed to third parties or copied or duplicated in any form, +# in whole or in part, without the prior written consent of Nintendo. +# +# $Log: $ +# $NoKeywords: $ +#--------------------------------------------------------------------------- +MEMORY +{ + (RWX) : ORIGIN = , LENGTH = 0x0 # > + + (RWX) : ORIGIN = , LENGTH = 0x0 # > + +# binary.AUTOLOAD_INFO (RWX) : ORIGIN = 0, LENGTH = 0x0 > +# binary.STATIC_FOOTER (RWX) : ORIGIN = 0, LENGTH = 0x0 >> + + (RW) : ORIGIN = AFTER(), LENGTH = 0x0 > + (RW) : ORIGIN = AFTER(), LENGTH = 0x0 > + + (RWXO): ORIGIN = , LENGTH = 0x0 > + + dummy.MAIN_EX (RW) : ORIGIN = 0x023e0000, LENGTH = 0x0 + arena.MAIN (RW) : ORIGIN = AFTER(,), LENGTH = 0x0 + arena.MAIN_EX (RW) : ORIGIN = AFTER(dummy.MAIN_EX,), LENGTH = 0x0 + arena.ITCM (RW) : ORIGIN = AFTER(ITCM,), LENGTH = 0x0 + arena.DTCM (RW) : ORIGIN = AFTER(DTCM,), LENGTH = 0x0 + binary.MODULE_FILES (RW) : ORIGIN = 0x0, LENGTH = 0x0 > component.files + check.ITCM (RWX) : ORIGIN = 0x0, LENGTH = 0x08000 > itcm.check + check.DTCM (RW) : ORIGIN = 0x0, LENGTH = 0x04000 > dtcm.check +} + +FORCE_ACTIVE +{ + SVC_SoftReset +} + +KEEP_SECTION +{ + .sinit +} + +SECTIONS +{ + ############################ STATIC ################################# + .: + { + ALIGNALL(4); . = ALIGN(32); # Fit to cache line + + + + # + # TEXT BLOCK: READ ONLY + # + SDK_STATIC_START =.; + SDK_STATIC_TEXT_START =.; + #:::::::::: text/rodata + crt0.o (.text) + libsyscall.a (.text) + crt0.o (.rodata) + # + # .version セクションを追加しています。 + # このセクションに含まれる情報はロットチェックの際に + # 必要となりますので、必ずこの位置に残すようにして下さい。 + # + * (.version) + OBJECT(TwlMain,*) + + + + + + + . = ALIGN(4); + * (.exception) + . = ALIGN(4); + SDK_STATIC_ETABLE_START =.; + EXCEPTION + SDK_STATIC_ETABLE_END =.; + . = ALIGN(4); + + + + + + + . = ALIGN(4); + + + + + + + . = ALIGN(4); + + SDK_STATIC_SINIT_START =.; + #:::::::::: ctor + + + + + + + + + + + + + WRITEW 0; + #:::::::::: ctor + SDK_STATIC_SINIT_END =.; + + #:::::::::: text/rodata + . = ALIGN(32); + SDK_STATIC_TEXT_END =.; + + # + # DATA BLOCK: READ WRITE + # + SDK_STATIC_DATA_START =.; + #:::::::::: data + + + + + + + . = ALIGN(4); + + + + + + + . = ALIGN(4); + SDK_OVERLAY_DIGEST =.; + # NO DIGEST + SDK_OVERLAY_DIGEST_END =.; + #:::::::::: data + . = ALIGN(32); + SDK_STATIC_DATA_END =.; + SDK_STATIC_END =.; + + SDK_STATIC_TEXT_SIZE = SDK_STATIC_TEXT_END - SDK_STATIC_TEXT_START; + SDK_STATIC_DATA_SIZE = SDK_STATIC_DATA_END - SDK_STATIC_DATA_START; + SDK_STATIC_SIZE = SDK_STATIC_END - SDK_STATIC_START; + __sinit__ = SDK_STATIC_SINIT_START; # for static initializer + __exception_table_start__ = SDK_STATIC_ETABLE_START; # for exception table + __exception_table_end__ = SDK_STATIC_ETABLE_END; # for exception table + } > + + ..bss: + { + ALIGNALL(4); . = ALIGN(32); + + + + # + # BSS BLOCK + # + SDK_STATIC_BSS_START =.; + #:::::::::: bss + + + + + + + . = ALIGN(4); + + + + + + + . = ALIGN(4); + #:::::::::: bss + . = ALIGN(32); + SDK_STATIC_BSS_END = .; + SDK_STATIC_BSS_SIZE = SDK_STATIC_BSS_END - SDK_STATIC_BSS_START; + + } >> + + + ############################ AUTOLOADS ############################## + SDK_AUTOLOAD.ITCM.START = 0x01ff8000; + SDK_AUTOLOAD.ITCM.END = SDK_AUTOLOAD.ITCM.START; + SDK_AUTOLOAD.ITCM.BSS_END = SDK_AUTOLOAD.ITCM.START; + SDK_AUTOLOAD.ITCM.SIZE = 0; + SDK_AUTOLOAD.ITCM.BSS_SIZE = 0; + SDK_AUTOLOAD.DTCM.START = 0x027e0000; + SDK_AUTOLOAD.DTCM.END = SDK_AUTOLOAD.DTCM.START; + SDK_AUTOLOAD.DTCM.BSS_END = SDK_AUTOLOAD.DTCM.START; + SDK_AUTOLOAD.DTCM.SIZE = 0; + SDK_AUTOLOAD.DTCM.BSS_SIZE = 0; + SDK_AUTOLOAD_START = SDK_STATIC_END; + SDK_AUTOLOAD_SIZE = 0; + SDK_AUTOLOAD_NUMBER = ; + + + .: + { + ALIGNALL(4); . = ALIGN(32); + + + + # + # TEXT BLOCK: READ ONLY + # + SDK_AUTOLOAD__ID =; + SDK_AUTOLOAD..ID =; + SDK_AUTOLOAD..START =.; + SDK_AUTOLOAD..TEXT_START =.; + #:::::::::: text/rodata + + + + + + + . = ALIGN(4); + + + + + + + . = ALIGN(4); + + + + + + + . = ALIGN(4); + #:::::::::: text/rodata + SDK_AUTOLOAD..TEXT_END =.; + + # + # DATA BLOCK: READ WRITE BLOCK + # + SDK_AUTOLOAD..DATA_START =.; + #:::::::::: data + + + + + + + . = ALIGN(4); + + + + + + + . = ALIGN(4); + + + + + + + . = ALIGN(4); + #:::::::::: data + . = ALIGN(32); + SDK_AUTOLOAD..DATA_END =.; + SDK_AUTOLOAD..END =.; + + SDK_AUTOLOAD..TEXT_SIZE = SDK_AUTOLOAD..TEXT_END - SDK_AUTOLOAD..TEXT_START; + SDK_AUTOLOAD..DATA_SIZE = SDK_AUTOLOAD..DATA_END - SDK_AUTOLOAD..DATA_START; + SDK_AUTOLOAD..SIZE = SDK_AUTOLOAD..END - SDK_AUTOLOAD..START; + SDK_AUTOLOAD_SIZE = SDK_AUTOLOAD_SIZE + SDK_AUTOLOAD..SIZE; + + } > + + ..bss: + { + ALIGNALL(4); . = ALIGN(32); + + + + # + # BSS BLOCK + # + SDK_AUTOLOAD..BSS_START = .; + #:::::::::: bss + + + + + + + . = ALIGN(4); + + + + + + + . = ALIGN(4); + + + + + + + . = ALIGN(4); + + + + + + + . = ALIGN(4); + #:::::::::: bss + . = ALIGN(32); + SDK_AUTOLOAD..BSS_END = .; + + SDK_AUTOLOAD..BSS_SIZE = SDK_AUTOLOAD..BSS_END - SDK_AUTOLOAD..BSS_START; + + } >> + + + + SDK_AUTOLOAD_ITCM_START = SDK_AUTOLOAD.ITCM.START; + SDK_AUTOLOAD_ITCM_END = SDK_AUTOLOAD.ITCM.END; + SDK_AUTOLOAD_ITCM_BSS_END = SDK_AUTOLOAD.ITCM.BSS_END; + SDK_AUTOLOAD_ITCM_SIZE = SDK_AUTOLOAD.ITCM.SIZE; + SDK_AUTOLOAD_ITCM_BSS_SIZE = SDK_AUTOLOAD.ITCM.BSS_SIZE; + SDK_AUTOLOAD_DTCM_START = SDK_AUTOLOAD.DTCM.START; + SDK_AUTOLOAD_DTCM_END = SDK_AUTOLOAD.DTCM.END; + SDK_AUTOLOAD_DTCM_BSS_END = SDK_AUTOLOAD.DTCM.BSS_END; + SDK_AUTOLOAD_DTCM_SIZE = SDK_AUTOLOAD.DTCM.SIZE; + SDK_AUTOLOAD_DTCM_BSS_SIZE = SDK_AUTOLOAD.DTCM.BSS_SIZE; + + ############################ AUTOLOAD_INFO ########################## + .binary.AUTOLOAD_INFO: + { + SDK_AUTOLOAD_LIST = .; + + WRITEW ADDR(.); + WRITEW SDK_AUTOLOAD..SIZE; + WRITEW SDK_AUTOLOAD..BSS_SIZE; + + SDK_AUTOLOAD_LIST_END = .; + } >> # > binary.AUTOLOAD_INFO + +# SDK_AUTOLOAD_LIST = SDK_AUTOLOAD_START + SDK_AUTOLOAD_SIZE; +# SDK_AUTOLOAD_LIST_END = SDK_AUTOLOAD_START + SDK_AUTOLOAD_SIZE + SIZEOF(.binary.AUTOLOAD_INFO); + SDK_AUTOLOAD_SIZE = SDK_AUTOLOAD_SIZE + SIZEOF(.binary.AUTOLOAD_INFO); + + ############################ STATIC_FOOTER ########################## + .binary.STATIC_FOOTER: + { + WRITEW 0xdec00621; # LE(0x2106C0DE) = NITRO CODE + WRITEW _start_ModuleParams - ADDR(.); + WRITEW 0; # NO DIGEST + } >> # > binary.STATIC_FOOTER + + ############################ OVERLAYS ############################### + SDK_OVERLAY_NUMBER = ; + + + .: + { + ALIGNALL(4); . = ALIGN(32); + + + + # + # TEXT BLOCK: READ ONLY + # + SDK_OVERLAY__ID =; ### SEGMENT OVERLAY ID + SDK_OVERLAY..ID =; + SDK_OVERLAY..START =.; + SDK_OVERLAY..TEXT_START =.; + #:::::::::: text/rodata + + + + + + + . = ALIGN(4); + + + + + + + . = ALIGN(4); + + + + + + + . = ALIGN(4); + SDK_OVERLAY..SINIT_START =.; + #:::::::::: ctor + + + + + + + + + + + + + WRITEW 0; + #:::::::::: ctor + SDK_OVERLAY..SINIT_END =.; + + #:::::::::: text/rodata + . = ALIGN(32); + SDK_OVERLAY..TEXT_END =.; + + # + # DATA BLOCK: READ WRITE + # + SDK_OVERLAY..DATA_START =.; + #:::::::::: data + + + + + + + . = ALIGN(4); + + + + + + + . = ALIGN(4); + #:::::::::: data + . = ALIGN(32); + SDK_OVERLAY..DATA_END =.; + SDK_OVERLAY..END =.; + + SDK_OVERLAY..TEXT_SIZE = SDK_OVERLAY..TEXT_END - SDK_OVERLAY..TEXT_START; + SDK_OVERLAY..DATA_SIZE = SDK_OVERLAY..DATA_END - SDK_OVERLAY..DATA_START; + SDK_OVERLAY..SIZE = SDK_OVERLAY..END - SDK_OVERLAY..START; + + } > + + ..bss: + { + ALIGNALL(4); . = ALIGN(32); + + + + # + # BSS BLOCK + # + SDK_OVERLAY..BSS_START = .; + #:::::::::: bss + + + + + + + . = ALIGN(4); + + + + + + + . = ALIGN(4); + #:::::::::: bss + . = ALIGN(32); + SDK_OVERLAY..BSS_END = .; + + SDK_OVERLAY..BSS_SIZE = SDK_OVERLAY..BSS_END - SDK_OVERLAY..BSS_START; + + } >> + + + + ############################ MAIN EX ################################## + # MAIN EX Area + .dummy.MAIN_EX: + { + . = ALIGN(32); + } > dummy.MAIN_EX + + ############################ ARENA ################################## + .arena.MAIN: + { + . = ALIGN(32); + SDK_SECTION_ARENA_START =.; + } > arena.MAIN + + .arena.MAIN_EX: + { + . = ALIGN(32); + SDK_SECTION_ARENA_EX_START =.; + } > arena.MAIN_EX + + .arena.ITCM: + { + . = ALIGN(32); + SDK_SECTION_ARENA_ITCM_START =.; + } > arena.ITCM + + .arena.DTCM: + { + . = ALIGN(32); + SDK_SECTION_ARENA_DTCM_START =.; + } > arena.DTCM + + ############################ OVERLAYDEFS ############################ + .: + { + ### module information + WRITEW ADDR(.); # load address + WRITEW _start; # entry address + WRITEW SDK_STATIC_SIZE + SDK_AUTOLOAD_SIZE; # size of module + WRITEW _start_AutoloadDoneCallback; # callback autoload done + + ### overlay filename + + WRITES (""); # Overlay + + + } > + + + ############################ OVERLAYTABLE ########################### + .: + { + + # Overlay + WRITEW ; # overlay ID + WRITEW ADDR(.); # load address + WRITEW SDK_OVERLAY..SIZE; # size of module + WRITEW SDK_OVERLAY..BSS_SIZE; # size of bss + WRITEW SDK_OVERLAY..SINIT_START; # start address of static init + WRITEW SDK_OVERLAY..SINIT_END; # end address of static init + WRITEW ; # ROM file ID + WRITEW 0; # Reserved + + + + } > + + + ############################ OTHERS ################################# + SDK_MAIN_ARENA_LO = SDK_SECTION_ARENA_START; + SDK_IRQ_STACKSIZE = ; # allocated in DTCM + SDK_SYS_STACKSIZE = ; # when 0 means all remains of DTCM + + # Module filelist + .binary.MODULE_FILES: + { + WRITES (""); + WRITES (""); + WRITES (""); + } > binary.MODULE_FILES + + # ITCM/DTCM size checker => check AUTOLOAD_ITCM/DTCM + .check.ITCM: + { + . = . + SDK_AUTOLOAD_ITCM_SIZE + SDK_AUTOLOAD_ITCM_BSS_SIZE; + } > check.ITCM + + SDK_SYS_STACKSIZE_SIGN = (SDK_SYS_STACKSIZE < 0x80000000) * 2 - 1; + .check.DTCM: + { + . = . + SDK_AUTOLOAD_DTCM_SIZE + SDK_AUTOLOAD_DTCM_BSS_SIZE; + . = . + SDK_IRQ_STACKSIZE + SDK_SYS_STACKSIZE * SDK_SYS_STACKSIZE_SIGN; + } > check.DTCM + +} diff --git a/include/firm/specfiles/ARM9-BB-GCDFIRM.lsf b/include/firm/specfiles/ARM9-BB-GCDFIRM.lsf new file mode 100644 index 00000000..e6488a8b --- /dev/null +++ b/include/firm/specfiles/ARM9-BB-GCDFIRM.lsf @@ -0,0 +1,41 @@ +#---------------------------------------------------------------------------- +# Project: TwlFirm - include +# File: ARM9-BB-NORFIRM.lsf +# +# Copyright 2007 Nintendo. All rights reserved. +# +# These coded insructions, statements, and computer programs contain +# proprietary information of Nintendo of America Inc. and/or Nintendo +# Company Ltd., and are protected by Federal copyright law. They may +# not be disclosed to third parties or copied or duplicated in any form, +# in whole or in part, without the prior written consent of Nintendo. +# +# $Log: $ +# $NoKeywords: $ +#---------------------------------------------------------------------------- +# +# TwlFirm LCF SPEC FILE +# + +Static $(TARGET_NAME) +{ + Address 0x03800000 + Object $(OBJS_STATIC) + Library $(LLIBS) $(GLIBS) $(CW_LIBS) +} + +Autoload ITCM +{ + Address 0x01ff8000 + Object * (.itcm) + Object $(OBJS_AUTOLOAD) (.text) +} + +Autoload DTCM +{ + Address 0x027e0000 + Object * (.dtcm) + Object $(OBJS_AUTOLOAD) (.data) + Object $(OBJS_AUTOLOAD) (.bss) +} + diff --git a/include/firm/specfiles/ARM9-BB-NORFIRM.lcf.template b/include/firm/specfiles/ARM9-BB-NORFIRM.lcf.template new file mode 100644 index 00000000..fcc55e5a --- /dev/null +++ b/include/firm/specfiles/ARM9-BB-NORFIRM.lcf.template @@ -0,0 +1,589 @@ +#--------------------------------------------------------------------------- +# Project: TwlFirm - tools - makelcf +# File: ARM9-BB-NORFIRM.lcf.template +# +# Copyright 2007 Nintendo. All rights reserved. +# +# These coded instructions, statements, and computer programs contain +# proprietary information of Nintendo of America Inc. and/or Nintendo +# Company Ltd., and are protected by Federal copyright law. They may +# not be disclosed to third parties or copied or duplicated in any form, +# in whole or in part, without the prior written consent of Nintendo. +# +# $Log: $ +# $NoKeywords: $ +#--------------------------------------------------------------------------- +MEMORY +{ + (RWX) : ORIGIN = , LENGTH = 0x0 # > + + (RWX) : ORIGIN = , LENGTH = 0x0 # > + +# binary.AUTOLOAD_INFO (RWX) : ORIGIN = 0, LENGTH = 0x0 > +# binary.STATIC_FOOTER (RWX) : ORIGIN = 0, LENGTH = 0x0 >> + + (RW) : ORIGIN = AFTER(), LENGTH = 0x0 > + (RW) : ORIGIN = AFTER(), LENGTH = 0x0 > + + (RWXO): ORIGIN = , LENGTH = 0x0 > + + dummy.MAIN_EX (RW) : ORIGIN = 0x023e0000, LENGTH = 0x0 + arena.MAIN (RW) : ORIGIN = AFTER(,), LENGTH = 0x0 + arena.MAIN_EX (RW) : ORIGIN = AFTER(dummy.MAIN_EX,), LENGTH = 0x0 + arena.ITCM (RW) : ORIGIN = AFTER(ITCM,), LENGTH = 0x0 + arena.DTCM (RW) : ORIGIN = AFTER(DTCM,), LENGTH = 0x0 + binary.MODULE_FILES (RW) : ORIGIN = 0x0, LENGTH = 0x0 > component.files + check.ITCM (RWX) : ORIGIN = 0x0, LENGTH = 0x08000 > itcm.check + check.DTCM (RW) : ORIGIN = 0x0, LENGTH = 0x04000 > dtcm.check +} + +FORCE_ACTIVE +{ + SVC_SoftReset +} + +KEEP_SECTION +{ + .sinit +} + +SECTIONS +{ + ############################ STATIC ################################# + .: + { + ALIGNALL(4); . = ALIGN(32); # Fit to cache line + + + + # + # TEXT BLOCK: READ ONLY + # + SDK_STATIC_START =.; + SDK_STATIC_TEXT_START =.; + #:::::::::: text/rodata + crt0.o (.text) + libsyscall.a (.text) + crt0.o (.rodata) + # + # .version セクションを追加しています。 + # このセクションに含まれる情報はロットチェックの際に + # 必要となりますので、必ずこの位置に残すようにして下さい。 + # + * (.version) + OBJECT(TwlMain,*) + + + + + + + . = ALIGN(4); + * (.exception) + . = ALIGN(4); + SDK_STATIC_ETABLE_START =.; + EXCEPTION + SDK_STATIC_ETABLE_END =.; + . = ALIGN(4); + + + + + + + . = ALIGN(4); + + + + + + + . = ALIGN(4); + + SDK_STATIC_SINIT_START =.; + #:::::::::: ctor + + + + + + + + + + + + + WRITEW 0; + #:::::::::: ctor + SDK_STATIC_SINIT_END =.; + + #:::::::::: text/rodata + . = ALIGN(32); + SDK_STATIC_TEXT_END =.; + + # + # DATA BLOCK: READ WRITE + # + SDK_STATIC_DATA_START =.; + #:::::::::: data + + + + + + + . = ALIGN(4); + + + + + + + . = ALIGN(4); + SDK_OVERLAY_DIGEST =.; + # NO DIGEST + SDK_OVERLAY_DIGEST_END =.; + #:::::::::: data + . = ALIGN(32); + SDK_STATIC_DATA_END =.; + SDK_STATIC_END =.; + + SDK_STATIC_TEXT_SIZE = SDK_STATIC_TEXT_END - SDK_STATIC_TEXT_START; + SDK_STATIC_DATA_SIZE = SDK_STATIC_DATA_END - SDK_STATIC_DATA_START; + SDK_STATIC_SIZE = SDK_STATIC_END - SDK_STATIC_START; + __sinit__ = SDK_STATIC_SINIT_START; # for static initializer + __exception_table_start__ = SDK_STATIC_ETABLE_START; # for exception table + __exception_table_end__ = SDK_STATIC_ETABLE_END; # for exception table + } > + + ..bss: + { + ALIGNALL(4); . = ALIGN(32); + + + + # + # BSS BLOCK + # + SDK_STATIC_BSS_START =.; + #:::::::::: bss + + + + + + + . = ALIGN(4); + + + + + + + . = ALIGN(4); + #:::::::::: bss + . = ALIGN(32); + SDK_STATIC_BSS_END = .; + SDK_STATIC_BSS_SIZE = SDK_STATIC_BSS_END - SDK_STATIC_BSS_START; + + } >> + + + ############################ AUTOLOADS ############################## + SDK_AUTOLOAD.ITCM.START = 0x01ff8000; + SDK_AUTOLOAD.ITCM.END = SDK_AUTOLOAD.ITCM.START; + SDK_AUTOLOAD.ITCM.BSS_END = SDK_AUTOLOAD.ITCM.START; + SDK_AUTOLOAD.ITCM.SIZE = 0; + SDK_AUTOLOAD.ITCM.BSS_SIZE = 0; + SDK_AUTOLOAD.DTCM.START = 0x027e0000; + SDK_AUTOLOAD.DTCM.END = SDK_AUTOLOAD.DTCM.START; + SDK_AUTOLOAD.DTCM.BSS_END = SDK_AUTOLOAD.DTCM.START; + SDK_AUTOLOAD.DTCM.SIZE = 0; + SDK_AUTOLOAD.DTCM.BSS_SIZE = 0; + SDK_AUTOLOAD_START = SDK_STATIC_END; + SDK_AUTOLOAD_SIZE = 0; + SDK_AUTOLOAD_NUMBER = ; + + + .: + { + ALIGNALL(4); . = ALIGN(32); + + + + # + # TEXT BLOCK: READ ONLY + # + SDK_AUTOLOAD__ID =; + SDK_AUTOLOAD..ID =; + SDK_AUTOLOAD..START =.; + SDK_AUTOLOAD..TEXT_START =.; + #:::::::::: text/rodata + + + + + + + . = ALIGN(4); + + + + + + + . = ALIGN(4); + + + + + + + . = ALIGN(4); + #:::::::::: text/rodata + SDK_AUTOLOAD..TEXT_END =.; + + # + # DATA BLOCK: READ WRITE BLOCK + # + SDK_AUTOLOAD..DATA_START =.; + #:::::::::: data + + + + + + + . = ALIGN(4); + + + + + + + . = ALIGN(4); + + + + + + + . = ALIGN(4); + #:::::::::: data + . = ALIGN(32); + SDK_AUTOLOAD..DATA_END =.; + SDK_AUTOLOAD..END =.; + + SDK_AUTOLOAD..TEXT_SIZE = SDK_AUTOLOAD..TEXT_END - SDK_AUTOLOAD..TEXT_START; + SDK_AUTOLOAD..DATA_SIZE = SDK_AUTOLOAD..DATA_END - SDK_AUTOLOAD..DATA_START; + SDK_AUTOLOAD..SIZE = SDK_AUTOLOAD..END - SDK_AUTOLOAD..START; + SDK_AUTOLOAD_SIZE = SDK_AUTOLOAD_SIZE + SDK_AUTOLOAD..SIZE; + + } > + + ..bss: + { + ALIGNALL(4); . = ALIGN(32); + + + + # + # BSS BLOCK + # + SDK_AUTOLOAD..BSS_START = .; + #:::::::::: bss + + + + + + + . = ALIGN(4); + + + + + + + . = ALIGN(4); + + + + + + + . = ALIGN(4); + + + + + + + . = ALIGN(4); + #:::::::::: bss + . = ALIGN(32); + SDK_AUTOLOAD..BSS_END = .; + + SDK_AUTOLOAD..BSS_SIZE = SDK_AUTOLOAD..BSS_END - SDK_AUTOLOAD..BSS_START; + + } >> + + + + SDK_AUTOLOAD_ITCM_START = SDK_AUTOLOAD.ITCM.START; + SDK_AUTOLOAD_ITCM_END = SDK_AUTOLOAD.ITCM.END; + SDK_AUTOLOAD_ITCM_BSS_END = SDK_AUTOLOAD.ITCM.BSS_END; + SDK_AUTOLOAD_ITCM_SIZE = SDK_AUTOLOAD.ITCM.SIZE; + SDK_AUTOLOAD_ITCM_BSS_SIZE = SDK_AUTOLOAD.ITCM.BSS_SIZE; + SDK_AUTOLOAD_DTCM_START = SDK_AUTOLOAD.DTCM.START; + SDK_AUTOLOAD_DTCM_END = SDK_AUTOLOAD.DTCM.END; + SDK_AUTOLOAD_DTCM_BSS_END = SDK_AUTOLOAD.DTCM.BSS_END; + SDK_AUTOLOAD_DTCM_SIZE = SDK_AUTOLOAD.DTCM.SIZE; + SDK_AUTOLOAD_DTCM_BSS_SIZE = SDK_AUTOLOAD.DTCM.BSS_SIZE; + + ############################ AUTOLOAD_INFO ########################## + .binary.AUTOLOAD_INFO: + { + SDK_AUTOLOAD_LIST = .; + + WRITEW ADDR(.); + WRITEW SDK_AUTOLOAD..SIZE; + WRITEW SDK_AUTOLOAD..BSS_SIZE; + + SDK_AUTOLOAD_LIST_END = .; + } >> # > binary.AUTOLOAD_INFO + +# SDK_AUTOLOAD_LIST = SDK_AUTOLOAD_START + SDK_AUTOLOAD_SIZE; +# SDK_AUTOLOAD_LIST_END = SDK_AUTOLOAD_START + SDK_AUTOLOAD_SIZE + SIZEOF(.binary.AUTOLOAD_INFO); + SDK_AUTOLOAD_SIZE = SDK_AUTOLOAD_SIZE + SIZEOF(.binary.AUTOLOAD_INFO); + + ############################ STATIC_FOOTER ########################## + .binary.STATIC_FOOTER: + { + WRITEW 0xdec00621; # LE(0x2106C0DE) = NITRO CODE + WRITEW _start_ModuleParams - ADDR(.); + WRITEW 0; # NO DIGEST + } >> # > binary.STATIC_FOOTER + + ############################ OVERLAYS ############################### + SDK_OVERLAY_NUMBER = ; + + + .: + { + ALIGNALL(4); . = ALIGN(32); + + + + # + # TEXT BLOCK: READ ONLY + # + SDK_OVERLAY__ID =; ### SEGMENT OVERLAY ID + SDK_OVERLAY..ID =; + SDK_OVERLAY..START =.; + SDK_OVERLAY..TEXT_START =.; + #:::::::::: text/rodata + + + + + + + . = ALIGN(4); + + + + + + + . = ALIGN(4); + + + + + + + . = ALIGN(4); + SDK_OVERLAY..SINIT_START =.; + #:::::::::: ctor + + + + + + + + + + + + + WRITEW 0; + #:::::::::: ctor + SDK_OVERLAY..SINIT_END =.; + + #:::::::::: text/rodata + . = ALIGN(32); + SDK_OVERLAY..TEXT_END =.; + + # + # DATA BLOCK: READ WRITE + # + SDK_OVERLAY..DATA_START =.; + #:::::::::: data + + + + + + + . = ALIGN(4); + + + + + + + . = ALIGN(4); + #:::::::::: data + . = ALIGN(32); + SDK_OVERLAY..DATA_END =.; + SDK_OVERLAY..END =.; + + SDK_OVERLAY..TEXT_SIZE = SDK_OVERLAY..TEXT_END - SDK_OVERLAY..TEXT_START; + SDK_OVERLAY..DATA_SIZE = SDK_OVERLAY..DATA_END - SDK_OVERLAY..DATA_START; + SDK_OVERLAY..SIZE = SDK_OVERLAY..END - SDK_OVERLAY..START; + + } > + + ..bss: + { + ALIGNALL(4); . = ALIGN(32); + + + + # + # BSS BLOCK + # + SDK_OVERLAY..BSS_START = .; + #:::::::::: bss + + + + + + + . = ALIGN(4); + + + + + + + . = ALIGN(4); + #:::::::::: bss + . = ALIGN(32); + SDK_OVERLAY..BSS_END = .; + + SDK_OVERLAY..BSS_SIZE = SDK_OVERLAY..BSS_END - SDK_OVERLAY..BSS_START; + + } >> + + + + ############################ MAIN EX ################################## + # MAIN EX Area + .dummy.MAIN_EX: + { + . = ALIGN(32); + } > dummy.MAIN_EX + + ############################ ARENA ################################## + .arena.MAIN: + { + . = ALIGN(32); + SDK_SECTION_ARENA_START =.; + } > arena.MAIN + + .arena.MAIN_EX: + { + . = ALIGN(32); + SDK_SECTION_ARENA_EX_START =.; + } > arena.MAIN_EX + + .arena.ITCM: + { + . = ALIGN(32); + SDK_SECTION_ARENA_ITCM_START =.; + } > arena.ITCM + + .arena.DTCM: + { + . = ALIGN(32); + SDK_SECTION_ARENA_DTCM_START =.; + } > arena.DTCM + + ############################ OVERLAYDEFS ############################ + .: + { + ### module information + WRITEW ADDR(.); # load address + WRITEW _start; # entry address + WRITEW SDK_STATIC_SIZE + SDK_AUTOLOAD_SIZE; # size of module + WRITEW _start_AutoloadDoneCallback; # callback autoload done + + ### overlay filename + + WRITES (""); # Overlay + + + } > + + + ############################ OVERLAYTABLE ########################### + .: + { + + # Overlay + WRITEW ; # overlay ID + WRITEW ADDR(.); # load address + WRITEW SDK_OVERLAY..SIZE; # size of module + WRITEW SDK_OVERLAY..BSS_SIZE; # size of bss + WRITEW SDK_OVERLAY..SINIT_START; # start address of static init + WRITEW SDK_OVERLAY..SINIT_END; # end address of static init + WRITEW ; # ROM file ID + WRITEW 0; # Reserved + + + + } > + + + ############################ OTHERS ################################# + SDK_MAIN_ARENA_LO = SDK_SECTION_ARENA_START; + SDK_IRQ_STACKSIZE = ; # allocated in DTCM + SDK_SYS_STACKSIZE = ; # when 0 means all remains of DTCM + + # Module filelist + .binary.MODULE_FILES: + { + WRITES (""); + WRITES (""); + WRITES (""); + } > binary.MODULE_FILES + + # ITCM/DTCM size checker => check AUTOLOAD_ITCM/DTCM + .check.ITCM: + { + . = . + SDK_AUTOLOAD_ITCM_SIZE + SDK_AUTOLOAD_ITCM_BSS_SIZE; + } > check.ITCM + + SDK_SYS_STACKSIZE_SIGN = (SDK_SYS_STACKSIZE < 0x80000000) * 2 - 1; + .check.DTCM: + { + . = . + SDK_AUTOLOAD_DTCM_SIZE + SDK_AUTOLOAD_DTCM_BSS_SIZE; + . = . + SDK_IRQ_STACKSIZE + SDK_SYS_STACKSIZE * SDK_SYS_STACKSIZE_SIGN; + } > check.DTCM + +} diff --git a/include/firm/specfiles/ARM9-BB-NORFIRM.lsf b/include/firm/specfiles/ARM9-BB-NORFIRM.lsf new file mode 100644 index 00000000..e6488a8b --- /dev/null +++ b/include/firm/specfiles/ARM9-BB-NORFIRM.lsf @@ -0,0 +1,41 @@ +#---------------------------------------------------------------------------- +# Project: TwlFirm - include +# File: ARM9-BB-NORFIRM.lsf +# +# Copyright 2007 Nintendo. All rights reserved. +# +# These coded insructions, statements, and computer programs contain +# proprietary information of Nintendo of America Inc. and/or Nintendo +# Company Ltd., and are protected by Federal copyright law. They may +# not be disclosed to third parties or copied or duplicated in any form, +# in whole or in part, without the prior written consent of Nintendo. +# +# $Log: $ +# $NoKeywords: $ +#---------------------------------------------------------------------------- +# +# TwlFirm LCF SPEC FILE +# + +Static $(TARGET_NAME) +{ + Address 0x03800000 + Object $(OBJS_STATIC) + Library $(LLIBS) $(GLIBS) $(CW_LIBS) +} + +Autoload ITCM +{ + Address 0x01ff8000 + Object * (.itcm) + Object $(OBJS_AUTOLOAD) (.text) +} + +Autoload DTCM +{ + Address 0x027e0000 + Object * (.dtcm) + Object $(OBJS_AUTOLOAD) (.data) + Object $(OBJS_AUTOLOAD) (.bss) +} + diff --git a/include/firm/specfiles/ARM9-TS-GCDFIRM.lcf.template b/include/firm/specfiles/ARM9-TS-GCDFIRM.lcf.template new file mode 100644 index 00000000..fab99dba --- /dev/null +++ b/include/firm/specfiles/ARM9-TS-GCDFIRM.lcf.template @@ -0,0 +1,589 @@ +#--------------------------------------------------------------------------- +# Project: TwlFirm - tools - makelcf +# File: ARM9-TS-NORFIRM.lcf.template +# +# Copyright 2007 Nintendo. All rights reserved. +# +# These coded instructions, statements, and computer programs contain +# proprietary information of Nintendo of America Inc. and/or Nintendo +# Company Ltd., and are protected by Federal copyright law. They may +# not be disclosed to third parties or copied or duplicated in any form, +# in whole or in part, without the prior written consent of Nintendo. +# +# $Log: $ +# $NoKeywords: $ +#--------------------------------------------------------------------------- +MEMORY +{ + (RWX) : ORIGIN = , LENGTH = 0x0 # > + + (RWX) : ORIGIN = , LENGTH = 0x0 # > + +# binary.AUTOLOAD_INFO (RWX) : ORIGIN = 0, LENGTH = 0x0 > +# binary.STATIC_FOOTER (RWX) : ORIGIN = 0, LENGTH = 0x0 >> + + (RW) : ORIGIN = AFTER(), LENGTH = 0x0 > + (RW) : ORIGIN = AFTER(), LENGTH = 0x0 > + + (RWXO): ORIGIN = , LENGTH = 0x0 > + + dummy.MAIN_EX (RW) : ORIGIN = 0x023e0000, LENGTH = 0x0 + arena.MAIN (RW) : ORIGIN = AFTER(,), LENGTH = 0x0 + arena.MAIN_EX (RW) : ORIGIN = AFTER(dummy.MAIN_EX,), LENGTH = 0x0 + arena.ITCM (RW) : ORIGIN = AFTER(ITCM,), LENGTH = 0x0 + arena.DTCM (RW) : ORIGIN = AFTER(DTCM,), LENGTH = 0x0 + binary.MODULE_FILES (RW) : ORIGIN = 0x0, LENGTH = 0x0 > component.files + check.ITCM (RWX) : ORIGIN = 0x0, LENGTH = 0x08000 > itcm.check + check.DTCM (RW) : ORIGIN = 0x0, LENGTH = 0x04000 > dtcm.check +} + +FORCE_ACTIVE +{ + SVC_SoftReset +} + +KEEP_SECTION +{ + .sinit +} + +SECTIONS +{ + ############################ STATIC ################################# + .: + { + ALIGNALL(4); . = ALIGN(32); # Fit to cache line + + + + # + # TEXT BLOCK: READ ONLY + # + SDK_STATIC_START =.; + SDK_STATIC_TEXT_START =.; + #:::::::::: text/rodata + crt0.o (.text) + libsyscall.a (.text) + crt0.o (.rodata) + # + # .version セクションを追加しています。 + # このセクションに含まれる情報はロットチェックの際に + # 必要となりますので、必ずこの位置に残すようにして下さい。 + # + * (.version) + OBJECT(TwlMain,*) + + + + + + + . = ALIGN(4); + * (.exception) + . = ALIGN(4); + SDK_STATIC_ETABLE_START =.; + EXCEPTION + SDK_STATIC_ETABLE_END =.; + . = ALIGN(4); + + + + + + + . = ALIGN(4); + + + + + + + . = ALIGN(4); + + SDK_STATIC_SINIT_START =.; + #:::::::::: ctor + + + + + + + + + + + + + WRITEW 0; + #:::::::::: ctor + SDK_STATIC_SINIT_END =.; + + #:::::::::: text/rodata + . = ALIGN(32); + SDK_STATIC_TEXT_END =.; + + # + # DATA BLOCK: READ WRITE + # + SDK_STATIC_DATA_START =.; + #:::::::::: data + + + + + + + . = ALIGN(4); + + + + + + + . = ALIGN(4); + SDK_OVERLAY_DIGEST =.; + # NO DIGEST + SDK_OVERLAY_DIGEST_END =.; + #:::::::::: data + . = ALIGN(32); + SDK_STATIC_DATA_END =.; + SDK_STATIC_END =.; + + SDK_STATIC_TEXT_SIZE = SDK_STATIC_TEXT_END - SDK_STATIC_TEXT_START; + SDK_STATIC_DATA_SIZE = SDK_STATIC_DATA_END - SDK_STATIC_DATA_START; + SDK_STATIC_SIZE = SDK_STATIC_END - SDK_STATIC_START; + __sinit__ = SDK_STATIC_SINIT_START; # for static initializer + __exception_table_start__ = SDK_STATIC_ETABLE_START; # for exception table + __exception_table_end__ = SDK_STATIC_ETABLE_END; # for exception table + } > + + ..bss: + { + ALIGNALL(4); . = ALIGN(32); + + + + # + # BSS BLOCK + # + SDK_STATIC_BSS_START =.; + #:::::::::: bss + + + + + + + . = ALIGN(4); + + + + + + + . = ALIGN(4); + #:::::::::: bss + . = ALIGN(32); + SDK_STATIC_BSS_END = .; + SDK_STATIC_BSS_SIZE = SDK_STATIC_BSS_END - SDK_STATIC_BSS_START; + + } >> + + + ############################ AUTOLOADS ############################## + SDK_AUTOLOAD.ITCM.START = 0x01ff8000; + SDK_AUTOLOAD.ITCM.END = SDK_AUTOLOAD.ITCM.START; + SDK_AUTOLOAD.ITCM.BSS_END = SDK_AUTOLOAD.ITCM.START; + SDK_AUTOLOAD.ITCM.SIZE = 0; + SDK_AUTOLOAD.ITCM.BSS_SIZE = 0; + SDK_AUTOLOAD.DTCM.START = 0x027e0000; + SDK_AUTOLOAD.DTCM.END = SDK_AUTOLOAD.DTCM.START; + SDK_AUTOLOAD.DTCM.BSS_END = SDK_AUTOLOAD.DTCM.START; + SDK_AUTOLOAD.DTCM.SIZE = 0; + SDK_AUTOLOAD.DTCM.BSS_SIZE = 0; + SDK_AUTOLOAD_START = SDK_STATIC_END; + SDK_AUTOLOAD_SIZE = 0; + SDK_AUTOLOAD_NUMBER = ; + + + .: + { + ALIGNALL(4); . = ALIGN(32); + + + + # + # TEXT BLOCK: READ ONLY + # + SDK_AUTOLOAD__ID =; + SDK_AUTOLOAD..ID =; + SDK_AUTOLOAD..START =.; + SDK_AUTOLOAD..TEXT_START =.; + #:::::::::: text/rodata + + + + + + + . = ALIGN(4); + + + + + + + . = ALIGN(4); + + + + + + + . = ALIGN(4); + #:::::::::: text/rodata + SDK_AUTOLOAD..TEXT_END =.; + + # + # DATA BLOCK: READ WRITE BLOCK + # + SDK_AUTOLOAD..DATA_START =.; + #:::::::::: data + + + + + + + . = ALIGN(4); + + + + + + + . = ALIGN(4); + + + + + + + . = ALIGN(4); + #:::::::::: data + . = ALIGN(32); + SDK_AUTOLOAD..DATA_END =.; + SDK_AUTOLOAD..END =.; + + SDK_AUTOLOAD..TEXT_SIZE = SDK_AUTOLOAD..TEXT_END - SDK_AUTOLOAD..TEXT_START; + SDK_AUTOLOAD..DATA_SIZE = SDK_AUTOLOAD..DATA_END - SDK_AUTOLOAD..DATA_START; + SDK_AUTOLOAD..SIZE = SDK_AUTOLOAD..END - SDK_AUTOLOAD..START; + SDK_AUTOLOAD_SIZE = SDK_AUTOLOAD_SIZE + SDK_AUTOLOAD..SIZE; + + } > + + ..bss: + { + ALIGNALL(4); . = ALIGN(32); + + + + # + # BSS BLOCK + # + SDK_AUTOLOAD..BSS_START = .; + #:::::::::: bss + + + + + + + . = ALIGN(4); + + + + + + + . = ALIGN(4); + + + + + + + . = ALIGN(4); + + + + + + + . = ALIGN(4); + #:::::::::: bss + . = ALIGN(32); + SDK_AUTOLOAD..BSS_END = .; + + SDK_AUTOLOAD..BSS_SIZE = SDK_AUTOLOAD..BSS_END - SDK_AUTOLOAD..BSS_START; + + } >> + + + + SDK_AUTOLOAD_ITCM_START = SDK_AUTOLOAD.ITCM.START; + SDK_AUTOLOAD_ITCM_END = SDK_AUTOLOAD.ITCM.END; + SDK_AUTOLOAD_ITCM_BSS_END = SDK_AUTOLOAD.ITCM.BSS_END; + SDK_AUTOLOAD_ITCM_SIZE = SDK_AUTOLOAD.ITCM.SIZE; + SDK_AUTOLOAD_ITCM_BSS_SIZE = SDK_AUTOLOAD.ITCM.BSS_SIZE; + SDK_AUTOLOAD_DTCM_START = SDK_AUTOLOAD.DTCM.START; + SDK_AUTOLOAD_DTCM_END = SDK_AUTOLOAD.DTCM.END; + SDK_AUTOLOAD_DTCM_BSS_END = SDK_AUTOLOAD.DTCM.BSS_END; + SDK_AUTOLOAD_DTCM_SIZE = SDK_AUTOLOAD.DTCM.SIZE; + SDK_AUTOLOAD_DTCM_BSS_SIZE = SDK_AUTOLOAD.DTCM.BSS_SIZE; + + ############################ AUTOLOAD_INFO ########################## + .binary.AUTOLOAD_INFO: + { + SDK_AUTOLOAD_LIST = .; + + WRITEW ADDR(.); + WRITEW SDK_AUTOLOAD..SIZE; + WRITEW SDK_AUTOLOAD..BSS_SIZE; + + SDK_AUTOLOAD_LIST_END = .; + } >> # > binary.AUTOLOAD_INFO + +# SDK_AUTOLOAD_LIST = SDK_AUTOLOAD_START + SDK_AUTOLOAD_SIZE; +# SDK_AUTOLOAD_LIST_END = SDK_AUTOLOAD_START + SDK_AUTOLOAD_SIZE + SIZEOF(.binary.AUTOLOAD_INFO); + SDK_AUTOLOAD_SIZE = SDK_AUTOLOAD_SIZE + SIZEOF(.binary.AUTOLOAD_INFO); + + ############################ STATIC_FOOTER ########################## + .binary.STATIC_FOOTER: + { + WRITEW 0xdec00621; # LE(0x2106C0DE) = NITRO CODE + WRITEW _start_ModuleParams - ADDR(.); + WRITEW 0; # NO DIGEST + } >> # > binary.STATIC_FOOTER + + ############################ OVERLAYS ############################### + SDK_OVERLAY_NUMBER = ; + + + .: + { + ALIGNALL(4); . = ALIGN(32); + + + + # + # TEXT BLOCK: READ ONLY + # + SDK_OVERLAY__ID =; ### SEGMENT OVERLAY ID + SDK_OVERLAY..ID =; + SDK_OVERLAY..START =.; + SDK_OVERLAY..TEXT_START =.; + #:::::::::: text/rodata + + + + + + + . = ALIGN(4); + + + + + + + . = ALIGN(4); + + + + + + + . = ALIGN(4); + SDK_OVERLAY..SINIT_START =.; + #:::::::::: ctor + + + + + + + + + + + + + WRITEW 0; + #:::::::::: ctor + SDK_OVERLAY..SINIT_END =.; + + #:::::::::: text/rodata + . = ALIGN(32); + SDK_OVERLAY..TEXT_END =.; + + # + # DATA BLOCK: READ WRITE + # + SDK_OVERLAY..DATA_START =.; + #:::::::::: data + + + + + + + . = ALIGN(4); + + + + + + + . = ALIGN(4); + #:::::::::: data + . = ALIGN(32); + SDK_OVERLAY..DATA_END =.; + SDK_OVERLAY..END =.; + + SDK_OVERLAY..TEXT_SIZE = SDK_OVERLAY..TEXT_END - SDK_OVERLAY..TEXT_START; + SDK_OVERLAY..DATA_SIZE = SDK_OVERLAY..DATA_END - SDK_OVERLAY..DATA_START; + SDK_OVERLAY..SIZE = SDK_OVERLAY..END - SDK_OVERLAY..START; + + } > + + ..bss: + { + ALIGNALL(4); . = ALIGN(32); + + + + # + # BSS BLOCK + # + SDK_OVERLAY..BSS_START = .; + #:::::::::: bss + + + + + + + . = ALIGN(4); + + + + + + + . = ALIGN(4); + #:::::::::: bss + . = ALIGN(32); + SDK_OVERLAY..BSS_END = .; + + SDK_OVERLAY..BSS_SIZE = SDK_OVERLAY..BSS_END - SDK_OVERLAY..BSS_START; + + } >> + + + + ############################ MAIN EX ################################## + # MAIN EX Area + .dummy.MAIN_EX: + { + . = ALIGN(32); + } > dummy.MAIN_EX + + ############################ ARENA ################################## + .arena.MAIN: + { + . = ALIGN(32); + SDK_SECTION_ARENA_START =.; + } > arena.MAIN + + .arena.MAIN_EX: + { + . = ALIGN(32); + SDK_SECTION_ARENA_EX_START =.; + } > arena.MAIN_EX + + .arena.ITCM: + { + . = ALIGN(32); + SDK_SECTION_ARENA_ITCM_START =.; + } > arena.ITCM + + .arena.DTCM: + { + . = ALIGN(32); + SDK_SECTION_ARENA_DTCM_START =.; + } > arena.DTCM + + ############################ OVERLAYDEFS ############################ + .: + { + ### module information + WRITEW ADDR(.); # load address + WRITEW _start; # entry address + WRITEW SDK_STATIC_SIZE + SDK_AUTOLOAD_SIZE; # size of module + WRITEW _start_AutoloadDoneCallback; # callback autoload done + + ### overlay filename + + WRITES (""); # Overlay + + + } > + + + ############################ OVERLAYTABLE ########################### + .: + { + + # Overlay + WRITEW ; # overlay ID + WRITEW ADDR(.); # load address + WRITEW SDK_OVERLAY..SIZE; # size of module + WRITEW SDK_OVERLAY..BSS_SIZE; # size of bss + WRITEW SDK_OVERLAY..SINIT_START; # start address of static init + WRITEW SDK_OVERLAY..SINIT_END; # end address of static init + WRITEW ; # ROM file ID + WRITEW 0; # Reserved + + + + } > + + + ############################ OTHERS ################################# + SDK_MAIN_ARENA_LO = SDK_SECTION_ARENA_START; + SDK_IRQ_STACKSIZE = ; # allocated in DTCM + SDK_SYS_STACKSIZE = ; # when 0 means all remains of DTCM + + # Module filelist + .binary.MODULE_FILES: + { + WRITES (""); + WRITES (""); + WRITES (""); + } > binary.MODULE_FILES + + # ITCM/DTCM size checker => check AUTOLOAD_ITCM/DTCM + .check.ITCM: + { + . = . + SDK_AUTOLOAD_ITCM_SIZE + SDK_AUTOLOAD_ITCM_BSS_SIZE; + } > check.ITCM + + SDK_SYS_STACKSIZE_SIGN = (SDK_SYS_STACKSIZE < 0x80000000) * 2 - 1; + .check.DTCM: + { + . = . + SDK_AUTOLOAD_DTCM_SIZE + SDK_AUTOLOAD_DTCM_BSS_SIZE; + . = . + SDK_IRQ_STACKSIZE + SDK_SYS_STACKSIZE * SDK_SYS_STACKSIZE_SIGN; + } > check.DTCM + +} diff --git a/include/firm/specfiles/ARM9-TS-GCDFIRM.lsf b/include/firm/specfiles/ARM9-TS-GCDFIRM.lsf new file mode 100644 index 00000000..05d913a8 --- /dev/null +++ b/include/firm/specfiles/ARM9-TS-GCDFIRM.lsf @@ -0,0 +1,41 @@ +#---------------------------------------------------------------------------- +# Project: TwlFirm - include +# File: ARM9-TS-NORFIRM.lsf +# +# Copyright 2007 Nintendo. All rights reserved. +# +# These coded insructions, statements, and computer programs contain +# proprietary information of Nintendo of America Inc. and/or Nintendo +# Company Ltd., and are protected by Federal copyright law. They may +# not be disclosed to third parties or copied or duplicated in any form, +# in whole or in part, without the prior written consent of Nintendo. +# +# $Log: $ +# $NoKeywords: $ +#---------------------------------------------------------------------------- +# +# TwlFirm LCF SPEC FILE +# + +Static $(TARGET_NAME) +{ + Address 0x03800000 + Object $(OBJS_STATIC) + Library $(LLIBS) $(GLIBS) $(CW_LIBS) +} + +Autoload ITCM +{ + Address 0x01ff8000 + Object * (.itcm) + Object $(OBJS_AUTOLOAD) (.text) +} + +Autoload DTCM +{ + Address 0x027e0000 + Object * (.dtcm) + Object $(OBJS_AUTOLOAD) (.data) + Object $(OBJS_AUTOLOAD) (.bss) +} + diff --git a/include/firm/specfiles/ARM9-TS-NORFIRM.lcf.template b/include/firm/specfiles/ARM9-TS-NORFIRM.lcf.template new file mode 100644 index 00000000..fab99dba --- /dev/null +++ b/include/firm/specfiles/ARM9-TS-NORFIRM.lcf.template @@ -0,0 +1,589 @@ +#--------------------------------------------------------------------------- +# Project: TwlFirm - tools - makelcf +# File: ARM9-TS-NORFIRM.lcf.template +# +# Copyright 2007 Nintendo. All rights reserved. +# +# These coded instructions, statements, and computer programs contain +# proprietary information of Nintendo of America Inc. and/or Nintendo +# Company Ltd., and are protected by Federal copyright law. They may +# not be disclosed to third parties or copied or duplicated in any form, +# in whole or in part, without the prior written consent of Nintendo. +# +# $Log: $ +# $NoKeywords: $ +#--------------------------------------------------------------------------- +MEMORY +{ + (RWX) : ORIGIN = , LENGTH = 0x0 # > + + (RWX) : ORIGIN = , LENGTH = 0x0 # > + +# binary.AUTOLOAD_INFO (RWX) : ORIGIN = 0, LENGTH = 0x0 > +# binary.STATIC_FOOTER (RWX) : ORIGIN = 0, LENGTH = 0x0 >> + + (RW) : ORIGIN = AFTER(), LENGTH = 0x0 > + (RW) : ORIGIN = AFTER(), LENGTH = 0x0 > + + (RWXO): ORIGIN = , LENGTH = 0x0 > + + dummy.MAIN_EX (RW) : ORIGIN = 0x023e0000, LENGTH = 0x0 + arena.MAIN (RW) : ORIGIN = AFTER(,), LENGTH = 0x0 + arena.MAIN_EX (RW) : ORIGIN = AFTER(dummy.MAIN_EX,), LENGTH = 0x0 + arena.ITCM (RW) : ORIGIN = AFTER(ITCM,), LENGTH = 0x0 + arena.DTCM (RW) : ORIGIN = AFTER(DTCM,), LENGTH = 0x0 + binary.MODULE_FILES (RW) : ORIGIN = 0x0, LENGTH = 0x0 > component.files + check.ITCM (RWX) : ORIGIN = 0x0, LENGTH = 0x08000 > itcm.check + check.DTCM (RW) : ORIGIN = 0x0, LENGTH = 0x04000 > dtcm.check +} + +FORCE_ACTIVE +{ + SVC_SoftReset +} + +KEEP_SECTION +{ + .sinit +} + +SECTIONS +{ + ############################ STATIC ################################# + .: + { + ALIGNALL(4); . = ALIGN(32); # Fit to cache line + + + + # + # TEXT BLOCK: READ ONLY + # + SDK_STATIC_START =.; + SDK_STATIC_TEXT_START =.; + #:::::::::: text/rodata + crt0.o (.text) + libsyscall.a (.text) + crt0.o (.rodata) + # + # .version セクションを追加しています。 + # このセクションに含まれる情報はロットチェックの際に + # 必要となりますので、必ずこの位置に残すようにして下さい。 + # + * (.version) + OBJECT(TwlMain,*) + + + + + + + . = ALIGN(4); + * (.exception) + . = ALIGN(4); + SDK_STATIC_ETABLE_START =.; + EXCEPTION + SDK_STATIC_ETABLE_END =.; + . = ALIGN(4); + + + + + + + . = ALIGN(4); + + + + + + + . = ALIGN(4); + + SDK_STATIC_SINIT_START =.; + #:::::::::: ctor + + + + + + + + + + + + + WRITEW 0; + #:::::::::: ctor + SDK_STATIC_SINIT_END =.; + + #:::::::::: text/rodata + . = ALIGN(32); + SDK_STATIC_TEXT_END =.; + + # + # DATA BLOCK: READ WRITE + # + SDK_STATIC_DATA_START =.; + #:::::::::: data + + + + + + + . = ALIGN(4); + + + + + + + . = ALIGN(4); + SDK_OVERLAY_DIGEST =.; + # NO DIGEST + SDK_OVERLAY_DIGEST_END =.; + #:::::::::: data + . = ALIGN(32); + SDK_STATIC_DATA_END =.; + SDK_STATIC_END =.; + + SDK_STATIC_TEXT_SIZE = SDK_STATIC_TEXT_END - SDK_STATIC_TEXT_START; + SDK_STATIC_DATA_SIZE = SDK_STATIC_DATA_END - SDK_STATIC_DATA_START; + SDK_STATIC_SIZE = SDK_STATIC_END - SDK_STATIC_START; + __sinit__ = SDK_STATIC_SINIT_START; # for static initializer + __exception_table_start__ = SDK_STATIC_ETABLE_START; # for exception table + __exception_table_end__ = SDK_STATIC_ETABLE_END; # for exception table + } > + + ..bss: + { + ALIGNALL(4); . = ALIGN(32); + + + + # + # BSS BLOCK + # + SDK_STATIC_BSS_START =.; + #:::::::::: bss + + + + + + + . = ALIGN(4); + + + + + + + . = ALIGN(4); + #:::::::::: bss + . = ALIGN(32); + SDK_STATIC_BSS_END = .; + SDK_STATIC_BSS_SIZE = SDK_STATIC_BSS_END - SDK_STATIC_BSS_START; + + } >> + + + ############################ AUTOLOADS ############################## + SDK_AUTOLOAD.ITCM.START = 0x01ff8000; + SDK_AUTOLOAD.ITCM.END = SDK_AUTOLOAD.ITCM.START; + SDK_AUTOLOAD.ITCM.BSS_END = SDK_AUTOLOAD.ITCM.START; + SDK_AUTOLOAD.ITCM.SIZE = 0; + SDK_AUTOLOAD.ITCM.BSS_SIZE = 0; + SDK_AUTOLOAD.DTCM.START = 0x027e0000; + SDK_AUTOLOAD.DTCM.END = SDK_AUTOLOAD.DTCM.START; + SDK_AUTOLOAD.DTCM.BSS_END = SDK_AUTOLOAD.DTCM.START; + SDK_AUTOLOAD.DTCM.SIZE = 0; + SDK_AUTOLOAD.DTCM.BSS_SIZE = 0; + SDK_AUTOLOAD_START = SDK_STATIC_END; + SDK_AUTOLOAD_SIZE = 0; + SDK_AUTOLOAD_NUMBER = ; + + + .: + { + ALIGNALL(4); . = ALIGN(32); + + + + # + # TEXT BLOCK: READ ONLY + # + SDK_AUTOLOAD__ID =; + SDK_AUTOLOAD..ID =; + SDK_AUTOLOAD..START =.; + SDK_AUTOLOAD..TEXT_START =.; + #:::::::::: text/rodata + + + + + + + . = ALIGN(4); + + + + + + + . = ALIGN(4); + + + + + + + . = ALIGN(4); + #:::::::::: text/rodata + SDK_AUTOLOAD..TEXT_END =.; + + # + # DATA BLOCK: READ WRITE BLOCK + # + SDK_AUTOLOAD..DATA_START =.; + #:::::::::: data + + + + + + + . = ALIGN(4); + + + + + + + . = ALIGN(4); + + + + + + + . = ALIGN(4); + #:::::::::: data + . = ALIGN(32); + SDK_AUTOLOAD..DATA_END =.; + SDK_AUTOLOAD..END =.; + + SDK_AUTOLOAD..TEXT_SIZE = SDK_AUTOLOAD..TEXT_END - SDK_AUTOLOAD..TEXT_START; + SDK_AUTOLOAD..DATA_SIZE = SDK_AUTOLOAD..DATA_END - SDK_AUTOLOAD..DATA_START; + SDK_AUTOLOAD..SIZE = SDK_AUTOLOAD..END - SDK_AUTOLOAD..START; + SDK_AUTOLOAD_SIZE = SDK_AUTOLOAD_SIZE + SDK_AUTOLOAD..SIZE; + + } > + + ..bss: + { + ALIGNALL(4); . = ALIGN(32); + + + + # + # BSS BLOCK + # + SDK_AUTOLOAD..BSS_START = .; + #:::::::::: bss + + + + + + + . = ALIGN(4); + + + + + + + . = ALIGN(4); + + + + + + + . = ALIGN(4); + + + + + + + . = ALIGN(4); + #:::::::::: bss + . = ALIGN(32); + SDK_AUTOLOAD..BSS_END = .; + + SDK_AUTOLOAD..BSS_SIZE = SDK_AUTOLOAD..BSS_END - SDK_AUTOLOAD..BSS_START; + + } >> + + + + SDK_AUTOLOAD_ITCM_START = SDK_AUTOLOAD.ITCM.START; + SDK_AUTOLOAD_ITCM_END = SDK_AUTOLOAD.ITCM.END; + SDK_AUTOLOAD_ITCM_BSS_END = SDK_AUTOLOAD.ITCM.BSS_END; + SDK_AUTOLOAD_ITCM_SIZE = SDK_AUTOLOAD.ITCM.SIZE; + SDK_AUTOLOAD_ITCM_BSS_SIZE = SDK_AUTOLOAD.ITCM.BSS_SIZE; + SDK_AUTOLOAD_DTCM_START = SDK_AUTOLOAD.DTCM.START; + SDK_AUTOLOAD_DTCM_END = SDK_AUTOLOAD.DTCM.END; + SDK_AUTOLOAD_DTCM_BSS_END = SDK_AUTOLOAD.DTCM.BSS_END; + SDK_AUTOLOAD_DTCM_SIZE = SDK_AUTOLOAD.DTCM.SIZE; + SDK_AUTOLOAD_DTCM_BSS_SIZE = SDK_AUTOLOAD.DTCM.BSS_SIZE; + + ############################ AUTOLOAD_INFO ########################## + .binary.AUTOLOAD_INFO: + { + SDK_AUTOLOAD_LIST = .; + + WRITEW ADDR(.); + WRITEW SDK_AUTOLOAD..SIZE; + WRITEW SDK_AUTOLOAD..BSS_SIZE; + + SDK_AUTOLOAD_LIST_END = .; + } >> # > binary.AUTOLOAD_INFO + +# SDK_AUTOLOAD_LIST = SDK_AUTOLOAD_START + SDK_AUTOLOAD_SIZE; +# SDK_AUTOLOAD_LIST_END = SDK_AUTOLOAD_START + SDK_AUTOLOAD_SIZE + SIZEOF(.binary.AUTOLOAD_INFO); + SDK_AUTOLOAD_SIZE = SDK_AUTOLOAD_SIZE + SIZEOF(.binary.AUTOLOAD_INFO); + + ############################ STATIC_FOOTER ########################## + .binary.STATIC_FOOTER: + { + WRITEW 0xdec00621; # LE(0x2106C0DE) = NITRO CODE + WRITEW _start_ModuleParams - ADDR(.); + WRITEW 0; # NO DIGEST + } >> # > binary.STATIC_FOOTER + + ############################ OVERLAYS ############################### + SDK_OVERLAY_NUMBER = ; + + + .: + { + ALIGNALL(4); . = ALIGN(32); + + + + # + # TEXT BLOCK: READ ONLY + # + SDK_OVERLAY__ID =; ### SEGMENT OVERLAY ID + SDK_OVERLAY..ID =; + SDK_OVERLAY..START =.; + SDK_OVERLAY..TEXT_START =.; + #:::::::::: text/rodata + + + + + + + . = ALIGN(4); + + + + + + + . = ALIGN(4); + + + + + + + . = ALIGN(4); + SDK_OVERLAY..SINIT_START =.; + #:::::::::: ctor + + + + + + + + + + + + + WRITEW 0; + #:::::::::: ctor + SDK_OVERLAY..SINIT_END =.; + + #:::::::::: text/rodata + . = ALIGN(32); + SDK_OVERLAY..TEXT_END =.; + + # + # DATA BLOCK: READ WRITE + # + SDK_OVERLAY..DATA_START =.; + #:::::::::: data + + + + + + + . = ALIGN(4); + + + + + + + . = ALIGN(4); + #:::::::::: data + . = ALIGN(32); + SDK_OVERLAY..DATA_END =.; + SDK_OVERLAY..END =.; + + SDK_OVERLAY..TEXT_SIZE = SDK_OVERLAY..TEXT_END - SDK_OVERLAY..TEXT_START; + SDK_OVERLAY..DATA_SIZE = SDK_OVERLAY..DATA_END - SDK_OVERLAY..DATA_START; + SDK_OVERLAY..SIZE = SDK_OVERLAY..END - SDK_OVERLAY..START; + + } > + + ..bss: + { + ALIGNALL(4); . = ALIGN(32); + + + + # + # BSS BLOCK + # + SDK_OVERLAY..BSS_START = .; + #:::::::::: bss + + + + + + + . = ALIGN(4); + + + + + + + . = ALIGN(4); + #:::::::::: bss + . = ALIGN(32); + SDK_OVERLAY..BSS_END = .; + + SDK_OVERLAY..BSS_SIZE = SDK_OVERLAY..BSS_END - SDK_OVERLAY..BSS_START; + + } >> + + + + ############################ MAIN EX ################################## + # MAIN EX Area + .dummy.MAIN_EX: + { + . = ALIGN(32); + } > dummy.MAIN_EX + + ############################ ARENA ################################## + .arena.MAIN: + { + . = ALIGN(32); + SDK_SECTION_ARENA_START =.; + } > arena.MAIN + + .arena.MAIN_EX: + { + . = ALIGN(32); + SDK_SECTION_ARENA_EX_START =.; + } > arena.MAIN_EX + + .arena.ITCM: + { + . = ALIGN(32); + SDK_SECTION_ARENA_ITCM_START =.; + } > arena.ITCM + + .arena.DTCM: + { + . = ALIGN(32); + SDK_SECTION_ARENA_DTCM_START =.; + } > arena.DTCM + + ############################ OVERLAYDEFS ############################ + .: + { + ### module information + WRITEW ADDR(.); # load address + WRITEW _start; # entry address + WRITEW SDK_STATIC_SIZE + SDK_AUTOLOAD_SIZE; # size of module + WRITEW _start_AutoloadDoneCallback; # callback autoload done + + ### overlay filename + + WRITES (""); # Overlay + + + } > + + + ############################ OVERLAYTABLE ########################### + .: + { + + # Overlay + WRITEW ; # overlay ID + WRITEW ADDR(.); # load address + WRITEW SDK_OVERLAY..SIZE; # size of module + WRITEW SDK_OVERLAY..BSS_SIZE; # size of bss + WRITEW SDK_OVERLAY..SINIT_START; # start address of static init + WRITEW SDK_OVERLAY..SINIT_END; # end address of static init + WRITEW ; # ROM file ID + WRITEW 0; # Reserved + + + + } > + + + ############################ OTHERS ################################# + SDK_MAIN_ARENA_LO = SDK_SECTION_ARENA_START; + SDK_IRQ_STACKSIZE = ; # allocated in DTCM + SDK_SYS_STACKSIZE = ; # when 0 means all remains of DTCM + + # Module filelist + .binary.MODULE_FILES: + { + WRITES (""); + WRITES (""); + WRITES (""); + } > binary.MODULE_FILES + + # ITCM/DTCM size checker => check AUTOLOAD_ITCM/DTCM + .check.ITCM: + { + . = . + SDK_AUTOLOAD_ITCM_SIZE + SDK_AUTOLOAD_ITCM_BSS_SIZE; + } > check.ITCM + + SDK_SYS_STACKSIZE_SIGN = (SDK_SYS_STACKSIZE < 0x80000000) * 2 - 1; + .check.DTCM: + { + . = . + SDK_AUTOLOAD_DTCM_SIZE + SDK_AUTOLOAD_DTCM_BSS_SIZE; + . = . + SDK_IRQ_STACKSIZE + SDK_SYS_STACKSIZE * SDK_SYS_STACKSIZE_SIGN; + } > check.DTCM + +} diff --git a/include/firm/specfiles/ARM9-TS-NORFIRM.lsf b/include/firm/specfiles/ARM9-TS-NORFIRM.lsf new file mode 100644 index 00000000..05d913a8 --- /dev/null +++ b/include/firm/specfiles/ARM9-TS-NORFIRM.lsf @@ -0,0 +1,41 @@ +#---------------------------------------------------------------------------- +# Project: TwlFirm - include +# File: ARM9-TS-NORFIRM.lsf +# +# Copyright 2007 Nintendo. All rights reserved. +# +# These coded insructions, statements, and computer programs contain +# proprietary information of Nintendo of America Inc. and/or Nintendo +# Company Ltd., and are protected by Federal copyright law. They may +# not be disclosed to third parties or copied or duplicated in any form, +# in whole or in part, without the prior written consent of Nintendo. +# +# $Log: $ +# $NoKeywords: $ +#---------------------------------------------------------------------------- +# +# TwlFirm LCF SPEC FILE +# + +Static $(TARGET_NAME) +{ + Address 0x03800000 + Object $(OBJS_STATIC) + Library $(LLIBS) $(GLIBS) $(CW_LIBS) +} + +Autoload ITCM +{ + Address 0x01ff8000 + Object * (.itcm) + Object $(OBJS_AUTOLOAD) (.text) +} + +Autoload DTCM +{ + Address 0x027e0000 + Object * (.dtcm) + Object $(OBJS_AUTOLOAD) (.data) + Object $(OBJS_AUTOLOAD) (.bss) +} + diff --git a/include/twl/os/common/systemCall.h b/include/twl/os/common/systemCall.h new file mode 100644 index 00000000..ed83d483 --- /dev/null +++ b/include/twl/os/common/systemCall.h @@ -0,0 +1,92 @@ +/*---------------------------------------------------------------------------* + Project: TwlSDK - OS - include + File: systemCall.h + + Copyright 2007 Nintendo. All rights reserved. + + These coded instructions, statements, and computer programs contain + proprietary information of Nintendo of America Inc. and/or Nintendo + Company Ltd., and are protected by Federal copyright law. They may + not be disclosed to third parties or copied or duplicated in any form, + in whole or in part, without the prior written consent of Nintendo. + + $Log: $ + $NoKeywords: $ + *---------------------------------------------------------------------------*/ + +#ifndef TWL_OS_SYSTEMCALL_H_ +#define TWL_OS_SYSTEMCALL_H_ + + +#ifdef __cplusplus +extern "C" { +#endif + +int SVC_InitSignHeap( + int acmemory_pool[3], + void* heap, + unsigned int length + ); + +int SVC_DecryptoRSA( + const void* acmemory_pool, + const void* pData, + unsigned int* len // 出力サイズ + ); + +int SVC_DecryptoSign( + const void* acmemory_pool, + void* buffer, // 出力領域 + const void* sgn_ptr, // データへのポインタ + const void* key_ptr // キーへのポインタ + ); + +int SVC_DecryptoSignDER( + const void* acmemory_pool, + void* buffer, // 出力領域 + const void* sgn_ptr, // データへのポインタ + const void* key_ptr // キーへのポインタ + ); + +void SVC_SHA1Init( void *c ); +void SVC_SHA1Update( void *c, const unsigned char *data, unsigned long len ); +void SVC_SHA1GetHash( unsigned char *md, void *c ); + +int SVC_CalcSHA1( + void* buffer, // 出力領域 + const void* buf, // データへのポインタ + unsigned int len // データの長さ + ); + +int SVC_CompareSHA1( + const void* decedHash, // SVC_Decrypto*の出力 + const void* digest // SVC_GetDigestの出力 + ); + +int SVC_RandomSHA1( + void* dest_ptr, // 出力データへのポインタ + unsigned int dest_len, // 出力データの長さ + const void* src_ptr, // 入力データへのポインタ + unsigned int src_len // 入力データの長さ + ); + +int SVC_UncompressLZ8FromDevice( const void* srcp, + void* destp, + const void* paramp, + const MIReadStreamCallbacks *callbacks + ); + +int SVC_UncompressLZ16FromDeviceIMG( const void* srcp, + void* destp, + const void* paramp, + const MIReadStreamCallbacks *callbacks + ); + + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +/* TWL_OS_SYSTEMCALL_H_ */ +#endif diff --git a/tools/bin/makenorfirm.exe b/tools/bin/makenorfirm.exe new file mode 100644 index 0000000000000000000000000000000000000000..743abd883b1a96fc62c41340ae6b06e242640fce GIT binary patch literal 105611 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≫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 Hh)wuk5d@pe literal 0 HcmV?d00001 diff --git a/tools/openssl/openssl.exe b/tools/openssl/openssl.exe new file mode 100644 index 0000000000000000000000000000000000000000..8967c31685635cce6f45ecac0dae00f79faff51c GIT binary patch literal 286720 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 z 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 literal 0 HcmV?d00001