diff --git a/FreeRTOS-Plus/Demo/FreeRTOS_Plus_TCP_Echo_Qemu_mps2/Readme.md b/FreeRTOS-Plus/Demo/FreeRTOS_Plus_TCP_Echo_Qemu_mps2/Readme.md index 71642633eb..2954fb78fb 100644 --- a/FreeRTOS-Plus/Demo/FreeRTOS_Plus_TCP_Echo_Qemu_mps2/Readme.md +++ b/FreeRTOS-Plus/Demo/FreeRTOS_Plus_TCP_Echo_Qemu_mps2/Readme.md @@ -1,166 +1,372 @@ -# Emulating MPS2 Cortex M3 AN385 on QEMU +# TCP Echo Client Demo for MPS2 Cortex-M3 AN385 emulated using QEMU +This FreeRTOS+TCP example demonstrates a TCP Echo Client which sends +echo requests to an Echo Server and then receives the echo reply. The +Echo Client runs on the MPS2 Cortex-M3 AN385 platform emulated using QEMU. -## Requirements -1. GNU Arm Embedded Toolchain download [here](https://developer.arm.com/tools-and-software/open-source-software/developer-tools/gnu-toolchain/gnu-rm/downloads) -3. qemu-arm-system download [here](https://www.qemu.org/download) -2. Make (tested on version 3.82) -4. Linux OS (tested on Ubuntu 18.04) +## Setup Description +The demo requires 2 components - +1. Echo Client - The demo in this repository. +1. Echo Server - An external echo server. -## How to download -Navigate to a parent directory of your choice and run the following command +We need a Virtual Machine (VM) running Linux OS to run this demo. Echo Client +runs in the Virtual Machine (VM) and Echo Server runs on the host machine. ``` -$ git clone https://github.com/FreeRTOS/FreeRTOS.git --recurse-submodules --depth 1 ++--------------------------------------------------------+ +| Host Machine | +| OS - Any | +| Runs - Echo Server | +| +--------------------------+ | +| | Virtual Machine (VM) | | +| | OS - Linux | | +| | Runs - Echo Client | | +| | | | +| +----------------+ | +----------------+ | | +| | | | | | | | +| | | | | | | | +| | Echo Server | <-------> | Echo Client | | | +| | | | | | | | +| | | | | | | | +| | | | | | | | +| +----------------+ | +----------------+ | | +| | | | +| +--------------------------+ | ++--------------------------------------------------------+ ``` -The previous command should create a directory named **FreeRTOS** -## Networking Echo client Demo -To make networking support possible a few steps needs to be done on the machine -lets assume the following interfaces using ubuntu 18.04 or Fedora 30 -(the interface names on your machine could be different) +## Setting up VM +1. Install a Virtual Machine software on your machine. On Windows you can use +[Oracle VirtualBox](https://www.virtualbox.org/) and on Mac you can use +[Parallels](https://www.parallels.com/products/desktop/). +2. Launch a Linux VM. We tested using Ubuntu 22.04. +3. Install the following tools in the VM: + * [GNU Arm Embedded Toolchain](https://developer.arm.com/tools-and-software/open-source-software/developer-tools/gnu-toolchain/gnu-rm/downloads). + * [qemu-arm-system](https://www.qemu.org/download). + * Make (Version 4.3): + ``` + sudo apt install make + ``` + * ipcalc: + ``` + sudo apt install ipcalc + ``` + * brctl: + ``` + sudo apt install bridge-utils + ``` +4. Clone the source code in the VM: + ```shell + git clone https://github.com/FreeRTOS/FreeRTOS.git --recurse-submodules --depth 1 + ``` + +## Launch Echo Server +Launch Echo Server on the host machine. +### Host OS is Linux +* Install `netcat`: + ``` + sudo apt install netcat + ``` +* Start an Echo Server on port 7: + ```shell + sudo nc -l 7 + ``` + +### Host OS is Windows +* Install [Npcap/Nmap](https://nmap.org/download.html#windows). +* Start an Echo Server on port 7: + ```shell + ncat -l 7 + ``` + +### Host OS is Mac +* Install `netcat`: + ```shell + brew install netcat + ``` +* Start an Echo Server on port 7: + ```shell + nc -l -p 7 + ``` + +## Enable Networking in QEMU +The Echo Client in this demo runs in QEMU inside the VM. We need to enable +networking in QEMU to enable the Echo Client to be able to reach the Echo +Server. Do the following steps in the VM: + + +1. Run the `ifconfig` command to find the VM's network interface details: +``` +enp0s3: flags=4163 mtu 9001 + inet 192.168.1.81 netmask 255.255.255.0 broadcast 192.168.15.255 + inet6 fe80::89c:55ff:fe3d:18ad prefixlen 64 scopeid 0x20 + ether 0a:9c:55:3d:18:ad txqueuelen 1000 (Ethernet) + RX packets 15001255 bytes 11443805826 (11.4 GB) + RX errors 0 dropped 0 overruns 0 frame 0 + TX packets 9248218 bytes 2080385000 (2.0 GB) + TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 + +``` + +2. Define a shell variable `VM_NETWORK_INTERFACE` and set its value to the +name of the network interface of the VM. For example, in the above output +of the `ifconfig` command, name of the the network interface is `enp0s3`: +```shell +export VM_NETWORK_INTERFACE=enp0s3 +``` + +3. Define a shell variable `VM_IP_ADDRESS` and set its value to the IP address +of the VM. For example, in the above output of the `ifconfig` command, IP +address of the VM is `192.168.1.81`: +```shell +export VM_IP_ADDRESS=192.168.1.81 +``` + +4. Define a shell variable `VM_NETMASK` and set its value to the netmask of +the VM. For example, in the above output of the `ifconfig` command, netmask +of the VM is `255.255.255.0`: +```shell +export VM_NETMASK=255.255.255.0 +``` + +5. Calculate the CIDR of the VM from the netmask: +```shell +$ ipcalc -b 1.1.1.1 $VM_NETMASK | grep Netmask +Netmask: 255.255.255.0 = 24 +``` +CIDR is `24` in the above output. + +6. Define a shell variable `VM_CIDR` and set its value to the CIDR of the VM +found in the above step. +```shell +export VM_CIDR=24 +``` + +7. Find the Default Gateway for the VM: +```shell +$ ip route show +default via 192.168.1.254 dev enp0s3 proto dhcp src 192.168.1.81 metric 100 +``` +Default Gateway is `192.168.1.254` in the above output. + +8. Define a shell variable `VM_DEFAULT_GATEWAY` and set its value to the +Default Gateway of the VM found in the above step. +```shell +export VM_DEFAULT_GATEWAY=192.168.1.254 +``` + +9. Find the DNS Server of the VM: +```shell +$ grep "nameserver" /etc/resolv.conf +nameserver 192.168.1.254 +``` + +10. Define a shell variable `VM_DNS_SERVER` and set its value to the +DNS Server of the host machine found in the above step. +```shell +export VM_DNS_SERVER=192.168.1.254 ``` -l0: loopback in terface -enp0s3: ethernet interface -virbr0: virtual bridge (to be created) -virbr0-nic: veth virtual interface (to be created) + +11. Pick an IP address for the QEMU which is in the same network as the VM. +This IP address must not be in-use by any other machine on the same network. +Define a shell variable `QEMU_IP_ADDRESS` and set its value to the +picked IP Address. For example, run the following command if you picked +`192.168.1.80`: +```shell +export QEMU_IP_ADDRESS=192.168.1.80 ``` -### A few assumptions (your numbers could vary) + +12. Pick a MAC address for the QEMU. Define a shell variable `QEMU_MAC_ADDRESS` +and set its value to the picked MAC Address. For example, run the following +command if you picked `52:54:00:12:34:AD`: +```shell +export QEMU_MAC_ADDRESS=52:54:00:12:34:AD ``` -Local Host IP address: 192.168.1.81 -Local FreeRTOS IP address: 192.168.1.80 -Local FreeRTOS Subnet mask: 255.255.255.0 -Default Gateway IP address: 192.168.1.254 -Default DNS IP address: 192.168.1.254 -Echo Server IP address: 192.168.1.204 -Echo Server Port: 7 -Local FreeRTOS Mac address: 52:54:00:12:34:AD + +13. Define a shell variable `ECHO_SERVER_IP_ADDRESS` and set its value to the +IP address of the Echo Server which is running on the host. For example, +run the following command if the IP address of the Echo Server is +`192.168.1.204`: +```shell +export ECHO_SERVER_IP_ADDRESS=192.168.1.204 +``` + +14. Turn off firewall on the VM. +On Ubuntu run: +```shell +sudo ufw disable +sudo ufw status ``` +On RedHat/Fedora system run: +```shell +sudo systemctl status firewalld +sudo systemctl stop firewalld +``` + +15. Create virtual bridge (virbr0) and virtual NIC (virbr0-nic) to enable +networking in QEMU. +```shell +sudo ip link add virbr0 type bridge +sudo ip tuntap add dev virbr0-nic mode tap -### Building and Running +sudo ip addr add $VM_IP_ADDRESS/$VM_CIDR dev virbr0 -1. Fill the defines values in FreeRTOSConfig.h with what is equivalent to the - above values on your system +sudo brctl addif virbr0 $VM_NETWORK_INTERFACE +sudo brctl addif virbr0 virbr0-nic + +sudo ip link set virbr0 up +sudo ip link set virbr0-nic up + +sudo ip route add default via $VM_DEFAULT_GATEWAY dev virbr0 +``` + +The following diagram shows the setup: +``` ++-------------------------------------------------------------------------+ +| Virtual Machine (VM) | +| | +| +-------------------------+ | VM NIC (enp0s3) +| | | Virtual NIC (virbr0-nic) +--+ +| | QEMU +--+ | | +| | | | +--------------+ | | +| | | +--------->| virbr0 | ---------->| +--------> Internet +| | | | +--------------+ | | +| | +--+ Virtual Bridge | | +| | | +--+ +| +-------------------------+ | +| | +| | ++-------------------------------------------------------------------------+ +``` + +## Build and Run +Do the following steps in the VM where you cloned the code: + +1. Set `configIP_ADDR0`-`configIP_ADDR3` in `FreeRTOSConfig.h` to the value +of `QEMU_IP_ADDRESS`: +```shell +echo $QEMU_IP_ADDRESS +``` ```c #define configIP_ADDR0 192 #define configIP_ADDR1 168 #define configIP_ADDR2 1 #define configIP_ADDR3 80 +``` +2. Set `configNET_MASK0`-`configNET_MASK3` in `FreeRTOSConfig.h` to the value +of `VM_NETMASK`: +```shell +echo $VM_NETMASK +``` +```c #define configNET_MASK0 255 #define configNET_MASK1 255 #define configNET_MASK2 255 #define configNET_MASK3 0 +``` +3. Set `configGATEWAY_ADDR0`-`configGATEWAY_ADDR3` in `FreeRTOSConfig.h` to +the value of `VM_DEFAULT_GATEWAY`: +```shell +echo $VM_DEFAULT_GATEWAY +``` +```c #define configGATEWAY_ADDR0 192 #define configGATEWAY_ADDR1 168 #define configGATEWAY_ADDR2 1 #define configGATEWAY_ADDR3 254 +``` +4. Set `configDNS_SERVER_ADDR0`-`configDNS_SERVER_ADDR3` in `FreeRTOSConfig.h` +to the value of `VM_DNS_SERVER`: +```shell +echo $VM_DNS_SERVER +``` +```c #define configDNS_SERVER_ADDR0 192 #define configDNS_SERVER_ADDR1 168 #define configDNS_SERVER_ADDR2 1 #define configDNS_SERVER_ADDR3 254 +``` +5. Set `configMAC_ADDR0`-`configMAC_ADDR5` in `FreeRTOSConfig.h` to the value +of `QEMU_MAC_ADDRESS`: +```shell +echo $QEMU_MAC_ADDRESS +``` +```c #define configMAC_ADDR0 0x52 #define configMAC_ADDR1 0x54 #define configMAC_ADDR2 0x00 #define configMAC_ADDR3 0x12 #define configMAC_ADDR4 0x34 #define configMAC_ADDR5 0xAD +``` +6. Set `configECHO_SERVER_ADDR0`-`configECHO_SERVER_ADDR3` in `FreeRTOSConfig.h` +to the value of `ECHO_SERVER_IP_ADDRESS`: +```shell +echo $ECHO_SERVER_IP_ADDRESS +``` +```c #define configECHO_SERVER_ADDR0 192 #define configECHO_SERVER_ADDR1 168 #define configECHO_SERVER_ADDR2 1 #define configECHO_SERVER_ADDR3 204 ``` -2. Build your software -``` -$ make +7. Build: +```shell +make ``` -options: DEBUG=1 to build with **-O0** and debugging symbols -3. On the remote machine (ip 192.168.1.204) -``` -$ sudo nc -l 7 -``` -4. Turn off the firewall if running -On RedHat/Fedora system (tested Fedora 30) run: -``` -sudo systemctl status firewalld -sudo systemctl stop firewalld -``` -On Ubuntu run: -``` -$ sudo ufw disable -$ sudo ufw status -``` -5. Setup the local machine -Run the following commands replacing the values and interface names -that conform to your system -``` -sudo ip link add virbr0 type bridge -sudo ip tuntap add dev virbr0-nic mode tap - -sudo ip addr add 192.168.1.81/24 dev virbr0 - -sudo brctl addif virbr0 enp0s3 -sudo brctl addif virbr0 virbr0-nic - -sudo ip link set virbr0 up -sudo ip link set virbr0-nic up - -sudo ip route add default via 192.168.1.254 dev virbr0 -``` - -6. Run the demo -``` -$ sudo qemu-system-arm -machine mps2-an385 -cpu cortex-m3 - -kernel ./build/RTOSDemo.axf \ +8. Run: +```shell +sudo qemu-system-arm -machine mps2-an385 -cpu cortex-m3 \ + -kernel ./build/freertos_tcp_mps2_demo.axf \ -netdev tap,id=mynet0,ifname=virbr0-nic,script=no \ - -net nic,macaddr=52:54:00:12:34:AD,model=lan9118,netdev=mynet0 \ + -net nic,macaddr=$QEMU_MAC_ADDRESS,model=lan9118,netdev=mynet0 \ -object filter-dump,id=tap_dump,netdev=mynet0,file=/tmp/qemu_tap_dump\ -display gtk -m 16M -nographic -serial stdio \ - -monitor null -semihosting -semihosting-config enable=on,target=native -``` -Replace the value of macaddr=52:54:00:12:34:AD with your own value from -``` -configMAC_ADDR0 through configMAC_ADDR5 + -monitor null -semihosting -semihosting-config enable=on,target=native ``` -7. Expectations -On the remote machine you should expect to see something similar to the -following: +9. You should see that following output on the terminal of the Echo Server (which +is running `sudo nc -l 7` or `netcat -l 7` depending on your OS): ``` -$ sudo nc -l 7 -Password: -TxRx message number 0FGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~0123456789:;<=> ? @ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~0123456789:;<=>? @ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~0123456789:;<=>? @ABCDEFGHIJKLM ``` -## How to start debugging -1. gdb -

