diff --git a/README.md b/README.md index 8998b80..2e199c4 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ but also many more coreboot distributions like heads. ## Usage ```bash -usage: openness_score.py [-o OUTPUT] [-v] [-V] [file] +usage: openness_score.py [-c proprietary_file] [-p PLATFORM] [-o OUTPUT] [-v] [-m [-V] [file] Calculate Dasharo Openness Score for firmware images @@ -32,6 +32,10 @@ positional arguments: file Firmware binary file to be parsed options: + -c proprietary_file, --compare proprietary_file + Compare Dasharo and proprietary firmware scores and store result in a Markdown table. file should be the Dasharo binary and proprietary_file should be the proprietary firmware binary. + -p PLATFORM, --platform PLATFORM + Platform model to provide to the table when --compare is set. -o OUTPUT, --output OUTPUT Specifies the directory where to store the results -a MICROARCH, --microarch MICROARCH @@ -75,6 +79,33 @@ The utility will produce 3 files: precise numbers and detailed classification of firmware image components to closed-source, open-source, data and empty categories +Example with `--compare` flag: + +```bash +./openness_score/openness_score.py msi_ms7d25_v1.1.4_ddr4.rom -c E7D25IMS.1M1 -p "MS-7D25" +``` + +Aside from the 3 files mentioned above, this will also produce `compare.md` - A +Markdown table containing the score comparison between the two binaries. If the +file already exists, the result will be appended as a single row. + +The table contains the following metrics: + +- `closed-source diff` +- `data size diff` +- `empty space diff` + +Each metric is calculated using the formula: + +```txt +(Dasharo size - Proprietary size) * 100 / Proprietary size +``` + +`` is replaced by `closed-source`, `data` or `empty space` accordingly. + +You can use `scripts/compare.sh` to generate a comparison table for common +Dasharo platforms. For more information, see the script's help. + **The utility currently supports coreboot and pure UEFI images only.** ### Examples diff --git a/openness_score/openness_score.py b/openness_score/openness_score.py index 33befa6..acbfd68 100755 --- a/openness_score/openness_score.py +++ b/openness_score/openness_score.py @@ -122,6 +122,57 @@ def export_data(args, image): image.export_charts(output_path) +def compare_scores(dasharo_image, proprietary_image): + """Compares metrics between Dasharo and proprietary firmware. + + :param dasharo_image: Dasharo image + :type dasharo_image: DasharoCorebootImage + :param proprietary_image: Proprietary firmware image + :type proprietary_image: DasharoCorebootImage + + :return: Three metrics as percentages rounded to 1 decimal: Closed source difference, Data size difference, Empty space size difference + :rtype: float, float, float + """ + closed_source_diff = 100 * (dasharo_image.closed_code_size - proprietary_image.closed_code_size) / proprietary_image.closed_code_size + data_size_diff = 100 * (dasharo_image.data_size - proprietary_image.data_size) / proprietary_image.data_size + empty_diff = 100 * (dasharo_image.empty_size - proprietary_image.empty_size) / proprietary_image.empty_size + return round(closed_source_diff, 1), round(data_size_diff, 1), round(empty_diff, 1) + + +def export_compared_data(args, dasharo_image, proprietary_image): + """Calls `openness_score.compare_scores` and saves the output to a Markdown file. + + :param args: Program arguments + :type args: argparse.Namespace + :param dasharo_image: Dasharo image + :type dasharo_image: DasharoCorebootImage + :param proprietary_image: Proprietary firmware image + :type proprietary_image: DasharoCorebootImage + """ + output_path = Path.cwd() + + if args.output is not None: + output_path = Path(args.output) + try: + output_path.mkdir(parents=True, exist_ok=True) + except PermissionError: + sys.exit('You do not have permission to write to %s' % args.output) + + closed_source_diff, data_size_diff, empty_diff = compare_scores(dasharo_image, proprietary_image) + dasharo_name = Path(args.file).name + proprietary_name = Path(args.compare).name + output_file = output_path.joinpath('compare.md') + if Path(output_file).is_file(): + with open(output_file, 'a') as md: + md.write("| %s | %s | %s | %s | %s | %s |\n" % (args.platform, dasharo_name, proprietary_name, closed_source_diff, data_size_diff, empty_diff)) + else: + with open(output_file, 'w') as md: + md.write("# Openness score comparison table\n\n") + md.write("|Platform | Dasharo Firmware file | Proprietary Firmware file | closed-source diff [%] | data size diff [%] | empty space diff [%] |\n") + md.write("| --- | --- | --- | --- | --- | --- |\n") + md.write("| %s | %s | %s | %s | %s | %s |\n" % (args.platform, dasharo_name, proprietary_name, closed_source_diff, data_size_diff, empty_diff)) + + def OpennessScore(): """Utility's entry point responsible for argument parsing and creating firmware image class instances based on detected image format. @@ -133,6 +184,10 @@ def OpennessScore(): parser.add_argument('file', help='Firmware binary file to be parsed', nargs='?') + parser.add_argument('-c', '--compare', metavar='proprietary_file', + help='Compare Dasharo and proprietary firmware scores and store result in a Markdown table.' \ + ' file should be the Dasharo binary and proprietary_file should be the proprietary firmware binary.') + parser.add_argument('-p', '--platform', default='-', help='Platform model to provide to the table when --compare is set.') parser.add_argument('-h', '--help', action='help', help=SUPPRESS) parser.add_argument('-o', '--output', default='out/', help='\n'.join([ 'Specifies the directory where to store the results'])) @@ -156,20 +211,33 @@ def OpennessScore(): parser.print_help(sys.stderr) sys.exit(0) - fw_is_cbfs, fw_is_uefi = check_file(args.file) - - if fw_is_cbfs: - print('\'%s\' detected as Dasharo image' % args.file) - print('\n\n\'%s\' Dasharo image statistics:' % args.file) - DasharoCbImg = DasharoCorebootImage(args.file, args.verbose, args.microarch) - print(DasharoCbImg) - export_data(args, DasharoCbImg) - elif fw_is_uefi: - print('\'%s\' detected as vendor image' % args.file) - print('\n\n\'%s\' vendor image statistics:' % args.file) - VendorImg = UEFIImage(args.file, args.verbose) - print(VendorImg) - export_data(args, VendorImg) + if args.compare: + fw_is_cbfs, _ = check_file(args.file) + if not fw_is_cbfs: + print("File must be Dasharo!") + sys.exit(1) + DasharoImage = DasharoCorebootImage(args.file, args.verbose) + fw_is_cbfs, fw_is_uefi = check_file(args.compare) + if fw_is_cbfs: + ProprietaryImage = DasharoCorebootImage(args.compare, args.verbose) + elif fw_is_uefi: + ProprietaryImage = UEFIImage(args.compare, args.verbose) + export_compared_data(args, DasharoImage, ProprietaryImage) + else: + fw_is_cbfs, fw_is_uefi = check_file(args.file) + + if fw_is_cbfs: + print('\'%s\' detected as Dasharo image' % args.file) + print('\n\n\'%s\' Dasharo image statistics:' % args.file) + DasharoCbImg = DasharoCorebootImage(args.file, args.verbose, args.microarch) + print(DasharoCbImg) + export_data(args, DasharoCbImg) + elif fw_is_uefi: + print('\'%s\' detected as vendor image' % args.file) + print('\n\n\'%s\' vendor image statistics:' % args.file) + VendorImg = UEFIImage(args.file, args.verbose) + print(VendorImg) + export_data(args, VendorImg) if __name__ == '__main__': diff --git a/scripts/compare.sh b/scripts/compare.sh new file mode 100755 index 0000000..2d9b73b --- /dev/null +++ b/scripts/compare.sh @@ -0,0 +1,197 @@ +#!/bin/bash + +MINIO_CREDS="$1" +NC_CREDS="$2" +NO_DOWNLOAD="${3:-false}" + +BINDIR="binaries" + +usage() { +cat <", + "password": "" +} + +Replace and with appropriate credentials. +EOF +} + +download_protectli() { + mkdir -p "$BINDIR/protectli" + repo_path="/tmp/protectli-firmware-updater" + git clone https://github.com/protectli-root/protectli-firmware-updater.git "$repo_path" + cp "$repo_path/images/fw6_all_YKR6LV30.bin" "$repo_path/images/protectli_all_fw6_vault_kbl_v1.0.14.rom" \ + "$repo_path/images/protectli_v1210_v0.9.3.rom" "$repo_path/images/v1210_JPL.2LAN.S4G.PCIE.6W.013.bin" \ + "$repo_path/images/protectli_v1211_v0.9.3.rom" "$repo_path/images/v1211_JPL.2LAN.D8G.PCIE.6W.009.bin"\ + "$repo_path/images/protectli_v1410_v0.9.3.rom" "$repo_path/images/v1410_JPL.4LAN.S8GB.PCIE.6W.007B.bin"\ + "$repo_path/images/protectli_v1610_v0.9.3.rom" "$repo_path/images/v1610_JPL.6LAN.D16G.PCIE.007.bin" \ + "$repo_path/images/protectli_vp2410_v1.1.1.rom" "$repo_path/images/vp2410_GLK4L280.bin" \ + "$repo_path/images/protectli_vp2420_v1.2.1.rom" "$repo_path/images/vp2420_YELD4L13P.bin" \ + "$repo_path/images/protectli_vp2430_v0.9.0.rom" "$repo_path/images/vp2430_PRALNDZ4L10.bin" \ + "$repo_path/images/protectli_vp4600_v1.2.0.rom" "$repo_path/images/vp4630_v2_YW6L2318.bin" \ + "$repo_path/images/protectli_vp6600_v0.9.2.rom" "$repo_path/images/vp6630_ADZ6L314.bin" \ + "$BINDIR/protectli" +} + +compare_protectli() { + ./openness_score/openness_score.py "$BINDIR/protectli/protectli_all_fw6_vault_kbl_v1.0.14.rom" \ + -c "$BINDIR/protectli/fw6_all_YKR6LV30.bin" -p "Protectli FW6" -a "sklkbl" + ./openness_score/openness_score.py "$BINDIR/protectli/protectli_v1210_v0.9.3.rom" \ + -c "$BINDIR/protectli/v1210_JPL.2LAN.S4G.PCIE.6W.013.bin" -p "Protectli V1210" -a "jsl" + ./openness_score/openness_score.py "$BINDIR/protectli/protectli_v1211_v0.9.3.rom" \ + -c "$BINDIR/protectli/v1211_JPL.2LAN.D8G.PCIE.6W.009.bin" -p "Protectli V1211" -a "jsl" + ./openness_score/openness_score.py "$BINDIR/protectli/protectli_v1410_v0.9.3.rom" \ + -c "$BINDIR/protectli/v1410_JPL.4LAN.S8GB.PCIE.6W.007B.bin" -p "Protectli V1410" -a "jsl" + ./openness_score/openness_score.py "$BINDIR/protectli/protectli_v1610_v0.9.3.rom" \ + -c "$BINDIR/protectli/v1610_JPL.6LAN.D16G.PCIE.007.bin" -p "Protectli V1610" -a "jsl" + ./openness_score/openness_score.py "$BINDIR/protectli/protectli_vp2410_v1.1.1.rom" \ + -c "$BINDIR/protectli/vp2410_GLK4L280.bin" -p "Protectli VP2410" -a "glk" + ./openness_score/openness_score.py "$BINDIR/protectli/protectli_vp2420_v1.2.1.rom" \ + -c "$BINDIR/protectli/vp2420_YELD4L13P.bin" -p "Protectli VP2420" -a "ehl" + ./openness_score/openness_score.py "$BINDIR/protectli/protectli_vp2430_v0.9.0.rom" \ + -c "$BINDIR/protectli/vp2430_PRALNDZ4L10.bin" -p "Protectli VP2430" -a "adl" + ./openness_score/openness_score.py "$BINDIR/protectli/protectli_vp4600_v1.2.0.rom" \ + -c "$BINDIR/protectli/vp4630_v2_YW6L2318.bin" -p "Protectli VP46XX" -a "cnl" + ./openness_score/openness_score.py "$BINDIR/protectli/protectli_vp6600_v0.9.2.rom" \ + -c "$BINDIR/protectli/vp6630_ADZ6L314.bin" -p "Protectli VP66XX" -a "adl" +} + +download_msi() { + mkdir -p "$BINDIR/msi" + + bios_url="https://download.msi.com/bos_exe/mb" + minio_bucket="dasharo-msi-uefi" + + # Z690-A DDR4 + wget -O "/tmp/7D25v1L.zip" "$bios_url/7D25v1L.zip" + unzip "/tmp/7D25v1L.zip" -d "/tmp" + cp "/tmp/7D25v1L/E7D25IMS.1L0" "$BINDIR/msi" + + # Z690-A-WIFI + wget -O "/tmp/7D25vAL.zip" "$bios_url/7D25vAL.zip" + unzip "/tmp/7D25vAL.zip" -d "/tmp" + cp "/tmp/7D25vAL/E7D25IMS.AL0" "$BINDIR/msi" + + # Z790-P DDR4 + wget -O "/tmp/7E06v1F.zip" "$bios_url/7E06v1F.zip" + unzip "/tmp/7E06v1F.zip" -d "/tmp" + cp "/tmp/7E06v1F/E7E06IMS.1F0" "$BINDIR/msi" + + # Z790-P WIFI + wget -O "/tmp/7E06vAH.zip" "$bios_url/7E06vAH.zip" + unzip "/tmp/7E06vAH.zip" -d "/tmp" + cp "/tmp/7E06vAH/E7E06IMS.AH0" "$BINDIR/msi" + + mc get "openness-score/$minio_bucket/MS-7D25/v1.1.4/msi_ms7d25_v1.1.4_ddr4.rom" "$BINDIR/msi" + mc get "openness-score/$minio_bucket/MS-7D25/v1.1.4/msi_ms7d25_v1.1.4_ddr5.rom" "$BINDIR/msi" + mc get "openness-score/$minio_bucket/MS-7E06/v0.9.2/msi_ms7e06_v0.9.2_ddr4.rom" "$BINDIR/msi" + mc get "openness-score/$minio_bucket/MS-7E06/v0.9.2/msi_ms7e06_v0.9.2_ddr5.rom" "$BINDIR/msi" +} + +compare_msi() { + ./openness_score/openness_score.py "$BINDIR/msi/msi_ms7d25_v1.1.4_ddr4.rom" -c "$BINDIR/msi/E7D25IMS.1L0" -p "MSI MS-7D25 DDR4" -a "adl" + ./openness_score/openness_score.py "$BINDIR/msi/msi_ms7d25_v1.1.4_ddr5.rom" -c "$BINDIR/msi/E7D25IMS.AL0" -p "MSI MS-7D25 DDR5" -a "adl" + ./openness_score/openness_score.py "$BINDIR/msi/msi_ms7e06_v0.9.2_ddr4.rom" -c "$BINDIR/msi/E7E06IMS.1F0" -p "MSI MS-7E06 DDR4" -a "adl" + ./openness_score/openness_score.py "$BINDIR/msi/msi_ms7e06_v0.9.2_ddr4.rom" -c "$BINDIR/msi/E7E06IMS.AH0" -p "MSI MS-7E06 DDR5" -a "adl" +} + +download_novacustom() { + mkdir -p "$BINDIR/novacustom" + bios_url="https://repo.palkeo.com/clevo-mirror" + dasharo_url="https://dl.3mdeb.com/open-source-firmware/Dasharo" + + wget_cmd="wget --user $NC_LOGIN --password $NC_PASSWORD" + + # V54/56 MTL + $wget_cmd -O "/tmp/B10709.zip" "$bios_url/Vxx0TU/B10709.zip" + unzip "/tmp/B10709.zip" -d "/tmp" + cp "/tmp/B10709/V5xxTU(32M).09" "$BINDIR/novacustom" + + # NV4x ADL + $wget_cmd -O "/tmp/B10703.zip" "$bios_url/NV4xPZ/B10703.zip" + unzip "/tmp/B10703.zip" -d "/tmp" + cp "/tmp/B10703/ALL/NV4xPZ(32M).03" "$BINDIR/novacustom" + + # NV4x TGL + $wget_cmd -O "/tmp/B10705.zip" "$bios_url/NV4xMB_ME/B10705.zip" + unzip "/tmp/B10705.zip" -d "/tmp" + cp "/tmp/B10705/ALL/NV4XBX.05" "$BINDIR/novacustom" + + # NS5x ADL + $wget_cmd -O "/tmp/B10709.zip" "$bios_url/NSxxPU/B10709.zip" + unzip "/tmp/B10709.zip" -d "/tmp" + cp "/tmp/B10709/ALL/NSx0PU(32M).09" "$BINDIR/novacustom" + + # NS5x TGL + $wget_cmd -O "/tmp/B10716N.zip" "$bios_url/NS5xMU/B10716N.zip" + unzip "/tmp/B10716N.zip" -d "/tmp" + cp "/tmp/NS50_70MU.16N/ALL/68C1/NS50_70MU.16N" "$BINDIR/novacustom" + + wget -O "$BINDIR/novacustom/novacustom_v54x_mtl_v0.9.0.rom" "$dasharo_url/novacustom_v54x_mtl/v0.9.0/novacustom_v54x_mtl_v0.9.0.rom" + wget -O "$BINDIR/novacustom/novacustom_v56x_mtl_v0.9.0.rom" "$dasharo_url/novacustom_v56x_mtl/v0.9.0/novacustom_v56x_mtl_v0.9.0.rom" + wget -O "$BINDIR/novacustom/novacustom_nv4x_adl_v1.7.2_full.rom" "$dasharo_url/novacustom_nv4x_adl/v1.7.2/novacustom_nv4x_adl_v1.7.2_full.rom" + wget -O "$BINDIR/novacustom/novacustom_nv4x_tgl_v1.5.2.rom" "$dasharo_url/novacustom_nv4x_tgl/v1.5.2/novacustom_nv4x_tgl_v1.5.2.rom" + wget -O "$BINDIR/novacustom/novacustom_ns5x_adl_v1.7.2.rom" "$dasharo_url/novacustom_ns5x_adl/v1.7.2/novacustom_ns5x_adl_v1.7.2.rom" + wget -O "$BINDIR/novacustom/novacustom_ns5x_tgl_v1.5.2.rom" "$dasharo_url/novacustom_ns5x_tgl/v1.5.2/novacustom_ns5x_tgl_v1.5.2.rom" +} + +compare_novacustom() { + ./openness_score/openness_score.py "$BINDIR/novacustom/novacustom_v54x_mtl_v0.9.0.rom" -c "$BINDIR/novacustom/V5xxTU(32M).09" -p "Novacustom V540TU" -a "mtl" + ./openness_score/openness_score.py "$BINDIR/novacustom/novacustom_v56x_mtl_v0.9.0.rom" -c "$BINDIR/novacustom/V5xxTU(32M).09" -p "Novacustom V560TU" -a "mtl" + ./openness_score/openness_score.py "$BINDIR/novacustom/novacustom_nv4x_adl_v1.7.2_full.rom" -c "$BINDIR/novacustom/NV4xPZ(32M).03" -p "Novacustom NV4xPZ" -a "adl" + ./openness_score/openness_score.py "$BINDIR/novacustom/novacustom_nv4x_tgl_v1.5.2.rom" -c "$BINDIR/novacustom/NV4XBX.05" -p "Novacustom NV4xME_MB" -a "tgl" + ./openness_score/openness_score.py "$BINDIR/novacustom/novacustom_ns5x_adl_v1.7.2.rom" -c "$BINDIR/novacustom/NSx0PU(32M).09" -p "Novacustom NSxxPU" -a "adl" + ./openness_score/openness_score.py "$BINDIR/novacustom/novacustom_ns5x_tgl_v1.5.2.rom" -c "$BINDIR/novacustom/NS50_70MU.16N" -p "Novacustom NS5xMU" -a "tgl" +} + +download_odroid() { + mkdir -p "$BINDIR/odroid" + bios_url="https://dn.odroid.com/ODROID-H4/bios" + minio_bucket="dasharo-odroid-h4-plus-uefi" + echo "Please download stock binaries from $bios_url to $BINDIR/odroid" + read -p "Press any key after downloading binaries" + + mc get "openness-score/$minio_bucket/hardkernel_odroid_h4/v0.9.0/hardkernel_odroid_h4_v0.9.0.rom" "$BINDIR/odroid" +} + +compare_odroid() { + ./openness_score/openness_score.py "$BINDIR/odroid/hardkernel_odroid_h4_v0.9.0.rom" -c "$BINDIR/odroid/ADLN-H4_B1.07.bin" -p "Odroid H4" -a "adl" +} + +if [ $# -lt 2 ]; then + usage + exit 1 +fi + +mkdir -p "$BINDIR" +MINIO_LOGIN=$(jq -r '.login' "$MINIO_CREDS") +MINIO_PASSWORD=$(jq -r '.password' "$MINIO_CREDS") +mc alias set openness-score "https://dl.dasharo.com" "$MINIO_LOGIN" "$MINIO_PASSWORD" + +NC_LOGIN=$(jq -r '.login' "$NC_CREDS") +NC_PASSWORD=$(jq -r '.password' "$NC_CREDS") + +if [ $NO_DOWNLOAD != "no-download" ]; then + download_msi + download_protectli + download_odroid + download_novacustom +fi +compare_msi +compare_protectli +compare_odroid +compare_novacustom