Files
StarPilot/panda/SConscript
T
firestar5683 5234a121f9 Add HKG EV App Start Climate wake
Add a StarPilot vehicle toggle that selects alternate Panda firmware for Hyundai/Kia/Genesis CAN-FD EVs. The firmware keeps the HKG CAN bus active while Panda is in power save and treats the app climate-active frame, bus 1 address 0x384 with byte 3 nonzero, as a Panda boot wake source without publishing it as CAN ignition.

Wire the toggle through StarPilot vehicle settings, Galaxy device settings, parameter definitions, and pandad firmware selection. Disabling the toggle selects the default Panda firmware again.

Tested on a Kia EV9 and 2023 Kia EV6. EV9 remote climate active used 0x384 byte 3 equal to 0x01; EV6 remote climate active used 0x0a. Both stopped/off states observed byte 3 equal to 0x00. Toggle-off negative testing on EV9 saw the remote climate frame on CAN but pandaStates.ignitionCan stayed false. Toggle-on testing verified the active Panda signature matched panda_h7_hkg_remote.bin.signed. Follow-up testing changed the HKG path to wake-only after remote climate caused partial-car fingerprinting and Dashcam Mode; wake-only firmware was built, flashed, and verified on both devices.
2026-07-01 11:32:29 -05:00

195 lines
6.2 KiB
Python

