1#!/bin/bash
2# Copyright (C) 2020-2022 Intel Corporation.
3# SPDX-License-Identifier: BSD-3-Clause
4
5build_dir="$PWD/build"
6cloud_image="${build_dir}/jammy-server-cloudimg-amd64.img"
7cloud_image_url=https://cloud-images.ubuntu.com/jammy/current/jammy-server-cloudimg-amd64.img
8hmi_vm_image="${build_dir}/hmi_vm.img"
9rt_vm_image="${build_dir}/rt_vm.img"
10rt_kernel=(linux-libc linux-headers linux-image)
11
12vm_type=$1
13if [[ ${vm_type} != "rt-vm" ]] && [[ ${vm_type} != "hmi-vm" ]]; then
14    cat <<EOT
15Usage: $0 <vm_type>
16This script creates VM images based on Ubuntu cloud images.
17
18VM type options:
19  hmi-vm        create a VM with GNOME desktop
20  rt-vm         create a VM with a preempt-RT-patched kernel and real-time test utilities
21EOT
22    exit 1
23fi
24
25########################################
26# Environment checks
27########################################
28
29if [[ ! -d /etc/schroot/chroot.d ]]; then
30    echo "Package schroot is not installed."
31    exit 1
32fi
33
34if [[ ! -d $(dirname $PWD)/build ]]; then
35    make -C $(dirname $PWD)
36fi
37
38arr=("$PWD/mnt" "$PWD/build")
39for dir in "${arr[@]}"; do
40    if [[ ! -d "$dir" ]]; then
41        mkdir $dir
42    fi
43done
44
45########################################
46# Helper functions
47########################################
48
49source logger.sh
50
51########################################
52# Actions defined as functions
53########################################
54
55function copy_rt_kernel() {
56    for file in ~/acrn-work/*rtvm*.deb
57    do
58        if [[ ${file} != *"dbg"* ]]; then
59            cp ${file} ${build_dir}
60       fi
61    done
62}
63
64function check_rt_kernel() {
65    for file in ${rt_kernel[@]}
66    do
67        ls ${build_dir}/*.deb | grep ${file}
68        if [ $? -eq 1 ]; then
69            echo "RT VM kernel package ${file} is not found."
70            exit
71        fi
72    done
73}
74
75function download_image() {
76    local dest=$1
77    local url=$2
78
79    if [[ -f ${dest} ]]; then
80        print_info "${dest} already exists. Do not redownload."
81    else
82        wget -O ${dest} ${url}
83    fi
84}
85
86function copy_and_enlarge_image() {
87    local source_image=$1
88    local dest_image=$2
89    local size_modifier=$3
90
91    if [[ -f ${dest_image} ]]; then
92        echo -n "${dest_image} already exists! Regenerate the image? (y/N)> "
93        read answer
94        if [[ $answer =~ ^[Yy]$ ]]; then
95            rm ${dest_image}
96        else
97            exit 1
98        fi
99    fi
100
101    qemu-img convert -f qcow2 -O raw ${source_image} ${dest_image} && \
102        qemu-img resize -f raw ${dest_image} ${size_modifier} && \
103        LANG=C growpart ${dest_image} 1
104}
105
106function dump_proxy() {
107	local temp_file=$(mktemp /tmp/proxy.XXXX)
108
109	sudo apt-config dump | grep -i proxy > ${temp_file} 2>&1
110	sudo mv ${temp_file} proxy.conf
111
112	echo "$(env | grep -Ei _proxy | sed -e 's/^/export /')" > bashrc
113}
114
115function resizing_guest_root() {
116    local part_file=$1
117
118    sudo e2fsck -f ${part_file} && \
119        sudo resize2fs ${part_file}
120}
121
122function mount_filesystem() {
123    local part_file=$1
124    local mount_point=$2
125
126    # The symlink /etc/resolv.conf in a fresh cloud image is broken, which will
127    # prevent schroot from working. Touch that linked file to work it around.
128    mkdir -p ${mount_point} && \
129        sudo mount ${part_file} ${mount_point} && \
130        sudo mkdir -p ${mount_point}/run/systemd/resolve/ && \
131        sudo touch ${mount_point}/run/systemd/resolve/stub-resolv.conf
132}
133
134function create_schroot_config() {
135    local mount_point=$1
136    local temp_file=$(mktemp /tmp/acrn-guest.XXXX)
137
138    cat << EOF > ${temp_file}
139[acrn-guest]
140description=Contains ACRN guest root file system.
141type=directory
142directory=${mount_point}
143users=root
144root-groups=root
145profile=desktop
146personality=linux
147preserve-environment=true
148EOF
149
150    sudo sed -ie "/passwd/d;/shadow/d;/group/d;/gshadow/d" \
151        /etc/schroot/desktop/nssdatabases && \
152    sudo mv ${temp_file} /etc/schroot/chroot.d/acrn-guest && \
153        sudo chown root:root /etc/schroot/chroot.d/acrn-guest
154}
155
156function create_uio_config() {
157    local mount_point=$1
158    local temp_file=$(mktemp /tmp/rc.local.XXXX)
159
160    cat << EOF > ${temp_file}
161#!/bin/bash
162modprobe uio
163modprobe uio_pci_generic
164
165for i in {0..2}
166do
167bash -c 'echo "1af4 1110" > /sys/bus/pci/drivers/uio_pci_generic/new_id'
168if [ $? -eq 0 ]; then
169    echo "uio setting result" $?
170    break
171fi
172echo "uio setting result" $? "try again"
173sleep 1
174done
175EOF
176
177    sudo mv ${temp_file} $mount_point/etc/rc.local && \
178        sudo chown root:root $mount_point/etc/rc.local && \
179	sudo chmod 755 $mount_point/etc/rc.local
180}
181
182function setup_hmi_vm_rootfs() {
183    local mount_point=$1
184
185    sudo cp setup_hmi_vm.sh logger.sh ${mount_point}/ && \
186	sudo cp $(dirname $PWD)/build/userApp $(dirname $PWD)/build/histapp.py ${mount_point}/root && \
187	sudo cp proxy.conf ${mount_point}/etc/apt/apt.conf.d/proxy.conf && \
188	sudo cp bashrc ${mount_point}/root/.bashrc && \
189        sudo schroot -c acrn-guest bash /setup_hmi_vm.sh && \
190        sudo rm ${mount_point}/setup_hmi_vm.sh ${mount_point}/logger.sh && \
191	sudo rm bashrc proxy.conf
192}
193
194function setup_rt_vm_rootfs() {
195    local mount_point=$1
196
197    sudo mv ${build_dir}/*rtvm*.deb ${mount_point}/root && \
198	sudo cp $(dirname $PWD)/build/rtApp ${mount_point}/root && \
199        sudo mkdir ${mount_point}/root/scripts && \
200	sudo cp proxy.conf ${mount_point}/etc/apt/apt.conf.d/proxy.conf && \
201	sudo cp bashrc ${mount_point}/root/.bashrc && \
202        sudo cp configRTcores.sh ${mount_point}/root/scripts/ && \
203        sudo cp setup_rt_vm.sh logger.sh ${mount_point}/ && \
204        sudo schroot -c acrn-guest bash /setup_rt_vm.sh && \
205        sudo rm ${mount_point}/setup_rt_vm.sh ${mount_point}/logger.sh && \
206        sudo rm bashrc proxy.conf
207}
208
209function cleanup() {
210    local mount_point=$1
211    local loop_dev=$2
212
213    sudo umount ${mount_point}
214    sudo rmdir ${mount_point}
215    sudo kpartx -vd /dev/${loop_dev}
216    sudo losetup -vd /dev/${loop_dev}
217    if [[ ${has_error} != 0 ]]; then
218        sudo rm ${target_image}
219    fi
220    true
221}
222
223########################################
224# Do it!
225########################################
226
227mount_point=$(pwd)/mnt
228if [[ ${vm_type} == "hmi-vm" ]]; then
229    target_image=${hmi_vm_image}
230    size_modifier="+7G"
231elif [[ ${vm_type} == "rt-vm" ]]; then
232    target_image=${rt_vm_image}
233    size_modifier="+1G"
234else
235    echo "Internal error: undefined VM type '${vm_type}'"
236    exit 1
237fi
238
239try_step "Download Ubuntu cloud image" download_image ${cloud_image} ${cloud_image_url}
240if [[ ${vm_type} == "rt-vm" ]]; then
241    try_step "Copy the RT kernel to build directory" copy_rt_kernel
242    try_step "Check availability of RT kernel image" check_rt_kernel
243fi
244try_step "Creating an enlarged copy of ${cloud_image}" copy_and_enlarge_image ${cloud_image} ${target_image} ${size_modifier}
245
246loop_dev=$(sudo kpartx -va ${target_image} 2>&1 | egrep -o -m 1 "loop[0-9]+")
247print_info "Guest image loop-mounted at /dev/${loop_dev}"
248
249try_step "Resizing guest root file system" resizing_guest_root /dev/mapper/${loop_dev}p1
250try_step "Mounting guest root file system at ${mount_point}" mount_filesystem /dev/mapper/${loop_dev}p1 ${mount_point}
251try_step "Preparing schroot configuration" create_schroot_config ${mount_point}
252try_step "Preparing uio configuration" create_uio_config ${mount_point}
253try_step "Extracting network proxy configurations" dump_proxy
254
255if [[ ${vm_type} == "hmi-vm" ]]; then
256    try_step "Initializing guest root file system for HMI VM" setup_hmi_vm_rootfs ${mount_point}
257else
258    try_step "Initializing guest root file system for RT VM" setup_rt_vm_rootfs ${mount_point}
259fi
260
261do_step  "Cleaning up" cleanup ${mount_point} ${loop_dev}
262print_info "VM image created at ${target_image}."
263
264