aboutsummaryrefslogtreecommitdiffstats
path: root/classes/qemuboot-xen-dtb.bbclass
blob: 1f17a8699ec5d800ef7146170850d263f6a76272 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
# Enable booting Xen with qemuboot / runqemu: generate device tree
#
# Copyright (c) 2021 Star Lab Corp. All rights reserved.
#
# Author: Christopher Clark <christopher.clark@starlab.io>

# Interface variables:
#
# QB_DTB : defined in qemuboot.bbclass.
# If set, this class will generate the specified device tree file.
#
# QB_XEN_CMDLINE_EXTRA :
# A string to be appended to the default Xen hypervisor boot command line,
# for supplying Xen boot options.
# The device tree that this bbclass generates will contain Xen command
# line options to connect the Xen console to the Qemu serial port.
#
# QB_XEN_DOMAIN_MODULES :
# A space-separated list of colon-separated entries:
#   "<file for the module>:<load memory address>:<module compatibility string>"
#
# QB_XEN_DOM0_BOOTARGS :
# A string for specifying Dom0 boot options for the Xen section of the device
# tree.
#
# QB_XEN_DTB_TASK_DEPENDS :
# The task dependencies for the DTB generation. A default is provided.
#
# See also: Other QB_ variables as defined by the qemuboot.bbclass.

write_lops_xen_section() {
    DOM0_BOOTARGS="$2"
    cat <<EOF >"$1"
/dts-v1/;
/ {
    compatible = "system-device-tree-v1";
    lops {
        /* Connect the Xen console to Qemu dtuart */
        lop_1 {
            compatible = "system-device-tree-v1,lop,code-v1";
            code = "
                chosen = node.tree['/chosen']
                stdout_path = str(chosen['stdout-path'].value[0])
                chosen['xen,xen-bootargs'] = \
                        'console=dtuart dtuart=%s' % stdout_path
                return True
            ";
        };
        lop_2 {
            compatible = "system-device-tree-v1,lop,modify";
            modify = "/chosen:xen,dom0-bootargs:${DOM0_BOOTARGS}";
        };
        lop_3 {
            compatible = "system-device-tree-v1,lop,modify";
            modify = "/chosen:#address-cells:<1>";
        };
        lop_4 {
            compatible = "system-device-tree-v1,lop,modify";
            modify = "/chosen:#size-cells:<1>";
        };
     };
};
EOF
}

write_lop_rm_pci() {
    cat <<EOF >"$1"
/dts-v1/;
/ {
    compatible = "system-device-tree-v1";
    lops {
        lop_1 {
            compatible = "system-device-tree-v1,lop,modify";
            modify = "/pcie@10000000::";
        };
     };
};
EOF
}

write_lop_add_to_xen_cmdline() {
    EXTRA_XEN_BOOTARGS="$2"
    cat <<EOF >"$1"
/dts-v1/;
/ {
    compatible = "system-device-tree-v1";
    lops {
        lop_1 {
            compatible = "system-device-tree-v1,lop,code-v1";
            options = "extra_xen_bootargs:${EXTRA_XEN_BOOTARGS}";
            code = "
                chosen = node.tree['/chosen']
                xen_bootargs = str(chosen['xen,xen-bootargs'].value)
                chosen['xen,xen-bootargs'] = '%s %s' % \
                     (xen_bootargs, extra_xen_bootargs)
                return True
            ";
        };
     };
};
EOF
}

write_lop_add_chosen_module() {
    ADDR="$2"
    SIZE="$3"
    MODULE_TYPE="$4"
    cat <<EOF >"$1"
/dts-v1/;
/ {
    compatible = "system-device-tree-v1";
    lops {
        lop_1 {
            compatible = "system-device-tree-v1,lop,add";
            node_src = "module@${ADDR}";
            node_dest = "/chosen/module@${ADDR}";

            module@${ADDR} {
                compatible = "multiboot,module", "${MODULE_TYPE}";
                reg = <${ADDR} ${SIZE}>;
            };
        };
     };
};
EOF
}

