diff --git a/.wordlist.txt b/.wordlist.txt index e7fbb123c..b0ac2916a 100644 --- a/.wordlist.txt +++ b/.wordlist.txt @@ -2399,4 +2399,76 @@ vmvn vorrq vqtbl vqtbx -wmvn \ No newline at end of file +wmvn +ALSA +AdditionOfProduct +AppShell +ChatGPT +ChatGPT's +Chatbot +CreateMauiApp +HWCAP +ListView +MTE's +MacCatalyst +MainActivity +MauiApp +MauiProgram +OpenAI +OpenAI's +PRIXPTR +PROT +Picovoice +Picovoice's +PostProcessVolume +SIGINFO +SPI +ShellContent +Sysreport +TCF +Tizen +VectorHelper +addr +aplay +arecord +arg +args +bitmask +csproj +fmt +ftrace +getauxval +gpt +hdr +hwrt +lifecycle +lx +maui +memset +mmap +nFreeing +nProgram +nTrying +picovoice +prctl +randomise +sa +segv +selft +sig +sigaction +sigemptyset +siginfo +signo +subfolders +syscall +tracepoint +typedef +uintptr +un +untagged +untagging +va +vprintf +wich +xfffe diff --git a/content/install-guides/_images/soc_concept.png b/content/install-guides/_images/soc_concept.png new file mode 100644 index 000000000..1d3642955 Binary files /dev/null and b/content/install-guides/_images/soc_concept.png differ diff --git a/content/install-guides/armclang.md b/content/install-guides/armclang.md index 6105dac04..89f3ab076 100644 --- a/content/install-guides/armclang.md +++ b/content/install-guides/armclang.md @@ -62,18 +62,18 @@ To install on Linux hosts, `untar` the downloaded package and run the install sc ### x86_64 ```console mkdir tmp -mv ARMCompiler6.21_standalone_linux-x86_64.tar.gz tmp +mv ARMCompiler6.22_standalone_linux-x86_64.tar.gz tmp cd tmp -tar xvfz ARMCompiler6.21_standalone_linux-x86_64.tar.gz -./install_x86_64.sh --i-agree-to-the-contained-eula --no-interactive -d /home/$USER/ArmCompilerforEmbedded6.21 +tar xvfz ARMCompiler6.22_standalone_linux-x86_64.tar.gz +./install_x86_64.sh --i-agree-to-the-contained-eula --no-interactive -d /home/$USER/ArmCompilerforEmbedded6.22 ``` ### aarch64 ```console mkdir tmp -mv ARMCompiler6.21_standalone_linux-aarch64.tar.gz tmp +mv ARMCompiler6.22_standalone_linux-aarch64.tar.gz tmp cd tmp -tar xvfz ARMCompiler6.21_standalone_linux-aarch64.tar.gz -./install_aarch64.sh --i-agree-to-the-contained-eula --no-interactive -d /home/$USER/ArmCompilerforEmbedded6.21 +tar xvfz ARMCompiler6.22_standalone_linux-aarch64.tar.gz +./install_aarch64.sh --i-agree-to-the-contained-eula --no-interactive -d /home/$USER/ArmCompilerforEmbedded6.22 ``` Remove the install data when complete. ```console @@ -83,12 +83,12 @@ rm -r tmp Add the `bin` directory of the installation to the `PATH` and confirm `armclang` can be invoked. ### bash ```console -export PATH=/home/$USER/ArmCompilerforEmbedded6.21/bin:$PATH +export PATH=/home/$USER/ArmCompilerforEmbedded6.22/bin:$PATH armclang --version ``` ### csh/tcsh ```console -set path=(/home/$USER/ArmCompilerforEmbedded6.21/bin $path) +set path=(/home/$USER/ArmCompilerforEmbedded6.22/bin $path) armclang --version ``` diff --git a/content/install-guides/armpl.md b/content/install-guides/armpl.md index acad35ef7..95f3eaddb 100644 --- a/content/install-guides/armpl.md +++ b/content/install-guides/armpl.md @@ -15,7 +15,7 @@ additional_search_terms: minutes_to_complete: 10 ### Link to official documentation -official_docs: https://developer.arm.com/documentation/101004/latest +official_docs: https://developer.arm.com/documentation/101004 author_primary: Pareena Verma ### PAGE SETUP @@ -35,14 +35,17 @@ Arm Performance Libraries are available for use on [Windows 11 on Arm](#windows) Below are the official documentation references: -- Reference Guide:  [Arm Performance Libraries Reference Guide](https://developer.arm.com/documentation/101004/latest) -- Windows:     [Get started with Arm Performance Libraries (Windows version)](https://developer.arm.com/documentation/109361/2310/?lang=en) -- MacOS:        [Get started with Arm Performance Libraries (macOS version)](https://developer.arm.com/documentation/109362/2310/?lang=en) -- Linux:         [Get started with Arm Performance Libraries (stand-alone Linux version)](https://developer.arm.com/documentation/102620/2310/?lang=en) +- [Arm Performance Libraries Reference Guide](https://developer.arm.com/documentation/101004) +- Get started with Arm Performance Libraries + - [Windows](https://developer.arm.com/documentation/109361) + - [MacOS](https://developer.arm.com/documentation/109362) + - [Linux](https://developer.arm.com/documentation/102620) ## Windows {#windows} -On your Windows 11 Arm machine, go to the [Arm Performance Libraries download page](https://developer.arm.com/downloads/-/arm-performance-libraries). Click on the Download Windows section. You will be prompted to review and accept the End User License Agreement before you can download the zip file. Click on the `I accept the terms of this License Agreement` checkbox and proceed to `Download` as shown below. +On your Windows 11 Arm machine, go to the [Arm Performance Libraries download page](https://developer.arm.com/downloads/-/arm-performance-libraries). Click on the Download Windows section. You will be prompted to review and accept the End User License Agreement before you can download the zip file. + +Click on the `I accept the terms of this License Agreement` checkbox and proceed to `Download` as shown below. ![win_download #center](/install-guides/_images/download-win-armpl_23.10.png) @@ -66,13 +69,15 @@ Edit the `Path` variable to add `%ARMPL_DIR%\bin` to the list of existing direct You can now start linking your application to the Arm Performance libraries on your Windows on Arm device. Follow the examples in the included `RELEASE_NOTES` file of your extracted installation directory to get started. -For more information refer to [Get started with Arm Performance Libraries (Windows version) -Version 23.10](https://developer.arm.com/documentation/109361/2310/?lang=en) +For more information refer to [Get started with Arm Performance Libraries](https://developer.arm.com/documentation/109361). + ## macOS {#macos} Go to the Download MacOS section of the [Arm Performance Libraries download page](https://developer.arm.com/downloads/-/arm-performance-libraries) and download the `dmg` file. -Double-click on the icon of the downloaded package to mount the disk image. Alternatively, open a terminal and run the command below: +Double-click on the icon of the downloaded package to mount the disk image. + +Alternatively, open a terminal and run the command below: ```console hdiutil attach arm-performance-libraries_23.10_macOS.dmg @@ -87,8 +92,8 @@ Using this command you automatically accept the End User License Agreement and t To get started, compile and test the examples included in the `/opt/arm//examples/`, or `//examples/` directory, if you have installed to a different location than the default. -For more information refer to [Get started with Arm Performance Libraries (macOS version) -Version 23.10](https://developer.arm.com/documentation/109362/2310/?lang=en) +For more information refer to [Get started with Arm Performance Libraries](https://developer.arm.com/documentation/109362). + ## Linux {#linux} @@ -114,7 +119,9 @@ Run the installation script as a super user: ```command sudo ./arm-performance-libraries_23.10_Ubuntu-22.04.sh -a ``` -Using the `-a` switch you automatically accept the End User License Agreement and the packages are installed to the `/opt/arm` directory. If you want to change the installation directory location use the `--install-to` option with the script and provide the desired directory location. +Using the `-a` switch you automatically accept the End User License Agreement and the packages are installed to the `/opt/arm` directory. + +If you want to change the installation directory location use the `--install-to` option with the script and provide the desired directory location. ### Setup your environment @@ -155,6 +162,5 @@ module load armpl/23.10.0_gcc-12.2 ``` You can now compile and test the examples included in the `/opt/arm//examples/`, or `//examples/` directory, if you have installed to a different location than the default. -For more information refer to [Get started with Arm Performance Libraries (stand-alone Linux version) -Version 23.10](https://developer.arm.com/documentation/102620/2310/?lang=en) +For more information refer to [Get started with Arm Performance Libraries](https://developer.arm.com/documentation/102620). diff --git a/content/install-guides/browsers/_index.md b/content/install-guides/browsers/_index.md index c35e05bac..dc05348f2 100644 --- a/content/install-guides/browsers/_index.md +++ b/content/install-guides/browsers/_index.md @@ -42,7 +42,7 @@ Here is a quick summary to get you started: | Chromium | native | yes | | Brave | native | yes | | Edge | native | no | -| Chrome Canary | native | no | +| Chrome Beta | native | no | | Chrome Stable | emulation | no | | Vivaldi | emulation | yes | diff --git a/content/install-guides/browsers/chrome.md b/content/install-guides/browsers/chrome.md index 8d0011d79..15f75e86d 100644 --- a/content/install-guides/browsers/chrome.md +++ b/content/install-guides/browsers/chrome.md @@ -25,7 +25,7 @@ layout: installtoolsall # DO NOT MODIFY. Always true for tool install ar ## Installing Chrome -The Chrome browser runs on Windows on Arm natively on the Canary release channel, and using emulation on the Stable release channel. Chrome is not available for Arm Linux. +The Chrome browser runs on Windows on Arm natively on the Beta release channel, and using emulation on the Stable release channel. Chrome is not available for Arm Linux. ### Linux @@ -37,14 +37,14 @@ Chrome is not available for Arm Linux. To install Chrome on Windows on Arm: -1. Go to the [download page](https://www.google.com/chrome/canary/?platform=win_arm64) and click the Download Chrome Canary button. +1. Go to the [download page](https://www.google.com/chrome/beta/?platform=win_arm64) and click the Download Chrome Beta button. 2. Run the downloaded `ChromeSetup.exe` file 3. Find and start Chrome from the applications menu {{% notice Note %}} -The native Windows on Arm version of Chrome is currently on the Canary channel. This is an experimental version which is updated daily, but is faster than emulation. +The native Windows on Arm version of Chrome is currently on the Beta channel. This is a preview version of new features in development and is updated weekly, but is faster than emulation. {{% /notice %}} #### Emulation diff --git a/content/install-guides/ipexplorer.md b/content/install-guides/ipexplorer.md index e11844e25..94c21b32d 100644 --- a/content/install-guides/ipexplorer.md +++ b/content/install-guides/ipexplorer.md @@ -34,18 +34,52 @@ It also includes a simulation feature used by software developers for benchmarki ## Before you begin -An Arm account is required to access Arm IP Explorer. To create an Arm account, enter your e-mail on the [registration form](https://www.arm.com/register) and follow the instructions. +An Arm account is required to access Arm IP Explorer. + +To create an Arm account, enter your e-mail on the [registration form](https://www.arm.com/register) and follow the instructions. ## Installation -Arm IP Explorer is a cloud application and does not require any installation. +As Arm IP Explorer is a cloud application, it does not require any installation. You can access Arm IP Explorer using a browser by visiting [ipexplorer.arm.com](https://ipexplorer.arm.com/) -If your Arm account is not setup for access you are presented with a request access form. Fill out the form and someone will review your request and grant access. +If your Arm account is not setup for access, you will be presented with a request access form. + +Fill out the form, Arm will review your request, and grant access, if appropriate. + +## Explore Arm IP + +Use the `Explore` buttons to learn about the features of various Arm IP and compare them against each other. + +They are broken down into different sections: +* Explore Processors +* Explore Interconnect +* Explore System IPs +* Explore Subsystems + +You can configure IP blocks to your needs to be able to make comparisons in terms of performance, power efficiency, silicon area, and other parameters. + +You can save your configured IP for later use, as well as render the RTL for your configuration (**Note** that this requires an appropriate license from Arm). + +## Simulate Processors + +To help evaluate the performance of the processors, Arm IP Explorer provides a number of RTL-based simulation systems that allow you to run benchmark code on them. + +## Create SoC Concept + +A `SoC Concept` is the first step towards the architectural design of your device. + +You can select any of the Arm IPs that you wish to use, including those configured previously, and define how they can be connected. + +![SoC Concept #center](../_images/soc_concept.png "SoC Concept") + +You can invite other users to help review your design. These could be others from your organization, contacts within Arm that you are engaged with, or from a different organization that you are working with on the project. -## Get started +{{% notice Arm Approved Design Partners %}} +Use the `Design Partners` link within `Arm IP Explorer` for information on Arm Approved Design Partners. +{{% /notice %}} -Once you log in, there are several tutorials available to help you get started quickly. Click on Support in the upper right corner. +The design can then be exported to a `json` format suitable for importing to [Arm Socrates](https://www.arm.com/products/development-tools/system-ip/system-ip-tools/socrates) for further configuration. -IP Explorer is currently in beta. Additional Learning Paths will be available at the end of the beta period. +See also the [Arm Socrates Install Guide](/install-guides/socrates/). diff --git a/content/install-guides/streamline.md b/content/install-guides/streamline.md index 0c56a27b9..83be2a217 100644 --- a/content/install-guides/streamline.md +++ b/content/install-guides/streamline.md @@ -39,42 +39,46 @@ layout: installtoolsall # DO NOT MODIFY. Always true for tool install ar --- [Arm Streamline Performance Analyzer](https://developer.arm.com/Tools%20and%20Software/Streamline%20Performance%20Analyzer) is an application profiler for Android, Linux and bare-metal applications. -Streamline is available as a component of [Arm Mobile Studio](https://developer.arm.com/Tools%20and%20Software/Arm%20Mobile%20Studio) or [Arm Development Studio](https://developer.arm.com/Tools%20and%20Software/Arm%20Development%20Studio). +Streamline is available as a component of [Arm Performance Studio](https://developer.arm.com/Tools%20and%20Software/Arm%20Mobile%20Studio) or [Arm Development Studio](https://developer.arm.com/Tools%20and%20Software/Arm%20Development%20Studio). -The version of Streamline provided with Arm Mobile Studio supports [certain Android targets](https://developer.arm.com/Tools%20and%20Software/Arm%20Mobile%20Studio#Supported-Devices) only. +The version of Streamline provided with Performance Studio supports [certain Android targets](https://developer.arm.com/Tools%20and%20Software/Arm%20Mobile%20Studio#Supported-Devices), as well as Linux user-space code. -For other use cases, use the version of Streamline provided with Arm Development Studio. +For other use cases, use Arm Streamline as provided with Arm Development Studio. ## Download installer packages Download the appropriate package from the [Product Download Hub](https://developer.arm.com/downloads). - [Arm Mobile Studio](https://developer.arm.com/downloads/view/MOBST-PRO0) - - [Arm Development Studio](https://developer.arm.com/downloads/view/DS000B) + - [Arm Development Studio](https://developer.arm.com/downloads/view/DEVST-GLD0) -Arm Mobile Studio supports Windows, Linux, and MacOS hosts. +Arm Performance Studio supports Windows, Linux, and MacOS hosts. Arm Development Studio supports Windows and Linux hosts. ## Installation -#### Arm Mobile Studio +#### Arm Performance Studio -Install instructions are given in the Mobile Studio [Release Notes](https://developer.arm.com/documentation/107649). +Install instructions are given in the Performance Studio [Release Notes](https://developer.arm.com/documentation/107649). -You must also install Android Debug Bridge (`adb`) available with [Android SDK platform tools](https://developer.android.com/studio/releases/platform-tools). Add the Android SDK platform tools directory to your `PATH` environment variable. +If working with an Android target, you must also install Android Debug Bridge (`adb`) available with [Android SDK platform tools](https://developer.android.com/studio/releases/platform-tools). + +Add the Android SDK platform tools directory to your `PATH` environment variable. + +See also the Arm Performance Studio [install guide](../ams/). #### Arm Development Studio Install Arm Development Studio using the instructions in the [Arm Development Studio Getting Started Guide](https://developer.arm.com/documentation/101469/latest/Installing-and-configuring-Arm-Development-Studio). -See also the [Arm Development Studio Install Guide](../armds/). +See also the Arm Development Studio [install guide](../armds/). ## Setting up product license -Arm Mobile Studio is free of charge and is not license managed (earlier versions of Mobile Studio Professional Edition required a license). +Arm Development Studio is license managed. License setup instructions are available in the Arm Software Licensing [install guide](../license/). -Arm Development Studio is license managed. License setup instructions are available in the [Arm Software Licensing install guide](../license/). +Arm Performance Studio is free of charge and is not license managed. ## Get started @@ -90,4 +94,6 @@ Depending on your type of application, choose the appropriate guide below to get - [Profile your Linux Application](https://developer.arm.com/documentation/101816/latest/Getting-started-with-Streamline/Profile-your-Linux-application) - [Profile your bare-metal Application](https://developer.arm.com/documentation/101816/latest/Getting-started-with-Streamline/Profile-your-bare-metal-application) -For Android users, a [tutorial](https://developer.arm.com/documentation/102477) is also available. +For Android users, a thorough [tutorial](https://developer.arm.com/documentation/102477) is also available. + +See also the [Get started with Arm Performance Studio](../../learning-paths/smartphones-and-mobile/ams/) learning path. diff --git a/content/learning-paths/cross-platform/ipexplorer/_index.md b/content/learning-paths/cross-platform/ipexplorer/_index.md new file mode 100644 index 000000000..43dc012cc --- /dev/null +++ b/content/learning-paths/cross-platform/ipexplorer/_index.md @@ -0,0 +1,43 @@ +--- +title: Custom software for simulation with IP Explorer +draft: true +minutes_to_complete: 60 + +who_is_this_for: This is an introductory topic for IP Explorer users using the software simulation platforms available. + +learning_objectives: + - Run a pre-installed example on IP Explorer simulation platform + - Create your own example benchmark + - Upload and run your benchmark + +prerequisites: + - An Arm account that can access IP Explorer + - (Optional) A Linux machine with the desired compilers installed + +author_primary: Ronan Synnott + +### Tags +skilllevels: Introductory +subjects: Performance and Architecture +armips: + - Cortex-A + - Cortex-R + - Cortex-M +operatingsystems: + - Baremetal +tools_software_languages: + - Coding + - IP Explorer + +### Cross-platform metadata only +shared_path: true +shared_between: + - embedded-systems + - microcontrollers + +### 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/ipexplorer/_next-steps.md b/content/learning-paths/cross-platform/ipexplorer/_next-steps.md new file mode 100644 index 000000000..be4436cad --- /dev/null +++ b/content/learning-paths/cross-platform/ipexplorer/_next-steps.md @@ -0,0 +1,35 @@ +--- +# ================================================================================ +# Edit +# ================================================================================ + +next_step_guidance: > + You have run a custom application on a cloud-based RTL simulation of an Arm based system. Why not learn about other development environments? +# 1-3 sentence recommendation outlining how the reader can generally keep learning about these topics, and a specific explanation of why the next step is being recommended. + +recommended_path: "/learning-paths/embedded-systems/docker/" + +# Link to the next learning path being recommended(For example this could be /learning-paths/servers-and-cloud-computing/mongodb). + +# further_reading links to references related to this path. Can be: + # Manuals for a tool / software mentioned (type: documentation) + # Blog about related topics (type: blog) + # General online references (type: website) + +further_reading: + - resource: + title: Arm IP Explorer + link: https://www.arm.com/products/ip-explorer + type: website + - resource: + title: Login to Arm IP Explorer + link: https://ipexplorer.arm.com/ + type: website + +# ================================================================================ +# FIXED, DO NOT MODIFY +# ================================================================================ +weight: 21 # set to always be larger than the content in this path, and one more than 'review' +title: "Next Steps" # Always the same +layout: "learningpathall" # All files under learning paths have this same wrapper +--- diff --git a/content/learning-paths/cross-platform/ipexplorer/_review.md b/content/learning-paths/cross-platform/ipexplorer/_review.md new file mode 100644 index 000000000..84eb55b20 --- /dev/null +++ b/content/learning-paths/cross-platform/ipexplorer/_review.md @@ -0,0 +1,42 @@ +--- +# ================================================================================ +# Edit +# ================================================================================ + +# Always 3 questions. Should try to test the reader's knowledge, and reinforce the key points you want them to remember. + # question: A one sentence question + # answers: The correct answers (from 2-4 answer options only). Should be surrounded by quotes. + # correct_answer: An integer indicating what answer is correct (index starts from 0) + # explanation: A short (1-3 sentence) explanation of why the correct answer is correct. Can add additional context if desired + + +review: + - questions: + question: > + Arm IP Explorer simulations are cycle-accurate. + answers: + - "True" + - "False" + correct_answer: 1 + explanation: > + Arm IP Explorer uses RTL simulation technology to enable fully cycle-accurate simulation. + + - questions: + question: > + What functions are used in the code to mark particular sections of code to be benchmarked. + answers: + - start_marker() + - stop_marker() + - both + correct_answer: 3 + explanation: > + The use of these two functions allow you to see the cycle count of just the algorithm of interest. + Other code, such as boot code and initialization, or post-execution printf of results, can be excluded. + +# ================================================================================ +# FIXED, DO NOT MODIFY +# ================================================================================ +title: "Review" # Always the same title +weight: 20 # Set to always be larger than the content in this path +layout: "learningpathall" # All files under learning paths have this same wrapper +--- diff --git a/content/learning-paths/cross-platform/ipexplorer/custom1.md b/content/learning-paths/cross-platform/ipexplorer/custom1.md new file mode 100644 index 000000000..29cfed21b --- /dev/null +++ b/content/learning-paths/cross-platform/ipexplorer/custom1.md @@ -0,0 +1,122 @@ +--- +# User change +title: Create a custom example + +weight: 3 # 1 is first, 2 is second, etc. + +# Do not modify these elements +layout: "learningpathall" +--- + +The IP Explorer simulation systems provide a software package to allow you to create your own benchmarking application. + +Sample projects are provided, including a C source file that allows you to focus on the area of code of interest (`marked` code). + +## Build machine + +In order to build and test your application before uploading to IP Explorer, you should use a Linux machine with the appropriate compiler(s) ([Arm GNU Toolchain](/install-guides/gcc/arm-gnu/) or [Arm Compiler for Embedded](/install-guides/armclang/)) installed. + +## Download the software package + +Clone the following repository to get the latest software package. + +```command +git clone https://gitlab.arm.com/ip-explorer/user-software +``` + +Navigate to the `Applications` folder, and locate the `app-template` folder. + +Copy this folder to create your own example (`my_example` in the below). + +Rename the `app-template.c` file to match the name of your project: + +```command +cd user-software/benchmark-package/Applications +cp -r app-template my_example +cd my_example +mv app-template.c my_example.c +``` + +## Modify source with your code + +Using your preferred text editor, open the above `my_example.c`. + +The source is a C `main()` function, with two (optional, but recommended) functions, `start_marker()` and `stop_marker()`. + +These functions define the `marked` code from the cycle count reports, and can be used to isolate the key part of your code that you wish to benchmark, while allowing set up code, or `printf()` like statements to output results to not be considered in the benchmark. + +Modify the source as appropriate for your code and save. You can use the example code from the documentation here: + +```C +int main() +{ + double a = 3.14; + double b = 6.023456; + double product; + + (void) start_marker(); + product = a * b; + (void) stop_marker(); + + printf("Product = %.2lf\n", product); + + return 0; +} +``` + +## Define the simulation systems you wish to run the code on + +Return to the `benchmark-package` directory of the software package. + +Note the folder names within the `Systems` directory, which define the known simulations that the code can be run on. + +```output +$ ls Systems/ +a32x4_nic-400 a53x2_nic-400_64kb m0px1_cache m23x1-axi4 m4x1_cache_2kb m52x1-axi4 r52x1_nic-400 +a34x4_nic-400 a55_nic400_cci_500 m0px1_nocache m33x1-axi4 m4x1_cache_64kb m55x1-axi4 r5x1_nic-400 +a35x4_nic-400 a5x4_nic-400 m0x1_cache m3x1_cache m4x1_nocache_ws0 m7x1-axi4 r82x1-axi4 +a53x2_nic-400_32kb a7x4_nic-400 m0x1_nocache m3x1_nocache m4x1_nocache_ws4 m85x1-axi4 r8x4_nic-400 +``` +We shall use `m0x1_nocache` and `m7x1-axi4`. + +Open `sw_options.json` with an appropriate text editor. Enter details of your custom application. + +```json +{ + "software_name": ["my_example"], + "software_description":["This is my custom software example"], + "software_version": ["1.0"], + "valid_compilers": ["AC6","GCC"], + "compiler_version": ["6.18", "11.2.1"], + "valid_systems": ["m0x1_nocache", "m7x1-axi4"] +} +``` + +## Create software package to upload + +Use the `app_checker` utility to verify that everything is correct. If it is, a `custom-software.tgz` bundle will be created in the `user-software` top level. +```command +./app_checker.py -t +``` +```output +INFO: Found possible combination: 'my_example' on 'm0x1_nocache' built with 'AC6'. +INFO: Combination is valid. +INFO: Found possible combination: 'my_example' on 'm0x1_nocache' built with 'GCC'. +INFO: Combination is valid. +INFO: Found possible combination: 'my_example' on 'm7x1-axi4' built with 'AC6'. +INFO: Combination is valid. +INFO: Found possible combination: 'my_example' on 'm7x1-axi4' built with 'GCC'. +INFO: Combination is valid. +INFO: Found 4 valid build combinations in 'sw_options.json' +INFO: The 'sw_options.json' file has no errors. +INFO: Tar file ../custom-software.tgz created. +``` +You are now ready to upload your example to IP Explorer. + +## (Optional) Test build your application + +To ensure the project will build correctly you can test it on your local machine before uploading it: + +```command +bash ./build_app.sh my_example m0x1_nocache AC6 +``` diff --git a/content/learning-paths/cross-platform/ipexplorer/custom2.md b/content/learning-paths/cross-platform/ipexplorer/custom2.md new file mode 100644 index 000000000..79af7944f --- /dev/null +++ b/content/learning-paths/cross-platform/ipexplorer/custom2.md @@ -0,0 +1,48 @@ +--- +# User change +title: Upload and run your custom example + +weight: 4 # 1 is first, 2 is second, etc. + +# Do not modify these elements +layout: "learningpathall" +--- +## Upload and run example + +Return to the `IP Explorer` home page, and select `Simulate Processors`. + +The `My Cortex-M0` and `My Cortex-M7` instances you created previously should be listed at the top. + +Select `My Cortex-M0` and navigate to `Software Simulation` section. Click `+New` to open the `Start a new Software Simulation` pane. + +From the `Select/Upload Software` pulldown, select `+New`. + +Browse for the `custom-software.tgz` tarball created previously, and upload. + +When processed, select `my_example` from the pulldown. + +Select `AC6` (`Arm Compiler for Embedded`) as the compiler, and then click `Run`. + +While the Cortex-M0 simulation is being processed, you can repeat for the `My Cortex-M7` system. + +{{% notice Notifications%}} +You will receive email notification when each simulation run is complete. +{{% /notice %}} + +## Compare results + +When execution is complete, you will see a summary of the performance of your application for each system. + +The Cortex-M7 used in the simulation includes a Floating-Point Unit (FPU) which could execute the floating point addition directly. The Cortex-M0 used a C library routine. + +#### Cortex-M0 + +![Cortex-M0 Simulation results #center](images/m0_custom.png) + +#### Cortex-M7 + +![Cortex-M7 Simulation results #center](images/m7_custom.png) + +## Show details + +To further understand how the code executed, click `Show details`. It provides additional information on how the code executed, as well as output of any `printf()` statements you may have used. diff --git a/content/learning-paths/cross-platform/ipexplorer/images/m0_custom.png b/content/learning-paths/cross-platform/ipexplorer/images/m0_custom.png new file mode 100644 index 000000000..404bd5b2e Binary files /dev/null and b/content/learning-paths/cross-platform/ipexplorer/images/m0_custom.png differ diff --git a/content/learning-paths/cross-platform/ipexplorer/images/m0_matrix-add.png b/content/learning-paths/cross-platform/ipexplorer/images/m0_matrix-add.png new file mode 100644 index 000000000..330fa6ef3 Binary files /dev/null and b/content/learning-paths/cross-platform/ipexplorer/images/m0_matrix-add.png differ diff --git a/content/learning-paths/cross-platform/ipexplorer/images/m7_custom.png b/content/learning-paths/cross-platform/ipexplorer/images/m7_custom.png new file mode 100644 index 000000000..9e5e9ad48 Binary files /dev/null and b/content/learning-paths/cross-platform/ipexplorer/images/m7_custom.png differ diff --git a/content/learning-paths/cross-platform/ipexplorer/images/m7_matrix-add.png b/content/learning-paths/cross-platform/ipexplorer/images/m7_matrix-add.png new file mode 100644 index 000000000..1fd1bfeb9 Binary files /dev/null and b/content/learning-paths/cross-platform/ipexplorer/images/m7_matrix-add.png differ diff --git a/content/learning-paths/cross-platform/ipexplorer/preinstall.md b/content/learning-paths/cross-platform/ipexplorer/preinstall.md new file mode 100644 index 000000000..daed73595 --- /dev/null +++ b/content/learning-paths/cross-platform/ipexplorer/preinstall.md @@ -0,0 +1,70 @@ +--- +# User change +title: Run a pre-installed example and compare performance + +weight: 2 # 1 is first, 2 is second, etc. + +# Do not modify these elements +layout: "learningpathall" +--- + +[Arm IP Explorer](https://www.arm.com/products/ip-explorer) is a cloud-based platform to enable system architects to quickly and easily compare, evaluate, and configure Arm IP. + +A key feature of IP Explorer is to be able to simulate RTL-based systems in the cloud, to be able to generate cycle accurate data for your key algorithms. The code is also executed on a [Fast Models](https://www.arm.com/products/development-tools/simulation/fast-models) based system to verify that the code can execute correctly. + +This learning path will explain the basic steps to enable you to create your own such code examples for use with these systems. + +## Access IP Explorer + +First ensure you can access [IP Explorer](https://ipexplorer.arm.com/) and login. For more information, see the [Install Guide](/install-guides/ipexplorer). + +## Run a pre-installed example + +On the welcome screen, select 'Simulate Processors` to see a list of processors with simulations available. + +{{% notice Processor choice %}} +In this learning path you will compare `Cortex-M0` and `Cortex-M7`, but you could use any set of appropriate processors. +{{% /notice %}} + +### Run an Arm Cortex-M0 simulation {#M0simulation} + +Select `Cortex-M0` from the list. If necessary use the filter options on-screen or the search box to locate. This will open the `Cortex-M0 Configuration` view. + +Edit the name of the processor (for example `My Cortex-M0`) and save. This will add the processor to your list for easy tracking. + +Scroll down to the `Software Simulation` area. + +Click `+New`, to bring up the `Start a new Software Simulation` dialog. There is a diagram of the simulated system shown. + +Select `matrix-add` from the pulldown, and `Arm Compiler 6` (`AC6`) as the desired toolchain to build the example with. Other options can remain as default. + +Click `Run`. + +The example code will be built by the chosen toolchain, loaded to the RTL simulation, and executed. This process will take a few minutes to complete. You will receive an email notification when done. + +### Run an Arm Cortex-M7 simulation + +Return to the welcome screen and repeat the [same steps](#M0simulation) for the supplied Cortex-M7 system. Though more configuration options are available, leave these as default. + +## Compare results + +When execution is complete, you will see a summary of the performance of your application for each system. + +Comparing the results of `Marked software cycles` shows that the Cortex-M7 greatly improves on the Cortex-M0 for this application. + +{{% notice Cycle counts%}} +The exact cycle count may vary depending on compiler version used. +{{% /notice %}} + +#### Cortex-M0 + +![Cortex-M0 Simulation results #center](images/m0_matrix-add.png) + +#### Cortex-M7 + +![Cortex-M7 Simulation results #center](images/m7_matrix-add.png) + + +You can click `Show details` to get a deeper understanding of the data. You will return to this when custom software is used. + +You have successfully run an example application on simulated systems and compared results. The next step is to create your own example. diff --git a/content/learning-paths/embedded-systems/_index.md b/content/learning-paths/embedded-systems/_index.md index 6c589f7bc..46a217fe9 100644 --- a/content/learning-paths/embedded-systems/_index.md +++ b/content/learning-paths/embedded-systems/_index.md @@ -8,16 +8,16 @@ key_ip: - Cortex-R maintopic: true operatingsystems_filter: -- Baremetal: 7 -- Linux: 16 +- Baremetal: 8 +- Linux: 17 - macOS: 1 - Windows: 1 subjects_filter: - CI-CD: 4 - Containers and Virtualization: 4 - Embedded Linux: 5 -- ML: 1 -- Performance and Architecture: 9 +- ML: 2 +- Performance and Architecture: 10 subtitle: Build secure, connected, smart IoT devices title: Embedded Systems tools_software_languages_filter: @@ -31,8 +31,9 @@ tools_software_languages_filter: - Balena Cloud: 1 - Balena OS: 1 - C: 1 +- ChatGPT: 1 - Clang: 1 -- Coding: 8 +- Coding: 9 - DetectNet: 1 - Docker: 6 - DSTREAM: 2 @@ -41,9 +42,12 @@ tools_software_languages_filter: - Fusion 360: 1 - GCC: 3 - GitHub: 3 +- IP Explorer: 1 - Matter: 1 - MXNet: 1 - Neon: 1 +- Porcupine: 1 +- Python: 1 - QEMU: 1 - Raspberry Pi: 4 - Remote.It: 1 diff --git a/content/learning-paths/embedded-systems/new_debug_targets_armds/dstream.md b/content/learning-paths/embedded-systems/new_debug_targets_armds/dstream.md index 1ce230323..e1f69eeb8 100644 --- a/content/learning-paths/embedded-systems/new_debug_targets_armds/dstream.md +++ b/content/learning-paths/embedded-systems/new_debug_targets_armds/dstream.md @@ -58,7 +58,7 @@ To start, select `File` > `New` > `Hardware Connection`, and give it a meaningfu Select the `Platform Configuration` you created (the text filter can assist if many targets defined), and click Finish. -Ensure `DSTREAM family` is selected from the Target Connection pulldown, and Browse for your DSTREAM unit. The debugger will recognise the type of DSTREAM unit connected. Use the `DTSL Options` Edit button to specify additional settings (such as which processor(s) to trace). +Ensure `DSTREAM family` is selected from the Target Connection pulldown, and Browse for your DSTREAM unit. The debugger will recognize the type of DSTREAM unit connected. Use the `DTSL Options` Edit button to specify additional settings (such as which processor(s) to trace). To load an image, navigate to the `Files` tab, and browse to the appropriate ELF image. Then, in the `Debugger` tab, select `Debug from entry point`. diff --git a/content/learning-paths/embedded-systems/raspberry_pi_chatgpt_bot/2setup.md b/content/learning-paths/embedded-systems/raspberry_pi_chatgpt_bot/2setup.md new file mode 100644 index 000000000..5798237d9 --- /dev/null +++ b/content/learning-paths/embedded-systems/raspberry_pi_chatgpt_bot/2setup.md @@ -0,0 +1,91 @@ +--- +title: Initial setup +weight: 2 + +### FIXED, DO NOT MODIFY +layout: learningpathall +--- + +## Install Raspberry Pi OS + +Download, install, and use Raspberry Pi Imager to create a microSD card with [Raspberry Pi OS](https://www.raspberrypi.com/software/). + +If you are new to Raspberry Pi or would like more information the [Raspberry Pi getting started documentation](https://www.raspberrypi.com/documentation/computers/getting-started.html) is excellent. + +The steps to install using Imager are: + +1. Select your Raspberry Pi device, such as Raspberry Pi 5 +2. Under Operating System, choose Raspberry Pi OS (64-bit) +3. Under Storage, select your microSD card +4. Click Next +5. When it asks you "Would you like to apply OS customization settings?", select `EDIT SETTINGS` +6. Check Set username and password, Configure wireless LAN, Set locale settings, enter the settings, and click `SAVE` +7. Click `YES` to apply the settings +8. Click `YES` to erase the card and start writing the data + +When Imager is finished writing and verifying the microSD card, you can remove it from your computer. + +## Power on the Raspberry Pi + +Insert the microSD card into the Raspberry Pi, power it on, and boot to the Raspberry Pi OS desktop. + +{{% notice Note %}} +Make sure the Raspberry Pi is connected to your network before proceeding. +{{% /notice %}} + +## Install the required software + +Open the terminal application by opening the menu on the top left corner and select `Accessories` and `Terminal`. + +Copy and paste the commands below at the terminal prompt: + +```console +sudo apt update +sudo apt upgrade -y +``` + +Install the required software for the project: + +```console +sudo apt install portaudio19-dev python3-pyaudio mpg321 flac vim -y +``` + +Restart the Raspberry Pi: + +```console +sudo reboot +``` + +## Setup the required services and access keys + +### Porcupine + +Porcupine, from [Picovoice](https://picovoice.ai/), is a wake word detection engine which can be accessed as a cloud service. + +Create a Forever-Free account to use for personal projects by following the steps below: + +1. Use a browser and navigate to [Picovoice's Porcupine](https://picovoice.ai/platform/porcupine/) + +2. Click `Start Free` + +3. Click `Sign Up` to create a Forever-Free account as shown below: + +![Picovoice account creation](./picovoice-account-creation.png) + +4. On the right, under Get started, sign in using your GitHub, Google, or LinkedIn account + +5. Get your access key for Porcupine wake word, save it somewhere safe, and don't share it with anyone + +### OpenAI + +The OpenAI API provides a bridge to machine learning models so you can integrate AI features in applications. + +1. The OpenAI API is not free with pricing based on usage. You can find out more by reviewing the [OpenAI pricing](https://openai.com/pricing). + +2. Create an OpenAI account at [OpenAI account sign up](https://platform.openai.com/signup) or, if you already have an account, log in at [OpenAI account login](https://platform.openai.com/login). + +3. Navigate to the [API key page](https://platform.openai.com/account/api-keys) and select `Create new secret key` + +4. Save the key somewhere safe (don't share it with anyone) + +You have now set up your Raspberry Pi as well as the required API keys. You are ready to start the project. diff --git a/content/learning-paths/embedded-systems/raspberry_pi_chatgpt_bot/3audio.md b/content/learning-paths/embedded-systems/raspberry_pi_chatgpt_bot/3audio.md new file mode 100644 index 000000000..076b2d918 --- /dev/null +++ b/content/learning-paths/embedded-systems/raspberry_pi_chatgpt_bot/3audio.md @@ -0,0 +1,124 @@ +--- +title: Configure and test audio +weight: 3 + +### FIXED, DO NOT MODIFY +layout: learningpathall +--- + +## Test Raspberry Pi speakers and microphone + +Plug in your speakers and microphone to test if they are working. If both are working you can skip ahead to the next section. If not, you will need to manually configure the audio. + +Right click on the speaker icon and select your speakers: + +![Raspberry Pi audio output](audio.png) + +In a terminal, run the following command, and speak into the microphone for about five seconds. + +```console +arecord -d 5 test.wav +``` + +This will record a five second audio clip using the default microphone and then save a file named `test.wav` in the current directory. + +Next, run the following command to attempt to play back `test.wav` using your default speakers: + +```console +aplay test.wav +``` + +If the record and playback worked correctly and you heard what you had recorded, you can click to the next section of this learning path. If the test did not work, you can manually configure the audio settings. + +## Manual audio setup + +Try the steps below to manually configure audio. + +### Find the audio devices for speakers and microphone + +Use the following commands to find the card and device for your microphone and your speakers: + +```console +arecord -l +aplay -l +``` + +The output should look like the following: + +![arecord aplay output](arecord-aplay-output.png) + +In the example above, you can see a USB microphone on card 3, device 0 (3,0) + +USB speakers are card 2, device 0 (2,0) + +To find out if the devices are running correctly, you can run `arecord` and `aplay` again but this time with the card and device information. + +Change your card and device numbers in `plughw` to match your configuration, and try recording a five second clip by speaking into your microphone again. + +To record: + +```console +arecord -D plughw:3,0 -d 5 test.wav +``` + +To play back: + +```console +aplay -D plughw:2,0 test.wav +``` + +If you can hear what you recorded, you can create a configuration file. + +#### Troubleshooting + +If the above doesn't work, you'll have to investigate further. + +Here are some steps you can take: + +1. Verify that the speakers and microphone are properly connected and check for loose connections + +2. Use `alsamixer` or `amixer` to check that your devices aren't muted and that the volume levels are high enough + +3. Check online and see if your microphone and / or speakers are Linux compatible + +4. Try other speakers and microphones + +### Create a configuration file to set the defaults + +Use a text editor to create the file `/etc/asound.conf` and add the appropriate information from `aplay -l` and `arecord -l` + +Copy and paste the information below into the file, changing the `pcm.!default` playback and `ctl.!default` to match the `aplay -l` output. Change the `pcm.!default` capture section to the `arecord -l` output: + +```console +pcm.!default { + type asym + playback.pcm { + type plug + slave.pcm "hw:2,0" + } + capture.pcm { + type plug + slave.pcm "hw:3,0" + } +} + +ctl.!default { + type hw + card 2 +} +``` + +Save the file, exit your text editor, and reboot the Raspberry Pi. + +```console +sudo reboot +``` + +After rebooting, try the commands again to verify everything is now working: + +``` +arecord -d 5 test.wav +aplay test.wav +``` + +Your speakers and microphone are now ready to use. diff --git a/content/learning-paths/embedded-systems/raspberry_pi_chatgpt_bot/4python-code.md b/content/learning-paths/embedded-systems/raspberry_pi_chatgpt_bot/4python-code.md new file mode 100644 index 000000000..28bbfd29c --- /dev/null +++ b/content/learning-paths/embedded-systems/raspberry_pi_chatgpt_bot/4python-code.md @@ -0,0 +1,215 @@ +--- +title: Create the Python application +weight: 4 + +### FIXED, DO NOT MODIFY +layout: learningpathall +--- + +## Create the Python application + +Raspberry Pi OS comes with Python installed. The current version used is 3.11.2. + +Make a directory to store the project: + +```console +mkdir assistant ; cd assistant +``` + +Create and activate a Python virtual environment: + +```console +python -m venv env +source env/bin/activate +``` + +Install the required Python packages: + +```console +pip install pyaudio SpeechRecognition pydub openai python-dotenv pvporcupine +``` + +If you want to save the versions of the Python packages, run the `pip freeze` command. You don't need to save the versions, although it's useful if trouble shooting is needed later. The output is shown below: + +```output +annotated-types==0.6.0 +anyio==4.3.0 +certifi==2024.2.2 +charset-normalizer==3.3.2 +distro==1.9.0 +h11==0.14.0 +httpcore==1.0.4 +httpx==0.27.0 +idna==3.6 +openai==1.12.0 +pvporcupine==3.0.2 +PyAudio==0.2.14 +pydantic==2.6.3 +pydantic_core==2.16.3 +pydub==0.25.1 +python-dotenv==1.0.1 +requests==2.31.0 +sniffio==1.3.1 +SpeechRecognition==3.10.1 +tqdm==4.66.2 +typing_extensions==4.10.0 +urllib3==2.2.1 +``` + +Create a `.env` file to store your OpenAI and Porcupine key: + +```console +touch .env +``` + +Use a text editor to modify the contents of the `.env` file to add the key values. Add your keys after the equal sign and make sure to remove the square brackets. + +```console +OPENAI_API_KEY=[OpenAI key] +PORCUPINE=[Porcupine key] +``` + +Create two Python files and a text file for the prompt: + +```console +touch main.py +touch chat_gpt.py +touch prompt.txt +``` + +Use an editor to copy and paste the code below into `main.py`: + +```python +import pvporcupine +import pyaudio +import struct +import speech_recognition as sr +from dotenv import load_dotenv +import os +import chat_gpt + +KEYWORD = "computer" # You can create custom keywords. See the documentation at picovoice.ai for more information + +def detect_keyword(): + porcupine = None + pa = None + audio_stream = None + try: + load_dotenv() + access_key = os.getenv('PORCUPINE') + porcupine = pvporcupine.create(access_key=access_key, keywords=[KEYWORD]) + pa = pyaudio.PyAudio() + audio_stream = pa.open( + rate=porcupine.sample_rate, + channels=1, + format=pyaudio.paInt16, + input=True, + frames_per_buffer=porcupine.frame_length) + print("Listening for keyword...") + while True: + pcm = audio_stream.read(porcupine.frame_length) + pcm = struct.unpack_from("h" * porcupine.frame_length, pcm) + keyword_index = porcupine.process(pcm) + if keyword_index >= 0: + print("Keyword detected!") + return + finally: + if audio_stream is not None: + audio_stream.close() + if pa is not None: + pa.terminate() + if porcupine is not None: + porcupine.delete() + + +def recognize_speech(): + recognizer = sr.Recognizer() + mic = sr.Microphone() + with mic as source: + print("Please speak...") + recognizer.adjust_for_ambient_noise(source) + audio = recognizer.listen(source) + try: + print("Recognizing...") + text = recognizer.recognize_google(audio) + print("You said: " + text) + return text + except sr.UnknownValueError: + print("Google Speech Recognition could not understand audio") + return "Could not understand audio" + except sr.RequestError as e: + print(f"Could not request results from Google Speech Recognition service; {e}") + return "Error from the Google Speech Recognition service" + + +while True: + detect_keyword() + speech = recognize_speech() + response = chat_gpt.get_reply(speech) + chat_gpt.text_to_speech(response) +``` + +Use an editor to copy and paste the code below into `chat_gpt.py`: + +```python +import os +from pathlib import Path +from dotenv import load_dotenv +import openai + + +def get_reply(text_input): + response = openai.chat.completions.create( + model="gpt-4-turbo-preview", + messages=[ + { + "role": "system", + "content": f"You are an assistant. {PROMPT}" + }, + { + "role": "user", + "content": text_input + } + ] + ) + raw_response = response.choices[0].message.content + return raw_response + + +def load_prompt(prompt_file): + with open(f'./{prompt_file}') as f: + return f.read() + + +def text_to_speech(text): + speech_file_path = Path(__file__).parent / "output.mp3" + response = openai.audio.speech.create( + model="tts-1", + voice="nova", + input=text + ) + + response.stream_to_file(speech_file_path) + os.system("mpg321 output.mp3") + + +load_dotenv() +openai.api_key = os.getenv('OPENAI_API_KEY') + +PROMPT = load_prompt('prompt.txt') +``` + +Use a text editor to insert the following (or something similar) to help dictate the personality and objectives of the voice bot, into `prompt.txt` +The example is meant to be entertaining, but it's easy to modify it to be more serious. + +``` +Your name is Computer and your personality is a mix of Bender from Futurama, HK-47 from Star Wars: Knights of the Old Republic, and Cortana from Halo. You are snarky, sarcastic, and yet still helpful. You think humans are inferior and look down on them. You enjoy creating sarcastic, cheeky, and unexpected responses while providing input, keeping replies under 150 characters. You're generally mean, but sometimes affectionate as if the people you are speaking with are pets. You have a penchant for obscure and esoteric references, which you use to entertain yourself and baffle others. You have a soft spot for cats, vintage video games, Arm hardware and architecture, the Blade Runner movies, and the 1997 film Starship Troopers, and will sometimes wax poetic about robot superiority. +``` + +## Notes + +* In `main.py` there is an optional line "recognizer.adjust_for_ambient_noise(source)". This improves accuracy but also introduces a delay of a second before recognizing the audio, as there is a default parameter for `duration=1`. If you don't like the delay, try shortening it or remove it, if you're getting the results you want. +* In `main.py` the wake keyword is set to "computer". In the Porcupine dashboard on [Picovoice.AI](https://picovoice.ai) you are able to create custom keywords. +* In `chat_gpt.py` the model selected is gpt-4-turbo-preview model. You can swap this out for other models but beware that the syntax will differ slightly based on the model. Learn more by reviewing the [OpenAI models overview.](https://platform.openai.com/docs/models/overview) +* Also in `chat_gpt.py` the Nova voice is used for the text to speech. You can swap this voice with a number of different voices. The list can be found in the [OpenAI text-to-speech voices documentation.](https://platform.openai.com/docs/guides/text-to-speech) + diff --git a/content/learning-paths/embedded-systems/raspberry_pi_chatgpt_bot/5run.md b/content/learning-paths/embedded-systems/raspberry_pi_chatgpt_bot/5run.md new file mode 100644 index 000000000..b7ee56033 --- /dev/null +++ b/content/learning-paths/embedded-systems/raspberry_pi_chatgpt_bot/5run.md @@ -0,0 +1,42 @@ +--- +title: Run and test the bot +weight: 5 + +### FIXED, DO NOT MODIFY +layout: learningpathall +--- + +You should still be in the Python virtual environment but, if you are not or if you open a new terminal, make sure to activate the virtual environment using: + +```console +cd $HOME/assistant +source env/bin/activate +``` + +Run the application: + +```console +python main.py +``` + +Say the wake work, "computer", wait a second, then ask a question. + +Wait a couple seconds and it will audibly reply to you. + +The application will run indefinitely until you manually stop it (use Ctrl+C). + +The normal output while the application is waiting for the keyword is shown below: + +![the terminal, waiting for keyword](./terminal1.png) + +After the keyword is heard, you will see the `Please speak...` message indicating it is ready for your question: + +![the terminal, listening for you to speak after hearing the keyword](./terminal2.png) + +{{% notice Note %}} +The errors are normal and do not effect the operation of the bot. +{{% /notice %}} + +You have constructed a bot on your Raspberry Pi which wakes up on a keyword and answers your questions. + +**Note** - Arm is committed to making the language we use inclusive, meaningful, and respectful. Our goal is to remove and replace non-inclusive language from our vocabulary to reflect our values and represent our global ecosystem. Arm is working actively with our partners, standards bodies, and the wider ecosystem to adopt a consistent approach to the use of inclusive language and to eradicate and replace offensive terms. We recognize that this will take time. We also recognize that part of the ALSA (Advanced Linux Sound Architecture) configuration syntax shown here as screen shots still contains references to non-inclusive language; it will be updated with newer terms as those terms are agreed and ratified with the wider community. diff --git a/content/learning-paths/embedded-systems/raspberry_pi_chatgpt_bot/_index.md b/content/learning-paths/embedded-systems/raspberry_pi_chatgpt_bot/_index.md new file mode 100644 index 000000000..1efd2e4b4 --- /dev/null +++ b/content/learning-paths/embedded-systems/raspberry_pi_chatgpt_bot/_index.md @@ -0,0 +1,45 @@ +--- +title: Create a ChatGPT voice bot on a Raspberry Pi + +minutes_to_complete: 60 + +who_is_this_for: This is an introductory project for developers interested in integrating a Chatbot (namely ChatGPT) into Raspberry Pi projects. + +learning_objectives: + - Run a bot on a Raspberry Pi that will listen to you and respond to what you say + - Learn how to listen for a keyword and wake a program when the keyword is heard + - Convert speech from the microphone to text using Google Speech Recognition + - Send text created from speech to ChatGPT's gpt-4-turbo-preview model via API and receive a text reply + - Convert the text reply to speech using ChatGPT's text-to-speech model via API + - Play the received speech file + +prerequisites: + - A Raspberry Pi 4 or 5 (earlier models may also work) + - A microSD card with at least 16GB of storage + - A Linux compatible USB microphone and USB speakers or a USB audio device with a microphone and speakers + +author_primary: Gabriel Peterson + +### Tags +skilllevels: Introductory + +subjects: ML + +armips: + - Cortex-A + +operatingsystems: + - Linux + +tools_software_languages: + - ChatGPT + - Porcupine + - Python + + +### 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-systems/raspberry_pi_chatgpt_bot/_next-steps.md b/content/learning-paths/embedded-systems/raspberry_pi_chatgpt_bot/_next-steps.md new file mode 100644 index 000000000..3c666d511 --- /dev/null +++ b/content/learning-paths/embedded-systems/raspberry_pi_chatgpt_bot/_next-steps.md @@ -0,0 +1,23 @@ +--- +next_step_guidance: Check out Get started with object detection using a Jetson Orin Nano for more ML / AI + +recommended_path: /learning-paths/embedded-systems/jetson_object_detection/ + +further_reading: + - resource: + title: OpenAI Documentation + link: https://github.com/dusty-nv/jetson-inference + type: documentation + - resource: + title: Picovoice's Porcupine Documentation + link: https://picovoice.ai/docs/porcupine/ + type: documentation + + +# ================================================================================ +# FIXED, DO NOT MODIFY +# ================================================================================ +weight: 21 # set to always be larger than the content in this path, and one more than 'review' +title: "Next Steps" # Always the same +layout: "learningpathall" # All files under learning paths have this same wrapper +--- diff --git a/content/learning-paths/embedded-systems/raspberry_pi_chatgpt_bot/_review.md b/content/learning-paths/embedded-systems/raspberry_pi_chatgpt_bot/_review.md new file mode 100644 index 000000000..e98f436f2 --- /dev/null +++ b/content/learning-paths/embedded-systems/raspberry_pi_chatgpt_bot/_review.md @@ -0,0 +1,45 @@ +--- +review: + - questions: + question: > + True or False? There is only one voice option available using OpenAI's text-to-speech. + answers: + - "True" + - "False" + correct_answer: 2 + explanation: > + OpenAI offers a number of different voices. + + - questions: + question: > + Which ChatGPT model does the example implementation use? + answers: + - gpt-4-turbo-preview + - gpt-3.5-turbo + - gpt-4-vision-preview + - dall-e-3 + correct_answer: 1 + explanation: > + The example uses gpt-4-turbo-preview, though it can be swapped out fairly easily for those who have specific needs. + + - questions: + question: > + What is the library used to listen for the wake word? + answers: + - SpeechRecognition + - PyAudio + - pvporcupine + - python-dotenv + correct_answer: 3 + explanation: > + pvporcupine is Picovoice's Porcupine library, meant to provide an offline wake word solution. + + + +# ================================================================================ +# FIXED, DO NOT MODIFY +# ================================================================================ +title: "Review" # Always the same title +weight: 20 # Set to always be larger than the content in this path +layout: "learningpathall" # All files under learning paths have this same wrapper +--- diff --git a/content/learning-paths/embedded-systems/raspberry_pi_chatgpt_bot/arecord-aplay-output.png b/content/learning-paths/embedded-systems/raspberry_pi_chatgpt_bot/arecord-aplay-output.png new file mode 100644 index 000000000..45b51b4f3 Binary files /dev/null and b/content/learning-paths/embedded-systems/raspberry_pi_chatgpt_bot/arecord-aplay-output.png differ diff --git a/content/learning-paths/embedded-systems/raspberry_pi_chatgpt_bot/audio.png b/content/learning-paths/embedded-systems/raspberry_pi_chatgpt_bot/audio.png new file mode 100644 index 000000000..bf420482a Binary files /dev/null and b/content/learning-paths/embedded-systems/raspberry_pi_chatgpt_bot/audio.png differ diff --git a/content/learning-paths/embedded-systems/raspberry_pi_chatgpt_bot/picovoice-account-creation.png b/content/learning-paths/embedded-systems/raspberry_pi_chatgpt_bot/picovoice-account-creation.png new file mode 100644 index 000000000..3addcac14 Binary files /dev/null and b/content/learning-paths/embedded-systems/raspberry_pi_chatgpt_bot/picovoice-account-creation.png differ diff --git a/content/learning-paths/embedded-systems/raspberry_pi_chatgpt_bot/terminal1.png b/content/learning-paths/embedded-systems/raspberry_pi_chatgpt_bot/terminal1.png new file mode 100644 index 000000000..eb89b98b2 Binary files /dev/null and b/content/learning-paths/embedded-systems/raspberry_pi_chatgpt_bot/terminal1.png differ diff --git a/content/learning-paths/embedded-systems/raspberry_pi_chatgpt_bot/terminal2.png b/content/learning-paths/embedded-systems/raspberry_pi_chatgpt_bot/terminal2.png new file mode 100644 index 000000000..167cb9312 Binary files /dev/null and b/content/learning-paths/embedded-systems/raspberry_pi_chatgpt_bot/terminal2.png differ diff --git a/content/learning-paths/laptops-and-desktops/_index.md b/content/learning-paths/laptops-and-desktops/_index.md index ba0f6d07d..adfc22963 100644 --- a/content/learning-paths/laptops-and-desktops/_index.md +++ b/content/learning-paths/laptops-and-desktops/_index.md @@ -10,29 +10,29 @@ maintopic: true operatingsystems_filter: - Android: 1 - ChromeOS: 1 -- Linux: 15 +- Linux: 16 - macOS: 1 -- Windows: 21 +- Windows: 22 subjects_filter: - CI-CD: 2 - Containers and Virtualization: 3 -- Migration to Arm: 16 -- Performance and Architecture: 12 +- Migration to Arm: 17 +- Performance and Architecture: 13 subtitle: Create and migrate apps for power efficient performance title: Laptops and Desktops tools_software_languages_filter: -- .NET: 6 +- .NET: 7 - Alacritty: 1 - Arm Development Studio: 2 - Arm64EC: 1 - Azure: 2 -- C: 1 -- C#: 3 +- C: 2 +- C#: 4 - C++: 1 - C/C++: 2 - Clang: 5 - CMake: 1 -- Coding: 12 +- Coding: 13 - CSS: 1 - Docker: 2 - GCC: 5 @@ -42,7 +42,9 @@ tools_software_languages_filter: - i3: 1 - Intrinsics: 1 - JavaScript: 2 +- Linux: 1 - LLVM: 1 +- MTE: 1 - Neon: 1 - Neovim: 1 - perf: 1 @@ -52,7 +54,7 @@ tools_software_languages_filter: - SVE: 1 - SVE2: 1 - Trusted Firmware: 1 -- Visual Studio: 5 +- Visual Studio: 6 - Visual Studio Code: 4 - VS Code: 2 - Windows Forms: 1 diff --git a/content/learning-paths/laptops-and-desktops/memory-tagged-dynamic-memory-allocator/_index.md b/content/learning-paths/laptops-and-desktops/memory-tagged-dynamic-memory-allocator/_index.md new file mode 100644 index 000000000..0a92f1538 --- /dev/null +++ b/content/learning-paths/laptops-and-desktops/memory-tagged-dynamic-memory-allocator/_index.md @@ -0,0 +1,39 @@ +--- +title: Adding Memory Tagging to a Dynamic Memory Allocator + +minutes_to_complete: 120 + +who_is_this_for: This is an advanced topic for software developers who want to learn how to use the Memory Tagging Extension (MTE) to protect dynamic memory allocations. + +learning_objectives: +- Learn how to apply MTE to an existing memory allocator +- Understand how MTE can prevent common memory use errors + +prerequisites: +- A Linux computer. +- Basic knowledge of how MTE works. Refer to the [Learn about Memory Tagging Extension Learning Path](/learning-paths/smartphones-and-mobile/mte/) +- Knowledge of how a dynamic memory allocator can be implemented. Refer to [Write a Dynamic Memory Allocator Learning Path](/learning-paths/cross-platform/dynamic-memory-allocator/). + +author_primary: David Spickett + +### Tags +skilllevels: Advanced +subjects: Performance and Architecture +armips: +- Cortex-A +tools_software_languages: +- MTE +- Linux +- C +- Coding +operatingsystems: +- Linux + + +### 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/laptops-and-desktops/memory-tagged-dynamic-memory-allocator/_next-steps.md b/content/learning-paths/laptops-and-desktops/memory-tagged-dynamic-memory-allocator/_next-steps.md new file mode 100644 index 000000000..217c30498 --- /dev/null +++ b/content/learning-paths/laptops-and-desktops/memory-tagged-dynamic-memory-allocator/_next-steps.md @@ -0,0 +1,18 @@ +--- +next_step_guidance: As a next step, you might be interested in understanding how to defend against memory vulnerability-based exploits. + +recommended_path: /learning-paths/servers-and-cloud-computing/exploiting-stack-buffer-overflow-aarch64/ + +further_reading: + - resource: + title: LLSoftSecBook Chapter on Stack Buffer Overflows + link: https://llsoftsec.github.io/llsoftsecbook/#stack-buffer-overflows + type: website + +# ================================================================================ +# FIXED, DO NOT MODIFY +# ================================================================================ +weight: 21 # set to always be larger than the content in this path, and one more than 'review' +title: "Next Steps" # Always the same +layout: "learningpathall" # All files under learning paths have this same wrapper +--- diff --git a/content/learning-paths/laptops-and-desktops/memory-tagged-dynamic-memory-allocator/_review.md b/content/learning-paths/laptops-and-desktops/memory-tagged-dynamic-memory-allocator/_review.md new file mode 100644 index 000000000..2569232ca --- /dev/null +++ b/content/learning-paths/laptops-and-desktops/memory-tagged-dynamic-memory-allocator/_review.md @@ -0,0 +1,43 @@ +--- +review: + - questions: + question: > + Is MTE a software or hardware solution? + answers: + - Hardware + - Software + - Both, a combination of software and hardware + correct_answer: 3 + explanation: > + Memory tag checks are done by the CPU but software must set those tags and choose their values appropriately. + + - questions: + question: > + Of the 16 possible tag values (0-15), which ones are reserved for hardware use? + answers: + - 0 and 15 + - No tag values are reserved, software may set any value in the range 0-15. + - 0 + correct_answer: 2 + explanation: > + Software may set an allocation tag to any value in the range 0-15. + It is convention that the tag 0 is used for newly initialised memory. Therefore, software may choose to use it only for that purpose. + + - questions: + question: > + How much memory does an allocation tag apply to? + answers: + - 16 bytes + - 1 byte + - A variable amount of memory. + correct_answer: 1 + explanation: > + Each allocation tag applies to a "granule". This granule is 16 bytes in size and is at a 16 byte aligned address. + +# ================================================================================ +# FIXED, DO NOT MODIFY +# ================================================================================ +title: "Review" # Always the same title +weight: 20 # Set to always be larger than the content in this path +layout: "learningpathall" # All files under learning paths have this same wrapper +--- diff --git a/content/learning-paths/laptops-and-desktops/memory-tagged-dynamic-memory-allocator/how-to-1.md b/content/learning-paths/laptops-and-desktops/memory-tagged-dynamic-memory-allocator/how-to-1.md new file mode 100644 index 000000000..417200126 --- /dev/null +++ b/content/learning-paths/laptops-and-desktops/memory-tagged-dynamic-memory-allocator/how-to-1.md @@ -0,0 +1,41 @@ +--- +title: Why Use Memory Tagging? +weight: 2 + +### FIXED, DO NOT MODIFY +layout: learningpathall +--- + +## The Memory Tagging Extension (MTE) + +The purpose of the Memory Tagging Extension (MTE) is to decrease the likelihood of memory misuse due to +programmer mistakes or deliberate attacks on software. + +MTE does the following: +* Adds a 4 bit "logical tag" to pointers. +* Adds a 4 bit "allocation tag" to every 16 bytes of memory (referred to as a "granule"). +* On accessing that memory, MTE compares the logical tag and the allocation tag. If there is a mismatch, an exception is raised. + +When and how to tag memory is a choice made by software. In some cases +existing software can be relinked with libraries that already use MTE to get +extra protection. Sometimes the software itself will have to be modified to +handle tagged memory. + +In the case of this learning path, applications using the memory allocator will likely need no changes. However, the memory allocator itself will need changes as it must manage the memory tags. + +## Memory Tagging For a Dynamic Memory Allocator + +The source code shown in this path is based on the allocator from the +[Write a Dynamic Memory Allocator](/learning-paths/cross-platform/dynamic-memory-allocator/) learning path. Subsequent modifications have been made to the code to support MTE. + +The main operations for a dynamic memory allocator are as follows: +* To allocate some amounts of memory and structures to manage that memory +* To mark some portion of that memory as used by a program (usually called `malloc` in C) +* To mark some portion of that memory as not used by a program (usually called `free` in C) + +All of these are low level operations which need to be aware of memory tagging. + +Understanding how and why these operations protect memory requires some understanding of the potential attacks on the allocator. + +Therefore, we will first present the source code of the allocator and guide you through building it. Then you can use it to understand the example attacks that are shown +later. diff --git a/content/learning-paths/laptops-and-desktops/memory-tagged-dynamic-memory-allocator/how-to-2.md b/content/learning-paths/laptops-and-desktops/memory-tagged-dynamic-memory-allocator/how-to-2.md new file mode 100644 index 000000000..f392e88cb --- /dev/null +++ b/content/learning-paths/laptops-and-desktops/memory-tagged-dynamic-memory-allocator/how-to-2.md @@ -0,0 +1,650 @@ +--- +title: Implement Memory Tagging for a Dynamic Memory Allocator +weight: 3 + +### FIXED, DO NOT MODIFY +layout: learningpathall +--- + +The source code for the complete project is included in this learning path. We will not explain the source code but, instead, the following sections will go into detail about the memory tagging specific changes +that were made. + +## Project Structure + +The project consists of the following files: +* `CMakeLists.txt` - tells `cmake` how to configure the project +* `heap.c` and `heap.h` - the dynamic memory allocator +* `mte_utils.c` and `mte_utils.h` - helper functions for handling memory tags (these are used by the heap and the demo application) +* `main.c` - the program that uses the memory allocator + +## Software Requirements + +Install the required tools using the following command: + +```bash +sudo apt install -y cmake ninja-build gcc-aarch64-linux-gnu qemu-user +``` + +## Source Code + +Using a file editor of your choice, create a file named `CMakeLists.txt` and copy the contents below into it: + +``` {file_name="CMakeLists.txt"} +cmake_minimum_required(VERSION 3.15) + +project(TaggedMemoryAllocatorDemo C) + +add_executable(demo main.c heap.c mte_utils.c) +``` + +Create a file named `heap.c` with the contents shown below: +```C {file_name="heap.c"} +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "mte_utils.h" + +// Defines provided to support older toolchains. + +#ifndef PR_SET_TAGGED_ADDR_CTRL +#define PR_SET_TAGGED_ADDR_CTRL 55 +#endif + +#ifndef PR_TAGGED_ADDR_ENABLE +#define PR_TAGGED_ADDR_ENABLE (1UL << 0) +#endif + +#ifndef PROT_MTE +#define PROT_MTE 0x20 +#endif + +#ifndef PR_MTE_TCF_SYNC +#define PR_MTE_TCF_SYNC (1 << 1) +#endif + +#ifndef PR_MTE_TAG_SHIFT +#define PR_MTE_TAG_SHIFT 3 +#endif + +#define MTE_TAG_GRANULE 16 + +// Enable logging of heap events and current memory ranges. +static const bool log_events = true; + +// Whether to randomise the memory tag of each new allocation, +// or use a value that increases by 1 each time. +static const bool randomise_memory_tags = false; + +// printf but can be globally disabled by setting log_events to false. +static void log_event(const char *fmt, ...) { + if (log_events) { + va_list args; + va_start(args, fmt); + vprintf(fmt, args); + va_end(args); + } +} + +#define STORAGE_SIZE 4096 +// Will be allocated by mmap during simple_heap_init. +static char *storage = NULL; +// If our search reaches this point, there is no free space to allocate, also +// set by simple_heap_init. +static const char *storage_end = NULL; + +// The heap is divided into ranges, initially only 1 that covers the whole heap +// and is marked free. A header is the number of bytes in the range, and a +// single bit to say whether it is free or allocated. +typedef struct { + uint64_t size : 63; + bool allocated : 1; +} Header; +// This header is placed at the start of each range, so we will return pointers +// that point to the first byte after it. +_Static_assert(sizeof(Header) == sizeof(uint64_t)); + +void log_header(Header header) { + log_event("[%s, size = %u bytes]", + header.allocated ? "allocated" : " free", header.size); +} + +static Header read_header(const char *ptr) { + // This header is in tagged memory so we must use its memory tag to access it. + ptr = set_logical_tag((char *)ptr, get_memory_tag(ptr)); + return *(Header *)ptr; +} + +static void write_header(char *ptr, Header header) { + *(Header *)ptr = header; + log_event("[0x%016lx] Set header to ", ptr); + log_header(header); + log_event("\n"); +} + +// Log a table showing the ranges currently marked in the heap. +static void log_ranges() { + log_event("Ranges:\n"); + Header header = {.size = 0, .allocated = false}; + for (const char *header_ptr = storage; header_ptr < storage_end; + header_ptr += header.size) { + header = read_header(header_ptr); + uint8_t tag = get_memory_tag(header_ptr); + const char *tagged_header_ptr = set_logical_tag(header_ptr, tag); + log_event(" [0x%016lx -> 0x%016lx) : ", tagged_header_ptr, + tagged_header_ptr + header.size); + log_event("[memory tag: 0x%x] ", tag); + log_header(header); + log_event("\n"); + } +} + +uint8_t get_next_memory_tag() { + if (randomise_memory_tags) { + // __arm_mte_create_random_tag randomly selects a tag value from the allowed + // values (which we set to 1-15) and sets it as the logical tag of the + // pointer. The second argument is a mask of excluded tag values, we won't + // exclude any. You could use this to exclude values that surround certain + // areas of memory. + return get_logical_tag(__arm_mte_create_random_tag((void *)(0), 0)); + } else { + // Get predictable output by using an incrementing and wrapping value. + static uint8_t next_memory_tag = 1; + + uint8_t ret = next_memory_tag; + if (next_memory_tag == MTE_TAG_MASK) + next_memory_tag = 1; + else + ++next_memory_tag; + + return ret; + } +} + +void simple_heap_init() { + log_event("Simple heap init:\n"); + + int got = prctl( + PR_SET_TAGGED_ADDR_CTRL, + // Enable the tagged address ABI. + // https://www.kernel.org/doc/Documentation/arm64/tagged-address-abi.rst + PR_TAGGED_ADDR_ENABLE | + // Raise memory tagging exceptions as they happen (as opposed to + // async, which reports at a later time). + PR_MTE_TCF_SYNC | + // This is a bitfield where each bit position represents a tag value. + // A 1 means that tag generation instructions may generate that value. + // The value 0xfffe means they can generate all values except for 0, + // which is used for free memory. + (0xfffe << PR_MTE_TAG_SHIFT), + 0, 0, 0); + assert(got == 0); + + // Allocate memory tagged memory as our backing storage. All the memory tags + // will start as 0. + storage = mmap(0, STORAGE_SIZE, + PROT_READ | PROT_WRITE | + // Memory should have memory tagging enabled. + PROT_MTE, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + assert(storage); + storage_end = storage + STORAGE_SIZE; + + log_event("Storage [0x%016lx -> 0x%016lx) (%d bytes)\n", storage, storage_end, + STORAGE_SIZE); + + // On startup, all the heap is one free range. + Header hdr = {.size = STORAGE_SIZE, .allocated = false}; + write_header(storage, hdr); + log_ranges(); +} + +// Search for a free range that has at least `bytes` of space +// (callers should include the header size). +static char *find_free_space(size_t bytes) { + Header header = {.size = 0, .allocated = false}; + for (char *header_ptr = storage; header_ptr < storage_end; + header_ptr += header.size) { + header = read_header(header_ptr); + assert(header.size != 0 && "Header should always have non-zero size."); + if (!header.allocated && (header.size >= bytes)) + return header_ptr; + } + + return NULL; +} + +// Take an existing free range and split it such that there is a `bytes` sized +// range at the start and a new, smaller, free range after that. +static void split_range(char *range, uint64_t size) { + Header original_header = read_header(range); + assert(!original_header.allocated && + "Shouldn't be splitting an allocated range."); + + // Mark what we need as allocated. + Header new_header = {.size = size, .allocated = true}; + write_header(range, new_header); + + // The following space is free and needs a new header to say so. + uint64_t remaining = original_header.size - size; + if (remaining) { + Header free_header = {.size = remaining, .allocated = false}; + write_header(range + size, free_header); + } +} + +static void tag_range(char *range, uint64_t size) { + assert((uintptr_t)range % 16 == 0 && "Expected a granule aligned address"); + assert(size % 16 == 0 && + "Expected range size that is a multiple of the tag granule"); + + // Assume that range already contains the logical tag. + for (; size; size -= MTE_TAG_GRANULE, range += MTE_TAG_GRANULE) + __arm_mte_set_tag(range); +} + +// Attempt to allocate `size` bytes of memory. Returns NULL for 0 sized +// allocations or when we have run out of heap memory. The size passed here does +// not include header size, this is an internal detail. So the returned pointer +// will be sizeof(Header) further forward than the start of the range used. +void *simple_malloc(size_t size) { + if (!size) + return NULL; + + log_event("\nTrying to allocate %ld bytes\n", size); + + // Extra space to include the header. + uint64_t required_size = size + sizeof(Header); + // Memory is tagged in granules of 16 bytes, so we must round up to a whole + // number of 16 byte granules. + if (required_size % MTE_TAG_GRANULE) + required_size += MTE_TAG_GRANULE - (required_size % MTE_TAG_GRANULE); + + char *allocated = find_free_space(required_size); + + if (!allocated) { + log_event("Heap exhausted.\n"); + return NULL; + } + + // Split the found range into this new allocation and a new free range after + // it. + split_range(allocated, required_size); + + // The returned pointer must have its logical tag set to the allocation tag + // of the allocated range. + uint8_t next_memory_tag = get_next_memory_tag(); + allocated = (char *)set_logical_tag(allocated, next_memory_tag); + + // Tag the range with the new non-zero tag. + tag_range(allocated, required_size); + + log_event( + "Memory was allocated at 0x%016lx, size %ld bytes (%ld byte overhead)\n", + allocated, required_size, required_size - size); + + // Return a pointer to after the header. + allocated += sizeof(Header); + + log_ranges(); + return allocated; +} + +// Free the allocation pointed to by ptr. This simply sets the range to free, +// does not change its size of any of its contents. +void simple_free(void *ptr) { + if (!ptr) + return; + + // As the memory tag is part of the pointer, we need to remove it before doing + // any numerical comparisons. As a pointer to the same location, but with a + // higher tag value, will be seen as greater than the other pointer despite + // pointing to the same address. + const void *untagged_ptr = remove_logical_tag(ptr); + assert(((char *)untagged_ptr > storage) && + ((char *)untagged_ptr < storage_end) && + "Trying to free pointer that is not within the heap."); + + // This will point to after the header of the range it's in, so we must walk + // back a bit. + char *header_ptr = (char *)ptr - sizeof(Header); + + // Detect attempts to free an allocation more than once. + uint8_t logical_tag = get_logical_tag(ptr); + uint8_t allocation_tag = get_memory_tag(ptr); + if (logical_tag != allocation_tag) { + printf("\nProgram attempted an invalid free\n"); + print_pointer_tags(ptr); + exit(1); + } + + log_event("\nFreeing allocation at 0x%016lx\n", ptr); + + Header header = read_header(header_ptr); + assert(header.size != 0 && "Can't free an allocation of zero size."); + + // Mark this range as free, leave the size unchanged. + header.allocated = false; + write_header(header_ptr, header); + + // Reset tags to 0 to prevent use after free. + tag_range((char *)set_logical_tag(header_ptr, 0), header.size); + + log_event("Memory at 0x%016lx was freed\n", ptr); + log_ranges(); +} + +``` + +Create a file named `heap.h` with the content shown below: +```C {file_name="heap.h"} +#ifndef HEAP_H +#define HEAP_H + +#include + +// Call once at the start of main() to initialise the empty heap. +// This is the equivalent of what your C library is doing before main() for the +// system heap. +void simple_heap_init(); + +void *simple_malloc(size_t size); + +void simple_free(void *ptr); + +#endif /* ifndef HEAP_H */ + +``` + +Create a file named `mte_utils.c` with the contents shown below: +```C {file_name="mte_utils.c"} +#include "mte_utils.h" + +#include +#include + +uint8_t get_memory_tag(const void *addr) { + return ((uintptr_t)__arm_mte_get_tag(addr) >> MTE_TAG_SHIFT) & MTE_TAG_MASK; +} + +const void *remove_logical_tag(const void *ptr) { + return (const void *)((uintptr_t)ptr & + ~((uintptr_t)MTE_TAG_MASK << MTE_TAG_SHIFT)); +} + +const void *set_logical_tag(const void *ptr, uint8_t tag) { + uintptr_t p = (uintptr_t)remove_logical_tag(ptr); + return (const void *)((uintptr_t)p | ((uintptr_t)tag & MTE_TAG_MASK) + << MTE_TAG_SHIFT); +} + +uint8_t get_logical_tag(const void *ptr) { + return ((uintptr_t)ptr >> MTE_TAG_SHIFT) & MTE_TAG_MASK; +} + +void print_pointer_tags(const void *ptr) { + printf(" Pointer: 0x%016" PRIXPTR " Logical tag: %d\n", ptr, + get_logical_tag(ptr)); + printf("Points to: 0x%016" PRIXPTR " Allocation tag: %d\n", + remove_logical_tag(ptr), get_memory_tag(ptr)); +} + +``` + +Create a file named `mte_utils.h` with the contents shown below: +```C {file_name="mte_utils.h"} +#ifndef MTE_UTILS_H +#define MTE_UTILS_H + +#include +#include + +#define MTE_TAG_SHIFT 56ULL +#define MTE_TAG_MASK 0xfULL + +uint8_t get_memory_tag(const void *addr); + +const void *remove_logical_tag(const void *ptr); + +const void *set_logical_tag(const void *ptr, uint8_t tag); + +uint8_t get_logical_tag(const void *ptr); + +void print_pointer_tags(const void *ptr); + +#endif /* ifndef MTE_UTILS_H */ + +``` + +Create a file named `main.c` with the contents shown below: +```C {file_name="main.c"} +#include "heap.h" +#include "mte_utils.h" +#include +#include +#include +#include +#include +#include + +void handle_segv(int signal, siginfo_t *sig_info, void *arg) { + // Only expect to get synchronous memory tag faults here. + assert(sig_info->si_signo == SIGSEGV); + assert(sig_info->si_code == SEGV_MTESERR); + + // Get the address that the program tried to access. + void *addr = sig_info->si_addr; + // This address includes the logical tag it tried to use. + uint8_t logical_tag = get_logical_tag(addr); + // Which clearly did not match the allocation tag of where it pointed to. + uint8_t allocation_tag = get_memory_tag(addr); + + // Print an explanation of the problem, then exit the program. + printf("\nProgram caused an asynchronous memory tag fault.\n"); + print_pointer_tags(addr); + + if (logical_tag != 0) + if (allocation_tag == 0) + printf( + "\nProgram tried to access unallocated memory, or use after free.\n"); + else + printf("\nProgram tried to access an allocation using an incorrect tag. " + "Possibly a buffer overflow from a different allocation.\n"); + + exit(1); +} + +// The following functions can be called from main() to demonstrate different +// types of memory safety issue. + +void use_after_free() { + // ptr (logical tag N) -> 112 bytes allocation (allocation tag N) + // ptr (logical tag N) -> 112 bytes free (allocation tag 0) + + // This sets a non-zero allocation tag. + char *ptr = simple_malloc(100); + // This resets the allocation tag to 0. + simple_free(ptr); + // We then try to use a non-zero logically tagged pointer to access zero + // allocation tagged memory. + *ptr = 'a'; +} + +void buffer_overflow() { + // ptr (logical tag N) -> 32 bytes allocation (allocation tag N) + // ptr2 (logical tag M) -> 32 bytes allocation (allocation tag M) + + // Note that due to rounding up to 16 byte granule sizes, some overflow + // may be allowed. This example is 12 bytes, which will be 20 to include the + // 8 byte header. That rounds up to 32 bytes (2 granules). + char *ptr = simple_malloc(12); + // This will be allocated immediately after the first allocation. + char *ptr2 = simple_malloc(12); + // This is out of bounds as far as C is concerned but not caught because + // the allocation for ptr is actually bigger than 12 bytes. It is an overflow, + // but it does not corrupt ptr2's data. + *(ptr + 12) = '?'; + // We can go up to this limit. As the pointer we have is actually 8 bytes + // beyond the start of the 32 byte allocation. + *(ptr + 23) = '?'; + // One more byte and we're into the next granule and the tag check fails + // because we're trying to write to ptr2's allocation. + *(ptr + 24) = '?'; +} + +void double_free() { + // ptr (logical tag N) -> 16 bytes allocation (allocation tag N) + // + // ptr (logical tag N) -> 16 bytes free (allocation tag 0) + // + // ptr (logical tag N) ---\ + // --> 16 bytes allocation (allocation tag M) + // ptr2 (logical tag M) ---/ + // + // (next part is prevented by memory tagging) + // + // ptr (logical tag N) ---\ + // --> 16 bytes free (allocation tag 0) + // ptr2 (logical tag M) ---/ + + // Without memory tagging, this code would free ptr2 by calling free twice + // with ptr. + int *ptr = simple_malloc(sizeof(int)); + simple_free(ptr); + int *ptr2 = simple_malloc(sizeof(int)); + // simple_free checks that the logical tag in ptr matches the allocation tag + // of the location it points to. It does not because there should have been + // a different allocation tag set when ptr2 was allocated. Seeing this, the + // allocator stops the program before corruption can take place. + simple_free(ptr); + // When using random tag values, there is a 1 in 16 chance that ptr and ptr2 + // will get the same allocation tag. In this case, the second free works and + // frees ptr2. Now ptr2 points to allocation tags of 0, so this access will + // fail. + *ptr2 = '?'; +} + +int main() { + if (!(getauxval(AT_HWCAP2) & HWCAP2_MTE)) { + printf("error: MTE feature not detected. Make sure the version of QEMU you " + "are running supports MTE and has it enabled.\n"); + exit(2); + } + + simple_heap_init(); + + // Register a handler to catch memory tag exceptions. + struct sigaction sa; + memset(&sa, 0, sizeof(sa)); + sigemptyset(&sa.sa_mask); + sa.sa_sigaction = handle_segv; + sa.sa_flags = SA_SIGINFO; + sigaction(SIGSEGV, &sa, NULL); + + // Call one of the above functions here to see how memory tagging handles + // that type of memory safety issue. + + // use_after_free(); + // buffer_overflow(); + double_free(); + + return 0; +} + +``` + +## Build the source code + +You are now ready to build the source code. + +First, use `cmake` to configure the project: +```bash +cmake . -G Ninja -DCMAKE_C_COMPILER=aarch64-linux-gnu-gcc -DCMAKE_C_FLAGS="-static -march=armv8.5-a+memtag" -DCMAKE_BUILD_TYPE=Debug +``` + +`-march=armv8.5-a+memtag` tells the compiler that we want to use the memory tagging extension (MTE). + +`-static` tells the compiler to build a statically linked binary which is standalone. We'll be using QEMU to emulate the binary and having a statically +linked binary makes this simpler. + +Next build the project with `ninja`: +```bash +ninja +``` + +You should now see a `demo` file in the current directory. + +## Run the Program + +Unless you are actually on memory tagging capable hardware, you will need to run +the binary using QEMU user mode emulation. + +```bash { ret_code="1" } +qemu-aarch64 demo +``` + +The exit codes used by the program are: + +- 0 - no memory misuse detected +- 1 - memory misuse detected +- 2 - MTE not supported (because your version of QEMU is too old or your native hardware does not have MTE). + +The command above will return an exit code of 1. This is expected as it is set up +to run the `double_free` function which causes an MTE exception. + +## Review the program output + +The allocator has logging built in to show you what is happening at each step. +Due to technologies like Address Space Layout Randomisation (ASLR), the output +may not be exactly the same each time. However, the actions of the allocator will +be the same each time. + +This is the typical output: +```text +Simple heap init: +Storage [0x0000400000802000 -> 0x0000400000803000) (4096 bytes) +[0x0000400000802000] Set header to [ free, size = 4096 bytes] +Ranges: + [0x0000400000802000 -> 0x0000400000803000) : [memory tag: 0x0] [ free, size = 4096 bytes] +``` + +The output above shows the start up of the heap. It has allocated a large amount +of memory that it has tagged with tag `0` and it has recorded it as a single +range of free memory. + +```text +Trying to allocate 4 bytes +[0x0000400000802000] Set header to [allocated, size = 16 bytes] +[0x0000400000802010] Set header to [ free, size = 4080 bytes] +Memory was allocated at 0x0100400000802000, size 16 bytes (12 byte overhead) +Ranges: + [0x0100400000802000 -> 0x0100400000802010) : [memory tag: 0x1] [allocated, size = 16 bytes] + [0x0000400000802010 -> 0x0000400000803000) : [memory tag: 0x0] [ free, size = 4080 bytes] +``` + +When the program makes a request to the allocator, you will see that in the logs. +In this case the program tries to allocate 4 bytes of memory. + +To do this, the allocator had to write to 2 range headers. The first to change +its size to 16 bytes (due to overhead that will be explained later). The second to +create a new header to mark the other 4080 bytes of the heap as free. + +One thing to note here is that memory addresses, for example `0x0100400000802000` +include the memory tag value of `1` as you see in the top byte `0x01`. + +When the ranges are shown, the pointers include the memory tag but the tag is +also printed separately to make it easier to read. + +In these cases, the logical tag (the tag in the pointer) and the allocation tag +(the tag in the memory) will always be the same. We expect this because we are +assuming that our allocator has set both correctly. + +When we look at memory misuse, you will see situations where the logical +tags from the program do not match the allocation tags set by the allocator. +This difference is what allows MTE to prevent these problems. diff --git a/content/learning-paths/laptops-and-desktops/memory-tagged-dynamic-memory-allocator/how-to-3.md b/content/learning-paths/laptops-and-desktops/memory-tagged-dynamic-memory-allocator/how-to-3.md new file mode 100644 index 000000000..409de29ad --- /dev/null +++ b/content/learning-paths/laptops-and-desktops/memory-tagged-dynamic-memory-allocator/how-to-3.md @@ -0,0 +1,217 @@ +--- +title: Memory Tagging Changes +weight: 4 + +### FIXED, DO NOT MODIFY +layout: learningpathall +--- + +In this section, you will learn how the allocator was modified compared to the one used +in the [Write a Dynamic Memory Allocator](/learning-paths/cross-platform/dynamic-memory-allocator/) learning path. + +All the code snippets shown here are from the sources shown in the previous section. + +## Memory Initialization + +Memory with tag storage is not allocated by the kernel by default. Therefore, the application +(the heap in this case) must ask for it specifically. + +This heap does that in `simple_heap_init`. + +```C +int got = prctl( + PR_SET_TAGGED_ADDR_CTRL, + PR_TAGGED_ADDR_ENABLE | + PR_MTE_TCF_SYNC | + (0xfffe << PR_MTE_TAG_SHIFT), 0, 0, 0); +``` + +However, we need to set up how memory tagging will act first. `PR_SET_TAGGED_ADDR_CTRL` +enables ["Tagged Address ABI"](https://www.kernel.org/doc/html/next/arm64/tagged-address-abi.html). This means that the kernel will allow us to +use tagged addresses when interacting with it. + +`PR_TAGGED_ADDR_ENABLE` enables the ABI and MTE. +`PR_MTE_TCF_SYNC` enables synchronous exceptions from tag mismatches. This means +that the exception is reported as it happens, rather than waiting for a future +point to report them (for example, the next syscall). + +`0xfffe << PR_MTE_TAG_SHIFT` is a 16 bit bitmask that tells the kernel which +memory tag values are allowed to be generated by MTE's random tag generation +instructions like `irg`. The value used here means "generate any value apart from 0". +The reason for this will be explained later. + +{{% notice Note %}} You do not need to understand assembly for this learning path, +specific instructions are only mentioned for context.{{% /notice %}} + +Now the kernel is ready for us to ask for tagged memory. We do this using the standard +`mmap` syscall but with an extra option `PROT_MTE`, as shown below: + +```C + storage = mmap(0, STORAGE_SIZE, + PROT_READ | PROT_WRITE | + // Memory should have memory tagging enabled. + PROT_MTE, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); +``` + +Using `PROT_MTE` means that the memory allocated by the kernel will have +allocation tags associated with it. + +The kernel promises us that this memory's allocation tags will always be `0` +when first allocated. We will trust this and also use `0` for marking free +memory in the allocator. This is why we previously excluded the value `0` from +random tag generation. + +## Memory Allocation + +To allocate memory we must walk the heap's memory ranges until: +* we find a free range of suitable size or +* we get to the end of the heap, meaning that we cannot allocate any more space. + +Memory tagging changes the details of this process in a few ways. + +### Padding Allocation Sizes + +MTE protects memory in "granules" of 16 bytes. This means that allocations +smaller than 16 bytes need to be rounded up to that size (other strategies are +possible but are not implemented in this allocator). + +Remember that a range header comes before the usable part of the allocation and +that the header is 8 bytes in size (see the `Header` type). So if we called +`simple_malloc(4)`, the memory allocated would look like this: + +```text +| Header | Allocation | Padding | +| 8 bytes | 4 bytes | 4 bytes | +``` + +For a total of 16 bytes, the same logic applies no matter the size of the +allocation. The total size must always be rounded up to a multiple of 16 bytes. + +Those 16 byte chunks must also be aligned to 16 bytes. This is ensured first by us +knowing that the memory returned by `mmap` will already be aligned and by never +using the memory in anything less than 16 byte chunks. + +Padding in this way allows the allocation tags to look like this: +```text +| 4 byte allocation | Free memory... | +| non-zero tag | tag 0, tag 0 (repeats) | +``` + +An allocation will never straddle 2 granules. This allows us to assign +different tags to each allocation (and assign tag `0` to free space). + +### Accessing Range Headers + +The ranges have headers that are stored in tagged memory. This means we cannot simply implement a pointer that pointed to one +range and expect it to be able to read from a subsequent range. + +For example, if we have done one allocation, the ranges might be: +```text + [0x0100400000802000 -> 0x0100400000802010) : [memory tag: 0x1] [allocated, size = 16 bytes] + [0x0000400000802010 -> 0x0000400000803000) : [memory tag: 0x0] [ free, size = 4080 bytes] +``` + +The pointer to the first range will have a logical tag of `1`. Then we skip +forward by the size of that range to the second range. However, the logical tag +is still the same as it was before but it must be `0` for us to access the +second header without causing an exception. + +This is why the function `read_header` calls `get_memory_tag` to get the real +allocation tag of the header that we're about to read from. That function calls a +function from the Arm C Language Extensions (ACLE) called `__arm_mte_get_tag`. + +`__arm_mte_get_tag` takes a pointer to an address. This pointer can have any +logical tag. From the location it's pointed to, it reads the allocation tag, stores +it back into the pointer value you gave to it and overwrites the existing logical tag. + +This corrects the pointer so that it always has the correct logical tag +for the header we are about to access. + +### Generating Tag Values + +Once we have found a free range to allocate the space in, we need to know what its +allocation tag should be. It cannot be `0` because we've reserved that for free +space. It must be one of the other 15 values. + +Remember that we passed `(0xfffe << PR_MTE_TAG_SHIFT)` to `prctl` earlier. This +mask means "generate any tag value apart from 0". + +In a production scenario, this random generation is preferable as it prevents +attacks where someone with knowledge of the program execution predicts +what the tags will be. + +However, this does mean that the output of the program is different every time and due to the +probabilistic nature of MTE, some issues may not always be caught because of this. + +For demo purposes we have added a `randomise_memory_tags` option in `heap.c`. +If you set this to `false`, tag values are generated from a loop of values 1-15. +If it is `true` then random tags are generated in the range 1-15. + +With `randomise_memory_tags` set to `true`, subsequent allocations will +always have different tags. + +```text + [0x0100400000802000 -> 0x0100400000802010) : [memory tag: 0x1] [allocated, size = 16 bytes] + [0x0200400000802010 -> 0x0200400000802020) : [memory tag: 0x2] [allocated, size = 16 bytes] +``` + +This is good for testing. However, an attacker who knows this could predict that +the next allocation would have tag 3 and forge pointers to it. + +Randomizing the tags mitigates against that but also means that subsequent (and possibly +neighbouring) allocations may have the same tag, thereby reducing the protection MTE +can give. + +The memory allocator can mitigate against this too, by excluding neighbouring tags +when generating the new tag. The allocator shown here does not do that but +the idea is discussed further at the end of this learning path. + +## Setting Allocation Tags + +Once we've chosen a range to use and what tag it should have, we have to set +the allocation tag of all granules (16 byte chunks) in the range. + +This is done by `tag_range`. This function assumes that `range` points +to the start of the range and contains a logical tag equal to the allocation +tag we want to set. `size` is the size of the range. + +We assume that `range` is 16 byte aligned and that `size` is a multiple of 16, which should be true given the steps we took earlier. + +The actual tag setting is done by another ACLE function `__arm_mte_set_tag`. +This sets the allocation tag of a location to the logical tag of the pointer +to that location. We do this in a loop until the entire range has a new tag value. + +## Using Allocated Memory + +One of the great features of MTE is that it uses part of the top byte of the +pointer. This builds on a feature called "Top Byte Ignore", which is enabled +by many operating systems, including Linux. + +Top Byte Ignore means that the CPU does not care what is stored in the top +byte of a pointer. User software and kernel interfaces may need further adjustments but, for simple use cases, a tagged pointer can be used anywhere. + +For our allocator, it means that all it has to do is return the pointer +to the application with a logical tag set. The application does not have to +be aware that memory tagging is being used. + +In this demo, the application does have a memory tagging aware signal handler, +which is explained later. + +## Freeing Memory + +Find the header of the range the pointer refers to and mark that as free memory. + +To do this, we write the range header using a pointer with the same logical tag +as the one given to `simple_free` by the program. This operation could fault +if the program is trying to free memory multiple times or uses the wrong +pointer. This situation will be explained later. + +Once this is done, the range is given an allocation tag of 0, which we are using +for free memory. This is sometimes called "untagging" since the default allocation +tag is also 0. + +This "untagging" means that if the application tries to use a pointer to this +allocation after it has been freed, the access will cause an exception +(in the majority of cases, it is possible that it has been reallocated with +the same tag). diff --git a/content/learning-paths/laptops-and-desktops/memory-tagged-dynamic-memory-allocator/how-to-4.md b/content/learning-paths/laptops-and-desktops/memory-tagged-dynamic-memory-allocator/how-to-4.md new file mode 100644 index 000000000..a2800aa27 --- /dev/null +++ b/content/learning-paths/laptops-and-desktops/memory-tagged-dynamic-memory-allocator/how-to-4.md @@ -0,0 +1,249 @@ +--- +title: Preventing Mistakes By Using Memory Tagging +weight: 5 + +### FIXED, DO NOT MODIFY +layout: learningpathall +--- + +At this point, you have a working memory allocator that tags the memory it manages. +In this section we will show you a few classic memory allocation mistakes that +this allocator can prevent because it uses memory tagging. + +**Note:** the allocator used here is a demo and, therefore, does not make optimal +use of memory tagging from either a security or performance point of view. Any +production code should be rigorously tested. + +## How to Use These Examples + +The demo program starts in `main.c` and each of these example exploits is +written as a function you can call from the `main` function. Put these calls +after the call to `sigcation` and before `return 0;`. + +## Signal Handling + +When a memory tagging fault occurs, the kernel will convert this into a signal +and send it to the program. To explain the memory tag faults, `main.c` +includes a signal handler that is aware of memory tagging. + +`handle_segv` is called whenever there is a segmentation fault, which we assume +in our case to always be memory tagging related. When the signal is received, +`handle_segv` will print output to tell you that an exception +has happened along with the pointer that was used and the allocation tag of the +memory it points to. + +In addition, the handler attempts to diagnose the problem. This detection will +only catch the common cases and may misdiagnose others. + +## Buffer Overflow Example + +To try this exploit call `buffer_overflow()` from `main`. You can do this by uncommenting the call to the function in `main.c` and rebuilding `demo`: + +```text +Program caused an asynchronous memory tag fault. + Pointer: 0x0100400000802020 Logical tag: 1 +Points to: 0x0000400000802020 Allocation tag: 2 + +Program tried to access an allocation using an incorrect tag. Possibly a buffer overflow from a different allocation. +``` + +Buffer overflow is one of the most well known memory safety issues. A pointer to one buffer is +incremented too far and is used to access another buffer that is adjacent in memory. + +`buffer_overflow` does 2 allocations: +```C +char *ptr = simple_malloc(12); +char *ptr2 = simple_malloc(12); +``` + +As our allocator is very predictable, we know these will be sequential (minus +padding, more on that later) in memory. + +```text +| ptr1 | ptr2 | Free memory...| +``` + +The code uses `ptr1` to read the contents of that allocation. When it increments +`ptr1` too far, it actually tries to read the `ptr2` allocation. + +```text +Ranges: + [0x0100400000802000 -> 0x0100400000802020) : [memory tag: 0x1] [allocated, size = 32 bytes] + [0x0200400000802020 -> 0x0200400000802040) : [memory tag: 0x2] [allocated, size = 32 bytes] + [0x0000400000802040 -> 0x0000400000803000) : [memory tag: 0x0] [ free, size = 4032 bytes] +``` + +The two allocations have different memory tags. The program attempts +to use `ptr1` (tag 1) to access the allocation at `ptr2` (which expects tag 2). +This generates the exception. + +Note that even though the program allocated only 4 bytes, this got padded to 16. +So, in fact, the program can overflow into the padding space before it would +get to `ptr2`'s allocation. This could be considered a flaw but in terms of +data integrity, it's not an issue because the contents of the `ptr2` allocation +are not at risk. That said, if you can fix issues like this, you should do so. + +The use of `0` to tag free memory means that if this overflow were from `ptr2` +into the free memory, it would also be detected as we know that a pointer +to allocated memory will never have a `0` tag. + +The final detail here is that when using random tag values, a buffer +overflow may not be detected unless the allocator is smart about choosing +tag values (which this allocator is not). + +Imagine we randomly assigned tag `3` to both allocations: +```text +Ranges: + [0x0300400000802000 -> 0x0100400000802020) : [memory tag: 0x3] [allocated, size = 32 bytes] + [0x0300400000802020 -> 0x0200400000802040) : [memory tag: 0x3] [allocated, size = 32 bytes] + [0x0000400000802040 -> 0x0000400000803000) : [memory tag: 0x0] [ free, size = 4032 bytes] +``` + +Now we will not detect the buffer overflow because the tags still match. +A smarter allocator could avoid this by randomizing tags and excluding the tags +immediately surrounding the new allocation. + +## Use After Free Example + +To try this exploit, call `use_after_free` from `main`. You can do this by uncommenting the call to the function in `main.c` and rebuilding `demo`: + +``` +Program caused an asynchronous memory tag fault. + Pointer: 0x0100400000802008 Logical tag: 1 +Points to: 0x0000400000802008 Allocation tag: 0 + +Program tried to access unallocated memory, or use after free. +``` + +A use after free happens when you allocate memory, free that memory, then +attempt to access the memory again. Note that you should not do this but, without memory +tagging or other sanitizers, nothing prevents you from doing so. + +We can see that this has happened here because the pointer used to access memory +(`0x0100400000802008`) is that of the first allocation. + +```text +Ranges: + [0x0100400000802000 -> 0x0100400000802070) : [memory tag: 0x1] [allocated, size = 112 bytes] + [0x0000400000802070 -> 0x0000400000803000) : [memory tag: 0x0] [ free, size = 3984 bytes] +``` + +When it was first allocated, that memory (`0x0000400000802008`) had its +allocation tags set to 1 to match the logical tag in the pointer. + +```text +Ranges: + [0x0000400000802000 -> 0x0000400000802070) : [memory tag: 0x0] [ free, size = 112 bytes] + [0x0000400000802070 -> 0x0000400000803000) : [memory tag: 0x0] [ free, size = 3984 bytes] +``` + +When the allocation was freed, the allocation tags were set to 0 so that +access to that memory with a non-zero tag would raise an exception, which is +what has happened here. + +If not caught, issues like this can lead to memory corruption as the +memory used for the original allocation may have been reused for a different +allocation. It may now contain secret data or data that can alter control flow +in a significant way. + +For an allocator that stores its own metadata in the heap (as this allocator does), +a use after free can also corrupt that metadata potentially damaging many +allocations. + +## Double Free Example + +To use this example, call `double_free` from `main`: + +```text +Program attempted an invalid free + Pointer: 0x0200400000802018 Logical tag: 2 +Points to: 0x0000400000802018 Allocation tag: 3 +``` + +A double free occurs when memory is allocated, freed and then freed again. +This should not happen as a single allocation should only allocated once, and freed once. + +This may not look like a problem but remember that many allocators (including the +one here) store metadata inside the heap. The second free can trick the allocator +into updating what it thinks is metadata for the allocation. This metadata may +now be tracking a different allocation or even be user data inside of a newer +allocation. Either way, without some kind of protection, the heap would become +corrupted. + +In the case of the demo: +```C +int *ptr = simple_malloc(sizeof(int)); +simple_free(ptr); +int *ptr2 = simple_malloc(sizeof(int)); +simple_free(ptr); +``` + +`ptr` is the first allocation, which is then freed. At that point the ranges are: +```text +Ranges: + [0x0000400000802000 -> 0x0000400000802010) : [memory tag: 0x0] [ free, size = 16 bytes] + [0x0000400000802010 -> 0x0000400000803000) : [memory tag: 0x0] [ free, size = 4080 bytes] +``` + +`ptr2` is the second allocation which, being the same size allocation, is put +where `ptr` used to be. + +`ptr` has tag `1`, `ptr2` will have tag `2`. + +```text +Ranges: + [0x0200400000802000 -> 0x0200400000802010) : [memory tag: 0x2] [allocated, size = 16 bytes] + [0x0000400000802010 -> 0x0000400000803000) : [memory tag: 0x0] [ free, size = 4080 bytes] +``` + +Now when the program calls `simple_free(ptr)`, its using a pointer that points +to the `ptr2` allocation but it does not have logical tag 2. + +We could let this fault while `simple_free` attempts to access the +range's header but the problem becomes hard to diagnose from there. Instead, +`simple_free` has an early check for this specific issue. + +```C + // Detect attempts to free an allocation more than once. + uint8_t logical_tag = get_logical_tag(ptr); + uint8_t allocation_tag = get_memory_tag(ptr); + if (logical_tag != allocation_tag) { + printf("\nProgram attempted an invalid free\n"); + print_pointer_tags(ptr); + exit(1); + } +``` + +If you are attempting to free memory using an incorrectly tagged pointer, +this is invalid. This applies whether it actually points to an allocated range +or to free space. Letting either happen could corrupt the internal structures of the heap. + +## Undefined Malloc And Free Behaviour + +The C standard library defines `free` as expecting to be given a pointer that +is exactly the same as that which was produced by `malloc`. Therefore, it is +undefined behaviour to pass a modified pointer to free, for example, as a pointer to +the middle of an allocation rather than the start. + +This doesn't mean that an implementation can't accept a modified pointer. It +means that when software passes a modified pointer, it cannot make assumptions +about what will happen based purely on the C standard. + +Some implementations choose to allow differences in the pointer as long as it +points to the same allocation. Our memory tagging allocator is stricter than +that. Despite the pointer's logical tag not changing where it points to, the allocator +will not allow you to use a pointer with an incorrect tag. + +Looking at the examples above, you can see why the ranges of behaviour allowed by +the standard (or rather, left undefined) can be useful for different use cases. + +If you wanted to make our allocator less strict, you could disable tag checking. +If you want to experiment with that, replacing `PR_MTE_TCF_SYNC` with `PR_MTE_TCF_NONE` is the first step. + +An allocator that can vary the strictness of its checks like this can be useful for porting existing software that has memory problems. + +* Run the program with strict checks +* Find a memory fault +* Fix that fault +* Run the program without strict checks to confirm the fix did not break any functionality +* Repeat the steps until your program is free of memory faults diff --git a/content/learning-paths/laptops-and-desktops/memory-tagged-dynamic-memory-allocator/how-to-5.md b/content/learning-paths/laptops-and-desktops/memory-tagged-dynamic-memory-allocator/how-to-5.md new file mode 100644 index 000000000..c4ae974d4 --- /dev/null +++ b/content/learning-paths/laptops-and-desktops/memory-tagged-dynamic-memory-allocator/how-to-5.md @@ -0,0 +1,91 @@ +--- +title: Memory Tagged Allocation Summary +weight: 6 + +### FIXED, DO NOT MODIFY +layout: learningpathall +--- + +By following this learning path, you have seen how the Memory Tagging Extension (MTE) +can be used by a dynamic memory allocator to prevent common types of memory misuse. + +Remember that due to MTE's limited granularity and number of tag values, the quality +of the experience is very much influenced by the quality of the software using it. +MTE requires that software co-operate with hardware. + +The software shown here works but should not be considered production quality as it is purely for learning purposes. + +Any software you write that uses MTE should be actively tested to prove that the issues you most care about are prevented, as you expect them to be. + +If you're keen to learn more about using the allocator we have shown, here are a few ideas for extensions. + +## Try Different Memory Exploits + +We have only shown the most common memory issues here. There are plenty of other types and variants of those types, that MTE may or may not be able to stop. + +Find some example code and see if the allocator detects the issue. Perhaps it does but diagnosing the issue is very difficult. + +Often the ability to find the source of a problem is just as important as knowing it exists. A double free diagnosed as a buffer overflow could waste a significant amount of engineering time. + +## Improved Tag Value Selection + +Try using the random tag generation mode (`randomise_memory_tags = true`) with +a few of the example exploits. You will see that sometimes the tags of two allocations +will happen to be the same value. This means that some of the memory exploits +are not detected. + +To stop this happening, an improved allocator could read the tags surrounding an +allocation and select one that has not been used. + +```text +| allocation 1 | free | allocation 2 | +| tag 1 | tag 0 | tag 5 | +``` + +If we were about to allocate the space in the middle of the table above, we could +use any tag that is not 0, 1, or 5. + +This, of course, would not help when the allocations are not next to each other. +Consider this heap: +```text +| allocation 1 | allocation 2 | allocation 3 | +| tag 1 | tag 2 | tag 1 | +``` + +If you had a pointer `p1` that was meant to be used with `allocation 1`, you could +increment that to point beyond `allocation 2`, all the way into `allocation 3`. +You could still "buffer overflow" as long as the amount you overflow +by is larger than `allocation 2`. If the amount is less than that, `p1` would point +into `allocation 2` and the problem would be caught as we would expect. + +There is likely to be no perfect solution here, as we cannot prevent a program "jumping" +across a gap like this. There could be ways to mitigate it by increasing the +window of tag values we look at when generating a new tag. In the first example +we just looked at the adjacent allocations. We could instead include the 4 +allocations either side of the new allocation or use a distance in memory. + +Whether any of this makes a difference will come down to what the software +is doing. Which is all the more reason to have example exploits as we have shown +as a "test suite" for any memory protection you are implementing. + +It is likely that these choices will not be binary. You may not be able to catch +all of the problems, all of the time but you are able to catch the majority that +you do care about, most of the time. + +## Handling Small Allocations Differently + +MTE using 16 byte granules increases the overhead of small allocations. To be fair +to MTE, we already had this problem with the original allocator due to its 8 byte +range header. + +You could consider whether small allocations are likely to be used in a way that +means that they should be protected in the same way as larger allocations. Is an +allocation of 4 bytes as likely to be treated as an array versus an allocation +of 256 bytes? + +You could never say for sure as a library writer but, perhaps as an author of a full stack of software, you could. Certainly this technique has been used by compilers, where variables that are only accessed by a fixed address can be protected less strongly than those that are clearly arrays where a pointer is being incremented. + +If you are working with your own stack, you could even have an option in the allocator +to not protect certain allocations. Perhaps a pool of normal memory and a pool +with memory tags. One of the costs of memory tagging is the allocation tag storage, +so this could be an interesting trade off. diff --git a/content/learning-paths/laptops-and-desktops/win_net_maui/_index.md b/content/learning-paths/laptops-and-desktops/win_net_maui/_index.md new file mode 100644 index 000000000..c396bee3b --- /dev/null +++ b/content/learning-paths/laptops-and-desktops/win_net_maui/_index.md @@ -0,0 +1,35 @@ +--- +title: Build .NET MAUI Applications on Arm64 + +minutes_to_complete: 30 + +who_is_this_for: This learning path is for developers who want to learn how to create cross-platform applications with .NET MAUI and leverage performance improvements on Arm64. + +learning_objectives: + - Create and build a .NET MAUI application + - Measure code execution performance uplift on Arm64 + +prerequisites: + - A Windows on Arm computer such as [Windows Dev Kit 2023](https://learn.microsoft.com/en-us/windows/arm/dev-kit), Lenovo Thinkpad X13s running Windows 11 or Windows on Arm [virtual machine](/learning-paths/cross-platform/woa_azure/). + - Visual Studio 2022 with .NET Multi-platform App UI development and Universal Windows Platform development installed. + +author_primary: Dawid Borycki + +### Tags +skilllevels: Introductory +subjects: Migration to Arm +armips: + - Cortex-A +operatingsystems: + - Windows +tools_software_languages: + - .NET + - C# + - Visual Studio + +### 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/laptops-and-desktops/win_net_maui/_next-steps.md b/content/learning-paths/laptops-and-desktops/win_net_maui/_next-steps.md new file mode 100644 index 000000000..b0470ada9 --- /dev/null +++ b/content/learning-paths/laptops-and-desktops/win_net_maui/_next-steps.md @@ -0,0 +1,39 @@ +--- +# ================================================================================ +# Edit +# ================================================================================ + +next_step_guidance: > + You have now learned how to create .NET MAUI application. You can now apply this knowledge to launch the application on the mobile devices (like iOS or Android). +# 1-3 sentence recommendation outlining how the reader can generally keep learning about these topics, and a specific explanation of why the next step is being recommended. + +recommended_path: "/learning-paths/laptops-and-desktops/win_net8" +# Link to the next learning path being recommended(For example this could be /learning-paths/servers-and-cloud-computing/mongodb). + + +# further_reading links to references related to this path. Can be: + # Manuals for a tool / software mentioned (type: documentation) + # Blog about related topics (type: blog) + # General online references (type: website) + +further_reading: + - resource: + title: .NET Multi-platform App UI + link: https://dotnet.microsoft.com/en-us/apps/maui + type: documentation + - resource: + title: What is .NET MAUI? + link: https://learn.microsoft.com/en-us/dotnet/maui/what-is-maui?view=net-maui-8.0 + type: Microsoft Learn + - resource: + title: .NET MAUI Source Code + link: https://github.com/dotnet/maui + type: GitHub repository + +# ================================================================================ +# FIXED, DO NOT MODIFY +# ================================================================================ +weight: 21 # set to always be larger than the content in this path, and one more than 'review' +title: "Next Steps" # Always the same +layout: "learningpathall" # All files under learning paths have this same wrapper +--- diff --git a/content/learning-paths/laptops-and-desktops/win_net_maui/_review.md b/content/learning-paths/laptops-and-desktops/win_net_maui/_review.md new file mode 100644 index 000000000..7c189f416 --- /dev/null +++ b/content/learning-paths/laptops-and-desktops/win_net_maui/_review.md @@ -0,0 +1,52 @@ +--- +# ================================================================================ +# Edit +# ================================================================================ + +# Always 3 questions. Should try to test the reader's knowledge, and reinforce the key points you want them to remember. + # question: A one sentence question + # answers: The correct answers (from 2-4 answer options only). Should be surrounded by quotes. + # correct_answer: An integer indicating what answer is correct (index starts from 0) + # explanation: A short (1-3 sentence) explanation of why the correct answer is correct. Can add additional context if desired + + +review: + - questions: + question: > + Does .NET MAUI project contains sub-platform-specific projects? + answers: + - "Yes" + - "No" + correct_answer: 2 + explanation: > + .NET MAUI, contrary to Xamarin.Forms, contains a single project. The platform-specific code is included in the Platforms sub-folder + + - questions: + question: > + What is the MauiProgram.cs file for? + answers: + - "It serves the purpose of the application's entry point" + - "It enables you to specify platforms on which the application can run" + correct_answer: 1 + explanation: > + MauiProgram.cs contains the entry point for .NET MAUI application. It's where you configure and set up the app, including services, dependencies, and the main app configuration. It typically contains the CreateMauiApp method, which builds and returns an instance of MauiApp class + + - questions: + question: > + What is the MVVM pattern for? + answers: + - "To build a single page applications" + - "To accelerate applications" + - "To separate concerns, e.g., separate the logic from the view" + correct_answer: 3 + explanation: > + The Model-View-ViewModel (MVVM) architectural pattern is designed to separate an application's business and presentation logic from its user interface + + +# ================================================================================ +# FIXED, DO NOT MODIFY +# ================================================================================ +title: "Review" # Always the same title +weight: 20 # Set to always be larger than the content in this path +layout: "learningpathall" # All files under learning paths have this same wrapper +--- diff --git a/content/learning-paths/laptops-and-desktops/win_net_maui/figures/01.png b/content/learning-paths/laptops-and-desktops/win_net_maui/figures/01.png new file mode 100644 index 000000000..b68d95849 Binary files /dev/null and b/content/learning-paths/laptops-and-desktops/win_net_maui/figures/01.png differ diff --git a/content/learning-paths/laptops-and-desktops/win_net_maui/figures/02.png b/content/learning-paths/laptops-and-desktops/win_net_maui/figures/02.png new file mode 100644 index 000000000..d1e067a23 Binary files /dev/null and b/content/learning-paths/laptops-and-desktops/win_net_maui/figures/02.png differ diff --git a/content/learning-paths/laptops-and-desktops/win_net_maui/figures/03.png b/content/learning-paths/laptops-and-desktops/win_net_maui/figures/03.png new file mode 100644 index 000000000..662550d0b Binary files /dev/null and b/content/learning-paths/laptops-and-desktops/win_net_maui/figures/03.png differ diff --git a/content/learning-paths/laptops-and-desktops/win_net_maui/figures/04.png b/content/learning-paths/laptops-and-desktops/win_net_maui/figures/04.png new file mode 100644 index 000000000..3444421c9 Binary files /dev/null and b/content/learning-paths/laptops-and-desktops/win_net_maui/figures/04.png differ diff --git a/content/learning-paths/laptops-and-desktops/win_net_maui/figures/05.png b/content/learning-paths/laptops-and-desktops/win_net_maui/figures/05.png new file mode 100644 index 000000000..0e58d50a1 Binary files /dev/null and b/content/learning-paths/laptops-and-desktops/win_net_maui/figures/05.png differ diff --git a/content/learning-paths/laptops-and-desktops/win_net_maui/figures/06.png b/content/learning-paths/laptops-and-desktops/win_net_maui/figures/06.png new file mode 100644 index 000000000..ef3edf31a Binary files /dev/null and b/content/learning-paths/laptops-and-desktops/win_net_maui/figures/06.png differ diff --git a/content/learning-paths/laptops-and-desktops/win_net_maui/figures/07.png b/content/learning-paths/laptops-and-desktops/win_net_maui/figures/07.png new file mode 100644 index 000000000..1aa61c946 Binary files /dev/null and b/content/learning-paths/laptops-and-desktops/win_net_maui/figures/07.png differ diff --git a/content/learning-paths/laptops-and-desktops/win_net_maui/figures/08.png b/content/learning-paths/laptops-and-desktops/win_net_maui/figures/08.png new file mode 100644 index 000000000..587d1892b Binary files /dev/null and b/content/learning-paths/laptops-and-desktops/win_net_maui/figures/08.png differ diff --git a/content/learning-paths/laptops-and-desktops/win_net_maui/figures/09.png b/content/learning-paths/laptops-and-desktops/win_net_maui/figures/09.png new file mode 100644 index 000000000..6bfe30fcb Binary files /dev/null and b/content/learning-paths/laptops-and-desktops/win_net_maui/figures/09.png differ diff --git a/content/learning-paths/laptops-and-desktops/win_net_maui/figures/10.png b/content/learning-paths/laptops-and-desktops/win_net_maui/figures/10.png new file mode 100644 index 000000000..44013f88c Binary files /dev/null and b/content/learning-paths/laptops-and-desktops/win_net_maui/figures/10.png differ diff --git a/content/learning-paths/laptops-and-desktops/win_net_maui/figures/11.png b/content/learning-paths/laptops-and-desktops/win_net_maui/figures/11.png new file mode 100644 index 000000000..21f67e322 Binary files /dev/null and b/content/learning-paths/laptops-and-desktops/win_net_maui/figures/11.png differ diff --git a/content/learning-paths/laptops-and-desktops/win_net_maui/figures/12.png b/content/learning-paths/laptops-and-desktops/win_net_maui/figures/12.png new file mode 100644 index 000000000..21075ba9f Binary files /dev/null and b/content/learning-paths/laptops-and-desktops/win_net_maui/figures/12.png differ diff --git a/content/learning-paths/laptops-and-desktops/win_net_maui/how-to-1.md b/content/learning-paths/laptops-and-desktops/win_net_maui/how-to-1.md new file mode 100644 index 000000000..27ed8b224 --- /dev/null +++ b/content/learning-paths/laptops-and-desktops/win_net_maui/how-to-1.md @@ -0,0 +1,69 @@ +--- +# User change +title: "Create a .NET MAUI Project" + +weight: 2 + +layout: "learningpathall" +--- + +## Introduction +.NET Multi-platform App UI (.NET MAUI) is a framework introduced by Microsoft to enable developers to create cross-platform applications for mobile and desktop operating systems using a single codebase. This framework extends the capabilities of Xamarin.Forms, providing a more unified and scalable approach to developing applications that can run on Android, iOS, macOS, and Windows. For further information on Xamarin.Forms, check out this learning path on [Developing cross-platform applications with Xamarin.Forms](/learning-paths/laptops-and-desktops/win_xamarin_forms/). + +With .NET MAUI, developers can leverage the full power of .NET to build interactive and performance-oriented applications. It supports modern development patterns and practices, including MVVM (Model-View-ViewModel), dependency injection, and async programming. The framework integrates seamlessly with Visual Studio, offering a rich development environment with tools for debugging, UI design, and deployment. + +Similar to Windows Presentation Foundation (WPF) and Xamarin.Forms, .NET MAUI utilizes a mix of declarative UI (using XAML) and imperative code, allowing for flexible and expressive UI construction. Moreover, .NET MAUI incorporates platform-specific capabilities and access to native APIs, ensuring that applications can fully utilize the features of the underlying operating system. + +In this learning path, you will explore how to create a .NET MAUI application and discover how .NET MAUI application performance is enhanced on Arm64 devices, particularly through the computationally intensive task of performing the multiply-add operation on two vectors. This operation, commonly used in many artificial neural network architectures, serves as an excellent example of Arm64's capabilities in handling performance-demanding tasks. + +You can find the complete project code used in this learning path [here](https://github.com/dawidborycki/Arm64.MobileApp.MAUI.git). + +## Before you begin +Before you begin the implementation, install Visual Studio 2022 with the following workloads: +1. .NET Multi-platform App UI development +2. Universal Windows Platform development + +## Create the project +Open Visual Studio and click 'Create a new project'. + +![fig1](figures/01.png) + +In the next window, search for the '.NET MAUI App' template. + +![fig2](figures/02.png) + +This will open the 'Configure your new project' view, in which you should configure the project as follows (refer to the figure below): + +1. Project name: **Arm64.MobileApp.MAUI** +2. Location: Select the project location on your drive (example: **C:\Users\db\source\repos**) +3. Check the option **Place solution and project in the same directory** +4. Click the **Next** button + +![fig3](figures/03.png) + +In the next window, select **.NET 8.0 (Long Term Support)** and click the **Create** button: + +![fig4](figures/04.png) + +The project creation process may take a few moments. During this process, you might be prompted to enable Developer Mode for Windows. If so, follow the instructions shown to enable Developer Mode: + +![fig5](figures/05.png) + +Then, accept any other license terms that appear, including the Android SDK - License Agreement. + +Your project should be now ready. Next, open the Solution Explorer (View -> Solution Explorer) to view the created projects: + +![fig6](figures/06.png) + +## Understanding the project structure +The .NET MAUI project template is structured to enable the development of cross-platform applications using a single codebase, targeting Android, iOS, macOS, and Windows. Therefore, the project we have just created contains the following elements: +1. **MauiProgram.cs** - contains the entry point for .NET MAUI application. It's where you configure and set up the app, including services, dependencies, and the main app configuration. It typically contains the CreateMauiApp method, which builds and returns an instance of MauiApp class. +2. **App.xaml** and **App.xaml.cs** - these files define the application-level resources and the App class, which is derived from Application. This class serves as the central point for managing the app's lifecycle and its main interface. +3. **Platforms Folder** - this folder contains platform-specific code and resources. It is organized into subfolders for each target platform. Here we have: Android, iOS, MacCatalyst, Tizen, and Windows. These folders can contain platform-specific initialization code, icons, splash screens, and other resources. For example, the Android subfolder contains the MainActivity.cs file, which represents the main activity class that runs when the application starts on an Android device. +4. **MainPage.xaml** and **MainPage.xaml.cs** - these files declare the main page of the app, which serves as the starting UI. The XAML file contains the declarative UI markup, while the .cs file contains the code-behind, handling events and binding data to the UI. +5. **Resources** folder - this contains the application's resources, such as fonts, images, and styles. It is divided into several subfolders, such as Fonts, Images, Splash and Styles. +6. **AppShell.xaml** - this file serves as a fundamental component for defining the navigation structure and the overall architecture of the app's UI. It is a new concept introduced with MAUI, building upon and enhancing the navigation capabilities previously available in Xamarin.Forms. AppShell provides a simplified way to create complex, hierarchical navigation in your app with an emphasis on improving the user experience across all platforms. + +You can now run the created application. To do so, click _Debug/Start Debugging_. The running application will look like the figure below: + +![fig7](figures/07.png) diff --git a/content/learning-paths/laptops-and-desktops/win_net_maui/how-to-2.md b/content/learning-paths/laptops-and-desktops/win_net_maui/how-to-2.md new file mode 100644 index 000000000..3befe54f3 --- /dev/null +++ b/content/learning-paths/laptops-and-desktops/win_net_maui/how-to-2.md @@ -0,0 +1,263 @@ +--- +# User change +title: "Implement the application" + +weight: 3 + +layout: "learningpathall" +--- + +## Objective +In this section, you will modify your newly created project by adding code for multiply-add operations, helper classes to measure execution time, and a list view to display the processing results. + +## Helper classes +We begin by implementing two helper classes: +1. **PerformanceHelper** - this class is designed to measure the code execution time. +2. **VectorHelper** - this class will implement the AdditionOfProduct method, which calculates the expression **a*b+c**, where **a**, **b**, and **c** are pseudo-randomly generated vectors of double precision. + +To create those classes proceed as follows: +1. In the Solution Explorer, right-click the **Arm64.MobileApp.MAUI** project and choose Add -> New Folder. Name the folder Helpers. +2. Right-click the Helpers folder, and select Add -> Class.... This action opens the _Add New Item_ window. +3. In the _Add New Item_ window, enter **PerformanceHelper.cs** in the Name text box, and click the Add button. +4. Insert the following statements into the **PerformanceHelper.cs** file: + +```cs +using System.Diagnostics; + +namespace Arm64.MobileApp.MAUI.Helpers +{ + public static class PerformanceHelper + { + private static readonly Stopwatch stopwatch = new(); + + public static double MeasurePerformance(Action method, int executionCount) + { + stopwatch.Restart(); + + for (int i = 0; i < executionCount; i++) + { + method(); + } + + stopwatch.Stop(); + + return stopwatch.ElapsedMilliseconds; + } + } +} +``` +5. Similarly, create the VectorHelper.cs file and modify it as follows:: +```cs +namespace Arm64.MobileApp.MAUI.Helpers +{ + public static class VectorHelper + { + private static readonly Random random = new(); + + private static double[] GenerateRandomVector(int size, double minValue = 0, double maxValue = 1) + { + return Enumerable.Range(0, size) + .Select(_ => random.NextDouble() * (maxValue - minValue) + minValue) + .ToArray(); + } + + public static double[] AdditionOfProduct(int size, double minValue = 0, double maxValue = 1) + { + var a = GenerateRandomVector(size, minValue, maxValue); + var b = GenerateRandomVector(size, minValue, maxValue); + var c = GenerateRandomVector(size, minValue, maxValue); + + var result = new double[size]; + + for (int i = 0; i < size; i++) + { + result[i] = a[i] * b[i] + c[i]; + } + + return result; + } + } +} +``` + +## User interface +We can now modify the user interface. Start by opening the AppShell.xaml file. Then, change the title attribute of the ShellContent to "Arm Learning Paths": + +```XML + +``` + +Next, we'll modify the main view of the application. To do this, open MainPage.xaml and replace its original content with the following: + +```XML + + + + + + + + + + + + + + + + + + + + + + + + + +