Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add automatic toolchain download #1

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 10 additions & 2 deletions .github/workflows/test-all.yml
Original file line number Diff line number Diff line change
Expand Up @@ -75,9 +75,12 @@ jobs:
- name: Build base image
# Always rebuild the base image when the scheduled workflow runs
if: inputs.flush_cache || steps.cache-image.outputs.cache-hit != 'true' || github.event_name == 'schedule'
env:
NXP_EMAIL: ${{ secrets.NXP_EMAIL }}
NXP_PASSWORD: ${{ secrets.NXP_PASSWORD }}
run: |
python3 -m pip install PyYAML
DOCKER_BUILDKIT=1 python3 build_image.py --config ofrak-core-dev.yml --base
DOCKER_BUILDKIT=1 python3 build_image.py --config ofrak-core-dev.yml --base --nxp-email "${NXP_EMAIL}" --nxp-password "${NXP_PASSWORD}"
- name: Export base image
if: inputs.flush_cache || steps.cache-image.outputs.cache-hit != 'true' || github.event_name == 'schedule'
run: |
Expand Down Expand Up @@ -113,14 +116,19 @@ jobs:
| docker load
docker images
- name: Build Ghidra image
env:
NXP_EMAIL: ${{ secrets.NXP_EMAIL }}
NXP_PASSWORD: ${{ secrets.NXP_PASSWORD }}
run: |
python3 -m pip install PyYAML
DOCKER_BUILDKIT=1 \
python3 build_image.py \
--config ofrak-ghidra.yml \
--base \
--finish \
--cache-from redballoonsecurity/ofrak/core-dev-base:latest
--cache-from redballoonsecurity/ofrak/core-dev-base:latest \
--nxp-email "${NXP_EMAIL}" \
--nxp-password "${NXP_PASSWORD}"
- name: Test documentation
run: |
docker run \
Expand Down
29 changes: 29 additions & 0 deletions build_image.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import tempfile
from dataclasses import dataclass
from enum import Enum
from typing import List, Optional
Expand Down Expand Up @@ -35,6 +36,8 @@ class OfrakImageConfig:
install_target: InstallTarget
cache_from: List[str]
entrypoint: Optional[str]
nxp_email: Optional[str]
nxp_password: Optional[str]

