diff --git a/.wordlist.txt b/.wordlist.txt index 881d973ccf..009599e7d6 100644 --- a/.wordlist.txt +++ b/.wordlist.txt @@ -7405,4 +7405,61 @@ requantized requantizing signedness virsh -virt \ No newline at end of file +virt +Autoboot +BOOTMODE +DTB +Decompile +EVM +FITs +HSM +Jamil +Keywriter +LBA +MBR +MCUboot +MoltenVK +SPL +ScaledObjects +Scaler's +Securable +TI's +TIFS +Terraform's +UBOOT +adab +adbc +afaf +arago +autoboot +bafb +bceffbb +bcfc +beeb +befc +bootloader's +caidas +cded +ceefb +cffee +decompiling +eFuses +eadeefc +edcf +efbc +evm +fbcacd +fcea +fcfa +fdaaaca +fdbc +lastmod +lxx +shaderFloat +subimages +swin +tiboot +tisdk +tispl +uboot +codename diff --git a/assets/contributors.csv b/assets/contributors.csv index aebcbfafd6..97c34196a6 100644 --- a/assets/contributors.csv +++ b/assets/contributors.csv @@ -141,4 +141,5 @@ Tirui Wu, Arm,,,, William Watson, Arm,,,, John O'Hara,Arm,JohnOhara-Arm,johnaohara,JohnnyDoItAll, Usamah Zaheer,Arm,usamahz,,, -Elad Gross,Arm,eladgr,,, +Roy Jamil,Ac6,RoyAc6,royjamil,,https://www.ac6.fr/en/ +Elad Gross,Arm,eladgr,,, diff --git a/assets/css/content.css b/assets/css/content.css index 254c7d9d64..a9abdb7bc5 100644 --- a/assets/css/content.css +++ b/assets/css/content.css @@ -186,15 +186,9 @@ div.answer label { .normal-tabpane { box-sizing: border-box; margin-left: 16px; + margin-right: 16px; margin-top: 8px; margin-bottom: 8px; - - padding-left: 16px; - padding-right: 16px; - padding-bottom: 16px; - - background-color: var(--arm-color-surface); - --ads-horizontal-tabs-navigation-background-color: var(--arm-color-surface); } .normal-horizontal-tabs { --ads-horizontal-tabs-tab-color: white; @@ -202,14 +196,25 @@ div.answer label { --ads-horizontal-tabs-underline-color: var(--arm-orange); --ads-horizontal-tabs-tab-color-hover: var(--lp-hover-text); + --ads-horizontal-tabs-navigation-background-color: var(--arm-color-surface); --ads-horizontal-tabs-tab-content-background-color: var(--arm-color-footing); } +.normal-horizontal-tabs .normal-tab { + padding-bottom: 16px; +} + .normal-horizontal-tabs table, .normal-horizontal-tabs p { padding-top: 16px; padding-left: 16px; padding-right: 16px; } +.normal-horizontal-tabs div.code-toolbar { + margin-left: 16px; + margin-right: 16px; + margin-top: 16px; + margin-bottom: 16px; +} .normal-horizontal-tabs table{ padding-bottom: 8px; } diff --git a/assets/css/list-pages.css b/assets/css/list-pages.css index ddc66c6478..1e3eb8b1a7 100644 --- a/assets/css/list-pages.css +++ b/assets/css/list-pages.css @@ -363,13 +363,13 @@ html[theme="dark"] .learning-path-title { .install-guide-title { margin-top: 0px; margin-bottom: 0px; - font-size: 1.25rem!important; + font-size: 1.1875rem!important; line-height: 1.3; } ads-card.tool-card { - height: 112px; + height: 128px; --ads-card-border-radius: 8px; --ads-card-border-width: 1px; } diff --git a/content/learning-paths/automotive/_index.md b/content/learning-paths/automotive/_index.md index 0ec2943ade..ede5cbdffa 100644 --- a/content/learning-paths/automotive/_index.md +++ b/content/learning-paths/automotive/_index.md @@ -19,7 +19,7 @@ subjects_filter: - Performance and Architecture: 10 operatingsystems_filter: - Baremetal: 1 -- Linux: 14 +- Linux: 15 - macOS: 1 - other: 1 - RTOS: 2 @@ -33,7 +33,7 @@ tools_software_languages_filter: - CMake: 1 - CPP: 1 - DDS: 1 -- Docker: 6 +- Docker: 7 - FreeRTOS: 1 - FVP: 2 - Gazebo: 1 @@ -43,17 +43,17 @@ tools_software_languages_filter: - Multipass: 1 - Navigation2: 1 - Perf: 1 -- Python: 2 +- Python: 3 - Raspberry Pi: 2 - rmw_zenoh: 2 -- ROS 2: 6 +- ROS 2: 7 - Rust: 1 - RViz: 1 - SME2: 1 - Tinkerblox: 1 - topdown-tool: 1 - Yocto: 3 -- Zenoh: 3 +- Zenoh: 4 # auto-generated padding to avoid Hugo YAML alias limit # auto-generated padding to avoid Hugo YAML alias limit # auto-generated padding to avoid Hugo YAML alias limit diff --git a/content/learning-paths/automotive/freertos-zena-css/Console_tmux.png b/content/learning-paths/automotive/freertos-zena-css/console_tmux.png similarity index 100% rename from content/learning-paths/automotive/freertos-zena-css/Console_tmux.png rename to content/learning-paths/automotive/freertos-zena-css/console_tmux.png diff --git a/content/learning-paths/cross-platform/cca_rme/_images/l1gpt_0xA.png b/content/learning-paths/cross-platform/cca_rme/_images/l1gpt_0xa.png similarity index 100% rename from content/learning-paths/cross-platform/cca_rme/_images/l1gpt_0xA.png rename to content/learning-paths/cross-platform/cca_rme/_images/l1gpt_0xa.png diff --git a/content/learning-paths/cross-platform/cca_rme/bare-metal.md b/content/learning-paths/cross-platform/cca_rme/bare-metal.md index 96855f4d6c..68d15c1195 100644 --- a/content/learning-paths/cross-platform/cca_rme/bare-metal.md +++ b/content/learning-paths/cross-platform/cca_rme/bare-metal.md @@ -66,7 +66,7 @@ The L1 table defines permissions for each 16KB granule. Use the Arm Debugger MMU/MPU pane to observe these attributes: -![Screenshot of Arm Debugger MMU/MPU pane showing L1 Granule Protection Table entries for memory region 0xA0000000, displaying Realm access permissions for 16KB granules#center](_images/l1gpt_0xA.png) +![Screenshot of Arm Debugger MMU/MPU pane showing L1 Granule Protection Table entries for memory region 0xA0000000, displaying Realm access permissions for 16KB granules#center](_images/l1gpt_0xa.png) ![Screenshot of Arm Debugger MMU/MPU pane showing L1 Granule Protection Table entries for memory region 0x80000000, displaying the protection attributes for this address range#center](_images/l1gpt_0x8.png) diff --git a/content/learning-paths/cross-platform/ros2-device-connect/_index.md b/content/learning-paths/cross-platform/ros2-device-connect/_index.md new file mode 100644 index 0000000000..dd1d57c4f8 --- /dev/null +++ b/content/learning-paths/cross-platform/ros2-device-connect/_index.md @@ -0,0 +1,85 @@ +--- +title: Get started with ROS 2 and Device Connect on Arm + +draft: true +cascade: + draft: true + +description: Learn how to expose a ROS 2 system running in Docker on an Arm-based Linux device as a discoverable Device Connect device, and inspect its ROS 2 graph through remote procedure calls. + +minutes_to_complete: 30 + +who_is_this_for: This is an introductory topic for robotics and edge developers who want to make a ROS 2 system discoverable and callable by other devices and AI agents using Device Connect, without changing the ROS 2 application itself. + +learning_objectives: + - Explain how a Device Connect adapter bridges a containerized ROS 2 system to a Device Connect network + - Set up ROS 2 in Docker and the Device Connect Python packages on an Arm-based Linux machine + - Run the adapter in device-to-device (D2D) mode and call read-only ROS 2 inspection RPCs from a Python client + - Describe how deployment profiles map the same adapter onto real hardware such as a Raspberry Pi 5 with a camera + +prerequisites: + - An Arm-based Linux machine, such as a Raspberry Pi 5, an Arm cloud instance, or an Arm-based laptop, running Ubuntu 22.04 or later + - Basic familiarity with Python, the Linux command line, and ROS 2 concepts such as nodes and topics + +author: + - Kieran Hejmadi + - Odin Shen + +# New Learning Paths are opted in for the next manual generated summary/FAQ run. +# The generator resets this to false after a successful write. +generate_summary_faq: true + +# Optional one-shot controls: set either field to true to regenerate just that +# generated section the next time the summary/FAQ tool runs. The tool resets +# them to false after a successful write. +rerun_summary: false +rerun_faqs: false + +### Tags +skilllevels: Introductory +subjects: Libraries +armips: + - Cortex-A + - Neoverse +tools_software_languages: + - ROS 2 + - Docker + - Python + - Zenoh +operatingsystems: + - Linux + +### Cross-platform metadata only +shared_path: true +shared_between: + - automotive + - embedded-and-microcontrollers + +further_reading: + - resource: + title: ros2-device-connect example repository + link: https://github.com/odincodeshen/ros2-device-connect + type: website + - resource: + title: Device Connect repository + link: https://github.com/arm/device-connect + type: website + - resource: + title: ROS 2 documentation + link: https://docs.ros.org/en/humble/ + type: documentation + - resource: + title: Device-to-Device communication with Device Connect + link: /learning-paths/embedded-and-microcontrollers/device-connect-d2d/ + type: website + - resource: + title: Build a ROS 2 and Zenoh simulation environment on an Arm server + link: /learning-paths/cross-platform/ros2-zenoh-arm/ + type: website + +### FIXED, DO NOT MODIFY +# ================================================================================ +weight: 1 # _index.md always has weight of 1 to order correctly +layout: "learningpathall" # All files under learning paths have this same wrapper +learning_path_main_page: "yes" # This should be surfaced when looking for related content. Only set for _index.md of learning path content. +--- diff --git a/content/learning-paths/cross-platform/ros2-device-connect/_next-steps.md b/content/learning-paths/cross-platform/ros2-device-connect/_next-steps.md new file mode 100644 index 0000000000..727b395ddd --- /dev/null +++ b/content/learning-paths/cross-platform/ros2-device-connect/_next-steps.md @@ -0,0 +1,8 @@ +--- +# ================================================================================ +# FIXED, DO NOT MODIFY THIS FILE +# ================================================================================ +weight: 21 # The weight controls the order of the pages. _index.md always has weight 1. +title: "Next Steps" # Always the same, html page title. +layout: "learningpathall" # All files under learning paths have this same wrapper for Hugo processing. +--- diff --git a/content/learning-paths/cross-platform/ros2-device-connect/background.md b/content/learning-paths/cross-platform/ros2-device-connect/background.md new file mode 100644 index 0000000000..21111d8c35 --- /dev/null +++ b/content/learning-paths/cross-platform/ros2-device-connect/background.md @@ -0,0 +1,68 @@ +--- +title: Understand ROS 2, Device Connect, and the example adapter +description: Learn what ROS 2 and Device Connect each provide, and how the ros2-device-connect adapter exposes a ROS 2 container as a Device Connect device. +weight: 2 + +### FIXED, DO NOT MODIFY +layout: learningpathall +--- + +## Why connect ROS 2 to Device Connect? + +A robot or smart camera built on ROS 2 already has a rich internal graph of nodes, topics, and services. That graph is designed for components *inside* the robot to talk to each other. It isn't designed for other devices on your network, or for AI agents, to discover the robot and ask it structured questions such as "which topics are you publishing?" or "capture a photo". + +Device Connect fills that gap. In this Learning Path, you'll run a small adapter next to an unchanged ROS 2 system on an Arm-based Linux machine. The adapter registers the ROS 2 system as a Device Connect device and exposes a safe set of remote procedure calls (RPCs) that a peer or agent can discover and invoke. + +For robotics, this pattern matters because a robot is not a single API. It is a live graph of sensors, controllers, diagnostics, services, and safety-critical actions. ROS 2 remains the robot's internal software graph. Device Connect adds an external capability layer, where the device owner chooses which ROS 2 operations become discoverable, typed, and remotely callable functions. + +## ROS 2 in brief + +ROS 2 (Robot Operating System 2) is an open source middleware and toolset for building robotics applications. Applications are split into *nodes* that exchange data over *topics* (publish/subscribe), *services* (request/response), and *actions* (long-running goals). ROS 2 publishes official `arm64` packages and container images, so it runs natively on Arm platforms from a Raspberry Pi to a Neoverse cloud server. + +This Learning Path uses ROS 2 inside a Docker container and doesn't go deeper into ROS 2 itself. For more background, see: + +- [ROS 2 install guide](/install-guides/ros2/) to install ROS 2 natively on Arm Linux and run the talker and listener demo +- [Build a ROS 2 and Zenoh simulation environment on an Arm server](/learning-paths/cross-platform/ros2-zenoh-arm/) for a full containerized ROS 2 robotics workload on Arm, including the `rmw_zenoh` middleware + +## Device Connect in brief + +[Device Connect](https://github.com/arm/device-connect) is an open-source framework that standardizes how edge devices advertise themselves and exchange structured messages, so that peer devices and AI agents can discover and control them through the same driver model. The pieces you'll use are: + +- `DeviceDriver`: a Python base class you subclass to describe a device +- `@rpc` and `@emit`: decorators that expose a method as a callable function or declare an event the device publishes +- `DeviceRuntime`: the runtime that brings a driver online on the messaging network +- `device-connect-agent-tools`: a client library to discover devices and invoke their RPCs from a script or an AI agent + +Device Connect supports two deployment styles. In *device-to-device (D2D)* mode, devices find each other directly on the local network using [Zenoh](https://zenoh.io/), with no server. In *server* (or *fabric*) mode, devices connect through a shared broker so they can be reached across networks. This Learning Path uses D2D mode. + +To learn the SDK primitives in more depth, see these Learning Paths: + +- [Device-to-Device communication with Device Connect](/learning-paths/embedded-and-microcontrollers/device-connect-d2d/) for the developer model and a sensor-to-monitor example +- [Deploy multi-network device meshes using Device Connect server and NATS](/learning-paths/embedded-and-microcontrollers/device-connect-server/) for server mode + +## The ros2-device-connect example + +The [ros2-device-connect](https://github.com/odincodeshen/ros2-device-connect) repository contains the adapter you'll run. It doesn't modify or link against ROS 2. Instead, it reaches the ROS 2 graph by running ROS 2 command-line tools inside the ROS 2 container with `docker exec`: + +```output +Python client ──Zenoh (D2D)──▶ Device Connect adapter ──docker exec──▶ ROS 2 container +(agent tools) (DeviceDriver on host) (ros2 topic list, ...) +``` + +The repository is organized in three layers: + +| Layer | Files | Role | +|---|---|---| +| Shared core | `ros2_common.py` | The `docker exec` bridge plus `Ros2InspectionMixin`, which provides six read-only inspection RPCs for nodes, topics, services, packages, interfaces, and topic info | +| Hardware drivers | `puppypi_device.py`, `camera_device.py` | `DeviceDriver` subclasses that combine the shared core with hardware-specific RPCs, such as capturing a camera frame | +| Profiles and launchers | `profiles/*.env`, `start_d2d.sh`, `start_fabric.sh` | Per-deployment settings that select the driver, the ROS 2 container, and the ROS 2 setup scripts | + +The adapter is deliberately narrow. It exposes read-only inspection plus a small, reviewed set of hardware-specific RPCs. It never offers arbitrary topic publishing or service passthrough, so a remote caller can't drive the ROS 2 system in ways the driver author didn't intend. + +This means the same adapter pattern can cover different robotics surfaces. A camera profile can expose perception data, a robot profile can expose diagnostics and bounded motion commands, and both can be called through the same Device Connect discovery and RPC model. + +## What you've learned and what's next + +You've learned that ROS 2 organizes a robot's internal software as a graph of nodes and topics, and that Device Connect makes a device discoverable and callable by peers and agents. The ros2-device-connect adapter joins the two by wrapping ROS 2 command-line tools in Device Connect RPCs. + +Next, you'll install Docker, ROS 2, and the Device Connect packages on your Arm-based Linux machine. diff --git a/content/learning-paths/cross-platform/ros2-device-connect/how-it-works.md b/content/learning-paths/cross-platform/ros2-device-connect/how-it-works.md new file mode 100644 index 0000000000..17d5995bb8 --- /dev/null +++ b/content/learning-paths/cross-platform/ros2-device-connect/how-it-works.md @@ -0,0 +1,282 @@ +--- +title: Connect ROS 2 to Device Connect and call inspection RPCs +description: Learn the layered programming pattern behind the ros2-device-connect adapter, start it in D2D mode against a ROS 2 container, and call its ROS 2 inspection RPCs from a Python client. +weight: 4 + +### FIXED, DO NOT MODIFY +layout: learningpathall +--- + +## The programming pattern: four layers around one ROS 2 command + +The adapter doesn't add a new way to talk to ROS 2. It wraps the same `ros2` command you ran from your terminal in the setup section, one layer at a time. Each layer adds one thing to the layer beneath it: + +| Layer | Component | What it is | What it adds | Example | +|---|---|---|---|---| +| 1 | ROS 2 command | A `ros2` CLI call run in the container from the host | The ROS 2 query itself | `docker exec ros2_test ... ros2 topic list` | +| 2 | Python wrapper | `run_ros()` | The same command, called from Python and returning structured output | `run_ros("ros2 topic list")` | +| 3 | RPC | An `@rpc` method on `Ros2InspectionMixin` | A named function that peers and agents can discover and call over the network | `get_ros_topics()` | +| 4 | Device | A driver class run by `DeviceRuntime` | A device that bundles those RPCs and joins the network | `PuppyPiRos2Driver` | + +Layer 1 is all you need when you have a shell on the machine. Layers 2 to 4 let a caller *without* shell access run the same query, safely and by name. + +The code excerpts in this section are simplified to show the pattern. The full versions are in `ros2_common.py` and `puppypi_device.py` in the repository. + +## Layer 1: run a ROS 2 command in the container + +You've already used this layer. From the host, `docker exec` runs a `ros2` command inside the container after sourcing the ROS 2 environment: + +```bash +docker exec ros2_test bash -lc 'source /opt/ros/humble/setup.bash && ros2 topic list' +``` + +```output +/chatter +/parameter_events +/rosout +``` + +Every RPC in this Learning Path ends up running a command like this one. + +## Layer 2: wrap the command in Python + +`run_ros()` in `ros2_common.py` builds that same `docker exec` command: + +```python +def run_ros(command: str, timeout: float = 10.0) -> dict[str, Any]: + script = f"source {ROS_SETUP} && source {WORKSPACE_SETUP} && {command}" + return run(["docker", "exec", "-u", EXEC_USER, CONTAINER, "bash", "-lc", script], timeout=timeout) +``` + +The container name, user, and setup scripts come from environment variables, so the same function works with any ROS 2 container. It returns a dictionary with `ok`, `stdout`, and `stderr` instead of printed text, and a timeout stops a stalled ROS 2 command from hanging the adapter. + +## Layer 3: expose the command as an RPC + +`Ros2InspectionMixin` turns `run_ros()` calls into Device Connect RPCs. The `@rpc()` decorator is what makes a method discoverable and callable over the network: + +```python +class Ros2InspectionMixin: + @rpc() + async def get_ros_topics(self, limit: int = 200, contains: str = "") -> dict[str, Any]: + """List active ROS2 topics.""" + return lines(run_ros("ros2 topic list"), limit=limit, contains=contains) + + @rpc() + async def get_topic_info(self, topic: str) -> dict[str, Any]: + """Return read-only metadata for a specific ROS2 topic.""" + if not TOPIC_NAME_RE.fullmatch(topic): + return {"ok": False, "error": "topic must match ^/[A-Za-z0-9_/]+$"} + return run_ros(f"ros2 topic info {topic}") +``` + +This is also where you apply safety rules, because it's the boundary where remote input arrives. `get_ros_topics` caps and filters its output with the `lines()` helper. `get_topic_info` validates the topic name before it reaches a shell command. + +The mixin provides six inspection RPCs in total, for nodes, topics, services, packages, interfaces, and topic info. They all follow the same pattern. + +The table below shows how the Device Connect RPC names map back to the ROS 2 operations they run or wrap: + +| Device Connect RPC | ROS 2 operation inside the container | Purpose | +|---|---|---| +| `get_ros_nodes()` | `ros2 node list` | List active ROS 2 nodes | +| `get_ros_topics()` | `ros2 topic list` | List active ROS 2 topics | +| `get_ros_services()` | `ros2 service list` | List active ROS 2 services | +| `get_ros_packages()` | `ros2 pkg list` | List installed ROS 2 packages | +| `get_ros_interfaces()` | `ros2 interface list` | List available ROS 2 message and service interfaces | +| `get_topic_info(topic)` | `ros2 topic info ` | Inspect one topic after validating the topic name | +| `get_raw_image()` | Subscribe to `/image_raw`, then JPEG/base64 encode one frame | Expose a camera stream as a callable perception RPC | +| `run_action(action)` | Call `/puppy_control/runActionGroup` with an allowlisted action file | Run a pre-approved PuppyPi motion | +| `set_velocity(x, y, yaw_rate)` | Publish once to `/puppy_control/velocity` | Send a bounded PuppyPi velocity command | +| `stop()` | Publish a zero-velocity command | Stop PuppyPi motion | + +Device Connect does not replace ROS 2. It wraps selected ROS 2 operations as discoverable, typed, remotely callable capabilities. + +## Layer 4: build the device + +The final layer is a driver class that inherits from both `Ros2InspectionMixin` and `DeviceDriver`. The mixin supplies the shared ROS 2 RPCs. `DeviceDriver` makes the class a Device Connect device, and you add any hardware-specific RPCs alongside the shared ones. The `rpi5` profile runs this driver from `puppypi_device.py`: + +```python +class PuppyPiRos2Driver(Ros2InspectionMixin, DeviceDriver): + device_type = os.getenv("DEVICE_TYPE", "quadruped") + + @rpc() + async def echo(self, text: str) -> dict[str, str]: + """Echo text for connectivity testing.""" + return {"echo": text} + + # get_status(), run_action(), set_velocity(), stop() ... + + +async def main() -> None: + runtime = DeviceRuntime(driver=PuppyPiRos2Driver(), device_id=os.getenv("DEVICE_ID")) + await runtime.run() +``` + +`DeviceRuntime` connects the driver to the messaging network and announces every `@rpc` method, both inherited and its own, to peers. To support new hardware, you write a new class at this layer. Layers 1 to 3 stay the same. + +## Start the adapter + +The `start_d2d.sh` launcher loads a profile, activates the `.venv` environment, sets D2D defaults (the Zenoh backend, TCP port 7447, and a device ID of `-d2d`), and runs the driver script that the profile names. + +Open a terminal on your Arm-based Linux machine and start the adapter with the `rpi5` profile, pointing it at the `ros2_test` container: + +```bash +cd ~/device_connect +DEVICE_PROFILE=rpi5 PROJECT_ROOT=$HOME/device_connect \ + ROS_CONTAINER=ros2_test WORKSPACE_SETUP=/opt/ros/humble/setup.bash \ + ./ros2-device-connect/start_d2d.sh +``` + +The inline variables override the values in `profiles/rpi5.env`. You need the `WORKSPACE_SETUP` override because the `rpi5` profile defaults to a robot workspace that doesn't exist in a plain `ros:humble` container. + +The adapter stays in the foreground. Its log shows that it has joined the Zenoh network in D2D mode as `rpi5-d2d`: + +```output +WARNING - Running in INSECURE mode (DEVICE_CONNECT_ALLOW_INSECURE=true). Do NOT use this in production! +INFO - Using ZENOH messaging backend +INFO - Driver connected: raspberry_pi +INFO - Subscribed to commands on device-connect.default.rpi5-d2d.cmd +INFO - D2D mode: skipping registry registration, using presence announcements +``` + +## Call the RPCs from a client + +Open a second terminal on the same machine. In `~/device_connect`, create a file named `client.py`: + +```python +import json + +from device_connect_agent_tools import connect, discover +from device_connect_agent_tools.tools import invoke + +DEVICE = "rpi5-d2d" + +CALLS = [ + ("echo", {"text": "hello from arm"}), + ("get_status", {}), + ("get_ros_topics", {}), + ("get_ros_packages", {"contains": "std_msgs"}), + ("get_topic_info", {"topic": "/chatter"}), + ("get_topic_info", {"topic": "bad;rm"}), +] + +connect() + +found = discover("device(*)") +print("discovered:", [d["device_id"] for d in found["results"]]) + +for function, params in CALLS: + result = invoke(f"device({DEVICE}).function({function})", params=params) + print(f"--- {function}", json.dumps(result, indent=2)) +``` + +The client uses two calls from the agent tools. `discover("device(*)")` lists every device on the network, and `invoke()` calls one function on a named device using a `device().function()` selector. The client doesn't know about ROS 2 or Docker. It only sees the device and its RPCs. + +Set the client's connection settings and run it: + +```bash +cd ~/device_connect +export MESSAGING_BACKEND=zenoh +export ZENOH_CONNECT=tcp/127.0.0.1:7447 +export DEVICE_CONNECT_DISCOVERY_MODE=d2d +export DEVICE_CONNECT_ALLOW_INSECURE=true +export TENANT=default +.venv/bin/python client.py +``` + +`ZENOH_CONNECT` points the client at the adapter's Zenoh endpoint. To reach the adapter from another machine on your LAN, replace `127.0.0.1` with the adapter host's IP address. + +The output is similar to the following, shortened here for readability: + +```output +discovered: ['rpi5-d2d'] +--- echo { + "success": true, + "device_id": "rpi5-d2d", + "function": "echo", + "result": { + "echo": "hello from arm" + } +} +--- get_status { + ... + "result": { + "device": "puppypi", + "container": "ros2_test", + "container_status": "running", + "ros_ok": false, + "ros_output": [ + "ROS_DISTRO=humble" + ], + ... + } +} +--- get_ros_topics { + ... + "result": { + "ok": true, + "count": 3, + "items": [ + "/chatter", + "/parameter_events", + "/rosout" + ], + "truncated": false, + ... + } +} +--- get_ros_packages { + ... + "result": { + "ok": true, + "count": 1, + "items": [ + "std_msgs" + ], + ... + } +} +--- get_topic_info { + ... + "result": { + "ok": true, + "topic": "/chatter", + "stdout": "Type: std_msgs/msg/String\nPublisher count: 1\nSubscription count: 0\n", + "stderr": "" + } +} +--- get_topic_info { + ... + "result": { + "ok": false, + "error": "topic must match ^/[A-Za-z0-9_/]+$" + } +} +``` + +Here's what each result shows: + +- `echo` confirms the full round trip from the client over Zenoh to the adapter. +- `get_ros_topics` returns the same three topics you saw at layer 1. It's the same `ros2 topic list` command, reached through all four layers. `get_topic_info` adds the message type of `/chatter`. +- `get_ros_packages` shows the `contains` filter reducing the package list to `std_msgs`. +- The second `get_topic_info` call is rejected at layer 3, so no command runs in the container. +- `get_status` reports that the container is running ROS 2 Humble. `ros_ok` is `false` because this driver's health check looks for PuppyPi robot packages, which aren't in a plain `ros:humble` container. This is expected. + +## Clean up + +Stop the adapter with `Ctrl+C` in its terminal. If you started it in the background, stop it with: + +```bash +pkill -f puppypi_device.py +``` + +The `ros2_test` container keeps running. When you no longer need it, remove it: + +```bash +docker rm -f ros2_test +``` + +## What you've accomplished and what's next + +You've learned the adapter's four-layer pattern: a ROS 2 command run in the container, a Python wrapper around it, an `@rpc` method that exposes it, and a driver class that inherits from `Ros2InspectionMixin` and `DeviceDriver` to bundle those RPCs into a device. You started the adapter in D2D mode and called its RPCs from a Python client that knows nothing about ROS 2. + +Next, you'll see how other profiles reuse layers 1 to 3 with different layer 4 drivers for real hardware, such as a Raspberry Pi 5 with a camera. diff --git a/content/learning-paths/cross-platform/ros2-device-connect/profiles-and-hardware.md b/content/learning-paths/cross-platform/ros2-device-connect/profiles-and-hardware.md new file mode 100644 index 0000000000..d2f56b740c --- /dev/null +++ b/content/learning-paths/cross-platform/ros2-device-connect/profiles-and-hardware.md @@ -0,0 +1,142 @@ +--- +title: Use deployment profiles and try the adapter on real hardware +description: Learn how ros2-device-connect profiles select drivers for different hardware, and how to run the adapter on a Raspberry Pi 5 with a camera or on a ROS 2 robot. +weight: 5 + +### FIXED, DO NOT MODIFY +layout: learningpathall +--- + +## How profiles select the hardware configuration + +In the previous section, you overrode the `rpi5` profile to point at a generic ROS 2 container. Each profile in `profiles/` is a shell file that describes one deployment. It sets which driver runs, which container the driver talks to, and which ROS 2 setup scripts it sources. + +| Profile | Driver | Default container | ROS 2 distribution | RPCs added to the shared inspection RPCs | +|---|---|---|---|---| +| `rpi5` | `puppypi_device.py` | `test` | Humble | The PuppyPi RPCs, which have no effect without a robot, so this profile works as a general test target | +| `rpi_camera` | `camera_device.py` | `pi_ros` | Jazzy | `get_raw_image(quality)` | +| `puppypi` | `puppypi_device.py` | `puppypi_ros2` | Humble | `run_action(action)`, `set_velocity(x, y, yaw_rate)`, `stop()` | + +For example, `profiles/rpi_camera.env` contains: + +```bash +export DEVICE_PROFILE="rpi_camera" +export PROJECT_ROOT="${PROJECT_ROOT:-${HOME}/device_connect}" +export VENV_DIR="${VENV_DIR:-.venv}" +export DEVICE_TYPE="${DEVICE_TYPE:-sentinel_camera}" +export DRIVER_SCRIPT="${DRIVER_SCRIPT:-camera_device.py}" +export ROS_CONTAINER="${ROS_CONTAINER:-pi_ros}" +export ROS_EXEC_USER="${ROS_EXEC_USER:-root}" +export ROS_SETUP="${ROS_SETUP:-/opt/ros/jazzy/setup.bash}" +export WORKSPACE_SETUP="${WORKSPACE_SETUP:-${ROS_SETUP}}" +``` + +Every value uses the `${VAR:-default}` form, so you can override any setting inline, as you did with `ROS_CONTAINER` and `WORKSPACE_SETUP`. For a new deployment, you can also point the launcher at your own profile file: + +```bash +PROFILE_FILE=/path/to/my-robot.env ./ros2-device-connect/start_d2d.sh +``` + +## Try a Raspberry Pi 5 with a camera + +The `rpi_camera` configuration is the lowest-risk real hardware example in this Learning Path. It exposes a sensor capability, not a motion capability: a ROS 2 image topic becomes a Device Connect RPC that any peer or agent can call for a photo. The caller does not need to know the ROS 2 topic name, QoS settings, camera container, or image encoding details. + +The camera must appear as a V4L2 video device, such as `/dev/video0`. A USB webcam works without extra setup. + +{{% notice Note %}} +Raspberry Pi Camera Modules connected by ribbon cable use the libcamera stack, and they don't always expose a frame-ready `/dev/video0` that `v4l2_camera` can read. If `v4l2_camera` can't read frames from your Pi Camera Module, start with a USB webcam, or set `VIDEO_DEVICE` to the video node your camera stack provides. +{{% /notice %}} + +On the Raspberry Pi 5, set up the same `~/device_connect` workspace as in the setup section, then bring up the camera stack: + +```bash +cd ~/device_connect +./ros2-device-connect/start_camera_ros2.sh +``` + +The `start_camera_ros2.sh` script is idempotent and does the following: + +- creates a `ros:jazzy` container named `pi_ros` with the camera passed through using `--device=/dev/video0` +- installs `ros-jazzy-v4l2-camera`, `ros-jazzy-cv-bridge`, and `python3-opencv` inside the container +- copies `capture_frame.py` into the container and starts `v4l2_camera_node` in the background +- verifies that the `/image_raw` topic is being published + +Start the adapter with the camera profile: + +```bash +cd ~/device_connect +DEVICE_PROFILE=rpi_camera ./ros2-device-connect/start_d2d.sh +``` + +The device is announced as `rpi_camera-d2d`. It exposes the same inspection RPCs as before, plus `get_raw_image`: + +```python +@rpc(labels={"category": "camera", "direction": "read", "safety": "informational"}) +async def get_raw_image(self, quality: int = DEFAULT_JPEG_QUALITY) -> dict[str, Any]: + ... + result = run_ros( + f"python3 {CAPTURE_SCRIPT_PATH} --topic {CAMERA_TOPIC} " + f"--timeout {CAPTURE_TIMEOUT_S} --quality {quality_int}", + timeout=CAPTURE_EXEC_TIMEOUT_S, + ) +``` + +The RPC runs `capture_frame.py` inside the container. The script subscribes to `/image_raw`, takes one frame, and returns it as a base64-encoded JPEG. It subscribes with ROS 2's `qos_profile_sensor_data` because `v4l2_camera` publishes with best-effort QoS. A subscriber that uses the default reliable QoS would never match the publisher, and the capture would silently time out. + +This is the perception side of the robotics pattern: a ROS 2 sensor stream is converted into a typed Device Connect capability. The PuppyPi profile below uses the same pattern for the action side of robotics. + +From a client, call the RPC and save the image. Use the same client environment variables as in the previous section, replacing `127.0.0.1` with the Raspberry Pi's IP address if you run the client on another machine: + +```python +import base64 +from device_connect_agent_tools import connect +from device_connect_agent_tools.tools import invoke + +connect() +reply = invoke("device(rpi_camera-d2d).function(get_raw_image)", params={"quality": 80}) +with open("capture.jpg", "wb") as f: + f.write(base64.b64decode(reply["result"]["jpeg_base64"])) +``` + +The repository's `view_image.py` script does the same thing against a device in fabric mode. + +## Try a ROS 2 robot + +The `puppypi` profile targets a [Hiwonder PuppyPi quadruped](https://www.hiwonder.com/products/puppypi), a small four-legged robot built around Raspberry Pi-class hardware and a ROS 2 control stack. In this Learning Path, PuppyPi serves as the real robot example: the same Device Connect adapter pattern used for camera perception is extended to selected locomotion capabilities. + +It shows how you can add motion control safely: + +- `run_action` accepts only a fixed allowlist of pre-recorded moves, such as `sit`, `stand`, and `wave`, and rejects any other value. +- `set_velocity` rejects out-of-range values instead of clamping them. +- `stop` publishes a zero-velocity command. + +Robot bring-up includes starting `puppy_control` and enabling servo torque. For those steps and the full safety model, see the [ros2-device-connect README](https://github.com/odincodeshen/ros2-device-connect#startup-checklist). + +The important design point is that the adapter does not expose arbitrary ROS 2 control. It turns a reviewed subset of the robot's ROS 2 graph into Device Connect capabilities: read-only diagnostics, allowlisted actions, bounded velocity, and an explicit stop command. + +To see this profile running on a real PuppyPi, watch the [ROS 2 and Device Connect PuppyPi demo](https://www.youtube.com/watch?v=R4DJ2_jKvZQ). + +## Build a profile for your own ROS 2 system + +To connect a different ROS 2 system, follow the same pattern: + +1. Create a driver class that combines `Ros2InspectionMixin` with `DeviceDriver`, and add only the hardware-specific RPCs you've reviewed. +2. Validate every caller-supplied value before it reaches `run_ros()`, as `get_topic_info` and `get_raw_image` do. +3. Add a `profiles/.env` file that sets `DRIVER_SCRIPT`, `ROS_CONTAINER`, `ROS_EXEC_USER`, `ROS_SETUP`, and `WORKSPACE_SETUP`. + +When the device needs to be reachable beyond your local network, run `start_fabric.sh` instead of `start_d2d.sh` with a device credentials file. This connects the device through a Device Connect server rather than Zenoh D2D discovery. For more information, see [Deploy multi-network device meshes using Device Connect server and NATS](/learning-paths/embedded-and-microcontrollers/device-connect-server/). + +{{% notice Warning %}} +D2D mode runs with `DEVICE_CONNECT_ALLOW_INSECURE=true` and no transport authentication. Anyone on the same network segment can discover the device and call its RPCs. Use D2D mode only on a trusted network, and add authentication before you expose motion-control RPCs such as `set_velocity` on a less trusted network. +{{% /notice %}} + +## What you've learned + +In this Learning Path, you: + +- learned how the ros2-device-connect adapter makes an unchanged ROS 2 system discoverable and callable through Device Connect, by wrapping ROS 2 CLI calls in `@rpc` methods run with `docker exec` +- set up a ROS 2 Humble container and the Device Connect Python packages on an Arm-based Linux machine +- ran the adapter in D2D mode and called read-only inspection RPCs from a Python client, including one call rejected by input validation +- saw how profiles reuse the same shared core for a Raspberry Pi 5 camera and a ROS 2 robot, and how to add a profile for your own hardware + +To go further, try building a larger ROS 2 workload on Arm with [Build a ROS 2 and Zenoh simulation environment on an Arm server](/learning-paths/cross-platform/ros2-zenoh-arm/). diff --git a/content/learning-paths/cross-platform/ros2-device-connect/setup.md b/content/learning-paths/cross-platform/ros2-device-connect/setup.md new file mode 100644 index 0000000000..f38e8dfb28 --- /dev/null +++ b/content/learning-paths/cross-platform/ros2-device-connect/setup.md @@ -0,0 +1,130 @@ +--- +title: Set up ROS 2 and Device Connect on Arm Linux +description: Install Docker, start a ROS 2 Humble container with a demo publisher, and install the Device Connect Python packages and the ros2-device-connect adapter. +weight: 3 + +### FIXED, DO NOT MODIFY +layout: learningpathall +--- + +## Before you begin + +Run every command in this section on your Arm-based Linux machine. The instructions assume Ubuntu 22.04 or later on an `aarch64` host, such as a Raspberry Pi 5 or an Arm cloud instance. Confirm the architecture: + +```bash +uname -m +``` + +```output +aarch64 +``` + +By the end of this section, you'll have: + +- a ROS 2 Humble container publishing a demo topic +- a Python virtual environment with the Device Connect packages +- the ros2-device-connect adapter cloned and ready to run + +## Install Docker + +The adapter talks to ROS 2 through Docker, so you need Docker Engine. Follow the [Docker Engine install guide](/install-guides/docker/docker-engine/), including the step that adds your user to the `docker` group. The adapter runs `docker exec` as your user, so Docker must work without `sudo`. + +Verify that Docker is installed and that you can connect to the Docker daemon without `sudo`: + +```bash +docker version +``` + +The command should display version information for both the Docker client and server. + +## Start a ROS 2 container + +You don't need to install ROS 2 on the host. The official `ros:humble` image is multi-architecture, so Docker pulls the `arm64` variant automatically. If you'd prefer a native install, see the [ROS 2 install guide](/install-guides/ros2/). + +Start a long-running container named `ros2_test`: + +```bash +docker run -d --name ros2_test ros:humble tail -f /dev/null +``` + +The `tail -f /dev/null` command keeps the container alive so you can run ROS 2 commands inside it with `docker exec`. Start a demo publisher that sends a `std_msgs/msg/String` message on the `/chatter` topic once per second: + +```bash +docker exec -d ros2_test bash -lc 'source /opt/ros/humble/setup.bash && ros2 topic pub -r 1 /chatter std_msgs/msg/String "{data: hello}"' +``` + +Check that the topic is being published: + +```bash +docker exec ros2_test bash -lc 'source /opt/ros/humble/setup.bash && ros2 node list && ros2 topic list' +``` + +The output lists the ROS 2 topics: + +```output +/chatter +/parameter_events +/rosout +``` + +The node list is empty because `ros2 topic pub` runs as a hidden node. This container now stands in for a real robot's ROS 2 stack. + +## Create the workspace + +The adapter's launch scripts expect a single project directory that holds the adapter repository and a Python virtual environment named `.venv`. Create that directory and clone both the adapter and the Device Connect source. The adapter is cloned at the `ros2_dc_lp` tag, which is the version this Learning Path was tested with: + +```bash +mkdir -p ~/device_connect +cd ~/device_connect +git clone --branch ros2_dc_lp https://github.com/odincodeshen/ros2-device-connect.git +git clone https://github.com/arm/device-connect.git +``` + +## Install uv and the Device Connect packages + +This Learning Path uses [uv](https://docs.astral.sh/uv/) to create the Python environment. Install it: + +```bash +curl -LsSf https://astral.sh/uv/install.sh | sh +export PATH="$HOME/.local/bin:$PATH" +uv --version +``` + +Create a Python virtual environment in the project directory, and install the Device Connect edge SDK and agent tools from the cloned source. The Device Connect packages require Python 3.11 or later and are tested on Python 3.11, 3.12, and 3.13. This example uses 3.12, but you can pass any supported version to `--python`. If that version isn't installed on your machine, uv downloads it for you. + +This host Python environment is separate from the Python version inside the ROS 2 container, so your choice here doesn't need to match your ROS 2 distribution. + +```bash +cd ~/device_connect +uv venv --python 3.12 .venv +VIRTUAL_ENV=.venv uv pip install \ + -e device-connect/packages/device-connect-edge \ + -e device-connect/packages/device-connect-agent-tools +``` + +The `device-connect-edge` package is the device runtime that the adapter runs on. The `device-connect-agent-tools` package is the client you'll use to discover the adapter and call its RPCs. + +Verify that both packages import: + +```bash +.venv/bin/python -c "import device_connect_edge, device_connect_agent_tools; print('Device Connect OK')" +``` + +```output +Device Connect OK +``` + +Your workspace now looks like this: + +```output +~/device_connect/ +├── .venv/ # Python environment with Device Connect +├── device-connect/ # Device Connect SDK source +└── ros2-device-connect/ # ROS 2 adapter +``` + +## What you've accomplished and what's next + +You've installed Docker, started a ROS 2 Humble container publishing on `/chatter`, and created a Python environment with the Device Connect packages next to the ros2-device-connect adapter. + +Next, you'll look at how the adapter code works, start it in D2D mode, and query the ROS 2 container through Device Connect. diff --git a/content/learning-paths/embedded-and-microcontrollers/_index.md b/content/learning-paths/embedded-and-microcontrollers/_index.md index 323b576e3e..9d3f43f7cf 100644 --- a/content/learning-paths/embedded-and-microcontrollers/_index.md +++ b/content/learning-paths/embedded-and-microcontrollers/_index.md @@ -15,19 +15,19 @@ pinned_learning_paths: operatingsystems_filter: - Android: 1 - Baremetal: 31 -- Linux: 60 +- Linux: 62 - macOS: 24 -- RTOS: 13 +- RTOS: 14 - Windows: 12 subjects_filter: - CI-CD: 7 - Containers and Virtualization: 10 - Embedded Linux: 6 -- Libraries: 5 +- Libraries: 6 - ML: 31 - Performance and Architecture: 24 - RTOS Fundamentals: 8 -- Security: 3 +- Security: 4 - Virtual Hardware: 2 subtitle: Learn best practices for IoT, embedded, and microcontroller development. title: Embedded and Microcontrollers @@ -53,7 +53,7 @@ tools_software_languages_filter: - Baremetal: 1 - Bash: 1 - BitBake: 1 -- C: 12 +- C: 13 - ChatGPT: 1 - Clang: 1 - CMake: 2 @@ -66,7 +66,7 @@ tools_software_languages_filter: - Containerd: 1 - CPP: 1 - DetectNet: 1 -- Docker: 19 +- Docker: 20 - DSTREAM: 2 - Edge AI: 2 - Edge Impulse: 2 @@ -77,7 +77,7 @@ tools_software_languages_filter: - Fusion 360: 1 - FVP: 11 - Gazebo: 1 -- GCC: 16 +- GCC: 17 - Generative AI: 3 - GitHub: 4 - GitLab: 2 @@ -113,19 +113,20 @@ tools_software_languages_filter: - ONNX: 1 - ONNX Runtime: 1 - OpenSSH: 1 +- OpenSSL: 1 - Paddle: 1 - Performance analysis: 1 - picocom: 1 - Porcupine: 1 -- Python: 24 +- Python: 25 - PyTorch: 9 -- QEMU: 2 +- QEMU: 3 - Raspberry Pi: 11 - Reachy Mini: 1 - Remote.It: 1 - remoteproc-runtime: 1 - rmw_zenoh: 2 -- ROS 2: 3 +- ROS 2: 4 - Runbook: 4 - RViz: 1 - SEGGER JLink: 1 @@ -144,14 +145,16 @@ tools_software_languages_filter: - Trusted Firmware: 2 - TrustZone: 2 - TVMC: 1 +- U-Boot: 1 - vcpkg: 1 - Vela: 2 - VGF: 1 -- Visual Studio Code: 2 +- Visual Studio Code: 3 +- Workbench for Zephyr: 1 - YAML: 1 - Yocto: 1 - Yocto Project: 1 -- Zenoh: 2 -- Zephyr: 6 +- Zenoh: 3 +- Zephyr: 7 weight: 5 --- diff --git a/content/learning-paths/embedded-and-microcontrollers/img_nn_stcube/build_nn.md b/content/learning-paths/embedded-and-microcontrollers/img_nn_stcube/build_nn.md index 4bb4d2bdfb..390f9818fc 100644 --- a/content/learning-paths/embedded-and-microcontrollers/img_nn_stcube/build_nn.md +++ b/content/learning-paths/embedded-and-microcontrollers/img_nn_stcube/build_nn.md @@ -92,7 +92,7 @@ plt.show() The expected output is shown below -![output1](images/lab4_1.PNG) +![output1](images/lab4_1.png) Next, normalize all the training and testing data to have values between 0 and 1. This normalization facilitates machine learning. Each RGB value ranges from 0 to 255, so divide the training and testing data by 255. @@ -125,7 +125,7 @@ You are going to create a small convolutional neural network for image classific Here is an image illustrating the network architecture. Note that only convolution and dense layers are illustrated in this image. -![output2](images/lab4_2.PNG) +![output2](images/lab4_2.png) Execute the code blocks below to create a sequential model and add the layers diff --git a/content/learning-paths/embedded-and-microcontrollers/img_nn_stcube/deploy_nn.md b/content/learning-paths/embedded-and-microcontrollers/img_nn_stcube/deploy_nn.md index d1131ba245..ffa2fa35de 100644 --- a/content/learning-paths/embedded-and-microcontrollers/img_nn_stcube/deploy_nn.md +++ b/content/learning-paths/embedded-and-microcontrollers/img_nn_stcube/deploy_nn.md @@ -53,15 +53,15 @@ In this section, you will deploy the model directly on the STM32 board. 5. Set `Toolchain/IDE` as `STM32CubeIDE` -![output3](images/lab4_3.PNG) +![output3](images/lab4_3.png) 6. Go to `Pinout & Configuration` and clear pinouts from the `Pinout` menu. -![output4](images/lab4_4.PNG) +![output4](images/lab4_4.png) 7. In `Software Packs` menu, click `Select Components`. Enable `X-CUBE-AI`. For device application, choose `Validation`. Click `OK` to save. -![output5](images/lab4_5.PNG) +![output5](images/lab4_5.png) 8. Navigate to `X-CUBE-AI` configuration. @@ -71,7 +71,7 @@ In this section, you will deploy the model directly on the STM32 board. 11. Generate the validation code for the model by clicking `Generate Code`. -![output6](images/lab4_6.PNG) +![output6](images/lab4_6.png) 12. Open STM32CubeIDE. @@ -81,10 +81,10 @@ In this section, you will deploy the model directly on the STM32 board. 15. Ensure that the board is connected to your computer. If it is correctly connected, build and flash the code by clicking `Run As`. -![output7](images/lab4_7.PNG) +![output7](images/lab4_7.png) 16. If you get an ‘undefined reference’ error, go to `Core/Src/main.c`. Remove `static` from the declaration of the `MX_USART1_UART_Init()` function and also from its definition. Try `Run As` again. -![output8](images/lab4_8.PNG) +![output8](images/lab4_8.png) With the model now deployed on the STM32 board, you are ready to test it. diff --git a/content/learning-paths/embedded-and-microcontrollers/img_nn_stcube/images/lab4_1.PNG b/content/learning-paths/embedded-and-microcontrollers/img_nn_stcube/images/lab4_1.png similarity index 100% rename from content/learning-paths/embedded-and-microcontrollers/img_nn_stcube/images/lab4_1.PNG rename to content/learning-paths/embedded-and-microcontrollers/img_nn_stcube/images/lab4_1.png diff --git a/content/learning-paths/embedded-and-microcontrollers/img_nn_stcube/images/lab4_10.PNG b/content/learning-paths/embedded-and-microcontrollers/img_nn_stcube/images/lab4_10.png similarity index 100% rename from content/learning-paths/embedded-and-microcontrollers/img_nn_stcube/images/lab4_10.PNG rename to content/learning-paths/embedded-and-microcontrollers/img_nn_stcube/images/lab4_10.png diff --git a/content/learning-paths/embedded-and-microcontrollers/img_nn_stcube/images/lab4_11.PNG b/content/learning-paths/embedded-and-microcontrollers/img_nn_stcube/images/lab4_11.png similarity index 100% rename from content/learning-paths/embedded-and-microcontrollers/img_nn_stcube/images/lab4_11.PNG rename to content/learning-paths/embedded-and-microcontrollers/img_nn_stcube/images/lab4_11.png diff --git a/content/learning-paths/embedded-and-microcontrollers/img_nn_stcube/images/lab4_12.PNG b/content/learning-paths/embedded-and-microcontrollers/img_nn_stcube/images/lab4_12.png similarity index 100% rename from content/learning-paths/embedded-and-microcontrollers/img_nn_stcube/images/lab4_12.PNG rename to content/learning-paths/embedded-and-microcontrollers/img_nn_stcube/images/lab4_12.png diff --git a/content/learning-paths/embedded-and-microcontrollers/img_nn_stcube/images/lab4_2.PNG b/content/learning-paths/embedded-and-microcontrollers/img_nn_stcube/images/lab4_2.png similarity index 100% rename from content/learning-paths/embedded-and-microcontrollers/img_nn_stcube/images/lab4_2.PNG rename to content/learning-paths/embedded-and-microcontrollers/img_nn_stcube/images/lab4_2.png diff --git a/content/learning-paths/embedded-and-microcontrollers/img_nn_stcube/images/lab4_3.PNG b/content/learning-paths/embedded-and-microcontrollers/img_nn_stcube/images/lab4_3.png similarity index 100% rename from content/learning-paths/embedded-and-microcontrollers/img_nn_stcube/images/lab4_3.PNG rename to content/learning-paths/embedded-and-microcontrollers/img_nn_stcube/images/lab4_3.png diff --git a/content/learning-paths/embedded-and-microcontrollers/img_nn_stcube/images/lab4_4.PNG b/content/learning-paths/embedded-and-microcontrollers/img_nn_stcube/images/lab4_4.png similarity index 100% rename from content/learning-paths/embedded-and-microcontrollers/img_nn_stcube/images/lab4_4.PNG rename to content/learning-paths/embedded-and-microcontrollers/img_nn_stcube/images/lab4_4.png diff --git a/content/learning-paths/embedded-and-microcontrollers/img_nn_stcube/images/lab4_5.PNG b/content/learning-paths/embedded-and-microcontrollers/img_nn_stcube/images/lab4_5.png similarity index 100% rename from content/learning-paths/embedded-and-microcontrollers/img_nn_stcube/images/lab4_5.PNG rename to content/learning-paths/embedded-and-microcontrollers/img_nn_stcube/images/lab4_5.png diff --git a/content/learning-paths/embedded-and-microcontrollers/img_nn_stcube/images/lab4_6.PNG b/content/learning-paths/embedded-and-microcontrollers/img_nn_stcube/images/lab4_6.png similarity index 100% rename from content/learning-paths/embedded-and-microcontrollers/img_nn_stcube/images/lab4_6.PNG rename to content/learning-paths/embedded-and-microcontrollers/img_nn_stcube/images/lab4_6.png diff --git a/content/learning-paths/embedded-and-microcontrollers/img_nn_stcube/images/lab4_7.PNG b/content/learning-paths/embedded-and-microcontrollers/img_nn_stcube/images/lab4_7.png similarity index 100% rename from content/learning-paths/embedded-and-microcontrollers/img_nn_stcube/images/lab4_7.PNG rename to content/learning-paths/embedded-and-microcontrollers/img_nn_stcube/images/lab4_7.png diff --git a/content/learning-paths/embedded-and-microcontrollers/img_nn_stcube/images/lab4_8.PNG b/content/learning-paths/embedded-and-microcontrollers/img_nn_stcube/images/lab4_8.png similarity index 100% rename from content/learning-paths/embedded-and-microcontrollers/img_nn_stcube/images/lab4_8.PNG rename to content/learning-paths/embedded-and-microcontrollers/img_nn_stcube/images/lab4_8.png diff --git a/content/learning-paths/embedded-and-microcontrollers/img_nn_stcube/images/lab4_9.PNG b/content/learning-paths/embedded-and-microcontrollers/img_nn_stcube/images/lab4_9.png similarity index 100% rename from content/learning-paths/embedded-and-microcontrollers/img_nn_stcube/images/lab4_9.PNG rename to content/learning-paths/embedded-and-microcontrollers/img_nn_stcube/images/lab4_9.png diff --git a/content/learning-paths/embedded-and-microcontrollers/img_nn_stcube/run_nn.md b/content/learning-paths/embedded-and-microcontrollers/img_nn_stcube/run_nn.md index cec4f2ef55..4a73da5d5f 100644 --- a/content/learning-paths/embedded-and-microcontrollers/img_nn_stcube/run_nn.md +++ b/content/learning-paths/embedded-and-microcontrollers/img_nn_stcube/run_nn.md @@ -38,20 +38,20 @@ If the board is not detected, click the black button on the board to reset, then Select the model network from the list of models deployed on the board. -![output9](images/lab4_9.PNG) +![output9](images/lab4_9.png) Select the network and the label file (`Data/labels/cifar10_labels.txt`) -![output10](images/lab4_10.PNG) +![output10](images/lab4_10.png) Open an image to test. The tool will automatically launch a new pane, and show the inference result. Observe that the model correctly predicted the label. In addition, note the time taken to finish the prediction. -![output11](images/lab4_11.PNG) +![output11](images/lab4_11.png) You can also use your workstation camera to test image classification. Hold an appropriate picture up to your camera, then press `S`. The tool captures the image and sends it to the board. -![output12](images/lab4_12.PNG) +![output12](images/lab4_12.png) You have now successfully ran the model on your STM32 board. diff --git a/content/learning-paths/embedded-and-microcontrollers/jetson_object_detection/2setup.md b/content/learning-paths/embedded-and-microcontrollers/jetson_object_detection/2setup.md index d9980ff787..7b45eff1ff 100644 --- a/content/learning-paths/embedded-and-microcontrollers/jetson_object_detection/2setup.md +++ b/content/learning-paths/embedded-and-microcontrollers/jetson_object_detection/2setup.md @@ -23,7 +23,7 @@ Click on the box titled "JETSON XAVIER NX DEVELOPER KIT & ORIN NANO DEVELOPER KI 1. Open balenaEtcher 2. Click "Flash from file" -![balenaEtcher interface](./balenaEtcher1.png) +![balenaEtcher interface](./balenaetcher1.png) 3. Select the zip file of the image you just downloaded (you don't need to unzip the file). 4. Click "Select target" and choose your microSD card 5. Click "Flash" and wait for the process to complete which will take around 10 minutes. You may be prompted to enter a username and password before it will start. diff --git a/content/learning-paths/embedded-and-microcontrollers/jetson_object_detection/balenaEtcher1.png b/content/learning-paths/embedded-and-microcontrollers/jetson_object_detection/balenaetcher1.png similarity index 100% rename from content/learning-paths/embedded-and-microcontrollers/jetson_object_detection/balenaEtcher1.png rename to content/learning-paths/embedded-and-microcontrollers/jetson_object_detection/balenaetcher1.png diff --git a/content/learning-paths/embedded-and-microcontrollers/streamline-kernel-module/4_sl_profile_oot.md b/content/learning-paths/embedded-and-microcontrollers/streamline-kernel-module/4_sl_profile_oot.md index 54afad166d..613321e8aa 100644 --- a/content/learning-paths/embedded-and-microcontrollers/streamline-kernel-module/4_sl_profile_oot.md +++ b/content/learning-paths/embedded-and-microcontrollers/streamline-kernel-module/4_sl_profile_oot.md @@ -86,7 +86,7 @@ Open the **Functions** tab. In the counters list, select one of the counters you In the **Functions** tab, look for the function `char_dev_cache_traverse()`. You'll see that it has the highest L1 Cache refill rate, which is expected for this example. Check the **Image** column on the right. This should show your module file name, `mychardrv.ko`. This confirms that Streamline is capturing performance data for your kernel module. -![Streamline Functions tab displaying a list of functions with performance metrics such as L1 Cache refill rates. The primary subject is the function char_dev_cache_traverse which is highlighted and shows the highest cache refill value. The right side of the table lists the image name as mychardrv.ko. The wider environment is a desktop profiling application window with columns labeled Function, Image, and various performance counters. Visible text includes function names, image names, and numerical metric values. The emotional tone is neutral and technical, supporting detailed analysis for Arm kernel module profiling. alt-text#center](./images/img08_Functions_Tab.png "Identify functions with highest cache refill rates") +![Streamline Functions tab displaying a list of functions with performance metrics such as L1 Cache refill rates. The primary subject is the function char_dev_cache_traverse which is highlighted and shows the highest cache refill value. The right side of the table lists the image name as mychardrv.ko. The wider environment is a desktop profiling application window with columns labeled Function, Image, and various performance counters. Visible text includes function names, image names, and numerical metric values. The emotional tone is neutral and technical, supporting detailed analysis for Arm kernel module profiling. alt-text#center](./images/img08_functions_tab.png "Identify functions with highest cache refill rates") To view the call path for `char_dev_cache_traverse()`, right-click the function name and select **Select in Call Paths**. diff --git a/content/learning-paths/embedded-and-microcontrollers/streamline-kernel-module/images/img08_Functions_Tab.png b/content/learning-paths/embedded-and-microcontrollers/streamline-kernel-module/images/img08_functions_tab.png similarity index 100% rename from content/learning-paths/embedded-and-microcontrollers/streamline-kernel-module/images/img08_Functions_Tab.png rename to content/learning-paths/embedded-and-microcontrollers/streamline-kernel-module/images/img08_functions_tab.png diff --git a/content/learning-paths/embedded-and-microcontrollers/tflow_nn_stcube/features.md b/content/learning-paths/embedded-and-microcontrollers/tflow_nn_stcube/features.md index a558fb2e34..4bbe133a6e 100644 --- a/content/learning-paths/embedded-and-microcontrollers/tflow_nn_stcube/features.md +++ b/content/learning-paths/embedded-and-microcontrollers/tflow_nn_stcube/features.md @@ -55,7 +55,7 @@ for idx, file in enumerate(data_files): You can check the extracted features with this code block. These are the extracted features from one data sample. Expected output shown below: -![output5](images/output5.PNG) +![output5](images/output5.png) ## Feature based model diff --git a/content/learning-paths/embedded-and-microcontrollers/tflow_nn_stcube/images/output1.PNG b/content/learning-paths/embedded-and-microcontrollers/tflow_nn_stcube/images/output1.png similarity index 100% rename from content/learning-paths/embedded-and-microcontrollers/tflow_nn_stcube/images/output1.PNG rename to content/learning-paths/embedded-and-microcontrollers/tflow_nn_stcube/images/output1.png diff --git a/content/learning-paths/embedded-and-microcontrollers/tflow_nn_stcube/images/output2.PNG b/content/learning-paths/embedded-and-microcontrollers/tflow_nn_stcube/images/output2.png similarity index 100% rename from content/learning-paths/embedded-and-microcontrollers/tflow_nn_stcube/images/output2.PNG rename to content/learning-paths/embedded-and-microcontrollers/tflow_nn_stcube/images/output2.png diff --git a/content/learning-paths/embedded-and-microcontrollers/tflow_nn_stcube/images/output3.PNG b/content/learning-paths/embedded-and-microcontrollers/tflow_nn_stcube/images/output3.png similarity index 100% rename from content/learning-paths/embedded-and-microcontrollers/tflow_nn_stcube/images/output3.PNG rename to content/learning-paths/embedded-and-microcontrollers/tflow_nn_stcube/images/output3.png diff --git a/content/learning-paths/embedded-and-microcontrollers/tflow_nn_stcube/images/output4.PNG b/content/learning-paths/embedded-and-microcontrollers/tflow_nn_stcube/images/output4.png similarity index 100% rename from content/learning-paths/embedded-and-microcontrollers/tflow_nn_stcube/images/output4.PNG rename to content/learning-paths/embedded-and-microcontrollers/tflow_nn_stcube/images/output4.png diff --git a/content/learning-paths/embedded-and-microcontrollers/tflow_nn_stcube/images/output5.PNG b/content/learning-paths/embedded-and-microcontrollers/tflow_nn_stcube/images/output5.png similarity index 100% rename from content/learning-paths/embedded-and-microcontrollers/tflow_nn_stcube/images/output5.PNG rename to content/learning-paths/embedded-and-microcontrollers/tflow_nn_stcube/images/output5.png diff --git a/content/learning-paths/embedded-and-microcontrollers/tflow_nn_stcube/test.md b/content/learning-paths/embedded-and-microcontrollers/tflow_nn_stcube/test.md index 0aab5f115d..4472fff631 100644 --- a/content/learning-paths/embedded-and-microcontrollers/tflow_nn_stcube/test.md +++ b/content/learning-paths/embedded-and-microcontrollers/tflow_nn_stcube/test.md @@ -78,7 +78,7 @@ plt.legend(loc='lower right') ``` In this example, see that the training and validation accuracy start to converge after around 150 epochs. This means that the 200 epochs are enough to train the model. If you train the model for too many epochs, then the validation accuracy may drop due to overfitting. If you experience this, re-run [training](#train) with an appropriate epoch value. -![output2](images/output2.PNG) +![output2](images/output2.png) ## Investigate learning rate (optional) @@ -103,7 +103,7 @@ plt.legend(loc='lower right') ``` Expected output shown below: -![output3](images/output3.PNG) +![output3](images/output3.png) Now try a lower learning rate, which is 0.0001. Execute the code block. The graph shows the training and validation loss values decrease much more slowly. So, it is important to use a proper learning rate in training. @@ -125,7 +125,7 @@ plt.legend(loc='lower right') Expected output shown below: -![output4](images/output4.PNG) +![output4](images/output4.png) With the model trained, you are now ready to test it. diff --git a/content/learning-paths/embedded-and-microcontrollers/tflow_nn_stcube/train_nn.md b/content/learning-paths/embedded-and-microcontrollers/tflow_nn_stcube/train_nn.md index 5b4fb1b521..4017d26cb3 100644 --- a/content/learning-paths/embedded-and-microcontrollers/tflow_nn_stcube/train_nn.md +++ b/content/learning-paths/embedded-and-microcontrollers/tflow_nn_stcube/train_nn.md @@ -163,4 +163,4 @@ plot_single_sample(data_sample=data[idx], label=labels[idx]) Example output is shown below: -![output1](images/output1.PNG) +![output1](images/output1.png) diff --git a/content/learning-paths/embedded-and-microcontrollers/uv_debug/2_basics.md b/content/learning-paths/embedded-and-microcontrollers/uv_debug/2_basics.md index 09763f606d..3a4a9e5ae2 100644 --- a/content/learning-paths/embedded-and-microcontrollers/uv_debug/2_basics.md +++ b/content/learning-paths/embedded-and-microcontrollers/uv_debug/2_basics.md @@ -22,12 +22,12 @@ To use hardware breakpoints in µVision follow the steps outlined below: 1. ![Run](./b_uv4_run.png) **Run (F5)** the application. 1. Bring `Blinky.c` in focus by clicking on its tab. If it is not visible, double-click on it in the **Project** window. 3. In `Blinky.c`, scroll down to the `while (1)` loop in the `main` function near line 42 as shown here: -![Inside the while loop](./mainWhileLoop.png) +![Inside the while loop](./mainwhileloop.png) 4. Note the darker grey blocks on the left of the line numbers. They indicate assembly code being present and that you can set a hardware breakpoint on these lines. You can also see these blocks in the **Disassembly** window: -![while loop Disassembly](./mainWhileLoopDisassembly.png) +![while loop Disassembly](./mainwhileloopdisassembly.png) 5. While the program is still running, click to the left of a suitable line in the `while` loop (for example at line 47) and a red circle will be created. This is a hardware breakpoint. The simulated Arm Cortex-M55 has eight hardware breakpoints. μVision will warn you if you exceed this limit. 6. The program will soon stop at the line where you have set the breakpoint on as shown below. The yellow arrow is the current program counter position. This will be the next instruction executed. The cyan arrow is a placeholder you can use to explore the relationship between a source window and the **Disassembly** window. -![Program execution stopped](./mainWhileLoopStopped.png) +![Program execution stopped](./mainwhileloopstopped.png) 7. Add another breakpoint in `while (1)` (for example at line 50). 8. Each time you click on ![Run](./b_uv4_run.png) **Run (F5)**, the program will cycle to the next breakpoint. @@ -42,7 +42,7 @@ To use hardware breakpoints in µVision follow the steps outlined below: ### Manage Breakpoints 1. Go to **Debug - Breakpoints (Ctrl-B)** to manage breakpoints: -![Manage Breakpoints](./manageBKPT.png) +![Manage Breakpoints](./managebkpt.png) 2. You can temporarily unselect, delete or create breakpoints in this window. It is easier to create a breakpoint by clicking in a source file or the **Disassembly** window. 3. [Watchpoints](#watchpoints) are also created in this window. 4. Select **Kill All** to remove all breakpoints. @@ -64,7 +64,7 @@ When the program is stopped, the list of stacked functions is displayed. This is 1. Stop the program with the ![Stop](./b_uv4_stop.png) **Stop** icon. The program will probably stop in the `Delay` function. 2. Click on the **Call Stack + Locals** window in the bottom right corner of μVision. 3. Inspect the various entries in the Call Stack + Locals window as shown below in this simple example. Local variables are displayed only when they are in scope: - ![Call Stack + Locals Window](./callStackLocals.png) + ![Call Stack + Locals Window](./callstacklocals.png) 1. Set a breakpoint in the `while (1)` loop at line 50 on the `g_ledSet = 1;`. 5. ![Run](./b_uv4_run.png) **Run (F5)** the application. 6. Shortly after, the program will stop there. @@ -72,7 +72,7 @@ When the program is stopped, the list of stacked functions is displayed. This is 9. Note how the variables displayed change in the **Call Stack + Locals** window. 1. ![Step into](./b_uv4_stepinto.png) **Step (F11)** a few more times. 1. Right-click on a function and select either **Show Caller Code** or **Show Callee Code** and this will be highlighted in the **Disassembly** and **source code** windows: - ![Call Stack + Locals Window](./callStackLocals_caller_callee.png) + ![Call Stack + Locals Window](./callstacklocals_caller_callee.png) 1. ![Step out](./b_uv4_stepout.png) **Step Out (Ctrl+F11)** to exit a function immediately. 1. Remove the **Breakpoint** (by clicking on its red circle ![Breakpoint](./bkpt.png)) to continue. @@ -95,9 +95,9 @@ There is a global variable `g_msTicks` located in `Blinky.c` near line 11 that y 1. ![Run](./b_uv4_run.png) **Run (F5)** the application. 2. Right click on `g_msTicks` in `Blinky.c` near line 11 and select **Add 'g_msTicks' to…** and select **Watch 1**. 3. Go to **View** in the main menu and enable **Periodic Window Update**: -![Periodic Window Update](./periodicWindowUpdate.png) +![Periodic Window Update](./periodicwindowupdate.png) 4. `g_msTicks` will be displayed in **Watch 1**: -![g_msTicks in Watch 1 Window](./gmsTicksWatch.png) +![g_msTicks in Watch 1 Window](./gmstickswatch.png) 5. The values of `g_msTicks` are updated in real-time if *Periodic Window Update* is enabled. 6. You can modify the value in a **Watch** window when the program is stopped or changing slowly. You can modify a variable in a **Memory** window anytime (see next section). @@ -118,7 +118,7 @@ You do not need to stop the program execution to enter variables, raw addresses 3. Right click in **Memory 1** and select **Unsigned Long** to see the data field as 32-bit numbers. 4. Add an ampersand `&` in front of the variable name `g_msTicks` and press Enter. Now the physical address is shown (0x2000_0008) in this case. This physical address could change with different compilation optimizations. 5. The data contents of `g_msTicks` is displayed as shown: -![g_msTicks in Memory 1 Window](./gmsTicksMemory.png) +![g_msTicks in Memory 1 Window](./gmsticksmemory.png) 6. Right click on the memory data value and select **Modify Memory at 0x3000000C**. Enter a value and this will be pushed into `g_msTicks`. Since `g_msTicks` is updated often, you will only see the new value displayed for a very short time. 7. ![Stop](./b_uv4_stop.png) **Stop** the application. diff --git a/content/learning-paths/embedded-and-microcontrollers/uv_debug/callStackLocals.png b/content/learning-paths/embedded-and-microcontrollers/uv_debug/callstacklocals.png similarity index 100% rename from content/learning-paths/embedded-and-microcontrollers/uv_debug/callStackLocals.png rename to content/learning-paths/embedded-and-microcontrollers/uv_debug/callstacklocals.png diff --git a/content/learning-paths/embedded-and-microcontrollers/uv_debug/callStackLocals_caller_callee.png b/content/learning-paths/embedded-and-microcontrollers/uv_debug/callstacklocals_caller_callee.png similarity index 100% rename from content/learning-paths/embedded-and-microcontrollers/uv_debug/callStackLocals_caller_callee.png rename to content/learning-paths/embedded-and-microcontrollers/uv_debug/callstacklocals_caller_callee.png diff --git a/content/learning-paths/embedded-and-microcontrollers/uv_debug/gmsTicksMemory.png b/content/learning-paths/embedded-and-microcontrollers/uv_debug/gmsticksmemory.png similarity index 100% rename from content/learning-paths/embedded-and-microcontrollers/uv_debug/gmsTicksMemory.png rename to content/learning-paths/embedded-and-microcontrollers/uv_debug/gmsticksmemory.png diff --git a/content/learning-paths/embedded-and-microcontrollers/uv_debug/gmsTicksWatch.png b/content/learning-paths/embedded-and-microcontrollers/uv_debug/gmstickswatch.png similarity index 100% rename from content/learning-paths/embedded-and-microcontrollers/uv_debug/gmsTicksWatch.png rename to content/learning-paths/embedded-and-microcontrollers/uv_debug/gmstickswatch.png diff --git a/content/learning-paths/embedded-and-microcontrollers/uv_debug/mainWhileLoop.png b/content/learning-paths/embedded-and-microcontrollers/uv_debug/mainwhileloop.png similarity index 100% rename from content/learning-paths/embedded-and-microcontrollers/uv_debug/mainWhileLoop.png rename to content/learning-paths/embedded-and-microcontrollers/uv_debug/mainwhileloop.png diff --git a/content/learning-paths/embedded-and-microcontrollers/uv_debug/mainWhileLoopDisassembly.png b/content/learning-paths/embedded-and-microcontrollers/uv_debug/mainwhileloopdisassembly.png similarity index 100% rename from content/learning-paths/embedded-and-microcontrollers/uv_debug/mainWhileLoopDisassembly.png rename to content/learning-paths/embedded-and-microcontrollers/uv_debug/mainwhileloopdisassembly.png diff --git a/content/learning-paths/embedded-and-microcontrollers/uv_debug/mainWhileLoopStopped.png b/content/learning-paths/embedded-and-microcontrollers/uv_debug/mainwhileloopstopped.png similarity index 100% rename from content/learning-paths/embedded-and-microcontrollers/uv_debug/mainWhileLoopStopped.png rename to content/learning-paths/embedded-and-microcontrollers/uv_debug/mainwhileloopstopped.png diff --git a/content/learning-paths/embedded-and-microcontrollers/uv_debug/manageBKPT.png b/content/learning-paths/embedded-and-microcontrollers/uv_debug/managebkpt.png similarity index 100% rename from content/learning-paths/embedded-and-microcontrollers/uv_debug/manageBKPT.png rename to content/learning-paths/embedded-and-microcontrollers/uv_debug/managebkpt.png diff --git a/content/learning-paths/embedded-and-microcontrollers/uv_debug/periodicWindowUpdate.png b/content/learning-paths/embedded-and-microcontrollers/uv_debug/periodicwindowupdate.png similarity index 100% rename from content/learning-paths/embedded-and-microcontrollers/uv_debug/periodicWindowUpdate.png rename to content/learning-paths/embedded-and-microcontrollers/uv_debug/periodicwindowupdate.png diff --git a/content/learning-paths/embedded-and-microcontrollers/zephyr-signed-fit-uboot-cortex-a/1-boot-chain.md b/content/learning-paths/embedded-and-microcontrollers/zephyr-signed-fit-uboot-cortex-a/1-boot-chain.md new file mode 100644 index 0000000000..196a456b18 --- /dev/null +++ b/content/learning-paths/embedded-and-microcontrollers/zephyr-signed-fit-uboot-cortex-a/1-boot-chain.md @@ -0,0 +1,91 @@ +--- +title: Understand where U-Boot verifies Zephyr in the Cortex-A boot chain +description: Learn why Zephyr on a Cortex-A processor is a payload that U-Boot can verify, what a FIT image contains, and how the boot chain hands control to it. +weight: 2 + +### FIXED, DO NOT MODIFY +layout: learningpathall +--- + +## Where Zephyr fits in the Cortex-A boot chain + +On a microcontroller (MCU), Zephyr runs first, so secure boot means adding a bootloader such as MCUboot. A Cortex-A processor starts in a read-only memory (ROM) inside the system on chip (SoC), and several firmware stages run before your code. Zephyr is the last stage, so the stage before it — usually U-Boot — can check it before starting it. + +You'll package Zephyr in a Flattened Image Tree (FIT) with a signature that U-Boot verifies before starting the image. + +You'll build and boot a verified Zephyr image either in QEMU without hardware, or on a TI AM62L evaluation module (EVM). You can use the same signing and verification workflow for both targets. The Learning Path includes separate instructions where setup differs. + +## The boot chain on a Cortex-A board + +On an Armv8-A board using U-Boot, boot proceeds from the boot ROM of the SoC through early firmware to U-Boot. U-Boot then loads and starts the payload. + +The early firmware is usually the Secondary Program Loader (SPL), a small U-Boot that loads the full one. In addition, Trusted Firmware-A (TF-A) and Open Portable Trusted Execution Environment (OP-TEE) run in the Secure world. Many SoCs add security firmware from the vendor, which checks the later stages. + +You'll build the SPL with U-Boot. The rest comes prebuilt in the SDK from the vendor. + +![Cortex-A boot chain from boot ROM through early firmware and U-Boot to a signed Zephyr FIT image. U-Boot verifies Zephyr; trust in the earlier stages depends on the secure-boot configuration of the platform.#center](images/boot-chain-generic.svg "U-Boot verifies Zephyr in the Cortex-A boot chain") + +## Choose your target + +Choose between QEMU and AM62L EVM. QEMU is the default target for which you need nothing but your host. The AM62L EVM shows the same work on real silicon. + +| Requirement or feature | QEMU | AM62L EVM | +|---|---|---| +| Hardware | None | The board, a micro-SD card and reader, a micro-USB cable, and a USB-C PD supply | +| Download | U-Boot source, 32 MB | TI Processor SDK, 4.5 GB, unpacks to 11 GB | +| Stages before U-Boot | None, as QEMU starts `u-boot.bin` directly | Boot ROM, `tiboot3.bin`, `tispl.bin` | +| Zephyr board | `qemu_cortex_a53` | `am62l_evm/am62l3/a53` | +| Boot media | File Allocation Table (FAT) partition in a 64 MiB disk image, `virtio 0:1` | FAT partition on a micro-SD card, `mmc 1:1` | +| Console | The terminal that you start QEMU in | USB serial at 115200 baud | + +### The boot chain in QEMU + +The QEMU `virt` machine has no boot ROM and no vendor firmware. It loads the file that you pass to `-bios` and starts the Cortex-A53 on it, so the chain is two links long: + +```output +QEMU -bios u-boot.bin -> U-Boot -> Zephyr inside a FIT image +``` + +QEMU runs the FIT verifier in U-Boot, but nothing authenticates U-Boot itself. + +### The boot chain on the AM62L EVM + +On the AM62L, the early stages are two files. `tiboot3.bin` holds the first stage of TF-A and TI Foundational Security (TIFS), TI's security firmware. `tispl.bin` holds the rest of TF-A, OP-TEE, and the SPL. You'll build them together with `u-boot.img` when you build U-Boot. The EVM ships in TI's High Security, Field Securable (HS-FS) development state. + +![AM62L boot chain from boot ROM through `tiboot3.bin`, `tispl.bin`, and `u-boot.img` to the Zephyr FIT image. In HS-FS development mode, the early stages accept any signing key; U-Boot verifies Zephyr against your key before starting it.#center](images/boot-chain.svg "The same chain on the AM62L, with TI's file names") + +## What a FIT image is + +A FIT is a container format for U-Boot boot images. It's a device tree blob (DTB) whose nodes hold images instead of hardware descriptions. + +The signed FIT that you'll build contains the following: + +- The image itself, which is the Zephyr binary `zephyr.bin` +- A hash of that image, so that U-Boot can tell if a byte changed +- A configuration that names the image to use (`kernel = "kernel-1"`) and carries a signature + +`mkimage`, a U-Boot host tool, builds the FIT from a text source `.its` file and signs it with your private key. On the target, the U-Boot `bootm` command parses the FIT, verifies the signature and the hash, and copies the image to its load address. + +You'll sign the FIT configuration with `sha256,rsa2048`, which combines SHA-256 hashing with a 2048-bit RSA key. + +The public key lives under a `/signature` node in the U-Boot control device tree. That key node carries `required = "conf"`. Every configuration in every FIT must carry a valid signature by this key, or U-Boot refuses to load it. Without `required`, a FIT with no signature still boots. + +![Signed FIT image beside the U-Boot control device tree, which holds the required public key. The signature covers the FIT configuration and payload hash; the hash covers the Zephyr image bytes.#center](images/fit-signature.svg "What the signature and the hash each cover in a FIT image") + +The signature doesn't cover the image bytes. It covers the configuration node plus the hash node, and the hash covers the bytes. + +## Verify Zephyr before starting it + +Configure U-Boot to verify the Zephyr FIT before starting Zephyr with `go`, a U-Boot command for jumping to an address. + +If you choose QEMU as your target, there's no key to fuse and nothing checks U-Boot at all. + +If you choose AM62L EVM as your target, leave it in its development state without fusing your key into the chip. In that state, the ROM and vendor firmware accept boot files signed with any key. U-Boot verifies Zephyr either way. + +After booting the image, you'll review [the Zephyr FIT verification boundary and production needs](/learning-paths/embedded-and-microcontrollers/zephyr-signed-fit-uboot-cortex-a/8-production/). + +## What you've learned and what's next + +You've learned that Zephyr is the last stage of a Cortex-A boot chain, so U-Boot can verify it. You also learned that `required = "conf"` makes U-Boot refuse any FIT it can't verify. + +Next, you'll set up the host tools for your target. diff --git a/content/learning-paths/embedded-and-microcontrollers/zephyr-signed-fit-uboot-cortex-a/2-set-up-tools.md b/content/learning-paths/embedded-and-microcontrollers/zephyr-signed-fit-uboot-cortex-a/2-set-up-tools.md new file mode 100644 index 0000000000..ecbf9a4936 --- /dev/null +++ b/content/learning-paths/embedded-and-microcontrollers/zephyr-signed-fit-uboot-cortex-a/2-set-up-tools.md @@ -0,0 +1,214 @@ +--- +title: Set up the host tools for your target +description: Prepare an Ubuntu host with Zephyr build tools, U-Boot source, a cross compiler, and a shared environment file for QEMU or the TI AM62L EVM. +weight: 3 + +### FIXED, DO NOT MODIFY +layout: learningpathall +--- + +## What you need to set up + +You need Zephyr host tools, U-Boot source, and a cross compiler for your chosen target. For QEMU, download U-Boot source and install the emulator and compiler from Ubuntu packages. For the AM62L evaluation module (EVM), the TI Processor SDK supplies U-Boot source, the cross compiler, and prebuilt early firmware. + +## Install the Zephyr host tools + +To install host tools on Visual Studio Code: + +1. Open Visual Studio Code and select **Workbench for Zephyr** in the Activity Bar. +2. In its panel, select **Install Host Tools** to install the dependencies used to build Zephyr: + + - Python + - CMake + - Ninja + - Git + - Device Tree Compiler + - West + +3. When installation finishes, select **Verify Host Tools**. Resolve any missing-tool errors before continuing. + +You'll import the AArch64 toolchain and create the West workspace later, before building Zephyr. + +## Install the host packages + +Install the additional packages used to build U-Boot, sign images, and prepare the boot media: + +```bash +sudo apt install -y build-essential bison flex swig python3-dev python3-setuptools libssl-dev libgnutls28-dev uuid-dev device-tree-compiler openssl mtools dosfstools xz-utils curl picocom +``` + +If you chose QEMU as your target, install the emulator and U-Boot cross compiler: + +```bash +sudo apt install -y qemu-system-arm gcc-aarch64-linux-gnu +``` +For the AM62L EVM, skip installing the emulator. You'll get the cross compiler from the TI SDK. + +## Create the working directory and environment file + +Keep your source, tools, and build outputs under `$HOME/zephyr-secure-boot`. An environment file stores the paths and target settings that you'll reuse throughout the workflow. + +Follow the instructions for your target to create and load its environment file, and create the signing-key and Flattened Image Tree (FIT) directories: + +{{< tabpane code=true >}} + {{< tab header="QEMU" language="bash" >}} +mkdir -p $HOME/zephyr-secure-boot +cat > $HOME/zephyr-secure-boot/env-qemu.sh <<'EOF' +export WORK=$HOME/zephyr-secure-boot + +# Target values +export BOARD=qemu_cortex_a53 # Zephyr board identifier +export ZEPHYR_ADDR=0x40000000 # Zephyr link address, FIT load and go target +export FIT_ADDR=0x48000000 # where U-Boot loads the FIT, clear of ZEPHYR_ADDR +export BOOT_DEV="virtio 0:1" # U-Boot device and partition that hold the files +export UBOOT_DEFCONFIG=qemu_arm64_defconfig # U-Boot configuration for the target + +# No vendor SDK: U-Boot from the U-Boot project, cross compiler from Ubuntu +export UBOOT_SRC=$WORK/u-boot-2025.07 +export CROSS=aarch64-linux-gnu- +export UBOOT_CC="${CROSS}gcc" + +# Your build outputs: U-Boot build, signing keys, signed FITs, and the boot volume +export UBOOT_OUT=$WORK/uboot-build +export KEYS=$WORK/keys +export FIT=$WORK/fit +export BOOT_IMG=$WORK/disk.img@@1048576 +EOF +source $HOME/zephyr-secure-boot/env-qemu.sh +mkdir -p $KEYS $FIT + {{< /tab >}} + {{< tab header="AM62L EVM" language="bash" >}} +mkdir -p $HOME/zephyr-secure-boot +cat > $HOME/zephyr-secure-boot/env-am62l.sh <<'EOF' +export WORK=$HOME/zephyr-secure-boot + +# Target values +export BOARD=am62l_evm/am62l3/a53 # Zephyr board identifier +export ZEPHYR_ADDR=0x82000000 # Zephyr link address, FIT load and go target +export FIT_ADDR=0x90000000 # where U-Boot loads the FIT, clear of ZEPHYR_ADDR +export BOOT_DEV="mmc 1:1" # U-Boot device and partition that hold the files +export UBOOT_DEFCONFIG=am62lx_evm_defconfig # U-Boot configuration for the target + +# Vendor SDK: U-Boot source, firmware that runs before U-Boot, cross compiler and its libraries +export SDK=$WORK/tisdk +export UBOOT_SRC=$SDK/board-support/ti-u-boot-2026.01+git +export PREBUILT=$SDK/board-support/prebuilt-images/am62lxx-evm +export CROSS=$SDK/linux-devkit/sysroots/x86_64-arago-linux/usr/bin/aarch64-oe-linux/aarch64-oe-linux- +export SYSROOT=$SDK/linux-devkit/sysroots/aarch64-oe-linux +export UBOOT_CC="${CROSS}gcc --sysroot=$SYSROOT" + +# Your build outputs: U-Boot build, signing keys, signed FITs, and the boot volume +export UBOOT_OUT=$WORK/uboot-build +export KEYS=$WORK/keys +export FIT=$WORK/fit +export BOOT_IMG=$WORK/sdcard.img@@1048576 +EOF +source $HOME/zephyr-secure-boot/env-am62l.sh +mkdir -p $KEYS $FIT + {{< /tab >}} +{{< /tabpane >}} + +The variables are available only in the shell where you load the environment file. If you open a new terminal, you'll have to load the environment file again. If you adapt the workflow to another board, you'll learn how to choose its target values and paths in the [optional board-adaptation guidance](/learning-paths/embedded-and-microcontrollers/zephyr-signed-fit-uboot-cortex-a/8-production/). + +{{% notice Note %}} +Example output shows paths under `/home/user/`, the home directory of the account that produced it. The paths in your output will reflect your own username. For example, on a standard Ubuntu cloud instance, they appear under `/home/ubuntu/`. The commands use `$HOME` and `$WORK`, so they adapt to your account automatically. Only the printed paths in the example outputs differ. +{{% /notice %}} + +## Get the U-Boot source and the cross compiler + +Follow the instructions for your target to download U-Boot and the cross compiler. + +{{< tabpane-normal >}} + {{< tab header="QEMU" >}} +Download U-Boot 2025.07 and unpack it into your working directory. The archive is about 32 MB: + +```bash +curl -L -o $WORK/u-boot-2025.07.tar.bz2 https://ftp.denx.de/pub/u-boot/u-boot-2025.07.tar.bz2 +tar -xf $WORK/u-boot-2025.07.tar.bz2 -C $WORK +ls -d $UBOOT_SRC +``` + +The expected output is: + +```output +/home/user/zephyr-secure-boot/u-boot-2025.07 +``` + +You'll add the public key and boot command through build configuration, without patching U-Boot source. + +Check that the cross compiler runs: + +```bash +${CROSS}gcc --version +``` + +The output is similar to: + +```output +aarch64-linux-gnu-gcc (Ubuntu 13.3.0-6ubuntu2~24.04.1) 13.3.0 +... +``` + +Check that the emulator is installed and report its version: + +```bash +qemu-system-aarch64 --version +``` + +The output is similar to: + +```output +QEMU emulator version 8.2.2 (Debian 1:8.2.2+ds-0ubuntu1.18) +... +``` + +You'll create a 64 MiB disk image when you prepare the boot media. You don't need to download one for QEMU. + {{< /tab >}} + {{< tab header="AM62L EVM" >}} +The TI Processor SDK Linux for AM62Lx supplies U-Boot source, prebuilt early firmware, and the cross compiler. You'll use these components to boot Zephyr. The installer is 4.5 GB and unpacks to 11 GB. + +Download version 12.01.00.05.03 from the [TI Processor SDK download page](https://www.ti.com/tool/download/AM62L-LINUX-SDK/12.01.00.05.03): + +```bash +curl -L -o $WORK/ti-processor-sdk-linux-am62lxx-evm-12.01.00.05.03-Linux-x86-Install.bin https://dr-download.ti.com/software-development/software-development-kit-sdk/MD-YjEeNKJJjt/12.01.00.05.03/ti-processor-sdk-linux-am62lxx-evm-12.01.00.05.03-Linux-x86-Install.bin +``` + +Make the installer executable and run it. `--mode unattended` uses the default settings without opening the graphical wizard. `--prefix` selects `$SDK` as the installation directory, without root privileges: + +```bash +chmod +x $WORK/ti-processor-sdk-linux-am62lxx-evm-12.01.00.05.03-Linux-x86-Install.bin +$WORK/ti-processor-sdk-linux-am62lxx-evm-12.01.00.05.03-Linux-x86-Install.bin --mode unattended --prefix $SDK +``` + +Check that the three paths exist and the compiler runs: + +```bash +ls -d $UBOOT_SRC $PREBUILT $SYSROOT +${CROSS}gcc --version +``` + +The output is similar to: + +```output +/home/user/zephyr-secure-boot/tisdk/board-support/prebuilt-images/am62lxx-evm +/home/user/zephyr-secure-boot/tisdk/board-support/ti-u-boot-2026.01+git +/home/user/zephyr-secure-boot/tisdk/linux-devkit/sysroots/aarch64-oe-linux +aarch64-oe-linux-gcc (GCC) 15.3.0 +... +``` + +Download TI's SD card image to preserve the boot partition layout expected by the AM62L boot ROM: + +```bash +curl -L -o $WORK/tisdk-default-image.wic.xz https://dr-download.ti.com/software-development/software-development-kit-sdk/MD-YjEeNKJJjt/12.01.00.05.03/tisdk-default-image-am62lxx-evm-12.01.00.05.03.rootfs.wic.xz +``` +The download is about 1.3 GB. Leave it compressed. When you prepare the boot media, you'll extract the boot partition and replace its files. + + {{< /tab >}} +{{< /tabpane-normal >}} + +## What you've accomplished and what's next + +You've installed the Zephyr host tools and U-Boot build packages. You've also obtained the U-Boot source and cross compiler for your target, and created a reusable environment file. For the AM62L EVM, you've additionally set up the early firmware and SD card image. + +Next, you'll import the AArch64 toolchain, create a West workspace, and build a small Zephyr application for your chosen target. diff --git a/content/learning-paths/embedded-and-microcontrollers/zephyr-signed-fit-uboot-cortex-a/3-build-zephyr.md b/content/learning-paths/embedded-and-microcontrollers/zephyr-signed-fit-uboot-cortex-a/3-build-zephyr.md new file mode 100644 index 0000000000..80f3e76997 --- /dev/null +++ b/content/learning-paths/embedded-and-microcontrollers/zephyr-signed-fit-uboot-cortex-a/3-build-zephyr.md @@ -0,0 +1,194 @@ +--- +title: Build a Zephyr image that U-Boot can start +description: Build a Zephyr image for QEMU or the TI AM62L EVM with Workbench for Zephyr, and check the arm64 header used to start it from U-Boot. +weight: 4 + +### FIXED, DO NOT MODIFY +layout: learningpathall +--- + +## Open Workbench for Zephyr + +Use Workbench for Zephyr to import an AArch64 toolchain, create a Zephyr workspace, and build your application. The steps are the same for both targets. Select the board that matches your target. + +Open Visual Studio Code on your working directory: + +```bash +code $HOME/zephyr-secure-boot +``` + +Select the **Workbench for Zephyr** icon in the Activity Bar. Its panel contains the **Applications**, **West workspaces**, **Toolchains**, and **Host tools** views. + +{{% notice Note %}} +The screenshots show Windows paths, a globally installed SDK, and the AM62L evaluation module (EVM). On Ubuntu, use your own paths and SDK location. If you chose QEMU, select `qemu_cortex_a53` wherever a board is requested. +{{% /notice %}} + +## Import the AArch64 toolchain + +Use the `aarch64-zephyr-elf` compiler from the Zephyr SDK for your Cortex-A53 target. Select the **Minimal** SDK type and **aarch64** architecture to download that toolchain. + +In the **Workbench for Zephyr** panel, select **Add Toolchain** and fill in the form: + +1. For **Toolchain family**, select **Zephyr SDK**. +2. For **Source**, select **Official**. +3. For **Destination**, select **Custom location**. +4. For **SDK Type**, select **Minimal**. +5. For **Version**, select **v1.0.1**. +6. Select **aarch64** and clear the other architecture checkboxes. +7. For **Location**, specify a parent directory where Workbench creates the SDK folder, such as `$HOME/zephyr-secure-boot`. Workbench adds a versioned subdirectory such as `zephyr-sdk-1.0.1` inside it. + +![Workbench for Zephyr Add Toolchain form with Zephyr SDK 1.0.1, the Minimal SDK type, and aarch64 selected. These settings provide the compiler for both Cortex-A53 targets.#center](images/wz-add-toolchain.webp "Import the AArch64 toolchain") + +8. Select **Import**. + +The download takes a few minutes. When it finishes, the **Toolchains** view shows `Zephyr SDK 1.0.1` with a `GNU` entry and `aarch64-zephyr-elf` under it. + +## Add a West workspace + +Create a Zephyr 4.4.2 workspace for your chosen target. The same workspace supports QEMU and the AM62L EVM. Select **Add West Workspace** and fill in the form: + +1. For **Source location**, select **From template**. +2. Under **Path**, select **Minimal**. +3. For **Template**, select **Texas Instruments** for either target. +4. For **Revision**, select **v4.4.2** or a later 4.x release. +5. For **Location**, specify **`$HOME/zephyr-secure-boot`** (shown expanded, such as `/home/ubuntu/zephyr-secure-boot`). +6. For **Subfolder**, select **`zephyrproject`**. + +![Workbench for Zephyr Add West Workspace form using the Minimal option, Texas Instruments template, and Zephyr v4.4.2. The workspace is named zephyrproject and supports either target.#center](images/wz-add-west-workspace.webp "Create the Zephyr 4.4.2 workspace") + +7. Select **Import**. + +Workbench downloads Zephyr, the hardware abstraction layer (HAL) from the vendor, and other modules into `zephyrproject/deps`. This can take several minutes. When it finishes, confirm that `zephyrproject` appears in the **West workspaces** view. + +## Create the application + +Select **Add Application** and fill in the wizard: + +1. For **Select West Workspace**, select **`zephyrproject`**. +2. For **Select Toolchain**, select **`zephyr-sdk-1.0.1`**. +3. For **SDK Variant**, select **GNU GCC**. +4. For **Select Board**, enter **qemu** and select **QEMU Emulation for ARM Cortex-A53**, or enter **am62l** and select **TI AM62L Evaluation Module (EVM)**. +5. For **New or existing application?**, select **Create new application**. +6. For **Select template**, select **`hello_world`**, under **`deps/zephyr/samples/hello_world`**. +7. For **Project Name**, enter **`hello`**. +8. For **Application type**, select **West workspace application**. +9. For **Project Location**, ensure the value is **`zephyrproject/applications/hello`**, filled in by the wizard. + + Check that the board identifier matches `BOARD` in your environment file: `qemu_cortex_a53` for QEMU or `am62l_evm/am62l3/a53` for the AM62L EVM. +10. Select **Create**. + + ![Workbench for Zephyr Add Application wizard creating hello from the hello_world sample with the zephyrproject workspace and Zephyr SDK 1.0.1. The AM62L EVM is selected; the callout identifies the alternative QEMU Cortex-A53 board.#center](images/wz-add-application.webp "Create hello for your chosen target") + +The application `hello` appears in the **Applications** view, marked `[with zephyrproject]`. `zephyrproject/applications/hello` holds the `CMakeLists.txt`, `prj.conf` and `src/main.c` files for the sample. + +## Replace files in the sample + +Leave `CMakeLists.txt` as it is and replace the other two files. + +Replace the contents of `prj.conf` with the following four lines: + +```text +CONFIG_ARMV8_A_NS=y +CONFIG_PRINTK=y +CONFIG_BOOT_BANNER=y +CONFIG_AARCH64_IMAGE_HEADER=y +``` + +`CONFIG_PRINTK` enables console output with `printk()`. `CONFIG_BOOT_BANNER` prints the `*** Booting Zephyr OS build ... ***` line, confirming that Zephyr has started. + +Replace the contents of `src/main.c` with the following code: + +```c +#include +#include + +int main(void) +{ + printk("\n"); + printk("################################################\n"); + printk("# #\n"); + printk("# Hello from ZEPHYR IMAGE A #\n"); + printk("# signed with key-a (TRUSTED by U-Boot) #\n"); + printk("# #\n"); + printk("################################################\n"); + printk("\n"); + printk("board : %s\n", CONFIG_BOARD_TARGET); + printk("arch : %s\n", CONFIG_ARCH); + printk("started by : U-Boot 'go' after FIT signature verification\n"); + printk("this image was verified by U-Boot before it ran.\n"); + return 0; +} +``` + +Its banner identifies image A and the key that you'll use to sign it. This helps you distinguish the images if you run the optional [wrong-key and tampered-image tests](/learning-paths/embedded-and-microcontrollers/zephyr-signed-fit-uboot-cortex-a/7-test-the-checks/). + +Save both files. + +## Configure Non-secure execution and the arm64 header + +`CONFIG_ARMV8_A_NS=y` tells Zephyr that it runs in the Non-secure world, the one U-Boot hands it. Without it, the driver of the Generic Interrupt Controller (GICv3) never programs the Non-secure registers. The timer interrupt never arrives, and Zephyr hangs right after its banner. + +`CONFIG_AARCH64_IMAGE_HEADER=y` puts a 64-byte arm64 header at the start of `zephyr.bin`, in the same format as a Linux kernel image. The first instruction in the header is `b __start`, a branch to the real entry point of Zephyr. U-Boot needs that branch only at offset 0. Without the header, `go` jumps into whatever the linker placed there. + +The configurations for `qemu_cortex_a53` and `am62l_evm/am62l3/a53` already set both options. Repeating them in `prj.conf` protects you on a board whose configuration doesn't. + +## Build the application + +In the **Applications** view, open the context menu for `hello` and select **Build**. Workbench runs `west build` in the terminal and writes the build outputs to the `build/primary` directory for the application. Near the end, the linker prints a memory report similar to: + +```output +Memory region Used Size Region Size %age Used + FLASH: 0 B 0 B + RAM: 4168 KB 2016 MB 0.20% + IDT_LIST: 0 B 32 KB 0.00% +``` + +![Workbench for Zephyr showing hello configured for the AM62L EVM and a memory report in the build terminal. The report appears near the end of the build; the generated zephyr.bin is checked in the next step.#center](images/wz-build.webp "Build hello for the AM62L EVM") + +The result is `$WORK/zephyrproject/applications/hello/build/primary/zephyr/zephyr.bin`, a raw binary linked at `ZEPHYR_ADDR`, the start of the `zephyr,sram` memory node for the target. The binary is about 37 KB for `qemu_cortex_a53`, whose memory report shows 128 MB of RAM. It's about 58 KB for the AM62L EVM, which reports 2016 MB of RAM. + +{{% notice Note %}} +On an `aarch64` host, the build can fail with `exec format error` when it invokes `cmake` from `~/.zinstaller`. Some versions of the Workbench for Zephyr host tools install `x86_64` builds of CMake and Ninja, which can't run on Arm. Verify that Host Tools can also report a misleading error, such as `-255 package(s) are not installed`, for the same reason. + +Confirm the architecture of the installed tools: + +```bash +file ~/.zinstaller/tools/cmake-*/bin/cmake ~/.zinstaller/tools/ninja/ninja +``` + +If the output reports `x86-64`, point the two tools at the Arm builds on your system, after installing them with `sudo apt install -y cmake ninja-build`: + +```bash +ln -sf "$(command -v cmake)" ~/.zinstaller/tools/cmake-*/bin/cmake +ln -sf "$(command -v ninja)" ~/.zinstaller/tools/ninja/ninja +``` + +Rebuild the application. The Zephyr SDK compiler is a native Arm binary and doesn't need this change. +{{% /notice %}} + +## Check the arm64 image header + +Open a terminal with **Terminal > New Terminal** in Visual Studio Code, or use an existing shell. Load the environment file for your target: + +- QEMU: `source $HOME/zephyr-secure-boot/env-qemu.sh` +- AM62L EVM: `source $HOME/zephyr-secure-boot/env-am62l.sh` + +Print the magic number in the header at offset `0x38`: + +```bash +od -An -c -j 0x38 -N4 $WORK/zephyrproject/applications/hello/build/primary/zephyr/zephyr.bin +``` + +The expected output is: + +```output + A R M d +``` + +`ARMd` is the arm64 image magic, so the header is in place and the image starts with the branch instruction that `go` jumps to. If you see anything else, check that `$WORK/zephyrproject/applications/hello/build/primary/zephyr/.config` contains `CONFIG_AARCH64_IMAGE_HEADER=y`. + +## What you've accomplished and what's next + +You've built a Zephyr image for the Cortex-A53 in your target, linked at `ZEPHYR_ADDR`, and checked its arm64 header. + +Next, you'll create the signing keys and package the image in a signed Flattened Image Tree (FIT) for U-Boot to verify. diff --git a/content/learning-paths/embedded-and-microcontrollers/zephyr-signed-fit-uboot-cortex-a/4-sign-zephyr.md b/content/learning-paths/embedded-and-microcontrollers/zephyr-signed-fit-uboot-cortex-a/4-sign-zephyr.md new file mode 100644 index 0000000000..aff90c6cda --- /dev/null +++ b/content/learning-paths/embedded-and-microcontrollers/zephyr-signed-fit-uboot-cortex-a/4-sign-zephyr.md @@ -0,0 +1,174 @@ +--- +title: Create signing keys and sign the Zephyr image into a FIT +description: Build the U-Boot signing tools, generate RSA key pairs with OpenSSL, and package Zephyr in a signed FIT image for U-Boot to verify. +weight: 5 + +### FIXED, DO NOT MODIFY +layout: learningpathall +--- + +## Build the U-Boot host tools + +Open a terminal and load the environment file for your target: + +- QEMU: `source $HOME/zephyr-secure-boot/env-qemu.sh` +- AM62L evaluation module (EVM): `source $HOME/zephyr-secure-boot/env-am62l.sh` + +Build `mkimage` to create and sign Flattened Image Tree (FIT) images, and `fit_check_sign` to verify them on the host. Use the U-Boot source tree for your target so that the host tools match the U-Boot version that you'll run. + +Before building the tools, generate `.config` from the default configuration for your target. Run the target named by `UBOOT_DEFCONFIG` in your environment file: + +```bash +make -C $UBOOT_SRC O=$UBOOT_OUT CROSS_COMPILE=$CROSS CC="$UBOOT_CC" $UBOOT_DEFCONFIG +``` + +`UBOOT_CC` stores the compiler command. For QEMU, it names the cross compiler installed from Ubuntu. For the AM62L EVM, it also includes `--sysroot` to locate the headers and libraries in the SDK. Keep `CC="$UBOOT_CC"` on every `make` command, including when you build U-Boot later. + +Then, build the host tools: + +```bash +make -C $UBOOT_SRC O=$UBOOT_OUT CROSS_COMPILE=$CROSS CC="$UBOOT_CC" tools +``` + +Both tools are written to `$UBOOT_OUT/tools/`. Check that `mkimage` runs: + +```bash +$UBOOT_OUT/tools/mkimage -V +``` + +The output is similar to: + +```output +mkimage version 2026.01-g5fb294342321 +``` + +The version string identifies your U-Boot source tree. The example output indicates the TI tree for the AM62L EVM, version 2026.01. For the QEMU setup, expect `mkimage version 2025.07`. + +{{% notice Note %}} +If the tools build reports a missing `pylibfdt` dependency, `swig`, or `gnutls/gnutls.h`, check the packages installed during [host-tool setup](/learning-paths/embedded-and-microcontrollers/zephyr-signed-fit-uboot-cortex-a/2-set-up-tools/). Run the shared `apt install` command again, then retry the tools build. +{{% /notice %}} + +## Create two signing key pairs + +Use the private key to sign the image and keep it on the host. You'll embed only the public key in U-Boot. + +Create two key pairs. You'll configure U-Boot to trust `key-a` and leave out the public key for `key-b`. The optional [wrong-key and tampered-image tests](/learning-paths/embedded-and-microcontrollers/zephyr-signed-fit-uboot-cortex-a/7-test-the-checks/) use `key-b` to demonstrate rejection of an image signed with an untrusted key. + +Generate both pairs with OpenSSL: + +```bash +cd $KEYS +for k in key-a key-b; do + openssl genpkey -algorithm RSA -quiet -out $k.key \ + -pkeyopt rsa_keygen_bits:2048 -pkeyopt rsa_keygen_pubexp:65537 + openssl req -batch -new -x509 -key $k.key -out $k.crt -subj "/CN=$k" -days 3650 +done +ls $KEYS +``` + +The expected output is: + +```output +key-a.crt key-a.key key-b.crt key-b.key +``` + +`mkimage -k ` expects the following files in the key directory: + +- `.key` holds the private key. +- `.crt` is a self-signed certificate containing the public key. +- The shared file-name stem, ``, matches `key-name-hint` in the FIT source. + +These 2048-bit keys are generated on the build host for this demonstration. The later section on [the Zephyr FIT verification boundary and production needs](/learning-paths/embedded-and-microcontrollers/zephyr-signed-fit-uboot-cortex-a/8-production/) covers production key handling. + +## Write the FIT source + +Describe the FIT in an image tree source (`.its`) file. `mkimage` compiles and signs it to produce an image tree blob (`.itb`). Create the source with the following command. The unquoted `EOF` delimiter lets the shell expand `$WORK` and `$ZEPHYR_ADDR`: + +```bash +cat > $FIT/zephyr-a.its <; + images { + kernel-1 { + description = "Zephyr RTOS image"; + data = /incbin/("$WORK/zephyrproject/applications/hello/build/primary/zephyr/zephyr.bin"); + type = "kernel"; + arch = "arm64"; + os = "u-boot"; + compression = "none"; + load = <$ZEPHYR_ADDR>; + entry = <$ZEPHYR_ADDR>; + hash-1 { algo = "sha256"; }; + }; + }; + configurations { + default = "conf-1"; + conf-1 { + description = "Zephyr on Cortex-A"; + kernel = "kernel-1"; + signature-1 { + algo = "sha256,rsa2048"; + key-name-hint = "key-a"; + sign-images = "kernel"; + }; + }; + }; +}; +EOF +``` + +The FIT source defines how U-Boot handles the payload: + +- `os = "u-boot"` marks Zephyr as a standalone program, so U-Boot verifies and copies it without looking for a Linux kernel header or device tree. +- `signature-1` sits under the configuration, because the `required = "conf"` rule checks the configuration U-Boot boots. With `sign-images = "kernel"`, the signature also covers the `hash-1` node of the image, and `key-name-hint` names the key in `$KEYS`. +- `load` and `entry` are `ZEPHYR_ADDR`, where U-Boot copies the verified payload and where `go` jumps. + +## Sign the image + +Compile and sign the FIT. `-k $KEYS` tells `mkimage` where the private key is: + +```bash +$UBOOT_OUT/tools/mkimage -f $FIT/zephyr-a.its -k $KEYS $FIT/zephyr-a.itb +``` + +`mkimage` prints the FIT contents. Your timestamps, `Hash value`, and `Sign value` will differ. The output is similar to: + +```output +FIT description: Zephyr RTOS, signed with key-a +Created: Fri Sep 11 19:14:00 2026 + Image 0 (kernel-1) + Description: Zephyr RTOS image + Created: Fri Sep 11 19:14:00 2026 + Type: Kernel Image + Compression: uncompressed + Data Size: 58340 Bytes = 56.97 KiB = 0.06 MiB + Architecture: AArch64 + OS: U-Boot + Load Address: 0x82000000 + Entry Point: 0x82000000 + Hash algo: sha256 + Hash value: 1d1d375f14c3354579e9e5310986a69b831bcd1944cd6b35aa8e83632b658166 + Default Configuration: 'conf-1' + Configuration 0 (conf-1) + Description: Zephyr on Cortex-A + Kernel: kernel-1 + Sign algo: sha256,rsa2048:key-a + Sign value: 6d5f9933722189a0dcee58791243429d0bf28fe0dce96ac093da23c1f24ead70... + Timestamp: Fri Sep 11 19:14:00 2026 +Signature written to '/home/user/zephyr-secure-boot/fit/zephyr-a.itb', node '/configurations/conf-1/signature-1' +``` +The `Sign value` is shortened for readability. + +Check `Sign algo: sha256,rsa2048:key-a` and the final `Signature written to ...` line. They confirm that `mkimage` used `key-a` and wrote its signature into `conf-1`. `Hash value` is the SHA-256 hash of `zephyr.bin`, which U-Boot recomputes during verification. + +The example output is for the AM62L EVM build. For the QEMU build, expect `Data Size: 37040 Bytes` and `Load Address: 0x40000000`. + +Omit `mkimage -K` when signing this FIT. You'll add the public key to the U-Boot control device tree during the build. + +## What you've accomplished and what's next + +You've built the U-Boot host tools, created two key pairs, and packaged Zephyr in the signed FIT `$FIT/zephyr-a.itb`. + +Next, you'll build U-Boot with the trusted public key and a boot command that starts Zephyr only after verification succeeds. diff --git a/content/learning-paths/embedded-and-microcontrollers/zephyr-signed-fit-uboot-cortex-a/5-build-uboot.md b/content/learning-paths/embedded-and-microcontrollers/zephyr-signed-fit-uboot-cortex-a/5-build-uboot.md new file mode 100644 index 0000000000..6b68fed60d --- /dev/null +++ b/content/learning-paths/embedded-and-microcontrollers/zephyr-signed-fit-uboot-cortex-a/5-build-uboot.md @@ -0,0 +1,372 @@ +--- +title: Build U-Boot with the public key and a boot command that fails closed +description: Build U-Boot with a trusted public key and a boot command that starts Zephyr only after verification, then check the signed FIT on the host. +weight: 6 + +### FIXED, DO NOT MODIFY +layout: learningpathall +--- + +## Check that U-Boot can verify signatures + +Open a terminal and load the environment file for your target: + +- QEMU: `source $HOME/zephyr-secure-boot/env-qemu.sh` +- AM62L evaluation module (EVM): `source $HOME/zephyr-secure-boot/env-am62l.sh` + +Both target default configurations enable Flattened Image Tree (FIT) signature verification. Check the relevant settings in `$UBOOT_OUT/.config`, generated when you built the host tools: + +```bash +grep -E '^CONFIG_(FIT|FIT_SIGNATURE|RSA|OF_SEPARATE)=|LEGACY_IMAGE_FORMAT' $UBOOT_OUT/.config +``` + +For the AM62L EVM, the expected output is: + +```output +CONFIG_FIT=y +CONFIG_FIT_SIGNATURE=y +# CONFIG_LEGACY_IMAGE_FORMAT is not set +CONFIG_OF_SEPARATE=y +CONFIG_RSA=y +``` + +`CONFIG_FIT_SIGNATURE` and `CONFIG_RSA` enable the verifier. `CONFIG_OF_SEPARATE` provides a separate control device tree blob (DTB), which will hold your public key. Legacy images, which carry no signature, are disabled in this configuration. + +You'll add the public key and boot command through build configuration, without patching U-Boot source. + +For QEMU, `qemu_arm64_defconfig` enables the unsigned legacy image format with `CONFIG_LEGACY_IMAGE_FORMAT=y`. The QEMU configuration fragment disables it and configures a control device tree containing your key. + +## Generate the public-key device-tree node + +Use a temporary FIT to make `mkimage` write the public key into a scratch DTB. You'll convert the key node to source for the U-Boot build. + +First, create an empty DTB and a placeholder payload in `$WORK`: + +```bash +printf '/dts-v1/;\n/ { };\n' | dtc -I dts -O dtb -o $WORK/scratch.dtb +printf 'payload' > $WORK/payload.bin +``` + +Create `key.its` using the same layout as `zephyr-a.its`, with the placeholder payload: + +```bash +cat > $WORK/key.its <<'EOF' +/dts-v1/; +/ { + description = "carrier used only to emit the public key for key-a"; + #address-cells = <1>; + images { + kernel-1 { + description = "placeholder payload"; + data = /incbin/("payload.bin"); + type = "kernel"; arch = "arm64"; os = "u-boot"; compression = "none"; + load = <0>; entry = <0>; + hash-1 { algo = "sha256"; }; + }; + }; + configurations { + default = "conf-1"; + conf-1 { + kernel = "kernel-1"; + signature-1 { algo = "sha256,rsa2048"; key-name-hint = "key-a"; sign-images = "kernel"; }; + }; + }; +}; +EOF +``` + +`mkimage` refuses the file without its two `description` properties, and `dtc` looks for `payload.bin` next to `key.its`. + +Sign the temporary FIT and convert the scratch DTB to `signature.dtsi`: + +```bash +$UBOOT_OUT/tools/mkimage -f $WORK/key.its -k $KEYS -K $WORK/scratch.dtb -r $WORK/key.itb +dtc -I dtb -O dts -q $WORK/scratch.dtb | sed '/^\/dts-v1\/;/d' > $WORK/signature.dtsi +``` + +`-K` writes the public key into `scratch.dtb`, and `-r` marks the key as required. The `sed` command removes the `/dts-v1/;` header so that the resulting `.dtsi` can be included after the main `.dts` header. + +Check the key name and required-signature setting: + +```bash +grep -E 'key-name-hint|required' $WORK/signature.dtsi +``` + +The expected output is: + +```output + required = "conf"; + key-name-hint = "key-a"; +``` + +Confirm that `key-name-hint` is `key-a` and `required` is `conf`. Leave out `key-b` so that U-Boot doesn't trust its signatures. + +The build incorporates `signature.dtsi` into the U-Boot control device tree. Use `mkimage -K` only with the scratch DTB. Adding the key directly to a finished `u-boot.dtb` would lose it when `make` rebuilds that file. + +## Write a boot command that fails closed + +An unverified boot command loads `zephyr.bin` and jumps to it. With your QEMU target values, the command looks like the following: + +```console +=> fatload virtio 0:1 0x40000000 zephyr.bin; dcache flush; icache flush; dcache off; icache off; go 0x40000000 +``` + +On the AM62L EVM, the same line reads `fatload mmc 1:1 0x82000000 zephyr.bin` and ends `go 0x82000000`. + +The command doesn't verify the image between `fatload` and `go`. Add `bootm` verification before the handover. The verified command chain uses the same QEMU target values and is split into lines for readability: + +```text +fatload virtio 0:1 0x48000000 ${fit} && +bootm start 0x48000000 && +bootm loados && +dcache flush && icache flush && dcache off && icache off && +go 0x40000000; +echo "*** REFUSED: Zephyr was NOT started ***" +``` + +The command chain loads, verifies, and starts Zephyr: + +- `fatload virtio 0:1 0x48000000 ${fit}` reads the FIT named in `${fit}` from `BOOT_DEV` to `FIT_ADDR`. `FIT_ADDR` is separate from `ZEPHYR_ADDR`, so copying the payload won't overwrite the FIT being read. +- `bootm start 0x48000000` parses the FIT, picks `conf-1`, verifies the RSA signature against `/signature/key-key-a`, then verifies the image hash. +- `bootm loados` copies the verified payload to its `load` address, `ZEPHYR_ADDR`. +- The cache commands flush and disable the caches. On arm64, `dcache off` also disables the memory management unit (MMU), preparing the state Zephyr expects at entry. +- `go 0x40000000` jumps to `ZEPHYR_ADDR` without performing verification. Using `bootm start` and `bootm loados` stops before the OS-specific boot code that a plain `bootm` would run. + +For QEMU, `BOOT_DEV` is `virtio 0:1`, the File Allocation Table (FAT) partition in the disk image. On the AM62L EVM, it's `mmc 1:1`, the first partition on the SD card. The board loads the FIT at `0x90000000` and copies Zephyr to `0x82000000`. + +Failing closed means stopping before Zephyr starts if any command fails. The `&&` operators enforce this condition: `a && b` runs `b` only if `a` succeeds. Separating the commands with `;` would allow execution to continue after a failed check. In this application, `go` doesn't return, so the refusal message prints only when an earlier command fails. + +Store the chain in the environment variable `zboot`. Three wrapper commands select which FIT it loads: + +| Variable | Definition | +|---|---| +| `zboot` | The chain, with `${fit}` as the file name | +| `a` | `setenv fit zephyr-a.itb; run zboot` | +| `b` | `setenv fit zephyr-b.itb; run zboot` | +| `t` | `setenv fit zephyr-tampered.itb; run zboot` | + +`a` boots the trusted image. `b` and `t` select the images that you'll create during the optional [wrong-key and tampered-image tests](/learning-paths/embedded-and-microcontrollers/zephyr-signed-fit-uboot-cortex-a/7-test-the-checks/). + +Autoboot runs `bootcmd` after a countdown. `CONFIG_PREBOOT` defines `zboot` and its wrappers before the countdown, making them available if you interrupt it. `CONFIG_BOOTCOMMAND="run a"` selects the trusted image by default. `CONFIG_BOOTDELAY=3` gives you three seconds to interrupt autoboot. + +The commands are compiled into U-Boot rather than stored on the card, where anyone could edit a boot script to skip the check. + +## Configure U-Boot + +Reset to the default configuration for your target before adding the custom settings: + +```bash +make -C $UBOOT_SRC O=$UBOOT_OUT CROSS_COMPILE=$CROSS CC="$UBOOT_CC" $UBOOT_DEFCONFIG +``` + +Append the boot-command settings to `.config`. The unquoted `EOF` delimiter lets the shell expand `$BOOT_DEV`, `$FIT_ADDR`, and `$ZEPHYR_ADDR`. Escaping `\${fit}` preserves `${fit}` for U-Boot to expand at runtime: + +```bash +cat >> $UBOOT_OUT/.config <}} + {{< tab header="QEMU" >}} +QEMU normally supplies the U-Boot device tree at runtime, so `CONFIG_DEVICE_TREE_INCLUDES` has no source tree to modify. Instead, you'll create a control device tree containing your key and pass it to the build. + +Add the following settings to enable that approach and disable unsigned legacy images. The settings also enable cache commands and keep the environment from being saved to flash: + +```bash +cat >> $UBOOT_OUT/.config <<'EOF' +CONFIG_OF_SEPARATE=y +# CONFIG_OF_BOARD is not set +# CONFIG_OF_OMIT_DTB is not set +# CONFIG_LEGACY_IMAGE_FORMAT is not set +CONFIG_CMD_CACHE=y +# CONFIG_ENV_IS_IN_FLASH is not set +CONFIG_ENV_IS_NOWHERE=y +EOF +``` + +`CONFIG_OF_BOARD` and `CONFIG_OF_OMIT_DTB` off make U-Boot carry a control device tree of its own, the one your key goes into. `CONFIG_LEGACY_IMAGE_FORMAT` off closes the older image format, which carries no signature. `CONFIG_CMD_CACHE` adds the `dcache` and `icache` commands that the boot command uses. `CONFIG_ENV_IS_NOWHERE` keeps the environment out of flash, so nothing saved at the prompt can replace your boot command. + {{< /tab >}} + {{< tab header="AM62L EVM" >}} +TI's U-Boot tree builds its control device tree from source. Set `CONFIG_DEVICE_TREE_INCLUDES` to include your public-key node in that build: + +```bash +cat >> $UBOOT_OUT/.config <}} +{{< /tabpane-normal >}} + +Run `olddefconfig` to resolve the configuration settings: + +```bash +make -C $UBOOT_SRC O=$UBOOT_OUT CROSS_COMPILE=$CROSS CC="$UBOOT_CC" olddefconfig +``` + +The output is similar to: + +```output +.config:2355:warning: override: reassigning to symbol DEVICE_TREE_INCLUDES +.config:2356:warning: override: reassigning to symbol USE_PREBOOT +.config:2358:warning: override: reassigning to symbol USE_BOOTCOMMAND +.config:2359:warning: override: reassigning to symbol BOOTCOMMAND +.config:2360:warning: override: reassigning to symbol BOOTDELAY +``` + +These override warnings are expected because your settings replace default values. The QEMU configuration can produce additional warnings for its target-specific overrides. + +## Build U-Boot + +Follow the instructions for your target to build U-Boot. + +{{< tabpane-normal >}} + {{< tab header="QEMU" >}} +Export the device tree for the QEMU machine that you'll boot later: + +```bash +qemu-system-aarch64 -machine virt,gic-version=3,dumpdtb=$WORK/qemu-virt.dtb -cpu cortex-a53 -m 1G -nographic +``` +This keeps the U-Boot drivers, console, and memory sizing consistent with the machine. QEMU writes the file and exits. + +Add your public-key node by decompiling the tree, appending `signature.dtsi`, and recompiling it: + +```bash +dtc -I dtb -O dts -q $WORK/qemu-virt.dtb > $WORK/uboot-control.dts +cat $WORK/signature.dtsi >> $WORK/uboot-control.dts +dtc -I dts -O dtb -q -o $WORK/uboot-control.dtb $WORK/uboot-control.dts +``` +The device tree compiler (`dtc`) merges the repeated root nodes, adding `/signature` to the exported tree. + +Build U-Boot with `EXT_DTB` pointing to your control device tree. Specify `u-boot.bin` and `u-boot.dtb` as the build targets: + +```bash +make -C $UBOOT_SRC O=$UBOOT_OUT CROSS_COMPILE=$CROSS CC="$UBOOT_CC" -j$(nproc) \ + EXT_DTB=$WORK/uboot-control.dtb u-boot.bin u-boot.dtb +``` +The `scripts/check-of.sh` script in U-Boot rejects the default `all` target for this machine, which normally receives its device tree from a prior stage. + +The build takes a couple of minutes. Check the two files: + +```bash +ls -l $UBOOT_OUT/u-boot.bin $UBOOT_OUT/u-boot.dtb +``` + +`u-boot.bin` is about 1.4 MB and ends with the bytes of `u-boot.dtb`, the control device tree with your key inside. + {{< /tab >}} + {{< tab header="AM62L EVM" >}} +TI's U-Boot tree uses binman, the U-Boot image packaging tool, to package the early stages. Point `BL1`, `BL31`, and `TEE` to the prebuilt TF-A and OP-TEE binaries in the SDK. Set `BINMAN_INDIRS` to the SDK directory containing TI's system firmware: + +```bash +make -C $UBOOT_SRC O=$UBOOT_OUT CROSS_COMPILE=$CROSS CC="$UBOOT_CC" -j$(nproc) \ + BL1=$PREBUILT/bl1.bin BL31=$PREBUILT/bl31.bin TEE=$PREBUILT/bl32.bin BINMAN_INDIRS=$PREBUILT +``` + +The build takes a few minutes. Check the three boot files: + +```bash +ls -l $UBOOT_OUT/tiboot3.bin $UBOOT_OUT/tispl.bin $UBOOT_OUT/u-boot.img +``` + +Expect sizes of about 0.2 MB for `tiboot3.bin`, 1.5 MB for `tispl.bin`, and 1.4 MB for `u-boot.img`. The build also writes the control device tree to `u-boot.dtb` in the same output directory. + {{< /tab >}} +{{< /tabpane-normal >}} + +## Check the embedded key and verify the signed FIT + +Decompile `$UBOOT_OUT/u-boot.dtb`, the control device tree that went into U-Boot, and print its `/signature` node: + +```bash +dtc -I dtb -O dts -q $UBOOT_OUT/u-boot.dtb | sed -n '/signature {/,/^\t};/p' +``` + +With the long values shortened, the output is similar to: + +```output + signature { + key-key-a { + required = "conf"; + algo = "sha256,rsa2048"; + rsa,r-squared = <...>; + rsa,modulus = <...>; + rsa,exponent = <0x00 0x10001>; + rsa,n0-inverse = <...>; + rsa,num-bits = <0x800>; + key-name-hint = "key-a"; + }; + }; +``` + +The `rsa,` properties contain the public key for `key-a`. Check that the node includes `required = "conf"`, which makes U-Boot require a valid configuration signature. + +Verify the trusted FIT on the host using the key in that DTB. `fit_check_sign` uses the verification code in U-Boot. `-f` selects the FIT, and `-k` selects the DTB containing the public key: + +```bash +$UBOOT_OUT/tools/fit_check_sign -f $FIT/zephyr-a.itb -k $UBOOT_OUT/u-boot.dtb +``` + +The output is similar to: + +```output +Verifying Hash Integrity for node 'conf-1'... sha256,rsa2048:key-a+ +Verified OK, loading images +## Loading kernel (any) from FIT Image at 7a44a7686000 ... + Using 'conf-1' configuration + Verifying Hash Integrity ... +sha256,rsa2048:key-a+ +OK + + Trying 'kernel-1' kernel subimage + Description: Zephyr RTOS image + Created: Fri Sep 11 19:14:00 2026 + Type: Kernel Image + Compression: uncompressed + Data Size: 58340 Bytes = 56.97 KiB = 0.06 MiB + Architecture: AArch64 + OS: U-Boot + Load Address: 0x82000000 + Entry Point: 0x82000000 + Hash algo: sha256 + Hash value: 1d1d375f14c3354579e9e5310986a69b831bcd1944cd6b35aa8e83632b658166 + Verifying Hash Integrity ... +sha256+ +OK + + Decrypting Data ... +OK + + Loading Kernel Image to 0 +## Loading fdt (any) from FIT Image at 7a44a7686000 ... + Using 'conf-1' configuration + Verifying Hash Integrity ... +sha256,rsa2048:key-a+ +OK + +Could not find subimage node type 'fdt' +## Loading ramdisk (any) from FIT Image at 7a44a7686000 ... + Using 'conf-1' configuration + Verifying Hash Integrity ... +sha256,rsa2048:key-a+ +OK + +Could not find subimage node type 'ramdisk' +Signature check OK +``` + +Check for `sha256,rsa2048:key-a+`, which confirms that the configuration signature verifies with `key-a`. The `+` indicates success. The final verdict is `Signature check OK`, and the exit code is zero. + +The tool also checks for device-tree and ramdisk subimages. The two `Could not find subimage node type` messages are expected because this FIT contains only Zephyr. + +## What you've accomplished and what's next + +You've built U-Boot with the public key for `key-a` and a boot command that starts Zephyr only after verification succeeds. The trusted FIT passes the host check against the embedded key. + +Next, you'll prepare the boot media and watch U-Boot verify and start Zephyr on your chosen target. diff --git a/content/learning-paths/embedded-and-microcontrollers/zephyr-signed-fit-uboot-cortex-a/6-boot-the-target.md b/content/learning-paths/embedded-and-microcontrollers/zephyr-signed-fit-uboot-cortex-a/6-boot-the-target.md new file mode 100644 index 0000000000..ce5a273c8c --- /dev/null +++ b/content/learning-paths/embedded-and-microcontrollers/zephyr-signed-fit-uboot-cortex-a/6-boot-the-target.md @@ -0,0 +1,290 @@ +--- +title: Boot the signed Zephyr FIT with U-Boot on QEMU or the TI AM62L EVM +description: Prepare boot media for QEMU or the TI AM62L EVM and confirm that U-Boot verifies the signed Zephyr FIT before starting the application. +weight: 7 + +### FIXED, DO NOT MODIFY +layout: learningpathall +--- + +## What your target boots from + +Open a terminal and load the environment file for your target: + +- QEMU: `source $HOME/zephyr-secure-boot/env-qemu.sh` +- AM62L evaluation module (EVM): `source $HOME/zephyr-secure-boot/env-am62l.sh` + +The AM62L boot ROM reads the first-stage file from a File Allocation Table (FAT) partition on the SD card. Start with TI's card image to preserve the expected layout. QEMU loads `u-boot.bin` from the command line, so its disk image needs only the signed Flattened Image Tree (FIT). + +On either target, the `fatload` of U-Boot reads `zephyr-a.itb` from the partition selected by `BOOT_DEV`. + +## Build the boot media + +Follow the instructions for your target to build the boot media. + +{{< tabpane-normal >}} + {{< tab header="QEMU" >}} +Create a 64 MiB disk image with one FAT16 partition starting at sector 2048. Use a master boot record (MBR) partition table and partition type `0x0e`, which specifies FAT16 with logical block addressing (LBA). These commands don't need root privileges: + +```bash +truncate -s 64M $WORK/disk.img +printf 'label: dos\nstart=2048, size=129024, type=e\n' | sfdisk $WORK/disk.img +mkfs.vfat --offset 2048 -F 16 -n ZEPHYRFIT $WORK/disk.img +``` + +Copy the signed FIT into the partition and list its contents. `BOOT_IMG` includes `@@1048576`, telling `mtools` that the volume starts 1 MiB into the image. `::` identifies the root directory of the volume: + +```bash +mcopy -o -i $BOOT_IMG $FIT/zephyr-a.itb :: +mdir -i $BOOT_IMG :: +``` + +The output is similar to: + +```output + Volume in drive : is ZEPHYRFIT + Volume Serial Number is B4EA-EA2A +Directory for ::/ + +zephyr-a itb 38742 2026-09-17 22:11 + 1 file 38 742 bytes + 65 871 872 bytes free +``` + +Confirm that the volume contains `zephyr-a.itb`. The partition is accessed as `virtio 0:1`, matching `BOOT_DEV` in `env-qemu.sh`. + {{< /tab >}} + {{< tab header="AM62L EVM" >}} +The boot ROM reads `tiboot3.bin` from the first FAT partition on the card. Preserve TI's FAT16 partition layout and replace its files. The supplied partition starts at sector 2048 and spans 262144 sectors. These values determine the extraction size in the command. + +Start from the `.wic.xz` that you downloaded. Extract the first 1 MiB, which holds the partition table, plus the 128 MiB partition into a new image file: + +```bash +xz -dc $WORK/tisdk-default-image.wic.xz | head -c $(( (2048+262144)*512 )) > $WORK/sdcard.img +``` + +The result is a 129 MiB image. Its partition table still lists TI's second partition, which points past the end of the extracted image. Remove that partition-table entry: + +```bash +sfdisk --delete $WORK/sdcard.img 2 +``` + +The output ends with: + +```output +The partition table has been altered. +Syncing disks. +``` + +Use `mtools` to remove TI's Linux files and copy your boot files and signed FIT into the FAT volume. `BOOT_IMG` includes `@@1048576`, selecting the volume 1 MiB into the image. `::` identifies its root directory: + +```bash +mdeltree -i $BOOT_IMG ::EFI +mdel -i $BOOT_IMG ::Image ::uEnv.txt +mcopy -o -i $BOOT_IMG $UBOOT_OUT/tiboot3.bin $UBOOT_OUT/tispl.bin $UBOOT_OUT/u-boot.img :: +mcopy -o -i $BOOT_IMG $FIT/zephyr-a.itb :: +mdir -i $BOOT_IMG :: +``` + +The output is similar to: + +```output + Volume in drive : is boot + Volume Serial Number is 40D2-DF89 +Directory for ::/ + +tiboot3 bin 216538 2026-09-15 16:49 +tispl bin 1467919 2026-09-15 16:49 +u-boot img 1422787 2026-09-15 16:49 +zephyr-a itb 60198 2026-09-15 16:49 + 4 files 3 167 442 bytes + 130 766 848 bytes free +``` + +Confirm that the volume contains the three boot files and `zephyr-a.itb`. + {{< /tab >}} +{{< /tabpane-normal >}} + +## Start the target + +Follow the instructions for your target to start the target. + +{{% notice Warning %}} +The AM62L EVM steps write to a whole disk with `dd`. Replace `/dev/sdX` with your card – for example, `/dev/sdb` — rather than a partition such as `/dev/sdb1`. `dd` erases everything on the target, so a wrong device name erases the wrong disk. +{{% /notice %}} + +{{< tabpane-normal >}} + {{< tab header="QEMU" >}} +Start QEMU with the U-Boot binary and disk image that you prepared. `-bios` selects the binary, and `-nographic` displays the serial console in your terminal. The `-drive` and `-device` options attach `disk.img` as the virtio device containing `virtio 0:1`: + +```bash +qemu-system-aarch64 -machine virt,gic-version=3 -cpu cortex-a53 -m 1G -nographic -no-reboot \ + -bios $UBOOT_OUT/u-boot.bin \ + -drive if=none,file=$WORK/disk.img,format=raw,id=hd0 \ + -device virtio-blk-device,drive=hd0 +``` + +Use the same `-machine`, `-cpu`, and `-m` settings as the device-tree export used to [build U-Boot](/learning-paths/embedded-and-microcontrollers/zephyr-signed-fit-uboot-cortex-a/5-build-uboot/). U-Boot should begin printing in the terminal. To exit QEMU, press **Ctrl+A**, then **X**. + {{< /tab >}} + {{< tab header="AM62L EVM" >}} +Run `lsblk` before and after inserting the micro-SD card. Identify the newly listed disk and confirm its size matches the card. Replace `/dev/sdX` with the device name of that disk and write the image to the whole card: + +```bash +sudo dd if=$WORK/sdcard.img of=/dev/sdX bs=4M conv=fsync status=progress +``` + +If you transfer the prepared image to Windows or macOS, you can write `sdcard.img` with balenaEtcher. Alternatively, write TI's unchanged `.wic.xz` image to the card. Open its first partition, remove `Image`, `uEnv.txt`, and the `EFI` directory, then copy in the three boot files and `zephyr-a.itb`. + +Set **SW3** using the table. This is the reduced pin-count setting from the [AM62L EVM user guide](https://www.ti.com/lit/pdf/SPRUJG8), where the ROM ignores **SW2** and **SW4**. The **ON** position is toward the **ON** label on the switch bank. + +| Switch | 1 | 2 | 3 | 4 | +|---|---|---|---|---| +| **SW3** | OFF | ON | OFF | ON | + +Connect the micro-USB port **J7** to your host. It creates four serial ports. The console is usually `/dev/ttyUSB0` and uses 115200 baud. Open the console: + +```bash +picocom -b 115200 /dev/ttyUSB0 +``` + +If `picocom` reports a permission error, add your user to the `dialout` group with `sudo usermod -aG dialout $USER`, then log out and back in. To leave `picocom` later, press **Ctrl+A**, then **Ctrl+X**. + +Move the SD card to the board, then connect a USB-C Power Delivery (PD) supply to **J17** or **J19**. Boot messages should appear in the console. + {{< /tab >}} +{{< /tabpane-normal >}} + +In QEMU, the log starts with U-Boot because no TF-A or Secondary Program Loader (SPL) stage runs first. The messages `Bloblist at 0 not found (err=-2)` and `Warning: Unexpected devicetree source (not from a prior stage)` are consistent with this setup. `Loading Environment from nowhere... OK` indicates that U-Boot is using its built-in environment. + +The AM62L EVM also prints messages from its early firmware stages. Its log up to the autoboot countdown is similar to the following example. Your dates, version strings, and countdown will differ: + +```output +NOTICE: Booting Trusted Firmware +NOTICE: BL1: v2.14.0(release):12.00.00.06-28-gb54338ab6 +NOTICE: BL1: Built : 09:15:06, Jul 8 2026 +NOTICE: BL1: dram_class: 11 +NOTICE: BL1: dram_size: 0x80000000 +NOTICE: DDR init done +NOTICE: ENTERING WFI - end of bl1 +NOTICE: BL31: v2.14.0(release):12.00.00.06-28-gb54338ab6 +NOTICE: BL31: Built : 09:15:06, Jul 8 2026 +NOTICE: SYSFW ABI: 4.0 (firmware rev 0x000c '12.1.2--v12.01.02 (Clever Cat)') +ERROR: Agent 0 Protocol 0x10 Message 0x7: not supported + +U-Boot SPL 2026.01-ti-g5fb294342321 (Jul 09 2026 - 22:23:09 +0000) +SPL initial stack usage: 1920 bytes +Trying to boot from MMC2 +Authentication passed +Authentication passed +ERROR: Agent 0 Protocol 0x10 Message 0x7: not supported + + +U-Boot 2026.01-g5fb294342321 (Sep 11 2026 - 18:13:32 +0200) + +SoC: AM62LX SR1.1 HS-FS +Model: Texas Instruments AM62L3 Evaluation Module +DRAM: 2 GiB +ERROR: Agent 0 Protocol 0x10 Message 0x7: not supported +Core: 89 devices, 34 uclasses, devicetree: separate +MMC: mmc@fa10000: 0, mmc@fa00000: 1 +Loading Environment from nowhere... OK +In: serial@2800000 +Out: serial@2800000 +Err: serial@2800000 +Net: eth0: ethernet@8000000port@1, eth1: ethernet@8000000port@2 +Hit any key to stop autoboot: 3 +``` + +Use the following messages to identify the boot stages and selected devices: + +- `NOTICE: BL1:` and `BL31:` identify TF-A running from `tiboot3.bin` and `tispl.bin`. +- `Authentication passed` reports successful authentication by TI's security firmware. +- `SoC: AM62LX SR1.1 HS-FS` identifies the development state of the system on chip (SoC). +- `MMC:` lists the eMMC as device zero and the SD card as device one. + +The `Agent 0 Protocol 0x10 Message 0x7: not supported` errors appear in this successful boot log. They don't prevent Zephyr from starting. + +This log comes from a board running TI's prebuilt first two stages, so its `U-Boot SPL` line shows TI's build date instead of yours. + +## Watch the trusted image boot + +After the three-second countdown, autoboot runs `run a`, the trusted-image command built into U-Boot. The following example output shows the AM62L EVM. Your timings and hash values will differ. The output is similar to: + +```output +60198 bytes read in 1 ms (57.4 MiB/s) +## Loading kernel (any) from FIT Image at 90000000 ... + Using 'conf-1' configuration + Verifying Hash Integrity ... sha256,rsa2048:key-a+ OK + Trying 'kernel-1' kernel subimage + Description: Zephyr RTOS image + Created: 2026-09-11 16:14:00 UTC + Type: Kernel Image + Compression: uncompressed + Data Start: 0x900000e8 + Data Size: 58340 Bytes = 57 KiB + Architecture: AArch64 + OS: U-Boot + Load Address: 0x82000000 + Entry Point: 0x82000000 + Hash algo: sha256 + Hash value: 1d1d375f14c3354579e9e5310986a69b831bcd1944cd6b35aa8e83632b658166 + Verifying Hash Integrity ... sha256+ OK + Loading Kernel Image to 82000000 +## Starting application at 0x82000000 ... +*** Booting Zephyr OS build 4.4.2 *** +Secondary CPU core 1 (MPID:0x1) is up + +################################################ +# # +# Hello from ZEPHYR IMAGE A # +# signed with key-a (TRUSTED by U-Boot) # +# # +################################################ + +board : am62l_evm/am62l3/a53 +arch : arm64 +started by : U-Boot 'go' after FIT signature verification +this image was verified by U-Boot before it ran. +``` + +Confirm verification and startup using the following messages: + +- `sha256,rsa2048:key-a+ OK` confirms that the signature verifies with the public key embedded in U-Boot. +- `sha256+ OK` confirms that the payload hash matches. +- `Loading Kernel Image to 82000000` shows `bootm loados` copying Zephyr to its link address. +- `## Starting application at 0x82000000 ...` shows `go` handing control to Zephyr. + +Zephyr then prints its boot banner and `Hello from ZEPHYR IMAGE A`. On the AM62L EVM, `Secondary CPU core 1 (MPID:0x1) is up` also reports startup of the second core. + +QEMU uses different load addresses and image sizes. For QEMU, expect `FIT Image at 48000000`, `Data Size: 37040 Bytes`, `Loading Kernel Image to 40000000`, and `board : qemu_cortex_a53/qemu_cortex_a53`. Its board configuration starts one core, so there's no secondary-core startup message. + +## Troubleshoot boot and console output + +Consider the following guidance to troubleshoot issues with your target. + +{{< tabpane-normal >}} + {{< tab header="QEMU" >}} +Check the symptom and try the corresponding step: + +1. Nothing prints, or startup hangs after the banner: Check that the control device tree matches the machine. Rebuild it from a fresh `dumpdtb` with the same `-machine`, `-cpu`, and `-m` used to start QEMU. +2. `Failed to load 'zephyr-a.itb'`: U-Boot found the volume but not the file. List it on the host with `mdir -i $BOOT_IMG ::`. +3. `** Bad device specification virtio 0 **`: Check the partition type with `sfdisk -l $WORK/disk.img`. If it isn't `0x0e`, rebuild the disk image with the specified type. +4. `Unknown command 'dcache'`: `CONFIG_CMD_CACHE` didn't make it into `.config`. Add it and build U-Boot again. + {{< /tab >}} + {{< tab header="AM62L EVM" >}} +If the terminal stays empty, check the console connection and whether the ROM loads `tiboot3.bin`: + +1. Try all four serial ports that **J7** creates. The console isn't always the first one. +2. Check **SW3**, or switch to the full pin-count setting, which uses all three switch banks. See BOOTMODE `0x0E43` in the AM62L EVM User Guide. +3. Check that the first partition of the card is still TI's FAT16 partition, not reformatted. +4. Flash TI's unchanged `.wic.xz` to a card and boot it. If that also produces no output, check the boot switches, serial port, and power supply before investigating your custom boot files. +5. If TI's card boots but yours never shows the SPL banner, copy TI's prebuilt first two stages over yours with `mcopy -o -i $BOOT_IMG $PREBUILT/tiboot3.bin $PREBUILT/tispl.bin ::`. Then, write the card again with the same `dd`. `u-boot.img` still carries the key and the boot command. + + {{< /tab >}} +{{< /tabpane-normal >}} + +If Zephyr prints its boot banner but no application output, check that the [Zephyr build configuration](/learning-paths/embedded-and-microcontrollers/zephyr-signed-fit-uboot-cortex-a/3-build-zephyr/) includes `CONFIG_ARMV8_A_NS=y`. + +## What you've accomplished and what's next + +You've prepared the boot media and watched U-Boot verify the FIT signed with `key-a` before starting Zephyr. + +Next, you can run the optional [wrong-key and tampered-image tests](/learning-paths/embedded-and-microcontrollers/zephyr-signed-fit-uboot-cortex-a/7-test-the-checks/) to confirm that verification failures prevent startup. You can also skip to review [the trust boundary and the additional work needed for production](/learning-paths/embedded-and-microcontrollers/zephyr-signed-fit-uboot-cortex-a/8-production/). diff --git a/content/learning-paths/embedded-and-microcontrollers/zephyr-signed-fit-uboot-cortex-a/7-test-the-checks.md b/content/learning-paths/embedded-and-microcontrollers/zephyr-signed-fit-uboot-cortex-a/7-test-the-checks.md new file mode 100644 index 0000000000..7f01b64bc3 --- /dev/null +++ b/content/learning-paths/embedded-and-microcontrollers/zephyr-signed-fit-uboot-cortex-a/7-test-the-checks.md @@ -0,0 +1,288 @@ +--- +title: (Optional) Test that U-Boot refuses a wrong key and a tampered image +description: "Optionally test on the host and target that U-Boot rejects Zephyr FIT images signed with an untrusted key or modified after signing." +weight: 8 + +### FIXED, DO NOT MODIFY +layout: learningpathall +--- + +## Test rejection of untrusted and tampered images + +The following tests are optional. Create one Flattened Image Tree (FIT) signed with an untrusted key and another modified after signing. Check both on the host, then confirm that U-Boot refuses to start them on your target. The `b` and `t` commands are already built into U-Boot to select these images. + +Open a terminal and load the environment file for your target: + +- QEMU: `source $HOME/zephyr-secure-boot/env-qemu.sh` +- AM62L evaluation module (EVM): `source $HOME/zephyr-secure-boot/env-am62l.sh` + +## Build a second Zephyr image + +Give the wrong-key image a distinct banner so that you can identify it if it unexpectedly starts. + +In Workbench for Zephyr, select **Add Application**. Use the same workspace, toolchain, board, and sample as the [trusted image](/learning-paths/embedded-and-microcontrollers/zephyr-signed-fit-uboot-cortex-a/3-build-zephyr/). Enter `hello_b` as the **Project Name**. Copy `prj.conf` and `src/main.c` from `hello`, then replace these two banner lines: + +```c + printk("# Hello from ZEPHYR IMAGE B #\n"); + printk("# signed with key-b (NOT trusted by U-Boot) #\n"); +``` + +In the **Applications** view, open the context menu for `hello_b` and select **Build**. The resulting binary is `$WORK/zephyrproject/applications/hello_b/build/primary/zephyr/zephyr.bin`. + +## Sign the second image with the untrusted key + +Use `key-b`, the second key pair that you created during [FIT signing](/learning-paths/embedded-and-microcontrollers/zephyr-signed-fit-uboot-cortex-a/4-sign-zephyr/). Copy the FIT source with `sed`, replacing `key-a` with `key-b` and the `hello` application path with `hello_b`. + +Then, sign the new FIT: + +```bash +sed 's/key-a/key-b/g; s#/hello/#/hello_b/#' $FIT/zephyr-a.its > $FIT/zephyr-b.its +$UBOOT_OUT/tools/mkimage -f $FIT/zephyr-b.its -k $KEYS $FIT/zephyr-b.itb +``` + +The output is similar to: + +```output +FIT description: Zephyr RTOS, signed with key-b +Created: Fri Sep 11 19:14:00 2026 + Image 0 (kernel-1) + Description: Zephyr RTOS image + Created: Fri Sep 11 19:14:00 2026 + Type: Kernel Image + Compression: uncompressed + Data Size: 58340 Bytes = 56.97 KiB = 0.06 MiB + Architecture: AArch64 + OS: U-Boot + Load Address: 0x82000000 + Entry Point: 0x82000000 + Hash algo: sha256 + Hash value: 42bf9a7ae3d4fe62f6acc09a1ca7abbd803d26c4b12f0dff717acce06e2b6a9b + Default Configuration: 'conf-1' + Configuration 0 (conf-1) + Description: Zephyr on Cortex-A + Kernel: kernel-1 + Sign algo: sha256,rsa2048:key-b + Sign value: 6137633e965eee69ee2cc27b1a904330cf056e1b9ec2371abaf26865763117ed... + Timestamp: Fri Sep 11 19:14:00 2026 +Signature written to '/home/user/zephyr-secure-boot/fit/zephyr-b.itb', node '/configurations/conf-1/signature-1' +``` + +Check for `Sign algo: sha256,rsa2048:key-b`. The `Hash value` differs from that of image A because the application banner changed. This FIT has a signature from `key-b`, but U-Boot trusts only `key-a` and must reject it. + +## Make a tampered copy of the trusted image + +Copy the trusted `.itb` and overwrite one byte inside the payload: + +```bash +cp $FIT/zephyr-a.itb $FIT/zephyr-tampered.itb +printf '\xff' | dd of=$FIT/zephyr-tampered.itb bs=1 seek=32768 conv=notrunc 2>/dev/null +``` + +In the example FITs, offset 32768 (`0x8000`) falls inside the Zephyr payload rather than the FIT metadata. The payload begins a few hundred bytes into the file and is tens of kilobytes long. + +Check that the copy now differs from the original: + +```bash +cmp $FIT/zephyr-a.itb $FIT/zephyr-tampered.itb +``` + +The output is similar to: + +```output +/home/user/zephyr-secure-boot/fit/zephyr-a.itb /home/user/zephyr-secure-boot/fit/zephyr-tampered.itb differ: byte 32769, line 94 +``` + +If `cmp` prints nothing, the selected byte was already `0xff`. Choose another offset inside the payload, such as `seek=32800`, then repeat the `dd` and `cmp` commands. + +Expect the signature check to pass and the payload hash check to fail. The signature covers the unchanged configuration and `hash-1` node. The hash covers the payload bytes that you modified. + +## Check both images on the host + +Your timings, hash values, and `FIT Image at` addresses will differ from these examples. Run `fit_check_sign` with the control device tree from your [U-Boot build](/learning-paths/embedded-and-microcontrollers/zephyr-signed-fit-uboot-cortex-a/5-build-uboot/). Check the wrong-key image first: + +```bash +$UBOOT_OUT/tools/fit_check_sign -f $FIT/zephyr-b.itb -k $UBOOT_OUT/u-boot.dtb +``` + +The output is similar to: + +```output +Verifying Hash Integrity for node 'conf-1'... sha256,rsa2048:key-b- + error! +Verification failed for '(null)' hash node in 'conf-1' config node +Failed to verify required signature 'key-key-a' +Signature check bad (error 1) +``` + +Check for `sha256,rsa2048:key-b-` and `Failed to verify required signature 'key-key-a'`. The final verdict, `Signature check bad (error 1)`, confirms rejection. The exit code is one. + +Then, check the tampered image: + +```bash +$UBOOT_OUT/tools/fit_check_sign -f $FIT/zephyr-tampered.itb -k $UBOOT_OUT/u-boot.dtb +``` + +The output is similar to: + +```output +Verifying Hash Integrity for node 'conf-1'... sha256,rsa2048:key-a+ +Verified OK, loading images +## Loading kernel (any) from FIT Image at 7ae29b84a000 ... + Using 'conf-1' configuration + Verifying Hash Integrity ... +sha256,rsa2048:key-a+ +OK + + Trying 'kernel-1' kernel subimage + Description: Zephyr RTOS image + Created: Fri Sep 11 19:14:00 2026 + Type: Kernel Image + Compression: uncompressed + Data Size: 58340 Bytes = 56.97 KiB = 0.06 MiB + Architecture: AArch64 + OS: U-Boot + Load Address: 0x82000000 + Entry Point: 0x82000000 + Hash algo: sha256 + Hash value: 1d1d375f14c3354579e9e5310986a69b831bcd1944cd6b35aa8e83632b658166 + Verifying Hash Integrity ... +sha256 error! +Bad hash value for 'hash-1' hash node in 'kernel-1' image node +Bad Data Hash + +## Loading fdt (any) from FIT Image at 7ae29b84a000 ... + Using 'conf-1' configuration + Verifying Hash Integrity ... +sha256,rsa2048:key-a+ +OK + +Could not find subimage node type 'fdt' +## Loading ramdisk (any) from FIT Image at 7ae29b84a000 ... + Using 'conf-1' configuration + Verifying Hash Integrity ... +sha256,rsa2048:key-a+ +OK + +Could not find subimage node type 'ramdisk' +Signature check bad (error 1) +``` + +Check that `sha256,rsa2048:key-a+` reports a valid signature, followed by `sha256 error!` and `Bad hash value for 'hash-1'`. The final verdict is `Signature check bad (error 1)`, and the exit code is one. The modified payload fails verification even though the configuration signature passes. + +## Add the two images to the boot media + +Copy both test FITs into the `BOOT_IMG` volume used to [prepare the boot media](/learning-paths/embedded-and-microcontrollers/zephyr-signed-fit-uboot-cortex-a/6-boot-the-target/), then list its contents: + +```bash +mcopy -o -i $BOOT_IMG $FIT/zephyr-b.itb $FIT/zephyr-tampered.itb :: +mdir -i $BOOT_IMG :: +``` + +The output is similar to: + +```output + Volume in drive : is boot + Volume Serial Number is 40D2-DF89 +Directory for ::/ + +tiboot3 bin 216538 2026-09-15 16:49 +tispl bin 1467919 2026-09-15 16:49 +u-boot img 1422787 2026-09-15 16:49 +zephyr-a itb 60198 2026-09-15 16:49 +zephyr-b itb 60198 2026-09-15 16:49 +ZEPHYR~1 ITB 60198 2026-09-15 16:49 zephyr-tampered.itb + 6 files 3 287 838 bytes + 130 643 968 bytes free +``` + +This example lists the six files of the AM62L EVM: three boot files and three FITs. The QEMU disk image contains only the three FITs. + +{{< tabpane-normal >}} + {{< tab header="QEMU" >}} +Restart QEMU with the same command used to boot the trusted image. QEMU reads the updated `disk.img` directly. No physical media needs to be written. + {{< /tab >}} + {{< tab header="AM62L EVM" >}} +Write the updated image to the card. Confirm the device name of the card before replacing `/dev/sdX`. `dd` overwrites the entire selected disk: + +```bash +sudo dd if=$WORK/sdcard.img of=/dev/sdX bs=4M conv=fsync status=progress +``` + +Move the card to the board, open the console with `picocom`, and power the board on. + {{< /tab >}} +{{< /tabpane-normal >}} + +## Run the wrong-key test + +Press a key during the three-second countdown to stop autoboot. If you miss the countdown, U-Boot starts image A. Start the target again and try again. At the prompt, run the wrong-key test: + +```console +=> run b +``` + +The expected output is: + +```output +60198 bytes read in 1 ms (57.4 MiB/s) +## Loading kernel (any) from FIT Image at 90000000 ... + Using 'conf-1' configuration + Verifying Hash Integrity ... sha256,rsa2048:key-b- error! +Verification failed for '' hash node in 'conf-1' config node +Failed to verify required signature 'key-key-a' +Bad Data Hash +ERROR -2: can't get kernel image! +*** REFUSED: Zephyr was NOT started *** +``` + +Check for `sha256,rsa2048:key-b- error!` and `Failed to verify required signature 'key-key-a'`. The `-` indicates failure, because the signature of the image doesn't satisfy the required `key-a` check. + +When `bootm start` fails, the `&&` chain skips `go` and prints `*** REFUSED: Zephyr was NOT started ***`. Confirm that no Zephyr banner appears. `Bad Data Hash` and `ERROR -2: can't get kernel image!` can appear for either rejection test. Use the preceding messages to identify which check failed. + +## Run the tampered-image test + +At the U-Boot prompt, run the tampered-image test: + +```console +=> run t +``` + +The expected output is: + +```output +60198 bytes read in 2 ms (28.7 MiB/s) +## Loading kernel (any) from FIT Image at 90000000 ... + Using 'conf-1' configuration + Verifying Hash Integrity ... sha256,rsa2048:key-a+ OK + Trying 'kernel-1' kernel subimage + Description: Zephyr RTOS image + Created: 2026-09-11 16:14:00 UTC + Type: Kernel Image + Compression: uncompressed + Data Start: 0x900000e8 + Data Size: 58340 Bytes = 57 KiB + Architecture: AArch64 + OS: U-Boot + Load Address: 0x82000000 + Entry Point: 0x82000000 + Hash algo: sha256 + Hash value: 1d1d375f14c3354579e9e5310986a69b831bcd1944cd6b35aa8e83632b658166 + Verifying Hash Integrity ... sha256 error! +Bad hash value for 'hash-1' hash node in 'kernel-1' image node +Bad Data Hash +ERROR -2: can't get kernel image! +*** REFUSED: Zephyr was NOT started *** +``` + +Check that `sha256,rsa2048:key-a+ OK` reports a valid signature. `sha256 error!` and `Bad hash value for 'hash-1'` indicate that the payload no longer matches its stored hash. Confirm that `*** REFUSED: Zephyr was NOT started ***` appears without a Zephyr banner. + +{{% notice Note %}} +If `run b` or `run t` starts Zephyr, verification isn't preventing startup. Check for a `;` where `&&` belongs in `CONFIG_PREBOOT`, and confirm that `signature.dtsi` includes `required = "conf"`. Compare both with the [U-Boot build settings](/learning-paths/embedded-and-microcontrollers/zephyr-signed-fit-uboot-cortex-a/5-build-uboot/), rebuild, and update the boot media. +{{% /notice %}} + +To boot the trusted image again, type `run a`. + +## What you've accomplished and what's next + +You've confirmed that U-Boot rejects the wrong-key image at the signature check and the tampered image at the payload hash check. Neither reaches `go` or starts Zephyr. + +Next, you'll review what these tests establish and what a production device still needs. diff --git a/content/learning-paths/embedded-and-microcontrollers/zephyr-signed-fit-uboot-cortex-a/8-production.md b/content/learning-paths/embedded-and-microcontrollers/zephyr-signed-fit-uboot-cortex-a/8-production.md new file mode 100644 index 0000000000..bca39e226c --- /dev/null +++ b/content/learning-paths/embedded-and-microcontrollers/zephyr-signed-fit-uboot-cortex-a/8-production.md @@ -0,0 +1,84 @@ +--- +title: Review the Zephyr FIT verification boundary and production needs +description: Review the trust boundary for Zephyr FIT verification in U-Boot and the key provisioning, boot controls, and release signing needed for production. +weight: 9 + +### FIXED, DO NOT MODIFY +layout: learningpathall +--- + +## Understand the verification boundary + +You've configured U-Boot to verify the Zephyr payload, the last link in the boot chain. U-Boot reads `zephyr-a.itb` and verifies `conf-1` with the embedded public key for `key-a`. It checks the payload hash before jumping to `ZEPHYR_ADDR`. + +If you ran the optional [refusal tests](/learning-paths/embedded-and-microcontrollers/zephyr-signed-fit-uboot-cortex-a/7-test-the-checks/), you also confirmed two rejection cases. The wrong-key Flattened Image Tree (FIT) fails with `Failed to verify required signature 'key-key-a'`. The modified payload fails with `Bad hash value for 'hash-1'`. Neither starts Zephyr. + +The earlier stages aren't authenticated against your key in either setup. QEMU loads `u-boot.bin` directly, without a boot ROM, security firmware, or eFuses to authenticate it. + +The AM62L evaluation module (EVM) has those earlier stages, but you've left it in TI's High Security, Field Securable (HS-FS) development state. In this state, the ROM and vendor firmware check boot files but accept any signing key. The U-Boot banner identifies it as `SoC: AM62LX SR1.1 HS-FS`. Provisioning your key into the one-time-programmable eFuses moves the device to High Security, Security Enforced (HS-SE). + +On the EVM, the trusted public key is inside `u-boot.img` on an unprotected File Allocation Table (FAT) partition. Anyone who can replace that file can replace the key and boot command. Production needs an authenticated U-Boot and controls that prevent bypassing its verification step. + +## What you need for production + +For production, ensure that you complete the following steps: + +### Fuse your key and move to the production state + +TI's one-time-programmable (OTP) Keywriter tool, called Keywriter Lite on the AM62L, burns the public-key hash into the eFuses and moves the chip to HS-SE. You then re-sign `tiboot3.bin`, `tispl.bin`, and `u-boot.img` with that key. + +The ROM and security firmware then require boot files signed with your key, including U-Boot. This authenticates the U-Boot image containing the FIT public key. Key provisioning is vendor-specific and isn't covered in these steps. + +### Keep the environment built in + +`bootcmd` selects the autoboot command. `preboot` defines `zboot` and the `a`, `b`, and `t` wrappers before autoboot starts. + +Both builds use `CONFIG_ENV_IS_NOWHERE=y`: you added it for QEMU, and the AM62L default configuration supplies it. This prevents `saveenv` from writing a persistent environment, so boot settings are loaded from the binary that you built. Check that the `.config` for your board includes the same setting. + +{{% notice Warning %}} +Don't enable `CONFIG_ENV_IS_IN_FAT` for this boot configuration. It stores the environment in `uboot.env` on the boot media. A saved environment can override `bootcmd` and `preboot`, allowing someone with write access to the card to bypass verification. +{{% /notice %}} + +### Lock the console + +With `CONFIG_BOOTDELAY=3`, someone with serial-console access can interrupt autoboot and run `fatload` and `go` without verification. Set `CONFIG_BOOTDELAY=-2` to remove the delay and keyboard check, or use `CONFIG_AUTOBOOT_KEYED` to require a known string to interrupt autoboot. + +These settings cover only the countdown. If `bootcmd` returns after rejecting an image, U-Boot still opens the `=>` prompt. In a production build, add `reset` after the refusal `echo` in `zboot`. This ensures that a failed boot restarts the board instead of opening the console. + +### Protect production keys and sign during release + +You used `sha256,rsa2048`. The boot chain of the AM62L uses `sha512,rsa4096`. To use the latter for your FIT: + +- Generate keys with `rsa_keygen_bits:4096`. +- Set `algo = "sha512,rsa4096"` in both `zephyr-a.its` and `key.its`. +- Confirm that `$UBOOT_OUT/.config` includes `CONFIG_SHA512=y`, adding it to the configuration fragment if needed. + +Generate production keys in a hardware security module (HSM), or at least away from the build host. Sign during the release process and restrict private-key access to that process. + +## Adapt the workflow to another Cortex-A board + +If you want to use another Cortex-A board, start by updating the target values and paths in your environment file: + +- `BOARD` is the Zephyr board identifier, from `west boards` or the Workbench board list. The board needs the two settings from [Build a Zephyr image that U-Boot can start](/learning-paths/embedded-and-microcontrollers/zephyr-signed-fit-uboot-cortex-a/3-build-zephyr/): the Non-secure world and the arm64 image header. +- `ZEPHYR_ADDR` is the start of the memory node that the device tree of the board selects as `zephyr,sram`. It's the link address of Zephyr, the FIT `load` and `entry`, and the `go` target, so one value serves all three. +- `FIT_ADDR` is any free address clear of `ZEPHYR_ADDR` and of the firmware regions. Otherwise, `bootm loados` copies Zephyr over the FIT it's still reading. +- `BOOT_DEV` is the U-Boot device and partition that hold the files. `mmc 1:1` is the first partition of the SD card on the AM62L EVM, while `mmc 0` is the eMMC. `mmc list` at the U-Boot prompt shows the numbering of your board. +- `UBOOT_DEFCONFIG` selects the U-Boot default configuration of the board. Check for `CONFIG_FIT=y`, `CONFIG_FIT_SIGNATURE=y`, and `CONFIG_RSA=y`. Add missing settings to the [U-Boot configuration fragment](/learning-paths/embedded-and-microcontrollers/zephyr-signed-fit-uboot-cortex-a/5-build-uboot/). Disable legacy images with `# CONFIG_LEGACY_IMAGE_FORMAT is not set`. +- `UBOOT_SRC`, `CROSS`, and `UBOOT_CC` point at the U-Boot source and the compiler that builds it. `SDK`, `PREBUILT`, and `SYSROOT` are added when they come from a vendor SDK, as on the AM62L. +- `BOOT_IMG` is the boot volume that `mcopy` writes into: the card image on the EVM, the disk image in QEMU, each with the `@@` offset of its FAT partition. + +Also adapt the U-Boot build command and boot-media preparation. The build inputs depend on the target: `EXT_DTB` for QEMU, or `BL1`, `BL31`, `TEE`, and `BINMAN_INDIRS` for the AM62L EVM. Output names also vary, often including `u-boot.img` or `u-boot.itb` and a Secondary Program Loader (SPL) file. Follow the requirements of the target for media layout, boot switches, and console access. + +Check how the target obtains its control device tree. `CONFIG_DEVICE_TREE_INCLUDES` adds the key node when the tree is built from source with `CONFIG_OF_SEPARATE=y` or `CONFIG_OF_EMBED=y`, as on the AM62L EVM. + +For a board using a tree from a prior stage (`CONFIG_OF_BOARD=y`), follow the QEMU approach: merge `signature.dtsi` into an exported tree and pass it to the build with `EXT_DTB=`. + +Reuse the FIT signing and verification workflow, adapting the target values in the FIT source and boot command as needed. + +## What you've accomplished + +You've established verification of the Zephyr payload and learned how you can extend the workflow to production and to another Cortex-A board. + +Before using this approach in production, authenticate the earlier boot stages. Also protect the boot environment and console and move signing into a controlled release process. + +To sign your own application for the same target, point `data` in `zephyr-a.its` to its `zephyr.bin` and sign the FIT again. To use another Cortex-A board, adapt the environment, build inputs, and boot media using the checklist in this section. diff --git a/content/learning-paths/embedded-and-microcontrollers/zephyr-signed-fit-uboot-cortex-a/_index.md b/content/learning-paths/embedded-and-microcontrollers/zephyr-signed-fit-uboot-cortex-a/_index.md new file mode 100644 index 0000000000..85db90bd15 --- /dev/null +++ b/content/learning-paths/embedded-and-microcontrollers/zephyr-signed-fit-uboot-cortex-a/_index.md @@ -0,0 +1,127 @@ +--- +title: Boot a signed Zephyr image with U-Boot on Arm Cortex-A + +description: Sign a Zephyr FIT image and configure U-Boot to verify its signature and payload hash before booting on Arm Cortex-A in QEMU or on a TI AM62L EVM. + +minutes_to_complete: 120 + +who_is_this_for: This is an advanced topic for embedded developers who boot Zephyr from U-Boot on an Arm Cortex-A processor and want U-Boot to verify the Zephyr image before starting it. + +learning_objectives: + - Identify where U-Boot verifies Zephyr in the Arm Cortex-A boot chain. + - Build and sign a Flattened Image Tree (FIT) containing Zephyr, and embed the public key in U-Boot without changing its source. + - Configure U-Boot to verify Zephyr before booting, and optionally test rejection of wrong-key and tampered images. + - Identify the verification boundary in QEMU and on a development board, and how fusing your key extends trust in production. + +prerequisites: + - One of two targets - QEMU, which needs no hardware, or a Texas Instruments (TI) [AM62L EVM](https://www.ti.com/tool/TMDS62LEVM) with accessories to power it, write an SD card, and attach a serial console + - A Linux host running Ubuntu 22.04 or 24.04, with about 20 GB of free disk space; QEMU runs on x86_64 or arm64, while the AM62L EVM needs x86_64 for the TI SDK + - Visual Studio Code with the [Workbench for Zephyr extension](https://marketplace.visualstudio.com/items?itemName=Ac6.zephyr-workbench) installed + - Basic knowledge of U-Boot and the Linux command line + +# START generated_summary_faq +generated_summary_faq: + template_version: summary-faq-v3 + generated_at: '2026-10-06T18:12:37Z' + generator: ai + ai_assisted: true + ai_review_required: true + model: gpt-5 + prompt_template: summary-faq-v3 + source_hash: d29bf65cd5b55f7b412beeb0a62873a02eec10189fb6c259f2a5f84f9d016416 + summary_generated_at: '2026-10-06T18:12:37Z' + summary_source_hash: d29bf65cd5b55f7b412beeb0a62873a02eec10189fb6c259f2a5f84f9d016416 + faq_generated_at: '2026-10-06T18:12:37Z' + faq_source_hash: d29bf65cd5b55f7b412beeb0a62873a02eec10189fb6c259f2a5f84f9d016416 + summary: >- + You'll build a Zephyr image for Arm Cortex-A and configure U-Boot to verify its signed FIT before booting. After choosing QEMU or a TI AM62L evaluation module (EVM), you'll set up the host + tools, build Zephyr, sign the FIT, and embed the trusted public key in U-Boot. You'll then boot + the image, optionally test rejection of untrusted or tampered images, and examine what remains + outside the verification boundary. + faqs: + - question: Do I need an AM62L EVM to follow this Learning Path? + answer: >- + No. You can use QEMU without hardware. If you have an AM62L EVM, follow its target-specific + setup and boot-media instructions instead. + - question: Do I need to patch the U-Boot source to add the trusted key? + answer: >- + No. You'll generate a public-key node in `signature.dtsi` and include it when you build + U-Boot. For the AM62L EVM, use `CONFIG_DEVICE_TREE_INCLUDES`. For QEMU, add the node to the + control device tree supplied with `EXT_DTB`. You don't need to change the U-Boot source files. + - question: How can I check the signed FIT before booting the target? + answer: >- + Use the U-Boot `fit_check_sign` tool with your signed FIT and built `u-boot.dtb`. This checks the + FIT against the public key in the control device tree before you prepare the boot media. + - question: How do I confirm that verification succeeded on the target? + answer: >- + Look for `sha256,rsa2048:key-a+ OK` in the U-Boot output, followed by the Zephyr image A banner. + You can also run the optional wrong-key and tampered-image tests to check that U-Boot refuses + to start either image. + - question: Does this setup authenticate U-Boot as well as Zephyr? + answer: >- + No. In QEMU, you'll run U-Boot without an earlier stage that authenticates it. On the AM62L EVM, + you'll leave the device in High Security, Field Securable (HS-FS) development state, where earlier stages accept boot files + signed with any key. To extend trust to U-Boot in production, you need to provision your key, + move the device to High Security, Security Enforced (HS-SE), and sign the earlier boot files. +# END generated_summary_faq + +author: + - Roy Jamil + - Odin Shen + +# New Learning Paths are opted in for the next manual generated summary/FAQ run. +# The generator resets this to false after a successful write. +generate_summary_faq: false + +# Optional one-shot controls: set either field to true to regenerate just that +# generated section the next time the summary/FAQ tool runs. The tool resets +# them to false after a successful write. +rerun_summary: false +rerun_faqs: false + +### Tags +skilllevels: Advanced +subjects: Security +armips: + - Cortex-A +tools_software_languages: + - Zephyr + - Workbench for Zephyr + - Visual Studio Code + - U-Boot + - QEMU + - OpenSSL + - GCC + - C +operatingsystems: + - RTOS + - Linux + +further_reading: + - resource: + title: U-Boot FIT signature verification + link: https://docs.u-boot.org/en/latest/usage/fit/signature.html + type: documentation + - resource: + title: U-Boot documentation for TI K3 boards, including the secure boot chain of trust + link: https://docs.u-boot.org/en/latest/board/ti/k3.html + type: documentation + - resource: + title: Zephyr AM62L EVM board documentation + link: https://docs.zephyrproject.org/latest/boards/ti/am62l_evm/doc/index.html + type: documentation + - resource: + title: U-Boot documentation for the QEMU Arm board + link: https://docs.u-boot.org/en/latest/board/emulation/qemu-arm.html + type: documentation + - resource: + title: Zephyr QEMU Cortex-A53 board documentation + link: https://docs.zephyrproject.org/latest/boards/qemu/cortex_a53/doc/index.html + type: documentation + +### FIXED, DO NOT MODIFY +# ================================================================================ +weight: 1 # _index.md always has weight of 1 to order correctly +layout: "learningpathall" # All files under learning paths have this same wrapper +learning_path_main_page: "yes" # This should be surfaced when looking for related content. Only set for _index.md of learning path content. +--- diff --git a/content/learning-paths/embedded-and-microcontrollers/zephyr-signed-fit-uboot-cortex-a/_next-steps.md b/content/learning-paths/embedded-and-microcontrollers/zephyr-signed-fit-uboot-cortex-a/_next-steps.md new file mode 100644 index 0000000000..c3db0de5a2 --- /dev/null +++ b/content/learning-paths/embedded-and-microcontrollers/zephyr-signed-fit-uboot-cortex-a/_next-steps.md @@ -0,0 +1,8 @@ +--- +# ================================================================================ +# FIXED, DO NOT MODIFY THIS FILE +# ================================================================================ +weight: 21 # Set to always be larger than the content in this path to be at the end of the navigation. +title: "Next Steps" # Always the same, html page title. +layout: "learningpathall" # All files under learning paths have this same wrapper for Hugo processing. +--- diff --git a/content/learning-paths/embedded-and-microcontrollers/zephyr-signed-fit-uboot-cortex-a/images/boot-chain-generic.svg b/content/learning-paths/embedded-and-microcontrollers/zephyr-signed-fit-uboot-cortex-a/images/boot-chain-generic.svg new file mode 100644 index 0000000000..8089d7312d --- /dev/null +++ b/content/learning-paths/embedded-and-microcontrollers/zephyr-signed-fit-uboot-cortex-a/images/boot-chain-generic.svg @@ -0,0 +1,54 @@ + + Generic Cortex-A boot chain with U-Boot verifying the Zephyr FIT image + + + + + + + + + + + + + Checked by the silicon vendor's ROM and firmware + against your fused key; before fusing, any key passes + + + Checked by U-Boot + with your key + + + + + Boot ROM + inside the SoC + runs first, cannot change + + + Vendor early boot stages + SPL, TF-A, OP-TEE, security firmware + bring up DRAM and the Secure world + + + U-Boot + holds your public key + in its device tree + + + Zephyr + inside a FIT image + signed with your key + + + + + + + + checked by the ROM + checked by the firmware + checked by U-Boot, + then started with go + diff --git a/content/learning-paths/embedded-and-microcontrollers/zephyr-signed-fit-uboot-cortex-a/images/boot-chain.svg b/content/learning-paths/embedded-and-microcontrollers/zephyr-signed-fit-uboot-cortex-a/images/boot-chain.svg new file mode 100644 index 0000000000..6d0f733a6f --- /dev/null +++ b/content/learning-paths/embedded-and-microcontrollers/zephyr-signed-fit-uboot-cortex-a/images/boot-chain.svg @@ -0,0 +1,61 @@ + + AM62L boot chain with the U-Boot to Zephyr verification added + + + + + + + + + + + + + Checked by TI's ROM and TIFS + On an HS-FS device such as this EVM, any key passes + + + Checked by U-Boot + with your key + + + + + Boot ROM + inside the AM62L + loads tiboot3.bin + + + tiboot3.bin + TF-A BL1 + TIFS + loads tispl.bin + + + tispl.bin + TF-A BL31, OP-TEE, SPL + loads u-boot.img + + + u-boot.img + U-Boot, with key-a's + public key inside + + + zephyr-a.itb + FIT image: Zephyr, + signed with key-a + + + + + + + + + checked by the ROM + checked by TIFS + checked by TIFS + checked by U-Boot, + then started with go + diff --git a/content/learning-paths/embedded-and-microcontrollers/zephyr-signed-fit-uboot-cortex-a/images/fit-signature.svg b/content/learning-paths/embedded-and-microcontrollers/zephyr-signed-fit-uboot-cortex-a/images/fit-signature.svg new file mode 100644 index 0000000000..d31b572d26 --- /dev/null +++ b/content/learning-paths/embedded-and-microcontrollers/zephyr-signed-fit-uboot-cortex-a/images/fit-signature.svg @@ -0,0 +1,67 @@ + + What the FIT signature covers and how U-Boot checks it + + + + + + + + + + + + + zephyr-a.itb (the FIT image, a device tree blob) + + + + images / kernel-1 + type = "kernel", os = "u-boot" + load = entry = 0x82000000 + + + hash-1 + sha256 of the Zephyr bytes + + + the hash covers the bytes + + + data + zephyr.bin, about 58 KB + the bytes that run + + + + configurations / conf-1 + kernel = "kernel-1" + signature-1 + sha256,rsa2048 with key-a + signs conf-1 and hash-1 + + + + the signature covers + the hash node, not the bytes + + + + u-boot.img (control device tree) + + /signature/key-key-a + rsa,modulus, rsa,exponent (public key) + required = "conf" + + + + + + + bootm start, then bootm loados + 1. Verify signature-1 with key-a + 2. Verify hash-1 against the bytes + 3. Copy the bytes to 0x82000000 + Then: go 0x82000000 + + diff --git a/content/learning-paths/embedded-and-microcontrollers/zephyr-signed-fit-uboot-cortex-a/images/wz-add-application.webp b/content/learning-paths/embedded-and-microcontrollers/zephyr-signed-fit-uboot-cortex-a/images/wz-add-application.webp new file mode 100644 index 0000000000..82578c5d48 Binary files /dev/null and b/content/learning-paths/embedded-and-microcontrollers/zephyr-signed-fit-uboot-cortex-a/images/wz-add-application.webp differ diff --git a/content/learning-paths/embedded-and-microcontrollers/zephyr-signed-fit-uboot-cortex-a/images/wz-add-toolchain.webp b/content/learning-paths/embedded-and-microcontrollers/zephyr-signed-fit-uboot-cortex-a/images/wz-add-toolchain.webp new file mode 100644 index 0000000000..565aa603a8 Binary files /dev/null and b/content/learning-paths/embedded-and-microcontrollers/zephyr-signed-fit-uboot-cortex-a/images/wz-add-toolchain.webp differ diff --git a/content/learning-paths/embedded-and-microcontrollers/zephyr-signed-fit-uboot-cortex-a/images/wz-add-west-workspace.webp b/content/learning-paths/embedded-and-microcontrollers/zephyr-signed-fit-uboot-cortex-a/images/wz-add-west-workspace.webp new file mode 100644 index 0000000000..f2f652bbc3 Binary files /dev/null and b/content/learning-paths/embedded-and-microcontrollers/zephyr-signed-fit-uboot-cortex-a/images/wz-add-west-workspace.webp differ diff --git a/content/learning-paths/embedded-and-microcontrollers/zephyr-signed-fit-uboot-cortex-a/images/wz-build.webp b/content/learning-paths/embedded-and-microcontrollers/zephyr-signed-fit-uboot-cortex-a/images/wz-build.webp new file mode 100644 index 0000000000..c38ce6837b Binary files /dev/null and b/content/learning-paths/embedded-and-microcontrollers/zephyr-signed-fit-uboot-cortex-a/images/wz-build.webp differ diff --git a/content/learning-paths/laptops-and-desktops/pytorch-finetuning-on-spark/1-setup.md b/content/learning-paths/laptops-and-desktops/pytorch-finetuning-on-spark/1-setup.md index e1eef83490..478526a37e 100644 --- a/content/learning-paths/laptops-and-desktops/pytorch-finetuning-on-spark/1-setup.md +++ b/content/learning-paths/laptops-and-desktops/pytorch-finetuning-on-spark/1-setup.md @@ -35,7 +35,7 @@ The first command grants your user Docker access, and `newgrp docker` activates NVIDIA provides pre-built PyTorch containers that include all the necessary frameworks, libraries, and dependencies optimized for NVIDIA GPUs. These containers are regularly updated and maintained, ensuring you have access to the latest stable versions without the complexity of manual dependency management. -Pull the latest PyTorch container from NVIDIA's container registry: +Pull the PyTorch 25.11 container from NVIDIA's container registry: ```bash docker pull nvcr.io/nvidia/pytorch:25.11-py3 @@ -67,23 +67,51 @@ After running the command, you'll be inside the container with a root shell prom ## Install dependencies -The base PyTorch container doesn't include all the specialized libraries needed for efficient model fine-tuning. You need to install several additional Python packages that provide transformer models, parameter-efficient fine-tuning methods, dataset utilities, and training frameworks. +The base PyTorch container doesn't include all the libraries needed for this fine-tuning workflow. Install the package versions used by the script and the rest of this Learning Path. Inside the running container, install the required dependencies: ```bash -pip install transformers peft datasets trl bitsandbytes +pip install \ + "accelerate==1.15.0" \ + "datasets==5.0.1" \ + "transformers==4.57.1" \ + "trl==1.13.0" +``` + +Verify the installed versions: + +```bash +python - <<'PY' +import accelerate +import datasets +import transformers +import trl + +print("accelerate", accelerate.__version__) +print("datasets", datasets.__version__) +print("transformers", transformers.__version__) +print("trl", trl.__version__) +PY +``` + +The output is: + +```output +accelerate 1.15.0 +datasets 5.0.1 +transformers 4.57.1 +trl 1.13.0 ``` These packages serve specific purposes: +- `accelerate` provides device placement and training-loop integration - `transformers` provides access to pre-trained language models and tokenizers from Hugging Face -- `peft` (Parameter-Efficient Fine-Tuning) enables techniques like LoRA and QLoRA that reduce memory requirements - `datasets` offers a standardized interface for loading and processing training datasets - `trl` (Transformer Reinforcement Learning) includes training utilities and recipes for language models -- `bitsandbytes` enables 4-bit and 8-bit quantization for memory-efficient training -The installation can take a few minutes as pip downloads and installs each package along with their dependencies. +The pinned versions prevent API changes in newer packages from breaking the script. The installation can take a few minutes as pip downloads each package and its dependencies. ## Authenticate with Hugging Face @@ -106,9 +134,30 @@ Clone the playbooks repository: ```bash git clone https://github.com/mhall119/finetuning-scripts.git cd finetuning-scripts/nvidia +git checkout 0076c9979dee71a16e3cca5e91da80d06cfb913a +``` + +The repository contains a customized copy of the 3B full fine-tuning script from [NVIDIA's PyTorch fine-tuning assets](https://github.com/NVIDIA/dgx-spark-playbooks/tree/main/nvidia/pytorch-fine-tune/assets). The customized script adds the dataset and output options used later in this Learning Path. The `git checkout` command selects the revision that matches the code walkthrough. + +TRL 1.13.0 uses chunked negative log likelihood (`chunked_nll`) by default. That loss mode isn't compatible with the script's compiled model wrapper. The existing `packing: False` setting controls dataset packing; it doesn't disable chunked loss. Update the script to use the standard negative log likelihood loss instead: + +```bash +sed -i '/"packing": False,/a\ "loss_type": "nll",' Llama3_3B_full_finetuning.py +``` + +Verify that the setting is present: + +```bash +grep -n '"loss_type": "nll"' Llama3_3B_full_finetuning.py +``` + +The output is similar to: + +```output +83: "loss_type": "nll", ``` -The repository contains a fork of the scripts found in [NVIDIA's Playbook](https://github.com/NVIDIA/dgx-spark-playbooks/nvidia/pytorch-fine-tune/assets) including the fine-tuning scripts you'll use in the next steps. This script is preconfigured with sensible defaults but also accepts command-line arguments for customization. +The line number can vary if the upstream script changes. The script is now configured to avoid the `AttributeError: 'function' object has no attribute '__func__'` error when `SFTTrainer` initializes. ## What you've accomplished and what's next @@ -117,6 +166,6 @@ In this section you: - Configured Docker permissions on DGX Spark - Pulled the NVIDIA PyTorch container and launched an interactive session - Installed fine-tuning libraries and authenticated with Hugging Face -- Cloned the fine-tuning scripts repository +- Cloned the fine-tuning scripts repository and selected a TRL-compatible loss mode In the next section, you'll learn how supervised fine-tuning works and what makes it effective for adapting pre-trained models to specific tasks. diff --git a/content/learning-paths/laptops-and-desktops/pytorch-finetuning-on-spark/2-finetuning.md b/content/learning-paths/laptops-and-desktops/pytorch-finetuning-on-spark/2-finetuning.md index 146c34b246..f73f6e6fc7 100644 --- a/content/learning-paths/laptops-and-desktops/pytorch-finetuning-on-spark/2-finetuning.md +++ b/content/learning-paths/laptops-and-desktops/pytorch-finetuning-on-spark/2-finetuning.md @@ -8,7 +8,7 @@ layout: learningpathall ## Why fine-tuning matters for domain knowledge -Pre-trained models like Llama 3.2 8B have broad language skills, but they don't know everything. Ask the base model about the maximum clock speed of the RP2350 microcontroller and it confidently answers "1.8 GHz," a completely fabricated number. The actual specification is 150 MHz. +Pre-trained models like Llama 3.2 3B have broad language skills, but they don't know everything. Ask the base model about the maximum clock speed of the RP2350 microcontroller and it confidently answers "1.8 GHz," a completely fabricated number. The actual specification is 150 MHz. Fine-tuning fixes this by training the model on real data from Raspberry Pi datasheets. After fine-tuning, the same model answers correctly: "The RP2350 supports up to 150 MHz." No hallucination, no guessing. diff --git a/content/learning-paths/laptops-and-desktops/pytorch-finetuning-on-spark/3-pytorch.md b/content/learning-paths/laptops-and-desktops/pytorch-finetuning-on-spark/3-pytorch.md index 86bfb6bb7b..bcb1337688 100644 --- a/content/learning-paths/laptops-and-desktops/pytorch-finetuning-on-spark/3-pytorch.md +++ b/content/learning-paths/laptops-and-desktops/pytorch-finetuning-on-spark/3-pytorch.md @@ -80,7 +80,7 @@ With the model and tokenizer loaded, the script prepares the training data by ca ## Training configuration -The training configuration controls how the SFT process runs. Notable parameters include `num_train_epochs` (initially set to 0.01 for a warmup pass, then updated for full training), `gradient_accumulation_steps` (batches to accumulate before each weight update), `learning_rate` (optimizer step size), and `max_length` (maximum sequence length). The logging parameters determine where and how often training metrics are recorded. +The training configuration controls how the SFT process runs. Notable parameters include `num_train_epochs` (initially set to 0.05 for a warmup pass, then updated for full training), `gradient_accumulation_steps` (batches to accumulate before each weight update), `learning_rate` (optimizer step size), and `max_length` (maximum sequence length). The logging parameters determine where and how often training metrics are recorded. ```python # Configure the SFT config @@ -95,6 +95,7 @@ The training configuration controls how the SFT process runs. Notable parameters "seed": 42, "dataset_text_field": "text", "packing": False, + "loss_type": "nll", "max_length": args.seq_length, "report_to": "none", "logging_dir": args.log_dir, @@ -103,6 +104,8 @@ The training configuration controls how the SFT process runs. Notable parameters } ``` +The explicit `loss_type` setting keeps TRL on its standard negative log likelihood path. TRL 1.13.0 otherwise defaults to chunked loss, which attempts to patch the `torch.compile()` wrapper and raises an `AttributeError` during `SFTTrainer` initialization. + ## Model compilation and training The script first optimizes the model graph for faster execution on the hardware using the `torch.compile()` function. A short warmup pass (0.05 epochs) triggers compilation so the overhead doesn't affect the actual training run. After warmup, the script creates an `SFTTrainer` with the full epoch count and calls `trainer.train()`. The returned `trainer_stats` object contains metrics like loss, throughput, and training time. @@ -167,7 +170,7 @@ python Llama3_3B_full_finetuning.py \ --output_dir "/workspace/models/Llama-3.2-3B-FineTuned" ``` -The `--dataset_size 300` flag tells the script to use all entries in the Raspberry Pi dataset (the default is 500, but a smaller, focused dataset can be more effective than a larger generic one). The `--output_dir` flag saves the fine-tuned model and tokenizer to the specified directory. Because you mounted your current directory into the container with `-v ${PWD}:/workspace`, the saved model is also accessible from the host system. +The `--dataset_size 300` flag tells the script to use all entries in the Raspberry Pi dataset (the default is 512, but a smaller, focused dataset can be more effective than a larger generic one). The `--output_dir` flag saves the fine-tuned model and tokenizer to the specified directory. Because you mounted your current directory into the container with `-v ${PWD}:/workspace`, the saved model is also accessible from the host system. Training takes a few minutes on DGX Spark. When it completes, you'll see a summary with metrics like runtime, samples per second, and loss, followed by a confirmation that the model was saved. @@ -179,4 +182,4 @@ In this section you: - Downloaded the Raspberry Pi datasheet Q&A pairs - Ran full fine-tuning and saved the resulting model with `--output_dir` -In the next section, you'll serve both the original and fine-tuned models and compare their responses to Raspberry Pi hardware questions. \ No newline at end of file +In the next section, you'll serve both the original and fine-tuned models and compare their responses to Raspberry Pi hardware questions. diff --git a/content/learning-paths/laptops-and-desktops/pytorch-finetuning-on-spark/4-testing.md b/content/learning-paths/laptops-and-desktops/pytorch-finetuning-on-spark/4-testing.md index 19300fcef7..d8d970a7ea 100644 --- a/content/learning-paths/laptops-and-desktops/pytorch-finetuning-on-spark/4-testing.md +++ b/content/learning-paths/laptops-and-desktops/pytorch-finetuning-on-spark/4-testing.md @@ -10,6 +10,12 @@ layout: learningpathall Now that you've fine-tuned your model on Raspberry Pi datasheet content, it's time to compare its behavior against the original. You'll serve both versions using vLLM, a high-performance inference server optimized for large language models, and observe how fine-tuning on domain-specific data changes the model's factual accuracy. +Exit the PyTorch container to return to the DGX Spark host. The model remains available in the host directory mounted at `/workspace`: + +```bash +exit +``` + ## Download vLLM container NVIDIA provides a pre-built vLLM container that includes all the necessary components for efficient model serving. This container is optimized for NVIDIA GPUs and includes support for various quantization methods and multi-GPU inference. @@ -50,7 +56,7 @@ Start the vLLM server with the original Llama 3.2 3B Instruct model: ```bash python3 -m vllm.entrypoints.openai.api_server \ --model "meta-llama/Llama-3.2-3B-Instruct" --trust-remote-code \ ---tensor-parallel-size 1 --quantization fp8 \ +--tensor-parallel-size 1 \ --gpu-memory-utilization 0.80 ``` @@ -59,7 +65,6 @@ The server exposes an OpenAI-compatible API with the following configuration: - `--model` specifies the Hugging Face model to load, which will be pulled from the Hugging Face cache we made available to this container - `--trust-remote-code` allows loading models with custom code (required for some architectures) - `--tensor-parallel-size 1` runs inference on a single GPU (like on a DGX Spark) -- `--quantization fp8` uses 8-bit floating point quantization to reduce memory usage and improve throughput - `--gpu-memory-utilization 0.80` limits memory usage to 80%, leaving room for the rest of the OS because the DGX Spark implements unified memory between CPU and GPU. Wait for the server to fully load the model and display the message indicating it's ready to accept requests (this typically takes 30-60 seconds). @@ -110,14 +115,6 @@ The base model confidently reports the RP2350 has "256MB of memory," which is of Now test your fine-tuned model to see how training on Raspberry Pi datasheet content improved its factual accuracy. Stop the current vLLM server (press Ctrl+C in the container terminal) before launching the fine-tuned model. -{{% notice Note %}} -As of this writing, vLLM does not support version 5 of the `transformers` library that was used when fine-tuning the model, so you need to patch its `tokenizer_config.json`. Run the following command to update the `tokenizer_class` to `PreTrainedTokenizerFast`, which is compatible with the older `transformers` version bundled in the vLLM container: - -```bash -sed -i 's/"tokenizer_class": "TokenizersBackend"/"tokenizer_class": "PreTrainedTokenizerFast"/' /workspace/models/Llama-3.2-3B-FineTuned/tokenizer_config.json -``` -{{% /notice %}} - ### Launch vLLM Start the vLLM server with your fine-tuned model: @@ -125,7 +122,7 @@ Start the vLLM server with your fine-tuned model: ```bash python3 -m vllm.entrypoints.openai.api_server \ --model "/workspace/models/Llama-3.2-3B-FineTuned" --trust-remote-code \ ---tensor-parallel-size 1 --quantization fp8 \ +--tensor-parallel-size 1 \ --gpu-memory-utilization 0.80 ``` @@ -198,4 +195,4 @@ You've successfully fine-tuned a large language model on domain-specific data us - Ran full fine-tuning to train the model on hardware specifications - Compared base and fine-tuned model responses to verify factual accuracy improvements -The approach you used can be applied to any domain where you need accurate, grounded responses. Consider experimenting with different datasets from your own technical documentation, product specifications, or internal knowledge bases. \ No newline at end of file +The approach you used can be applied to any domain where you need accurate, grounded responses. Consider experimenting with different datasets from your own technical documentation, product specifications, or internal knowledge bases. diff --git a/content/learning-paths/mobile-graphics-and-gaming/_index.md b/content/learning-paths/mobile-graphics-and-gaming/_index.md index dd91335a71..22a4c93cc5 100644 --- a/content/learning-paths/mobile-graphics-and-gaming/_index.md +++ b/content/learning-paths/mobile-graphics-and-gaming/_index.md @@ -13,13 +13,13 @@ pinned_learning_paths: - nfru-unreal - model-training-gym-nfru operatingsystems_filter: -- Android: 54 +- Android: 55 - Linux: 54 - macOS: 27 - Windows: 25 subjects_filter: - Gaming: 6 -- Graphics: 8 +- Graphics: 9 - ML: 46 - Performance and Architecture: 38 subtitle: Optimize Android apps and build faster games using cutting-edge Arm tech. @@ -40,12 +40,11 @@ tools_software_languages_filter: - Bash: 2 - Bazel: 2 - C: 7 -- C++: 2 - CameraX: 1 - CCA: 1 - Clang: 13 - CMake: 6 -- CPP: 20 +- CPP: 22 - csharp: 3 - Docker: 2 - ETDump: 1 @@ -102,7 +101,7 @@ tools_software_languages_filter: - VGF: 3 - Visual Studio: 4 - Visual Studio Code: 1 -- Vulkan: 12 +- Vulkan: 13 - Vulkan SDK: 3 - XNNPACK: 10 weight: 3 diff --git a/content/learning-paths/mobile-graphics-and-gaming/omm-mali-intro/1-decide-if-an-asset-needs-omm.md b/content/learning-paths/mobile-graphics-and-gaming/omm-mali-intro/1-decide-if-an-asset-needs-omm.md new file mode 100644 index 0000000000..25da316bfe --- /dev/null +++ b/content/learning-paths/mobile-graphics-and-gaming/omm-mali-intro/1-decide-if-an-asset-needs-omm.md @@ -0,0 +1,71 @@ +--- +title: Decide if an asset needs OMM +description: Identify alpha-tested assets that can benefit from Opacity Micromaps during ray traversal. +weight: 2 + +### FIXED, DO NOT MODIFY +layout: learningpathall +--- + +## Identify the ray tracing problem + +Imagine a ray passing through a tree filled with hundreds of leaves. Each leaf might be a rectangular mesh with a texture that makes most of that rectangle transparent. The ray can therefore intersect the rectangle without hitting the visible part of the leaf. + +The renderer must check the leaf's alpha mask before it accepts the intersection. One check is inexpensive, but the work adds up when shadow and reflection rays pass through many layers of foliage, fences, or grilles. + +Opacity Micromaps (OMM) move much of this decision into ray traversal. OMM stores a simplified description of the alpha mask in a form that the traversal hardware can read. This can prevent the renderer from running shader-side material logic for every candidate intersection. + +## Separate geometry from opacity + +A leaf often uses a quad made from two triangles. The geometry describes the complete rectangle, while an alpha texture cuts the leaf shape out of it. It helps to treat these as two separate layers of information: + +| Data layer | Question it answers | +| --- | --- | +| Geometry | Where can a ray intersect a triangle? | +| Opacity | Is the intersected position visible? | + +![Three-panel diagram showing a ray intersecting a leaf quad. Geometry reports a hit on the complete rectangle, while the alpha mask shows whether that point is part of the visible leaf.#center](images/omm-introduction/01-masked-geometry.webp "Geometry and alpha provide separate answers for the same ray intersection") + +The figure shows a ray intersecting the transparent part of the quad. The geometry test reports a valid triangle hit, but the alpha test shows that the ray missed the visible leaf. OMM helps the traversal hardware make that second decision earlier. + +## Compare rasterization and ray traversal + +You might already handle this kind of material with alpha testing during rasterization. Ray tracing solves the same visual problem at a different point in the rendering process. Rasterization creates a fragment before the pixel shader checks the alpha texture. Ray traversal finds a possible triangle intersection before it knows whether that point is visible. + +| Rendering path | Opacity decision | +| --- | --- | +| Rasterization | A pixel shader samples alpha and discards the fragment when it fails the cutoff | +| Ray tracing without OMM | An any-hit shader or ray-query candidate handler evaluates the mask | +| Ray tracing with OMM | Traversal resolves known regions and keeps only unknown regions for shader-side evaluation | + +A shadow or reflection ray can pass through several overlapping leaves. Without OMM, each possible hit can repeat texture access, material setup, and control flow. With OMM, traversal first reads a compact state for the small region that the ray hit: + +| OMM state | Traversal action | +| --- | --- | +| Opaque | Accept the region as opaque | +| Transparent | Ignore the region and continue traversal | +| Unknown | Keep the candidate for shader-side evaluation | + +## Identify suitable assets + +Not every alpha-tested asset benefits from OMM. You get the clearest value when the opacity pattern remains stable and most of the mask can be classified before the game runs. Use the following examples as a starting point when you review an asset: + +| Asset pattern | OMM fit | Evidence to check | +| --- | --- | --- | +| Static foliage | Strong candidate | Stable mask, repeated ray hits, and narrow alpha edges | +| Chain-link fence or grille | Strong candidate | Stable binary cutouts and enough subdivision for thin features | +| Animated or changing mask | Conditional | A defined update or rebuild policy | +| Alpha-blended surface | Poor candidate | Continuous transparency needs a blending path | +| Rasterization-only asset | No benefit | The asset never participates in ray traversal | + +Also check every mesh level of detail (LOD) separately. Its UV coordinates, triangle links, and alpha data must match the version used to create the OMM. Otherwise, valid OMM data can become attached to the wrong triangle and produce incorrect opacity results. + +## Decide whether to continue + +Before continuing, choose one alpha-tested asset from your own project or picture a common example, such as a leaf or wire fence. Ask three questions: does it participate in ray tracing, does its alpha mask remain stable, and will rays cross it often enough for repeated opacity checks to matter? + +If the answer to all three questions is yes, the asset is a reasonable OMM candidate. Keep dynamic masks and assets with mostly uncertain opacity on the existing shader-side path until profiling gives you a reason to change them. + +### What you've learned + +You can now distinguish the geometry intersection from the opacity decision and identify assets that are reasonable OMM candidates. Next, choose how microtriangles represent the asset's opacity. diff --git a/content/learning-paths/mobile-graphics-and-gaming/omm-mali-intro/2-choose-an-omm-representation.md b/content/learning-paths/mobile-graphics-and-gaming/omm-mali-intro/2-choose-an-omm-representation.md new file mode 100644 index 0000000000..f6f790ee0c --- /dev/null +++ b/content/learning-paths/mobile-graphics-and-gaming/omm-mali-intro/2-choose-an-omm-representation.md @@ -0,0 +1,102 @@ +--- +title: Choose an OMM representation +description: Classify microtriangles and choose an OMM subdivision level and state format for an alpha-tested asset. +weight: 3 + +### FIXED, DO NOT MODIFY +layout: learningpathall +--- + +## Turn an alpha mask into traversal data + +After you identify a suitable asset, you need to describe its opacity in a form that ray traversal can use. OMM does this by dividing each original triangle into smaller regions called *microtriangles*. Each microtriangle records whether its part of the alpha mask is opaque, transparent, or still uncertain. + +This subdivision changes the OMM data, not the mesh. It doesn't add polygons to the source model or to the bottom-level acceleration structure (BLAS). You can think of it as placing a finer decision grid over the original triangle. + +![Diagram showing a leaf alpha mask covered by microtriangles. Opaque cells fill the leaf, transparent cells cover empty space, and unknown cells follow the boundary where shader evaluation can still be needed.#center](images/omm-introduction/02-microtriangle-states.webp "Microtriangle states follow the leaf's alpha boundary") + +The figure keeps the two original triangles and overlays a finer OMM grid. Opaque regions cover the inside of the leaf, transparent regions cover empty parts of the quad, and unknown regions follow the detailed edge. The closer the grid follows that edge, the more opacity decisions traversal can make without shader help. + +## Choose a subdivision level + +The subdivision level controls the resolution of the OMM grid. A low level uses a few large regions. A higher level uses many smaller regions and can follow a curved or detailed alpha boundary more closely. + +This extra detail has a cost: each step up creates four times as many microtriangles. For level `N`, the number in each original triangle is: + +```text +microtriangle count = 4^N = 2^(2N) +``` + +| Level | Microtriangles per original triangle | Typical use | +| ---: | ---: | --- | +| 0 | 1 | One state for the complete triangle | +| 1 | 4 | A coarse cutout | +| 2 | 16 | Large silhouette changes | +| 3 | 64 | Curved alpha edges | +| 4 | 256 | Fine detail with higher data cost | + +Choose the level by comparing the size of the source triangle with the detail in its alpha mask. Also consider the intended LOD and viewing distance. Don't map the subdivision level directly to screen pixels because the same triangle can appear at many sizes on screen. + +## Choose a state format + +Subdivision decides how small the regions are. The state format decides what each region can say about its opacity. + +The `VK_KHR_opacity_micromap` extension defines 2-state and 4-state formats. A 2-state OMM must call every region opaque or transparent. A 4-state OMM can also leave a region unknown, allowing the existing shader path to resolve difficult edges at runtime. + +| Format | State | Normal traversal action | If forced to 2-state | +| --- | --- | --- | --- | +| 2-state | Fully transparent | Ignore the hit | No change | +| 2-state | Fully opaque | Handle the region as opaque | No change | +| 4-state | Fully transparent | Ignore the hit | No change | +| 4-state | Fully opaque | Handle the region as opaque | No change | +| 4-state | Unknown-transparent | Keep shader-side evaluation available | Treat the region as transparent | +| 4-state | Unknown-opaque | Keep shader-side evaluation available | Treat the region as opaque | + +During normal 4-state traversal, both unknown values mean that more evaluation is needed. The transparent and opaque variants only produce different results when a ray or instance forces the OMM into 2-state evaluation. + +This gives you two practical starting strategies: + +| Strategy | OMM setup | Main tradeoff | +| --- | --- | --- | +| Shader-assisted edge detail | 4-state with low or moderate subdivision | Smaller OMM data, but unknown edges retain some shader-side work | +| Hardware-resolved binary detail | 2-state with higher subdivision | No unknown states, but more microtriangles and possible silhouette error | + +Use the 2-state strategy only when you can classify every region without damaging the image. Marking visible detail as transparent can remove a valid hit. Marking empty space as opaque can create false shadows or reflections. + +## Classify sample microtriangles + +You can practice the classification step without OMM-capable hardware. The exercise uses an alpha cutoff of `0.5`: values below the cutoff are transparent, while values at or above it are opaque. Each row contains representative samples from one microtriangle. + +| Region | Representative alpha samples | Your classification | +| --- | --- | --- | +| A | `1.00`, `0.92`, `0.87` | Opaque, transparent, or unknown? | +| B | `0.00`, `0.08`, `0.14` | Opaque, transparent, or unknown? | +| C | `0.05`, `0.62`, `0.91` | Opaque, transparent, or unknown? | +| D | `0.42`, `0.45`, `0.48` | Opaque, transparent, or unknown? | +| E | `0.49`, `0.50`, `0.51` | Opaque, transparent, or unknown? | + +Classify each region, then calculate the raw state payload for one level-2 triangle. Level 2 contains 16 microtriangles. The 2-state format uses one bit per microtriangle, while the 4-state format uses two bits. + +### Check your classification and payload calculation + +Compare your answers with the following classifications: + +| Region | Classification | Reason | +| --- | --- | --- | +| A | Opaque | Every sample is on the visible side of the cutoff | +| B | Transparent | Every sample is below the cutoff | +| C | Unknown | The region crosses the cutoff | +| D | Transparent | Every sample is below the cutoff | +| E | Unknown | The region lies on both sides of the cutoff | + +A level-2 triangle needs 16 bits, or 2 bytes, of raw 2-state data. It needs 32 bits, or 4 bytes, of raw 4-state data. These values describe the state payload only. Record metadata, alignment, and implementation-specific storage can increase the final size. + +## Interpret the result + +The result should contain large areas of opaque and transparent microtriangles, with a narrow band of unknown regions around the alpha boundary. If much of the triangle remains unknown, traversal still needs shader help for many hits. + +A wide unknown band can mean that the subdivision level is too low. It can also point to a mismatch in filtering or cutoff, texture detail that is too fine for the grid, or material logic that the baker can't reproduce. + +### What you've learned + +You have classified opacity regions and compared the accuracy and storage implications of the two formats. Next, connect the baked representation to the Mali G2-Ultra NX traversal path. diff --git a/content/learning-paths/mobile-graphics-and-gaming/omm-mali-intro/3-connect-omm-to-mali-g2-ultra-nx.md b/content/learning-paths/mobile-graphics-and-gaming/omm-mali-intro/3-connect-omm-to-mali-g2-ultra-nx.md new file mode 100644 index 0000000000..bab0b9d876 --- /dev/null +++ b/content/learning-paths/mobile-graphics-and-gaming/omm-mali-intro/3-connect-omm-to-mali-g2-ultra-nx.md @@ -0,0 +1,86 @@ +--- +title: Connect OMM to Arm Mali G2-Ultra NX +description: Follow OMM data into a Vulkan acceleration structure and enable hardware traversal on Arm Mali G2-Ultra NX. +weight: 4 + +### FIXED, DO NOT MODIFY +layout: learningpathall +--- + +## Connect baked data to hardware traversal + +So far, you have treated OMM as asset data: a grid of microtriangles with opacity states. To gain a runtime benefit, the engine must turn that data into a GPU resource and connect it to the matching scene geometry. + +Arm Mali G2-Ultra NX can read OMM data in its hardware ray tracing path. Your engine prepares and links the data before traversal begins. When a ray reaches a triangle, the hardware reads the state for the microtriangle it hit and decides whether it can resolve the intersection immediately. + +![Flow diagram showing Mali G2-Ultra NX traversal moving from scene traversal to a triangle hit and hardware OMM lookup. The lookup accepts opaque regions, skips transparent regions, and sends unknown regions to the fallback path.#center](images/omm-introduction/04-mali-hardware-flow.webp "Hardware OMM lookup during Mali G2-Ultra NX ray traversal") + +This distinction matters when you debug the feature. A successful offline bake proves that you produced OMM data, but it doesn't prove that the GPU is using it. The resource build, triangle mapping, and traversal controls must also be correct. + +## Follow the runtime lifecycle + +It helps to follow one asset from its source files to a rendered frame. At each stage, keep evidence that you can inspect if the final image or performance doesn't match your expectations. + +| Stage | What your engine does | Evidence to retain | +| --- | --- | --- | +| Prepare and bake | Keep the mesh, triangle order, UVs, alpha source, cutoff, filtering, format, and subdivision on one asset version | Bake report and source version | +| Build the micromap | Query build sizes, allocate result and scratch storage, and build the OMM data | Successful build and resource sizes | +| Link triangle data | Map each source triangle to its OMM record during the BLAS build | Triangle mapping and BLAS build input | +| Enable traversal | Configure the ray pipeline or ray-query shader for OMM | Active pipeline flag or execution mode | +| Keep resources valid | Synchronize builds and keep the micromap alive while the BLAS uses it | Resource lifetime and synchronization records | + +Under `VK_KHR_opacity_micromap`, the micromap GPU resource uses a `VkAccelerationStructureKHR` handle. When your engine builds the BLAS, it attaches the micromap and provides a mapping from every source triangle to the correct OMM record. This mapping is what connects an intersection in the scene to the opacity states created by the baker. + +## Query device support + +Knowing that your target uses Mali G2-Ultra NX isn't enough to enable OMM. The Vulkan driver must also expose the extension and feature. Query `VK_KHR_opacity_micromap`, then enable its `micromap` feature before you create OMM resources. + +The driver also reports limits that you must compare with the data produced by your asset pipeline: + +| Property | Decision it constrains | +| --- | --- | +| `maxOpacity2StateSubdivisionLevel` | Highest usable 2-state detail level | +| `maxOpacity4StateSubdivisionLevel` | Highest lossless 4-state detail level | +| `maxOpacityLossy4StateSubdivisionLevel` | Highest level available to a lossy 4-state build | +| `maxMicromapTriangles` | Maximum number of triangles in one micromap | + +These limits turn device support into an asset compatibility decision. For example, a device can support OMM while rejecting an asset whose subdivision level is too high. Confirm the extension, feature, relevant limits, and engine configuration before loading an OMM-enabled BLAS. + +## Enable the traversal path + +Vulkan doesn't assume that every shader is prepared to traverse geometry with OMM data. You must opt in through the path your renderer uses. Ray pipelines and inline ray queries use different controls: + +| Traversal path | Required enablement | +| --- | --- | +| Ray pipeline | Create the pipeline with `VK_PIPELINE_CREATE_RAY_TRACING_OPACITY_MICROMAP_BIT_KHR` | +| Ray query | Declare the `OpacityMicromapIdKHR` SPIR-V execution mode with a value that resolves to `true` | + +For a GLSL shader that uses ray queries, enable `GL_EXT_opacity_micromap_ray_query_mode` and redeclare the built-in constant: + +```glsl +#extension GL_EXT_opacity_micromap_ray_query_mode : require +const bool gl_EnableOpacityMicromapEXT = true; +``` + +You can use a specialization constant instead if the engine selects the behavior when it creates the pipeline. In either form, the value must resolve to `true` before the shader performs a ray query against an acceleration structure that can contain OMM data. + +> **Ray query correctness requirement** +> +> Traversing an OMM-enabled acceleration structure without enabling `OpacityMicromapIdKHR` is undefined behavior. The implementation does not automatically fall back to shader-side opacity evaluation. + +## Define the unsupported fallback + +Not every device or configuration will take the OMM path, so preserve the renderer's original alpha-tested path as a fallback. The two paths should differ in how they resolve opacity, not in what the player sees. + +| Path | Micromap and BLAS behavior | Opacity behavior | +| --- | --- | --- | +| Supported | Build the micromap and attach its triangle mapping to the BLAS | Traversal resolves known states; unknown states retain shader-side evaluation | +| Unsupported or disabled | Build the original alpha-tested BLAS without OMM data | The ray pipeline uses its any-hit path, or the ray query uses its existing candidate handler | + +Both paths must use the same alpha source, cutoff, filtering, and material logic. When you disable OMM, the amount of traversal and shader work can change, but the rendered image should not. + +If you have a Mali G2-Ultra NX test device, record its reported limits and compare them with the format and subdivision level you selected. You can still complete the final exercise without a device because it includes a saved capability report. + +### What you've learned + +You can now trace an OMM from asset data into a BLAS, identify the required Vulkan opt-in, and define a safe fallback. Next, use that information to make and validate an adoption decision. diff --git a/content/learning-paths/mobile-graphics-and-gaming/omm-mali-intro/4-build-and-validate-an-omm-adoption-plan.md b/content/learning-paths/mobile-graphics-and-gaming/omm-mali-intro/4-build-and-validate-an-omm-adoption-plan.md new file mode 100644 index 0000000000..950383d6dd --- /dev/null +++ b/content/learning-paths/mobile-graphics-and-gaming/omm-mali-intro/4-build-and-validate-an-omm-adoption-plan.md @@ -0,0 +1,93 @@ +--- +title: Build and validate an OMM adoption plan +description: Choose an OMM strategy, fallback, and validation plan for a sample alpha-tested asset. +weight: 5 + +### FIXED, DO NOT MODIFY +layout: learningpathall +--- + +## Make a decision you can validate + +You now have all the pieces needed to evaluate OMM, but they only become useful when you connect them in one decision. A suitable asset still needs a sensible bake strategy, a supported runtime path, and a fallback that preserves the original image. + +The following exercise asks you to make that decision for a sample leaf asset. You don't need OMM-capable hardware because the exercise provides both bake results and a saved device capability report. + +![Pipeline diagram assigning OMM responsibilities across content authoring, the engine and RHI, the Vulkan API and driver, and Mali hardware. Each stage passes its output to the next, from per-triangle data to the final opacity decision.#center](images/omm-introduction/06-responsibility-map.webp "Ownership and outputs across the OMM pipeline") + +The responsibility map shows how content, baking, engine integration, and the driver contribute to the final result. Use it to identify where each piece of evidence comes from and who should investigate a failure. + +## Review the sample asset + +Consider a team that wants to use OMM for a static ginkgo leaf. The asset is a two-triangle quad with an alpha mask, and it appears in ray-traced shadows and reflections. Its mesh, UV coordinates, alpha mask, cutoff, and filtering remain stable for each level of detail (LOD), so it passes the initial suitability check. + +The team tests two bake strategies. The results are illustrative, and the raw payload sizes don't include record metadata, alignment, or implementation-specific storage. + +| Evidence | 4-state candidate | 2-state candidate | +| --- | --- | --- | +| Subdivision level | 3 | 5 | +| Microtriangles per original triangle | 64 | 1,024 | +| Raw state payload per original triangle | 16 bytes | 128 bytes | +| Resolved regions | 82% | 100% | +| Unknown regions | 18%, concentrated along the edge and stem | None | +| Reference-image comparison | Matches | Matches | +| Runtime behavior | Edge candidates retain shader-side evaluation | Every OMM candidate resolves during traversal | + +The target profile sets a budget of 32 bytes of raw OMM state data for each original triangle. Profiling also shows that shader-side evaluation along the leaf edge is measurable, but it doesn't dominate frame time. Your decision therefore needs to balance storage against the benefit of resolving every region in hardware. + +Before choosing a candidate, also check that the target device can use it. The saved capability report contains these illustrative values: + +| Capability | Value | +| --- | --- | +| `VK_KHR_opacity_micromap` | Available | +| `micromap` | `VK_TRUE` | +| `maxOpacity2StateSubdivisionLevel` | 5 | +| `maxOpacity4StateSubdivisionLevel` | 4 | +| Required ray-query execution mode | Enabled | + +## Record your decision + +Use the evidence to complete the decision record. There can be more than one technically valid approach, but your choice should respect the data budget and explain how you will preserve image quality. + +| Decision | Your answer | +| --- | --- | +| Should this asset use OMM? | Yes or no, with a reason | +| Format and subdivision | Choose one candidate | +| Fallback | Define the behavior when OMM is unavailable | +| Image validation | State what you will compare | +| Runtime validation | State what proves the OMM path executed | +| Rebuild rule | State which asset changes invalidate the bake | + +### Review the suggested adoption plan + +For the stated constraints, use the level-3, 4-state candidate. It stays within the 32-byte raw payload budget, matches the reference image, and resolves most regions during traversal. It leaves the difficult leaf edge and thin stem as unknown, where shader-side evaluation can preserve their detail. + +| Decision | Suggested record | +| --- | --- | +| Use OMM | Yes; the mask is stable, the asset receives rays, and most regions resolve | +| Format and subdivision | 4-state at level 3 | +| Fallback | Build the original alpha-tested BLAS without OMM and keep the same cutoff, filtering, and alpha source | +| Image validation | Compare OMM-on, OMM-off, and reference images across shadows, reflections, LODs, and camera distances | +| Runtime validation | Record device support, successful micromap and BLAS builds, triangle links, state ratios, and active pipeline or shader controls | +| Rebuild rule | Rebuild after changes to triangle order, UVs, alpha data, cutoff, filtering, or LOD source data | + +The device also supports the level-5, 2-state candidate, and that option removes all unknown regions. However, its 128-byte raw payload exceeds the asset budget. Reconsider it only if the resource budget changes and profiling shows enough benefit from removing the remaining shader-side work. + +## Diagnose a failed validation + +Validation can fail even when the bake itself succeeds. Start with the symptom you can observe, then work backward through the owner and data most likely to explain it: + +| Observation | Start with | Inspect | +| --- | --- | --- | +| The leaf loses edge detail | Content and baker | Cutoff, UVs, subdivision, filtering, and states | +| The OMM build fails | Engine and driver | Features, sizes, buffers, and synchronization | +| The BLAS has no OMM links | Engine | Triangle mapping and BLAS input | +| Mali G2-Ultra NX does not enable OMM | Engine and driver | Extension, feature, limits, and pipeline settings | +| Unknown covers too much area | Content and baker | Texture detail, format, subdivision, and filtering | +| OMM and reference images differ | All owners | Asset version, links, states, and runtime controls | + +## What you've accomplished + +You started with a familiar rendering problem: rays repeatedly testing transparent parts of alpha-masked geometry. From there, you qualified an asset, classified its opacity regions, and chose a format and subdivision level. You then connected that data to the Mali G2-Ultra NX traversal path and kept the original alpha-tested path as a fallback. + +Your adoption record gives the team something concrete to review. It captures not only the chosen settings, but also the image and runtime evidence needed to check the decision again after an asset, engine, or driver change. diff --git a/content/learning-paths/mobile-graphics-and-gaming/omm-mali-intro/_index.md b/content/learning-paths/mobile-graphics-and-gaming/omm-mali-intro/_index.md new file mode 100644 index 0000000000..81771f4a71 --- /dev/null +++ b/content/learning-paths/mobile-graphics-and-gaming/omm-mali-intro/_index.md @@ -0,0 +1,65 @@ +--- +title: Evaluate Opacity Micromaps for ray-traced game assets + +draft: true +cascade: + draft: true + +description: Learn how Opacity Micromaps reduce opacity work during ray traversal, then choose an OMM strategy and fallback for an alpha-tested asset. + +minutes_to_complete: 40 + +who_is_this_for: Use this Learning Path if you develop game graphics and need to evaluate whether an alpha-tested asset is suitable for Opacity Micromaps on Arm Mali G2-Ultra NX. You can complete the exercises without OMM-capable hardware. + +learning_objectives: + - Classify microtriangles as opaque, transparent, or unknown from alpha-mask data. + - Trace OMM data from baking through acceleration-structure traversal. + - Choose an OMM format, subdivision strategy, fallback, and validation plan for an asset. + - Identify the Vulkan enablement requirements for ray pipelines and ray queries. + +prerequisites: + - Basic familiarity with triangle geometry and alpha-tested materials + - Basic familiarity with ray tracing intersections and acceleration structures + - No OMM-capable hardware is needed for the main exercises + - An Arm Mali G2-Ultra NX device and Vulkan capability tools are optional for device validation + +author: Patrick Wang + +### Tags +skilllevels: Advanced +subjects: Graphics +armips: + - Mali +tools_software_languages: + - Vulkan +operatingsystems: + - Android + +further_reading: + - resource: + title: VK_KHR_opacity_micromap + link: https://registry.khronos.org/vulkan/specs/latest/man/html/VK_KHR_opacity_micromap.html + type: documentation + - resource: + title: Vulkan opacity micromap chapter + link: https://docs.vulkan.org/spec/latest/chapters/VK_KHR_opacity_micromap/micromaps.html + type: documentation + - resource: + title: SPV_KHR_opacity_micromap + link: https://github.khronos.org/SPIRV-Registry/extensions/KHR/SPV_KHR_opacity_micromap.html + type: documentation + - resource: + title: GLSL opacity micromap ray query mode + link: https://github.com/KhronosGroup/GLSL/blob/main/extensions/ext/GLSL_EXT_opacity_micromap_ray_query_mode.txt + type: documentation + - resource: + title: Arm Mali G2-Ultra NX + link: https://www.arm.com/products/silicon-ip-multimedia/gpu/mali-g2-ultra-nx + type: website + +### FIXED, DO NOT MODIFY +# ================================================================================ +weight: 1 +layout: "learningpathall" +learning_path_main_page: "yes" +--- diff --git a/content/learning-paths/mobile-graphics-and-gaming/omm-mali-intro/_next-steps.md b/content/learning-paths/mobile-graphics-and-gaming/omm-mali-intro/_next-steps.md new file mode 100644 index 0000000000..e20dfa6d43 --- /dev/null +++ b/content/learning-paths/mobile-graphics-and-gaming/omm-mali-intro/_next-steps.md @@ -0,0 +1,8 @@ +--- +# ================================================================================ +# FIXED, DO NOT MODIFY THIS FILE +# ================================================================================ +weight: 21 +title: "Next Steps" +layout: "learningpathall" +--- diff --git a/content/learning-paths/mobile-graphics-and-gaming/omm-mali-intro/images/omm-introduction/01-masked-geometry.webp b/content/learning-paths/mobile-graphics-and-gaming/omm-mali-intro/images/omm-introduction/01-masked-geometry.webp new file mode 100644 index 0000000000..2c61e099a5 Binary files /dev/null and b/content/learning-paths/mobile-graphics-and-gaming/omm-mali-intro/images/omm-introduction/01-masked-geometry.webp differ diff --git a/content/learning-paths/mobile-graphics-and-gaming/omm-mali-intro/images/omm-introduction/02-microtriangle-states.webp b/content/learning-paths/mobile-graphics-and-gaming/omm-mali-intro/images/omm-introduction/02-microtriangle-states.webp new file mode 100644 index 0000000000..a1374ce8fc Binary files /dev/null and b/content/learning-paths/mobile-graphics-and-gaming/omm-mali-intro/images/omm-introduction/02-microtriangle-states.webp differ diff --git a/content/learning-paths/mobile-graphics-and-gaming/omm-mali-intro/images/omm-introduction/04-mali-hardware-flow.webp b/content/learning-paths/mobile-graphics-and-gaming/omm-mali-intro/images/omm-introduction/04-mali-hardware-flow.webp new file mode 100644 index 0000000000..a982daf9d0 Binary files /dev/null and b/content/learning-paths/mobile-graphics-and-gaming/omm-mali-intro/images/omm-introduction/04-mali-hardware-flow.webp differ diff --git a/content/learning-paths/mobile-graphics-and-gaming/omm-mali-intro/images/omm-introduction/06-responsibility-map.webp b/content/learning-paths/mobile-graphics-and-gaming/omm-mali-intro/images/omm-introduction/06-responsibility-map.webp new file mode 100644 index 0000000000..fe415696ad Binary files /dev/null and b/content/learning-paths/mobile-graphics-and-gaming/omm-mali-intro/images/omm-introduction/06-responsibility-map.webp differ diff --git a/content/learning-paths/mobile-graphics-and-gaming/run-text-to-text-models-on-android/2-build-and-run.md b/content/learning-paths/mobile-graphics-and-gaming/run-text-to-text-models-on-android/2-build-and-run.md index 5bb5e25cca..8bfeee4d0c 100644 --- a/content/learning-paths/mobile-graphics-and-gaming/run-text-to-text-models-on-android/2-build-and-run.md +++ b/content/learning-paths/mobile-graphics-and-gaming/run-text-to-text-models-on-android/2-build-and-run.md @@ -87,6 +87,7 @@ When you want to try another runtime or workload, return to the following table: | [SmolLM2 360M Instruct](https://huggingface.co/Arm/smollm2-360m-instruct-8da4w-xnnpack-executorch) | ExecuTorch 1.1.0 | Text generation | `smollm2-executorch` | | [BGE Base English v1.5](https://huggingface.co/Arm/bge-base-en-v1.5-int8-litert) | LiteRT 1.4.2 | Text embedding | `bge-base-litert` | | [TinyLlama 1.1B Chat](https://huggingface.co/Arm/tinyllama-1-1b-chat-onnx-genai-int4-kquantlast-emb-int8-vivo-x300) | ONNX Runtime GenAI | Text generation | `tinyllama-onnx-genai` | +| [Llama 3.2 1B Instruct](https://huggingface.co/Arm/llama-3-2-1b-instruct-onnx-genai-int4-kquantlast-emb-int8-vivo-x300) | ONNX Runtime GenAI | Text generation | `llama-3-2-onnx-genai` | The model and runtime change together in these examples, so their results and timings aren't controlled runtime benchmarks. diff --git a/content/learning-paths/mobile-graphics-and-gaming/run-text-to-text-models-on-android/4-compare-runtime-adapters.md b/content/learning-paths/mobile-graphics-and-gaming/run-text-to-text-models-on-android/4-compare-runtime-adapters.md index e338631573..da6866e310 100644 --- a/content/learning-paths/mobile-graphics-and-gaming/run-text-to-text-models-on-android/4-compare-runtime-adapters.md +++ b/content/learning-paths/mobile-graphics-and-gaming/run-text-to-text-models-on-android/4-compare-runtime-adapters.md @@ -24,7 +24,7 @@ The downloaded model package isn't an Android executable. Gradle packages the ru ## Choose a text-generation path -The application includes two validated text-generation paths that share the interface and deployment flow. However, each needs an adapter because its runtime API, model format, tokenizer handling, and output contract differ. +The application includes three validated text-generation examples that share the interface and deployment flow. However, each needs an adapter because its runtime API, model format, tokenizer handling, and output contract differ. The following table describes the paths: @@ -32,6 +32,7 @@ The following table describes the paths: | --- | --- | --- | --- | --- | | SmolLM2 with ExecuTorch | `executorch` | `inference/models/smollm2/ExecuTorchTextGenerationAdapter` | `.pte`, configuration, tokenizer, and chat template | The recommended default and smallest validated generation example. | | TinyLlama with ONNX Runtime GenAI | `onnxruntime` | `inference/models/tinyllama/OnnxTextGenerationAdapter` | ONNX model directory, configuration, tokenizer, and chat template | A directory-based GenAI package with an additional Android AAR dependency. | +| Llama 3.2 with ONNX Runtime GenAI | `onnxruntime` | `inference/models/llama32/OnnxTextGenerationAdapter` | ONNX model directory, configuration, tokenizer, and chat template | The same GenAI runtime flow with the Llama 3.2 package. | The repository also contains `inference/models/bge/LiteRtEmbeddingAdapter` for the catalog runtime `litert`. That adapter runs the BGE text-embedding workload and returns vectors rather than generated text, so it is listed separately from the text-generation paths. @@ -53,43 +54,86 @@ This checks compatibility with the adapter and doesn't evaluate the model's lang ### Prepare the ONNX Runtime GenAI path -The TinyLlama example uses `com.microsoft.onnxruntime:onnxruntime-android:1.27.0` and the official ONNX Runtime GenAI Android AAR. First, complete the [ONNX Runtime GenAI Android Learning Path](https://learn.arm.com/learning-paths/mobile-graphics-and-gaming/build-android-chat-app-using-onnxruntime/) to build `onnxruntime-genai-release.aar`. +The ONNX Runtime GenAI examples use `com.microsoft.onnxruntime:onnxruntime-android:1.27.0` and the official ONNX Runtime GenAI Android AAR. Download the official pre-compiled ONNX Runtime GenAI Android AAR before you apply an ONNX Runtime GenAI adapter. -Create a separate project and download the complete model directory: +{{< tabpane code=true >}} + {{< tab header="macOS or Linux" language="bash" >}} +export WORK_DIR="$HOME/text-to-text-android" +mkdir -p "$WORK_DIR" + +export ONNX_GENAI_AAR="$WORK_DIR/onnxruntime-genai-release.aar" + +curl --fail --location \ + "https://github.com/microsoft/onnxruntime-genai/releases/download/v0.16.0/onnxruntime-genai-android-0.16.0.aar" \ + --output "$ONNX_GENAI_AAR" + +test -f "$ONNX_GENAI_AAR" + {{< /tab >}} + {{< tab header="Windows PowerShell" language="powershell" >}} +$WORK_DIR = Join-Path $HOME "text-to-text-android" +New-Item -ItemType Directory -Force -Path $WORK_DIR + +$ONNX_GENAI_AAR = Join-Path $WORK_DIR "onnxruntime-genai-release.aar" + +Invoke-WebRequest ` + -Uri "https://github.com/microsoft/onnxruntime-genai/releases/download/v0.16.0/onnxruntime-genai-android-0.16.0.aar" ` + -OutFile $ONNX_GENAI_AAR + +Test-Path $ONNX_GENAI_AAR + {{< /tab >}} +{{< /tabpane >}} + +Create a separate project and select one ONNX Runtime GenAI example: {{< tabpane code=true >}} {{< tab header="macOS or Linux" language="bash" >}} -cd .. +cd "$WORK_DIR" git clone https://github.com/arm-education/ai-portal-android-app-text-to-text.git \ ai-portal-android-app-text-to-text-onnx cd ai-portal-android-app-text-to-text-onnx -cp -R adapter-examples/tinyllama-onnx-genai/app/. app/ - +# Choose one validated ONNX Runtime GenAI example. +export ONNX_EXAMPLE="tinyllama-onnx-genai" export MODEL_ID="tinyllama-1-1b-chat-onnx-genai-int4-kquantlast-emb-int8-vivo-x300" +# Or use Llama 3.2 instead: +# export ONNX_EXAMPLE="llama-3-2-onnx-genai" +# export MODEL_ID="llama-3-2-1b-instruct-onnx-genai-int4-kquantlast-emb-int8-vivo-x300" + +cp -R "adapter-examples/$ONNX_EXAMPLE/app/." app/ +mkdir -p app/libs +cp "$ONNX_GENAI_AAR" app/libs/onnxruntime-genai-release.aar + export MODEL_DIR="../model-onnx" ../.hf-venv/bin/hf download "Arm/$MODEL_ID" --local-dir "$MODEL_DIR" {{< /tab >}} {{< tab header="Windows PowerShell" language="powershell" >}} -Set-Location .. +Set-Location $WORK_DIR git clone https://github.com/arm-education/ai-portal-android-app-text-to-text.git ` ai-portal-android-app-text-to-text-onnx Set-Location ai-portal-android-app-text-to-text-onnx -Copy-Item -Path "adapter-examples\tinyllama-onnx-genai\app\*" ` +# Choose one validated ONNX Runtime GenAI example. +$ONNX_EXAMPLE = "tinyllama-onnx-genai" +$MODEL_ID = "tinyllama-1-1b-chat-onnx-genai-int4-kquantlast-emb-int8-vivo-x300" +# Or use Llama 3.2 instead: +# $ONNX_EXAMPLE = "llama-3-2-onnx-genai" +# $MODEL_ID = "llama-3-2-1b-instruct-onnx-genai-int4-kquantlast-emb-int8-vivo-x300" + +Copy-Item -Path "adapter-examples\$ONNX_EXAMPLE\app\*" ` -Destination "app" -Recurse -Force +New-Item -ItemType Directory -Force -Path "app\libs" +Copy-Item -Path $ONNX_GENAI_AAR -Destination "app\libs\onnxruntime-genai-release.aar" -Force -$MODEL_ID = "tinyllama-1-1b-chat-onnx-genai-int4-kquantlast-emb-int8-vivo-x300" $MODEL_DIR = "..\model-onnx" ..\.hf-venv\Scripts\hf.exe download "Arm/$MODEL_ID" --local-dir $MODEL_DIR {{< /tab >}} {{< /tabpane >}} -Copy the generated AAR to `app/libs/onnxruntime-genai-release.aar` before continuing. Keep every file in the downloaded ONNX model directory together, including external model data, `genai_config.json`, tokenizer files, and the chat template. +The commands above copy the generated AAR to `app/libs/onnxruntime-genai-release.aar`. Keep every file in the downloaded ONNX model directory together, including external model data, `genai_config.json`, tokenizer files, and the chat template. ## Build and run the selected alternative -The ONNX Runtime GenAI alternative now follows the same workflow: build the APK, install the APK, copy the chosen model package to the directory named by its catalog ID, and start the application. +The selected ONNX Runtime GenAI alternative now follows the same workflow: build the APK, install the APK, copy the chosen model package to the directory named by its catalog ID, and start the application. Continue in the same terminal so that `MODEL_ID` and `MODEL_DIR` remain set: diff --git a/content/learning-paths/mobile-graphics-and-gaming/swin2sr-vgf/1-prepare-environment.md b/content/learning-paths/mobile-graphics-and-gaming/swin2sr-vgf/1-prepare-environment.md index 7f6a5eeb8e..3c10e91642 100644 --- a/content/learning-paths/mobile-graphics-and-gaming/swin2sr-vgf/1-prepare-environment.md +++ b/content/learning-paths/mobile-graphics-and-gaming/swin2sr-vgf/1-prepare-environment.md @@ -1,5 +1,5 @@ --- -title: Prepare your environment and image +title: Prepare your host and image for Swin2SR description: Install the Swin2SR example and Arm ML SDK, then prepare a 64 × 64 input image and its high-resolution reference. weight: 2 @@ -7,19 +7,25 @@ weight: 2 layout: learningpathall --- +## Swin2SR and super-resolution + +[Swin2SR](https://github.com/mv-lab/swin2sr) is a neural network for image super-resolution and restoration, built on the Swin Transformer V2 architecture. Super-resolution means estimating a higher-resolution image from a lower-resolution input. The model uses patterns learned during training to reconstruct details such as edges and textures. + +The Swin2SR transformer processes image features in small regions called windows and shifts those windows between layers to share information across regions. You'll use a pretrained model, so that you can run it without training it yourself. + ## What you'll build -You will upscale a low-resolution image using Swin2SR, a pretrained image super-resolution model. It predicts finer detail as it doubles the image's width and height. +You'll use the ×2 version of Swin2SR to turn a 64 × 64 image into a 128 × 128 image. It doubles the width and height, predicting finer detail. These predictions can differ from the original image, which is why you compare the result with a high-resolution reference. -You export the model with ExecuTorch, then run it through the Arm Vulkan Graph Format (VGF) backend. The result is a PNG image you can open and compare with a high-resolution reference. +You'll export the model with ExecuTorch to create a `.pte` program, then build a host runner to execute it through the Arm Vulkan Graph Format (VGF) backend. An image helper connects your input image to the runner and saves the result in PNG format. You'll then check its dimensions and compare it with a high-resolution reference. ![Equal-size views compare a 64 by 64 low-resolution input with its 128 by 128 Swin2SR output. The input is enlarged for display only; labels show actual pixel dimensions. ExecuTorch and Arm VGF run the model on the host.#center](swin2sr-image-flow.svg "Images shown at the same display size to compare detail; labels show actual resolution") -This workflow runs on your Linux host using the Arm ML SDK's Vulkan emulation layer. It introduces the model execution flow used by Arm neural graphics; it doesn't deploy an application to a phone or measure Mali GPU performance. +You'll run the workflow on your Linux host using the Arm ML SDK's Vulkan emulation layer. The emulation layer introduces the model execution flow used by Arm neural graphics. It doesn't deploy an application to a phone or measure Mali GPU performance. ## Prepare the Linux host -Use a 64-bit Linux system (AArch64 or x86_64) with a working Vulkan 1.3 GPU driver. The packaged ML SDK checks for the `shaderFloat64` feature, even though you export a floating-point 32-bit model. The setup script stops if your GPU doesn't support it. Apple Silicon with MoltenVK needs a separate source-built SDK, which isn't covered here. +Use a 64-bit Linux system (AArch64 or x86_64) with a working Vulkan 1.3 GPU driver. The packaged ML SDK checks for the `shaderFloat64` feature, even though you'll export a floating-point 32-bit model. The setup script stops if your GPU doesn't support it. Apple silicon with MoltenVK needs a separate source-built SDK. On Ubuntu 24.04, install Python 3.12, the build tools, and the Vulkan development files: @@ -31,11 +37,9 @@ sudo apt-get install -y \ libvulkan1 libvulkan-dev vulkan-tools unzip xz-utils ``` -These packages don't install your GPU's vendor-specific driver. The Python installation step installs CMake 3.31.10. - ## Get the ExecuTorch release -Use [ExecuTorch 1.5.1](https://github.com/pytorch/executorch/releases/tag/v1.5.1) for both the Python package and native source. From a path without spaces or an existing `executorch` folder, clone the matching release. Keep the checkout folder named `executorch`; the build requires this exact name: +Use [ExecuTorch 1.5.1](https://github.com/pytorch/executorch/releases/tag/v1.5.1) for both the Python package and native source. Clone the matching release from a directory whose path has no spaces and that has no existing `executorch` folder. Keep the checkout folder named `executorch` as the build requires this exact name: ```bash git clone --branch v1.5.1 --single-branch --depth 1 \ @@ -46,11 +50,13 @@ git submodule sync --recursive git submodule update --init --recursive ``` +The `test` command checks that the checkout matches the expected commit. Success produces no output. The recursive submodule commands synchronize and download the source dependencies, including nested submodules. + Run the remaining commands from this repository root, in the same terminal. ## Configure the Arm ML SDK -Create a fresh Python environment: +Create a fresh Python environment to keep this example's packages separate from your system Python. Activate the environment so that subsequent `python` and `pip` commands use it: ```bash python3.12 -m venv .venv @@ -58,13 +64,13 @@ source .venv/bin/activate python -m pip install --upgrade pip ``` -Review the [ML SDK license terms](https://github.com/arm/ai-ml-sdk-for-vulkan/tree/main/LICENSES) and the Vulkan SDK terms. Use the setup script included in the release to install the SDK tools: +Review the [ML SDK license terms](https://github.com/arm/ai-ml-sdk-for-vulkan/tree/main/LICENSES) and the Vulkan SDK terms. Use the release's setup script to install the SDK tools. `--enable-mlsdk-deps` selects the ML SDK dependencies for VGF, and `--disable-ethos-u-deps` skips the Ethos-U dependencies: ```bash bash examples/arm/setup.sh --disable-ethos-u-deps --enable-mlsdk-deps ``` -Remove three developer packages installed by setup that this example doesn't use: +Remove three installed developer packages that you don't need for the Learning Path: ```bash python -m pip uninstall -y \ @@ -87,13 +93,13 @@ python -m pip install \ python -m pip check ``` -`pip check` should report `No broken requirements found.` The CPU wheels are for export; the host runner still uses Vulkan. +After a successful installation, `pip check` should report `No broken requirements found.` The CPU wheels are for export. The host runner still uses Vulkan. These commands replace `install_executorch.sh`, which uses nightly and test indexes. If you rerun SDK setup, repeat the package removal and installation. ## Check the tools -Activate the SDK paths and check the export and GPU prerequisites: +Source `setup_path.sh` to make the SDK tools and libraries available in this terminal. The `--aot` check verifies the ahead-of-time export dependencies. The import check tests ExecuTorch's export module and runtime bindings, while `vulkaninfo` checks the GPU feature required by the SDK: ```bash source examples/arm/arm-scratch/setup_path.sh @@ -102,7 +108,30 @@ python -c "import executorch.exir; from executorch.extension.pybindings import p vulkaninfo | grep shaderFloat64 ``` -Resolve any `FAIL` entries before continuing. Confirm `shaderFloat64 = true` for your device, and keep this terminal open for the remaining steps. +The output includes lines similar to: + +```output +[PASS] Python version + Python 3.12 meets the recommended VGF minimum (3.12). + +[PASS] TOSA serializer + Imported tosa_serializer from /home/ubuntu/executorch/.venv/lib/python3.12/site-packages/tosa_serializer/__init__.py (version=1.1). + +[PASS] MLSDK model converter + /home/ubuntu/executorch/.venv/bin/model-converter --version succeeded (version=0.10.0): +{ + "version": "19d1d0f", + "dependencies": [ + "argparse=v3.1-0-g68fd027", + +[PASS] MODEL_CONVERTER_LIB_DIR + MODEL_CONVERTER_LIB_DIR is not set; relying on the process loader paths. This is OK when model-converter --version succeeds. +ExecuTorch is ready +shaderFloat64 = true +``` +Your paths might vary depending on your checkout location. + +The `[PASS]` entries report successful export dependency checks. `ExecuTorch is ready` confirms that the imports succeeded. Confirm `shaderFloat64 = true` for your device, and keep this terminal open for the remaining steps. ## Prepare the input image @@ -113,15 +142,26 @@ python examples/arm/super_resolution_example_vgf/model_export/prepare_demo_asset --output-dir swin2sr-work ``` -The script creates a 128 × 128 crop and downsizes a copy to 64 × 64. You use these two files: +The output is similar to: -| File in `swin2sr-work/runtime/` | What it is | +```output +Prepared demo assets under /home/ubuntu/executorch/swin2sr-work +Runtime input: /home/ubuntu/executorch/swin2sr-work/runtime/demo_lr_64.png +Runtime reference: /home/ubuntu/executorch/swin2sr-work/runtime/demo_hr_128.png +``` +The paths reflect your checkout location and might vary. + +The script creates a 128 × 128 crop and downsizes a copy to 64 × 64. You'll use the following files: + +| File in `swin2sr-work/runtime/` | What the image is | | --- | --- | -| `demo_lr_64.png` | Small image you give to the model | -| `demo_hr_128.png` | High-resolution reference: the original crop before downsampling | +| `demo_lr_64.png` | Small image that you'll pass to the model | +| `demo_hr_128.png` | A high-resolution reference: the original crop before downsampling | -The helper also prepares calibration and evaluation folders. You don't need them for this floating-point walkthrough. +The helper also prepares calibration and evaluation folders that you don't need for this workflow. ## What you've accomplished and what's next -You have the example, its tools, and a small input image with a high-resolution reference. Next, export Swin2SR as an ExecuTorch program. +You've set up the example, its tools, and a small input image with a high-resolution reference. + +Next, you'll export Swin2SR as an ExecuTorch program. diff --git a/content/learning-paths/mobile-graphics-and-gaming/swin2sr-vgf/2-export-model.md b/content/learning-paths/mobile-graphics-and-gaming/swin2sr-vgf/2-export-model.md index 6ea07ed64a..5b8687389a 100644 --- a/content/learning-paths/mobile-graphics-and-gaming/swin2sr-vgf/2-export-model.md +++ b/content/learning-paths/mobile-graphics-and-gaming/swin2sr-vgf/2-export-model.md @@ -9,7 +9,7 @@ layout: learningpathall ## Export the pretrained model -You don't need to train Swin2SR. The exporter downloads the pretrained ×2 checkpoint and converts it into an ExecuTorch `.pte` program. The pinned checkpoint revision keeps the model weights consistent between runs. +You don't need to train Swin2SR. The exporter downloads the pretrained ×2 checkpoint — the saved model weights — and converts it into an ExecuTorch `.pte` program. The pinned checkpoint revision keeps the model weights consistent between runs. Run the exporter from your ExecuTorch directory: @@ -30,16 +30,18 @@ The `64` dimensions fix the input size for this export. The model produces a 128 ## Keep the program and metadata together -After export finishes, check the two files the runner needs: +After export finishes, check the two files that the runner needs: ```bash ls -lh swin2sr-work/swin2sr.pte swin2sr-work/swin2sr.json ``` -`swin2sr.pte` contains the executable model, including its VGF graphs. `swin2sr.json` tells the image helper how to read the input and reconstruct the output. Keep both files in the same directory with the same base name. +`swin2sr.pte` contains the executable model, including its Vulkan Graph Format (VGF) graphs. `swin2sr.json` tells the image helper how to read the input and reconstruct the output. Keep both files in the same directory with the same base name. The exporter also saves `swin2sr_delegation.txt`. It records which operations run through VGF and which remain in ExecuTorch. You don't need to change this report to run the example. ## What you've accomplished and what's next -You have a floating-point Swin2SR program configured for one 64 × 64 RGB image. Next, build the host runner and use it to upscale your image. +You have a floating-point Swin2SR program configured for one 64 × 64 image with red, green, and blue (RGB) color channels. + +Next, you'll build the host runner and use it to upscale your image. diff --git a/content/learning-paths/mobile-graphics-and-gaming/swin2sr-vgf/3-run-model.md b/content/learning-paths/mobile-graphics-and-gaming/swin2sr-vgf/3-run-model.md index 407edeab5c..4632d50863 100644 --- a/content/learning-paths/mobile-graphics-and-gaming/swin2sr-vgf/3-run-model.md +++ b/content/learning-paths/mobile-graphics-and-gaming/swin2sr-vgf/3-run-model.md @@ -11,21 +11,21 @@ layout: learningpathall ExecuTorch's `executor_runner` loads your `.pte` program and executes it. Continue from the same terminal in your `executorch` directory. -If you opened a new terminal, restore the environment first: +If you open a new terminal, you'll have to restore the environment: ```bash source .venv/bin/activate source examples/arm/arm-scratch/setup_path.sh ``` -Use the repository's build script to enable VGF and the runtime libraries it needs: +Use the repository's build script to enable Vulkan Graph Format (VGF) and the runtime libraries that it needs: ```bash bash backends/arm/scripts/build_executor_runner_vkml.sh \ --output=swin2sr-work/build ``` -This builds a Release executable at `swin2sr-work/build/executor_runner`. You only need to build it once for this walkthrough. +This builds a release executable at `swin2sr-work/build/executor_runner`. You need to build the executable only once. ## Upscale the image @@ -39,10 +39,19 @@ python examples/arm/super_resolution_example_vgf/runtime/run_super_resolution.py --output-image swin2sr-work/runtime/demo_sr_128.png ``` -The helper converts the image into a tensor, runs the model, and saves the output tensor as a PNG. A successful run ends with `Saved super-resolved image to` followed by the full path to `demo_sr_128.png`. +The output is similar to: -Your input must be exactly 64 × 64 pixels. If you see an `expected (1, 3, 64, 64)` error, check that you used `demo_lr_64.png`, not the larger reference image. +```output +Saved super-resolved image to /home/ubuntu/executorch/swin2sr-work/runtime/demo_sr_128.png +``` +The path reflects your checkout location. + +The helper converts the image into a tensor — a numerical representation of its pixels. It runs the model and saves the output tensor as a PNG. A successful run ends with `Saved super-resolved image to`, followed by the full path to `demo_sr_128.png`. + +Your input needs to be exactly 64 × 64 pixels. If you see an `expected (1, 3, 64, 64)` error, check that you used `demo_lr_64.png`, not the larger reference image. ## What you've accomplished and what's next -You have executed the exported program and saved its 128 × 128 output. Next, open the result and compare it with the input and high-resolution reference. +You've executed the exported program and saved its 128 × 128 output. + +Next, you'll open the result and compare it with the input and high-resolution reference. diff --git a/content/learning-paths/mobile-graphics-and-gaming/swin2sr-vgf/4-inspect-result.md b/content/learning-paths/mobile-graphics-and-gaming/swin2sr-vgf/4-inspect-result.md index fc14eacea2..63fa2090e5 100644 --- a/content/learning-paths/mobile-graphics-and-gaming/swin2sr-vgf/4-inspect-result.md +++ b/content/learning-paths/mobile-graphics-and-gaming/swin2sr-vgf/4-inspect-result.md @@ -1,5 +1,5 @@ --- -title: Inspect the upscaled image +title: Inspect the Swin2SR output description: Verify the Swin2SR output size and compare the generated image with the low-resolution input and high-resolution reference. weight: 5 @@ -7,7 +7,7 @@ weight: 5 layout: learningpathall --- -## Check the image dimensions +## Check the dimensions of the upscaled image Confirm that the input is 64 × 64 and that the model created a 128 × 128 RGB image: @@ -33,17 +33,23 @@ Twice the width and twice the height give you four times as many pixels. ## Compare the images -Open `swin2sr-work/runtime/` in your image viewer. Compare the low-resolution input, the Swin2SR output, and the high-resolution reference. Enlarge the input to the same display size so you can compare the same text and edges. +Open `swin2sr-work/runtime/` in your image viewer. Compare the low-resolution input, the Swin2SR output, and the high-resolution reference. Enlarge the input to the same display size so that you can compare the same text and edges. -The high-resolution reference is the original 128 × 128 crop, before downsampling creates the 64 × 64 input. You keep it for comparison; the model receives only the low-resolution input. +The high-resolution reference is the original 128 × 128 crop, before downsampling creates the 64 × 64 input. Use the reference for comparison; the model receives only the low-resolution input. ![Low-resolution input enlarged for display, an actual Swin2SR output from an earlier run, and the high-resolution reference at the same display size. Compare letter edges to see reconstructed detail and remaining differences.#center](swin2sr-result-comparison.png "Low-resolution input, Swin2SR output, and high-resolution reference at the same display size") -The example output shown here comes from an earlier run with the same checkpoint and demo image. It illustrates the comparison; it isn't a new measurement on your host. +The example output comes from an earlier run with the same checkpoint and demo image. The output illustrates the comparison and isn't a new measurement on your host. -Look at the edges of the letters. The output estimates detail that was lost when the original was reduced to 64 × 64. It won't reproduce every detail of the original, and a larger image doesn't automatically mean a more accurate one. +Look at the edges of the letters. The output estimates detail that was lost when the original was reduced to 64 × 64. It won't reproduce every detail of the original. A larger image doesn't automatically mean a more accurate one. -Your end-to-end flow succeeds when the runner finishes, the generated image has the expected dimensions, and it shows the same scene without obvious corruption. This visual check doesn't establish a quality benchmark or a performance result. +The following factors indicate the successful completion of the image upscaling flow: + +- The runner finishes +- The generated image has the expected dimensions +- The generated image shows the same scene without obvious corruption + +This visual check doesn't establish a quality benchmark or a performance result. ## Try another image @@ -61,4 +67,6 @@ For a different input size, export a matching program first. The helper doesn't ## What you've accomplished -You have prepared an image, exported a pretrained Swin2SR model, run it through Arm VGF with ExecuTorch, and inspected the upscaled output. You can now repeat the same flow with your own 64 × 64 images. +You've prepared an image, exported a pretrained Swin2SR model, and run it through Arm Vulkan Graph Format (VGF) with ExecuTorch. You've then inspected the upscaled output. + +You can now repeat the same flow with your own 64 × 64 images. diff --git a/content/learning-paths/mobile-graphics-and-gaming/swin2sr-vgf/_index.md b/content/learning-paths/mobile-graphics-and-gaming/swin2sr-vgf/_index.md index ba401ed631..d10b58be61 100644 --- a/content/learning-paths/mobile-graphics-and-gaming/swin2sr-vgf/_index.md +++ b/content/learning-paths/mobile-graphics-and-gaming/swin2sr-vgf/_index.md @@ -1,22 +1,18 @@ --- title: Upscale an image with Swin2SR and Arm VGF -draft: true -cascade: - draft: true - description: Export Swin2SR with ExecuTorch and run it through Arm VGF on your Linux host to upscale a low-resolution image and reconstruct finer detail. minutes_to_complete: 60 lastmod: 2026-09-14 -who_is_this_for: This is an introductory topic for machine learning and graphics developers who want to run image super-resolution with ExecuTorch and Arm VGF. +who_is_this_for: This Learning Path is for machine learning and graphics developers getting started with image super-resolution using ExecuTorch and Arm Vulkan Graph Format (VGF). learning_objectives: - - Prepare ExecuTorch, the Arm ML SDK for Vulkan, and a sample image - - Export a pretrained Swin2SR model as a VGF-backed ExecuTorch program - - Build the host runner and upscale a 64 × 64 image to 128 × 128 - - Check the output dimensions and compare the result with the high-resolution reference + - Prepare ExecuTorch, the Arm ML SDK for Vulkan, and a sample image. + - Export a pretrained Swin2SR model as a VGF-backed ExecuTorch program. + - Build the host runner and upscale a 64 × 64 image to 128 × 128. + - Check the output dimensions and compare the result with the high-resolution reference. prerequisites: - A 64-bit Linux host (AArch64 or x86_64) with a Vulkan 1.3 GPU and driver that support shaderFloat64, as required by the packaged ML SDK emulation layer @@ -24,9 +20,54 @@ prerequisites: - An internet connection to download ExecuTorch, model weights, and the Arm ML SDK dependencies - Basic familiarity with Python and command-line tools +# START generated_summary_faq +generated_summary_faq: + template_version: summary-faq-v3 + generated_at: '2026-10-05T19:42:27Z' + generator: ai + ai_assisted: true + ai_review_required: true + model: gpt-5 + prompt_template: summary-faq-v3 + source_hash: 1da8efb1fa123c1aeb23cf8610070bd13ebb38d1fb191f33bd83b284b7b0f266 + summary_generated_at: '2026-10-05T19:42:27Z' + summary_source_hash: 1da8efb1fa123c1aeb23cf8610070bd13ebb38d1fb191f33bd83b284b7b0f266 + faq_generated_at: '2026-10-05T19:42:27Z' + faq_source_hash: 1da8efb1fa123c1aeb23cf8610070bd13ebb38d1fb191f33bd83b284b7b0f266 + summary: >- + You'll set up a Linux host and sample image, export a pretrained Swin2SR ×2 model as an + ExecuTorch program, and build a host runner for Arm VGF. Then, you'll + upscale a 64 × 64 image to 128 × 128. Finally, you'll check the output dimensions and compare + its reconstructed detail with the high-resolution reference. + faqs: + - question: How do I restore the environment if I open a new terminal? + answer: >- + From your `executorch` directory, run `source .venv/bin/activate` and then + `source examples/arm/arm-scratch/setup_path.sh` before building or running the model. + - question: Do I need to train Swin2SR or prepare calibration images? + answer: >- + No. You'll export a pretrained ×2 checkpoint pinned to a specific revision. The export uses + `--quantization-mode none`, so you don't need calibration images. + - question: Why do I need both Swin2SR PTE and JSON files? + answer: >- + The `.pte` file contains your executable model, while the `.json` file tells the image + helper how to read the input and reconstruct the output. Keep them in the same directory + with the same base name. + - question: Can I use my own image with the exported program? + answer: >- + Yes, if your image is 64 × 64 pixels and RGB. For a different input size, export a + matching program first. The helper doesn't resize or tile images automatically. + - question: How do I know that the image run succeeded? + answer: >- + Look for `Saved super-resolved image to` in the terminal output. Check that your generated + image is 128 × 128 and shows the same scene without obvious corruption. You can + compare it with the high-resolution reference, but the visual check isn't a quality + benchmark or performance result. +# END generated_summary_faq + author: Usamah Zaheer -generate_summary_faq: true +generate_summary_faq: false rerun_summary: false rerun_faqs: false diff --git a/content/learning-paths/mobile-graphics-and-gaming/using-neon-intrinsics-to-optimize-unity-on-android/7-analyzing.md b/content/learning-paths/mobile-graphics-and-gaming/using-neon-intrinsics-to-optimize-unity-on-android/7-analyzing.md index c5b193bb42..66c4a0ee35 100644 --- a/content/learning-paths/mobile-graphics-and-gaming/using-neon-intrinsics-to-optimize-unity-on-android/7-analyzing.md +++ b/content/learning-paths/mobile-graphics-and-gaming/using-neon-intrinsics-to-optimize-unity-on-android/7-analyzing.md @@ -33,7 +33,7 @@ To compare the results you will use the Analyzer to import all three datasets bu You now have two datasets loaded, one from the unoptimized mode and one from the Burst mode. This is what the data looks like collected from our sample device. -![Plain vs Burst#center](images/analyzer-plain-vs-burst.PNG) +![Plain vs Burst#center](images/analyzer-plain-vs-burst.png) Focus on the collision-related markers by searching for _collisioncalc_ in the _Name Filter_. Select a representative frame by clicking on a later frame where both seem relatively settled (and with no odd spikes). @@ -45,6 +45,6 @@ In the top 10 markers, the unoptimized data is in blue. The Burst data is in ora Follow the same process again but this time we will compare Burst against Neon. -![Burst vs Neon#center](images/analyzer-burst-vs-neon.PNG) +![Burst vs Neon#center](images/analyzer-burst-vs-neon.png) Again, we can see an improvement, mostly with the dynamic (character-character) collision detection but together it still makes a meaningful improvement. diff --git a/content/learning-paths/mobile-graphics-and-gaming/using-neon-intrinsics-to-optimize-unity-on-android/images/analyzer-burst-vs-neon.PNG b/content/learning-paths/mobile-graphics-and-gaming/using-neon-intrinsics-to-optimize-unity-on-android/images/analyzer-burst-vs-neon.png similarity index 100% rename from content/learning-paths/mobile-graphics-and-gaming/using-neon-intrinsics-to-optimize-unity-on-android/images/analyzer-burst-vs-neon.PNG rename to content/learning-paths/mobile-graphics-and-gaming/using-neon-intrinsics-to-optimize-unity-on-android/images/analyzer-burst-vs-neon.png diff --git a/content/learning-paths/mobile-graphics-and-gaming/using-neon-intrinsics-to-optimize-unity-on-android/images/analyzer-plain-vs-burst.PNG b/content/learning-paths/mobile-graphics-and-gaming/using-neon-intrinsics-to-optimize-unity-on-android/images/analyzer-plain-vs-burst.png similarity index 100% rename from content/learning-paths/mobile-graphics-and-gaming/using-neon-intrinsics-to-optimize-unity-on-android/images/analyzer-plain-vs-burst.PNG rename to content/learning-paths/mobile-graphics-and-gaming/using-neon-intrinsics-to-optimize-unity-on-android/images/analyzer-plain-vs-burst.png diff --git a/content/learning-paths/servers-and-cloud-computing/_index.md b/content/learning-paths/servers-and-cloud-computing/_index.md index a7e2bb052c..6edb9b3634 100644 --- a/content/learning-paths/servers-and-cloud-computing/_index.md +++ b/content/learning-paths/servers-and-cloud-computing/_index.md @@ -344,7 +344,8 @@ tools_software_languages_filter: - Vectorscan: 1 - Velox: 1 - Veraison: 3 -- virsh / virt-manager: 1 +- virsh: 1 +- virt-install: 1 - Visual Studio Code: 7 - vLLM: 4 - VMAS: 1 diff --git a/content/learning-paths/servers-and-cloud-computing/arm_linux_page_size/_index.md b/content/learning-paths/servers-and-cloud-computing/arm_linux_page_size/_index.md index 13a4bf856d..8e8f7825ab 100644 --- a/content/learning-paths/servers-and-cloud-computing/arm_linux_page_size/_index.md +++ b/content/learning-paths/servers-and-cloud-computing/arm_linux_page_size/_index.md @@ -1,6 +1,6 @@ --- title: Explore performance gains by increasing the Linux kernel page size on Arm -description: Learn how to install and configure a Linux kernel with 64K page size support on Arm systems to improve memory efficiency and performance for memory-intensive workloads. +description: Learn how to install and configure Linux kernels with 16K or 64K page sizes on Arm systems, then evaluate the memory and performance trade-offs. minutes_to_complete: 30 @@ -10,8 +10,8 @@ learning_objectives: - Explain the differences in page size configuration between Arm64 and x86 architectures. - Understand how page size affects memory efficiency and system performance. - Check the current memory page size on an Arm-based Linux system. - - Install and boot into a Linux kernel configured with 64K page size support. - - Confirm that the 64K page size is active. + - Install and boot into a Linux kernel configured with 16K or 64K page size support. + - Confirm that the selected page size is active. - Optionally revert to the default 4K page size kernel. prerequisites: @@ -32,13 +32,11 @@ generated_summary_faq: faq_generated_at: '2026-06-26T17:30:49Z' faq_source_hash: 67004eb9a75926144eb6f75124104972b3cf20a57a22b698c9359d20db3eca02 summary: >- - You'll evaluate and change the Linux kernel base page size on Arm, focusing - on installing and booting a 64K configuration. First, you'll start with page size fundamentals and a - quick check of the current setting using `getconf` and `uname`. OS-specific steps include Ubuntu - and CentOS package-based flows, while Debian requires building a 64K kernel from source using - the Debian source package. After reboot, you'll validate the change by seeing `65536` from `getconf` - and the expected kernel string. You'll also review common Arm page size options and an optional - step to revert to the default 4K kernel after testing. + You'll evaluate and change the Linux kernel base page size on Arm by installing and booting a + 16K or 64K configuration. You'll check the current setting with `getconf` and `uname`, then use + distribution-specific instructions. Debian provides a packaged 16K kernel and a source-build + route for 64K, while Ubuntu and CentOS provide packaged 64K kernels. After rebooting, you'll + validate the selected page size and optionally return to the default 4K kernel. faqs: - question: How do I check the current base page size and kernel in use? answer: >- @@ -50,13 +48,13 @@ generated_summary_faq: as `16384` or `65536`. Note the current value and proceed only if a change is needed. - question: Which distribution-specific section should I follow? answer: >- - Use the Ubuntu section for Ubuntu 22.04 LTS or later, the Debian section for Debian 11 or - later, and the CentOS section for CentOS 9 or later. Debian doesn't provide a 64K kernel - package, so follow the steps to build from the Debian source package. - - question: How do I confirm that the 64K page size is active after installation and reboot? + Use the Ubuntu section for Ubuntu 22.04 LTS or later, the Debian section for Debian 12 or + later, and the CentOS section for CentOS 9 or later. On Debian, you can install the packaged + 16K kernel or build a 64K kernel from the Debian source package. + - question: How do I confirm that the selected page size is active after installation and reboot? answer: >- - Re-run `getconf PAGESIZE` and expect `65536`. Also check `uname -r` to verify the kernel string - matches the newly installed kernel. + Re-run `getconf PAGESIZE` and expect `16384` for a 16K kernel or `65536` for a 64K kernel. Also + check `uname -r` to verify that the running kernel matches the kernel you installed. - question: How can I revert to a 4K page size after testing? answer: >- Use the optional revert step to return to the distribution’s default 4K kernel, then verify diff --git a/content/learning-paths/servers-and-cloud-computing/arm_linux_page_size/debian.md b/content/learning-paths/servers-and-cloud-computing/arm_linux_page_size/debian.md index 487bb9ef7b..2f22bb605c 100644 --- a/content/learning-paths/servers-and-cloud-computing/arm_linux_page_size/debian.md +++ b/content/learning-paths/servers-and-cloud-computing/arm_linux_page_size/debian.md @@ -5,39 +5,91 @@ weight: 4 layout: learningpathall --- -Follow the steps below to install a 64K page size kernel on [Debian 11 “Bullseye” or later](https://www.debian.org/releases/bullseye/). - -Debian does not provide a 64K kernel package, so you will need to compile it from source. - -There are two ways to do this: -- Download the source from kernel.org. -- Use the Debian source package. - -The instructions below use the Debian source package. +You can install a packaged 16K page size kernel on Debian 12 or later. Debian doesn't provide a 64K kernel package, so you need to compile a 64K kernel from source. ## Verify the current page size -Verify you’re using a 4KB pagesize kernel by entering the following commands: +Verify that you're using a 4 KB page size kernel: ```bash getconf PAGESIZE uname -r ``` -The output should be similar to below. The kernel flavor (the string after the version number) may vary, but the first line should always be 4096. +The output is similar to the following. The kernel flavor (the string after the version number) can vary, but the first line is always 4096: ```output 4096 6.1.0-34-cloud-arm64 ``` -The 4096 indicates the current page size is 4KB. If you see a value that is different, you are already using a page size other than 4096 (4K). On Arm systems, the valid options are 4K, 16K, and 64K. +The 4096 indicates the current page size is 4 KB. If you see a different value, you're already using a page size other than 4 KB. On Arm systems, the valid options are 4 KB, 16 KB, and 64 KB. + +## Install a packaged 16K kernel + +Debian provides the [`linux-image-arm64-16k` package](https://packages.debian.org/linux-image-arm64-16k) for Debian 13 (Trixie) and through backports for Debian 12 (Bookworm). + +Check your Debian release codename: -## Install the Debian kernel source package +```bash +. /etc/os-release +echo "$VERSION_CODENAME" +``` + +The output is either `trixie` for Debian 13 or `bookworm` for Debian 12. -Follow the steps below to install a 64K kernel using the Debian kernel source package. +### Install on Debian 13 -First, update, and install the required software: +On Debian 13, install the 16K kernel directly from the standard package repositories: + +```bash +sudo apt-get update +sudo apt-get install linux-image-arm64-16k +``` + +### Install on Debian 12 + +On Debian 12, add the official Bookworm Backports repository by following the [Debian Backports configuration format](https://backports.debian.org/Instructions/): + +```bash +sudo tee /etc/apt/sources.list.d/debian-backports.sources > /dev/null <<'EOF' +Types: deb +URIs: http://deb.debian.org/debian +Suites: bookworm-backports +Components: main +Signed-By: /usr/share/keyrings/debian-archive-keyring.gpg +EOF +``` + +Update the package index and install the 16K kernel from backports: + +```bash +sudo apt-get update +sudo apt-get install -t bookworm-backports linux-image-arm64-16k +``` + +Reboot the system to load the new kernel: + +```bash +sudo reboot +``` + +After the system restarts, verify the page size and running kernel: + +```bash +getconf PAGESIZE +uname -r +``` + +The first command returns `16384`, and the kernel name ends in `arm64-16k`. + +If you want to test a 64K page size instead, continue with the source-build instructions. + +## Install a 64K kernel from source + +You can build a 64K kernel from the Debian source package. Another option is to download the source from kernel.org, but these instructions use the Debian source package. + +First, update the package index and install the required software: ```bash sudo apt-get -y update @@ -53,7 +105,7 @@ apt source linux cd -- linux*/ ``` -## Build and install the kernel +## Build and install the 64K kernel Now that you have the kernel source, follow these steps to build and install the kernel: @@ -100,9 +152,22 @@ The output shows the 64k kernel is running: 6.12.22-64k ``` -This indicates the current page size is 64K, and you are using the new custom-built 64k kernel. +This indicates the current page size is 64 KB, and you're using the new custom-built 64K kernel. + +## Revert from the 16K kernel + +Boot into the original 4K kernel from the bootloader's advanced options. Then remove the 16K kernel packages: + +```bash +dpkg-query -W -f='${Package}\n' 'linux-image-*16k*' 'linux-headers-*16k*' 2>/dev/null \ + | xargs --no-run-if-empty sudo apt-get purge -y +sudo update-grub +sudo reboot +``` + +After the system restarts, verify that `getconf PAGESIZE` returns `4096`. -## Revert to the 4K kernel +## Revert from the 64K kernel To revert to the kernel we started with, enter: @@ -127,4 +192,4 @@ The output should be similar to below -- the full kernel name may vary, but the 6.1.0-34-cloud-arm64 ``` -The 4096 indicates the current page size has been reverted to 4 KB. \ No newline at end of file +The 4096 indicates the current page size has been reverted to 4 KB. diff --git a/content/learning-paths/servers-and-cloud-computing/arm_linux_page_size/overview.md b/content/learning-paths/servers-and-cloud-computing/arm_linux_page_size/overview.md index 8eeced1079..90af3575cf 100644 --- a/content/learning-paths/servers-and-cloud-computing/arm_linux_page_size/overview.md +++ b/content/learning-paths/servers-and-cloud-computing/arm_linux_page_size/overview.md @@ -19,7 +19,7 @@ To keep track of these mappings efficiently, CPUs use a fast lookup cache called On x86 systems, 4K pages are the standard, while Arm-based systems support multiple page sizes - typically 4K, 16K, or 64K. This flexibility allows developers to fine-tune performance for specific workloads. -This Learning Path explains how to switch between 4K and 64K pages on different Linux distributions. +You'll learn how to switch between 4K, 16K, and 64K pages on different Linux distributions. ## How does the CPU locate data in memory? @@ -39,15 +39,14 @@ Changing the page size has a cascading effect on system performance: ### Trade-offs to consider -| Aspect | 4K Pages | 64K Pages | -|-----------------|--------------------------------------|----------------------------------------| -| **Size** | Small “bricks” (4 KB each) | Big “bricks” (64 KB each) | -| **Flexibility** | Best for flexibility and compatibility | Best for large, contiguous memory workloads | -| **Efficiency** | Needs more entries (more bookkeeping) | Needs fewer entries (less bookkeeping) | -| **Waste** | At most 4 KB unused per page | Up to ~63 KB unused if not fully used | -| **TLB reach** | Lower, more misses | Higher, fewer misses | +| Aspect | 4K pages | 16K pages | 64K pages | +|---|---|---|---| +| **Flexibility** | Highest compatibility | Balance of compatibility and reach | Best for large, contiguous memory workloads | +| **Efficiency** | Needs the most page table entries | Needs fewer entries than 4K pages | Needs the fewest entries | +| **Internal fragmentation** | Up to about 4 KB per partly used page | Up to about 16 KB per partly used page | Up to about 64 KB per partly used page | +| **TLB reach** | Lowest | Higher than 4K pages | Highest | -This Learning Path covers switching between 4K and 64K page sizes because these are supported by most Arm Linux distributions. In some cases, you may find that 16K page size is a sweet spot for your application, but Linux kernel, hardware, and software support is limited. One example of 16K page size is [Asahi Linux](https://asahilinux.org/). +Linux distribution support varies. Debian 12 and later provide a packaged 16K kernel, while the Ubuntu and CentOS instructions use packaged 64K kernels. Debian users can also build a 64K kernel from source. ## How do I select the memory page size? @@ -55,6 +54,8 @@ Points to consider when thinking about page size: - **4K pages** are the safe, default choice. They let you use memory in small slices and keep waste low. Since they are smaller, you need more of them when handling larger memory footprint applications. This creates more overhead for the operating system to manage, but it may be worth it for the flexibility. They are great for applications that need to access small bits of data frequently, like web servers or databases with lots of small transactions. +- **16K pages** provide a middle ground. They increase TLB reach compared with 4K pages while reducing the internal fragmentation risk of 64K pages. + - **64K pages** shine when you work with large, contiguous data such as video frames or large database caches because they cut down on management overhead. They will use more memory if you don’t use the whole page, but they can also speed up access times for large data sets. Choosing the right page size depends on how your application uses memory, as both the data size and retrieval patterns of the data you are working with are influencing factors. Benchmark different options under real-world workloads to determine which delivers better performance. @@ -69,7 +70,7 @@ The best way to determine the impact of page size on application performance is Do not modify the Linux kernel page size in a production environment. It can lead to system instability or failure. Perform testing in a non-production environment before applying to production systems. {{% /notice %}} -Select the Arm Linux distribution you are using to find out how to install the 64K page size kernel. +Select your Arm Linux distribution to install an available larger-page kernel. The Debian instructions cover both 16K and 64K page sizes. - [Ubuntu](/learning-paths/servers-and-cloud-computing/arm_linux_page_size/ubuntu/) - [Debian](/learning-paths/servers-and-cloud-computing/arm_linux_page_size/debian/) diff --git a/content/learning-paths/servers-and-cloud-computing/kedify-http-autoscaling/_index.md b/content/learning-paths/servers-and-cloud-computing/kedify-http-autoscaling/_index.md index 1005b475bc..fb3a489dda 100644 --- a/content/learning-paths/servers-and-cloud-computing/kedify-http-autoscaling/_index.md +++ b/content/learning-paths/servers-and-cloud-computing/kedify-http-autoscaling/_index.md @@ -8,9 +8,9 @@ who_is_this_for: This is an introductory topic for developers running HTTP workl description: Enable event-driven autoscaling for HTTP workloads on Kubernetes by installing Kedify and KEDA with Helm and testing autoscaling behavior. learning_objectives: - - Install Kedify (KEDA build, HTTP Scaler, and Kedify Agent) with Helm - - Verify that Kedify and KEDA components are running in the cluster - - Deploy a sample HTTP application and test autoscaling behavior + - Install Kedify (KEDA build, HTTP Scaler, and Kedify Agent) with Helm. + - Verify that Kedify and KEDA components are running in the cluster. + - Deploy a sample HTTP application and test autoscaling behavior. prerequisites: - A running Kubernetes cluster (local or cloud) @@ -42,9 +42,9 @@ generated_summary_faq: answer: >- Run `kubectl get pods -n keda` and confirm that the KEDA, HTTP scaler, and Kedify Agent pods show `1/1` in `READY` and `Running` in `STATUS` before continuing. - - question: Do I still need to install Traefik if my cluster already has an ingress controller? + - question: Do I need to install Traefik if my cluster already has an ingress controller? answer: >- - No. If your cluster already has an ingress controller, skip the Traefik installation and use its endpoint and IngressClass name in the application steps. + No. If your cluster already has an ingress controller, skip the Traefik installation and use its endpoint and `IngressClass` name while deploying the application. - question: Which deployment target should I use for the ingress controller on Arm-based nodes? answer: >- Install Traefik with Helm and set its node selector and toleration for arm64 nodes to run the controller on Arm nodes in your cluster. diff --git a/content/learning-paths/servers-and-cloud-computing/kedify-http-autoscaling/configure-autoscaling.md b/content/learning-paths/servers-and-cloud-computing/kedify-http-autoscaling/configure-autoscaling.md new file mode 100644 index 0000000000..6dbdf15e7f --- /dev/null +++ b/content/learning-paths/servers-and-cloud-computing/kedify-http-autoscaling/configure-autoscaling.md @@ -0,0 +1,168 @@ +--- +title: "Configure and test HTTP autoscaling with Kedify" +description: Configure Kedify HTTP autoscaling for the deployed application and observe replica changes as traffic varies. +weight: 5 +layout: "learningpathall" +--- + +## How application autoscaling with Kedify works + +Configure autoscaling for your deployed HTTP application with [Kedify’s HTTP Scaler](https://docs.kedify.io/scalers/http-scaler/). You'll then generate traffic and observe scale-out, scale-in, and scale-to-zero behavior when idle. + +Kedify’s ingress autowiring routes traffic through its proxy so that requests are measured and drive scaling. + +For more information, see [Scaling Deployments, StatefulSets & Custom Resources](https://keda.sh/docs/latest/concepts/scaling-deployments/) on the KEDA website. + +With ingress autowiring enabled, Kedify automatically routes traffic through its proxy before it reaches your service and deployment: + +```text +Ingress → kedify-proxy → Service → Deployment +``` + +The [Kedify proxy](https://docs.kedify.io/scalers/http-scaler/#kedify-proxy) gathers request metrics used by the scaler to make decisions. + +There are three main components involved in the process: + +- For the application deployment and service, there's an HTTP server with a small response delay to simulate work. +- For ingress, there's a public entry point that's configured using the `application.keda` host. +- For the `ScaledObject`, there's a Kedify HTTP scaler using `trafficAutowire: ingress`. + +## Enable autoscaling with Kedify + +The application is now running. Next, enable autoscaling so that it can scale dynamically between zero and 10 replicas. Kedify holds requests while the application scales from zero, subject to client timeouts. Apply the `ScaledObject` by running the following command: + +```bash +cat <<'EOF' | kubectl apply -f - +apiVersion: keda.sh/v1alpha1 + +kind: ScaledObject +metadata: + name: application +spec: + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: application + cooldownPeriod: 5 + minReplicaCount: 0 + maxReplicaCount: 10 + fallback: + failureThreshold: 2 + replicas: 1 + advanced: + restoreToOriginalReplicaCount: true + horizontalPodAutoscalerConfig: + behavior: + scaleDown: + stabilizationWindowSeconds: 5 + triggers: + - type: kedify-http + metadata: + hosts: application.keda + pathPrefixes: / + service: application-service + port: "8080" + scalingMetric: requestRate + targetValue: "10" + granularity: 1s + window: 10s + trafficAutowire: ingress +EOF +``` + +Use the following field descriptions to understand how the `ScaledObject` controls HTTP-driven autoscaling and how each setting affects traffic routing and scale decisions: + +- `type: kedify-http` - Uses Kedify’s HTTP scaler. +- `hosts`, `pathPrefixes` - Define which requests are monitored for scaling decisions. +- `service`, `port` - Identify the Kubernetes Service and port that receive traffic. +- `scalingMetric: requestRate`, `granularity: 1s`, `window: 10s`, `targetValue: "10"` - Scale out when the average request rate exceeds ~10 requests per second (rps) per replica over the last 10 seconds. +- `minReplicaCount: 0` - Enables scale to zero when there is no traffic. +- `trafficAutowire: ingress` - Automatically wires your ingress to the Kedify proxy for seamless traffic management. + +After applying the settings, the `ScaledObject` will appear in the [Kedify dashboard](https://dashboard.kedify.io/). + +![Kedify dashboard with the ScaledObjects tab selected. The application row shows the kedify-http trigger, minimum zero and maximum ten replicas, and READY status after you apply the ScaledObject.#center](images/scaledobject.png "Kedify dashboard: ScaledObject") + +## Send traffic and observe scaling + +Because no traffic is currently being sent to the application, it'll eventually scale down to zero replicas. + +### Verify scale to zero + +To confirm that the application has scaled down, run the following command and watch until the number of replicas reaches zero: + +```bash +watch kubectl get deployment application -n default +``` + +The output is similar to: + +```output +Every 2,0s: kubectl get deployment application -n default + +NAME READY UP-TO-DATE AVAILABLE AGE +application 0/0 0 0 110s +``` + +This continuously monitors the deployment status in the `default` namespace. After traffic stops and the idle window has passed, you should see the application deployment report `0/0` replicas. `0/0` replicas indicates that the application has successfully scaled to zero. + +### Verify that the app can scale from zero + +Send a request to trigger scale-up: + +```bash +curl -I -H "Host: application.keda" http://$INGRESS_ADDRESS +``` + +The application scales from zero to one replica automatically. You should receive an HTTP `200 OK` response, confirming that the service is reachable again. + +### Generate load and observe scale-out + +Now, generate a heavier, sustained load against the application. You can use `hey`, or a similar benchmarking tool: + +```bash +hey -n 40000 -c 200 -t 60 -host "application.keda" http://$INGRESS_ADDRESS +``` + +While the load test is running, open another terminal and monitor the deployment replicas in real time: + +```bash +watch kubectl get deployment application -n default +``` + +You'll see that the number of replicas change dynamically. + +The output is similar to: + +```output +Every 2,0s: kubectl get deployment application -n default + +NAME READY UP-TO-DATE AVAILABLE AGE +application 5/5 5 5 23m +``` + +The following is the expected behavior: +- On bursty load, Kedify scales the deployment up toward `maxReplicaCount`. +- When traffic subsides, replicas scale down. After the cooldown, they can return to zero. + +You can also monitor traffic and scaling in the Kedify dashboard: + +![Kedify dashboard showing the application's ScaledObject Summary tab. Compare the Scaler's metrics and Number of replicas graphs to observe traffic metrics and replica changes during testing.#center](images/load.webp "Kedify dashboard: request load and scaling over time") + +## Clean up resources + +When you've finished testing, remove the resources that you created to free up your cluster: + +```bash +kubectl delete scaledobject application +kubectl delete ingress application-ingress +kubectl delete service application-service +kubectl delete deployment application +``` +This will delete the `ScaledObject`, Ingress, Service, and Deployment associated with the demo application. + +## What you've accomplished + +You've configured a Kedify `ScaledObject` and tested scale-to-zero, scale-up, and replica changes under load. You've monitored scaling in the dashboard. After testing, you removed the sample application resources. + +You can further explore the [Kedify How-To Guides](https://docs.kedify.io/how-to/) for more configurations such as Gateway API, Istio VirtualService, or OpenShift Routes. diff --git a/content/learning-paths/servers-and-cloud-computing/kedify-http-autoscaling/http-scaling.md b/content/learning-paths/servers-and-cloud-computing/kedify-http-autoscaling/http-scaling.md index 2da8b6b060..849354eb69 100644 --- a/content/learning-paths/servers-and-cloud-computing/kedify-http-autoscaling/http-scaling.md +++ b/content/learning-paths/servers-and-cloud-computing/kedify-http-autoscaling/http-scaling.md @@ -1,52 +1,33 @@ --- -title: "Autoscale HTTP applications with Kedify and Kubernetes Ingress" +title: "Deploy an HTTP application through Kubernetes Ingress" +description: Deploy a sample HTTP application through Kubernetes Ingress and verify that its endpoint returns an HTTP response. weight: 4 layout: "learningpathall" --- -## Overview -In this section, you’ll gain hands-on experience with Kedify HTTP autoscaling. You will deploy a small web service, expose it through a standard Kubernetes Ingress, and rely on Kedify’s autowiring to route traffic through its proxy so that requests are measured and drive scaling. +## Deploy and verify an HTTP application -You will scale a real HTTP app exposed through Kubernetes Ingress using [Kedify’s HTTP Scaler](https://docs.kedify.io/scalers/http-scaler/), and then move on to deploy a simple application, enable autoscaling with a scaled object, generate load, and observe the system scale out and back in (including scale-to-zero when idle). - -For more information, see [Scaling Deployments, StatefulSets & Custom Resources](https://keda.sh/docs/latest/concepts/scaling-deployments/) on the KEDA website. - -## How it works - -With ingress autowiring enabled, Kedify automatically routes traffic through its proxy before it reaches your service and deployment: - -```output -Ingress → kedify-proxy → Service → Deployment -``` - -The [Kedify proxy](https://docs.kedify.io/scalers/http-scaler/#kedify-proxy) gathers request metrics used by the scaler to make decisions. - -## Deployment overview - -There are three main components involved in the process: -* For the application deployment and service, there is an HTTP server with a small response delay to simulate work. -* For ingress, there is a public entry point that is configured using the `application.keda` host. -* For the ScaledObject, there is a Kedify HTTP scaler using `trafficAutowire: ingress`. +You'll deploy a sample HTTP application and expose it through Kubernetes Ingress. Then, you'll send a request to verify that its endpoint returns an HTTP response. ## Configure the ingress address -Before testing the application, make sure `INGRESS_ADDRESS` is set to your ingress controller's external IP address or hostname. For a local cluster, you can use the forwarded address from the previous section. +Before testing the application, make sure that `INGRESS_ADDRESS` is set to your ingress controller's external IP address or hostname. For a local cluster, you can use the address that you forwarded using `port-forward` earlier. -If you followed the [Install an ingress controller](../install-ingress/) section, you should already have this set. Otherwise, if you installed Traefik separately, run: +If you completed [Install an ingress controller for HTTP autoscaling in Kubernetes](/learning-paths/servers-and-cloud-computing/kedify-http-autoscaling/install-ingress/), you should already have this set. Otherwise, if you installed Traefik separately, run: ```bash export INGRESS_ADDRESS=$(kubectl get service traefik --namespace traefik -o jsonpath='{.status.loadBalancer.ingress[0].ip}{.status.loadBalancer.ingress[0].hostname}') echo "Ingress address: $INGRESS_ADDRESS" ``` -If the Service has no external address, use the local port forwarding option in the previous section. +If the service has no external address, use `port-forward`. {{% notice Note %}} -If you use an existing ingress controller, set `INGRESS_ADDRESS` to its endpoint and change `ingressClassName` in the following manifest to its IngressClass name. +If you use an existing ingress controller, set `INGRESS_ADDRESS` to its endpoint and change `ingressClassName` in the following manifest to its `IngressClass` name. {{% /notice %}} -## Deploy the application and configure Ingress +## Deploy the application and configure ingress -Now you will deploy a simple HTTP server and expose it using an Ingress resource. The source code for this application is available on the [Kedify GitHub repository](https://github.com/kedify/examples/tree/main/samples/http-server). +Now you'll deploy an HTTP server and expose it using an `Ingress` resource. For more information, see the [Kedify sample HTTP-server source code](https://github.com/kedify/examples/tree/main/samples/http-server). Run the following command to deploy your application: @@ -119,22 +100,21 @@ spec: EOF ``` -## Key settings explained +The manifest includes the following options that affect scaling behavior: -The manifest includes a few key options that affect scaling behavior: - -- `RESPONSE_DELAY` is set in the Deployment manifest above and adds approximately 300 ms latency per request; this slower response time increases the number of concurrent requests, making scaling effects easier to observe. +- `RESPONSE_DELAY` is set in the `Deployment` manifest and adds approximately 300 ms latency per request. This slower response time increases the number of concurrent requests, making scaling effects easier to observe. - The ingress uses the host `application.keda`. To access this app, use your ingress controller's address with a `Host:` header. -## Verify the application is running +## Verify that the application is running -Run the following command to check that 1 replica is ready: +Run the following command to check that one replica is ready: ```bash kubectl get deployment application ``` -Expected output includes 1 available replica: +The output is similar to: + ```output NAME READY UP-TO-DATE AVAILABLE AGE application 1/1 1 1 3m44s @@ -142,13 +122,16 @@ application 1/1 1 1 3m44s ## Test the application -Once the application and Ingress are deployed, verify that everything is working correctly by sending a request to the exposed endpoint. Run the following command: +After the application and ingress are deployed, verify that everything is working correctly by sending a request to the exposed endpoint. + +Run the following command: ```bash curl -I -H "Host: application.keda" http://$INGRESS_ADDRESS ``` -If the routing is set up properly, you should see a response similar to: +The output is similar to: + ```output HTTP/1.1 200 OK Date: Thu, 11 Sep 2025 14:11:24 GMT @@ -157,137 +140,8 @@ Content-Length: 301 Connection: keep-alive ``` -## Enable autoscaling with Kedify - -The application is now running. Next, enable autoscaling so that it can scale dynamically between 0 and 10 replicas. Kedify holds requests while the application scales from zero, subject to client timeouts. Apply the `ScaledObject` by running the following command: - -```bash -cat <<'EOF' | kubectl apply -f - -apiVersion: keda.sh/v1alpha1 - -kind: ScaledObject -metadata: - name: application -spec: - scaleTargetRef: - apiVersion: apps/v1 - kind: Deployment - name: application - cooldownPeriod: 5 - minReplicaCount: 0 - maxReplicaCount: 10 - fallback: - failureThreshold: 2 - replicas: 1 - advanced: - restoreToOriginalReplicaCount: true - horizontalPodAutoscalerConfig: - behavior: - scaleDown: - stabilizationWindowSeconds: 5 - triggers: - - type: kedify-http - metadata: - hosts: application.keda - pathPrefixes: / - service: application-service - port: "8080" - scalingMetric: requestRate - targetValue: "10" - granularity: 1s - window: 10s - trafficAutowire: ingress -EOF -``` - -## Key fields explained - -Use the following field descriptions to understand how the `ScaledObject` controls HTTP-driven autoscaling and how each setting affects traffic routing and scale decisions: - -- `type: kedify-http` - Uses Kedify’s HTTP scaler. -- `hosts`, `pathPrefixes` - Define which requests are monitored for scaling decisions. -- `service`, `port` - Identify the Kubernetes Service and port that receive traffic. -- `scalingMetric: requestRate`, `granularity: 1s`, `window: 10s`, `targetValue: "10"` - Scales out when the average request rate exceeds ~10 requests/second (rps) per replica over the last 10 seconds. -- `minReplicaCount: 0` - Enables scale to zero when there is no traffic. -- `trafficAutowire: ingress` - Automatically wires your Ingress to the Kedify proxy for seamless traffic management. - -After applying, the `ScaledObject` will appear in the Kedify dashboard (https://dashboard.kedify.io/). - -![Kedify dashboard showing the ScaledObject alt-text#center](images/scaledobject.png "Kedify dashboard: ScaledObject") - -## Send traffic and observe scaling - -Since no traffic is currently being sent to the application, it will eventually scale down to zero replicas. - -## Verify scale to zero - -To confirm that the application has scaled down, run the following command and watch until the number of replicas reaches 0: - -```bash -watch kubectl get deployment application -n default -``` - -You should see output similar to: -```output -Every 2,0s: kubectl get deployment application -n default - -NAME READY UP-TO-DATE AVAILABLE AGE -application 0/0 0 0 110s -``` -This continuously monitors the deployment status in the default namespace. Once traffic stops and the idle window has passed, you should see the application deployment report 0/0 replicas, indicating that it has successfully scaled to zero. - -## Verify the app can scale from zero - -Send a request to trigger scale-up: - -```bash -curl -I -H "Host: application.keda" http://$INGRESS_ADDRESS -``` -You should receive an HTTP 200 OK response, confirming that the service is reachable again. - -The application scales from 0 → 1 replica automatically, and you should receive an HTTP `200 OK` response. - -Now, generate a heavier, sustained load against the application. You can use `hey` (or a similar benchmarking tool): - -```bash -hey -n 40000 -c 200 -t 60 -host "application.keda" http://$INGRESS_ADDRESS -``` - -While the load test is running, open another terminal and monitor the deployment replicas in real time: - -```bash -watch kubectl get deployment application -n default -``` - -You will see the number of replicas change dynamically. For example: - -```output -Every 2,0s: kubectl get deployment application -n default - -NAME READY UP-TO-DATE AVAILABLE AGE -application 5/5 5 5 23m -``` - -Expected behavior: -- On bursty load, Kedify scales the Deployment up toward `maxReplicaCount`. -- When traffic subsides, replicas scale down. After the cooldown, they can return to zero. - -You can also monitor traffic and scaling in the Kedify dashboard: - -![Kedify dashboard showing request load and scaling over time alt-text#center](images/load.webp "Kedify dashboard: request load and scaling over time") - -## Clean up - -When you have finished testing, remove the resources created in this Learning Path to free up your cluster: - -```bash -kubectl delete scaledobject application -kubectl delete ingress application-ingress -kubectl delete service application-service -kubectl delete deployment application -``` -This will delete the `ScaledObject`, Ingress, Service, and Deployment associated with the demo application. +## What you've accomplished -## Next steps +You've deployed the sample HTTP application through Kubernetes Ingress and tested its endpoint for an HTTP response. -To go further, you can explore the [Kedify How-To Guides](https://docs.kedify.io/how-to/) for more configurations such as Gateway API, Istio VirtualService, or OpenShift Routes. +Next, you'll configure and test Kedify HTTP autoscaling using this application and `INGRESS_ADDRESS`. \ No newline at end of file diff --git a/content/learning-paths/servers-and-cloud-computing/kedify-http-autoscaling/install-ingress.md b/content/learning-paths/servers-and-cloud-computing/kedify-http-autoscaling/install-ingress.md index bbabbfd5da..9a8f13054f 100644 --- a/content/learning-paths/servers-and-cloud-computing/kedify-http-autoscaling/install-ingress.md +++ b/content/learning-paths/servers-and-cloud-computing/kedify-http-autoscaling/install-ingress.md @@ -1,26 +1,26 @@ --- -title: "Install an ingress controller" +title: "Install an ingress controller for testing HTTP autoscaling" +description: Install Traefik with Helm and prepare an ingress endpoint for testing Kedify HTTP autoscaling on Kubernetes. weight: 3 layout: "learningpathall" --- -## Install an ingress controller for HTTP autoscaling on Kubernetes +## Install Traefik with Helm -To expose the sample application, you need an ingress controller to handle incoming traffic. This Learning Path uses Traefik with the standard Kubernetes Ingress API and schedules Traefik on arm64 nodes. The Traefik chart used here needs Kubernetes 1.25 or later. +To expose the sample application, you need an ingress controller to handle incoming traffic. One option is to use Traefik with the standard Kubernetes Ingress API and schedule Traefik on arm64 nodes. {{% notice Note %}} -If your cluster already has an ingress controller installed and configured, you can skip this step and proceed to the [Autoscale HTTP applications with Kedify and Kubernetes Ingress section](../http-scaling/). +- If your cluster already has an ingress controller installed and configured, you can skip this step and proceed to [Deploy an HTTP application through Kubernetes Ingress](/learning-paths/servers-and-cloud-computing/kedify-http-autoscaling/http-scaling/). +- Before installing Traefik, ensure that you're using Kubernetes version 1.25 or later. {{% /notice %}} -## Install Traefik with Helm - Add the Traefik Helm repository: ```bash helm repo add traefik https://traefik.github.io/charts helm repo update ``` -Install Traefik with a node selector and toleration for arm64 nodes. The chart creates an IngressClass named `traefik` and a LoadBalancer Service. Set `ingressClass.isDefaultClass=false` so this installation does not change the default class for other Ingress resources: +Install Traefik with a node selector and toleration for arm64 nodes. The chart creates an IngressClass named `traefik` and a LoadBalancer service. Set `ingressClass.isDefaultClass=false` so that the installation doesn't change the default class for other ingress resources: ```bash helm upgrade --install traefik traefik/traefik \ --version 41.6.1 \ @@ -34,28 +34,28 @@ helm upgrade --install traefik traefik/traefik \ --set ingressClass.isDefaultClass=false ``` -Confirm the Traefik Deployment is available and its IngressClass exists: +Confirm the Traefik Deployment is available and its `IngressClass` exists: ```bash kubectl wait --namespace traefik --for=condition=available deployment/traefik --timeout=300s kubectl get ingressclass traefik ``` -Managed clouds can take a few minutes to allocate a public IP address or hostname for the Traefik Service. +Managed clouds can take a few minutes to allocate a public IP address or hostname for the Traefik service. ## Get the external endpoint -Retrieve the Traefik Service's external IP address or hostname and store it in an environment variable: +Retrieve the Traefik service's external IP address or hostname and store it in an environment variable: ```bash export INGRESS_ADDRESS=$(kubectl get service traefik --namespace traefik -o jsonpath='{.status.loadBalancer.ingress[0].ip}{.status.loadBalancer.ingress[0].hostname}') echo "Ingress address: $INGRESS_ADDRESS" ``` -Typical values by provider: -- **AWS EKS**: Load balancer hostname (for example, `a1234567890abcdef-123456789.us-west-2.elb.amazonaws.com`) -- **Google GKE**: IP address (for example, `34.102.136.180`) -- **Azure AKS**: IP address (for example, `20.62.196.123`) +The following are typical endpoint values by cloud provider: +- Amazon Elastic Kubernetes Service (EKS): Load balancer hostname (for example, `a1234567890abcdef-123456789.us-west-2.elb.amazonaws.com`) +- Google Kubernetes Engine (GKE): IP address (for example, `34.102.136.180`) +- Azure Kubernetes Service (AKS): IP address (for example, `20.62.196.123`) -If no value is printed on a managed cluster, wait briefly and run the command again. For a local cluster such as k3d, forward a local port to Traefik in a separate terminal. Use this method even if the Service reports an address on the cluster's private container network: +If no value is printed on a managed cluster, wait briefly and run the command again. For a local cluster such as k3d, forward a local port to Traefik in a separate terminal. Use this method even if the service reports an address on the cluster's private container network: ```bash kubectl port-forward --namespace traefik service/traefik 8080:80 @@ -67,23 +67,27 @@ Keep that terminal open. In your original terminal, use the local address for th export INGRESS_ADDRESS=127.0.0.1:8080 ``` -## Configure access +## Configure access to the applications -You have two options: +You have two options for configuring access: -- Option 1: DNS (recommended for production): - create a DNS record pointing `application.keda` to the external IP address or hostname of your ingress controller. +- DNS (recommended for production): + Create a DNS record pointing `application.keda` to the external IP address or hostname of your ingress controller. -- Option 2: Host header (quick test): - Use `INGRESS_ADDRESS` with a `Host: application.keda` header when you test the application in the next section. The address can be Traefik's external endpoint or your forwarded local port. +- Host header (recommended for a quick test): + Use `INGRESS_ADDRESS` with a `Host: application.keda` header when you test the application. The address can be Traefik's external endpoint or your forwarded local port. ## Verify the installation -List the controller pods and confirm they are running: +List the controller pods and confirm that they're running: ```bash kubectl get pods --namespace traefik ``` You should see the `traefik` pod in `Running` status. -Now that you have an ingress controller installed and configured, proceed to the next section to deploy an application and configure Kedify autoscaling. +## What you've accomplished and what's next + +You've installed Traefik with Helm, checked its Deployment, IngressClass, and pods, and prepared an ingress endpoint and access method for testing. + +Next, you'll deploy an HTTP application through Kubernetes Ingress and verify its HTTP response, then configure and test Kedify autoscaling. diff --git a/content/learning-paths/servers-and-cloud-computing/kedify-http-autoscaling/install-kedify-helm.md b/content/learning-paths/servers-and-cloud-computing/kedify-http-autoscaling/install-kedify-helm.md index 158f7ba342..4a32cde799 100644 --- a/content/learning-paths/servers-and-cloud-computing/kedify-http-autoscaling/install-kedify-helm.md +++ b/content/learning-paths/servers-and-cloud-computing/kedify-http-autoscaling/install-kedify-helm.md @@ -1,19 +1,27 @@ --- title: "Install Kedify using Helm" +description: Install the Kedify build of KEDA, HTTP Scaler, and Kedify Agent with Helm, then verify the components for HTTP autoscaling. weight: 2 layout: "learningpathall" --- -## Overview -In this section, you will install Kedify on your Kubernetes cluster using Helm. You will add the Kedify chart repository, then install three separate Helm charts: KEDA (Kedify build) for event-driven autoscaling, the HTTP Scaler for HTTP-based scaling, and the Kedify Agent for connecting your cluster to Kedify's cloud service. You will then verify the installation. This enables HTTP autoscaling on Kubernetes with KEDA and Kedify, including arm64 nodes. +## Helm charts for installing Kedify + +Install Kedify on your Kubernetes cluster with three Helm charts: + +- The Kedify build of KEDA for event-driven autoscaling +- The HTTP Scaler for HTTP-based scaling +- The Kedify Agent to connect your cluster to Kedify's cloud service + +After installation, verify that the components are running. For more information and other installation methods on Arm, see the [Kedify installation documentation](https://docs.kedify.io/installation/helm#installation-on-arm). ## Before you begin -You will need: +Before installation, you'll need the following: -- A running Kubernetes cluster (for example, kind, minikube, EKS, GKE, or AKS), hosted on any cloud provider or local environment +- A running Kubernetes cluster hosted on any cloud provider or local environment — for example, kind, minikube, Amazon Elastic Kubernetes Service (EKS), Google Kubernetes Engine (GKE), or Azure Kubernetes Service (AKS) - Kubectl and Helm installed and configured to communicate with your cluster - A Kedify Service account to obtain your Organization ID and API key (sign up at the [Kedify dashboard](https://dashboard.kedify.io/)) @@ -25,10 +33,10 @@ If you already have a Kedify Agent deployed, decode the key from the existing Se ```bash kubectl get secret -n keda kedify-agent -o=jsonpath='{.data.apikey}' | base64 --decode ``` -Otherwise, in the Kedify dashboard go to **Organization** → **API Keys**, select **Create Agent Key**, and copy the key. +Otherwise, in the Kedify dashboard, navigate to **Organization** → **API Keys**, select **Create Agent Key**, and copy the key. {{% notice Note %}} -The API key is shared across all agent installations. If you regenerate it, update existing agents and keep it secret. +The API key is shared across all agent installations. If you regenerate the key, update existing agents and keep it as a secret. {{% /notice %}} Optionally, export these values for reuse in the following commands: @@ -48,13 +56,11 @@ helm repo update ## Install components with Helm -Most providers (such as EKS and AKS) schedule pods on Arm nodes when you specify a `nodeSelector` for `kubernetes.io/arch=arm64`. On Google Kubernetes Engine (GKE), Arm nodes commonly have an explicit taint, so matching `tolerations` are required. To stay portable across providers, configure both `nodeSelector` and `tolerations`. +Most providers such as EKS and AKS schedule pods on Arm nodes when you specify a `nodeSelector` for `kubernetes.io/arch=arm64`. On GKE, Arm nodes commonly have an explicit taint, so you need matching `tolerations`. -{{% notice Note %}} -For a portable deployment across cloud providers, configure both `nodeSelector` and `tolerations` in your Helm values or CLI flags. -{{% /notice %}} +To stay portable across providers, configure both `nodeSelector` and `tolerations` in your Helm values or CLI flags. -## Install the Kedify build of KEDA +### Install the Kedify build of KEDA Run the following Helm command to install the Kedify build of KEDA into the `keda` namespace: ```bash @@ -69,7 +75,7 @@ helm upgrade --install keda kedifykeda/keda \ --set "tolerations[0].effect=NoSchedule" ``` -## Install the Kedify HTTP Scaler +### Install the Kedify HTTP Scaler Install the Kedify HTTP Scaler with matching node selector and tolerations: ```bash @@ -88,7 +94,7 @@ helm upgrade --install keda-add-ons-http kedifykeda/keda-add-ons-http \ --set "scaler.tolerations[0].effect=NoSchedule" ``` -## Install the Kedify Agent +### Install the Kedify Agent Edit the cluster name, Organization ID, and API key (or rely on the exported environment variables), then run: ```bash @@ -111,12 +117,13 @@ helm upgrade --install kedify-agent kedifykeda/kedify-agent \ ## Verify installation -List pods in the `keda` namespace to confirm all components are running: +List pods in the `keda` namespace to confirm that all components are running: ```bash kubectl get pods -n keda ``` -Expected output (names might vary): +Pod names might vary. The output is similar to: + ```output NAME READY STATUS RESTARTS AGE keda-add-ons-http-external-scaler-xxxxx 1/1 Running 0 1m @@ -127,4 +134,8 @@ keda-operator-metrics-apiserver-xxxxx 1/1 Running 0 1m kedify-agent-xxxxx 1/1 Running 0 1m ``` -Proceed to the next section to install an ingress controller, deploy a sample HTTP app, and test autoscaling. +## What you've accomplished + +You've gathered Kedify credentials, installed the three Helm charts with Arm node selectors and tolerations, and checked the component pods in the `keda` namespace. + +Next, you'll install an ingress controller for the HTTP sample application that you'll deploy. diff --git a/content/learning-paths/servers-and-cloud-computing/multiarch_nginx_on_aks/_index.md b/content/learning-paths/servers-and-cloud-computing/multiarch_nginx_on_aks/_index.md index 07fccf7fdb..d3c63269c6 100644 --- a/content/learning-paths/servers-and-cloud-computing/multiarch_nginx_on_aks/_index.md +++ b/content/learning-paths/servers-and-cloud-computing/multiarch_nginx_on_aks/_index.md @@ -16,10 +16,58 @@ prerequisites: - An [Azure account](https://azure.microsoft.com/en-us/free/) - A local machine with [`jq`](https://jqlang.org/download/), [`curl`](https://curl.se/download.html), [`wrk`](https://github.com/wg/wrk), [Azure CLI](/install-guides/azure-cli/), and [`kubectl`](/install-guides/kubectl/) installed +# START generated_summary_faq +generated_summary_faq: + template_version: summary-faq-v3 + generated_at: '2026-10-01T19:35:41Z' + generator: ai + ai_assisted: true + ai_review_required: true + model: gpt-5 + prompt_template: summary-faq-v3 + source_hash: 9c684631877441d8756182571940b0aa63797c5adab26c46663b95492bceffbb + summary_generated_at: '2026-10-01T19:35:41Z' + summary_source_hash: 9c684631877441d8756182571940b0aa63797c5adab26c46663b95492bceffbb + faq_generated_at: '2026-10-01T19:35:41Z' + faq_source_hash: 9c684631877441d8756182571940b0aa63797c5adab26c46663b95492bceffbb + summary: >- + You'll build a hybrid AKS cluster with x86 and Arm nodes, then deploy + NGINX workloads to both architectures. First, you'll create the node pools, connect with `kubectl`, + and use a utility script to inspect and test each deployment. Then, you'll add a multi-architecture + service and compare its routing with the architecture-specific services. While you run load tests, you'll monitor both workloads. + faqs: + - question: How do I know that the AKS cluster is set up for both architectures before deploying + NGINX? + answer: >- + Confirm that the cluster has two node pools, one x86 and one Arm, and that `kubectl` can reach + the cluster. Check that nodes report the expected CPU architecture so scheduling can target + each pool. + - question: Which NGINX image should I use to run on both Arm and x86 nodes? + answer: >- + Use a multi-architecture NGINX image from Docker Hub. The container runtime + pulls the correct image variant based on the node’s CPU architecture. + - question: What result should I expect when the NGINX services become available? + answer: >- + When you request a service's external endpoint, you receive JSON with `message`, `timestamp`, + `server`, and `request_uri` fields. You see `nginx response` as the message and the serving pod's + name in `server`. + - question: How do I confirm that each NGINX pod is running on the correct architecture? + answer: >- + Use `kubectl` to check each pod’s node assignment and labels. The Arm service selects pods + with `app: nginx-multiarch` and `arch: arm`. Verify that Arm pods run on the Arm node pool + and x86 pods run on the x86 pool. + - question: What should I check if the Arm deployment doesn't start or the service shows no + endpoints? + answer: >- + Verify that the Arm node pool exists and is ready. Ensure that the deployment and service selectors + match, including `app: nginx-multiarch` and `arch: arm`. Also confirm that the namespace and shared + ConfigMap were created before applying the deployment. +# END generated_summary_faq + author: - Geremy Cohen -generate_summary_faq: true +generate_summary_faq: false rerun_summary: false rerun_faqs: false @@ -70,4 +118,3 @@ weight: 1 # _index.md always has weight of 1 to order corr layout: "learningpathall" # All files under learning paths have this same wrapper learning_path_main_page: "yes" # This should be surfaced when looking for related content. Only set for _index.md of learning path content. --- - diff --git a/content/learning-paths/servers-and-cloud-computing/multiarch_ollama_on_gke/_index.md b/content/learning-paths/servers-and-cloud-computing/multiarch_ollama_on_gke/_index.md index 183b72685c..a1e16b5bd1 100644 --- a/content/learning-paths/servers-and-cloud-computing/multiarch_ollama_on_gke/_index.md +++ b/content/learning-paths/servers-and-cloud-computing/multiarch_ollama_on_gke/_index.md @@ -3,7 +3,7 @@ title: Add Arm nodes to your GKE cluster using a multi-architecture Ollama conta minutes_to_complete: 30 -who_is_this_for: This Learning Path is for developers who want to compare the performance of amd64 and arm64 deployments by running inferences on a hybrid GKE cluster using an Ollama multi-architecture container image. +who_is_this_for: This Learning Path is for developers who want to compare the performance of amd64 and arm64 deployments by running inferences on a hybrid Google Kubernetes Engine (GKE) cluster using an Ollama multi-architecture container image. learning_objectives: - Create a hybrid GKE cluster with amd64 and arm64 nodes. @@ -11,14 +11,62 @@ learning_objectives: - Validate deployments by pinging, pulling models, and running inferences to compare architecture performance. prerequisites: - - A [Google Cloud account](https://console.cloud.google.com/). - - A local machine with [Google Cloud CLI](/install-guides/gcloud/) and [kubectl](/install-guides/kubectl/) installed. - - The [GKE Cloud Plugin](https://cloud.google.com/kubernetes-engine/docs/how-to/cluster-access-for-kubectl#gcloud) installed. + - A [Google Cloud account](https://console.cloud.google.com/) + - A local machine with [Google Cloud CLI](/install-guides/gcloud/) and [kubectl](/install-guides/kubectl/) installed + - The [GKE Cloud Plugin](https://cloud.google.com/kubernetes-engine/docs/how-to/cluster-access-for-kubectl#gcloud) installed + +# START generated_summary_faq +generated_summary_faq: + template_version: summary-faq-v3 + generated_at: '2026-10-01T19:36:03Z' + generator: ai + ai_assisted: true + ai_review_required: true + model: gpt-5 + prompt_template: summary-faq-v3 + source_hash: 524da8c0a000a03613c8fca09d0a78b8bb3fbe57a53fdaaaca79d4d7431bb44c + summary_generated_at: '2026-10-01T19:36:03Z' + summary_source_hash: 524da8c0a000a03613c8fca09d0a78b8bb3fbe57a53fdaaaca79d4d7431bb44c + faq_generated_at: '2026-10-01T19:36:03Z' + faq_source_hash: 524da8c0a000a03613c8fca09d0a78b8bb3fbe57a53fdaaaca79d4d7431bb44c + summary: >- + You'll build a hybrid GKE cluster that runs Ollama on amd64 and Arm nodes. + First, you'll deploy Ollama to the initial amd64 pool, add an Arm node pool, and create a matching arm64 + deployment. Then, you'll use a multi-architecture service and a script to route requests. You'll + identify the pod and node that handled each call, load a model, and compare inference behavior + across both architectures. + faqs: + - question: Which namespace should I use for all Kubernetes objects? + answer: >- + Use the `ollama` namespace created by applying `namespace.yaml`. All subsequent deployments + and services are scoped to this namespace. + - question: How do I verify that the initial amd64 deployment is ready before adding Arm nodes? + answer: >- + Confirm that the Ollama deployment and service for amd64 exist in the `ollama` namespace and + report ready. Don't proceed until the pods are running and the service is available. + - question: What values should I set when creating the Arm node pool? + answer: >- + Name the pool `arm64-pool`, set **Size** to **1**, and enable **Specify node locations** with + **us-central1-a** selected. On the **Nodes** tab, select the **C4A** series and `c4a-standard-4` + machine type, then select **Create**. + - question: How do I send a request without selecting an architecture and see which architecture handled + it? + answer: >- + Run `./model_util.sh multiarch hello` to target the multi-architecture service. The response + includes `Ollama is running`. Under **Pod log output**, you see a pod and container identifier + with a timestamp. Use the `amd64` or `arm64` in the pod name to identify its architecture. + - question: What should I check if requests always go to the same architecture? + answer: >- + Check that both the amd64 and arm64 deployments have running, ready pods in the `ollama` + namespace. Confirm that both pods have the `app: ollama-multiarch` label and that + `ollama-multiarch-svc` selects that label. Repeat `./model_util.sh multiarch hello` to observe + the pod identifiers in the logs. +# END generated_summary_faq author: - Geremy Cohen -generate_summary_faq: true +generate_summary_faq: false rerun_summary: false rerun_faqs: false @@ -81,4 +129,3 @@ weight: 1 # _index.md always has weight of 1 to order corr layout: "learningpathall" # All files under learning paths have this same wrapper learning_path_main_page: "yes" # This should be surfaced when looking for related content. Only set for _index.md of learning path content. --- - diff --git a/content/learning-paths/servers-and-cloud-computing/mysql-azure/_index.md b/content/learning-paths/servers-and-cloud-computing/mysql-azure/_index.md index f4e88b58b1..5e3808cddf 100644 --- a/content/learning-paths/servers-and-cloud-computing/mysql-azure/_index.md +++ b/content/learning-paths/servers-and-cloud-computing/mysql-azure/_index.md @@ -6,17 +6,60 @@ minutes_to_complete: 30 who_is_this_for: This is an introductory topic for developers migrating MySQL applications from x86_64 to Arm. learning_objectives: - - Provision an Azure Arm64 virtual machine using Azure console, with Ubuntu Pro 24.04 LTS as the base image - - Deploy MySQL on the Ubuntu virtual machine - - Perform MySQL baseline testing and benchmarking on Arm64 virtual machines + - Provision an Azure Arm64 virtual machine (VM) using the console, with Ubuntu Pro 24.04 LTS as the base image. + - Deploy MySQL on the Ubuntu VM. + - Perform MySQL baseline testing and benchmarking on Arm64 VMs. prerequisites: - A [Microsoft Azure](https://azure.microsoft.com/) account with access to Cobalt 100 based instances (Dpsv6) - Familiarity with relational databases and the basics of [MySQL](https://dev.mysql.com/doc/refman/8.0/en/introduction.html) +# START generated_summary_faq +generated_summary_faq: + template_version: summary-faq-v3 + generated_at: '2026-10-01T19:36:26Z' + generator: ai + ai_assisted: true + ai_review_required: true + model: gpt-5 + prompt_template: summary-faq-v3 + source_hash: 2a34346f8cd7954362d479429942c9b66c863f4470d3e7cdf49efbc6093c54cd + summary_generated_at: '2026-10-01T19:36:26Z' + summary_source_hash: 2a34346f8cd7954362d479429942c9b66c863f4470d3e7cdf49efbc6093c54cd + faq_generated_at: '2026-10-01T19:36:26Z' + faq_source_hash: 2a34346f8cd7954362d479429942c9b66c863f4470d3e7cdf49efbc6093c54cd + summary: >- + You'll deploy MySQL on an Arm64 Azure virtual machine powered by Cobalt 100, and establish a performance baseline. + First, you'll provision an Ubuntu Pro 24.04 LTS Dpsv6 VM in the Azure portal, then install, secure, + and validate MySQL. After creating sample data, you'll use `mysqlslap` to run read and write + benchmarks. You'll interpret the reported timings and capture results that you can use in later + comparisons. + faqs: + - question: Which Azure VM size and image should I select? + answer: >- + Use a general-purpose Dpsv6 series instance based on Azure Cobalt 100, and choose Ubuntu + Pro 24.04 LTS as the base image. + - question: What do I use to connect to the VM after deployment? + answer: >- + Use the private SSH key that you downloaded when you created the VM, along with the administrator + username and the VM's public IP address. + - question: How do I confirm that MySQL is running? + answer: >- + Start MySQL and enable it to start on boot using the provided `systemctl` commands. Then, perform + the functional validation to confirm queries run and users can authenticate. + - question: What does mysql_secure_installation help me configure? + answer: >- + The interactive script helps you set a strong root password and remove anonymous users. It also helps disable + remote root access, remove test databases, and reload the privilege tables. + - question: What should I do before running mysqlslap, and what output should I expect? + answer: >- + Connect to MySQL and create the sample database and table before benchmarking. `mysqlslap` + simulates multiple clients and reports timing statistics that you can use as your baseline. +# END generated_summary_faq + author: Pareena Verma -generate_summary_faq: true +generate_summary_faq: false rerun_summary: false rerun_faqs: false @@ -61,4 +104,3 @@ weight: 1 # _index.md always has weight of 1 to order corr layout: "learningpathall" # All files under learning paths have this same wrapper learning_path_main_page: "yes" # This should be surfaced when looking for related content. Only set for _index.md of learning path content. --- - diff --git a/content/learning-paths/servers-and-cloud-computing/mysql-lns-azure/_index.md b/content/learning-paths/servers-and-cloud-computing/mysql-lns-azure/_index.md index fc56c24282..07d9d51c58 100644 --- a/content/learning-paths/servers-and-cloud-computing/mysql-lns-azure/_index.md +++ b/content/learning-paths/servers-and-cloud-computing/mysql-lns-azure/_index.md @@ -1,23 +1,72 @@ --- -title: Migrate MySQL from on-premises x64 to Azure Cobalt 100 Arm VMs +title: Migrate MySQL from on-premises x64 to Arm-based Azure virtual machines description: Learn how to migrate a MySQL database from an on-premises x64 environment to an Arm-based Azure Cobalt 100 VM and validate performance with sysbench. minutes_to_complete: 30 -who_is_this_for: This Learning Path is for developers who want to migrate MySQL from an on-premises x64 environment to an Arm-based Azure Cobalt 100 virtual machine. +who_is_this_for: This Learning Path is for developers who want to migrate MySQL from an on-premises x64 environment to an Arm-based virtual machine (VM) powered by Azure Cobalt 100. learning_objectives: - - Provision an Arm-based Azure Cobalt 100 virtual machine using Terraform and Azure CLI - - Export and restore a MySQL database from an on-premises x64 simulator into the Arm VM - - Run sysbench on the migrated database and interpret key performance metrics + - Provision an Arm-based Azure VM powered by Cobalt 100 using Terraform and Azure CLI. + - Export and restore a MySQL database from an on-premises x64 simulator into the Arm VM. + - Run sysbench on the migrated database and interpret key performance metrics. prerequisites: - A [Microsoft Azure](https://azure.microsoft.com/) account with access to Cobalt 100 based instances (Dpsv6) - Basic familiarity with SSH and MySQL command-line tools + +# START generated_summary_faq +generated_summary_faq: + template_version: summary-faq-v3 + generated_at: '2026-10-01T19:36:51Z' + generator: ai + ai_assisted: true + ai_review_required: true + model: gpt-5 + prompt_template: summary-faq-v3 + source_hash: d6d2cda672e5ed693210356f269aa015cb9b3b80fc92c9c33c200e6d83717b15 + summary_generated_at: '2026-10-01T19:36:51Z' + summary_source_hash: d6d2cda672e5ed693210356f269aa015cb9b3b80fc92c9c33c200e6d83717b15 + faq_generated_at: '2026-10-01T19:36:51Z' + faq_source_hash: d6d2cda672e5ed693210356f269aa015cb9b3b80fc92c9c33c200e6d83717b15 + summary: >- + You'll migrate a MySQL database from a simulated on-premises x64 server to an Arm-based Azure VM powered by Cobalt + 100. First, you'll prepare the source VM, install the required tools, and load a sample + database. You'll then configure and run the migration scripts to provision the target and restore + the database. Finally, you'll connect to the Arm VM, run a `sysbench` workload, and review the + results. + faqs: + - question: Which architecture should I choose for the on-premises simulator VM? + answer: >- + Choose x64 for the simulator VM. It acts as the on-premises MySQL source that you migrate + from. + - question: Where do I run the migration scripts, and what SSH key do they generate? + answer: >- + Open an SSH shell on your on-premises x64 simulator and run the scripts from `$HOME/lift-n-shift-assets`. + Run `scripts/create_ssh_key.sh` to generate an SSH key pair in `$HOME/.ssh` and print the public + key. Press **Enter** to leave the passphrase empty, and save the printed `ssh-rsa` + public key string for use with the Azure VM. + - question: Where can I find the password requested when restoring the database on the cloud VM? + answer: >- + Open a second SSH session to the Arm-based Azure VM. Run `sudo su -`, then + `cat /root/mysql_root_password.txt` to retrieve the password. Enter it at the restore + prompt in your original session on the on-premises simulator. + - question: How do I connect to the Arm-based Azure VM before running the benchmark? + answer: >- + From the on-premises simulator, SSH to the VM using the key created in `$HOME/.ssh` + and the `azureadmin` user at the VM’s public IP. + - question: Where should I create and run the benchmarking script, and what result should I + expect? + answer: >- + Create and run the script, for example `run.sh`, on the Arm-based Azure VM after + connecting with SSH. Expect `sysbench` to produce a summary of performance metrics that you + can review to validate the migrated database. +# END generated_summary_faq + author: Doug Anson -generate_summary_faq: true +generate_summary_faq: false rerun_summary: false rerun_faqs: false diff --git a/content/learning-paths/servers-and-cloud-computing/mysql/_index.md b/content/learning-paths/servers-and-cloud-computing/mysql/_index.md index 825c4840b7..e546749a08 100644 --- a/content/learning-paths/servers-and-cloud-computing/mysql/_index.md +++ b/content/learning-paths/servers-and-cloud-computing/mysql/_index.md @@ -1,21 +1,66 @@ --- -title: Learn how to deploy MySQL +title: Deploy MySQL on Arm minutes_to_complete: 30 who_is_this_for: This is an introductory topic for software developers who want to deploy MySQL on Arm. learning_objectives: - - Learn about the various ways MySQL can be deployed. - - Learn how to interact with a MySQL database using a MySQL client CLI tool. + - Identify the various ways MySQL can be deployed. + - Interact with a MySQL database using a MySQL client CLI tool. prerequisites: - - An Arm based instance from a cloud service provider, or an on-premise Arm server. - - If you do not have an Arm node, the next section discusses some options. + - An Arm based instance from a cloud service provider, or an on-premise Arm server + +# START generated_summary_faq +generated_summary_faq: + template_version: summary-faq-v3 + generated_at: '2026-10-01T19:37:38Z' + generator: ai + ai_assisted: true + ai_review_required: true + model: gpt-5 + prompt_template: summary-faq-v3 + source_hash: 743cde2f666e32bdd9dd2c412037696d36fbe12c5d89d854b04102d9a74a05ec + summary_generated_at: '2026-10-01T19:37:38Z' + summary_source_hash: 743cde2f666e32bdd9dd2c412037696d36fbe12c5d89d854b04102d9a74a05ec + faq_generated_at: '2026-10-01T19:37:38Z' + faq_source_hash: 743cde2f666e32bdd9dd2c412037696d36fbe12c5d89d854b04102d9a74a05ec + summary: >- + You'll deploy MySQL on an Arm-based Linux system and verify that the database is ready to use. First, you'll + review deployment options for bare-metal systems, cloud virtual machines, and managed SQL + services before choosing an environment. Then, you'll identify MySQL documentation to install + and configure the server. You'll connect with the MySQL clients and run SQL commands. + faqs: + - question: Which version of the MySQL reference manual should I use? + answer: >- + Use the MySQL reference manual for the version of MySQL you're working with. + Check the version selected in the documentation before following its installation, + configuration, or client instructions. + - question: What SQL operations can I try after connecting to MySQL? + answer: >- + You can create and select a database, create and inspect a table, insert sample records, and + query the table to display its contents. + - question: What result should I expect after I install and check MySQL? + answer: >- + You should have a running MySQL server on an Arm-based Linux system. A successful connection + with the MySQL client and the ability to issue basic SQL statements indicate that the setup + is working. + - question: Can I use a managed SQL service instead of installing MySQL myself? + answer: >- + Yes. You can use Amazon Relational Database Service (RDS) and select an Arm-based instance. Amazon RDS handles MySQL installation + for you. If you use a bare-metal server or cloud VM, review the MySQL installation options and + choose how to install it yourself. + - question: Where should I start if I don't have an Arm node? + answer: >- + Provision an Arm environment before you install + MySQL. For more information, see the [Get started + with Arm-based cloud instances](/learning-paths/servers-and-cloud-computing/csp/) Learning Path. +# END generated_summary_faq author: Jason Andrews -generate_summary_faq: true +generate_summary_faq: false rerun_summary: false rerun_faqs: false @@ -55,4 +100,3 @@ weight: 1 # _index.md always has weight of 1 to order corr layout: "learningpathall" # All files under learning paths have this same wrapper learning_path_main_page: "yes" # This should be surfaced when looking for related content. Only set for _index.md of learning path content. --- - diff --git a/content/learning-paths/servers-and-cloud-computing/mysql_benchmark/_index.md b/content/learning-paths/servers-and-cloud-computing/mysql_benchmark/_index.md index c2537c883c..5ca9fe861b 100644 --- a/content/learning-paths/servers-and-cloud-computing/mysql_benchmark/_index.md +++ b/content/learning-paths/servers-and-cloud-computing/mysql_benchmark/_index.md @@ -6,16 +6,61 @@ minutes_to_complete: 30 who_is_this_for: This is an introductory topic for performance engineers who want to benchmark MySQL using Sysbench and optimize performance on Arm Linux systems. learning_objectives: - - Run Sysbench to benchmark a MySQL database server - - Enable profile-guided optimization (PGO) for MySQL and examine the performance improvements + - Run Sysbench to benchmark a MySQL database server. + - Enable profile-guided optimization (PGO) for MySQL and examine the performance improvements. prerequisites: - Basic knowledge of [MySQL databases](https://www.mysql.com/) - Two Arm servers running Ubuntu 22.04, one for the MySQL server and the other for the Sysbench client +# START generated_summary_faq +generated_summary_faq: + template_version: summary-faq-v3 + generated_at: '2026-10-01T19:38:09Z' + generator: ai + ai_assisted: true + ai_review_required: true + model: gpt-5 + prompt_template: summary-faq-v3 + source_hash: 5fc0ba5fb5befc9ae9ba54e8d7734e5673c2e7a5b5c1eee6e6cdb181cad2de48 + summary_generated_at: '2026-10-01T19:38:09Z' + summary_source_hash: 5fc0ba5fb5befc9ae9ba54e8d7734e5673c2e7a5b5c1eee6e6cdb181cad2de48 + faq_generated_at: '2026-10-01T19:38:09Z' + faq_source_hash: 5fc0ba5fb5befc9ae9ba54e8d7734e5673c2e7a5b5c1eee6e6cdb181cad2de48 + summary: >- + You'll build MySQL from source on an Arm Ubuntu 22.04 server and benchmark it from a second Arm Linux + system. First, you'll configure the server, build `sysbench` with the required MySQL client libraries, + and run baseline workloads. You'll then rebuild MySQL with GCC PGO, + first collecting profile data and then applying it. Finally, you'll repeat the workloads and + compare the results with your baseline. + faqs: + - question: Can I use a different Linux distribution than Ubuntu 22.04? + answer: >- + Other Linux distributions and Ubuntu versions + might also work. If you use a different distribution, expect to adapt package installation commands to + match your environment. + - question: Why do I need to build and install MySQL on the Sysbench client? + answer: >- + `sysbench` needs MySQL client libraries to build and run its MySQL tests. You only build + and install MySQL on the client to provide these libraries. You don't configure or run + the MySQL server on the client. + - question: How much disk space do I need on the server and client? + answer: >- + Allocate at least 200 GB of disk space on the MySQL server system. Allocate at least 30 + GB on the Sysbench client system. + - question: Which compiler should I use for PGO, and what builds will I create? + answer: >- + Use GCC for PGO. You'll create two additional MySQL installations: one built with + profile generation to collect data and another rebuilt with profile use to apply that data. + - question: How do I confirm that PGO changed performance? + answer: >- + Run the same `sysbench` workload on the baseline and profile-use builds, then compare their + results. Keep the workload and environment consistent so the comparison is meaningful. +# END generated_summary_faq + author: Bolt Liu -generate_summary_faq: true +generate_summary_faq: false rerun_summary: false rerun_faqs: false @@ -59,4 +104,3 @@ weight: 1 layout: learningpathall learning_path_main_page: 'yes' --- - diff --git a/content/learning-paths/servers-and-cloud-computing/mysql_tune/_index.md b/content/learning-paths/servers-and-cloud-computing/mysql_tune/_index.md index 2ebd0b809d..858e891b11 100644 --- a/content/learning-paths/servers-and-cloud-computing/mysql_tune/_index.md +++ b/content/learning-paths/servers-and-cloud-computing/mysql_tune/_index.md @@ -12,7 +12,7 @@ learning_objectives: - Evaluate storage, kernel, compiler, and library choices that can affect MySQL performance. prerequisites: - - On-prem or cloud [installation of MySQL](https://dev.mysql.com/doc/refman/en/) + - On-premise or cloud [installation of MySQL](https://dev.mysql.com/doc/refman/en/) - A repeatable MySQL workload or benchmark that you can run before and after tuning # START generated_summary_faq @@ -42,7 +42,7 @@ generated_summary_faq: Run the same repeatable workload before and after the change and compare throughput, latency, and profiles. Change one parameter at a time or use a designed experiment so results are attributable to specific settings. - - question: Should I set MySQL parameters in an option file or on the `mysqld` command line? + - question: Should I set MySQL parameters in an option file or on the mysqld command line? answer: >- Use an option file for persistent tuning so changes are reviewable, version controlled, and applied on restart. The examples in the Learning Path target the `[mysqld]` group. Command-line flags are suitable diff --git a/content/learning-paths/servers-and-cloud-computing/neoverse-rdv3-swstack/2_rdv3_bootseq.md b/content/learning-paths/servers-and-cloud-computing/neoverse-rdv3-swstack/2_rdv3_bootseq.md index 63f307520f..d66b7efa59 100644 --- a/content/learning-paths/servers-and-cloud-computing/neoverse-rdv3-swstack/2_rdv3_bootseq.md +++ b/content/learning-paths/servers-and-cloud-computing/neoverse-rdv3-swstack/2_rdv3_bootseq.md @@ -26,7 +26,7 @@ After BL2, the Runtime Security Engine (RSE, Cortex-M55) authenticates critical ## Stage 2: Early hardware initialization (SCP/MCP) -Once RSE completes verification, the System Control Processor (SCP, Cortex-M7) and the Management Control Processor (MCP, where present) are released from reset. +Once RSE completes verification, the System Control Processor (SCP, Cortex-M7) and the Manageability Control Processor (MCP, where present) are released from reset. They perform essential bring-up: * Initializing clocks, reset lines, and power domains diff --git a/content/learning-paths/servers-and-cloud-computing/neoverse-rdv3-swstack/5_rdv3_modify.md b/content/learning-paths/servers-and-cloud-computing/neoverse-rdv3-swstack/5_rdv3_modify.md index a42fcc66f6..3668485cb2 100644 --- a/content/learning-paths/servers-and-cloud-computing/neoverse-rdv3-swstack/5_rdv3_modify.md +++ b/content/learning-paths/servers-and-cloud-computing/neoverse-rdv3-swstack/5_rdv3_modify.md @@ -8,7 +8,7 @@ layout: learningpathall ## The RD-V3-R1 dual-chip platform -The RD-V3-R1 platform is a dual-chip simulation environment built to model multi-die Arm server SoCs. It expands on the single-die RD-V3 design by introducing a second application processor and a Management Control Processor (MCP). +The RD-V3-R1 platform is a dual-chip simulation environment built to model multi-die Arm server SoCs. It expands on the single-die RD-V3 design by introducing a second application processor and a Manageability Control Processor (MCP). Key use cases of RD-V3-R1 are: diff --git a/content/learning-paths/servers-and-cloud-computing/neoverse-rdv3-swstack/_index.md b/content/learning-paths/servers-and-cloud-computing/neoverse-rdv3-swstack/_index.md index 5f2433687c..3a0dde416a 100644 --- a/content/learning-paths/servers-and-cloud-computing/neoverse-rdv3-swstack/_index.md +++ b/content/learning-paths/servers-and-cloud-computing/neoverse-rdv3-swstack/_index.md @@ -1,17 +1,17 @@ --- -title: Develop and Validate Firmware Pre-Silicon on Arm Neoverse CSS V3 +title: Develop and validate firmware pre-silicon on Arm Neoverse CSS V3 minutes_to_complete: 90 who_is_this_for: This advanced topic is for firmware developers, system architects, and silicon validation engineers working on Arm Neoverse CSS platforms who require a pre-silicon workflow for the CSS-V3 reference design using Fixed Virtual Platforms (FVPs). learning_objectives: - - Explain the CSS-V3 architecture and the RD-V3 firmware boot sequence (TF-A, RSE, SCP/MCP/LCP, UEFI/GRUB, Linux) - - Set up a containerized build environment and sync sources with a pinned manifest using repo - - Build and boot the RD-V3 firmware stack on FVP and map UART consoles to components - - Interpret boot logs to verify bring-up and diagnose boot-stage issues - - Modify platform control firmware (for example, SCP/MCP) and validate changes via pre-silicon simulation - - Launch a dual-chip RD-V3-R1 simulation and verify AP/MCP coordination + - Understand the CSS-V3 architecture and the RD-V3 firmware boot sequence. + - Set up a containerized build environment and sync sources with a pinned manifest using repo. + - Build and boot the RD-V3 firmware stack on FVP and map UART consoles to components. + - Interpret boot logs to verify bring-up and diagnose boot-stage issues. + - Modify platform control firmware such as System Control Processor (SCP) and Manageability Control Processor (MCP), and validate changes through pre-silicon simulation. + - Launch a dual-chip RD-V3-R1 simulation and verify application processor (AP) and MCP coordination. prerequisites: - Access to an Arm Neoverse-based Linux machine (cloud or local) with at least 80 GB of free storage @@ -19,11 +19,58 @@ prerequisites: - Understanding of firmware boot stages and SoC-level architecture - Docker installed, or a GitHub Codespaces-compatible development environment +# START generated_summary_faq +generated_summary_faq: + template_version: summary-faq-v3 + generated_at: '2026-10-01T19:38:48Z' + generator: ai + ai_assisted: true + ai_review_required: true + model: gpt-5 + prompt_template: summary-faq-v3 + source_hash: edcf755937689018d68a687def84b27f119fcfa2542949294f965c21e0d0d642 + summary_generated_at: '2026-10-01T19:38:48Z' + summary_source_hash: edcf755937689018d68a687def84b27f119fcfa2542949294f965c21e0d0d642 + faq_generated_at: '2026-10-01T19:38:48Z' + faq_source_hash: edcf755937689018d68a687def84b27f119fcfa2542949294f965c21e0d0d642 + summary: >- + You'll build the Arm Neoverse RD-V3 firmware stack and test it on an Arm FVP + before hardware is available. First, you'll prepare a repeatable environment, sync pinned sources, compile + the boot chain, and select the FVP version that matches your release. You'll then map UART consoles, + follow the boot to a Buildroot Linux shell, validate firmware changes, and run the dual-chip + RD-V3-R1 simulation. + faqs: + - question: How do I choose the correct FVP model for my RD-V3 build? + answer: >- + Match your FVP version to your RD-V3 release tag. For `RD-INFRA-2025.07.03`, + use FVP version `11.29.35`. Check the release tags list for other version mappings. + - question: What result should I expect when the single-die simulation boots successfully? + answer: >- + In `terminal_ns_uart0`, select **RD-V3 Buildroot** from the GRUB menu and press **Enter**. + You should see `Welcome to Buildroot`, then a `buildroot login:` prompt. Log in as + `root` to reach the Linux shell. + - question: Where can I find the UART logs from the RD-V3 simulation? + answer: >- + Look in `~/rdv3/model-scripts/rdinfra/platforms/rdv3/rdv3`, where each UART is redirected + to its own log file. Use the UART-to-terminal mapping to identify the subsystem you + want to inspect, such as Linux, TF-A and UEFI, SCP, RSE, MCP, or LCP. + - question: How do I validate the MCP firmware change on RD-V3-R1? + answer: >- + Add `FWK_LOG_CRIT("[FWK] Customer code here");` in `fwk_module_start()` in + `~/rdv3r1/host/scp/framework/src/fwk_module.c`. Rebuild and repackage with `rdv3r1` build commands, then relaunch the FVP. Check your MCP UART + output for `[FWK] Customer code here` to confirm that your change executes. + - question: How do I know the dual-chip RD-V3-R1 simulation is running correctly? + answer: >- + After launching `./boot-buildroot.sh -p rdv3r1`, check the UART consoles to verify + that both application processors, AP0 and AP1, boot in coordination. You can inspect + the additional MCP console and logs in `~/rdv3r1/model-scripts/rdinfra/platforms/rdv3r1/rdv3r1`. +# END generated_summary_faq + author: - Odin Shen - Ann Cheng -generate_summary_faq: true +generate_summary_faq: false rerun_summary: false rerun_faqs: false @@ -64,4 +111,3 @@ weight: 1 # _index.md always has weight of 1 to order corr layout: "learningpathall" # All files under learning paths have this same wrapper learning_path_main_page: "yes" # This should be surfaced when looking for related content. Only set for _index.md of learning path content. --- - diff --git a/content/learning-paths/servers-and-cloud-computing/nested-virtualization/1-preparation.md b/content/learning-paths/servers-and-cloud-computing/nested-virtualization/1-preparation.md index 9d663aade5..914375b0c8 100644 --- a/content/learning-paths/servers-and-cloud-computing/nested-virtualization/1-preparation.md +++ b/content/learning-paths/servers-and-cloud-computing/nested-virtualization/1-preparation.md @@ -1,52 +1,84 @@ --- -title: Preparing the Host for Nested Virtualization +title: Preparing the host for nested virtualization weight: 2 ### FIXED, DO NOT MODIFY layout: learningpathall --- -## Why use Nested Virtualization? +## Why use nested virtualization? -Virtualization allows organizations to mutualize large multi-core servers into smaller environments with a strong level of resource isolation. It is a foundational technology for cloud computing, and Cloud Service Providers typically provide compute resources to their customers using virtualization. +Virtualization allows organizations to partition large multi-core servers into smaller environments with a strong level of resource isolation. It is a foundational technology for cloud computing, and Cloud Service Providers typically provide compute resources to their customers using virtualization. -Nested virtualization enables guest virtual machines to serve as hypervisors, and run guest virtual machines inside a VM or cloud instance. There are a few use-cases where this facility is very useful: +Nested virtualization enables guest virtual machines to serve as hypervisors, and run guest virtual machines inside a VM or cloud instance. There are a few use cases where this facility is useful. -* **Resource isolation:** When running container applications in cloud instances, containers share a common operating system. By running containers inside microVMs like Firecracker VM, the container workloads share nothing with the host operating system -* **Mobile application development and testing:** It is very desirable to build and test Android applications on cloud infrastructure like Kubernetes. Cloud hosted Kubernetes compute nodes are typically virtual machine instances. Running Android environments in Kubernetes in this situation requires nested virtualization. -* **Testing and development of Embedded applications:** Many embedded applications are running specialized real-time or embedded operating systems, rather than Linux. Developing and testing these environments requires virtualization, which means developing on bare metal or VMs with nested virtualization enabled. +### Resource isolation -## Terminology and server set-up +When running container applications in cloud instances, containers share a common operating system. By running containers inside microVMs such as Firecracker VM, the container workloads share nothing with the host operating system. -Throughout this Learning Path, we will refer to the bare metal server as L0, guest VMs running on the bare metal server as L1 VMs, and nested VMs as L2 VMs. We will be starting two VMs on the host, which we will call `fedora-l1-guest` and `fedora-l1-hyper`, and inside `fedora-l1-hyper` we will create another VM called `fedora-l2-guest`. Both "guest" VMs will be 8-core virtual machines, and the `fedora-l1-hyper` VM will be assigned 16 cores. +### Mobile application development and testing -This will allow us to allocate similar amounts of resources, and pin those resources to specific cores, to minimize any potential resource conflicts across the bare metal host and the VMs when we are comparing the performance of a reference benchmark at the end. +Developers often want to build and test Android applications on cloud infrastructure such as Kubernetes. Cloud hosted Kubernetes compute nodes are typically virtual machine instances. Running Android environments in Kubernetes in this situation requires nested virtualization. -Nested virtualization: -![Server layout with Nested Virtualization#center](nv2_stack.png "Figure 1. Nested virtualization") +### Testing and development of embedded applications + +Many embedded applications run specialized real-time or embedded operating systems, rather than Linux. Developing and testing these environments requires virtualization, which means developing on bare metal or VMs with nested virtualization enabled. + +## Terminology and server setup + +The bare metal server is referred to as L0, guest VMs running on the bare metal server as L1 VMs, and nested VMs as L2 VMs. The examples assume an Arm64 bare metal host with 64 cores or more. You start two VMs on the host, called `fedora-l1-guest` and `fedora-l1-hyper`, and inside `fedora-l1-hyper` you create another VM called `fedora-l2-guest`. Both "guest" VMs are 8-core virtual machines, and the `fedora-l1-hyper` VM is assigned 16 cores. + +This lets you allocate similar amounts of resources, and pin those resources to specific cores, to minimize any potential resource conflicts across the bare metal host and the VMs when you compare the performance of a reference benchmark at the end. + +The following diagram shows the nested virtualization layout: +![Layered diagram showing nested virtualization. Hardware at the base runs a hypervisor, which hosts two VMs: one VM runs its own hypervisor with two nested VMs inside it, and a second VM runs applications directly. Each VM shows its apps and kernel.#center](nv2_stack.webp "Nested virtualization layout") ## Installing the virtualization software stack -To run a nested VM on Arm64, you need both a recent Linux kernel (6.13 or later, preferably 7.2 or later) and a recent version of qemu-kvm (version 10.1.0 or later), and all of the other virtualization tooling that we will need to create and run virtual machines using virsh and NV2_stack - -1. Connect to a host running Fedora 44 or later, and install prerequisite packages: - ``` - sudo dnf -y install libvirt libvirt-daemon-qemu libvirt-client qemu-kvm virt-install dnsmasq genisoimage - ``` -2. Enable nested virtualization on the host by passing the kernel argument `kvm-arm.mode=nested` to the Linux kernel at boot time, then reboot the server: - ``` - sudo grubby --args="kvm-arm.mode=nested" --update-kernel=ALL - sudo reboot - ``` -3. Verify after reboot that capability is available: - ``` - sudo dmesg | grep -i "nested\|vhe\|nv" - # Expected: "CPU features: detected: Nested Virtualization Support" - # Expected: "kvm [1]: VHE+NV2 mode initialized successfully" - ``` - -At the end of this step, you should have installed all of the virtualization tools that we will use to prepare and start virtual machines on the bare metal host, and confirmed that your host is now running with nested virtualization enabled. - -Next, we will create and start an L1 guest VM running Fedora 44. +The host is an Arm64 server running Fedora 44, which includes a recent enough kernel and `qemu-kvm` to support nested virtualization. You install the remaining virtualization tools with the `dnf` package manager in the following steps. + +{{% notice Before you start %}} +This must run on a bare metal Arm64 server, whether a cloud bare metal instance or a local physical machine. Nested virtualization needs direct access to the processor's EL2 virtualization support, which is not available inside a standard virtual machine. Running these steps inside an existing VM does not enable nested virtualization. + +Nested virtualization on Arm64 also depends on a recent Linux kernel (6.13 or later, preferably 7.2 or later) and `qemu-kvm` version 10.1.0 or later. Fedora 44 meets these requirements. These version details are included for awareness if you want to adapt the steps to another distribution. +{{% /notice %}} + +### Install the virtualization packages + +Install the prerequisite packages: + +```bash +sudo dnf -y install libvirt libvirt-daemon-qemu libvirt-client qemu-kvm virt-install dnsmasq genisoimage +``` + +### Enable nested virtualization + +Enable nested virtualization on the host by passing the kernel argument `kvm-arm.mode=nested` to the Linux kernel at boot time, then reboot the server: + +```bash +sudo grubby --args="kvm-arm.mode=nested" --update-kernel=ALL +sudo reboot +``` + +### Verify nested virtualization is enabled + +After the reboot, verify that the capability is available: + +```bash +sudo dmesg | grep -iE "Nested Virtualization Support|VHE\+NV2" +``` + +The output is similar to: + +```output +CPU features: detected: Nested Virtualization Support +kvm [1]: VHE+NV2 mode initialized successfully +``` + +The `VHE+NV2 mode initialized successfully` line is the key confirmation that nested virtualization is active. If you only see `VHE mode initialized successfully` without `+NV2`, nested virtualization is not enabled, which usually means the kernel argument was not applied or the hardware does not support FEAT_NV2. + +At the end of this step, you should have installed all of the virtualization tools you use to prepare and start virtual machines on the bare metal host, and confirmed that your host is now running with nested virtualization enabled. + +Next, you create and start an L1 guest VM running Fedora 44. diff --git a/content/learning-paths/servers-and-cloud-computing/nested-virtualization/2-create-l1-guest.md b/content/learning-paths/servers-and-cloud-computing/nested-virtualization/2-create-l1-guest.md index c3da7537b8..412547ac1b 100644 --- a/content/learning-paths/servers-and-cloud-computing/nested-virtualization/2-create-l1-guest.md +++ b/content/learning-paths/servers-and-cloud-computing/nested-virtualization/2-create-l1-guest.md @@ -1,91 +1,128 @@ --- -title: Preparing and Starting an L1 Guest Virtual Machine +title: Preparing and starting an L1 guest virtual machine weight: 3 ### FIXED, DO NOT MODIFY layout: learningpathall --- -## Preparing and Starting an L1 Guest VM - -Before we create a nested VM, we will create an L1 Guest VM which will be running on our host, so that we have an unnested VM to compare the performance overhead of virtualization with the additional overnead of nested virtualization, and to compare the additional work required to create an L1 hypervisor VM at the next step. - -To create a VM, we will first download a cloud image, make some configuration changes to the base image using cloud-config, and install and start the VM using `virt-install`. Once we have started the VM, we will connect to it to confirm that it has started correctly. - -1. Download an Arm64 cloud image to use for the VMs. To be consistent with the host, we use Fedora 44, but any recent Linux distribution will do: - ``` - curl -LO https://dl.fedoraproject.org/pub/fedora/linux/releases/42/Cloud/aarch64/images/Fedora-Cloud-Base-Generic-42-1.1.aarch64.qcow2 - ``` - -2. We will make copies of our base disk image and resize it to give the VM more disk space. For convenience, we also set some environment variables: - ``` - export VMDIR="/var/lib/libvirt/images” - export IMG="${HOME}/Fedora-Cloud-Base-Generic-42-1.1.aarch64.qcow2" - export DISK_L1_GUEST="${VMDIR}/fedora-l1-guest.qcow2" - - # Prepare the L1 guest image - sudo cp "$IMG" "$DISK_L1_GUEST" - sudo qemu-img resize "$DISK_L1_GUEST" 40G - ``` - -3. Create a seed ISO image for `cloud-init`. `cloud-init` will run automatically the first time that we instantiate the virtual machine, and can perform some initialization actions for us. `cloud-init` looks for special files called `meta-data` and `user-data` for configuration information. In this case, we want to create a default unprivileged user (fedora), ensure that the user has permission to run commands as root using `sudo` without a password, and that our public SSH key is included in the `authorized_keys` of that user so that we can SSH into the virtual machine from the host. If you do not have a public/private key-pair, this is a good time to generate the pair, and use the public key file here. - - After creating these files, we will generate a special CD disk image (format ISO) which will be attached to the VM when we instantiate it. Once we have the ISO image, we copy it to the same directory as the qcow2 VM image we created in the previous step. - - ``` - cat > /tmp/user-data < - chpasswd: - expire: False - ssh_pwauth: False - EOF - - cat > /tmp/meta-data <` with the contents of `~/.ssh/guest_key.pub`. You use the matching private key, `~/.ssh/guest_key`, to connect to the VM later. + +After creating these files, you generate a special CD disk image (format ISO) that is attached to the VM when you instantiate it. After you have the ISO image, copy it to the same directory as the qcow2 VM image you created in the previous step. + +```bash + cat > /tmp/user-data < + chpasswd: + expire: False + ssh_pwauth: False + EOF + + cat > /tmp/meta-data < /tmp/user-data < - chpasswd: - expire: False - ssh_pwauth: False - EOF - - cat > /tmp/meta-data < block that enables virtualization functionality in the guest. - - ``` - sudo virsh edit fedora-l1-hyper - - # In the XML file defining the virtual machine, change the opening tag to: - - - # At the end of the file, just before the tag, add the following: - - - - - - # Save the XML file, and verify that it passes validation against the libvirt schema - ``` - -6. At this point, we should be able to boot our L1 hypervisor VM and verify that virtualization is supported: - - ``` - sudo virsh start fedora-l1-hyper - # After a few seconds... - sudo virsh net-dhcp-leases default - ssh –i /path/to/private_key fedora@{ip address} - - #Inside the fedora-l1-hyper VM, verify that virtualization functionality has been passed through - sudo dmesg | grep -i "el2\|vhe\|kvm" - # Expected: "CPU: All CPU(s) started at EL2" - # Expected: "kvm [1]: VHE mode initialized successfully" - ls -la /dev/kvm - # /dev/kvm should exist inside the VM - ``` - -If you see the line “CPU: All CPU(s) started at EL2”, congratulations! You now have a virtual machine that can function as a hypervisor. We will now install virtualization tooling and instantiate our first L2 guest VM. - +## Create a hypervisor virtual machine on the host + +At this point, you have used command line tools to create, boot, and connect to a guest virtual machine running on your Arm64 host. By the end of this step, you have another virtual machine running on the host that can serve as a hypervisor for nested virtual machines. To complete this step, you connect to it over SSH and verify that it is capable of running a virtual machine. + +This process is essentially the same as the previous step up to the point where you get a VM running. + +## Set environment variables + +Set similar convenience environment variables to the previous step. The L1 hypervisor needs a bigger disk than the L1 guest, because it contains an identical 40GB L2 guest image inside it: + +```bash +# If these environment variables are already set, there is no need to re-set them +export VMDIR="/var/lib/libvirt/images" +export IMG="${HOME}/Fedora-Cloud-Base-Generic-44-1.7.aarch64.qcow2" + +# Use a different QCOW2 filename for the hypervisor VM +export DISK_L1_HYPER="${VMDIR}/fedora-l1-hyper.qcow2" + +# Prepare the L1 hypervisor image +sudo cp "$IMG" "$DISK_L1_HYPER" +sudo qemu-img resize "$DISK_L1_HYPER" 80G +``` + +## Create a cloud-init seed ISO + +Create a seed ISO image for the guest hypervisor for cloud-init. Use the same SSH key pair that you generated or used in the previous step - the main change here is the hostname of the guest. + +```bash + cat > /tmp/user-data < + chpasswd: + expire: False + ssh_pwauth: False + EOF + + cat > /tmp/meta-data <` block that enables virtualization functionality in the guest. + +Open the VM configuration in an editor: + +```bash +sudo virsh edit fedora-l1-hyper +``` + +Change the opening `` tag to import the qemu namespace schema: + +```xml + +``` + +Then, just before the closing `` tag, add the following block: + +```xml + + + + +``` + +Save the file. `virsh` validates the XML against the libvirt schema when you save. + +## Boot the hypervisor VM and verify virtualization support + +Boot the L1 hypervisor VM and verify that virtualization is supported: + +```bash + sudo virsh start fedora-l1-hyper + # After a few seconds... + sudo virsh net-dhcp-leases default + ssh -i ~/.ssh/guest_key fedora@{ip address} +``` + +Inside the `fedora-l1-hyper` VM, verify that virtualization functionality has been passed through: + +```bash +sudo dmesg | grep -i "el2\|vhe\|kvm" +``` + +The output is similar to: + +```output +CPU: All CPU(s) started at EL2 +kvm [1]: VHE mode initialized successfully +``` + +Confirm that `/dev/kvm` exists inside the VM: + +```bash +ls -la /dev/kvm +``` + +If you see the line `CPU: All CPU(s) started at EL2`, you now have a virtual machine that can function as a hypervisor. Next, you install virtualization tooling and instantiate your first L2 guest VM. diff --git a/content/learning-paths/servers-and-cloud-computing/nested-virtualization/4-create-l2-guest.md b/content/learning-paths/servers-and-cloud-computing/nested-virtualization/4-create-l2-guest.md index a1c9928c00..697acab39e 100644 --- a/content/learning-paths/servers-and-cloud-computing/nested-virtualization/4-create-l2-guest.md +++ b/content/learning-paths/servers-and-cloud-computing/nested-virtualization/4-create-l2-guest.md @@ -1,6 +1,6 @@ --- -title: Booting a nested L2 guest VM in the Hypervisor VM -weight: 6 +title: Booting a nested L2 guest VM in the hypervisor VM +weight: 5 ### FIXED, DO NOT MODIFY layout: learningpathall @@ -8,94 +8,119 @@ layout: learningpathall ## Prepare and boot a nested L2 guest VM inside the hypervisor VM -This process will essentially be identical to the process we followed to create the L1 guest VM on the host. We install all the virtualziation tools we will need in our Hypervisor VM, copy the base image from the host to the fedora-l1-hyper VM, resize its disk, create a new seed ISO, and re-run virt-install for fedora-l2-guest. - -1. Copy the Fedora cloud image and private SSH key from the host into the hypervisor using scp. - - ``` - # In the host: - scp –i /path/to/private/key $IMG /path/to/private/key fedora@{fedora-l1-hyper IP address}:~/ - ``` - -2. Log in to the `fedora-l1-hyper` VM, and install the same virtualization software stack as we installed on the host: - ``` - sudo dnf -y install libvirt libvirt-daemon-qemu libvirt-client qemu-kvm virt-install dnsmasq genisoimage - ``` - -3. After the installation has completed, restart the hypervisor VM from the host to start virtualization services. You will need to wait a minute to be able to connect again with SSH: - ``` - # In the host: - sudo virsh reboot fedora-l1-hyper - # After boot is complete - ssh -i /path/to/private/key fedora@{fedora-l1-hyper IP address} - ``` - -4. After the VM restarts, connect to it again and create the L2 guest VM image: - ``` - export VMDIR="/var/lib/libvirt/images" - export IMG="/home/fedora/Fedora-Cloud-Base-Generic-44-1.7.aarch64.qcow2" - export DISK_L2_GUEST="/var/lib/libvirt/images/fedora-l2-guest.qcow2" - - # Prepare the L2 guest image - sudo cp "$IMG" "$DISK_L2_GUEST" - sudo qemu-img resize "$DISK_L2_GUEST" 40G - ``` - -5. Create a seed ISO image for `cloud-config` similar to the other VMs. - - ``` - cat > /tmp/user-data < - chpasswd: - expire: False - ssh_pwauth: False - EOF - - cat > /tmp/meta-data <` below: + +```bash + cat > /tmp/user-data < + chpasswd: + expire: False + ssh_pwauth: False + EOF + + cat > /tmp/meta-data <` with the domain name for each of the VMs (`fedora-l1-guest`, etc): - ``` - sudo virsh vcpuinfo - sudo virsh emulatorpin - ``` -4. Install the sysbench benchmarking tool in all sustems under test (the host, the L1 guest, and the L2 guest): - ``` - sudo dnf install -y sysbench - ``` -5. On each of the systems under test, run the same benchmark test. You can run these in 3 different terminals, at the same time, and since they are using different cores, they should not significantly interfere with each other: - ``` - # On the host: - taskset -c 8-15 sysbench cpu --cpu-max-prime=20000 --threads=8 --time=60 run - - # On the L1 and L2 guests - sysbench cpu --cpu-max-prime=20000 --threads=8 --time=60 run - ``` - -In our tests, the results were as follows: +## Test nested virtualization overhead + +To measure the overhead of nested virtualization, you install and run a reference benchmark across the three layers: L0 (bare metal), the L1 guest VM, and the L2 guest VM. To make the test comparable, and to minimize interaction with other workloads running on the server, you pin each system under test to a fixed set of host cores. + +The following table shows how each system is pinned: + +| System under test | vCPUs | Pinned to | +|-------------------|-------|-----------| +| Host (L0) benchmark | | Host cores 8-15 | +| L1 guest | 0-7 | Host cores 16-23 | +| L1 hypervisor | 0-15 | Host cores 24-39 | +| L2 guest | 0-7 | Hypervisor cores 8-15 (host cores 32-39) | + +After the core pinning is in place and sysbench is installed on all systems under test, you run the test, verify that the correct cores run at 100% CPU for the duration of the test, and check the results to see how much virtualization passthrough affects performance. You can apply this core pinning while the VMs are running by using the `--live` flag for virsh. + +## Pin cores for the L1 guest and L1 hypervisor + +Pin the cores for the L1 guest and L1 hypervisor: + +```bash + # Pin L1 guest to cores 16-23 + for i in $(seq 0 7); do + sudo virsh vcpupin fedora-l1-guest $i $((i+16)) --config --live + done + sudo virsh emulatorpin fedora-l1-guest 16-23 --config --live + + # Pin L1 hypervisor to cores 24-39 + for i in $(seq 0 15); do + sudo virsh vcpupin fedora-l1-hyper $i $((i+24)) --config --live + done + sudo virsh emulatorpin fedora-l1-hyper 24-39 --config --live +``` + +## Pin cores for the L2 guest + +Log in to `fedora-l1-hyper` and pin the cores of `fedora-l2-guest` to cores 8-15: + +```bash + # On host: + ssh -i ~/.ssh/guest_key fedora@${fedora-l1-hyper IP address} + + # Inside L1 hypervisor: pin L2 guest to vCPUs 8-15 + for i in $(seq 0 7); do + sudo virsh vcpupin fedora-l2-guest $i $((i+8)) --config --live + done + sudo virsh emulatorpin fedora-l2-guest 8-15 --config --live +``` + +## Verify the core pinning + +Verify that the pinning is correct, replacing `` with the domain name for each of the VMs, such as `fedora-l1-guest`: + +```bash +sudo virsh vcpuinfo +sudo virsh emulatorpin +``` + +## Install sysbench + +Install the sysbench benchmarking tool on all systems under test (the host, the L1 guest, and the L2 guest): + +```bash +sudo dnf install -y sysbench +``` + +## Run the benchmark + +Run the same benchmark test on each system under test. You can run these in three different terminals at the same time, and because they use different cores, they should not significantly interfere with each other. + +On the bare metal host, use `taskset` to bind the benchmark to physical cores 8-15: + +```bash +taskset -c 8-15 sysbench cpu --cpu-max-prime=20000 --threads=8 --time=60 run +``` + +On the L1 guest and the L2 guest, run the benchmark without `taskset`, because you already pinned their virtual CPUs with virsh in the previous steps: + +```bash +sysbench cpu --cpu-max-prime=20000 --threads=8 --time=60 run +``` + +The following is an example of the output from a single run: + +```output +sysbench 1.0.20 (using system LuaJIT 2.1.1761727121) + +Running the test with following options: +Number of threads: 8 +Initializing random number generator from current time + + +Prime numbers limit: 20000 + +Initializing worker threads... + +Threads started! + +CPU speed: + events per second: 9693.21 + +General statistics: + total time: 60.0007s + total number of events: 581607 + +Latency (ms): + min: 0.82 + avg: 0.83 + max: 0.98 + 95th percentile: 0.83 + sum: 479906.25 + +Threads fairness: + events (avg/stddev): 72700.8750/20.69 + execution time (avg/stddev): 59.9883/0.00 +``` + +### How to read the output + +The fields that matter most for comparing the three layers are: + +- `events per second` under `CPU speed` is the throughput. Higher is better. This is the primary number to compare across bare metal, L1, and L2. +- The `Latency (ms)` block reports per-request latency. `min`, `avg`, and `95th percentile` describe typical responsiveness, while `max` captures the worst-case spike. Lower is better. +- `events (avg/stddev)` under `Threads fairness` shows how evenly work was spread across threads. The second number is the standard deviation; a smaller value means more even distribution. + +### Record your results + +Run the benchmark on each layer and record the values from your own output. Fill in a table like this one with your results: + +| Metric | Bare metal (L0) | L1 guest | L2 nested guest | +| ------ | --------------- | -------- | --------------- | +| Events/sec | | | | +| Overhead vs bare metal | | | | +| Min latency (ms) | | | | +| Avg latency (ms) | | | | +| Max latency (ms) | | | | +| 95th percentile (ms) | | | | +| Thread stddev | | | | + +To calculate the overhead for a layer, compare its events/sec against the bare metal result: + +```text +overhead = (bare_metal_events_per_second - layer_events_per_second) / bare_metal_events_per_second * 100 +``` + +For example, if bare metal reports 9693 events/sec and the L2 nested guest reports 9219 events/sec, the overhead is `(9693 - 9219) / 9693 * 100`, or less than 5%. + +Compare the events/sec and latency values across the three layers to see how virtualization and nested virtualization affect your workload. The average and 95th percentile latencies are often close across all three layers, while the maximum latency tends to increase with each level of nesting. + +{{% notice Note %}} +Results vary with the Arm server, CPU generation, kernel, and workload, so draw your conclusions from your own measurements rather than from any single reference figure. Nested virtualization support continues to improve, so newer Arm Neoverse generations typically show lower overhead than older ones. Focus on the relative difference between bare metal, L1, and L2 on your own hardware. +{{% /notice %}} + +Here is an example of a completed table: | Metric | Bare Metal | L1 KVM Guest | L2 Nested Guest | | ------ | ---------- | ------------ | --------------- | | Events/sec | 2533.02 | 2525.84 | 2306.22 | -| Overhead vs bare metal | — | -0.3% | -8.9% | +| Overhead vs bare metal | N/A | -0.3% | -8.9% | | Min latency | 3.14ms | 3.15ms | 3.38ms | | Avg latency | 3.16ms | 3.17ms | 3.47ms | | Max latency | 9.16ms | 13.02ms | 19.72ms | -| 95th percentile | 3.13ms | 3.19ms | 3.55ms | -| Thread stddev | 52.18 | 41.46 | 73.47 | +| 95th percentile | 3.13ms | 3.19ms | 3.55ms | +| Thread stddev | 52.18 | 41.46 | 73.47 | + +## What you've accomplished + +You have configured and run nested virtualization on an Arm server from end to end. Along the way, you: + +- Enabled nested virtualization on an Arm64 bare metal host and confirmed FEAT_NV2 support. +- Created and started an L1 guest VM with `virt-install` and `cloud-init`. +- Configured a second VM to act as a hypervisor by enabling virtualization passthrough. +- Booted a nested L2 guest VM inside the hypervisor. +- Pinned CPU cores across all three layers and benchmarked them with sysbench. -We see some performance drop-off for the nested VM in events/sec (about 10% between bare metal and nested virtual machine), and there is a significant difference in max latency across the three levels, but the average and P95 latencies are very consistent across all three systems under test. +You can now use these techniques to run virtual machines inside virtual machines on Arm, whether for workload isolation, hypervisor development and testing, or running microVMs in cloud environments. -What this shows is that while latency-sensitive workloads may not be a good match for nested VMs in performance critical situations, the performance of applications is good enough for the test & development environments where nested virt is the best fit. diff --git a/content/learning-paths/servers-and-cloud-computing/nested-virtualization/_index.md b/content/learning-paths/servers-and-cloud-computing/nested-virtualization/_index.md index 0a5a2746d8..25774db3f1 100644 --- a/content/learning-paths/servers-and-cloud-computing/nested-virtualization/_index.md +++ b/content/learning-paths/servers-and-cloud-computing/nested-virtualization/_index.md @@ -1,26 +1,26 @@ --- -title: Nested Virtualization on an Arm64 Server +title: Configure and run nested virtualization on an Arm server draft: true cascade: draft: true -description: Configure an Arm server to allow guest virtual machines to act as hypervisors, enabling users to run virtual machines inside a virtual machine. +description: Set up nested virtualization on an Arm server so guest virtual machines can act as hypervisors and run other virtual machines inside them. -minutes_to_complete: 20 +minutes_to_complete: 60 -who_is_this_for: This is an advanced topic for System Administrators who are interested in enabling users to run VMs inside virtual machines. +who_is_this_for: This is an advanced topic for system administrators who want to enable nested virtualization, so users can run virtual machines inside other virtual machines. learning_objectives: - - Starting a Virtual Machine on Linux using virsh - - Configuring a host and guest VM to allow the guest VM to function as a hypervisor - - Using a standard benchmark suite, measure the performance impact of running software inside a nested virtual machine against a regular guest VM and on bare metal + - Enable nested virtualization on an Arm server and verify the host supports it + - Create and start Arm64 guest virtual machines using virt-install, cloud-init, and virsh + - Configure a guest VM to act as a hypervisor and run a nested VM inside it + - Measure the performance overhead of a nested VM against a standard guest VM and bare metal using sysbench prerequisites: - - An Arm64 server supporting FEAT_NV2 (available in Arm v8.4-A and later) - - Fedora 44 (or any distribution with a version 7.2 or later Linux kernel) + - An Arm64 bare metal server with 64 cores or more, supporting FEAT_NV2 (available in Arm v8.4-A and later), with Fedora 44 installed -author: David Neary +author: Dave Neary # New Learning Paths are opted in for the next manual generated summary/FAQ run. # The generator resets this to false after a successful write. @@ -40,22 +40,23 @@ armips: tools_software_languages: - KVM - libvirt - - virsh / virt-manager + - virsh + - virt-install operatingsystems: - Linux further_reading: - resource: - title: PLACEHOLDER MANUAL - link: PLACEHOLDER MANUAL LINK + title: Nested virtualization in the Arm AArch64 virtualization guide + link: https://support.arm.com/documentation/102142/0100/Nested-virtualization?lang=en type: documentation - resource: - title: PLACEHOLDER BLOG - link: PLACEHOLDER BLOG LINK + title: 'Unlocking Layers: Powering Nested Virtualization with Ampere CPUs' + link: https://amperecomputing.com/blogs/unlocking-layers type: blog - resource: - title: PLACEHOLDER GENERAL WEBSITE - link: PLACEHOLDER GENERAL WEBSITE LINK + title: 'Fedora Discussion: nested virtualization on Arm64' + link: https://discussion.fedoraproject.org/t/nested-virtualization/179358 type: website ### FIXED, DO NOT MODIFY diff --git a/content/learning-paths/servers-and-cloud-computing/nested-virtualization/nv2_stack.png b/content/learning-paths/servers-and-cloud-computing/nested-virtualization/nv2_stack.png deleted file mode 100644 index f1be7a3af0..0000000000 Binary files a/content/learning-paths/servers-and-cloud-computing/nested-virtualization/nv2_stack.png and /dev/null differ diff --git a/content/learning-paths/servers-and-cloud-computing/nested-virtualization/nv2_stack.webp b/content/learning-paths/servers-and-cloud-computing/nested-virtualization/nv2_stack.webp new file mode 100644 index 0000000000..efba4ef52c Binary files /dev/null and b/content/learning-paths/servers-and-cloud-computing/nested-virtualization/nv2_stack.webp differ diff --git a/content/learning-paths/servers-and-cloud-computing/net-aspire/_index.md b/content/learning-paths/servers-and-cloud-computing/net-aspire/_index.md index 666de5fe99..a4d26cb135 100644 --- a/content/learning-paths/servers-and-cloud-computing/net-aspire/_index.md +++ b/content/learning-paths/servers-and-cloud-computing/net-aspire/_index.md @@ -6,18 +6,66 @@ minutes_to_complete: 60 who_is_this_for: This is an introductory topic for software developers interested in learning how to deploy .NET Aspire applications on Arm-based virtual machines (VMs) on Amazon Web Services (AWS) and Google Cloud Platform (GCP). learning_objectives: - - Demonstrate knowledge and understanding of .NET Aspire developer tools. + - Understand .NET Aspire developer tools. - Create a .NET Aspire application. - Modify code on a Windows on Arm development machine. - Deploy a .NET Aspire application to Arm-powered virtual machines in the Cloud. prerequisites: - - A Windows on Arm machine, for example the Lenovo Thinkpad X13s running Windows 11 to build the .NET Aspire project. - - An [Arm-based instance](/learning-paths/servers-and-cloud-computing/csp/) from AWS or GCP. - - Any code editor. [Visual Studio Code for Arm64](https://code.visualstudio.com/docs/?dv=win32arm64user) is an example of a suitable editor. + - A Windows on Arm machine such as the Lenovo Thinkpad X13s running Windows 11, to build the .NET Aspire project + - An [Arm-based instance](/learning-paths/servers-and-cloud-computing/csp/) from AWS or GCP + - A code editor, such as [Visual Studio Code for Arm64](https://code.visualstudio.com/docs/?dv=win32arm64user) + +# START generated_summary_faq +generated_summary_faq: + template_version: summary-faq-v3 + generated_at: '2026-10-01T19:39:10Z' + generator: ai + ai_assisted: true + ai_review_required: true + model: gpt-5 + prompt_template: summary-faq-v3 + source_hash: 09c62748f1623172f6adbc48bb3411abd4bad6c0e477c8c0aa66b19f8369db69 + summary_generated_at: '2026-10-01T19:39:10Z' + summary_source_hash: 09c62748f1623172f6adbc48bb3411abd4bad6c0e477c8c0aa66b19f8369db69 + faq_generated_at: '2026-10-01T19:39:10Z' + faq_source_hash: 09c62748f1623172f6adbc48bb3411abd4bad6c0e477c8c0aa66b19f8369db69 + summary: >- + You'll create and run a .NET Aspire application on a Windows on Arm development machine, then deploy + it to an Arm-based VM. First, you'll install the Aspire workload, trust the local HTTPS + certificate, and inspect the application through its startup output and dashboard. You'll then + add a compute-intensive service, rebuild the application, and deploy it to an AWS Graviton-based + EC2 instance. You can also complete these steps on an Arm-based Google Cloud VMs. + faqs: + - question: How do I verify the .NET SDK and install the Aspire workload before creating the + project? + answer: >- + Run `dotnet --version` in PowerShell and confirm that you have .NET SDK 8.0 or later. + Then run `dotnet workload install aspire` and check for `Successfully installed workload(s) aspire.` + - question: Why do I need to trust the HTTPS development certificate, and how do I do it? + answer: >- + You use HTTPS when running the application locally. Run `dotnet dev-certs https --trust` + before starting your application to trust its HTTPS development certificate. + - question: Which project do I run to start the distributed application, and what output should + I expect? + answer: >- + From the project directory, run `dotnet run --project NetAspire.Arm.AppHost`. You should see + messages such as `Building...`, an Aspire version line, and `Distributed application starting`. + - question: Where do I add the intensive computations, and how can I confirm the change worked? + answer: >- + Add the supplied `ComputationService.cs` code to `NetAspire.Arm.ApiService`. In `Program.cs`, + update the `/weatherforecast` handler to call `ComputationService.PerformIntensiveCalculations(matrixSize: 800)`. + Run `dotnet run --project NetAspire.Arm.AppHost`, select **Weather** in your web frontend, + then open **Traces** in the dashboard to inspect the longer request duration. + - question: Which ports do I need to expose to access the deployed application? + answer: >- + Allow TCP traffic on the ports that your application uses. The example uses `7133`, + `7511`, and `17222`. Match your application's port values in the Amazon EC2 security group + or a Google Cloud firewall rule associated with your VM's `dotnet-app` network tag. +# END generated_summary_faq author: Dawid Borycki -generate_summary_faq: true +generate_summary_faq: false rerun_summary: false rerun_faqs: false @@ -60,4 +108,3 @@ weight: 1 # _index.md always has weight of 1 to order corr layout: "learningpathall" # All files under learning paths have this same wrapper learning_path_main_page: "yes" # This should be surfaced when looking for related content. Only set for _index.md of learning path content. --- - diff --git a/content/learning-paths/servers-and-cloud-computing/nginx-on-azure/_index.md b/content/learning-paths/servers-and-cloud-computing/nginx-on-azure/_index.md index 62cafc4cb1..35cb08ff5c 100644 --- a/content/learning-paths/servers-and-cloud-computing/nginx-on-azure/_index.md +++ b/content/learning-paths/servers-and-cloud-computing/nginx-on-azure/_index.md @@ -6,17 +6,61 @@ minutes_to_complete: 30 who_is_this_for: This is an introductory topic for system administrators and developers who want to learn how to deploy and benchmark NGINX on Microsoft Azure Cobalt 100 Arm-based instances. learning_objectives: - - Create an Arm64 virtual machine on Azure Cobalt 100 (Dpsv6) using the Azure console with Ubuntu Pro 24.04 LTS as the base image - - Install and configure the NGINX web server on the Azure Arm64 virtual machine - - Configure and test a static website with NGINX on the virtual machine - - Run baseline NGINX performance tests with ApacheBench (ab) on Ubuntu Pro 24.04 LTS Arm64 + - Create an Arm64 virtual machine (VM) on Azure Cobalt 100 (Dpsv6) using the Azure console with Ubuntu Pro 24.04 LTS as the base image. + - Install and configure the NGINX web server on the Azure Arm64 VM. + - Configure and test a static website with NGINX on the VM. + - Run baseline NGINX performance tests with ApacheBench (ab). prerequisites: - - A [Microsoft Azure](https://azure.microsoft.com/) account with access to Cobalt 100 based instances (Dpsv6) + - A [Microsoft Azure](https://azure.microsoft.com/) account with access to Cobalt 100-based instances (Dpsv6) + +# START generated_summary_faq +generated_summary_faq: + template_version: summary-faq-v3 + generated_at: '2026-10-01T19:39:38Z' + generator: ai + ai_assisted: true + ai_review_required: true + model: gpt-5 + prompt_template: summary-faq-v3 + source_hash: f556dee0540494860c18e38f186e7a602db0635cded4fd4c6cbb649e115ef294 + summary_generated_at: '2026-10-01T19:39:38Z' + summary_source_hash: f556dee0540494860c18e38f186e7a602db0635cded4fd4c6cbb649e115ef294 + faq_generated_at: '2026-10-01T19:39:38Z' + faq_source_hash: f556dee0540494860c18e38f186e7a602db0635cded4fd4c6cbb649e115ef294 + summary: >- + You'll deploy NGINX on an Arm-based Microsoft Azure VM powered by Cobalt 100, and capture a baseline + benchmark. First, you'll create an Ubuntu Pro 24.04 LTS Dpsv6 VM in the Azure portal, install NGINX, + and verify its default page. You'll then configure a static site and confirm that NGINX serves + your content. Finally, you'll install ApacheBench and record initial throughput and timing results. + faqs: + - question: Which Azure VM size and image should I choose? + answer: >- + Use an Arm-based VM in the Dpsv6 series with the Ubuntu Pro 24.04 LTS Arm64 image. + - question: How do I know that NGINX installed correctly before I change any configuration? + answer: >- + Run `sudo systemctl status nginx` and check for `Active: active (running)`. + Use `curl -I http://localhost/` to check for `HTTP/1.1 200 OK`. Then, open + your VM's public IP address in a browser to confirm that you see the default welcome page. + - question: Where should I put my static site files, and what should I expect when it works? + answer: >- + Create `/var/www/my-static-site/index.html` and configure `/etc/nginx/conf.d/static-site.conf` + to use that directory and `index.html`. Disable the packaged default site with + `sudo unlink /etc/nginx/sites-enabled/default`. Run `sudo nginx -t` before + `sudo nginx -s reload`, then open your VM's public IP address to check your custom page. + - question: Should I run ApacheBench on the VM or from another machine? + answer: >- + Run ApacheBench on your NGINX VM to match the demonstrated baseline test. + Use `ab -n 1000 -c 50 http://localhost/` to send 1,000 requests with 50 concurrent connections. + - question: Which package installs ApacheBench on Ubuntu Pro 24.04 LTS, and how do I verify it? + answer: >- + Install the `apache2-utils` package. Run `ab -V` to confirm that ApacheBench is available and + prints a version string. +# END generated_summary_faq author: Pareena Verma -generate_summary_faq: true +generate_summary_faq: false rerun_summary: false rerun_faqs: false @@ -56,4 +100,3 @@ weight: 1 # _index.md always has weight of 1 to order corr layout: "learningpathall" # All files under learning paths have this same wrapper learning_path_main_page: "yes" # This should be surfaced when looking for related content. Only set for _index.md of learning path content. --- - diff --git a/content/learning-paths/servers-and-cloud-computing/nginx/_index.md b/content/learning-paths/servers-and-cloud-computing/nginx/_index.md index 206f6eee82..00ac9953d8 100644 --- a/content/learning-paths/servers-and-cloud-computing/nginx/_index.md +++ b/content/learning-paths/servers-and-cloud-computing/nginx/_index.md @@ -1,23 +1,70 @@ --- -title: Learn how to deploy Nginx +title: Deploy NGINX on Arm minutes_to_complete: 60 -who_is_this_for: This is an introductory topic for engineers who want to use Nginx on Arm. +who_is_this_for: This is an introductory topic for engineers who want to use NGINX on Arm. learning_objectives: - - Install and run Nginx on Arm servers - - Set up Nginx as a web server, reverse proxy, or an API Gateway - - Verify Nginx is working correctly + - Install and run NGINX on Arm servers. + - Set up NGINX as a web server, reverse proxy, or an API Gateway. + - Verify NGINX is working correctly. prerequisites: - - To create a file server you will need at least one [Arm based instance](/learning-paths/servers-and-cloud-computing/csp/) from a cloud service provider or one on-premises Arm server. - - To create a reverse proxy or API gateway you will need at least three Arm based instances from a cloud service provider or at least three on-premises Arm servers. - - Network settings (firewalls and security groups) which allow communication on port 22 (SSH) and port 443 (HTTPS). + - At least one [Arm based instance](/learning-paths/servers-and-cloud-computing/csp/) from a cloud service provider, or one on-premises Arm server, to create a file server + - At least three Arm based instances from a cloud service provider, or at least three on-premises Arm servers, to create a reverse proxy or API gateway + - Network settings (firewalls and security groups) which allow communication on port 22 (SSH) and port 443 (HTTPS) + +# START generated_summary_faq +generated_summary_faq: + template_version: summary-faq-v3 + generated_at: '2026-10-01T19:40:11Z' + generator: ai + ai_assisted: true + ai_review_required: true + model: gpt-5 + prompt_template: summary-faq-v3 + source_hash: 877a608fc16135d4bcfc1ce710fb257fcea3ec3af75052841a3182bd024f8384 + summary_generated_at: '2026-10-01T19:40:11Z' + summary_source_hash: 877a608fc16135d4bcfc1ce710fb257fcea3ec3af75052841a3182bd024f8384 + faq_generated_at: '2026-10-01T19:40:11Z' + faq_source_hash: 877a608fc16135d4bcfc1ce710fb257fcea3ec3af75052841a3182bd024f8384 + summary: >- + You'll deploy NGINX on Arm-based Linux servers and progress from a package installation to a working + web-service configuration. First, you'll install NGINX from a package and inspect the packaged build options that you can use to compile + NGINX from source when you need a custom build. You'll then configure an HTTPS static file server, + create its key and certificate, and verify file delivery. Finally, you'll configure a third node + as a reverse proxy and API gateway for two upstream servers. + faqs: + - question: How do I know which NGINX features are enabled in the packaged install? + answer: >- + Run `nginx -V` to inspect your packaged build. You can see the NGINX and OpenSSL + versions, compiler flags, module options, and configured paths in the output. + - question: Do I need to build NGINX from source? + answer: >- + You can install NGINX with a package manager. If you choose a source build, + use the packaged build options as a starting point and enable the features you need. + - question: What do I need before setting up the reverse proxy and API gateway? + answer: >- + Set up two file servers and prepare a third node for the reverse + proxy and API gateway. Ensure network settings allow communication on ports 22 (SSH) and 443 + (HTTPS). + - question: How do I verify that the static HTTPS file server is working? + answer: >- + Run `wget --no-check-certificate https://localhost/file.txt` on your server, then + `wget --no-check-certificate https:///file.txt` from another node. + You can also check the index page with `curl -k https:///index.html`. + - question: How do I verify the setup of the reverse proxy and API gateway? + answer: >- + Retrieve `file.txt` through the reverse proxy and `apigw_file.txt` through + `/api_old/apigw_file.txt`. Your API gateway rewrites `/api_old/` to `/api_new/` + before forwarding the request upstream. Verify retrieval with `wget` + commands on the proxy node and another node that isn't the proxy. +# END generated_summary_faq author: Julio Suarez -generate_summary_faq: true +generate_summary_faq: false rerun_summary: false rerun_faqs: false @@ -61,4 +108,3 @@ weight: 1 # _index.md always has weight of 1 to order corr layout: "learningpathall" # All files under learning paths have this same wrapper learning_path_main_page: "yes" # This should be surfaced when looking for related content. Only set for _index.md of learning path content. --- - diff --git a/content/learning-paths/servers-and-cloud-computing/nginx_tune/_index.md b/content/learning-paths/servers-and-cloud-computing/nginx_tune/_index.md index 4194b1b189..aa9951355f 100644 --- a/content/learning-paths/servers-and-cloud-computing/nginx_tune/_index.md +++ b/content/learning-paths/servers-and-cloud-computing/nginx_tune/_index.md @@ -13,9 +13,9 @@ learning_objectives: - Measure NGINX performance before and after tuning. prerequisites: - - An NGINX file server, reverse proxy, or API gateway running on a cloud instance, bare-metal server, or Arm AGI CPU platform. - - A repeatable HTTP workload or load test that you can run before and after tuning. - - If you don't already have an NGINX setup, see [Learn how to deploy NGINX](/learning-paths/servers-and-cloud-computing/nginx/). + - An NGINX file server, reverse proxy, or API gateway running on a cloud instance, bare-metal server, or Arm AGI CPU platform + - A repeatable HTTP workload or load test that you can run before and after tuning + - An [NGINX setup](/learning-paths/servers-and-cloud-computing/nginx/) # START generated_summary_faq generated_summary_faq: @@ -91,7 +91,7 @@ operatingsystems: test_images: - ubuntu:latest test_link: null -test_maintenance: true +test_maintenance: false further_reading: - resource: diff --git a/content/learning-paths/servers-and-cloud-computing/nlp-hugging-face/_index.md b/content/learning-paths/servers-and-cloud-computing/nlp-hugging-face/_index.md index 72616389e4..2ed9338ae2 100644 --- a/content/learning-paths/servers-and-cloud-computing/nlp-hugging-face/_index.md +++ b/content/learning-paths/servers-and-cloud-computing/nlp-hugging-face/_index.md @@ -3,7 +3,7 @@ title: Run a Natural Language Processing (NLP) model from Hugging Face on Arm se minutes_to_complete: 20 -who_is_this_for: This is an introductory topic for software developers who want to learn how to run a Natural Language Processing (NLP) model from Hugging Face using PyTorch on Arm based servers. +who_is_this_for: This is an introductory topic for software developers who want to learn how to run a natural language processing (NLP) model from Hugging Face using PyTorch on Arm based servers. learning_objectives: - Deploy a PyTorch NLP model from Hugging Face on an Arm AArch64 CPU @@ -12,9 +12,53 @@ learning_objectives: prerequisites: - An [Arm based instance](/learning-paths/servers-and-cloud-computing/csp/) from a cloud service provider or an on-premise Arm server. +# START generated_summary_faq +generated_summary_faq: + template_version: summary-faq-v3 + generated_at: '2026-10-01T19:40:33Z' + generator: ai + ai_assisted: true + ai_review_required: true + model: gpt-5 + prompt_template: summary-faq-v3 + source_hash: 5d0c0a2a23fba1796458c60ac49fa5d4b43e0d827463e049afb0e1b79eb9292f + summary_generated_at: '2026-10-01T19:40:33Z' + summary_source_hash: 5d0c0a2a23fba1796458c60ac49fa5d4b43e0d827463e049afb0e1b79eb9292f + faq_generated_at: '2026-10-01T19:40:33Z' + faq_source_hash: 5d0c0a2a23fba1796458c60ac49fa5d4b43e0d827463e049afb0e1b79eb9292f + summary: >- + You'll run and profile a Hugging Face NLP model with PyTorch on an Arm + AArch64 CPU. First, you'll prepare an Ubuntu 22.04 LTS Arm server and install PyTorch, Transformers, + and SciPy. You'll then run a RoBERTa sentiment-analysis model and inspect its ranked labels and + confidence scores. Finally, you'll add the PyTorch Profiler and review the CPU execution time + for individual operators. + faqs: + - question: How can I analyze my own text with the example? + answer: >- + Replace the string assigned to `text` in `sentiment-analysis.py`, save the file, and run + `python sentiment-analysis.py` again. The script preprocesses your text before passing it + to the tokenizer and model. + - question: How does the example handle usernames and links? + answer: >- + When you run the script, its `preprocess` function replaces words that start with `@` and + contain more than one character with `@user`. It replaces words that start with `http` + with `http` before tokenization. + - question: Which Hugging Face model should I use? + answer: >- + Use the `cardiffnlp/twitter-roberta-base-sentiment-latest` model. + - question: What result should I expect when I run the model? + answer: >- + A successful run prints three ranked sentiment labels with confidence scores. The scores + should sum to 1. The first line is the model's strongest prediction. + - question: How do I know that the PyTorch profiler captured my run? + answer: >- + You should see a table of CPU operators and their execution times before the classification + output. The `model_inference` row summarizes the profiled inference region. +# END generated_summary_faq + author: Pareena Verma -generate_summary_faq: true +generate_summary_faq: false rerun_summary: false rerun_faqs: false @@ -59,4 +103,3 @@ weight: 1 # _index.md always has weight of 1 to order corr layout: "learningpathall" # All files under learning paths have this same wrapper learning_path_main_page: "yes" # This should be surfaced when looking for related content. Only set for _index.md of learning path content. --- - diff --git a/content/learning-paths/servers-and-cloud-computing/node-js-gcp/_index.md b/content/learning-paths/servers-and-cloud-computing/node-js-gcp/_index.md index 811d016346..ff4dd91c45 100644 --- a/content/learning-paths/servers-and-cloud-computing/node-js-gcp/_index.md +++ b/content/learning-paths/servers-and-cloud-computing/node-js-gcp/_index.md @@ -1,23 +1,66 @@ --- -title: Deploy Node.js on Google Cloud C4A Arm-based Axion VMs +title: Deploy Node.js on Google Cloud C4A Arm-based Axion virtual machines minutes_to_complete: 30 -who_is_this_for: This is an introductory topic for software developers migrating Node.js workloads from x86_64 to Arm-based servers, specifically on Google Cloud C4A virtual machines built on Axion processors. +who_is_this_for: This is an introductory topic for software developers migrating Node.js workloads from x86_64 to Arm-based servers, specifically on Google Cloud C4A virtual machines (VMs) built on Axion processors. learning_objectives: - - Provision an Arm-based SUSE Linux Enterprise Server virtual machine on Google Cloud C4A instances with Axion processors - - Install and configure Node.js on a SUSE Arm64 (C4A) instance - - Validate Node.js functionality with baseline HTTP server tests - - Benchmark Node.js performance using Autocannon on Arm64 (AArch64) architecture + - Provision an Arm-based SUSE Linux Enterprise Server VM on Google Cloud C4A instances with Axion processors. + - Install and configure Node.js on a SUSE Arm64 (C4A) instance. + - Validate Node.js functionality with baseline HTTP server tests. + - Benchmark Node.js performance using Autocannon on Arm64 (AArch64) architecture. prerequisites: - A [Google Cloud Platform (GCP)](https://cloud.google.com/free) account with billing enabled - Familiarity with networking concepts and [Node.js event-driven architecture](https://nodejs.org/en/docs/guides/event-loop-timers-and-nexttick) +# START generated_summary_faq +generated_summary_faq: + template_version: summary-faq-v3 + generated_at: '2026-10-01T19:41:01Z' + generator: ai + ai_assisted: true + ai_review_required: true + model: gpt-5 + prompt_template: summary-faq-v3 + source_hash: c21d775ef47f9befc1046df0d89f746550a180b29974c45261e0478f59df503e + summary_generated_at: '2026-10-01T19:41:01Z' + summary_source_hash: c21d775ef47f9befc1046df0d89f746550a180b29974c45261e0478f59df503e + faq_generated_at: '2026-10-01T19:41:01Z' + faq_source_hash: c21d775ef47f9befc1046df0d89f746550a180b29974c45261e0478f59df503e + summary: >- + You'll deploy and benchmark a Node.js workload on an Arm-based Google Cloud C4A VM. + First, you'll provision a SUSE Linux Enterprise Server instance on Google Axion, then install Node.js + with Node Version Manager (NVM). You'll then validate the runtime in the read-eval-print loop (REPL) + and with a minimal HTTP server. Finally, you'll use Autocannon to measure request throughput + and latency and capture a baseline for the VM. + faqs: + - question: Which VM options should I select when creating the instance? + answer: >- + Use a C4A machine type, such as `c4a-standard-4` with four vCPUs and 16 GB. Select a SUSE Linux + Enterprise Server image for Arm64. + - question: How do I select Node.js v24 for new terminal sessions? + answer: >- + After installing Node.js with `nvm install v24`, add `nvm use v24` to your `~/.bashrc` file. + Place it after the commands that load NVM so that new Bash sessions select Node.js v24. + - question: How do I confirm that Node.js is working before I benchmark? + answer: >- + Run the REPL test to print a message and start the sample HTTP server. Proceed when the + server responds to an HTTP request on the configured port. + - question: What URL should I pass to Autocannon? + answer: >- + Use `http://localhost:80` when you run Autocannon on the same VM as the sample + Node.js server. + - question: How can I change the load and duration of the Autocannon benchmark? + answer: >- + Adjust `-c` to set the number of concurrent connections and `-d` to set the test duration + in seconds. The example uses `-c 100 -d 10` to run 100 concurrent connections for ten seconds. +# END generated_summary_faq + author: Pareena Verma -generate_summary_faq: true +generate_summary_faq: false rerun_summary: false rerun_faqs: false @@ -61,4 +104,3 @@ weight: 1 layout: "learningpathall" learning_path_main_page: "yes" --- - diff --git a/content/learning-paths/servers-and-cloud-computing/oci-terraform/_index.md b/content/learning-paths/servers-and-cloud-computing/oci-terraform/_index.md index d307b2ca5e..826ca76535 100644 --- a/content/learning-paths/servers-and-cloud-computing/oci-terraform/_index.md +++ b/content/learning-paths/servers-and-cloud-computing/oci-terraform/_index.md @@ -1,20 +1,64 @@ --- -title: Deploy Arm Instances on Oracle Cloud Infrastructure (OCI) using Terraform +title: Deploy Arm Instances on Oracle Cloud Infrastructure using Terraform minutes_to_complete: 60 who_is_this_for: This is an introductory topic for software developers who are new to deploying Arm instances on Oracle Cloud Infrastructure (OCI) using Terraform. learning_objectives: - - Automate Arm virtual machine creation on OCI using Terraform + - Automate Arm virtual machine (VM) creation on OCI using Terraform prerequisites: - - An OCI account + - An [OCI account](/learning-paths/servers-and-cloud-computing/csp/oci/) - A computer with Terraform installed +# START generated_summary_faq +generated_summary_faq: + template_version: summary-faq-v3 + generated_at: '2026-10-01T19:41:27Z' + generator: ai + ai_assisted: true + ai_review_required: true + model: gpt-5 + prompt_template: summary-faq-v3 + source_hash: f927b34764047b05b4bd225ef1080f9cc345d6bd91ec8220fd23d710a478013d + summary_generated_at: '2026-10-01T19:41:27Z' + summary_source_hash: f927b34764047b05b4bd225ef1080f9cc345d6bd91ec8220fd23d710a478013d + faq_generated_at: '2026-10-01T19:41:27Z' + faq_source_hash: f927b34764047b05b4bd225ef1080f9cc345d6bd91ec8220fd23d710a478013d + summary: >- + You'll use Terraform to automate the creation of an Arm VM on + OCI Ampere Compute. First, you'll prepare a Linux control environment, then define the OCI resources as + code and run the Terraform workflow to provision the instance. You can use the resulting + configuration as a repeatable foundation for additional Arm infrastructure on OCI. + faqs: + - question: Which computer can I use to run Terraform? + answer: >- + You can use any computer with the required tools installed, including a desktop, laptop, + or a virtual machine. The command format assumes that you're working on a Linux machine. + - question: What are the OCI API keys and SSH keys used for? + answer: >- + The OCI API key pair is used to authenticate Terraform's requests to OCI. The + SSH key pair is used to connect to the provisioned instance. Set `private_key_path` for the API + private key and `ssh_authorized_keys_path` and `ssh_private_key_path` for your SSH keys. + - question: How can I test OCI authentication before creating the VM? + answer: >- + Create `availability-domains.tf` with the provided data source and output definition, then + run `terraform plan`. If authentication succeeds, you'll see the availability domains in your + tenancy. Keep this file in the same directory as `provider.tf`. + - question: How can I display the provisioned instance's public IP address? + answer: >- + Create `outputs.tf` with the provided `public_ip` output definition, which reads + `oci_core_instance.Ampere.public_ip`. After provisioning the instance, run `terraform refresh` + to display its public IP address. + - question: What result should I expect after I complete the workflow? + answer: >- + You'll finish with an OCI Arm instance provisioned from your Terraform configuration. +# END generated_summary_faq + author: Frédéric -lefred- Descamps -generate_summary_faq: true +generate_summary_faq: false rerun_summary: false rerun_faqs: false @@ -49,4 +93,3 @@ weight: 1 # _index.md always has weight of 1 to order corr layout: "learningpathall" # All files under learning paths have this same wrapper learning_path_main_page: "yes" # This should be surfaced when looking for related content. Only set for _index.md of learning path content. --- - diff --git a/content/learning-paths/servers-and-cloud-computing/onnx-on-azure/_index.md b/content/learning-paths/servers-and-cloud-computing/onnx-on-azure/_index.md index 87c35b38dd..2c6c7e0aca 100644 --- a/content/learning-paths/servers-and-cloud-computing/onnx-on-azure/_index.md +++ b/content/learning-paths/servers-and-cloud-computing/onnx-on-azure/_index.md @@ -6,17 +6,64 @@ minutes_to_complete: 60 who_is_this_for: This Learning Path is for developers deploying ONNX-based applications on Arm-based machines. learning_objectives: - - Provision an Azure Arm64 virtual machine using Azure console, with Ubuntu Pro 24.04 LTS as the base image - - Perform ONNX baseline testing and benchmarking on Arm64 virtual machines + - Provision an Azure Arm64 virtual machine (VM) using Azure console, with Ubuntu Pro 24.04 LTS as the base image. + - Perform ONNX baseline testing and benchmarking on Arm64 VMs. prerequisites: - - A [Microsoft Azure](https://azure.microsoft.com/) account with access to Cobalt 100 based instances (Dpsv6) + - A [Microsoft Azure](https://azure.microsoft.com/) account with access to Cobalt 100-based instances (Dpsv6) - Basic understanding of Python and machine learning concepts - Familiarity with [ONNX Runtime](https://onnxruntime.ai/docs/) and Azure cloud services +# START generated_summary_faq +generated_summary_faq: + template_version: summary-faq-v3 + generated_at: '2026-10-01T19:41:46Z' + generator: ai + ai_assisted: true + ai_review_required: true + model: gpt-5 + prompt_template: summary-faq-v3 + source_hash: 3dfb5d613a9e88a35777de0adc65fbcacd46a906031c4d933c744abf9689e6a0 + summary_generated_at: '2026-10-01T19:41:46Z' + summary_source_hash: 3dfb5d613a9e88a35777de0adc65fbcacd46a906031c4d933c744abf9689e6a0 + faq_generated_at: '2026-10-01T19:41:46Z' + faq_source_hash: 3dfb5d613a9e88a35777de0adc65fbcacd46a906031c4d933c744abf9689e6a0 + summary: >- + You'll run and benchmark SqueezeNet 1.0 INT8 inference with ONNX Runtime on an Arm-based Azure VM powered by Cobalt + 100. First, you'll provision an Ubuntu Pro 24.04 LTS Dpsv6 VM and prepare a Python + environment. You'll then run a baseline script to validate inference and record latency. Finally, + you'll use `onnxruntime_perf_test` to collect more detailed performance statistics on the Arm64 + system. + faqs: + - question: Which Azure VM size and OS image should I use? + answer: >- + Select `D4ps_v6` from the Dpsv6 series powered by Azure Cobalt 100. + Use the Ubuntu Pro 24.04 LTS image and select **Arm64** as the VM architecture. + - question: What do I need in place before running the baseline latency test? + answer: >- + Activate your Python virtual environment, have the `baseline.py` script ready, and ensure that + the SqueezeNet INT8 model file is available as `squeezenet-int8.onnx`. Place the model in + the same working directory as the script or provide its full path. + - question: How do I know the baseline test worked correctly? + answer: >- + The script completes without errors and reports a timing result for a single inference through + the SqueezeNet INT8 model. Successful execution confirms that ONNX Runtime is functioning + on the VM. + - question: When should I use onnxruntime_perf_test, and what output should I expect? + answer: >- + Use `onnxruntime_perf_test` after validating the Python baseline to capture more detailed + performance statistics. Expect a summary of inference metrics that you can use to evaluate + ONNX Runtime efficiency on the Azure Arm64 instance. + - question: Does the baseline script use a real image as input? + answer: >- + No. The script uses data in `float32` with shape `(1, 3, 224, 224)`. This represents one input + with three color channels and dimensions of 224 by 224 pixels. You'll use this synthetic input + to measure inference latency. +# END generated_summary_faq + author: Pareena Verma -generate_summary_faq: true +generate_summary_faq: false rerun_summary: false rerun_faqs: false diff --git a/content/learning-paths/servers-and-cloud-computing/onnx/_index.md b/content/learning-paths/servers-and-cloud-computing/onnx/_index.md index aa75028f3b..c876ea5fad 100644 --- a/content/learning-paths/servers-and-cloud-computing/onnx/_index.md +++ b/content/learning-paths/servers-and-cloud-computing/onnx/_index.md @@ -7,17 +7,64 @@ who_is_this_for: This is an advanced topic for developers, ML engineers, and clo learning_objectives: - Quantize and run the Phi-4-mini model with ONNX Runtime on Azure. - - Analyze performance on Arm Neoverse N2 based Azure Cobalt 100 VMs. + - Analyze performance on Arm Neoverse N2-based Azure VMs powered by Cobalt 100. prerequisites: - - An [Arm-based instance](/learning-paths/servers-and-cloud-computing/csp/) from an appropriate cloud service provider. This Learning Path has been tested on an Azure Cobalt 100 virtual machine. - - Basic understanding of Python and machine learning concepts. - - Familiarity with ONNX Runtime and Azure cloud services. - - Knowledge of Large Language Model (LLM) fundamentals. + - A Microsoft Azure [Arm-based instance](/learning-paths/servers-and-cloud-computing/csp/) + - Basic understanding of Python and machine learning concepts + - Familiarity with ONNX Runtime and Azure cloud services + - Knowledge of large language model (LLM) fundamentals + +# START generated_summary_faq +generated_summary_faq: + template_version: summary-faq-v3 + generated_at: '2026-10-01T19:42:07Z' + generator: ai + ai_assisted: true + ai_review_required: true + model: gpt-5 + prompt_template: summary-faq-v3 + source_hash: 8c80b9778bfd1b0393e43e5eff673f0f83ec1e49c221ad33fe378fa6e5eba082 + summary_generated_at: '2026-10-01T19:42:07Z' + summary_source_hash: 8c80b9778bfd1b0393e43e5eff673f0f83ec1e49c221ad33fe378fa6e5eba082 + faq_generated_at: '2026-10-01T19:42:07Z' + faq_source_hash: 8c80b9778bfd1b0393e43e5eff673f0f83ec1e49c221ad33fe378fa6e5eba082 + summary: >- + You'll build ONNX Runtime on an Arm-based Azure server powered by Cobalt 100 and use it to run a Phi-4-mini + chatbot. First, you'll prepare Ubuntu 24.04 LTS, quantize and convert the model, and start the Python + chatbot with `onnxruntime_genai`. You'll then send text prompts and inspect the terminal metrics, + including tokens per second and time to first token, to validate inference and assess basic + performance on the Arm CPU. + faqs: + - question: What do I pass to the `--model_path` argument in the chatbot script? + answer: >- + Use the directory containing `genai_config.json` and `model.onnx` for the downloaded + INT4-quantized Phi-4-mini model. With the provided download command, pass + `cpu_and_mobile/cpu-int4-rtn-block-32-acc-level-4` as the model path. + - question: Which execution provider should I choose when running the chatbot? + answer: >- + If you're unsure, keep the default execution provider, `follow_config`, + so that the script uses providers defined in the model config. Override only if you need to explicitly + set a different provider, as the script clears providers when you do. + - question: How do I know that the chatbot server started correctly? + answer: >- + You should see the model load without errors and a prompt to enter text. If you start + the script with `--timings` or `-g`, you'll also see tokens + per second and time to first token after sending a prompt. + - question: What Azure VM configuration should I use? + answer: >- + Use the tested configuration as your reference: an Azure `Dpls_v6` Cobalt 100 VM + with 32 cores, 64 GB of RAM, and 32 GB of disk space. + - question: How can I limit the number of tokens in a chatbot response? + answer: >- + Set `--max_length` (or `-l`) when you run `phi4.py`. This limit includes both the prompt + and generated tokens, so it isn't a limit on the response alone. If you omit the option, + the script uses a maximum length of 2048 tokens. +# END generated_summary_faq author: Nobel Chowdary Mandepudi -generate_summary_faq: true +generate_summary_faq: false rerun_summary: false rerun_faqs: false @@ -54,4 +101,3 @@ weight: 1 # _index.md always has a weight of 1 to order co layout: "learningpathall" # All files under learning paths use this wrapper learning_path_main_page: "yes" # This should be surfaced when looking for related content. Only set for _index.md of learning path content. --- - diff --git a/content/learning-paths/servers-and-cloud-computing/openbmc-rdv3/_index.md b/content/learning-paths/servers-and-cloud-computing/openbmc-rdv3/_index.md index 12564e8751..4aca1e088d 100644 --- a/content/learning-paths/servers-and-cloud-computing/openbmc-rdv3/_index.md +++ b/content/learning-paths/servers-and-cloud-computing/openbmc-rdv3/_index.md @@ -6,11 +6,11 @@ minutes_to_complete: 120 who_is_this_for: This advanced topic is for firmware developers, platform software engineers, and system integrators working on Arm Neoverse-based platforms. It is especially useful for developers exploring pre-silicon development, testing, and integration of Baseboard Management Controllers (BMC) with UEFI firmware. If you are building or validating server-class reference platforms such as RD-V3, before hardware is available, this Learning Path shows you how to simulate and debug the full boot path using Fixed Virtual Platforms (FVPs). learning_objectives: - - Understand the role of OpenBMC and UEFI in the Arm server boot flow - - Simulate the firmware using the RD-V3 FVP - - Build and launch OpenBMC and UEFI images on the RD-V3 FVP - - Validate host–BMC communication using UART and Serial over LAN (SoL) - - Implement and validate a custom IPMI command in OpenBMC + - Understand the role of OpenBMC and UEFI in the Arm server boot flow. + - Simulate the firmware using the RD-V3 FVP. + - Build and launch OpenBMC and UEFI images on the RD-V3 FVP. + - Validate host–BMC communication using UART and Serial over LAN (SoL). + - Implement and validate a custom IPMI command in OpenBMC. prerequisites: - An Arm Neoverse-based Linux machine (cloud or local) running Ubuntu 22.04 LTS @@ -18,11 +18,56 @@ prerequisites: - Working knowledge of Docker, Git, and common Linux terminal tools - Basic understanding of the server firmware stack (such as UEFI, BMC, and TF-A) +# START generated_summary_faq +generated_summary_faq: + template_version: summary-faq-v3 + generated_at: '2026-10-01T19:42:25Z' + generator: ai + ai_assisted: true + ai_review_required: true + model: gpt-5 + prompt_template: summary-faq-v3 + source_hash: 285ebf154e6d58463132fc7914c672250ff2d66d0657710ec7eebd557386cbd4 + summary_generated_at: '2026-10-01T19:42:25Z' + summary_source_hash: 285ebf154e6d58463132fc7914c672250ff2d66d0657710ec7eebd557386cbd4 + faq_generated_at: '2026-10-01T19:42:25Z' + faq_source_hash: 285ebf154e6d58463132fc7914c672250ff2d66d0657710ec7eebd557386cbd4 + summary: >- + You'll simulate an Arm server boot flow on the Neoverse RD-V3 r1 FVP. First, you'll + build OpenBMC and host UEFI images in a Docker environment. Then, you'll launch the FVP and follow the + firmware logs through multiple UART consoles. You'll bridge virtual UARTs to validate SoL communication. Finally, you'll add a C++ Intelligent Platform Management Interface + (IPMI) handler to OpenBMC, rebuild the image, and verify its response. + faqs: + - question: What result should I expect when the RD-V3 FVP starts? + answer: >- + Multiple UART consoles open in separate terminal windows for different subsystems, such + as Neoverse V3, Cortex-M55, Cortex-M7, and the Cortex-A BMC. You should see boot logs for + both the BMC and host UEFI across these consoles. + - question: Will the FVP UART consoles appear over SSH? + answer: >- + The consoles are graphical terminals and require a desktop session. If you're connected + over SSH only, the windows won't render. To render the windows, launch the simulation from a desktop session. + - question: Which ports should I bridge to enable SoL? + answer: >- + Use the provided bridge: `socat -x tcp:localhost:5005 tcp:localhost:5067`. Verify that these + port mappings match the endpoints exposed by your running simulation. + - question: After creating the UART bridge, what should I see when opening the host console + from the BMC web UI? + answer: >- + The host console should be accessible through SoL and display the host’s console output. + You should see ongoing boot messages or a prompt from the host side. + - question: How do I validate that my custom IPMI command works? + answer: >- + Run `ipmitool raw 0x30 0x20` in the OpenBMC console. You'll see hexadecimal bytes. Exclude the first byte, which represents the payload length, + and decode the remaining bytes using the provided command. The decoded string should be + `Hello from OpenBMC IPMI!`. +# END generated_summary_faq + author: - Odin Shen - Ken Zhang -generate_summary_faq: true +generate_summary_faq: false rerun_summary: false rerun_faqs: false @@ -69,4 +114,3 @@ weight: 1 # _index.md always has weight of 1 to order corr layout: "learningpathall" # All files under learning paths have this same wrapper learning_path_main_page: "yes" # This should be surfaced when looking for related content. Only set for _index.md of learning path content. --- - diff --git a/content/learning-paths/servers-and-cloud-computing/opencv-on-axion/_index.md b/content/learning-paths/servers-and-cloud-computing/opencv-on-axion/_index.md index a67ee968e1..c76f20853a 100644 --- a/content/learning-paths/servers-and-cloud-computing/opencv-on-axion/_index.md +++ b/content/learning-paths/servers-and-cloud-computing/opencv-on-axion/_index.md @@ -7,20 +7,62 @@ minutes_to_complete: 45 who_is_this_for: This is an introductory topic for DevOps engineers, software developers, and AI practitioners who want to build and run computer vision pipelines on SUSE Linux Enterprise Server (SLES) Arm64 using OpenCV, process images and videos, visualize outputs in real time, and integrate ML models. learning_objectives: - - Install and configure OpenCV on Google Cloud C4A Axion Arm64 instances - - Build image processing pipelines using OpenCV - - Develop video processing pipelines with real-time frame updates - - Visualize OpenCV outputs in the browser using an HTTP server - - Integrate OpenCV pipelines with machine learning models + - Install and configure OpenCV on Google Cloud C4A Axion Arm64 virtual machines (VMs). + - Build image processing pipelines using OpenCV. + - Develop video processing pipelines with real-time frame updates. + - Visualize OpenCV outputs in the browser using an HTTP server. + - Integrate OpenCV pipelines with machine learning models. prerequisites: - A [Google Cloud Platform (GCP)](https://cloud.google.com/free) account with billing enabled - Basic familiarity with Python and Linux command line - - Understanding of basic image/video processing concepts + - Understanding of basic image and video processing concepts + +# START generated_summary_faq +generated_summary_faq: + template_version: summary-faq-v3 + generated_at: '2026-10-01T19:42:48Z' + generator: ai + ai_assisted: true + ai_review_required: true + model: gpt-5 + prompt_template: summary-faq-v3 + source_hash: 6382d70b3cd7040b984083cffee014bd2377ba150508b818e393263542a56333 + summary_generated_at: '2026-10-01T19:42:48Z' + summary_source_hash: 6382d70b3cd7040b984083cffee014bd2377ba150508b818e393263542a56333 + faq_generated_at: '2026-10-01T19:42:48Z' + faq_source_hash: 6382d70b3cd7040b984083cffee014bd2377ba150508b818e393263542a56333 + summary: >- + You'll build browser-based computer-vision pipelines with OpenCV on a Google Cloud C4A Axion VM. First, you'll provision a SUSE Linux VM, open port `8000`, and prepare a Python 3.11 virtual + environment. Then, you'll create image and video pipelines that process frames and serve updated + output through an HTTP server. Finally, you'll add a machine learning model, stream its visual + predictions, and validate the output from the VM's external IP address. + faqs: + - question: Which C4A machine type should I use? + answer: >- + Use the `c4a-standard-4` machine type with four vCPUs and 16 GB of memory. + - question: Which port do I open for the browser view, and how do I check that it works? + answer: >- + Open TCP port `8000` with a VPC firewall rule. After you start the project's HTTP server, + visit `http://EXTERNAL_IP:8000`. A page that loads confirms the rule and service are configured. + - question: What Python version do I need during setup? + answer: >- + Install Python 3.11. Additionally, install development tools with zypper to support building + OpenCV’s pip package. + - question: Where are the project files and virtual environment created? + answer: >- + Use the `~/opencv-project` directory and a virtual environment named `cv-env`. Run subsequent + scripts from that directory with the environment activated. + - question: How do I save the trained model and load it in the OpenCV pipeline? + answer: >- + Use `joblib.dump` in the training script to save the classifier as `iris_model.joblib` + and the label names as `iris_labels.joblib`. In the OpenCV pipeline, use `joblib.load` + to load both files so you can generate predictions without retraining the model. +# END generated_summary_faq author: Pareena Verma -generate_summary_faq: true +generate_summary_faq: false rerun_summary: false rerun_faqs: false diff --git a/content/learning-paths/servers-and-cloud-computing/openebs-cobalt/_index.md b/content/learning-paths/servers-and-cloud-computing/openebs-cobalt/_index.md index beac330100..da397928e9 100644 --- a/content/learning-paths/servers-and-cloud-computing/openebs-cobalt/_index.md +++ b/content/learning-paths/servers-and-cloud-computing/openebs-cobalt/_index.md @@ -8,10 +8,10 @@ minutes_to_complete: 60 who_is_this_for: This is an introductory topic for DevOps engineers, platform engineers, cloud-native developers, and Kubernetes administrators who want to deploy lightweight Kubernetes-native persistent storage on Arm-based cloud infrastructure. learning_objectives: - - Install and configure K3s Kubernetes on Arm64 virtual machines powered by Azure Cobalt 100 - - Deploy OpenEBS LocalPV using Helm - - Configure Kubernetes storage classes and PersistentVolumeClaims (PVCs) - - Deploy and validate stateful Kubernetes workloads with persistent storage + - Install and configure K3s Kubernetes on Arm64 virtual machines (VMs) powered by Azure Cobalt 100. + - Deploy OpenEBS LocalPV using Helm. + - Configure Kubernetes storage classes and PersistentVolumeClaims (PVCs). + - Deploy and validate stateful Kubernetes workloads with persistent storage. prerequisites: - A [Microsoft Azure account](https://azure.microsoft.com/) with access to Cobalt 100-based instances (Dpsv6) @@ -19,9 +19,51 @@ prerequisites: - Familiarity with SSH and remote server access - Basic understanding of Kubernetes concepts and containerized applications +# START generated_summary_faq +generated_summary_faq: + template_version: summary-faq-v3 + generated_at: '2026-10-01T19:43:29Z' + generator: ai + ai_assisted: true + ai_review_required: true + model: gpt-5 + prompt_template: summary-faq-v3 + source_hash: 33e9470ccc06f4835028050615832b7200164ec6ba1fdbc26689e6f4fd43bfa4 + summary_generated_at: '2026-10-01T19:43:29Z' + summary_source_hash: 33e9470ccc06f4835028050615832b7200164ec6ba1fdbc26689e6f4fd43bfa4 + faq_generated_at: '2026-10-01T19:43:29Z' + faq_source_hash: 33e9470ccc06f4835028050615832b7200164ec6ba1fdbc26689e6f4fd43bfa4 + summary: >- + You'll add Kubernetes-native persistent storage to a workload on an Arm64 Azure VM powered by Cobalt 100. First, you'll create a single-node K3s cluster, install OpenEBS LocalPV with Helm, and configure + its storage class. You'll then create a PersistentVolumeClaim, mount it in an NGINX application, + and verify that test data survives pod recreation. Finally, you'll expose the application through + a NodePort and update the Azure network security group for inbound access. + faqs: + - question: How can I install a specific version of the OpenEBS Helm chart? + answer: >- + Run `helm search repo openebs/openebs --versions` to list available chart versions, then add + `--version ` to the provided `helm install` command. Without this option, you install + the latest available chart. + - question: How do I expose the sample application so I can reach it from my browser? + answer: >- + Expose the NGINX deployment as a NodePort service and run `kubectl get svc` to see the assigned + port. Then add an inbound rule for that NodePort in the VM’s Network Security Group. + - question: What storage configuration should I use for the PersistentVolumeClaim? + answer: >- + Use `storageClassName: openebs-hostpath` and `accessModes: ReadWriteOnce` as shown in the PersistentVolumeClaim + manifest. This configuration targets OpenEBS LocalPV for dynamic provisioning. + - question: How do I know persistent storage is working before I move on? + answer: >- + Write data to the mounted volume in the NGINX pod, delete the pod, and let it recreate. + If the data is still present after the pod comes back, the volume is persisting as expected. + - question: Do I need a multi-node cluster? + answer: >- + No. Use a lightweight single-node Kubernetes cluster with K3s on the VM. +# END generated_summary_faq + author: Pareena Verma -generate_summary_faq: true +generate_summary_faq: false rerun_summary: false rerun_faqs: false diff --git a/content/learning-paths/servers-and-cloud-computing/openjdk-pacbti-gcp/_index.md b/content/learning-paths/servers-and-cloud-computing/openjdk-pacbti-gcp/_index.md index c45eafa423..76c404ec68 100644 --- a/content/learning-paths/servers-and-cloud-computing/openjdk-pacbti-gcp/_index.md +++ b/content/learning-paths/servers-and-cloud-computing/openjdk-pacbti-gcp/_index.md @@ -1,24 +1,67 @@ --- -title: Validate PAC/BTI security features in OpenJDK on Google Cloud C4A +title: Validate Pointer Authentication and Branch Target Identification in OpenJDK on Google Cloud C4A description: Test Pointer Authentication (PAC) and Branch Target Identification (BTI) support in OpenJDK on Google Cloud C4A. Learn to validate hardware security capabilities and verify JVM compiler support for Arm security features. minutes_to_complete: 30 -who_is_this_for: This is for Java developers running OpenJDK on Arm Neoverse platforms who want to verify PAC/BTI security features are properly enabled. Pointer Authentication (PAC) cryptographically signs return addresses to detect tampering, while Branch Target Identification (BTI) restricts where indirect branches can land. You'll learn to test both hardware capabilities and JVM compiler support for these Armv9 security features. +who_is_this_for: This is for Java developers running OpenJDK on Arm Neoverse platforms who want to verify Pointer Authentication (PAC) and Branch Target Identification (BTI) security features are properly enabled. PAC cryptographically signs return addresses to detect tampering, while BTI restricts where indirect branches can land. learning_objectives: - - Provision a Google Cloud C4A Arm-based virtual machine with SUSE Linux Enterprise Server. + - Provision a Google Cloud C4A Arm-based virtual machine (VM) with SUSE Linux Enterprise Server. - Install OpenJDK on the Arm-based VM. - - Verify PAC/BTI readiness in the installed JVM runtime. + - Verify PAC and BTI readiness in the installed Java VM (JVM) runtime. prerequisites: - A [Google Cloud Platform (GCP)](https://cloud.google.com/free) account with billing enabled - Optionally, [install the gcloud CLI](/install-guides/gcloud/) to connect to the VM from a local terminal instead of using the browser-based SSH +# START generated_summary_faq +generated_summary_faq: + template_version: summary-faq-v3 + generated_at: '2026-10-01T19:43:54Z' + generator: ai + ai_assisted: true + ai_review_required: true + model: gpt-5 + prompt_template: summary-faq-v3 + source_hash: 1e27a00bafb12e1648af702e7eadeefc15ceefb119b7e6eb3e4824787d547271 + summary_generated_at: '2026-10-01T19:43:54Z' + summary_source_hash: 1e27a00bafb12e1648af702e7eadeefc15ceefb119b7e6eb3e4824787d547271 + faq_generated_at: '2026-10-01T19:43:54Z' + faq_source_hash: 1e27a00bafb12e1648af702e7eadeefc15ceefb119b7e6eb3e4824787d547271 + summary: >- + You'll validate PAC and BTI support in Java + on a Google Axion C4A VM. First, you'll provision a SUSE Linux Enterprise Server VM and install + OpenJDK. You'll run a script that checks platform capabilities and JVM + just-in-time (JIT) compiler support. Then, you'll install Oracle JDK 21, repeat the tests, and + compare how each runtime reports branch-protection support. + faqs: + - question: How do I verify which JVM is active after installation? + answer: >- + Run `java --version`. The output should identify the distribution and version, for example + OpenJDK 17 on `aarch64`. + - question: How do I know that the C4A VM exposes PAC and BTI? + answer: >- + Run the provided test script and review its hardware checks. It should report that PAC and + BTI are available before you evaluate JVM compiler behavior. + - question: What result should I expect from the SUSE OpenJDK 17 tests? + answer: >- + The tests confirm the platform exposes PAC and BTI, but the SUSE-packaged OpenJDK 17 JIT + doesn't emit PAC and BTI instructions. This distinguishes hardware capability from the compiler’s + code generation. + - question: How do I ensure that the tests run against Oracle JDK 21? + answer: >- + Run `JAVA=./jdk-21.0.11/bin/java ./test-pacbti.sh` to select the Oracle JDK for the + test script. Replace `jdk-21.0.11` with your extracted directory name if it differs. + - question: Which C4A machine type should I use for this validation? + answer: >- + Use the `c4a-standard-4` machine type. +# END generated_summary_faq + author: Doug Anson -generate_summary_faq: true +generate_summary_faq: false rerun_summary: false rerun_faqs: false diff --git a/content/learning-paths/servers-and-cloud-computing/openrng-with-performix/_index.md b/content/learning-paths/servers-and-cloud-computing/openrng-with-performix/_index.md index d2de8ee51f..13af845ec8 100644 --- a/content/learning-paths/servers-and-cloud-computing/openrng-with-performix/_index.md +++ b/content/learning-paths/servers-and-cloud-computing/openrng-with-performix/_index.md @@ -8,18 +8,65 @@ minutes_to_complete: 45 who_is_this_for: This is an introductory topic for C++ developers who want to profile a data-processing workload on Arm Linux, identify performance bottlenecks with Arm Performix, and accelerate random number generation using OpenRNG and Arm Performance Libraries. learning_objectives: - - Build and run a baseline C++ data-processing workload on Arm Linux - - Use Arm Performix Code Hotspots to identify the highest-impact optimization target - - Accelerate random number generation by integrating OpenRNG and Arm Performance Libraries - - Measure performance improvements using a microbenchmark across multiple data sizes + - Build and run a baseline C++ data-processing workload on Arm Linux. + - Use Arm Performix Code Hotspots to identify the highest-impact optimization target. + - Accelerate random number generation by integrating OpenRNG and Arm Performance Libraries. + - Measure performance improvements using a microbenchmark across multiple data sizes. prerequisites: - - An Arm Linux (aarch64) server, such as an AWS Graviton3 instance + - An Arm Linux (aarch64) server, such as an AWS Graviton3-based instance - Basic understanding of C++ and CMake +# START generated_summary_faq +generated_summary_faq: + template_version: summary-faq-v3 + generated_at: '2026-10-01T19:44:16Z' + generator: ai + ai_assisted: true + ai_review_required: true + model: gpt-5 + prompt_template: summary-faq-v3 + source_hash: 004969d91992cbe6672e86cb8cc4734742e4461085198eaf09f3e2ff2afaf24d + summary_generated_at: '2026-10-01T19:44:16Z' + summary_source_hash: 004969d91992cbe6672e86cb8cc4734742e4461085198eaf09f3e2ff2afaf24d + faq_generated_at: '2026-10-01T19:44:16Z' + faq_source_hash: 004969d91992cbe6672e86cb8cc4734742e4461085198eaf09f3e2ff2afaf24d + summary: >- + You'll accelerate random-number generation in a C++ workload on Arm Linux with OpenRNG. First, you'll build + and run the baseline application, then use Arm Performix Code Hotspots to identify expensive + functions. You'll then replace scalar distribution generation with the OpenRNG vector API from Arm + Performance Libraries and profile the updated build. Finally, you'll run a microbenchmark across + eight input sizes and compare the baseline and accelerated timings. + faqs: + - question: Which package manager should I use during setup if I'm using Ubuntu or Debian? + answer: >- + Replace `dnf` with `apt` on Ubuntu or Debian. + - question: What result should I expect when I run the baseline example? + answer: >- + You'll see the number of generated points, the count within a rectangular window, and + the shortest distance from the origin across the full distribution. You generate the points + by adding Gaussian and uniform distributions. Use this run to establish baseline behavior + before profiling and acceleration. + - question: What should I look for in the Arm Performix Code Hotspots report? + answer: >- + Focus on `generateDistribution`, the primary hotspot in the example flame graph. You'll see + more time spent in Gaussian sampling with `std::normal_distribution` than in uniform sampling. + Prioritize random-number generation rather than `min_length`, which contributes negligible runtime + in the example. + - question: Which OpenRNG API should I use to speed up distribution generation? + answer: >- + Use the Vector Statistical Library (VSL) API with a stream object to generate values in + bulk. This vector approach reduces per-call overhead for random number generation. + - question: What output indicates the microbenchmark sweep ran correctly? + answer: >- + The sweep times the isolated distribution generator across eight sizes from 2^8 to 2^15 + and reports elapsed time in microseconds. Run both baseline and accelerated builds, then compare + timings across sizes to evaluate scaling and improvements. +# END generated_summary_faq + author: Kieran Hejmadi -generate_summary_faq: true +generate_summary_faq: false rerun_summary: false rerun_faqs: false @@ -56,4 +103,3 @@ weight: 1 # _index.md always has weight of 1 to order corr layout: "learningpathall" # All files under learning paths have this same wrapper learning_path_main_page: "yes" # This should be surfaced when looking for related content. Only set for _index.md of learning path content. --- - diff --git a/content/stats/content_over_time.png b/content/stats/content_over_time.png index ec677601f3..901983ee2b 100644 Binary files a/content/stats/content_over_time.png and b/content/stats/content_over_time.png differ diff --git a/data/stats_weekly_data.yml b/data/stats_weekly_data.yml index ff4f640e33..e6493f8c41 100644 --- a/data/stats_weekly_data.yml +++ b/data/stats_weekly_data.yml @@ -10098,3 +10098,195 @@ stars: 152 forks: 330 open_prs: 12 +- date: '2026-10-05' + content: + total: 591 + install-guides: 104 + automotive: 13 + cross-platform: 55 + embedded-and-microcontrollers: 88 + laptops-and-desktops: 91 + mobile-graphics-and-gaming: 94 + servers-and-cloud-computing: 281 + individual_authors: + adnan-alsinan: 3 + ajeet-singh-raina: 1 + akash-malik: 2 + alaaeddine-chakroun: 2 + albin-bernhardsson: 2 + alejandro-martinez-vicente: 1 + alex-su: 1 + alexandre-romana: 1 + alexandros-lamprineas: 1 + andrew-choi: 2 + andrew-kilroy: 1 + andrew-pickard: 1 + ann-cheng: 1 + anna-mayne: 1 + annie-tallund: 17 + anton-antonov: 3 + anupras-mohapatra: 3 + arnaud-de-grandmaison: 7 + arnaud-grasset: 1 + aryan-bhusari: 1 + ash-naik: 1 + asher-dobrescu: 1 + asier-arranz: 1 + aude-vuilliomenet: 2 + ayoub-bourjilat: 2 + barbara-corriero: 1 + basma-el-gaabouri: 1 + ben-clark: 4 + bhanu-arya: 1 + bolt-liu: 2 + brenda-strech: 1 + brendan-long: 2 + bright-edudzi-gershon-kordorwu: 1 + chaodong-gong: 1 + chen-zhang: 1 + chenying-kuo: 1 + chris-moroney: 1 + christophe-favergeon: 1 + christopher-seidl: 10 + claudio-martino: 1 + cyril-rohr: 1 + damien-dooley: 1 + daniel-gubay: 1 + daniel-nguyen: 2 + daniel-schleicher: 1 + dave-neary: 1 + david-spickett: 2 + david-wong: 1 + dawid-borycki: 35 + diego-russo: 2 + dominica-abena-o.-amanfo: 2 + doug-anson: 7 + elad-gross: 1 + elham-harirpoush: 2 + felix-johnny-thomasmathibalan: 1 + fidel-makatia-omusilibwa: 3 + florent-lebeau: 5 + frédéric--lefred--descamps: 2 + gabriel-peterson: 6 + gary-carroll: 1 + gayathri-narayana-yegna-narayanan: 2 + georgios-mermigkis: 1 + geremy-cohen: 7 + gian-marco-iodice: 1 + graham-woodward: 1 + habib-ogunbanwo: 3 + han-yin: 1 + henry-wang: 1 + iago-calvo-lista: 1 + jai-adam-schrem: 1 + jaidev-singh-chadha: 1 + james-whitaker: 1 + jason-andrews: 120 + jason-zhu: 1 + jeff-young: 1 + jett-zhou: 2 + jiaming-guo: 1 + joana-cruz: 2 + joe-stech: 8 + johanna-skinnider: 2 + john-o'hara: 1 + johnny-nunez: 2 + jose-emilio-munoz-lopez: 1 + joshua-marshall-law: 5 + julie-gaskin: 8 + julien-jayat: 1 + julien-simon: 2 + julio-suarez: 6 + jun-he: 1 + kasper-mecklenburg: 1 + kavya-sri-chennoju: 2 + kelsey-steele: 1 + ken-zhang: 1 + ker-liu: 1 + kiel-friedt: 1 + kieran-hejmadi: 20 + koki-mitsunami: 3 + konstantinos-margaritis: 8 + kristof-beyls: 1 + kwashie-andoh: 7 + leandro-nunes: 1 + marek-michałowski: 1 + mark-thurman: 1 + martin-ma: 1 + masoud-koleini: 2 + mathias-brossard: 1 + matt-cossins: 10 + michael-hall: 6 + mohamad-najem: 1 + mohamed-ismail: 1 + na-li: 2 + nader-zouaoui: 2 + neethu-elizabeth-simon: 1 + nii-osae-osae-dade: 2 + nikhil-gupta: 3 + nina-drozd: 2 + nobel-chowdary-mandepudi: 6 + odin-shen: 27 + oliver-grainge: 1 + owen-wu: 2 + pareena-verma: 103 + parichay-das: 1 + paschalis-mpeis: 1 + paschalis-mpeis,-jonathan-davies: 2 + paul-howard: 3 + peter-harris: 1 + phalani-paladugu: 1 + pinru-wang: 1 + powen-yang: 2 + pranay-bakre: 5 + preema-merlin-dsouza: 1 + prince-agyeman: 1 + przemyslaw-wirkus: 2 + qixiang-xu: 3 + rachel-belachew: 2 + rani-chowdary-mandepudi: 3 + raymond-lo: 1 + richard-burton: 2 + roberto-lopez-mendez: 2 + ronan-synnott: 38 + rui-chang: 1 + ruifeng-wang: 1 + sabika-tasneem: 1 + sagar-surendran: 1 + sayandip-pal: 2 + shuheng-deng: 1 + steve-jordahl: 1 + steve-suzuki: 1 + sue-wu: 2 + thirdai: 1 + tianyu-li: 2 + tinkerblox: 1 + tirui-wu: 1 + tom-dunkle: 1 + tom-pilar: 1 + tomas-agustin-gonzalez-orlando: 2 + tony-morri: 2 + tyler-mullenbach: 1 + uma-ramalingam: 1 + usamah-zaheer: 3 + varun-chari: 4 + victor-do-nascimento: 1 + waheed-brown: 4 + willen-yang: 1 + william-liang: 1 + william-watson: 1 + yahya-abouelseoud: 1 + ying-yu: 4 + yiyang-fan: 1 + zach-lasiuk: 2 + zbynek-roubalik: 1 + zenon-zhilong-xiu: 4 + zhengjun-xing: 2 + éliás-bálint: 1 + contributions: + internal: 539 + external: 160 + github: + stars: 154 + forks: 334 + open_prs: 13