diff --git a/integrations/docker/images/base/chip-build/version b/integrations/docker/images/base/chip-build/version index 20e7ff0d5a702b..d5830b41578b40 100644 --- a/integrations/docker/images/base/chip-build/version +++ b/integrations/docker/images/base/chip-build/version @@ -1 +1 @@ -47 : [Telink] Update Docker image (Zephyr update) +48 : [QEMU] Add QEMU Dockerfile for ble-wifi testing on Linux diff --git a/integrations/docker/images/stage-2/chip-build-linux-qemu/Dockerfile b/integrations/docker/images/stage-2/chip-build-linux-qemu/Dockerfile new file mode 100644 index 00000000000000..b14a00c2ec9710 --- /dev/null +++ b/integrations/docker/images/stage-2/chip-build-linux-qemu/Dockerfile @@ -0,0 +1,216 @@ +ARG VERSION=latest +ARG UBUNTU_QEMU_DIR_DEFAULT="/opt/ubuntu-qemu" +ARG UBUNTU_QEMU_IMG_DEFAULT="${UBUNTU_QEMU_DIR_DEFAULT}/ubuntu-20.04.img" + +FROM ghcr.io/project-chip/chip-build:${VERSION} as build-env +LABEL org.opencontainers.image.source https://github.com/project-chip/connectedhomeip + +ARG BLUEZ_VERSION=5.72 +ARG ELL_VERSION=0.62 +ARG KERNEL_VERSION=6.7.3 +ARG UBUNTU_QEMU_DIR_DEFAULT +ARG UBUNTU_QEMU_IMG_DEFAULT + +ENV UBUNTU_QEMU_DIR=${UBUNTU_QEMU_DIR_DEFAULT} +ENV UBUNTU_QEMU_IMG=${UBUNTU_QEMU_IMG_DEFAULT} + +RUN mkdir -p /tmp/workdir/linux +COPY files/linux/0001-Bluetooth-MGMT-Synchronize-scan-start-and-LE-Meta-ev.patch /tmp/workdir/linux/0001-Bluetooth-MGMT-Synchronize-scan-start-and-LE-Meta-ev.patch +COPY files/bluetooth/main.conf /tmp/workdir/main.conf +RUN set -x \ + && apt-get update \ + && DEBIAN_FRONTEND=noninteractive apt-get install -fy \ + bc \ + cpio \ + dwarves \ + elfutils \ + fakeroot \ + libdw-dev \ + libelf-dev \ + libell-dev \ + libell0 \ + libguestfs-tools \ + linux-image-generic \ + ncurses-dev \ + qemu \ + xz-utils \ + zstd \ + && apt-get clean \ + && rm -rf /var/lib/apt/lists/* \ + && rm -rf /var/cache/apt/* \ + && : # last line + +# Download Linux kernel source +RUN mkdir -p /tmp/workdir/linux \ + && export MAKEFLAGS=-j$(nproc) \ + && cd /tmp/workdir \ + && curl https://cdn.kernel.org/pub/linux/kernel/v6.x/linux-${KERNEL_VERSION}.tar.xz -o /tmp/workdir/linux-${KERNEL_VERSION}.tar.xz \ + && tar -xJf linux-${KERNEL_VERSION}.tar.xz -C /tmp/workdir/linux --strip-components=1 \ + && rm /tmp/workdir/linux-${KERNEL_VERSION}.tar.xz \ + # Set configuration for btvirt + && cd /tmp/workdir/linux \ + && patch -p1 < /tmp/workdir/linux/0001-Bluetooth-MGMT-Synchronize-scan-start-and-LE-Meta-ev.patch \ + && make x86_64_defconfig \ + && ./scripts/config -e BT \ + && ./scripts/config -e BT_BREDR \ + && ./scripts/config -e BT_HCIVHCI \ + && ./scripts/config -e CONFIG_BRIDGE \ + && ./scripts/config -e CONFIG_CRYPTO_AES \ + && ./scripts/config -e CONFIG_CRYPTO_CMAC \ + && ./scripts/config -e CONFIG_CRYPTO_ECB \ + && ./scripts/config -e CONFIG_CRYPTO_USER \ + && ./scripts/config -e CONFIG_CRYPTO_USER_API_HASH \ + && ./scripts/config -e CONFIG_CRYPTO_USER_API_SKCIPHER \ + && ./scripts/config -e CONFIG_VETH \ + && ./scripts/config -e MAC80211 \ + && ./scripts/config -e MAC80211_HWSIM \ + # Compile + && make olddefconfig \ + && make \ + && mkdir -p /opt/ubuntu-qemu/rootfs \ + && make modules_install INSTALL_MOD_PATH=/opt/ubuntu-qemu/rootfs \ + && cp /tmp/workdir/linux/arch/x86/boot/bzImage /opt/ubuntu-qemu/bzImage \ + # Build bluez + && git clone git://git.kernel.org/pub/scm/libs/ell/ell.git /tmp/workdir/ell --depth 1 --branch ${ELL_VERSION} \ + && git clone https://github.com/bluez/bluez.git /tmp/workdir/bluez --depth 1 --branch ${BLUEZ_VERSION} \ + && cd /tmp/workdir/bluez \ + && ./bootstrap \ + && ./configure \ + --enable-backtrace \ + --enable-debug \ + --enable-deprecated \ + --enable-experimental \ + --enable-library \ + --enable-monitor \ + --enable-pie \ + --enable-test \ + --enable-testing \ + --enable-tools \ + --enable-tools \ + --enable-udev \ + --disable-a2dp \ + --disable-avrcp \ + --disable-bap \ + --disable-bass \ + --disable-csip \ + --disable-cups \ + --disable-cups \ + --disable-health \ + --disable-hid \ + --disable-hid2hci \ + --disable-hog \ + --disable-manpages \ + --disable-mcp \ + --disable-mesh \ + --disable-micp \ + --disable-midi \ + --disable-network \ + --disable-obex \ + --disable-optimization \ + --disable-sap \ + --disable-silent-rules \ + --disable-vcp \ + --prefix=/usr \ + --mandir=/usr/share/man \ + --sysconfdir=/etc \ + --localstatedir=/var \ + --with-systemdsystemunitdir=/lib/systemd/system \ + --with-systemduserunitdir=/usr/lib/systemd \ + && make \ + && make install DESTDIR=/opt/ubuntu-qemu/rootfs && mkdir -p /opt/ubuntu-qemu/rootfs/usr/bin && cp /tmp/workdir/bluez/emulator/btvirt /opt/ubuntu-qemu/rootfs/usr/bin \ + # Download Ubuntu image for QEMU + && curl https://cloud-images.ubuntu.com/minimal/releases/focal/release/ubuntu-20.04-minimal-cloudimg-amd64.img \ + -o /tmp/workdir/ubuntu-20.04-minimal-cloudimg-amd64.img \ + # Prepare ubuntu image + && qemu-img create -f qcow2 -o preallocation=off $UBUNTU_QEMU_IMG 10G \ + && virt-resize --expand /dev/sda1 /tmp/workdir/ubuntu-20.04-minimal-cloudimg-amd64.img $UBUNTU_QEMU_IMG \ + && guestfish -a $UBUNTU_QEMU_IMG \ + --mount /dev/sda3:/ \ + --network \ + copy-in /opt/ubuntu-qemu/rootfs/lib /usr : \ + copy-in /opt/ubuntu-qemu/rootfs/usr / : \ + sh 'apt-get remove -y snapd' : \ + sh 'apt-get update' : \ + sh 'DEBIAN_FRONTEND=noninteractive apt-get install -y dnsmasq hostapd wpasupplicant iw libdw1 rfkill' : \ + sh '/usr/bin/systemctl enable bluetooth.service' : \ + sh '/usr/bin/systemctl disable cloud-init.service' : \ + sh '/usr/bin/systemctl disable dbus-fi.w1.wpa_supplicant1.service' : \ + sh '/usr/bin/systemctl disable dnsmasq.service' : \ + sh '/usr/bin/systemctl disable hostapd.service' : \ + sh '/usr/bin/systemctl disable lxd-agent.service' : \ + sh '/usr/bin/systemctl disable systemd-networkd-wait-online.service' : \ + sh '/usr/bin/systemctl disable systemd-timesyncd.service' : \ + sh '/usr/bin/systemctl disable wpa_supplicant.service' : \ + sh '/usr/bin/systemctl mask cloud-init.service' : \ + sh '/usr/bin/systemctl mask dbus-fi.w1.wpa_supplicant1.service' : \ + sh '/usr/bin/systemctl mask dnsmasq.service' : \ + sh '/usr/bin/systemctl mask hostapd.service' : \ + sh '/usr/bin/systemctl mask lxd-agent.service' : \ + sh '/usr/bin/systemctl mask systemd-networkd-wait-online.service' : \ + sh '/usr/bin/systemctl mask systemd-timesyncd.service' : \ + sh '/usr/bin/systemctl mask wpa_supplicant.service' : \ + sh 'passwd -d root' : \ + sh 'ssh-keygen -A' : \ + sh '/bin/echo -e "PermitRootLogin yes\nPasswordAuthentication yes\nPermitEmptyPasswords yes" > /etc/ssh/sshd_config' : \ + mkdir-p "/etc/netplan" : \ + sh '/bin/echo -e "network:\n version: 2\n renderer: networkd\n ethernets:\n enp0s4:\n dhcp4: true\n" > /etc/netplan/01-netcfg.yaml' : \ + sh 'chmod -R 700 /etc/netplan' : \ + sh 'sed -i "s#^ExecStart=.*#ExecStart=-/sbin/agetty -o \"-p -- \\\\\\\\u\" -a root --keep-baud 115200,38400,9600 %I \$TERM#" "/usr/lib/systemd/system/serial-getty@.service"' : \ + mkdir-p "/etc/bluetooth" : \ + copy-in /tmp/workdir/main.conf /etc/bluetooth : \ + sh 'sed -i "s#^ExecStart=.*#ExecStart=-/usr/libexec/bluetooth/bluetoothd -E#" /lib/systemd/system/bluetooth.service' : \ + sh 'rm -f /etc/resolv.conf && /bin/echo -e "nameserver 8.8.8.8" > /etc/resolv.conf' : \ + sh '/bin/echo -e "host0 /chip 9p trans=virtio,version=9p2000.L 0 0" >> /etc/fstab' : \ + sh '/bin/echo -e "export PW_ENVIRONMENT_ROOT=/root/pw_root\n[ -x /launcher.sh ] && /launcher.sh\n" >> /root/.profile' : \ + sh 'DEBIAN_FRONTEND=noninteractive apt-get -y install git gcc g++ pkg-config libssl-dev libdbus-1-dev libglib2.0-dev libavahi-client-dev ninja-build python3 python3-venv python3-dev python3-pip unzip libgirepository1.0-dev libcairo2-dev libreadline-dev' : \ + sh 'git config --file /root/.gitconfig --add safe.directory "*"' : \ + sh 'apt-get clean' : \ + sh 'rm -rf /var/lib/apt/lists/*' : \ + sh 'rm -rf /var/cache/apt/*' : \ + sh 'echo Configuration completed.' \ + && mkdir -p /chip \ + && rm -rf /opt/ubuntu-qemu/rootfs \ + && echo -n \ + "#!/bin/bash\n" \ + "grep -q 'rootshell' /proc/cmdline && exit\n" \ + "if [[ -x /chip/runner.sh ]]; then\n" \ + " echo '### RUNNER START ###'\n" \ + " cd /chip\n" \ + " bash /chip/runner.sh\n" \ + " status=\$?\n" \ + " echo \"### RUNNER STOP, RETURN: \$status\"\n" \ + " echo \$status > /chip/runner_status\n" \ + "else\n" \ + " read -r -t 5 -p 'Press ENTER to access root shell...' && exit || echo ' timeout.'\n" \ + "fi\n" \ + "echo 'Shutting down emulated system...'\n" \ + "echo o > /proc/sysrq-trigger\n" \ + | guestfish --rw -a $UBUNTU_QEMU_IMG -m /dev/sda3:/ upload - /launcher.sh : chmod 0755 /launcher.sh \ + && virt-sparsify --compress ${UBUNTU_QEMU_IMG} ${UBUNTU_QEMU_IMG}.compressed \ + && mv ${UBUNTU_QEMU_IMG}.compressed ${UBUNTU_QEMU_IMG} \ + && rm -rf /var/tmp/.guestfs-0/* \ + && rm -rf /tmp/* \ + && : # last line + +FROM ghcr.io/project-chip/chip-build:${VERSION} + +ARG UBUNTU_QEMU_DIR_DEFAULT +ARG UBUNTU_QEMU_IMG_DEFAULT + +ENV UBUNTU_QEMU_DIR=${UBUNTU_QEMU_DIR_DEFAULT} +ENV UBUNTU_QEMU_IMG=${UBUNTU_QEMU_IMG_DEFAULT} +ENV PW_ENVIRONMENT_ROOT="/root/pw_root" + +RUN set -x \ + && apt-get update \ + && DEBIAN_FRONTEND=noninteractive apt-get install -fy \ + cpu-checker \ + qemu \ + qemu-system-x86 \ + && apt-get clean \ + && rm -rf /var/lib/apt/lists/* \ + && rm -rf /var/cache/apt/* \ + && : # last line +COPY --from=build-env ${UBUNTU_QEMU_DIR} ${UBUNTU_QEMU_DIR} + +WORKDIR /chip diff --git a/integrations/docker/images/stage-2/chip-build-linux-qemu/build.sh b/integrations/docker/images/stage-2/chip-build-linux-qemu/build.sh new file mode 120000 index 00000000000000..46b20313461454 --- /dev/null +++ b/integrations/docker/images/stage-2/chip-build-linux-qemu/build.sh @@ -0,0 +1 @@ +../../../build.sh \ No newline at end of file diff --git a/integrations/docker/images/stage-2/chip-build-linux-qemu/files/bluetooth/main.conf b/integrations/docker/images/stage-2/chip-build-linux-qemu/files/bluetooth/main.conf new file mode 100644 index 00000000000000..1d29f696077c7d --- /dev/null +++ b/integrations/docker/images/stage-2/chip-build-linux-qemu/files/bluetooth/main.conf @@ -0,0 +1,2 @@ +[GATT] +Cache=no diff --git a/integrations/docker/images/stage-2/chip-build-linux-qemu/files/linux/0001-Bluetooth-MGMT-Synchronize-scan-start-and-LE-Meta-ev.patch b/integrations/docker/images/stage-2/chip-build-linux-qemu/files/linux/0001-Bluetooth-MGMT-Synchronize-scan-start-and-LE-Meta-ev.patch new file mode 100644 index 00000000000000..4f2383caa93b87 --- /dev/null +++ b/integrations/docker/images/stage-2/chip-build-linux-qemu/files/linux/0001-Bluetooth-MGMT-Synchronize-scan-start-and-LE-Meta-ev.patch @@ -0,0 +1,136 @@ +From 4dc80d22bd964c0c3fcd0840b6728b3884d2ff1b Mon Sep 17 00:00:00 2001 +From: Arkadiusz Bokowy +Date: Thu, 28 Sep 2023 13:38:17 +0200 +Subject: [PATCH] Bluetooth: MGMT: Synchronize scan start and LE Meta events + +It is possible that the Bluetooth management will receive scan enabled +signal and LE meta events one by another without any delay. Since the +start discovery procedure is performed in an asynchronous manner, it is +possible that these HCI events will be processed concurrently by two +different worker threads. In such case, it is possible that the LE meta +event, which reports new device, will be discarded, because discovering +is still in the starting state. + +The problem is most prominent with the btvirt userspace tool, which +sends LE Meta events just after reporting scan as enabled. Testing +scenario: + + 1. Create two HCI interfaces: + > btvirt -l2 + + 2. Setup BLE advertisement on hci1: + > bluetoothctl + >> select 00:AA:01:00:00:00 + >> menu advertise + >> uuids 03B80E5A-EDE8-4B33-A751-6CE34EC4C700 + >> discoverable on + >> back + >> advertise peripheral + + 3. Start scanning on hci2: + > bluetoothctl + >> select 00:AA:01:01:00:01 + >> scan le + // From time to time, new device is not reported + +This patch adds synchronization for start discovery procedure and device +found reporting by the Bluetooth management. In case of discovering +being in the starting state, the worker which processes LE Meta event +will wait for the cmd_sync_work on which the start discovery procedure +is queued. + +Signed-off-by: Arkadiusz Bokowy +--- + include/net/bluetooth/hci_core.h | 5 +++++ + include/net/bluetooth/hci_sync.h | 1 + + net/bluetooth/hci_sync.c | 7 +++++++ + net/bluetooth/mgmt.c | 17 +++++++++++++++-- + 4 files changed, 28 insertions(+), 2 deletions(-) + +diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h +index f36c1fd5d64e..456bbdf56246 100644 +--- a/include/net/bluetooth/hci_core.h ++++ b/include/net/bluetooth/hci_core.h +@@ -916,6 +916,11 @@ static inline void hci_discovery_filter_clear(struct hci_dev *hdev) + + bool hci_discovery_active(struct hci_dev *hdev); + ++static inline bool hci_discovery_starting(struct hci_dev *hdev) ++{ ++ return hdev->discovery.state == DISCOVERY_STARTING; ++} ++ + void hci_discovery_set_state(struct hci_dev *hdev, int state); + + static inline int inquiry_cache_empty(struct hci_dev *hdev) +diff --git a/include/net/bluetooth/hci_sync.h b/include/net/bluetooth/hci_sync.h +index 6efbc2152146..67cf6689a692 100644 +--- a/include/net/bluetooth/hci_sync.h ++++ b/include/net/bluetooth/hci_sync.h +@@ -43,6 +43,7 @@ void hci_cmd_sync_init(struct hci_dev *hdev); + void hci_cmd_sync_clear(struct hci_dev *hdev); + void hci_cmd_sync_cancel(struct hci_dev *hdev, int err); + void __hci_cmd_sync_cancel(struct hci_dev *hdev, int err); ++void hci_cmd_sync_flush(struct hci_dev *hdev); + + int hci_cmd_sync_submit(struct hci_dev *hdev, hci_cmd_sync_work_func_t func, + void *data, hci_cmd_sync_work_destroy_t destroy); +diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c +index 3640d73f9595..58905a5b7b1e 100644 +--- a/net/bluetooth/hci_sync.c ++++ b/net/bluetooth/hci_sync.c +@@ -681,6 +681,13 @@ void hci_cmd_sync_cancel(struct hci_dev *hdev, int err) + } + EXPORT_SYMBOL(hci_cmd_sync_cancel); + ++/* Wait for all pending HCI commands to complete. ++ */ ++void hci_cmd_sync_flush(struct hci_dev *hdev) ++{ ++ flush_work(&hdev->cmd_sync_work); ++} ++ + /* Submit HCI command to be run in as cmd_sync_work: + * + * - hdev must _not_ be unregistered +diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c +index ba2e00646e8e..fc494348f2f7 100644 +--- a/net/bluetooth/mgmt.c ++++ b/net/bluetooth/mgmt.c +@@ -10374,18 +10374,31 @@ void mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, + { + struct sk_buff *skb; + struct mgmt_ev_device_found *ev; +- bool report_device = hci_discovery_active(hdev); ++ bool report_device; + + if (hci_dev_test_flag(hdev, HCI_MESH) && link_type == LE_LINK) + mesh_device_found(hdev, bdaddr, addr_type, rssi, flags, + eir, eir_len, scan_rsp, scan_rsp_len, + instant); + ++ /* Discovery start procedure is perfomed on a workqueue in an ++ * asynchronous manner. This procedure is finished when the scan ++ * enabled signal is received from the controller. Just after ++ * this signal, the controller might send another event (e.g. LE ++ * Meta). In such case, we need to make sure that the discovery ++ * procedure is finished, because otherwise we might omit some ++ * scan results. ++ */ ++ if (hci_discovery_starting(hdev)) ++ hci_cmd_sync_flush(hdev); ++ ++ report_device = hci_discovery_active(hdev); ++ + /* Don't send events for a non-kernel initiated discovery. With + * LE one exception is if we have pend_le_reports > 0 in which + * case we're doing passive scanning and want these events. + */ +- if (!hci_discovery_active(hdev)) { ++ if (!report_device) { + if (link_type == ACL_LINK) + return; + if (link_type == LE_LINK && !list_empty(&hdev->pend_le_reports)) +-- +2.34.1 + diff --git a/integrations/docker/images/stage-2/chip-build-linux-qemu/files/wifi/dnsmasq.conf b/integrations/docker/images/stage-2/chip-build-linux-qemu/files/wifi/dnsmasq.conf new file mode 100644 index 00000000000000..a8ea3c69ce29ed --- /dev/null +++ b/integrations/docker/images/stage-2/chip-build-linux-qemu/files/wifi/dnsmasq.conf @@ -0,0 +1,5 @@ +port=0 +interface=wlan1 +listen-address=192.168.200.1 +dhcp-range=192.168.200.10,192.168.200.200 + diff --git a/integrations/docker/images/stage-2/chip-build-linux-qemu/files/wifi/hostapd.conf b/integrations/docker/images/stage-2/chip-build-linux-qemu/files/wifi/hostapd.conf new file mode 100644 index 00000000000000..cab96f219f9b53 --- /dev/null +++ b/integrations/docker/images/stage-2/chip-build-linux-qemu/files/wifi/hostapd.conf @@ -0,0 +1,13 @@ +interface=wlan1 +driver=nl80211 +country_code=DE +ssid=Virtual_Wifi +channel=1 +hw_mode=g +wpa=3 +wpa_key_mgmt=WPA-PSK +wpa_pairwise=TKIP CCMP +wpa_passphrase=ExamplePassword +auth_algs=3 +beacon_int=100 + diff --git a/integrations/docker/images/stage-2/chip-build-linux-qemu/files/wifi/wpa_supplicant.conf b/integrations/docker/images/stage-2/chip-build-linux-qemu/files/wifi/wpa_supplicant.conf new file mode 100644 index 00000000000000..38673fea93fc06 --- /dev/null +++ b/integrations/docker/images/stage-2/chip-build-linux-qemu/files/wifi/wpa_supplicant.conf @@ -0,0 +1,3 @@ +ctrl_interface=DIR=/run/wpa_supplicant +ctrl_interface_group=root +update_config=1 diff --git a/integrations/docker/images/stage-2/chip-build-linux-qemu/run-img.sh b/integrations/docker/images/stage-2/chip-build-linux-qemu/run-img.sh new file mode 100755 index 00000000000000..43cc4f80a9effa --- /dev/null +++ b/integrations/docker/images/stage-2/chip-build-linux-qemu/run-img.sh @@ -0,0 +1,30 @@ +#!/bin/bash + +KERNEL="/opt/ubuntu-qemu/bzImage" +IMG="/opt/ubuntu-qemu/ubuntu-20.04.img" +ADDITIONAL_ARGS=() +PROJECT_PATH="$(realpath "$(dirname "$0")/../../../../..")" + +if kvm-ok; then + echo "KVM is available, running with it." + ADDITIONAL_ARGS+=("-enable-kvm" "-cpu" "host") +else + echo "KVM is not available, running without it." +fi + +/usr/bin/qemu-system-x86_64 \ + -machine ubuntu \ + -smp 4 \ + -m 2048 \ + -nographic \ + -monitor null \ + -serial stdio \ + -display none \ + -device virtio-blk-pci,drive=virtio-blk1 \ + -drive file="$IMG",id=virtio-blk1,if=none,format=qcow2,readonly=off \ + -kernel "$KERNEL" \ + -append 'console=ttyS0 mac80211_hwsim.radios=2 root=/dev/vda3' \ + -netdev user,id=network0,hostfwd=tcp::2222-:22 \ + -device e1000,netdev=network0,mac=52:54:00:12:34:56 \ + -virtfs "local,path=$PROJECT_PATH,mount_tag=host0,security_model=passthrough,id=host0" \ + "${ADDITIONAL_ARGS[@]}" diff --git a/integrations/docker/images/stage-2/chip-build-linux-qemu/run.sh b/integrations/docker/images/stage-2/chip-build-linux-qemu/run.sh new file mode 120000 index 00000000000000..9bbfad86d46e50 --- /dev/null +++ b/integrations/docker/images/stage-2/chip-build-linux-qemu/run.sh @@ -0,0 +1 @@ +../../../run.sh \ No newline at end of file diff --git a/integrations/docker/images/stage-2/chip-build-linux-qemu/version b/integrations/docker/images/stage-2/chip-build-linux-qemu/version new file mode 120000 index 00000000000000..a40ba48b0188a8 --- /dev/null +++ b/integrations/docker/images/stage-2/chip-build-linux-qemu/version @@ -0,0 +1 @@ +../../base/chip-build/version \ No newline at end of file