-Append the -s and -S switches to the previous command (qemu-system-arm)
--s: allow gdb to be attached to the process remotely at port 1234
--S: start the program in the paused state
+## Debug +1. Build with debugging symbols: +``` +make DEBUG=1 +``` -run: (make sure you build the debug version) +2. Start QEMU in the paused state waiting for GDB connection: +```shell +sudo qemu-system-arm -machine mps2-an385 -cpu cortex-m3 -s -S \ + -kernel ./build/freertos_tcp_mps2_demo.axf \ + -netdev tap,id=mynet0,ifname=virbr0-nic,script=no \ + -net nic,macaddr=$QEMU_MAC_ADDRESS,model=lan9118,netdev=mynet0 \ + -object filter-dump,id=tap_dump,netdev=mynet0,file=/tmp/qemu_tap_dump\ + -display gtk -m 16M -nographic -serial stdio \ + -monitor null -semihosting -semihosting-config enable=on,target=native ``` -$ arm-none-eabi-gdb -q ./build/RTOSDemo.axf + +3. Run GDB: +```shell +$ arm-none-eabi-gdb -q ./build/freertos_tcp_mps2_demo.axf (gdb) target remote :1234 (gdb) break main (gdb) c ``` -2. tcpdump -To monitor packets received to qemu running the qemu command (qemu-system-arm) - shown above will create a network packet dump that you could inspect with - -``` -$ sudo tcpdump -r /tmp/qemu_tap_dump | less +4. The above QEMU command creates a network packet dump in the file +`/tmp/qemu_tap_dump` which you can examine using `tcpdump` or WireShark: +```shell +sudo tcpdump -r /tmp/qemu_tap_dump | less ``` -