mirror of
https://github.com/NVIDIA/open-gpu-kernel-modules.git
synced 2026-01-26 19:19:47 +00:00
580.65.06
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
# SPDX-FileCopyrightText: Copyright (c) 2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
||||
# SPDX-FileCopyrightText: Copyright (c) 2023-2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
||||
# SPDX-License-Identifier: MIT
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a
|
||||
@@ -27,10 +27,12 @@
|
||||
import sys
|
||||
import os
|
||||
import argparse
|
||||
import shutil
|
||||
import re
|
||||
import gzip
|
||||
import struct
|
||||
import zlib
|
||||
import tempfile
|
||||
import urllib.request
|
||||
|
||||
class MyException(Exception):
|
||||
pass
|
||||
@@ -109,15 +111,15 @@ def getbytes(filename, array):
|
||||
return output
|
||||
else:
|
||||
if len(output) != data_size:
|
||||
raise MyException(f"array {array} in {filename} should be {compressed_size} bytes but is actually {len(output)}.")
|
||||
raise MyException(f"array {array} in {filename} should be {data_size} bytes but is actually {len(output)}.")
|
||||
return output
|
||||
|
||||
# GSP bootloader
|
||||
def bootloader(gpu, type):
|
||||
def bootloader(gpu, fuse):
|
||||
global outputpath
|
||||
global version
|
||||
|
||||
GPU=gpu.upper()
|
||||
GPU = gpu.upper()
|
||||
filename = f"src/nvidia/generated/g_bindata_kgspGetBinArchiveGspRmBoot_{GPU}.c"
|
||||
|
||||
print(f"Creating nvidia/{gpu}/gsp/bootloader-{version}.bin")
|
||||
@@ -125,12 +127,12 @@ def bootloader(gpu, type):
|
||||
|
||||
with open(f"{outputpath}/nvidia/{gpu}/gsp/bootloader-{version}.bin", "wb") as f:
|
||||
# Extract the actual bootloader firmware
|
||||
array = f"kgspBinArchiveGspRmBoot_{GPU}_ucode_image{type}data"
|
||||
array = f"kgspBinArchiveGspRmBoot_{GPU}_ucode_image{fuse}data"
|
||||
firmware = getbytes(filename, array)
|
||||
firmware_size = len(firmware)
|
||||
|
||||
# Extract the descriptor (RM_RISCV_UCODE_DESC)
|
||||
array = f"kgspBinArchiveGspRmBoot_{GPU}_ucode_desc{type}data"
|
||||
array = f"kgspBinArchiveGspRmBoot_{GPU}_ucode_desc{fuse}data"
|
||||
descriptor = getbytes(filename, array)
|
||||
descriptor_size = len(descriptor)
|
||||
|
||||
@@ -146,7 +148,7 @@ def bootloader(gpu, type):
|
||||
f.write(firmware)
|
||||
|
||||
# GSP Booter load and unload
|
||||
def booter(gpu, load, sigsize):
|
||||
def booter(gpu, load, sigsize, fuse = "prod"):
|
||||
global outputpath
|
||||
global version
|
||||
|
||||
@@ -159,13 +161,13 @@ def booter(gpu, load, sigsize):
|
||||
os.makedirs(f"{outputpath}/nvidia/{gpu}/gsp/", exist_ok = True)
|
||||
|
||||
with open(f"{outputpath}/nvidia/{gpu}/gsp/booter_{load}-{version}.bin", "wb") as f:
|
||||
# Extract the actual scrubber firmware
|
||||
array = f"kgspBinArchiveBooter{LOAD}Ucode_{GPU}_image_prod_data"
|
||||
# Extract the actual booter firmware
|
||||
array = f"kgspBinArchiveBooter{LOAD}Ucode_{GPU}_image_{fuse}_data"
|
||||
firmware = getbytes(filename, array)
|
||||
firmware_size = len(firmware)
|
||||
|
||||
# Extract the signatures
|
||||
array = f"kgspBinArchiveBooter{LOAD}Ucode_{GPU}_sig_prod_data"
|
||||
array = f"kgspBinArchiveBooter{LOAD}Ucode_{GPU}_sig_{fuse}_data"
|
||||
signatures = getbytes(filename, array)
|
||||
signatures_size = len(signatures)
|
||||
if signatures_size % sigsize:
|
||||
@@ -206,17 +208,17 @@ def booter(gpu, load, sigsize):
|
||||
f.write(struct.pack("<6L", patchloc, 0, fuse_ver, engine_id, ucode_id, num_sigs))
|
||||
|
||||
# Extract the descriptor (nvkm_gsp_booter_fw_hdr)
|
||||
array = f"kgspBinArchiveBooter{LOAD}Ucode_{GPU}_header_prod_data"
|
||||
array = f"kgspBinArchiveBooter{LOAD}Ucode_{GPU}_header_{fuse}_data"
|
||||
descriptor = getbytes(filename, array)
|
||||
|
||||
# Fifth, the descriptor
|
||||
f.write(descriptor)
|
||||
|
||||
# And finally, the actual scrubber image
|
||||
# And finally, the actual booter image
|
||||
f.write(firmware)
|
||||
|
||||
# GPU memory scrubber, needed for some GPUs and configurations
|
||||
def scrubber(gpu, sigsize):
|
||||
def scrubber(gpu, sigsize, fuse = "prod"):
|
||||
global outputpath
|
||||
global version
|
||||
|
||||
@@ -231,12 +233,12 @@ def scrubber(gpu, sigsize):
|
||||
|
||||
with open(f"{outputpath}/nvidia/{gpu}/gsp/scrubber-{version}.bin", "wb") as f:
|
||||
# Extract the actual scrubber firmware
|
||||
array = f"ksec2BinArchiveSecurescrubUcode_{GPUX}_image_prod_data[]"
|
||||
array = f"ksec2BinArchiveSecurescrubUcode_{GPUX}_image_{fuse}_data[]"
|
||||
firmware = getbytes(filename, array)
|
||||
firmware_size = len(firmware)
|
||||
|
||||
# Extract the signatures
|
||||
array = f"ksec2BinArchiveSecurescrubUcode_{GPUX}_sig_prod_data"
|
||||
array = f"ksec2BinArchiveSecurescrubUcode_{GPUX}_sig_{fuse}_data"
|
||||
signatures = getbytes(filename, array)
|
||||
signatures_size = len(signatures)
|
||||
if signatures_size % sigsize:
|
||||
@@ -277,7 +279,7 @@ def scrubber(gpu, sigsize):
|
||||
f.write(struct.pack("<6L", patchloc, 0, fuse_ver, engine_id, ucode_id, num_sigs))
|
||||
|
||||
# Extract the descriptor (nvkm_gsp_booter_fw_hdr)
|
||||
array = f"ksec2BinArchiveSecurescrubUcode_{GPUX}_header_prod_data"
|
||||
array = f"ksec2BinArchiveSecurescrubUcode_{GPUX}_header_{fuse}_data"
|
||||
descriptor = getbytes(filename, array)
|
||||
|
||||
# Fifth, the descriptor
|
||||
@@ -286,6 +288,191 @@ def scrubber(gpu, sigsize):
|
||||
# And finally, the actual scrubber image
|
||||
f.write(firmware)
|
||||
|
||||
ELF_HDR_SIZE = 52
|
||||
ELF_SHDR_SIZE = 40
|
||||
ELF_ALIGNMENT = 4
|
||||
|
||||
# Create a 32-bit generic ELF header with no program header, and 'shnum'
|
||||
# section headers, not including the .shstrtab and NULL sections.
|
||||
# The section headers appear after the ELF header, and the section data
|
||||
# follows. Note that e_shstrndx cannot be zero, because that implies
|
||||
# that the .shstrndx section does not exist.
|
||||
def elf_header(shnum: int):
|
||||
bytes = struct.pack("<B3s5B7xHH5I6H",
|
||||
0x7f, b'ELF',
|
||||
1, 1, 1, 0, 0, # EI_CLASS, EI_DATA, EI_VERSION, EI_OSABI, EI_ABIVERSION
|
||||
0, 0, 1, # e_type, e_machine, e_version
|
||||
0, 0, ELF_HDR_SIZE, 0, # e_entry, e_phoff, e_shoff, e_flags
|
||||
ELF_HDR_SIZE, 0, 0, # e_ehsize, e_phentsize, e_phnum
|
||||
ELF_SHDR_SIZE, shnum + 2, 1) # e_shentsize, e_shnum, e_shstrndx
|
||||
|
||||
return bytes
|
||||
|
||||
# Create a 32-bit ELF section header, where 'sh_name' is the offset of the
|
||||
# section name, 'sh_offset' is the offset of the section data, and 'sh_size'
|
||||
# is the size (in bytes) of the image in the section data.
|
||||
# We set sh_flags to SHF_OS_NONCONFORMING and use the sh_info field to store
|
||||
# a 32-bit CRC of the image data.
|
||||
def elf_section_header(sh_name, sh_offset, sh_size, sh_info):
|
||||
bytes = struct.pack("<10I",
|
||||
sh_name,
|
||||
1, 0xFFF00102, 0, # sh_type, sh_flags, sh_addr
|
||||
sh_offset, sh_size,
|
||||
0, # sh_link
|
||||
sh_info,
|
||||
4, 0) # sh_addralign, sh_entsize
|
||||
|
||||
return bytes
|
||||
|
||||
# A little-known fact about ELF files is that the first section header must
|
||||
# be empty. Readelf doesn't care about that, but objdump does. This may be
|
||||
# why the first byte of the .shstrtab should be zero.
|
||||
def elf_section_header_null():
|
||||
return b'\0' * ELF_SHDR_SIZE
|
||||
|
||||
# Create a 64-bit .shstrtab ELF section header.
|
||||
# 'shnum' is the number of sections.
|
||||
# 'sh_offset' is the offset of the .shstrtab section.
|
||||
# 'sh_size' is the unpadded size of the section.
|
||||
# The section itself should be padded to the nearest 8-byte boundary, so that
|
||||
# all the sections are aligned.
|
||||
def elf_section_header_shstrtab(sh_name, shnum, sh_size):
|
||||
sh_offset = ELF_HDR_SIZE + ELF_SHDR_SIZE * (shnum + 2);
|
||||
|
||||
bytes = struct.pack("<10I",
|
||||
sh_name,
|
||||
3, 0x20, 0, # sh_type (SHT_STRTAB), sh_flags (SHF_STRINGS), sh_addr
|
||||
sh_offset, sh_size,
|
||||
0, 0, 1, 1) # sh_link, sh_info, sh_addralign, sh_entsize
|
||||
|
||||
return bytes
|
||||
|
||||
# Build the .shstrtab section, where 'names' is a list of strings
|
||||
def elf_build_shstrtab(names):
|
||||
bytes = bytearray(b'\0')
|
||||
for name in ['.shstrtab'] + names:
|
||||
bytes.extend(name.encode('ascii') + b'\x00')
|
||||
|
||||
return bytes
|
||||
|
||||
# Returns a tuple of the size of a bytearray and the size rounded up to the next 8
|
||||
def sizes(b):
|
||||
return (len(b), round_up_to_base(len(b), ELF_ALIGNMENT))
|
||||
|
||||
# Returns the sh_name offset of a given section name in the .shstrtab section
|
||||
# 'needle' is the name of the section
|
||||
# 'haystack' is the .shstrtab section
|
||||
def offset_of(needle, haystack):
|
||||
null_terminated = bytearray(needle.encode('ascii') + b'\x00')
|
||||
position = haystack.find(null_terminated)
|
||||
if position == -1:
|
||||
raise MyException(f"unknown section name {needle}")
|
||||
|
||||
return position
|
||||
|
||||
# Writes a bunch of bytes to f, padded with zeroes to the nearest 4 bytes
|
||||
# Returns the total number of bytes written
|
||||
def write_padded(f, b):
|
||||
f.write(b)
|
||||
|
||||
(len, padded) = sizes(b)
|
||||
if padded > len:
|
||||
padding_length = padded - len;
|
||||
f.write(b'\0' * padding_length)
|
||||
|
||||
return padded
|
||||
|
||||
# Unlike the other images, FMC firmware and its metadata are encapsulated in
|
||||
# an ELF image. FMC metadata is simpler than the other firmware types, as it
|
||||
# comprises just three binary blobs.
|
||||
def fmc(gpu, fuse = "Prod"):
|
||||
global outputpath
|
||||
global version
|
||||
|
||||
GPU=gpu.upper()
|
||||
filename = f"src/nvidia/generated/g_bindata_kgspGetBinArchiveGspRmFmcGfw{fuse}Signed_{GPU}.c"
|
||||
|
||||
print(f"Creating nvidia/{gpu}/gsp/fmc-{version}.bin")
|
||||
os.makedirs(f"{outputpath}/nvidia/{gpu}/gsp/", exist_ok = True)
|
||||
|
||||
array = f"kgspBinArchiveGspRmFmcGfw{fuse}Signed_{GPU}_ucode_hash_data"
|
||||
ucode_hash = getbytes(filename, array)
|
||||
(ucode_hash_size, ucode_hash_padded_size) = sizes(ucode_hash)
|
||||
|
||||
array = f"kgspBinArchiveGspRmFmcGfw{fuse}Signed_{GPU}_ucode_sig_data"
|
||||
ucode_sig = getbytes(filename, array)
|
||||
(ucode_sig_size, ucode_sig_padded_size) = sizes(ucode_sig)
|
||||
|
||||
array = f"kgspBinArchiveGspRmFmcGfw{fuse}Signed_{GPU}_ucode_pkey_data"
|
||||
ucode_pkey = getbytes(filename, array)
|
||||
(ucode_pkey_size, ucode_pkey_padded_size) = sizes(ucode_pkey)
|
||||
|
||||
array = f"kgspBinArchiveGspRmFmcGfw{fuse}Signed_{GPU}_ucode_image_data"
|
||||
ucode_image = getbytes(filename, array)
|
||||
(ucode_image_size, ucode_image_padded_size) = sizes(ucode_image)
|
||||
|
||||
shnum = 4 # The number of image sections
|
||||
|
||||
# Build the .shstrtab section data
|
||||
shstrtab = elf_build_shstrtab(['hash', 'signature', 'publickey', 'image'])
|
||||
(shstrtab_size, shstrtab_padded_size) = sizes(shstrtab)
|
||||
|
||||
# Calculate the offsets of each section
|
||||
shstrtab_offset = ELF_HDR_SIZE + ELF_SHDR_SIZE * (shnum + 2)
|
||||
hash_offset = shstrtab_offset + shstrtab_padded_size
|
||||
signature_offset = hash_offset + ucode_hash_padded_size
|
||||
pkey_offset = signature_offset + ucode_sig_padded_size
|
||||
image_offset = pkey_offset + ucode_pkey_padded_size
|
||||
|
||||
with open(f"{outputpath}/nvidia/{gpu}/gsp/fmc-{version}.bin", "wb") as f:
|
||||
# Create the ELF header
|
||||
header = elf_header(shnum)
|
||||
f.write(header)
|
||||
|
||||
# Add the section headers
|
||||
|
||||
header = elf_section_header_null()
|
||||
f.write(header)
|
||||
|
||||
header = elf_section_header_shstrtab(offset_of(".shstrtab", shstrtab), shnum, len(shstrtab))
|
||||
f.write(header)
|
||||
|
||||
header = elf_section_header(offset_of("hash", shstrtab),
|
||||
hash_offset, ucode_hash_size, zlib.crc32(ucode_hash))
|
||||
f.write(header)
|
||||
|
||||
header = elf_section_header(offset_of("signature", shstrtab),
|
||||
signature_offset, ucode_sig_size, zlib.crc32(ucode_sig))
|
||||
f.write(header)
|
||||
|
||||
header = elf_section_header(offset_of("publickey", shstrtab),
|
||||
pkey_offset, ucode_pkey_size, zlib.crc32(ucode_pkey))
|
||||
f.write(header)
|
||||
|
||||
header = elf_section_header(offset_of("image", shstrtab),
|
||||
image_offset, ucode_image_size, zlib.crc32(ucode_image))
|
||||
f.write(header)
|
||||
|
||||
# Make sure we're where we are supposed to be
|
||||
assert f.tell() == ELF_HDR_SIZE + ELF_SHDR_SIZE * (shnum + 2)
|
||||
|
||||
# Write the .shstrtab section data.
|
||||
write_padded(f, shstrtab)
|
||||
assert f.tell() % 4 == 0
|
||||
|
||||
# Finally, write the four images in sequence
|
||||
write_padded(f, ucode_hash)
|
||||
assert f.tell() % 4 == 0
|
||||
|
||||
write_padded(f, ucode_sig)
|
||||
assert f.tell() % 4 == 0
|
||||
|
||||
write_padded(f, ucode_pkey)
|
||||
assert f.tell() % 4 == 0
|
||||
|
||||
write_padded(f, ucode_image)
|
||||
assert f.tell() % 4 == 0
|
||||
|
||||
# Extract the GSP-RM firmware from the .run file and copy the binaries
|
||||
# to the target directory.
|
||||
def gsp_firmware(filename):
|
||||
@@ -293,36 +480,50 @@ def gsp_firmware(filename):
|
||||
global version
|
||||
|
||||
import subprocess
|
||||
import tempfile
|
||||
import shutil
|
||||
import time
|
||||
|
||||
basename = os.path.basename(filename)
|
||||
|
||||
with tempfile.TemporaryDirectory() as temp:
|
||||
os.chdir(temp)
|
||||
|
||||
try:
|
||||
print(f"Extracting {os.path.basename(filename)} to {temp}")
|
||||
print(f"Validating {basename}")
|
||||
|
||||
result = subprocess.run(['/bin/sh', filename, '--check'], shell=False,
|
||||
check=True, timeout=10,
|
||||
stdout = subprocess.PIPE, stderr = subprocess.STDOUT)
|
||||
output = result.stdout.strip().decode("ascii")
|
||||
if not "check sums and md5 sums are ok" in output:
|
||||
raise MyException(f"{basename} is not a valid Nvidia driver .run file")
|
||||
except subprocess.CalledProcessError as error:
|
||||
print(error.output.decode())
|
||||
raise
|
||||
|
||||
try:
|
||||
print(f"Extracting {basename} to {temp}")
|
||||
# The -x parameter tells the installer to only extract the
|
||||
# contents and then exit.
|
||||
subprocess.run(['/bin/sh', filename, '-x'], shell=False,
|
||||
check=True, timeout=60,
|
||||
stdout = subprocess.PIPE, stderr = subprocess.STDOUT)
|
||||
except subprocess.CalledProcessError as error:
|
||||
except subprocess.SubprocessError as error:
|
||||
print(error.output.decode())
|
||||
sys.exit(error.returncode)
|
||||
except subprocess.TimeoutExpired as error:
|
||||
print(error.output.decode())
|
||||
sys.exit(error.returncode)
|
||||
raise
|
||||
|
||||
try:
|
||||
# The .run file extracts its contents to a directory with the same
|
||||
# name as the file itself, minus the .run. The GSP-RM firmware
|
||||
# images are in the 'firmware' subdirectory.
|
||||
directory = os.path.splitext(os.path.basename(filename))[0]
|
||||
result = subprocess.run(['/bin/sh', filename, '--target-directory'], shell=False,
|
||||
check=True, timeout=10,
|
||||
stdout = subprocess.PIPE, stderr = subprocess.DEVNULL)
|
||||
directory = result.stdout.strip().decode("ascii")
|
||||
os.chdir(f"{directory}/firmware")
|
||||
except:
|
||||
print("Firmware failed to extract")
|
||||
sys.exit(1)
|
||||
except subprocess.SubprocessError as e:
|
||||
print(error.output.decode())
|
||||
raise
|
||||
|
||||
if not os.path.exists('gsp_tu10x.bin') or not os.path.exists('gsp_ga10x.bin'):
|
||||
print("Firmware files are missing")
|
||||
@@ -334,15 +535,15 @@ def gsp_firmware(filename):
|
||||
print(f"Copied gsp_ga10x.bin to ga102/gsp/gsp-{version}.bin")
|
||||
|
||||
# Create a symlink, deleting the existing file/link if necessary
|
||||
def symlink(src, dst, target_is_directory = False):
|
||||
def symlink(dest, source, target_is_directory = False):
|
||||
import errno
|
||||
|
||||
try:
|
||||
os.symlink(src, dst, target_is_directory = target_is_directory)
|
||||
os.symlink(dest, source, target_is_directory = target_is_directory)
|
||||
except OSError as e:
|
||||
if e.errno == errno.EEXIST:
|
||||
os.remove(dst)
|
||||
os.symlink(src, dst, target_is_directory = target_is_directory)
|
||||
os.remove(source)
|
||||
os.symlink(dest, source, target_is_directory = target_is_directory)
|
||||
else:
|
||||
raise e
|
||||
|
||||
@@ -355,54 +556,81 @@ def symlinks():
|
||||
print(f"Creating symlinks in {outputpath}/nvidia")
|
||||
os.chdir(f"{outputpath}/nvidia")
|
||||
|
||||
for d in ['tu116', 'ga100', 'ad102']:
|
||||
os.makedirs(d, exist_ok = True)
|
||||
|
||||
for d in ['tu104', 'tu106', 'tu117']:
|
||||
os.makedirs(d, exist_ok = True)
|
||||
symlink('../tu102/gsp', f"{d}/gsp", target_is_directory = True)
|
||||
|
||||
for d in ['ad103', 'ad104', 'ad106', 'ad107']:
|
||||
os.makedirs(d, exist_ok = True)
|
||||
symlink('../ad102/gsp', f"{d}/gsp", target_is_directory = True)
|
||||
|
||||
for d in ['ga103', 'ga104', 'ga106', 'ga107']:
|
||||
os.makedirs(d, exist_ok = True)
|
||||
symlink('../ga102/gsp', f"{d}/gsp", target_is_directory = True)
|
||||
|
||||
for d in ['ad103', 'ad104', 'ad106', 'ad107']:
|
||||
# Some older versions of /lib/firmware had symlinks from ad10x/gsp to ad102/gsp,
|
||||
# even though there were no other directories in ad10x. Delete the existing
|
||||
# ad10x directory so that we can replace it with a symlink.
|
||||
if os.path.islink(f"{d}/gsp"):
|
||||
os.remove(f"{d}/gsp")
|
||||
os.rmdir(d)
|
||||
symlink('ad102', d, target_is_directory = True)
|
||||
|
||||
# TU11x uses the same bootloader as TU10x
|
||||
symlink(f"../../tu102/gsp/bootloader-{version}.bin", f"tu116/gsp/bootloader-{version}.bin")
|
||||
|
||||
# Blackwell is only supported with GSP, so we can symlink the top-level directories
|
||||
# instead of just the gsp/ subdirectories.
|
||||
for d in ['gb102']:
|
||||
symlink('gb100', d, target_is_directory = True)
|
||||
|
||||
for d in ['gb203', 'gb205', 'gb206', 'gb207']:
|
||||
symlink('gb202', d, target_is_directory = True)
|
||||
|
||||
# Symlink the GSP-RM image
|
||||
symlink(f"../../tu102/gsp/gsp-{version}.bin", f"tu116/gsp/gsp-{version}.bin")
|
||||
symlink(f"../../tu102/gsp/gsp-{version}.bin", f"ga100/gsp/gsp-{version}.bin")
|
||||
symlink(f"../../ga102/gsp/gsp-{version}.bin", f"ad102/gsp/gsp-{version}.bin")
|
||||
symlink(f"../../ga102/gsp/gsp-{version}.bin", f"gh100/gsp/gsp-{version}.bin")
|
||||
symlink(f"../../ga102/gsp/gsp-{version}.bin", f"gb100/gsp/gsp-{version}.bin")
|
||||
symlink(f"../../ga102/gsp/gsp-{version}.bin", f"gb202/gsp/gsp-{version}.bin")
|
||||
|
||||
# Create a text file that can be inserted as-is to the WHENCE file of the
|
||||
# linux-firmware git repository.
|
||||
# linux-firmware git repository. Note that existing firmware versions in
|
||||
# the repository must be maintained, so those entries are hard-coded here.
|
||||
# Also note that Nouveau supports Ada and later only with GSP, which is why
|
||||
# ga103/gsp -> ga102/gsp, but ad103 -> ad102.
|
||||
def whence():
|
||||
global outputpath
|
||||
global version
|
||||
|
||||
whence = f"""
|
||||
whence = f"""File: nvidia/tu102/gsp/bootloader-535.113.01.bin
|
||||
File: nvidia/tu102/gsp/booter_load-535.113.01.bin
|
||||
File: nvidia/tu102/gsp/booter_unload-535.113.01.bin
|
||||
File: nvidia/tu102/gsp/bootloader-{version}.bin
|
||||
File: nvidia/tu102/gsp/booter_load-{version}.bin
|
||||
File: nvidia/tu102/gsp/booter_unload-{version}.bin
|
||||
Link: nvidia/tu104/gsp -> ../tu102/gsp
|
||||
Link: nvidia/tu106/gsp -> ../tu102/gsp
|
||||
|
||||
File: nvidia/tu116/gsp/booter_load-535.113.01.bin
|
||||
File: nvidia/tu116/gsp/booter_unload-535.113.01.bin
|
||||
Link: nvidia/tu116/gsp/bootloader-535.113.01.bin -> ../../tu102/gsp/bootloader-535.113.01.bin
|
||||
File: nvidia/tu116/gsp/booter_load-{version}.bin
|
||||
File: nvidia/tu116/gsp/booter_unload-{version}.bin
|
||||
Link: nvidia/tu116/gsp/bootloader-{version}.bin -> ../../tu102/gsp/bootloader-{version}.bin
|
||||
Link: nvidia/tu117/gsp -> ../tu116/gsp
|
||||
|
||||
File: nvidia/ga100/gsp/bootloader-535.113.01.bin
|
||||
File: nvidia/ga100/gsp/booter_load-535.113.01.bin
|
||||
File: nvidia/ga100/gsp/booter_unload-535.113.01.bin
|
||||
File: nvidia/ga100/gsp/bootloader-{version}.bin
|
||||
File: nvidia/ga100/gsp/booter_load-{version}.bin
|
||||
File: nvidia/ga100/gsp/booter_unload-{version}.bin
|
||||
|
||||
File: nvidia/ad102/gsp/bootloader-{version}.bin
|
||||
File: nvidia/ad102/gsp/booter_load-{version}.bin
|
||||
File: nvidia/ad102/gsp/booter_unload-{version}.bin
|
||||
Link: nvidia/ad103/gsp -> ../ad102/gsp
|
||||
Link: nvidia/ad104/gsp -> ../ad102/gsp
|
||||
Link: nvidia/ad106/gsp -> ../ad102/gsp
|
||||
Link: nvidia/ad107/gsp -> ../ad102/gsp
|
||||
|
||||
File: nvidia/ga102/gsp/bootloader-535.113.01.bin
|
||||
File: nvidia/ga102/gsp/booter_load-535.113.01.bin
|
||||
File: nvidia/ga102/gsp/booter_unload-535.113.01.bin
|
||||
File: nvidia/ga102/gsp/bootloader-{version}.bin
|
||||
File: nvidia/ga102/gsp/booter_load-{version}.bin
|
||||
File: nvidia/ga102/gsp/booter_unload-{version}.bin
|
||||
@@ -411,6 +639,41 @@ Link: nvidia/ga104/gsp -> ../ga102/gsp
|
||||
Link: nvidia/ga106/gsp -> ../ga102/gsp
|
||||
Link: nvidia/ga107/gsp -> ../ga102/gsp
|
||||
|
||||
File: nvidia/ad102/gsp/bootloader-535.113.01.bin
|
||||
File: nvidia/ad102/gsp/booter_load-535.113.01.bin
|
||||
File: nvidia/ad102/gsp/booter_unload-535.113.01.bin
|
||||
File: nvidia/ad102/gsp/bootloader-{version}.bin
|
||||
File: nvidia/ad102/gsp/booter_load-{version}.bin
|
||||
File: nvidia/ad102/gsp/booter_unload-{version}.bin
|
||||
File: nvidia/ad102/gsp/scrubber-{version}.bin
|
||||
Link: nvidia/ad103 -> ad102
|
||||
Link: nvidia/ad104 -> ad102
|
||||
Link: nvidia/ad106 -> ad102
|
||||
Link: nvidia/ad107 -> ad102
|
||||
|
||||
File: nvidia/gh100/gsp/bootloader-{version}.bin
|
||||
File: nvidia/gh100/gsp/fmc-{version}.bin
|
||||
|
||||
File: nvidia/gb100/gsp/bootloader-{version}.bin
|
||||
File: nvidia/gb100/gsp/fmc-{version}.bin
|
||||
Link: nvidia/gb102 -> gb100
|
||||
|
||||
File: nvidia/gb202/gsp/bootloader-{version}.bin
|
||||
File: nvidia/gb202/gsp/fmc-{version}.bin
|
||||
Link: nvidia/gb203 -> gb202
|
||||
Link: nvidia/gb205 -> gb202
|
||||
Link: nvidia/gb206 -> gb202
|
||||
Link: nvidia/gb207 -> gb202
|
||||
|
||||
File: nvidia/tu102/gsp/gsp-535.113.01.bin
|
||||
Origin: gsp_tu10x.bin from NVIDIA-Linux-x86_64-535.113.01.run
|
||||
Link: nvidia/tu116/gsp/gsp-535.113.01.bin -> ../../tu102/gsp/gsp-535.113.01.bin
|
||||
Link: nvidia/ga100/gsp/gsp-535.113.01.bin -> ../../tu102/gsp/gsp-535.113.01.bin
|
||||
|
||||
File: nvidia/ga102/gsp/gsp-535.113.01.bin
|
||||
Origin: gsp_ga10x.bin from NVIDIA-Linux-x86_64-535.113.01.run
|
||||
Link: nvidia/ad102/gsp/gsp-535.113.01.bin -> ../../ga102/gsp/gsp-535.113.01.bin
|
||||
|
||||
File: nvidia/tu102/gsp/gsp-{version}.bin
|
||||
Origin: gsp_tu10x.bin from NVIDIA-Linux-x86_64-{version}.run
|
||||
Link: nvidia/tu116/gsp/gsp-{version}.bin -> ../../tu102/gsp/gsp-{version}.bin
|
||||
@@ -419,6 +682,9 @@ Link: nvidia/ga100/gsp/gsp-{version}.bin -> ../../tu102/gsp/gsp-{version}.bin
|
||||
File: nvidia/ga102/gsp/gsp-{version}.bin
|
||||
Origin: gsp_ga10x.bin from NVIDIA-Linux-x86_64-{version}.run
|
||||
Link: nvidia/ad102/gsp/gsp-{version}.bin -> ../../ga102/gsp/gsp-{version}.bin
|
||||
Link: nvidia/gh100/gsp/gsp-{version}.bin -> ../../ga102/gsp/gsp-{version}.bin
|
||||
Link: nvidia/gb100/gsp/gsp-{version}.bin -> ../../ga102/gsp/gsp-{version}.bin
|
||||
Link: nvidia/gb202/gsp/gsp-{version}.bin -> ../../ga102/gsp/gsp-{version}.bin
|
||||
"""
|
||||
|
||||
with open(f"{outputpath}/WHENCE.txt", 'w') as f:
|
||||
@@ -437,16 +703,21 @@ def main():
|
||||
' the firmware files directly where Nouveau expects them.'
|
||||
' The --revision option is useful for testing new firmware'
|
||||
' versions without changing Nouveau source code.'
|
||||
' The --driver option lets you specify the path to the .run file,'
|
||||
' and this script also will extract and copy the GSP-RM firmware images.')
|
||||
' The --driver option lets you specify the local path to the .run file,'
|
||||
' or the URL of a file to download, and this script also will extract'
|
||||
' and copy the GSP-RM firmware images. If no path/url is provided, then'
|
||||
' the script will guess the URL and download the file automatically.')
|
||||
parser.add_argument('-i', '--input', default = os.getcwd(),
|
||||
help = 'Path to source directory (where version.mk exists)')
|
||||
parser.add_argument('-o', '--output', default = os.path.abspath(os.getcwd() + '/_out'),
|
||||
help = 'Path to target directory (where files will be written)')
|
||||
parser.add_argument('-r', '--revision',
|
||||
help = 'Files will be named with this version number')
|
||||
parser.add_argument('--debug-fused', action='store_true',
|
||||
help = 'Extract debug instead of production images')
|
||||
parser.add_argument('-d', '--driver',
|
||||
help = 'Path to Nvidia driver .run package, for also extracting the GSP-RM firmware')
|
||||
nargs = '?', const = '',
|
||||
help = 'Path or URL to NVIDIA-Linux-x86_64-<version>.run driver package, for also extracting the GSP-RM firmware')
|
||||
parser.add_argument('-s', '--symlink', action='store_true',
|
||||
help = 'Also create symlinks for all supported GPUs')
|
||||
parser.add_argument('-w', '--whence', action='store_true',
|
||||
@@ -455,15 +726,11 @@ def main():
|
||||
|
||||
os.chdir(args.input)
|
||||
|
||||
if args.driver:
|
||||
if not os.path.exists(args.driver):
|
||||
print(f"File {args.driver} does not exist.")
|
||||
sys.exit(1)
|
||||
|
||||
version = args.revision
|
||||
if not version:
|
||||
with open("version.mk") as f:
|
||||
version = re.search(r'^NVIDIA_VERSION = ([^\s]+)', f.read(), re.MULTILINE).group(1)
|
||||
del f
|
||||
|
||||
print(f"Generating files for version {version}")
|
||||
|
||||
@@ -472,29 +739,65 @@ def main():
|
||||
|
||||
os.makedirs(f"{outputpath}/nvidia", exist_ok = True)
|
||||
|
||||
booter("tu102", "load", 16)
|
||||
booter("tu102", "unload", 16)
|
||||
# TU10x and GA100 do not have debug-fused versions of the bootloader
|
||||
if args.debug_fused:
|
||||
print("Generation images for debug-fused GPUs")
|
||||
bootloader_fuse = "_dbg_"
|
||||
booter_fuse = "dbg" # Also used for scrubber
|
||||
fmc_fuse = "Debug"
|
||||
else:
|
||||
bootloader_fuse = "_prod_"
|
||||
booter_fuse = "prod"
|
||||
fmc_fuse = "Prod"
|
||||
|
||||
booter("tu102", "load", 16, booter_fuse)
|
||||
booter("tu102", "unload", 16, booter_fuse)
|
||||
bootloader("tu102", "_")
|
||||
|
||||
booter("tu116", "load", 16)
|
||||
booter("tu116", "unload", 16)
|
||||
booter("tu116", "load", 16, booter_fuse)
|
||||
booter("tu116", "unload", 16, booter_fuse)
|
||||
# TU11x uses the same bootloader as TU10x
|
||||
|
||||
booter("ga100", "load", 384)
|
||||
booter("ga100", "unload", 384)
|
||||
booter("ga100", "load", 384, booter_fuse)
|
||||
booter("ga100", "unload", 384, booter_fuse)
|
||||
bootloader("ga100", "_")
|
||||
|
||||
booter("ga102", "load", 384)
|
||||
booter("ga102", "unload", 384)
|
||||
bootloader("ga102", "_prod_")
|
||||
booter("ga102", "load", 384, booter_fuse)
|
||||
booter("ga102", "unload", 384, booter_fuse)
|
||||
bootloader("ga102", bootloader_fuse)
|
||||
|
||||
booter("ad102", "load", 384)
|
||||
booter("ad102", "unload", 384)
|
||||
bootloader("ad102", "_prod_")
|
||||
# scrubber("ad102", 384) # Not currently used by Nouveau
|
||||
booter("ad102", "load", 384, booter_fuse)
|
||||
booter("ad102", "unload", 384, booter_fuse)
|
||||
bootloader("ad102", bootloader_fuse)
|
||||
scrubber("ad102", 384, booter_fuse) # Not currently used by Nouveau
|
||||
|
||||
if args.driver:
|
||||
gsp_firmware(os.path.abspath(args.driver))
|
||||
bootloader("gh100", bootloader_fuse)
|
||||
fmc("gh100", fmc_fuse)
|
||||
|
||||
bootloader("gb100", bootloader_fuse)
|
||||
fmc("gb100", fmc_fuse)
|
||||
|
||||
bootloader("gb202", bootloader_fuse)
|
||||
fmc("gb202", fmc_fuse)
|
||||
|
||||
if args.driver is not None:
|
||||
if args.driver == '':
|
||||
# No path/url provided, so make a guess of the URL
|
||||
# to automatically download the right version.
|
||||
args.driver = f'https://download.nvidia.com/XFree86/Linux-x86_64/{version}/NVIDIA-Linux-x86_64-{version}.run'
|
||||
|
||||
if re.search('^http[s]://', args.driver):
|
||||
with tempfile.NamedTemporaryFile(prefix = f'NVIDIA-Linux-x86_64-{version}-', suffix = '.run') as f:
|
||||
print(f"Downloading driver from {args.driver} as {f.name}")
|
||||
urllib.request.urlretrieve(args.driver, f.name)
|
||||
gsp_firmware(f.name)
|
||||
del f
|
||||
else:
|
||||
if not os.path.exists(args.driver):
|
||||
print(f"File {args.driver} does not exist.")
|
||||
sys.exit(1)
|
||||
|
||||
gsp_firmware(os.path.abspath(args.driver))
|
||||
|
||||
if args.symlink:
|
||||
symlinks()
|
||||
|
||||
445
nouveau/extract-firmware-nouveau/Cargo.lock
generated
Normal file
445
nouveau/extract-firmware-nouveau/Cargo.lock
generated
Normal file
@@ -0,0 +1,445 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "adler2"
|
||||
version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627"
|
||||
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "1.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstream"
|
||||
version = "0.6.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"anstyle-parse",
|
||||
"anstyle-query",
|
||||
"anstyle-wincon",
|
||||
"colorchoice",
|
||||
"is_terminal_polyfill",
|
||||
"utf8parse",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle"
|
||||
version = "1.0.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9"
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-parse"
|
||||
version = "0.2.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9"
|
||||
dependencies = [
|
||||
"utf8parse",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-query"
|
||||
version = "1.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c"
|
||||
dependencies = [
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-wincon"
|
||||
version = "3.0.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ca3534e77181a9cc07539ad51f2141fe32f6c3ffd4df76db8ad92346b003ae4e"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"once_cell",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "2.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8f68f53c83ab957f72c32642f3868eec03eb974d1fb82e453128456482613d36"
|
||||
|
||||
[[package]]
|
||||
name = "byteorder"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.5.29"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8acebd8ad879283633b343856142139f2da2317c96b05b4dd6181c61e2480184"
|
||||
dependencies = [
|
||||
"clap_builder",
|
||||
"clap_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_builder"
|
||||
version = "4.5.29"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f6ba32cbda51c7e1dfd49acc1457ba1a7dec5b64fe360e828acb13ca8dc9c2f9"
|
||||
dependencies = [
|
||||
"anstream",
|
||||
"anstyle",
|
||||
"clap_lex",
|
||||
"strsim",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_derive"
|
||||
version = "4.5.28"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bf4ced95c6f4a675af3da73304b9ac4ed991640c36374e4b46795c49e17cf1ed"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_lex"
|
||||
version = "0.7.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6"
|
||||
|
||||
[[package]]
|
||||
name = "colorchoice"
|
||||
version = "1.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990"
|
||||
|
||||
[[package]]
|
||||
name = "crc32fast"
|
||||
version = "1.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0"
|
||||
|
||||
[[package]]
|
||||
name = "errno"
|
||||
version = "0.3.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "extract-firmware-nouveau"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"clap",
|
||||
"flate2",
|
||||
"itertools",
|
||||
"regex",
|
||||
"tempfile",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fastrand"
|
||||
version = "2.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be"
|
||||
|
||||
[[package]]
|
||||
name = "flate2"
|
||||
version = "1.0.35"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c936bfdafb507ebbf50b8074c54fa31c5be9a1e7e5f467dd659697041407d07c"
|
||||
dependencies = [
|
||||
"crc32fast",
|
||||
"miniz_oxide",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "43a49c392881ce6d5c3b8cb70f98717b7c07aabbdff06687b9030dbfbe2725f8"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"wasi",
|
||||
"windows-targets",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "heck"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
||||
|
||||
[[package]]
|
||||
name = "is_terminal_polyfill"
|
||||
version = "1.70.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf"
|
||||
|
||||
[[package]]
|
||||
name = "itertools"
|
||||
version = "0.14.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285"
|
||||
dependencies = [
|
||||
"either",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.169"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a"
|
||||
|
||||
[[package]]
|
||||
name = "linux-raw-sys"
|
||||
version = "0.4.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.7.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
|
||||
|
||||
[[package]]
|
||||
name = "miniz_oxide"
|
||||
version = "0.8.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b3b1c9bd4fe1f0f8b387f6eb9eb3b4a1aa26185e5750efb9140301703f62cd1b"
|
||||
dependencies = [
|
||||
"adler2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.20.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "945462a4b81e43c4e3ba96bd7b49d834c6f61198356aa858733bc4acf3cbe62e"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.93"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.38"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-automata",
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-automata"
|
||||
version = "0.4.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.8.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
|
||||
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
version = "0.38.44"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"errno",
|
||||
"libc",
|
||||
"linux-raw-sys",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "strsim"
|
||||
version = "0.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.98"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "36147f1a48ae0ec2b5b3bc5b537d267457555a10dc06f3dbc8cb11ba3006d3b1"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tempfile"
|
||||
version = "3.16.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "38c246215d7d24f48ae091a2902398798e05d978b24315d6efbc00ede9a8bb91"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"fastrand",
|
||||
"getrandom",
|
||||
"once_cell",
|
||||
"rustix",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a210d160f08b701c8721ba1c726c11662f877ea6b7094007e1ca9a1041945034"
|
||||
|
||||
[[package]]
|
||||
name = "utf8parse"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.13.3+wasi-0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "26816d2e1a4a36a2940b96c5296ce403917633dff8f3440e9b236ed6f6bacad2"
|
||||
dependencies = [
|
||||
"wit-bindgen-rt",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.59.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
|
||||
dependencies = [
|
||||
"windows-targets",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
|
||||
dependencies = [
|
||||
"windows_aarch64_gnullvm",
|
||||
"windows_aarch64_msvc",
|
||||
"windows_i686_gnu",
|
||||
"windows_i686_gnullvm",
|
||||
"windows_i686_msvc",
|
||||
"windows_x86_64_gnu",
|
||||
"windows_x86_64_gnullvm",
|
||||
"windows_x86_64_msvc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnullvm"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
||||
|
||||
[[package]]
|
||||
name = "wit-bindgen-rt"
|
||||
version = "0.33.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3268f3d866458b787f390cf61f4bbb563b922d091359f9608842999eaee3943c"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
]
|
||||
12
nouveau/extract-firmware-nouveau/Cargo.toml
Normal file
12
nouveau/extract-firmware-nouveau/Cargo.toml
Normal file
@@ -0,0 +1,12 @@
|
||||
[package]
|
||||
name = "extract-firmware-nouveau"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
byteorder = "1.5.0"
|
||||
clap = { version = "4.5.23", features = ["derive", "string"] }
|
||||
flate2 = { version = "1.0.35" }
|
||||
itertools = "0.14.0"
|
||||
regex = "1.11.1"
|
||||
tempfile = "3.16.0"
|
||||
817
nouveau/extract-firmware-nouveau/src/main.rs
Normal file
817
nouveau/extract-firmware-nouveau/src/main.rs
Normal file
@@ -0,0 +1,817 @@
|
||||
use byteorder::{ByteOrder, LittleEndian};
|
||||
use clap::Parser;
|
||||
use flate2::{Decompress, FlushDecompress};
|
||||
use itertools::Itertools;
|
||||
use regex::Regex;
|
||||
use std::fs;
|
||||
use std::io::BufRead;
|
||||
use std::os::unix;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::process::Command;
|
||||
use std::process::ExitCode;
|
||||
use std::{env, io::Write};
|
||||
use tempfile::tempdir;
|
||||
|
||||
/// Clap value parser function that verifies that a path (directory) exists
|
||||
/// and converts it to its absolute path form.
|
||||
fn parser_path_exists(s: &str) -> Result<PathBuf, String> {
|
||||
let path = fs::canonicalize(PathBuf::from(s))
|
||||
.map_err(|err| format!("Invalid path {s}: {err}"))?;
|
||||
|
||||
if fs::exists(&path).is_ok_and(|b| b == true) && path.is_dir() {
|
||||
Ok(path)
|
||||
} else {
|
||||
Err(format!("Path {} does not exist or is not accessible", path.display()))
|
||||
}
|
||||
}
|
||||
|
||||
/// Clap value parser function that verifies that a file exists
|
||||
fn parser_file_exists(s: &str) -> Result<PathBuf, String> {
|
||||
let file = fs::canonicalize(PathBuf::from(s))
|
||||
.map_err(|err| format!("Invalid file {s}: {err}"))?;
|
||||
|
||||
if fs::exists(&file).is_ok_and(|b| b == true) && file.is_file() {
|
||||
Ok(file)
|
||||
} else {
|
||||
Err(format!("File {} does not exist or is not accessible", file.display()))
|
||||
}
|
||||
}
|
||||
|
||||
/// Clap value parser function that verifies that a given string is a valid version number
|
||||
fn parser_valid_revision(s: &str) -> Result<String, String> {
|
||||
// Match any version number, e.g. X.Y, X.Y.Z, X.Y.Z.W, etc
|
||||
let re = Regex::new(r"^[0-9]+(\.[0-9]+){1,}$").unwrap();
|
||||
|
||||
if re.is_match(s) {
|
||||
Ok(s.to_string())
|
||||
} else {
|
||||
Err(format!("Invalid revision \"{s}\""))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Parser, Debug)]
|
||||
/// Extract firmware binaries from the OpenRM git repository in a format expected by the Nouveau device driver.
|
||||
struct Cli {
|
||||
/// Path to source directory (where version.mk exists)
|
||||
#[arg (short, long,
|
||||
default_value=env::current_dir().unwrap().into_os_string(),
|
||||
value_parser=parser_path_exists)]
|
||||
input: PathBuf,
|
||||
|
||||
/// Path to target directory (where files will be written)
|
||||
/// Unlike the Python version, the output directory must already exist.
|
||||
#[arg (short, long,
|
||||
default_value=env::current_dir().unwrap().into_os_string(),
|
||||
value_parser=parser_path_exists)]
|
||||
output: PathBuf,
|
||||
|
||||
/// Files will be named with this version number
|
||||
#[arg (short, long,
|
||||
value_parser=parser_valid_revision)]
|
||||
revision: Option<String>,
|
||||
|
||||
/// Path to Nvidia driver .run package, for also extracting the GSP-RM firmware
|
||||
#[arg (short, long,
|
||||
value_parser=parser_file_exists)]
|
||||
driver: Option<PathBuf>,
|
||||
|
||||
/// Also create symlinks for all supported GPUs
|
||||
#[arg(short, long, action)]
|
||||
symlink: bool,
|
||||
|
||||
/// Also generate a WHENCE file
|
||||
#[arg(short, long, action)]
|
||||
whence: bool,
|
||||
}
|
||||
|
||||
/// Extract the driver/firmware version number from the version.mk file
|
||||
fn find_version() -> Option<String> {
|
||||
let re = Regex::new(r"^NVIDIA_VERSION = ([^\s]+)").unwrap();
|
||||
let file = fs::File::open("version.mk").unwrap();
|
||||
let reader = std::io::BufReader::new(file);
|
||||
|
||||
// Alternative approach: https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=fa91a4ad9024a836a7fd74de387755c1
|
||||
// Or https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=d3dd6b77c08a9bf9d16510dea9ba1a86
|
||||
|
||||
for line in reader.lines() {
|
||||
let line = line.unwrap();
|
||||
if let Some(version) = re.captures(&line) {
|
||||
return Some(version[1].to_string());
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
// Executes a shell command, and either returns stdout as success,
|
||||
// or an error message as failure.
|
||||
fn command(filename: &Path, parameter: &str) -> Result<String, String> {
|
||||
let output = Command::new("/bin/sh")
|
||||
.arg(filename)
|
||||
.arg(parameter)
|
||||
.output()
|
||||
.map_err(|err| format!("Could not execute {} {parameter}: {err}", filename.display()))?;
|
||||
|
||||
if !output.status.success() {
|
||||
return Err(format!(
|
||||
"Command {} {parameter} failed with exit code {}.\n{}\n{}",
|
||||
filename.display(),
|
||||
output.status.code().unwrap_or(0),
|
||||
String::from_utf8_lossy(&output.stdout),
|
||||
String::from_utf8_lossy(&output.stderr)
|
||||
));
|
||||
}
|
||||
|
||||
Ok(String::from_utf8_lossy(&output.stdout).trim().to_string())
|
||||
}
|
||||
|
||||
// Extract the GSP-RM firmware from the .run file and copy the binaries
|
||||
// to the target directory.
|
||||
fn gsp_firmware(output: &Path, version: &str, runfile: &Path) -> Result<(), String> {
|
||||
// When tempdir goes out of scope, its destructor will delete the directory
|
||||
let tempdir = tempdir()
|
||||
.map_err(|err| format!("Could not create temp directory: {err}"))?;
|
||||
|
||||
// Chdir to the new temporary directory, because the .run file only extracts
|
||||
// to the current directory.
|
||||
env::set_current_dir(&tempdir)
|
||||
.map_err(|err| format!("Could not chdir to {}: {err}", tempdir.path().display()))?;
|
||||
|
||||
let directory = command(runfile, "--target-directory")?;
|
||||
|
||||
// Extract the firmware images from the .run file
|
||||
command(runfile, "-x")?;
|
||||
|
||||
// Chdir to the directory that has the firmware images
|
||||
let imagedir = format!("{directory}/firmware");
|
||||
env::set_current_dir(&imagedir)
|
||||
.map_err(|err| format!("Could not chdir to {imagedir}: {err}"))?;
|
||||
|
||||
let target = output.join(format!("tu102/gsp/gsp-{version}.bin"));
|
||||
let source = "gsp_tu10x.bin";
|
||||
fs::copy(source, &target)
|
||||
.map_err(|err| format!("Could not copy {source} to {}: {err}", target.display()))?;
|
||||
|
||||
let target = output.join(format!("ga102/gsp/gsp-{version}.bin"));
|
||||
let source = "gsp_ga10x.bin";
|
||||
fs::copy(source, &target)
|
||||
.map_err(|err| format!("Could not copy {source} to {}: {err}", target.display()))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn whence(output: &Path, version: &str) -> Result<(), String> {
|
||||
let text = format!(
|
||||
"File: nvidia/tu102/gsp/bootloader-{version}.bin
|
||||
File: nvidia/tu102/gsp/booter_load-{version}.bin
|
||||
File: nvidia/tu102/gsp/booter_unload-{version}.bin
|
||||
Link: nvidia/tu104/gsp -> ../tu102/gsp
|
||||
Link: nvidia/tu106/gsp -> ../tu102/gsp
|
||||
|
||||
File: nvidia/tu116/gsp/booter_load-{version}.bin
|
||||
File: nvidia/tu116/gsp/booter_unload-{version}.bin
|
||||
Link: nvidia/tu116/gsp/bootloader-{version}.bin -> ../../tu102/gsp/bootloader-{version}.bin
|
||||
Link: nvidia/tu117/gsp -> ../tu116/gsp
|
||||
|
||||
File: nvidia/ga100/gsp/bootloader-{version}.bin
|
||||
File: nvidia/ga100/gsp/booter_load-{version}.bin
|
||||
File: nvidia/ga100/gsp/booter_unload-{version}.bin
|
||||
|
||||
File: nvidia/ad102/gsp/bootloader-{version}.bin
|
||||
File: nvidia/ad102/gsp/booter_load-{version}.bin
|
||||
File: nvidia/ad102/gsp/booter_unload-{version}.bin
|
||||
Link: nvidia/ad103/gsp -> ../ad102/gsp
|
||||
Link: nvidia/ad104/gsp -> ../ad102/gsp
|
||||
Link: nvidia/ad106/gsp -> ../ad102/gsp
|
||||
Link: nvidia/ad107/gsp -> ../ad102/gsp
|
||||
|
||||
File: nvidia/ga102/gsp/bootloader-{version}.bin
|
||||
File: nvidia/ga102/gsp/booter_load-{version}.bin
|
||||
File: nvidia/ga102/gsp/booter_unload-{version}.bin
|
||||
Link: nvidia/ga103/gsp -> ../ga102/gsp
|
||||
Link: nvidia/ga104/gsp -> ../ga102/gsp
|
||||
Link: nvidia/ga106/gsp -> ../ga102/gsp
|
||||
Link: nvidia/ga107/gsp -> ../ga102/gsp
|
||||
|
||||
File: nvidia/tu102/gsp/gsp-{version}.bin
|
||||
Origin: gsp_tu10x.bin from NVIDIA-Linux-x86_64-{version}.run
|
||||
Link: nvidia/tu116/gsp/gsp-{version}.bin -> ../../tu102/gsp/gsp-{version}.bin
|
||||
Link: nvidia/ga100/gsp/gsp-{version}.bin -> ../../tu102/gsp/gsp-{version}.bin
|
||||
|
||||
File: nvidia/ga102/gsp/gsp-{version}.bin
|
||||
Origin: gsp_ga10x.bin from NVIDIA-Linux-x86_64-{version}.run
|
||||
Link: nvidia/ad102/gsp/gsp-{version}.bin -> ../../ga102/gsp/gsp-{version}.bin
|
||||
");
|
||||
|
||||
let whence = "WHENCE.txt";
|
||||
let filename = output.join(whence);
|
||||
|
||||
let mut file = fs::OpenOptions::new()
|
||||
.write(true)
|
||||
.create(true)
|
||||
.open(&filename)
|
||||
.map_err(|err| format!("Could not create {whence}: {err}"))?;
|
||||
|
||||
file.write_all(text.as_bytes())
|
||||
.map_err(|err| format!("Could not write to {whence}: {err}"))?;
|
||||
|
||||
file.sync_all()
|
||||
.map_err(|err| format!("Failed to sync {whence}: {err}"))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Create a symlink if it doesn't exist already, or return an error
|
||||
fn symlink(original: &Path, link: &Path) -> Result<(), String> {
|
||||
match fs::read_link(&link) {
|
||||
Ok(o) if o == original => {
|
||||
// Symlink already exists and is correct
|
||||
}
|
||||
Ok(o) => {
|
||||
// Symlink exists but points to wrong file for some reason
|
||||
return Err(format!("Symlink {} points to wrong item {}", link.display(), o.display()));
|
||||
}
|
||||
Err(err) if err.kind() == std::io::ErrorKind::NotFound => {
|
||||
// Symlink doesn't exist, so we can create it.
|
||||
unix::fs::symlink(&original, &link)
|
||||
.map_err(|err| format!("Could not create symlink {} -> {}: {err}", link.display(), original.display()))?;
|
||||
}
|
||||
Err(err) => {
|
||||
// Something else is wrong
|
||||
return Err(format!("File/symlink {} is broken: {err}", link.display()));
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Create symlinks in the target directory for the other GPUs. This mirrors
|
||||
/// what the WHENCE file in linux-firmware does.
|
||||
fn symlinks(output: &Path, version: &str) -> Result<(), String> {
|
||||
env::set_current_dir(&output)
|
||||
.map_err(|err| format!("Could not chdir to {}: {err}", output.display()))?;
|
||||
|
||||
// For these paths, we can just create a symlink of the entire 'gsp' directory.
|
||||
// For example, tu104, tu106, and tu117 all use the same binaries as tu102.
|
||||
let paths = [
|
||||
("tu104", "tu102"),
|
||||
("tu106", "tu102"),
|
||||
("tu117", "tu102"),
|
||||
("ad103", "ad102"),
|
||||
("ad104", "ad102"),
|
||||
("ad106", "ad102"),
|
||||
("ad107", "ad102"),
|
||||
("ga103", "ga102"),
|
||||
("ga104", "ga102"),
|
||||
("ga106", "ga102"),
|
||||
("ga107", "ga102"),
|
||||
];
|
||||
|
||||
// Create the source directory for each GPU, and then create symlink
|
||||
// for the gsp directory to the "parent" GPU.
|
||||
for (source, dest) in paths {
|
||||
fs::create_dir_all(&source)
|
||||
.map_err(|err| format!("Could not create {}/{source}: {err}", output.display()))?;
|
||||
|
||||
let original = PathBuf::from(format!("../{dest}/gsp"));
|
||||
let link = PathBuf::from(format!("{source}/gsp"));
|
||||
|
||||
symlink(&original, &link)?;
|
||||
}
|
||||
|
||||
// Create additional symlinks for specific firmware files. TU116 is a special case
|
||||
// because it uses the same bootloader as TU102, but the booter images are different.
|
||||
let paths = [
|
||||
("tu116", "tu102", "bootloader"),
|
||||
("tu116", "tu102", "gsp"),
|
||||
("ga100", "tu102", "gsp"),
|
||||
("ad102", "ga102", "gsp"),
|
||||
];
|
||||
|
||||
for (source, dest, kind) in paths {
|
||||
// These directories should already exist, but create them anyway for consistency.
|
||||
let path = format!("{source}/gsp");
|
||||
fs::create_dir_all(&path)
|
||||
.map_err(|err| format!("Could not create {}/{path}: {err}", output.display()))?;
|
||||
|
||||
let link = PathBuf::from(format!("{path}/{kind}-{version}.bin"));
|
||||
let original = PathBuf::from(format!("../../{dest}/gsp/{kind}-{version}.bin"));
|
||||
|
||||
symlink(&original, &link)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn get_bytes(filename: &Path, array: &str, expected_size: Option<usize>) -> Result<Vec<u8>, String> {
|
||||
let re_datasize = Regex::new(r"DATA SIZE \(bytes\): (\d+)").unwrap();
|
||||
let re_compressedsize = Regex::new(r"COMPRESSED SIZE \(bytes\): (\d+)").unwrap();
|
||||
let re_bytes = Regex::new(r"0x([0-9a-f][0-9a-f])[^0-9a-f]").unwrap();
|
||||
|
||||
let file = fs::File::open(filename)
|
||||
.map_err(|err| format!("Could not open {}: {err}", filename.display()))?;
|
||||
let reader = std::io::BufReader::new(file);
|
||||
|
||||
let mut compressed: bool = false;
|
||||
let mut data_size: usize = 0;
|
||||
let mut compressed_size: usize = 0;
|
||||
let mut bytes: Vec<u8> = vec![];
|
||||
let mut in_bytes: bool = false;
|
||||
|
||||
for line in reader.lines() {
|
||||
let line = line.unwrap();
|
||||
if in_bytes {
|
||||
if line.contains("};") {
|
||||
break;
|
||||
}
|
||||
|
||||
// Extract all of the two-digit hex numbers from the line and convert
|
||||
// them to a vector of u8. captures_iter() will return an iterator only
|
||||
// over matches, ignoring everything else, even if there are 0 matches.
|
||||
let row = re_bytes
|
||||
.captures_iter(&line)
|
||||
.map(|c| u8::from_str_radix(&c[1], 16).unwrap());
|
||||
bytes.extend(row);
|
||||
continue;
|
||||
}
|
||||
if line.contains("COMPRESSION: NO") {
|
||||
compressed = false;
|
||||
continue;
|
||||
}
|
||||
if line.contains("COMPRESSION: YES") {
|
||||
compressed = true;
|
||||
continue;
|
||||
}
|
||||
if let Some(size) = re_datasize.captures(&line) {
|
||||
data_size = size[1].parse().unwrap();
|
||||
continue;
|
||||
}
|
||||
if let Some(size) = re_compressedsize.captures(&line) {
|
||||
compressed_size = size[1].parse().unwrap();
|
||||
continue;
|
||||
}
|
||||
if line.contains(&format!("static BINDATA_CONST NvU8 {array}")) {
|
||||
in_bytes = true;
|
||||
bytes.reserve_exact(if compressed {
|
||||
compressed_size
|
||||
} else {
|
||||
data_size
|
||||
});
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if bytes.is_empty() {
|
||||
return Err(format!("Error: no data found for {array}"));
|
||||
}
|
||||
|
||||
let output = if compressed {
|
||||
if bytes.len() != compressed_size {
|
||||
return Err(format!("compressed array {array} in {} should be {compressed_size} bytes but is actually {}",
|
||||
filename.display(), bytes.len()));
|
||||
}
|
||||
let mut uncompressed = Vec::<u8>::with_capacity(data_size);
|
||||
let mut decompressor = Decompress::new(false);
|
||||
if let Err(err) =
|
||||
decompressor.decompress_vec(&bytes, &mut uncompressed, FlushDecompress::Finish)
|
||||
{
|
||||
return Err(format!("array {array} in {} decompressed to {} bytes but should have been {data_size} bytes: {err}",
|
||||
filename.display(), bytes.len()));
|
||||
}
|
||||
|
||||
uncompressed
|
||||
} else {
|
||||
if bytes.len() != data_size {
|
||||
return Err(format!("array {array} in {} should be {data_size} bytes but is actually {}",
|
||||
filename.display(), bytes.len()));
|
||||
}
|
||||
|
||||
bytes
|
||||
};
|
||||
|
||||
if let Some(size) = expected_size {
|
||||
if size != output.len() {
|
||||
return Err(format!("array {array} in {} is {} bytes but should be {size} bytes",
|
||||
filename.display(), output.len()));
|
||||
}
|
||||
}
|
||||
|
||||
Ok(output)
|
||||
}
|
||||
|
||||
fn round_up_to_base(x: u32, base: u32) -> u32 {
|
||||
((x + base - 1) / base) * base
|
||||
}
|
||||
|
||||
fn bootloader(output: &Path, version: &str, gpu: &str, prod: &str) -> Result<(), String> {
|
||||
let gpu_upper = gpu.to_uppercase();
|
||||
let filename = PathBuf::from(format!(
|
||||
"src/nvidia/generated/g_bindata_kgspGetBinArchiveGspRmBoot_{gpu_upper}.c"
|
||||
));
|
||||
|
||||
println!("Creating nvidia/{gpu}/gsp/bootloader-{version}");
|
||||
|
||||
let gsp = output.join(format!("{gpu}/gsp"));
|
||||
fs::create_dir_all(&gsp)
|
||||
.map_err(|err| format!("Could not create nvidia/{gpu}/gsp/: {err}"))?;
|
||||
|
||||
// Extract the actual bootloader firmware
|
||||
let array = format!("kgspBinArchiveGspRmBoot_{gpu_upper}_ucode_image_{prod}data");
|
||||
let firmware = get_bytes(&filename, &array, None)?;
|
||||
let firmware_size: u32 = firmware.len() as u32;
|
||||
|
||||
// Extract the descriptor (RM_RISCV_UCODE_DESC)
|
||||
let array = format!("kgspBinArchiveGspRmBoot_{gpu_upper}_ucode_desc_{prod}data");
|
||||
let descriptor = get_bytes(&filename, &array, None)?;
|
||||
let descriptor_size: u32 = descriptor.len() as u32;
|
||||
|
||||
// Create the output
|
||||
let binfile: PathBuf = gsp.join(format!("bootloader-{version}.bin"));
|
||||
let mut file = fs::OpenOptions::new().write(true).create(true).open(&binfile)
|
||||
.map_err(|err| format!("Could not create {}: {err}", binfile.display()))?;
|
||||
|
||||
// First, add the nvfw_bin_hdr header
|
||||
let total_size: u32 = round_up_to_base(24 + firmware_size + descriptor_size, 256);
|
||||
let firmware_offset: u32 = 24 + descriptor_size;
|
||||
|
||||
let nvfw_bin_hdr = [0x10de, 1, total_size, 24, firmware_offset, firmware_size]
|
||||
.iter()
|
||||
.flat_map(|x| x.to_le_bytes())
|
||||
.collect::<Vec<_>>();
|
||||
file.write_all(&nvfw_bin_hdr)
|
||||
.map_err(|err| format!("Could not write to {}: {err}", filename.display()))?;
|
||||
|
||||
// Second, add the descriptor
|
||||
file.write_all(&descriptor)
|
||||
.map_err(|err| format!("Could not write to {}: {err}", filename.display()))?;
|
||||
|
||||
// Finally, the actual bootloader image
|
||||
file.write_all(&firmware)
|
||||
.map_err(|err| format!("Could not write to {}: {err}", filename.display()))?;
|
||||
|
||||
file.sync_all()
|
||||
.map_err(|err| format!("Failed to sync {}: {err}", filename.display()))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
trait StrExt {
|
||||
fn capitalize_first_letter(&self) -> String;
|
||||
fn replace_last_with_x(&self) -> String;
|
||||
}
|
||||
|
||||
impl StrExt for str {
|
||||
fn capitalize_first_letter(&self) -> String {
|
||||
let (first, rest) = self.split_at(1);
|
||||
|
||||
first.to_ascii_uppercase() + rest
|
||||
}
|
||||
|
||||
fn replace_last_with_x(&self) -> String {
|
||||
format!("{}X", &self[..self.len() - 1])
|
||||
}
|
||||
}
|
||||
|
||||
/// GSP Booter load and unload
|
||||
fn booter(output: &Path, version: &str, gpu: &str, load: &str, sigsize: u32) -> Result<(), String> {
|
||||
let gpu_upper = gpu.to_uppercase();
|
||||
let load_upper: String = load.capitalize_first_letter();
|
||||
let filename: PathBuf = PathBuf::from(format!(
|
||||
"src/nvidia/generated/g_bindata_kgspGetBinArchiveBooter{load_upper}Ucode_{gpu_upper}.c"
|
||||
));
|
||||
|
||||
println!("Creating nvidia/{gpu}/gsp/booter_{load}-{version}.bin");
|
||||
|
||||
let gsp: PathBuf = output.join(format!("{gpu}/gsp"));
|
||||
fs::create_dir_all(&gsp)
|
||||
.map_err(|err| format!("Could not create nvidia/{gpu}/gsp/: {err}"))?;
|
||||
|
||||
// Extract the actual booter firmware
|
||||
let array = format!("kgspBinArchiveBooter{load_upper}Ucode_{gpu_upper}_image_prod_data");
|
||||
let firmware = get_bytes(&filename, &array, None)?;
|
||||
let firmware_size = firmware.len() as u32;
|
||||
|
||||
// Extract the signatures
|
||||
let array = format!("kgspBinArchiveBooter{load_upper}Ucode_{gpu_upper}_sig_prod_data");
|
||||
let signatures = get_bytes(&filename, &array, None)?;
|
||||
let signatures_size = signatures.len() as u32;
|
||||
if (signatures_size % sigsize) != 0 {
|
||||
return Err(format!("signature file size for {array} is uneven value of {sigsize}"));
|
||||
}
|
||||
let num_sigs = signatures_size / sigsize;
|
||||
if num_sigs < 1 {
|
||||
return Err(format!("invalid number of signatures: {num_sigs}"));
|
||||
}
|
||||
|
||||
// Extract the patch location
|
||||
let array = format!("kgspBinArchiveBooter{load_upper}Ucode_{gpu_upper}_patch_loc_data");
|
||||
let patch_loc_data = get_bytes(&filename, &array, Some(4))?;
|
||||
let patch_loc = LittleEndian::read_u32(&patch_loc_data);
|
||||
|
||||
// Extract the patch meta variables
|
||||
let array = format!("kgspBinArchiveBooter{load_upper}Ucode_{gpu_upper}_patch_meta_data");
|
||||
let patch_meta_data = get_bytes(&filename, &array, Some(12))?;
|
||||
|
||||
let (fuse_ver, engine_id, ucode_id) = patch_meta_data
|
||||
.chunks(4)
|
||||
.map(LittleEndian::read_u32)
|
||||
.collect_tuple()
|
||||
.unwrap();
|
||||
|
||||
let binfile: PathBuf = gsp.join(format!("booter_{load}-{version}.bin"));
|
||||
let mut file = fs::OpenOptions::new().write(true).create(true).open(&binfile)
|
||||
.map_err(|err| format!("Could not create {}: {err}", binfile.display()))?;
|
||||
|
||||
// First, add the nvfw_bin_hdr header
|
||||
let total_size: u32 = round_up_to_base(120 + signatures_size + firmware_size, 256);
|
||||
let firmware_offset: u32 = 120 + signatures_size;
|
||||
|
||||
let nvfw_bin_hdr = [0x10de, 1, total_size, 24, firmware_offset, firmware_size]
|
||||
.iter()
|
||||
.flat_map(|x| x.to_le_bytes())
|
||||
.collect::<Vec<u8>>();
|
||||
file.write_all(&nvfw_bin_hdr)
|
||||
.map_err(|err| format!("Could not write to {}: {err}", filename.display()))?;
|
||||
|
||||
// Second, add the nvfw_hs_header_v2 header
|
||||
let patch_loc_offset = 60 + signatures_size;
|
||||
let patch_sig_offset = patch_loc_offset + 4;
|
||||
let meta_data_offset = patch_sig_offset + 4;
|
||||
let num_sig_offset = meta_data_offset + 12;
|
||||
let header_offset = num_sig_offset + 4;
|
||||
|
||||
let nvfw_hs_header_v2 = [
|
||||
60,
|
||||
signatures_size,
|
||||
patch_loc_offset,
|
||||
patch_sig_offset,
|
||||
meta_data_offset,
|
||||
12,
|
||||
num_sig_offset,
|
||||
header_offset,
|
||||
36,
|
||||
]
|
||||
.iter()
|
||||
.flat_map(|x| x.to_le_bytes())
|
||||
.collect::<Vec<u8>>();
|
||||
file.write_all(&nvfw_hs_header_v2)
|
||||
.map_err(|err| format!("Could not write to {}: {err}", filename.display()))?;
|
||||
|
||||
// Third, the actual signatures
|
||||
file.write_all(&signatures)
|
||||
.map_err(|err| format!("Could not write to {}: {err}", filename.display()))?;
|
||||
|
||||
// Fourth, patch_loc[], patch_sig[], fuse_ver, engine_id, ucode_id, and num_sigs
|
||||
let patch_meta = [patch_loc, 0, fuse_ver, engine_id, ucode_id, num_sigs]
|
||||
.iter()
|
||||
.flat_map(|x| x.to_le_bytes())
|
||||
.collect::<Vec<u8>>();
|
||||
file.write_all(&patch_meta)
|
||||
.map_err(|err| format!("Could not write to {}: {err}", filename.display()))?;
|
||||
|
||||
// Extract the descriptor (nvkm_gsp_booter_fw_hdr)
|
||||
let array = format!("kgspBinArchiveBooter{load_upper}Ucode_{gpu_upper}_header_prod_data");
|
||||
let descriptor = get_bytes(&filename, &array, Some(36))?;
|
||||
|
||||
// Fifth, the descriptor
|
||||
file.write_all(&descriptor)
|
||||
.map_err(|err| format!("Could not write to {}: {err}", filename.display()))?;
|
||||
|
||||
// And finally, the actual scrubber image
|
||||
file.write_all(&firmware)
|
||||
.map_err(|err| format!("Could not write to {}: {err}", filename.display()))?;
|
||||
|
||||
file.sync_all()
|
||||
.map_err(|err| format!("Failed to sync {}: {err}", filename.display()))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// GPU memory scrubber, needed for some GPUs and configurations
|
||||
#[allow(dead_code)]
|
||||
fn scrubber(output: &Path, version: &str, gpu: &str, sigsize: u32) -> Result<(), String> {
|
||||
// Unfortunately, RM breaks convention with the scrubber image and labels
|
||||
// the files and arrays with AD10X instead of AD102.
|
||||
let gpux = gpu.replace_last_with_x().to_uppercase();
|
||||
|
||||
let filename = PathBuf::from(
|
||||
format!("src/nvidia/generated/g_bindata_ksec2GetBinArchiveSecurescrubUcode_{gpux}.c"));
|
||||
|
||||
println!("Creating nvidia/{gpu}/gsp/scrubber-{version}.bin");
|
||||
|
||||
let gsp: PathBuf = output.join(format!("{gpu}/gsp"));
|
||||
fs::create_dir_all(&gsp)
|
||||
.map_err(|err| format!("Could not create nvidia/{gpu}/gsp/: {err}"))?;
|
||||
|
||||
// Extract the actual scrubber firmware
|
||||
let array = format!("ksec2BinArchiveSecurescrubUcode_{gpux}_image_prod_data");
|
||||
let firmware = get_bytes(&filename, &array, None)?;
|
||||
let firmware_size = firmware.len() as u32;
|
||||
|
||||
// Extract the signatures
|
||||
let array = format!("ksec2BinArchiveSecurescrubUcode_{gpux}_sig_prod_data");
|
||||
let signatures = get_bytes(&filename, &array, None)?;
|
||||
let signatures_size = signatures.len() as u32;
|
||||
if (signatures_size % sigsize) != 0 {
|
||||
return Err(format!("signature file size for {array} is uneven value of {sigsize}"));
|
||||
}
|
||||
let num_sigs = signatures_size / sigsize;
|
||||
if num_sigs < 1 {
|
||||
return Err(format!("invalid number of signatures: {num_sigs}"));
|
||||
}
|
||||
|
||||
// Extract the patch location
|
||||
let array = format!("ksec2BinArchiveSecurescrubUcode_{gpux}_patch_loc_data");
|
||||
let patch_loc_data = get_bytes(&filename, &array, Some(4))?;
|
||||
let patch_loc = LittleEndian::read_u32(&patch_loc_data);
|
||||
|
||||
// Extract the patch meta variables
|
||||
let array = format!("ksec2BinArchiveSecurescrubUcode_{gpux}_patch_meta_data");
|
||||
let patch_meta_data = get_bytes(&filename, &array, Some(12))?;
|
||||
|
||||
let (fuse_ver, engine_id, ucode_id) = patch_meta_data
|
||||
.chunks(4)
|
||||
.map(LittleEndian::read_u32)
|
||||
.collect_tuple()
|
||||
.unwrap();
|
||||
|
||||
let binfile = gsp.join(format!("scrubber-{version}.bin"));
|
||||
let mut file = fs::OpenOptions::new().write(true).create(true).open(&binfile)
|
||||
.map_err(|err| format!("Could not create {}: {err}", binfile.display()))?;
|
||||
|
||||
// First, add the nvfw_bin_hdr header
|
||||
let total_size: u32 = round_up_to_base(120 + signatures_size + firmware_size, 256);
|
||||
let firmware_offset: u32 = 120 + signatures_size;
|
||||
|
||||
let nvfw_bin_hdr = [0x10de, 1, total_size, 24, firmware_offset, firmware_size]
|
||||
.iter()
|
||||
.flat_map(|x| x.to_le_bytes())
|
||||
.collect::<Vec<u8>>();
|
||||
file.write_all(&nvfw_bin_hdr)
|
||||
.map_err(|err| format!("Could not write to {}: {err}", filename.display()))?;
|
||||
|
||||
// Second, add the nvfw_hs_header_v2 header
|
||||
let patch_loc_offset = 60 + signatures_size;
|
||||
let patch_sig_offset = patch_loc_offset + 4;
|
||||
let meta_data_offset = patch_sig_offset + 4;
|
||||
let num_sig_offset = meta_data_offset + 12;
|
||||
let header_offset = num_sig_offset + 4;
|
||||
|
||||
let nvfw_hs_header_v2 = [
|
||||
60,
|
||||
signatures_size,
|
||||
patch_loc_offset,
|
||||
patch_sig_offset,
|
||||
meta_data_offset,
|
||||
12,
|
||||
num_sig_offset,
|
||||
header_offset,
|
||||
36,
|
||||
]
|
||||
.iter()
|
||||
.flat_map(|x| x.to_le_bytes())
|
||||
.collect::<Vec<u8>>();
|
||||
file.write_all(&nvfw_hs_header_v2)
|
||||
.map_err(|err| format!("Could not write to {}: {err}", filename.display()))?;
|
||||
|
||||
// Third, the actual signatures
|
||||
file.write_all(&signatures)
|
||||
.map_err(|err| format!("Could not write to {}: {err}", filename.display()))?;
|
||||
|
||||
// Fourth, patch_loc[], patch_sig[], fuse_ver, engine_id, ucode_id, and num_sigs
|
||||
let patch_meta = [patch_loc, 0, fuse_ver, engine_id, ucode_id, num_sigs]
|
||||
.iter()
|
||||
.flat_map(|x| x.to_le_bytes())
|
||||
.collect::<Vec<u8>>();
|
||||
file.write_all(&patch_meta)
|
||||
.map_err(|err| format!("Could not write to {}: {err}", filename.display()))?;
|
||||
|
||||
// Extract the descriptor (nvkm_gsp_booter_fw_hdr)
|
||||
let array = format!("ksec2BinArchiveSecurescrubUcode_{gpux}_header_prod_data");
|
||||
let descriptor = get_bytes(&filename, &array, Some(36))?;
|
||||
|
||||
// Fifth, the descriptor
|
||||
file.write_all(&descriptor)
|
||||
.map_err(|err| format!("Could not write to {}: {err}", filename.display()))?;
|
||||
|
||||
// And finally, the actual scrubber image
|
||||
file.write_all(&firmware)
|
||||
.map_err(|err| format!("Could not write to {}: {err}", filename.display()))?;
|
||||
|
||||
file.sync_all()
|
||||
.map_err(|err| format!("Failed to sync {}: {err}", filename.display()))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn main() -> ExitCode {
|
||||
let args = Cli::parse();
|
||||
|
||||
// If -i was specified, then we start by chdir to that path
|
||||
if let Err(err) = env::set_current_dir(&args.input) {
|
||||
println!("Error: could not chdir to {}: {err}", args.input.display());
|
||||
return ExitCode::from(2);
|
||||
}
|
||||
|
||||
// version.mk must exist in the current directory
|
||||
match fs::exists("version.mk") {
|
||||
Ok(false) | Err(_) => {
|
||||
println!("Error: version.mk not found");
|
||||
return ExitCode::from(2);
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
|
||||
// If -r is passed, then use that as the version number.
|
||||
// Otherwise, extract it from version.mk
|
||||
let version: String = if let Some(r) = &args.revision {
|
||||
r.clone()
|
||||
} else {
|
||||
let v = find_version();
|
||||
if v == None {
|
||||
println!("Error: could not determine firmware version");
|
||||
return ExitCode::from(1);
|
||||
}
|
||||
v.unwrap()
|
||||
};
|
||||
|
||||
println!("Generating files for version {version}");
|
||||
|
||||
// Create the "nvidia" subdir where all the files will go
|
||||
let output = args.output.join("nvidia");
|
||||
match fs::create_dir(&output) {
|
||||
Ok(_) => println!("Writing files to {}.", output.display()),
|
||||
Err(e) if e.kind() == std::io::ErrorKind::AlreadyExists =>
|
||||
println!("Overwriting files in {}", output.display()),
|
||||
Err(e) => {
|
||||
println!("Error: could not create \"nvidia\" target directory: {e}");
|
||||
return ExitCode::from(1);
|
||||
}
|
||||
}
|
||||
|
||||
let booters = [
|
||||
("tu102", "load", 16),
|
||||
("tu102", "unload", 16),
|
||||
("tu116", "load", 16),
|
||||
("tu116", "unload", 16),
|
||||
("ga100", "load", 384),
|
||||
("ga100", "unload", 384),
|
||||
("ga102", "load", 384),
|
||||
("ga102", "unload", 384),
|
||||
("ad102", "load", 384),
|
||||
("ad102", "unload", 384),
|
||||
];
|
||||
for (gpu, load, sigsize) in booters {
|
||||
if let Err(err) = booter(&output, &version, gpu, load, sigsize) {
|
||||
println!("{err}");
|
||||
return ExitCode::from(1);
|
||||
}
|
||||
}
|
||||
|
||||
let bootloaders = [
|
||||
("tu102", ""),
|
||||
("ga100", ""),
|
||||
("ga102", "prod_"),
|
||||
("ad102", "prod_"),
|
||||
];
|
||||
for (gpu, prod) in bootloaders {
|
||||
if let Err(err) = bootloader(&output, &version, gpu, prod) {
|
||||
println!("{err}");
|
||||
return ExitCode::from(1);
|
||||
}
|
||||
}
|
||||
|
||||
// Scrubber is currently not used, but let's generate the files anyway
|
||||
if let Err(err) = scrubber(&output, &version, "ad102", 384) {
|
||||
println!("{err}");
|
||||
return ExitCode::from(1);
|
||||
}
|
||||
|
||||
if args.driver.is_some() {
|
||||
let driver = args.driver.unwrap();
|
||||
println!("Extracting GSP-RM firmware from {}", driver.display());
|
||||
if let Err(err) = gsp_firmware(&output, &version, &driver) {
|
||||
println!("{err}");
|
||||
return ExitCode::from(1);
|
||||
}
|
||||
}
|
||||
|
||||
if args.whence {
|
||||
// Unlike all other output, the whence file goes in the root of the target
|
||||
// directory.
|
||||
println!("Creating {}/WHENCE.txt", args.output.display());
|
||||
if let Err(err) = whence(&args.output, &version) {
|
||||
println!("{err}");
|
||||
return ExitCode::from(1);
|
||||
}
|
||||
}
|
||||
|
||||
if args.symlink {
|
||||
println!("Creating symlinks in {}", output.display());
|
||||
if let Err(err) = symlinks(&output, &version) {
|
||||
println!("{err}");
|
||||
return ExitCode::from(1);
|
||||
}
|
||||
}
|
||||
|
||||
ExitCode::SUCCESS
|
||||
}
|
||||
Binary file not shown.
Reference in New Issue
Block a user