Skip to content

Commit

Permalink
Adopt swiftlang soundness workflow (#80)
Browse files Browse the repository at this point in the history
Replaces our lint workflow with a more general solution from the swiftlang/github-workflow repo.
  • Loading branch information
rauhul authored Jan 3, 2025
1 parent 34d0bb5 commit 8795b3f
Show file tree
Hide file tree
Showing 12 changed files with 155 additions and 100 deletions.
10 changes: 10 additions & 0 deletions .flake8
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
[flake8]

ignore =
# These are needed to make our license headers pass the linting
E265,
E266,

# 10% larger than the standard 80 character limit. Conforms to the black
# standard and Bugbear's B950.
max-line-length = 88
2 changes: 1 addition & 1 deletion .github/workflows/build-esp.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ on:
branches: ["main"]
schedule:
# Build on Mondays at 9am PST every week
- cron: '0 17 * * 1'
- cron: '0 17 * * 1'

jobs:
build-esp:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/build-nuttx.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ on:
branches: ["main"]
schedule:
# Build on Mondays at 9am PST every week
- cron: '0 17 * * 1'
- cron: '0 17 * * 1'

jobs:
build-nuttx:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/build-pico-sdk.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ on:
branches: ["main"]
schedule:
# Build on Mondays at 9am PST every week
- cron: '0 17 * * 1'
- cron: '0 17 * * 1'

jobs:
build-pico-sdk:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/build-zephyr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ on:
branches: ["main"]
schedule:
# Build on Mondays at 9am PST every week
- cron: '0 17 * * 1'
- cron: '0 17 * * 1'

jobs:
build-zephyr:
Expand Down
18 changes: 11 additions & 7 deletions .github/workflows/lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,22 +7,26 @@ on:
branches: ["main"]

jobs:
lint:
name: Lint
validate_format_config:
name: Validate Format Config
runs-on: ubuntu-latest
container: swift:6.0-jammy

steps:
- name: Checkout repo
uses: actions/checkout@v4

- name: Install apt dependencies
run: apt-get -qq update && apt-get -qq -y install curl
run: sudo apt-get -qq update && sudo apt-get -qq -y install curl

- name: Compare against swift-mmio swift-format config
run: |
curl -sL https://raw.githubusercontent.com/apple/swift-mmio/refs/heads/main/.swift-format -o .swift-format-mmio
diff .swift-format .swift-format-mmio
- name: Lint
run: swift-format lint --recursive --strict .
soundness:
name: Soundness
uses: swiftlang/github-workflows/.github/workflows/soundness.yml@main
with:
api_breakage_check_enabled: false # this repo doesn't vend any API
license_header_check_enabled: false # feature: https://github.com/swiftlang/github-workflows/issues/78
license_header_check_project_name: "Swift.org" # bug: https://github.com/swiftlang/github-workflows/issues/76
unacceptable_language_check_enabled: false # unfortunately many hardware specs use terms like master/slave in their documentation
68 changes: 45 additions & 23 deletions Tools/macho2bin.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,36 +8,45 @@
# See https://swift.org/LICENSE.txt for license information

#
# macho2bin -- Converts a statically-linked executable Mach-O into a flat "BIN" file suitable for flashing as a single
# contiguous blob onto some embedded devices. Note that this format assumes the embedded device can boot from a state
# where the entire firmware (all segments) are flashed contigously into one smalle address range. This is true for e.g.
# the STM32F746 devices if we place the vector table at 0x00200000, and code and data right after it, as the vector
# table also contains a pointer to the initial PC. This setup might not work for other devices.
# macho2bin -- Converts a statically-linked executable Mach-O into a flat "BIN" file
# suitable for flashing as a single contiguous blob onto some embedded devices. Note
# that this format assumes the embedded device can boot from a state where the entire
# firmware (all segments) are flashed contigously into one smalle address range. This
# is true for e.g. the STM32F746 devices if we place the vector table at 0x00200000,
# and code and data right after it, as the vector table also contains a pointer to the
# initial PC. This setup might not work for other devices.
#
# Usage:
# $ macho2bin.py <input> <output> --base-address <base-address> --segments <segment-list>
# $ macho2bin.py <input> <output> --base-address <base-address> --segments
# <segment-list>
#
# Example:
# $ macho2bin.py ./blink ./blink.bin --base-address 0x00200000 --segments '__TEXT,__DATA,__VECTORS'
# $ macho2bin.py ./blink ./blink.bin --base-address 0x00200000 --segments
# '__TEXT,__DATA,__VECTORS'
#
# Requirements and notes:
# * The output BIN file is a flat contiguous representation of the segments (--segments) based on their VM addresses.
# * The BIN file's first byte corresponds to the specified base address (--base-address).
# * The output BIN file is a flat contiguous representation of the segments
# (--segments) based on their VM addresses.
# * The BIN file's first byte corresponds to the specified base address
# (--base-address).
# * Any gaps between segments are filled with zero bytes.
# * Because of that, you want the input Mach-O to have all segments "close", and not have gaps.
# * Because of that, you want the input Mach-O to have all segments "close", and not
# have gaps.
#

import argparse
import os

from macholib import MachO
from macholib import mach_o


def main():
parser = argparse.ArgumentParser()
parser.add_argument('input')
parser.add_argument('output')
parser.add_argument('--base-address', required=True)
parser.add_argument('--segments', required=True)
parser.add_argument("input")
parser.add_argument("output")
parser.add_argument("--base-address", required=True)
parser.add_argument("--segments", required=True)
args = parser.parse_args()
args.base_address = int(args.base_address, 16)
args.segments = args.segments.split(",")
Expand All @@ -49,17 +58,23 @@ def main():
for command in mh.commands:
if isinstance(command[1], mach_o.segment_command):
(_, segment, sections) = command
segname = segment.segname.decode().strip('\0')
if segname not in args.segments: continue
segname = segment.segname.decode().strip("\0")
if segname not in args.segments:
continue

with open(args.input, "rb") as f:
f.seek(mh.offset + segment.fileoff)
data = f.read(segment.filesize)
segments.append({"vmaddr": segment.vmaddr, "data": data, "name": segname})

segments.append(
{"vmaddr": segment.vmaddr, "data": data, "name": segname}
)

segments = sorted(segments, key=lambda x: x["vmaddr"])

assert segments[0]["vmaddr"] == args.base_address, f"first segment's vmaddr 0x{segments[0]['vmaddr']:08x} does not match the passed --base-address 0x{args.base_address:08x}"
assert segments[0]["vmaddr"] == args.base_address, (
f"first segment's vmaddr 0x{segments[0]['vmaddr']:08x} does not match the"
f" passed --base-address 0x{args.base_address:08x}"
)

if os.path.exists(args.output):
os.unlink(args.output)
Expand All @@ -69,15 +84,22 @@ def main():
for segment in segments:
gap = segment["vmaddr"] - vmaddr
if gap != 0:
print(f"Writing gap of size {gap} (0x{gap:0x}) at vmaddr 0x{vmaddr:08x}")
f.write(b'\0' * gap)
print(
f"Writing gap of size {gap} (0x{gap:0x}) at vmaddr 0x{vmaddr:08x}"
)
f.write(b"\0" * gap)
assert gap >= 0
vmaddr = segment["vmaddr"]
print(f"Writing segment {segment['name']} size {len(segment['data'])} (0x{len(segment['data']):x}) at vmaddr 0x{vmaddr:08x}")
print(
f"Writing segment {segment['name']} size"
f" {len(segment['data'])} (0x{len(segment['data']):x}) at vmaddr"
f" 0x{vmaddr:08x}"
)
f.write(segment["data"])
vmaddr = segment["vmaddr"] + len(segment["data"])

print(f"Produced {args.output} with {vmaddr - args.base_address} bytes")

if __name__ == '__main__':

if __name__ == "__main__":
main()
97 changes: 60 additions & 37 deletions Tools/macho2uf2.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,80 +8,97 @@
# See https://swift.org/LICENSE.txt for license information

#
# macho2uf2 -- Converts a statically-linked executable Mach-O into a flat "UF2" file suitable for flashing as a single
# contiguous blob onto some embedded devices, in particular Raspberry Pi Pico (W). Note that the UF2 format allows for
# discontiguous memory regions, but this utility does not support that.
# macho2uf2 -- Converts a statically-linked executable Mach-O into a flat "UF2" file
# suitable for flashing as a single contiguous blob onto some embedded devices, in
# particular Raspberry Pi Pico (W). Note that the UF2 format allows for discontiguous
# memory regions, but this utility does not support that.
#
# Usage:
# $ macho2uf2.py <input> <output> --base-address <base-address> --segments <segment-list>
# $ macho2uf2.py <input> <output> --base-address <base-address> --segments
# <segment-list>
#
# Example:
# $ macho2uf2.py ./blink ./blink.uf2 --base-address 0x00200000 --segments '__TEXT,__DATA,__VECTORS'
# $ macho2uf2.py ./blink ./blink.uf2 --base-address 0x00200000 --segments
# '__TEXT,__DATA,__VECTORS'
#
# Requirements and notes:
# * The output UF2 file is a flat contiguous representation of the segments (--segments) based on their VM addresses.
# * The UF2 file's first byte corresponds to the specified base address (--base-address).
# * The output UF2 file is a flat contiguous representation of the segments
# (--segments) based on their VM addresses.
# * The UF2 file's first byte corresponds to the specified base address
# (--base-address).
# * Any gaps between segments are filled with zero bytes.
# * Because of that, you want the input Mach-O to have all segments "close", and not have gaps.
# * Because of that, you want the input Mach-O to have all segments "close", and not
# have gaps.
#

import argparse
import os
import subprocess
import struct
from macholib import MachO
from macholib import mach_o
import subprocess

MY_DIR = os.path.dirname(os.path.abspath(__file__))


def main():
parser = argparse.ArgumentParser()
parser.add_argument('input')
parser.add_argument('output')
parser.add_argument('--base-address', required=True)
parser.add_argument('--segments', required=True)
parser.add_argument('--pico-family', required=True)
parser.add_argument("input")
parser.add_argument("output")
parser.add_argument("--base-address", required=True)
parser.add_argument("--segments", required=True)
parser.add_argument("--pico-family", required=True)
args = parser.parse_args()
args.base_address = int(args.base_address, 16)
args.segments = args.segments.split(",")
if args.pico_family == "rp2040":
family_id = 0xE48BFF56
add_errata_block = False
elif args.pico_family == "rp2350":
family_id = 0XE48BFF59
family_id = 0xE48BFF59
add_errata_block = True
else:
assert False

subprocess.check_call([MY_DIR + "/macho2bin.py", args.input, args.input + ".bin", "--base-address", "0x%08x" % args.base_address, "--segments", ",".join(args.segments)])
subprocess.check_call(
[
MY_DIR + "/macho2bin.py",
args.input,
args.input + ".bin",
"--base-address",
"0x%08x" % args.base_address,
"--segments",
",".join(args.segments),
]
)

def emit_block(output, block, vmaddr, block_number, num_blocks, family_id):
assert len(block) <= 256

if len(block) < 256:
block += b'\0' * (256 - len(block))
block += b"\0" * (256 - len(block))

# UF2_Block header
output += struct.pack("<I", 0x0A324655) # magicStart0
output += struct.pack("<I", 0x9E5D5157) # magicStart1
output += struct.pack("<I", 0x2000) # flags: familyID present
output += struct.pack("<I", vmaddr) # targetAddr
output += struct.pack("<I", 256) # payloadSize
output += struct.pack("<I", block_number) # blockNo
output += struct.pack("<I", num_blocks) # numBlocks
output += struct.pack("<I", family_id) # fileSize / familyID: rp2040/rp2350 family ID
output += struct.pack("<I", 0x0A324655) # magicStart0
output += struct.pack("<I", 0x9E5D5157) # magicStart1
output += struct.pack("<I", 0x2000) # flags: familyID present
output += struct.pack("<I", vmaddr) # targetAddr
output += struct.pack("<I", 256) # payloadSize
output += struct.pack("<I", block_number) # blockNo
output += struct.pack("<I", num_blocks) # numBlocks
output += struct.pack(
"<I", family_id
) # fileSize / familyID: rp2040/rp2350 family ID

# Data
if len(block) < 476:
block += b'\0' * (476 - len(block))
block += b"\0" * (476 - len(block))
output += block

# UF2_Block footer
output += struct.pack("<I", 0x0AB16F30) # magicEnd
output += struct.pack("<I", 0x0AB16F30) # magicEnd

return output

output = b''
output = b""
with open(args.input + ".bin", "rb") as f:
if add_errata_block:
# RP2350-E10 errata
Expand All @@ -97,14 +114,20 @@ def emit_block(output, block, vmaddr, block_number, num_blocks, family_id):
block = f.read(256)
if len(block) == 0:
break
output = emit_block(output, block, vmaddr, block_number, num_blocks, family_id)
output = emit_block(
output, block, vmaddr, block_number, num_blocks, family_id
)
block_number += 1
vmaddr += 256

with open(args.output, "wb") as f:
f.write(output)

print(f"Produced {args.output} with total size {len(output)} (0x{len(output):0x}) bytes, payload size {total_size} (0x{total_size:0x}) bytes")

if __name__ == '__main__':
print(
f"Produced {args.output} with total size {len(output)} (0x{len(output):0x})"
f" bytes, payload size {total_size} (0x{total_size:0x}) bytes"
)


if __name__ == "__main__":
main()
4 changes: 2 additions & 2 deletions nrfx-blink-sdk/west.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,5 @@ manifest:
revision: main
import:
name-allowlist:
- cmsis # required by the ARM port
- hal_nordic # required by the custom_plank board (Nordic based)
- cmsis # required by the ARM port
- hal_nordic # required by the custom_plank board (Nordic based)
Loading

0 comments on commit 8795b3f

Please sign in to comment.