generate_xen_qemuboot_dtb() {
    # First: invoke qemu to generate an initial device tree.
    # Parameters supplied here inspired by inspection of:
    #   runqemu "${IMAGE_BASENAME}" nographic slirp \
    #            qemuparams='-dtb "" -machine dumpdtb=${B}/qemu-dumped.dtb'
    ${QB_SYSTEM_NAME} \
        -device qemu-xhci \
        -device usb-tablet \
        -device usb-kbd \
        ${QB_MACHINE} \
        ${QB_CPU} \
        ${QB_SMP} \
        ${QB_MEM} \
        -nographic \
        -serial mon:stdio \
        -machine "dumpdtb=${B}/qemu-dumped.dtb"

    # Lopper generates temporary files in cwd, so run it within ${B}
    cd "${B}"
    write_lops_xen_section "${B}/lop-insert-xen-section.dts" \
        "${QB_XEN_DOM0_BOOTARGS}"

    write_lop_add_to_xen_cmdline "${B}/lop-xen-cmdline.dts" \
        "${QB_XEN_CMDLINE_EXTRA}"

    # On Qemu Arm32, Dom0 accessing PCI config space ends up in an
    # infinite loop. Remove pci node from the device tree
    if [ "${MACHINE}" = "qemuarm" ]; then
        write_lop_rm_pci "${B}/lop-rm-pci.dts"
        LOP_MODULE_ARGS="${LOP_MODULE_ARGS} -i ${B}/lop-rm-pci.dts"
    fi

    if [ -z "${QB_XEN_DOMAIN_MODULES}" ]; then
        bbwarn "No domain modules: please set QB_XEN_DOMAIN_MODULES"
    fi

    for DOMAIN_MODULE in ${QB_XEN_DOMAIN_MODULES}
    do
        MODULE_FILE="$(echo ${DOMAIN_MODULE} | cut -f1 -d:)"
        ADDR="$(echo ${DOMAIN_MODULE} | cut -f2 -d:)"
        MODULE_TYPE="$(echo ${DOMAIN_MODULE} | cut -f3 -d:)"
        RESOLVED_FILE="$(readlink -f ${MODULE_FILE})"
        SIZE=$(printf '0x%x\n' $(stat -c '%s' "${RESOLVED_FILE}"))
        [ "x${SIZE}" != "x0x0" ] || bbfatal No module: "${MODULE_FILE}"
        write_lop_add_chosen_module "${B}/lop-add-module-${ADDR}.dts" \
            "${ADDR}" "${SIZE}" "${MODULE_TYPE}"
        LOP_MODULE_ARGS="${LOP_MODULE_ARGS} -i ${B}/lop-add-module-${ADDR}.dts"
    done

    QEMUBOOT_DTB="${IMGDEPLOYDIR}/${QB_DTB}"
    QEMUBOOT_DTB_LINK="${IMGDEPLOYDIR}/${QB_DTB_LINK}"

    lopper --werror --verbose \
        -i "${B}/lop-insert-xen-section.dts" \
        -i "${B}/lop-xen-cmdline.dts" \
        ${LOP_MODULE_ARGS} \
        -f -o "${QEMUBOOT_DTB}" \
            "${B}/qemu-dumped.dtb"

    # To assist debugging:
    dtc -I dtb -O dts -o "${B}/output.dts" "${QEMUBOOT_DTB}"

    if [ "${QEMUBOOT_DTB_LINK}" != "${QEMUBOOT_DTB}" ] ; then
        if [ -e "${QEMUBOOT_DTB_LINK}" ] ; then
            rm "${QEMUBOOT_DTB_LINK}"
        fi
        ln -s "${QB_DTB}" "${QEMUBOOT_DTB_LINK}"
    fi
}

do_write_xen_qemuboot_dtb() {
    # Not all architectures qemuboot with a device tree binary, so check
    # to see if one is needed. This allows this bbclass file to be used
    # in the same image recipe for multiple architectures.
    if [ -n "${QB_DTB}" ] && [ -n "${QB_SYSTEM_NAME}" ] ; then
        generate_xen_qemuboot_dtb
    fi
}

addtask do_write_xen_qemuboot_dtb after do_write_qemuboot_conf before do_image
# Task dependency:
# An expected common case is that the kernel for at least one of the initial
# domains (eg. dom0) is deployed from the virtual/kernel recipe, so
# add virtual/kernel:do_deploy as a task dependency here since the kernel size
# needs to be known for generating the device tree.
# Dependencies are only introduced if a device tree will be generated.
QB_XEN_DTB_TASK_DEPENDS ?= " \
    ${@[ ' \
        qemu-helper-native:do_populate_sysroot \
        lopper-native:do_populate_sysroot \
        dtc-native:do_populate_sysroot \
        virtual/kernel:do_deploy \
    ', ''][d.getVar('QB_DTB') == '' or d.getVar('QB_DTB') is None]} \
    "
do_write_xen_qemuboot_dtb[depends] = "${QB_XEN_DTB_TASK_DEPENDS}"

def qemuboot_dtb_vars(d):
    build_vars = ['MACHINE', 'TUNE_ARCH', 'DEPLOY_DIR_IMAGE',
                'KERNEL_IMAGETYPE', 'IMAGE_NAME', 'IMAGE_LINK_NAME',
                'STAGING_DIR_NATIVE', 'STAGING_BINDIR_NATIVE',
                'STAGING_DIR_HOST', 'SERIAL_CONSOLES']
    return build_vars + [k for k in d.keys() if k.startswith('QB_')]

do_write_qemuboot_dtb[vardeps] += "${@' '.join(qemuboot_dtb_vars(d))}"
do_write_qemuboot_dtb[vardepsexclude] += "TOPDIR"