def validate_serial_txt_existence(self):
"""
Expand Down Expand Up @@ -81,11 +84,27 @@ def main():
for cache in config.cache_from:
cache_args.append("--cache-from")
cache_args.append(cache)
nxp_args = []
email_file = password_file = None
if config.nxp_email and config.nxp_password:
email_file = tempfile.NamedTemporaryFile(suffix=".txt", mode="w+")
email_file.write(config.nxp_email)
email_file.flush()
password_file = tempfile.NamedTemporaryFile(suffix=".txt", mode="w+")
password_file.write(config.nxp_password)
password_file.flush()
nxp_args = [
"--secret",
f"id=nxp_email,src={email_file.name}",
"--secret",
f"id=nxp_password,src={password_file.name}",
]
base_command = [
"docker",
"build",
"--build-arg",
"BUILDKIT_INLINE_CACHE=1",
*nxp_args,
"--cache-from",
f"{full_base_image_name}:master",
*cache_args,
Expand All @@ -108,6 +127,9 @@ def main():
print(f"Error running command: '{' '.join(error.cmd)}'")
print(f"Exit status: {error.returncode}")
sys.exit(error.returncode)
if email_file and password_file:
email_file.close()
password_file.close()

if config.build_finish:
full_image_name = "/".join((config.registry, config.image_name))
Expand Down Expand Up @@ -146,7 +168,12 @@ def parse_args() -> OfrakImageConfig:
default=InstallTarget.DEVELOP.value,
)
parser.add_argument("--cache-from", action="append")
parser.add_argument("--nxp-email")
parser.add_argument("--nxp-password")
args = parser.parse_args()
if (not not args.nxp_email) ^ (not not args.nxp_password):
raise RuntimeError("Must include the NXP email and password!")

with open(args.config) as file_handle:
config_dict = yaml.safe_load(file_handle)
image_config = OfrakImageConfig(
Expand All @@ -161,6 +188,8 @@ def parse_args() -> OfrakImageConfig:
InstallTarget(args.target),
args.cache_from,
config_dict.get("entrypoint"),
args.nxp_email,
args.nxp_password,
)
image_config.validate_serial_txt_existence()
return image_config
Expand Down
17 changes: 10 additions & 7 deletions ofrak_patch_maker/Dockerstub
Original file line number Diff line number Diff line change
Expand Up @@ -77,16 +77,19 @@ RUN if [ "$TARGETARCH" = "amd64" ]; then \
fi;

#PPCVLE 4 NXP GCC Fork
# Download the toolchain into your OFRAK directory from here (requires sign up):
# https://www.nxp.com/design/software/development-software/s32-design-studio-ide/s32-design-studio-for-power-architecture:S32DS-PA
# if the file `gcc-4.9.4-Ee200-eabivle-x86_64-linux-g2724867.zip` doesn't exist, the toolchain won't be installed, and corresponding tests will be skipped.
# Only runs if the NXP email and password to log in and download the toolchain are passed to build_image.py via the CLI flags
ARG OFRAK_DIR=.
COPY $OFRAK_DIR/gcc-4.9.4-Ee200-eabivle-x86_64-linux-g2724867.zi[p] /tmp
RUN test -f /tmp/gcc-4.9.4-Ee200-eabivle-x86_64-linux-g2724867.zip && \
COPY $OFRAK_DIR/ofrak_patch_maker/download_ppcvle.py /tmp/
RUN --mount=type=secret,id=nxp_email,dst=/tmp/nxp_email.txt \
--mount=type=secret,id=nxp_password,dst=/tmp/nxp_password.txt \
test -f /tmp/nxp_email.txt && \
test -f /tmp/nxp_password.txt && \
python3 -m pip install playwright && \
playwright install --with-deps chromium && \
python3 /tmp/download_ppcvle.py "$(cat /tmp/nxp_email.txt)" "$(cat /tmp/nxp_password.txt)" && \
cd /tmp && \
unzip -q gcc-4.9.4-Ee200-eabivle-x86_64-linux-g2724867.zip && \
cp -r powerpc-eabivle-4_9 /opt/rbs/toolchain/ && \
rm -rf powerpc-eabivle-4_9 && \
mv powerpc-eabivle-4_9 /opt/rbs/toolchain/ && \
rm gcc-4.9.4-Ee200-eabivle-x86_64-linux-g2724867.zip && \
dpkg --add-architecture i386 && \
apt-get update && \
Expand Down
53 changes: 53 additions & 0 deletions ofrak_patch_maker/download_ppcvle.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import argparse
import os

from playwright.sync_api import sync_playwright


START_URL = "https://www.nxp.com/design/software/development-software/s32-design-studio-ide/s32-design-studio-for-power-architecture:S32DS-PA"


def run(page, email: str, password: str, outfile: str) -> None:
print(f"Going to page {START_URL}", flush=True)
page.goto(START_URL)
page.get_by_role("listitem").filter(
has_text="Build Tools NXP Embedded GCC for Power Architecture"
).filter(has_text="Linux").get_by_role("link", name="Download", exact=True).click()

print("Signing in", flush=True)
page.locator("#username").click()
page.keyboard.type(email)
page.locator("#password").click()
page.keyboard.type(password)
page.get_by_role("button", name="SIGN IN").click()

print("Accepting terms and conditions", flush=True)
page.get_by_role("button", name="I Accept").click()

print("Waiting for download", flush=True)
with page.expect_download() as download_info:
# Download begins when the page is loaded
pass
os.rename(download_info.value.path(), outfile)

print(f"Complete! Saved to {outfile}", flush=True)


def main(args):
with sync_playwright() as playwright:
browser = playwright.chromium.launch(headless=True)
context = browser.new_context()
page = context.new_page()
run(page, args.email, args.password, args.outfile)
context.close()
browser.close()


if __name__ == "__main__":
argument_parser = argparse.ArgumentParser()
argument_parser.add_argument("email")
argument_parser.add_argument("password")
argument_parser.add_argument(
"-o", "--outfile", default="/tmp/gcc-4.9.4-Ee200-eabivle-x86_64-linux-g2724867.zip"
)
main(argument_parser.parse_args())