import os
import opendbc
import subprocess
PREFIX = "arm-none-eabi-"
BUILDER = "DEV"
common_flags = []
if os.getenv("RELEASE"):
BUILD_TYPE = "RELEASE"
cert_fn = os.getenv("CERT")
assert cert_fn is not None, 'No certificate file specified. Please set CERT env variable'
assert os.path.exists(cert_fn), 'Certificate file not found. Please specify absolute path'
else:
BUILD_TYPE = "DEBUG"
cert_fn = File("./certs/debug").srcnode().relpath
common_flags += ["-DALLOW_DEBUG"]
if os.getenv("DEBUG"):
common_flags += ["-DDEBUG"]
def objcopy(source, target, env, for_signature):
return '$OBJCOPY -O binary %s %s' % (source[0], target[0])
def get_version(builder, build_type):
try:
git = subprocess.check_output(["git", "rev-parse", "--short=8", "HEAD"], encoding='utf8').strip()
except subprocess.CalledProcessError:
git = "unknown"
return f"{builder}-{git}-{build_type}"
def get_key_header(name):
from Crypto.PublicKey import RSA
public_fn = File(f'./certs/{name}.pub').srcnode().get_path()
with open(public_fn) as f:
rsa = RSA.importKey(f.read())
assert(rsa.size_in_bits() == 1024)
rr = pow(2**1024, 2, rsa.n)
n0inv = 2**32 - pow(rsa.n, -1, 2**32)
r = [
f"RSAPublicKey {name}_rsa_key = {{",
f" .len = 0x20,",
f" .n0inv = {n0inv}U,",
f" .n = {to_c_uint32(rsa.n)},",
f" .rr = {to_c_uint32(rr)},",
f" .exponent = {rsa.e},",
f"}};",
]
return r
def to_c_uint32(x):
nums = []
for _ in range(0x20):
nums.append(x % (2**32))
x //= (2**32)
return "{" + 'U,'.join(map(str, nums)) + "U}"
def build_project(project_name, project, main, extra_flags):
project_dir = Dir(f'./board/obj/{project_name}/')
flags = project["FLAGS"] + extra_flags + common_flags + [
"-Wall",
"-Wextra",
"-Wstrict-prototypes",
"-Werror",
"-mlittle-endian",
"-mthumb",
"-nostdlib",
"-fno-builtin",
"-std=gnu11",
"-fmax-errors=1",
f"-T{File(project['LINKER_SCRIPT']).srcnode().relpath}",
"-fsingle-precision-constant",
"-Os",
"-g",
]
env = Environment(
ENV=os.environ,
CC=PREFIX + 'gcc',
AS=PREFIX + 'gcc',
OBJCOPY=PREFIX + 'objcopy',
OBJDUMP=PREFIX + 'objdump',
OBJPREFIX=project_dir,
CFLAGS=flags,
ASFLAGS=flags,
LINKFLAGS=flags,
CPPPATH=[Dir("./"), "./board/stm32f4/inc", "./board/stm32h7/inc", opendbc.INCLUDE_PATH],
ASCOM="$AS $ASFLAGS -o $TARGET -c $SOURCES",
BUILDERS={
'Objcopy': Builder(generator=objcopy, suffix='.bin', src_suffix='.elf')
},
tools=["default", "compilation_db"],
)
startup = env.Object(project["STARTUP_FILE"])
# Build bootstub
bs_env = env.Clone()
bs_env.Append(CFLAGS="-DBOOTSTUB", ASFLAGS="-DBOOTSTUB", LINKFLAGS="-DBOOTSTUB")
bs_elf = bs_env.Program(f"{project_dir}/bootstub.elf", [
startup,
"./crypto/rsa.c",
"./crypto/sha.c",
"./board/bootstub.c",
])
bs_env.Objcopy(f"./board/obj/bootstub.{project_name}.bin", bs_elf)
# Build + sign main (aka app)
main_elf = env.Program(f"{project_dir}/main.elf", [
startup,
main
], LINKFLAGS=[f"-Wl,--section-start,.isr_vector={project['APP_START_ADDRESS']}"] + flags)
main_bin = env.Objcopy(f"{project_dir}/main.bin", main_elf)
sign_py = File(f"./crypto/sign.py").srcnode().relpath
env.Command(f"./board/obj/{project_name}.bin.signed", main_bin, f"SETLEN=1 {sign_py} $SOURCE $TARGET {cert_fn}")
base_project_f4 = {
"STARTUP_FILE": "./board/stm32f4/startup_stm32f413xx.s",
"LINKER_SCRIPT": "./board/stm32f4/stm32f4_flash.ld",
"APP_START_ADDRESS": "0x8004000",
"FLAGS": [
"-mcpu=cortex-m4",
"-mhard-float",
"-DSTM32F4",
"-DSTM32F413xx",
"-Iboard/stm32f4/inc",
"-mfpu=fpv4-sp-d16",
],
}
base_project_h7 = {
"STARTUP_FILE": "./board/stm32h7/startup_stm32h7x5xx.s",
"LINKER_SCRIPT": "./board/stm32h7/stm32h7x5_flash.ld",
"APP_START_ADDRESS": "0x8020000",
"FLAGS": [
"-mcpu=cortex-m7",
"-mhard-float",
"-DSTM32H7",
"-DSTM32H725xx",
"-Iboard/stm32h7/inc",
"-mfpu=fpv5-d16",
],
}
# Common autogenerated includes
with open("board/obj/gitversion.h", "w") as f:
version = get_version(BUILDER, BUILD_TYPE)
version_len = len(version) + 1 # include terminating NUL
f.write(f'extern const uint8_t gitversion[{version_len}];\n')
f.write(f'const uint8_t gitversion[{version_len}] = "{version}";\n')
with open("board/obj/version", "w") as f:
f.write(f'{get_version(BUILDER, BUILD_TYPE)}')
certs = [get_key_header(n) for n in ["debug", "release"]]
with open("board/obj/cert.h", "w") as f:
for cert in certs:
f.write("\n".join(cert) + "\n")
# panda fw
build_project("panda", base_project_f4, "./board/main.c", [])
build_project("panda_h7", base_project_h7, "./board/main.c", [])
build_project("panda_remote", base_project_f4, "./board/main.c", ["-DPANDA_GM_REMOTE_START_C9"])
build_project("panda_h7_remote", base_project_h7, "./board/main.c", ["-DPANDA_GM_REMOTE_START_C9"])
build_project("panda_hkg_remote", base_project_f4, "./board/main.c", ["-DPANDA_HKG_REMOTE_START"])
build_project("panda_h7_hkg_remote", base_project_h7, "./board/main.c", ["-DPANDA_HKG_REMOTE_START"])
build_project("panda_can_ignition_only", base_project_f4, "./board/main.c", ["-DPANDA_IGNORE_IGNITION_LINE"])
build_project("panda_h7_can_ignition_only", base_project_h7, "./board/main.c", ["-DPANDA_IGNORE_IGNITION_LINE"])
build_project("panda_remote_can_ignition_only", base_project_f4, "./board/main.c", ["-DPANDA_GM_REMOTE_START_C9", "-DPANDA_IGNORE_IGNITION_LINE"])
build_project("panda_h7_remote_can_ignition_only", base_project_h7, "./board/main.c", ["-DPANDA_GM_REMOTE_START_C9", "-DPANDA_IGNORE_IGNITION_LINE"])
build_project("panda_hkg_remote_can_ignition_only", base_project_f4, "./board/main.c", ["-DPANDA_HKG_REMOTE_START", "-DPANDA_IGNORE_IGNITION_LINE"])
build_project("panda_h7_hkg_remote_can_ignition_only", base_project_h7, "./board/main.c", ["-DPANDA_HKG_REMOTE_START", "-DPANDA_IGNORE_IGNITION_LINE"])
# panda jungle fw
flags = [
"-DPANDA_JUNGLE",
]
if os.getenv("FINAL_PROVISIONING"):
flags += ["-DFINAL_PROVISIONING"]
build_project("panda_jungle_h7", base_project_h7, "./board/jungle/main.c", flags)
# body fw
build_project("body_h7", base_project_h7, "./board/body/main.c", ["-DPANDA_BODY"])
# test files
if GetOption('extras'):
SConscript('tests/libpanda/SConscript')