summaryrefslogtreecommitdiffstats
path: root/meta-xilinx-core/classes-recipe
diff options
context:
space:
mode:
Diffstat (limited to 'meta-xilinx-core/classes-recipe')
-rw-r--r--meta-xilinx-core/classes-recipe/amd_spi_image.bbclass142
-rw-r--r--meta-xilinx-core/classes-recipe/dfx_user_dts.bbclass320
-rw-r--r--meta-xilinx-core/classes-recipe/fw-package.bbclass94
-rw-r--r--meta-xilinx-core/classes-recipe/image-wic-utils.bbclass54
-rw-r--r--meta-xilinx-core/classes-recipe/kernel-simpleimage.bbclass35
-rw-r--r--meta-xilinx-core/classes-recipe/qemuboot-xilinx.bbclass140
-rw-r--r--meta-xilinx-core/classes-recipe/xilinx-fetch-restricted.bbclass35
-rw-r--r--meta-xilinx-core/classes-recipe/xilinx-platform-init.bbclass14
8 files changed, 834 insertions, 0 deletions
diff --git a/meta-xilinx-core/classes-recipe/amd_spi_image.bbclass b/meta-xilinx-core/classes-recipe/amd_spi_image.bbclass
new file mode 100644
index 00000000..ed4c1f87
--- /dev/null
+++ b/meta-xilinx-core/classes-recipe/amd_spi_image.bbclass
@@ -0,0 +1,142 @@
1#
2# Copyright (C) 2023, Advanced Micro Devices, Inc. All rights reserved.
3#
4# SPDX-License-Identifier: MIT
5#
6
7QSPI_SIZE ?= "0x2280000"
8
9# Register values
10IDN_REG ?= "0x4D554241"
11VERSION_REG ?= "0x1"
12LENGTH_REG ?= "0x4"
13PERSISTENT_REG ?= "0x01010000"
14
15# QSPI Offsets
16IMAGE_SELECTOR_OFFSET ?= "0x0"
17IMAGE_SELECTOR_BACKUP_OFFSET ?= "0x80000"
18PERSISTENT_REG_OFFSET ?= "0x100000"
19PERSISTENT_REG_BACKUP_OFFSET ?= "0x120000"
20IMAGE_A_OFFSET ?= "0x200000"
21IMAGE_A_IMGSEL_OFFSET ?= "0xF00000"
22IMAGE_B_OFFSET ?= "0xF80000"
23IMAGE_B_IMGSEL_OFFSET ?= "0x1C80000"
24IMAGE_RCVRY_OFFSET ?= "0x1E00000"
25IMAGE_RCVRY_BACKUP_OFFSET ?= "0x2000000"
26VERSION_OFFSET ?= "0x2240000"
27CHECKSUM_OFFSET ?= "0x2250000"
28
29def generate_spi_image(d):
30
31 import io
32 import hashlib
33 import time
34
35 qspi_size = int(d.getVar("QSPI_SIZE") or '0', 0)
36 int(d.getVar("QSPI_SIZE") or '0', 0)
37
38 # Register values
39 idn_reg = int(d.getVar("IDN_REG") or '0', 0)
40 version_reg = int(d.getVar("VERSION_REG") or '0', 0)
41 length_reg = int(d.getVar("LENGTH_REG") or '0', 0)
42 persistent_reg = int(d.getVar("PERSISTENT_REG") or '0', 0)
43
44 # QSPI Offsets
45 image_selector_offset = int(d.getVar("IMAGE_SELECTOR_OFFSET") or '0', 0)
46 image_selector_backup_offset = int(d.getVar("IMAGE_SELECTOR_BACKUP_OFFSET") or '0', 0)
47 persistent_reg_offset = int(d.getVar("PERSISTENT_REG_OFFSET") or '0', 0)
48 persistent_reg_backup_offset = int(d.getVar("PERSISTENT_REG_BACKUP_OFFSET") or '0', 0)
49 image_a_offset = int(d.getVar("IMAGE_A_OFFSET") or '0', 0)
50 image_a_imgsel_offset = int(d.getVar("IMAGE_A_IMGSEL_OFFSET") or '0', 0)
51 image_b_offset = int(d.getVar("IMAGE_B_OFFSET") or '0', 0)
52 image_b_imgsel_offset = int(d.getVar("IMAGE_B_IMGSEL_OFFSET") or '0', 0)
53 image_rcvry_offset = int(d.getVar("IMAGE_RCVRY_OFFSET") or '0', 0)
54 image_rcvry_backup_offset = int(d.getVar("IMAGE_RCVRY_BACKUP_OFFSET") or '0', 0)
55 version_offset = int(d.getVar("VERSION_OFFSET") or '0', 0)
56 checksum_offset = int(d.getVar("CHECKSUM_OFFSET") or '0', 0)
57
58 # QSPI data
59 qspi_data = io.BytesIO()
60 qspi_data.write(b'\xFF' * qspi_size)
61
62 # Image Selector - Primary, Backup, Image A and Image B
63 imgsel_file = d.getVar("DEPLOY_DIR_IMAGE")+"/imgsel-"+d.getVar("MACHINE")+".bin"
64 try:
65 with open(imgsel_file, "rb") as il:
66 imgsel = il.read(-1)
67 except OSError as err:
68 bb.fatal("Unable to open imgsel file: " + str(err))
69
70 qspi_data.seek(image_selector_offset)
71 qspi_data.write(imgsel)
72 qspi_data.seek(image_selector_backup_offset)
73 qspi_data.write(imgsel)
74 qspi_data.seek(image_a_imgsel_offset)
75 qspi_data.write(imgsel)
76 qspi_data.seek(image_b_imgsel_offset)
77 qspi_data.write(imgsel)
78
79 # Persistent Registers - Primary and Backup
80 p_reg = [idn_reg, version_reg, length_reg, persistent_reg, \
81 image_a_offset, image_b_offset, image_rcvry_offset]
82 checksum = 0xffffffff - (0xffffffff & sum(p_reg))
83 p_reg.insert(3, checksum)
84
85 qspi_data.seek(persistent_reg_offset)
86 for value in p_reg:
87 qspi_data.write(value.to_bytes(4, byteorder="little"))
88
89 qspi_data.seek(persistent_reg_backup_offset)
90 for value in p_reg:
91 qspi_data.write(value.to_bytes(4, byteorder="little"))
92
93 # Image A and B - boot.bin
94 try:
95 with open(d.getVar("DEPLOY_DIR_IMAGE")+"/boot.bin", "rb") as bo:
96 bootbin = bo.read(-1)
97 except OSError as err:
98 bb.fatal("Unable to open boot.bin file: " + str(err))
99
100 qspi_data.seek(image_a_offset)
101 qspi_data.write(bootbin)
102 qspi_data.seek(image_b_offset)
103 qspi_data.write(bootbin)
104
105 # Recovery Image & Recovery Image Backup
106 imgrcry_file = d.getVar("DEPLOY_DIR_IMAGE")+"/imgrcry-"+d.getVar("MACHINE")+".bin"
107 try:
108 with open(imgrcry_file, "rb") as iy:
109 imgrcry = iy.read(-1)
110 except OSError as err:
111 bb.fatal("Unable to open imgrcry file: " + str(err))
112
113 qspi_data.seek(image_rcvry_offset)
114 qspi_data.write(imgrcry)
115 qspi_data.seek(image_rcvry_backup_offset)
116 qspi_data.write(imgrcry)
117
118 # Version string and checksum
119 version = d.getVar("QSPI_IMAGE_VERSION")
120 date = time.strftime("%m%d%H%M")
121 machine = d.getVar("MACHINE")[:3]
122 image_name = d.getVar("QSPI_IMAGE_NAME")
123
124 qspi_version = f"{image_name}-{machine}-v{version}-{date}\x00"
125 qspi_data.seek(version_offset)
126 qspi_data.write(qspi_version.encode())
127
128 qspi_sha = hashlib.sha256(qspi_data.getbuffer())
129 qspi_data.seek(checksum_offset)
130 qspi_data.write(qspi_sha.digest())
131
132 # Write the QSPI data to file
133 with open(d.getVar("B") + "/" + d.getVar("IMAGE_NAME") + ".bin", "wb") as sq:
134 sq.write(qspi_data.getbuffer())
135
136do_compile[depends] += "virtual/boot-bin:do_deploy virtual/imgsel:do_deploy virtual/imgrcry:do_deploy"
137
138python amd_spi_image_do_compile() {
139 generate_spi_image(d)
140}
141
142EXPORT_FUNCTIONS do_compile
diff --git a/meta-xilinx-core/classes-recipe/dfx_user_dts.bbclass b/meta-xilinx-core/classes-recipe/dfx_user_dts.bbclass
new file mode 100644
index 00000000..188d594b
--- /dev/null
+++ b/meta-xilinx-core/classes-recipe/dfx_user_dts.bbclass
@@ -0,0 +1,320 @@
1#
2# Copyright (C) 2023-2024, Advanced Micro Devices, Inc. All rights reserved.
3#
4# SPDX-License-Identifier: MIT
5#
6# This bbclass is inherited by flat, DFx Static and DFx RP firmware recipes.
7# dfx_user_dts.bbclass expects user to generate pl dtsi for flat, DFx Static
8# and DFx RP xsa outside of yocto.
9
10inherit devicetree
11
12DEPENDS = "dtc-native bootgen-native"
13
14# recipes that inherit from this class need to use an appropriate machine
15# override for COMPATIBLE_MACHINE to build successfully; don't allow building
16# for microblaze MACHINE
17COMPATIBLE_MACHINE ?= "^$"
18COMPATIBLE_MACHINE:microblaze = "^$"
19
20PACKAGE_ARCH = "${MACHINE_ARCH}"
21
22PROVIDES = ""
23
24do_fetch[cleandirs] = "${B}"
25
26DT_PADDING_SIZE = "0x1000"
27BOOTGEN_FLAGS ?= " -arch ${SOC_FAMILY} -w ${@bb.utils.contains('SOC_FAMILY','zynqmp','','-process_bitstream bin',d)}"
28
29S ?= "${WORKDIR}"
30FW_DIR ?= ""
31DTSI_PATH ?= ""
32DTBO_PATH ?= ""
33BIT_PATH ?= ""
34BIN_PATH ?= ""
35PDI_PATH ?= ""
36JSON_PATH ?= ""
37XCl_PATH ?= ""
38DT_FILES_PATH = "${S}/${DTSI_PATH}"
39FIRMWARE_NAME_DT_FILE ?= ""
40USER_DTS_FILE ?= ""
41
42FIRMWARE_NAME_DT_FILE[doc] = "DT file which has firmware-name device-tree property"
43USER_DTS_FILE[doc] = "Final DTSI or DTS file which is used for packaging final DT overlay"
44DTSI_PATH[doc] = "Absolute '.dtsi' or ''.dts' file path as input to SRC_URI"
45DTBO_PATH[doc] = "Absolute '.dtbo' file path as input to SRC_URI"
46BIT_PATH[doc] = "Absolute '.bit' file path as input to SRC_URI"
47BIN_PATH[doc] = "Absolute '.bin' file path as input to SRC_URI"
48JSON_PATH[doc] = "Absolute '.json' file path as input to SRC_URI"
49XCL_PATH[doc] = "Absolute '.xclbin' file path as input to SRC_URI"
50
51python() {
52 import re
53 soc_family = d.getVar("SOC_FAMILY")
54 if "git://" in d.getVar("SRC_URI") or "https://" in d.getVar("SRC_URI"):
55 d.setVar("S",'${WORKDIR}/git/'+d.getVar("FW_DIR"))
56 else:
57 dtsi_found = False
58 dtbo_found = False
59 bit_found = False
60 bin_found = False
61 pdi_found = False
62
63 # Required Inputs
64 for s in d.getVar("SRC_URI").split():
65 if s.endswith(('.dtsi', '.dts')):
66 dtsi_found = True
67 d.setVar("DTSI_PATH",os.path.dirname(s.lstrip('file://')))
68 if s.endswith('.dtbo'):
69 if dtbo_found:
70 bb.warn("More then one '.dtbo' file specified in SRC_URI.")
71 dtbo_found = True
72 d.setVar("DTBO_PATH",os.path.dirname(s.lstrip('file://')))
73 if soc_family == "zynq" or soc_family == "zynqmp":
74 if s.endswith('.bit'):
75 if bit_found:
76 bb.warn("More then one '.bit' file specified in SRC_URI.")
77 bit_found = True
78 d.setVar("BIT_PATH",os.path.dirname(s.lstrip('file://')))
79 if s.endswith('.bin'):
80 if bin_found:
81 bb.warn("More then one '.bin' file specified in SRC_URI.")
82 bin_found = True
83 d.setVar("BIN_PATH",os.path.dirname(s.lstrip('file://')))
84 else:
85 if s.endswith('.pdi'):
86 if pdi_found:
87 bb.warn("More then one '.pdi' file specified in SRC_URI.")
88 pdi_found = True
89 d.setVar("PDI_PATH",os.path.dirname(s.lstrip('file://')))
90
91 # Optional input
92 if s.endswith('.json'):
93 d.setVar("JSON_PATH",os.path.dirname(s.lstrip('file://')))
94
95 if s.endswith('.xclbin'):
96 d.setVar("XCL_PATH",os.path.dirname(s.lstrip('file://')))
97
98 # Check for valid combination of input files in SRC_URI
99 # Skip recipe if any of the below conditions are not satisfied.
100 # 1. At least one bit or bin or pdi should exists.
101 # 2. More than one dtbo.
102 # 3. More than one bit or bin or pdi.
103 # 4. More than one dts and zero dtsi.
104 # 5. More than one dtsi and zero dts
105 # 6. Both bit and bin exists.
106 # 7. Both bit or bin and pdi exits.
107 # 8. Both dts or dtsi and dtbo exists.
108 if bit_found or bin_found or pdi_found:
109 bb.debug(2, "dtsi or dtbo or bitstream or pdi found in SRC_URI")
110 if bit_found and pdi_found :
111 raise bb.parse.SkipRecipe("Both '.bit' and '.pdi' file found in SRC_URI, this is invalid use case.")
112
113 if bin_found and pdi_found :
114 raise bb.parse.SkipRecipe("Both '.bin' and '.pdi' file found in SRC_URI, this is invalid use case.")
115
116 if bit_found and bin_found:
117 raise bb.parse.SkipRecipe("Both '.bit' and '.bin' file found in SRC_URI, either .bit or .bin file is supported but not both.")
118
119 if dtsi_found and dtbo_found:
120 raise bb.parse.SkipRecipe("Both '.dts or dtsi' and '.dtbo' file found in SRC_URI, either .dts/dtsi or .dtbo file is supported but not both.")
121 else:
122 raise bb.parse.SkipRecipe("Need one '.bit' or '.bin' or '.pdi' file added to SRC_URI.")
123
124 # Check for valid combination of dtsi and dts files in SRC_URI
125 # Following file combinations are not supported use case.
126 # 1. More than one '.dtsi' and zero '.dts' file.
127 # 2. More than one '.dts' and zero or more than one '.dtsi'file .
128 pattern_dts = re.compile(r'[.]+dts\b')
129 pattern_dtsi = re.compile(r'[.]+dtsi\b')
130 dts_count = len([*re.finditer(pattern_dts, d.getVar('SRC_URI'))])
131 dtsi_count = len([*re.finditer(pattern_dtsi, d.getVar("SRC_URI"))])
132
133 if dtsi_count > 1 and dts_count == 0:
134 raise bb.parse.SkipRecipe("Recipe has more than one '.dtsi' and zero '.dts' found, this is an unsupported use case")
135 elif dts_count > 1:
136 raise bb.parse.SkipRecipe("Recipe has more than one '.dts' and zero or more than one '.dtsi' found, this is an unsupported use case")
137}
138
139# Function to search for dt firmware-name property in dts or dtsi file.
140python find_firmware_file() {
141 import glob
142 pattern_fw = 'firmware-name'
143 search_count = 0
144 for dt_files in glob.iglob((d.getVar('S') + '/' + (d.getVar('DTSI_PATH')) + '/*.dts*'),recursive=True):
145 with open(dt_files, "r") as f:
146 current_fd = f.read()
147 if pattern_fw in current_fd:
148 search_count += 1
149 if search_count > 1:
150 bb.error("firmware-name dt property found in more than one dt files! Please fix the dts or dtsi file.")
151 break
152 else:
153 d.setVar('FIRMWARE_NAME_DT_FILE', os.path.basename(dt_files))
154}
155
156do_configure[prefuncs] += "find_firmware_file"
157
158python do_configure() {
159 import glob, re, shutil
160 soc_family = d.getVar("SOC_FAMILY")
161
162 if bb.utils.contains('MACHINE_FEATURES', 'fpga-overlay', False, True, d):
163 bb.warn("Using dfx_user_dts.bbclass requires fpga-overlay MACHINE_FEATURE to be enabled")
164
165 # Renaming firmware-name using $PN as bitstream/PDI will be renamed using
166 # $PN when generating the bin/pdi file.
167 if os.path.isfile(d.getVar('S') + '/' + (d.getVar('DTSI_PATH') or '') + '/' + d.getVar('FIRMWARE_NAME_DT_FILE')):
168 orig_dtsi = glob.glob(d.getVar('S') + '/' + (d.getVar('DTSI_PATH') or '') + '/' + d.getVar('FIRMWARE_NAME_DT_FILE'))[0]
169 new_dtsi = d.getVar('S') + '/' + (d.getVar('DTSI_PATH') or '') + '/pl.dtsi_firmwarename'
170 with open(new_dtsi, 'w') as newdtsi:
171 with open(orig_dtsi) as olddtsi:
172 for line in olddtsi:
173 if soc_family == 'zynq' or soc_family == 'zynqmp':
174 newdtsi.write(re.sub('firmware-name.*\".*\"','firmware-name = \"'+d.getVar('PN')+'.bin\"',line))
175 else:
176 newdtsi.write(re.sub('firmware-name.*\".*\"','firmware-name = \"'+d.getVar('PN')+'.pdi\"',line))
177 shutil.move(new_dtsi,orig_dtsi)
178}
179
180do_compile[prefuncs] += "find_firmware_file"
181
182python devicetree_do_compile:append() {
183 import glob, subprocess, shutil
184 soc_family = d.getVar("SOC_FAMILY")
185
186 dtbo_count = sum(1 for f in glob.iglob((d.getVar('S') + '/' + (d.getVar('DTSI_PATH') or '') + '/*.dtbo'),recursive=True) if os.path.isfile(f))
187 bin_count = sum(1 for f in glob.iglob((d.getVar('S') + '/' + (d.getVar('BIN_PATH') or '') + '/*.bin'),recursive=True) if os.path.isfile(f))
188 bit_count = sum(1 for f in glob.iglob((d.getVar('S') + '/' + (d.getVar('BIT_PATH') or '') + '/*.bit'),recursive=True) if os.path.isfile(f))
189 # Skip devicetree do_compile task if input file is dtbo or bin in SRC_URI
190 if not dtbo_count and not bin_count and bit_count:
191 # Convert .bit to .bin format only if dtsi is input.
192 # In case of dtbo as input, bbclass doesn't know if firmware-name is .bit
193 # or .bin format and corresponding file name. Hence we are not doing .bin
194 # conversion.
195 if soc_family == 'zynq' or soc_family == 'zynqmp' and glob.glob(d.getVar('S') + '/' +(d.getVar('DTSI_PATH') or '') + '/' + d.getVar('FIRMWARE_NAME_DT_FILE')):
196 pn = d.getVar('PN')
197 biffile = pn + '.bif'
198 with open(biffile, 'w') as f:
199 f.write('all:\n{\n\t' + glob.glob(d.getVar('S') + '/' + (d.getVar('BIT_PATH') or '') + '/*.bit')[0] + '\n}')
200
201 bootgenargs = ["bootgen"] + (d.getVar("BOOTGEN_FLAGS") or "").split()
202 bootgenargs += ["-image", biffile, "-o", pn + ".bin"]
203 subprocess.run(bootgenargs, check = True)
204
205 # In Zynq7k using both "-process_bitstream bin" and "-o" in bootgen flag,
206 # to convert bit file to bin format, "-o" option will not be effective
207 # and generated output file name is ${S}+${BIT_PATH}/<bit_file_name>.bin
208 # file, Hence we need to rename this file from <bit_file_name>.bin to
209 # ${PN}.bin which matches the firmware name in dtbo and move
210 # ${PN}.bin to ${B} directory.
211 if soc_family == 'zynq':
212 src_bitbin_file = glob.glob(d.getVar('S') + '/' + (d.getVar('BIT_PATH') or '') + '/*.bin')[0]
213 dst_bitbin_file = d.getVar('B') + '/' + pn + '.bin'
214 shutil.move(src_bitbin_file, dst_bitbin_file)
215
216 if not os.path.isfile(pn + ".bin"):
217 bb.fatal("Couldn't find %s file, Enable '-log trace' in BOOTGEN_FLAGS" \
218 "and check bootgen_log.txt" % (d.getVar('B') + '/' + pn + '.bin'))
219}
220
221# If user inputs both dtsi and dts files then device-tree will generate dtbo
222# files for each dt file, Hence to package the firmware pick the right user dt
223# overlay file.
224python find_user_dts_overlay_file() {
225 import glob
226 dtbo_count = sum(1 for f in glob.iglob((d.getVar('S') + '/' + d.getVar('DTBO_PATH') + '/*.dtbo'),recursive=True) if os.path.isfile(f))
227 dts_count = sum(1 for f in glob.iglob((d.getVar('S') + '/' + d.getVar('DTSI_PATH') + '/*.dts'),recursive=True) if os.path.isfile(f))
228 dtsi_count = sum(1 for f in glob.iglob((d.getVar('S') + '/' + d.getVar('DTSI_PATH') + '/*.dtsi'),recursive=True) if os.path.isfile(f))
229 # Set USER_DTS_FILE if input file is dts/dtsi in SRC_URI else skip operation.
230 if not dtbo_count and dts_count or dtsi_count:
231 if dtsi_count == 1 and dts_count == 0:
232 dts_file = glob.glob(d.getVar('S') + '/' + (d.getVar('DTSI_PATH') or '') + '/*.dtsi')[0]
233 elif dtsi_count >=0 and dts_count == 1:
234 dts_file = glob.glob(d.getVar('S') + '/' + (d.getVar('DTSI_PATH') or '') + '/*.dts')[0]
235 else:
236 dts_file = ''
237
238 d.setVar('USER_DTS_FILE', os.path.splitext(os.path.basename(dts_file))[0])
239 elif dtbo_count:
240 bb.debug(2, "Firmware recipe input file is dtbo in SRC_URI")
241 else:
242 bb.debug(2, "Firmware recipe input file is bit/bin/pdi in SRC_URI")
243}
244
245do_install[prefuncs] += "find_user_dts_overlay_file"
246
247do_install() {
248 install -d ${D}/${nonarch_base_libdir}/firmware/xilinx/${PN}/
249
250 # Install dtbo
251 # In case of dtbo as input, dtbo will be copied from directly from ${S}
252 # In case of dtsi as input, dtbo will be copied from directly from ${B}
253 # If more than one dtbo file is found then fatal the task.
254 # If no dtbo file is found add warning message as in some use case if IP
255 # doesn't have any driver then user can load pdi/bit/bin file.
256 if [ `ls ${S}/*.dtbo | wc -l` -eq 1 ]; then
257 install -Dm 0644 ${S}/*.dtbo ${D}/${nonarch_base_libdir}/firmware/xilinx/${PN}/
258 elif [ `ls ${S}/*.dtbo | wc -l` -gt 1 ]; then
259 bbfatal "Multiple DTBO found, use the right DTBO in SRC_URI from the following:\n$(basename -a ${S}/*.dtbo)"
260 elif [ -f ${B}/${USER_DTS_FILE}.dtbo ]; then
261 install -Dm 0644 ${B}/${USER_DTS_FILE}.dtbo ${D}/${nonarch_base_libdir}/firmware/xilinx/${PN}/${PN}.dtbo
262 else
263 bbnote "A dtbo ending '.dtbo' expected but not found in ${S} or ${B}, This means firmware can be loaded without dtbo dependency."
264 fi
265
266 # Install bit or bin if soc family is zynq-7000 or zynqmp.
267 # In case of dtbo as input or no dtbo exists in ${B}, then .bit or .bin will
268 # be copied from directly from ${S} without renaming the .bit/.bin name to
269 # ${PN}.bit/${PN}.bin.
270 # if more than one .bit/.bin file is found then fatal the task.
271 # if no .bit/.bin file is found then fatal the task.
272 if [ "${SOC_FAMILY}" = "zynq" ] || [ "${SOC_FAMILY}" = "zynqmp" ]; then
273 if [ `ls ${S}/*.bit | wc -l` -gt 1 ]; then
274 bbfatal "Multiple .bit found, use the right .bit in SRC_URI from the following:\n$(basename -a ${S}/*.bit)"
275 elif [ `ls ${S}/*.bin | wc -l` -gt 1 ]; then
276 bbfatal "Multiple .bin found, use the right .bin in SRC_URI from the following:\n$(basename -a ${S}/*.bin)"
277 elif [ `ls ${S}/*.bit | wc -l` -eq 1 ] && [ ! -f ${B}/${USER_DTS_FILE}.dtbo ]; then
278 install -Dm 0644 ${S}/*.bit ${D}/${nonarch_base_libdir}/firmware/xilinx/${PN}/
279 elif [ `ls ${S}/*.bin | wc -l` -eq 1 ]; then
280 install -Dm 0644 ${S}/*.bin ${D}/${nonarch_base_libdir}/firmware/xilinx/${PN}/
281 elif [ -f ${B}/${PN}.bin ] && [ -f ${B}/${USER_DTS_FILE}.dtbo ]; then
282 install -Dm 0644 ${B}/${PN}.bin ${D}/${nonarch_base_libdir}/firmware/xilinx/${PN}/${PN}.bin
283 else
284 bbfatal "A bitstream file with '.bit' or '.bin' expected but not found"
285 fi
286 fi
287
288 # Install pdi if soc family is versal or new silicon.
289 # In case of dtbo as input or no dtbo exists in ${B}, then .pdi will be copied
290 # from directly from ${S} without renaming the pdi name to ${PN}.pdi
291 # if more than one .pdi file is found then fail the task.
292 # In case of Versal DFx Static, only static dtbo can be loaded as BOOT.bin
293 # already contains static pdi. bbclass is not smart enough to determine
294 # whether it is static pdi or not, hence change fatal to warn if no PDI is found.
295 if [ "${SOC_FAMILY}" != "zynq" ] && [ "${SOC_FAMILY}" != "zynqmp" ]; then
296 if [ `ls ${S}/*.pdi | wc -l` -eq 1 ] && [ ! -f ${B}/${USER_DTS_FILE}.dtbo ]; then
297 install -Dm 0644 ${S}/*.pdi ${D}/${nonarch_base_libdir}/firmware/xilinx/${PN}/
298 elif [ `ls ${S}/*.pdi | wc -l` -gt 1 ]; then
299 bbfatal "Multiple PDI found, use the right PDI in SRC_URI from the following:\n$(basename -a ${S}/*.pdi)"
300 elif [ `ls ${S}/*.pdi | wc -l` -eq 1 ] && [ -f ${B}/${USER_DTS_FILE}.dtbo ]; then
301 install -Dm 0644 ${S}/*.pdi ${D}/${nonarch_base_libdir}/firmware/xilinx/${PN}/${PN}.pdi
302 else
303 bbwarn "A PDI file with '.pdi' expected but not found"
304 fi
305 fi
306
307 # Install xclbin
308 if ls ${S}/${XCL_PATH}/*.xclbin >/dev/null 2>&1; then
309 install -Dm 0644 ${S}/${XCL_PATH}/*.xclbin ${D}/${nonarch_base_libdir}/firmware/xilinx/${PN}/${PN}.xclbin
310 fi
311
312 # Install shell.json or accel.json
313 if [ -f ${S}/${JSON_PATH}/shell.json ] || [ -f ${S}/${JSON_PATH}/accel.json ]; then
314 install -Dm 0644 ${S}/${JSON_PATH}/*.json ${D}/${nonarch_base_libdir}/firmware/xilinx/${PN}/
315 fi
316}
317
318do_deploy[noexec] = "1"
319
320FILES:${PN} += "${nonarch_base_libdir}/firmware/xilinx/${PN}"
diff --git a/meta-xilinx-core/classes-recipe/fw-package.bbclass b/meta-xilinx-core/classes-recipe/fw-package.bbclass
new file mode 100644
index 00000000..e9847d33
--- /dev/null
+++ b/meta-xilinx-core/classes-recipe/fw-package.bbclass
@@ -0,0 +1,94 @@
1#
2# Copyright (C) 2024, Advanced Micro Devices, Inc. All rights reserved.
3#
4# SPDX-License-Identifier: MIT
5#
6# This bbclass provides infrastructure to package and deploy firmware baremetal
7# or freertos application elf or bin files to linux root filesystem under
8# /lib/firmware directory.
9
10inherit deploy
11
12INHERIT_DEFAULT_DEPENDS = "1"
13
14# Since we're just copying, we can run any config.
15COMPATIBLE_HOST = ".*"
16
17PACKAGE_ARCH = "${MACHINE_ARCH}"
18
19# Default expects the user to provide the fw app in the deploy directory.
20# A machine, multiconfig, or local.conf should override this.
21FW_NAME ??= ""
22TARGET_MC ??= ""
23FW_DEPENDS ??= ""
24FW_MCDEPENDS ??= ""
25FW_DEPLOY_DIR ??= "${DEPLOY_DIR_IMAGE}"
26FW_DEPLOY_DIR[vardepsexclude] += "TOPDIR"
27FW_IMAGE_NAME ??= "${FW_NAME}-${MACHINE}-${TARGET_MC}"
28
29# Default is for the multilib case (without the extension .elf/.bin)
30FW_FILE ??= "${FW_DEPLOY_DIR}/${FW_IMAGE_NAME}"
31FW_FILE[vardepsexclude] = "FW_DEPLOY_DIR"
32
33do_fetch[depends] += "${FW_DEPENDS}"
34do_fetch[mcdepends] += "${FW_MCDEPENDS}"
35
36# Set default destination directory is /lib/firmware, user can change this value
37# to /boot directory depending on requirement.
38DESTDIR ??= "${nonarch_base_libdir}/firmware/xilinx"
39SYSROOT_DIRS += "/boot"
40
41INSANE_SKIP:${PN} = "arch"
42INSANE_SKIP:${PN}-dbg = "arch"
43
44# Disable buildpaths QA check warnings.
45INSANE_SKIP:${PN} += "buildpaths"
46
47do_install() {
48 if [ ! -e ${FW_FILE}.elf ]; then
49 echo "Unable to find FW_FILE (${FW_FILE}.elf)"
50 exit 1
51 fi
52
53 install -Dm 0644 ${FW_FILE}.elf ${D}${DESTDIR}/${FW_IMAGE_NAME}.elf
54}
55
56# If the item is already in OUR deploy_image_dir, nothing to deploy!
57SHOULD_DEPLOY = "${@'false' if (d.getVar('FW_FILE')).startswith(d.getVar('DEPLOY_DIR_IMAGE')) else 'true'}"
58do_deploy() {
59 # If the item is already in OUR deploy_image_dir, nothing to deploy!
60 if ${SHOULD_DEPLOY}; then
61 install -Dm 0644 ${FW_FILE}.elf ${DEPLOYDIR}/${FW_IMAGE_NAME}.elf
62 install -Dm 0644 ${FW_FILE}.bin ${DEPLOYDIR}/${FW_IMAGE_NAME}.bin
63 fi
64}
65
66FILES:${PN} += "${DESTDIR}/${FW_IMAGE_NAME}*"
67
68def check_fw_vars(d):
69 # If both are blank, the user MUST pass in the path to the firmware!
70 if not d.getVar('FW_DEPENDS') and not d.getVar('FW_MCDEPENDS'):
71 # Don't cache this, as the items on disk can change!
72 d.setVar('BB_DONT_CACHE', '1')
73
74 msg = ""
75 fail = False
76 if not os.path.exists(d.getVar('FW_FILE') + ".elf"):
77 msg = msg + "The expected file %s.elf is not available. " % d.getVar('FW_FILE')
78 fail = True
79 if not os.path.exists(d.getVar('FW_FILE') + ".bin"):
80 msg = msg + "The expected file %s.bin is not available. " % d.getVar('FW_FILE')
81 fail = True
82 if fail:
83 if not d.getVar('WITHIN_EXT_SDK'):
84 raise bb.parse.SkipRecipe("%s\nSee the meta-xilinx-core README." % msg)
85 else:
86 # We found the file, so be sure to track it
87 d.setVar('SRC_URI', 'file://${FW_FILE}.elf file://${FW_FILE}.bin')
88 d.setVarFlag('do_install', 'file-checksums', '${FW_FILE}.elf:True ${FW_FILE}.bin:True')
89 d.setVarFlag('do_deploy', 'file-checksums', '${FW_FILE}.elf:True ${FW_FILE}.bin:True')
90
91python() {
92 # Need to allow bbappends to change the check
93 check_fw_vars(d)
94}
diff --git a/meta-xilinx-core/classes-recipe/image-wic-utils.bbclass b/meta-xilinx-core/classes-recipe/image-wic-utils.bbclass
new file mode 100644
index 00000000..41ad8148
--- /dev/null
+++ b/meta-xilinx-core/classes-recipe/image-wic-utils.bbclass
@@ -0,0 +1,54 @@
1# Helper/utility functions to work with the IMAGE_BOOT_FILES variable and its
2# expected behvaior with regards to the contents of the DEPLOY_DIR_IMAGE.
3#
4# The use of these functions assume that the deploy directory is populated with
5# any dependent files/etc. Such that the recipe using these functions depends
6# on the recipe that provides the files being used/queried.
7
8def boot_files_split_expand(d):
9 # IMAGE_BOOT_FILES has extra renaming info in the format '<source>;<target>'
10 for f in (d.getVar("IMAGE_BOOT_FILES") or "").split(" "):
11 parts = f.split(";", 1)
12 sources = [parts[0].strip()]
13 if "*" in parts[0]:
14 # has glob part
15 import glob
16 deployroot = d.getVar("DEPLOY_DIR_IMAGE")
17 sources = []
18 for i in glob.glob(os.path.join(deployroot, parts[0])):
19 sources.append(os.path.basename(i))
20
21 # for all sources, yield an entry
22 for s in sources:
23 if len(parts) == 2:
24 if parts[1].endswith('/'):
25 yield s, '%s%s' % (parts[1].strip(), s)
26 else:
27 yield s, parts[1].strip()
28 yield s, s
29
30def boot_files_bitstream(d):
31 expectedfiles = [("bitstream", True)]
32 expectedexts = [(".bit", True), (".bin", False)]
33 # search for bitstream paths, use the renamed file. First matching is used
34 for source, target in boot_files_split_expand(d):
35 # skip boot.bin and u-boot.bin, it is not a bitstream
36 skip = ["boot.bin", "u-boot.bin"]
37 if source in skip or target in skip:
38 continue
39
40 for e, t in expectedfiles:
41 if source == e or target == e:
42 return target, t
43 for e, t in expectedexts:
44 if source.endswith(e) or target.endswith(e):
45 return target, t
46 return "", False
47
48def boot_files_dtb_filepath(d):
49 dtbs = (d.getVar("IMAGE_BOOT_FILES") or "").split(" ")
50 for source, target in boot_files_split_expand(d):
51 if target.endswith(".dtb"):
52 return target
53 return ""
54
diff --git a/meta-xilinx-core/classes-recipe/kernel-simpleimage.bbclass b/meta-xilinx-core/classes-recipe/kernel-simpleimage.bbclass
new file mode 100644
index 00000000..110ee254
--- /dev/null
+++ b/meta-xilinx-core/classes-recipe/kernel-simpleimage.bbclass
@@ -0,0 +1,35 @@
1python __anonymous () {
2 kerneltypes = set((d.getVar("KERNEL_IMAGETYPE") or "").split())
3 kerneltypes |= set((d.getVar("KERNEL_IMAGETYPES") or "").split())
4 if any(t.startswith("simpleImage.") for t in kerneltypes):
5 # Enable building of simpleImage
6 bb.build.addtask('do_prep_simpleimage', 'do_compile', 'do_configure', d)
7 uarch = d.getVar("UBOOT_ARCH")
8 if uarch == "microblaze":
9 d.appendVarFlag('do_prep_simpleimage', 'depends', ' virtual/dtb:do_populate_sysroot')
10}
11
12do_prep_simpleimage[dirs] += "${B}"
13do_prep_simpleimage () {
14 install -d ${B}/arch/${ARCH}/boot/dts
15 for type in ${KERNEL_IMAGETYPES} ; do
16 if [ -z "${type##*simpleImage*}" ] && [ ${ARCH} = "microblaze" ]; then
17 ext="${type##*.}"
18 # Microblaze simpleImage only works with dts file
19 cp ${RECIPE_SYSROOT}/boot/devicetree/${ext}.dts ${B}/arch/${ARCH}/boot/dts/
20 fi
21 done
22}
23
24do_deploy:append () {
25 for type in ${KERNEL_IMAGETYPES} ; do
26 if [ -z "${type##*simpleImage*}" ] && [ ${ARCH} = "microblaze" ]; then
27 base_name=${type}-${KERNEL_IMAGE_NAME}
28 install -m 0644 ${KERNEL_OUTPUT_DIR}/${type}.strip $deployDir/${base_name}.strip
29 install -m 0644 ${KERNEL_OUTPUT_DIR}/${type}.unstrip $deployDir/${base_name}.unstrip
30 symlink_name=${type}-${KERNEL_IMAGE_LINK_NAME}
31 ln -sf ${base_name}.strip $deployDir/${symlink_name}.strip
32 ln -sf ${base_name}.unstrip $deployDir/${symlink_name}.unstrip
33 fi
34 done
35}
diff --git a/meta-xilinx-core/classes-recipe/qemuboot-xilinx.bbclass b/meta-xilinx-core/classes-recipe/qemuboot-xilinx.bbclass
new file mode 100644
index 00000000..7466ab5e
--- /dev/null
+++ b/meta-xilinx-core/classes-recipe/qemuboot-xilinx.bbclass
@@ -0,0 +1,140 @@
1
2# enable the overrides for the context of the conf only
3OVERRIDES .= ":qemuboot-xilinx"
4
5# Default machine targets for Xilinx QEMU (FDT Generic)
6# Allow QB_MACHINE to be overridden by a BSP config
7QB_MACHINE ?= "${QB_MACHINE_XILINX}"
8QB_RNG=""
9QB_MACHINE_XILINX:aarch64 = "-machine arm-generic-fdt"
10QB_MACHINE_XILINX:arm = "-M arm-generic-fdt-7series"
11QB_MACHINE_XILINX:microblaze = "-M microblaze-fdt-plnx"
12
13QB_SYSTEM_NAME ?= "${@qemu_target_binary(d)}"
14QB_DEFAULT_FSTYPE ?= "${@qemu_rootfs_params(d,'fstype')}"
15QB_ROOTFS ?= "${@qemu_rootfs_params(d,'rootfs')}"
16QB_ROOTFS_OPT ?= "${@qemu_rootfs_params(d,'rootfs-opt')}"
17QB_DTB ?= "${@qemu_default_dtb(d)}"
18
19# defaults
20QB_DEFAULT_KERNEL ?= "none"
21QB_DEFAULT_KERNEL:zynq ?= "${@'zImage' if \
22 d.getVar('INITRAMFS_IMAGE_BUNDLE') != '1' else 'zImage-initramfs-${MACHINE}.bin'}"
23QB_DEFAULT_KERNEL:microblaze ?= "${@'simpleImage.mb' if \
24 d.getVar('INITRAMFS_IMAGE_BUNDLE') != '1' else 'simpleImage.mb-initramfs-${MACHINE}.bin'}"
25
26inherit qemuboot
27
28def qemu_target_binary(data):
29 package_arch = data.getVar("PACKAGE_ARCH")
30 qemu_target_binary = (data.getVar("QEMU_TARGET_BINARY_%s" % package_arch) or "")
31 if qemu_target_binary:
32 return qemu_target_binary
33
34 target_arch = data.getVar("TARGET_ARCH")
35 if target_arch == "microblazeeb":
36 target_arch = "microblaze"
37 elif target_arch == "aarch64":
38 target_arch += "-multiarch"
39 elif target_arch == "arm":
40 target_arch = "aarch64"
41 return "qemu-system-%s" % target_arch
42
43def qemu_add_extra_args(data):
44 initramfs_image = data.getVar('INITRAMFS_IMAGE') or ""
45 bundle_image = data.getVar('INITRAMFS_IMAGE_BUNDLE') or ""
46 deploy_dir = data.getVar('DEPLOY_DIR_IMAGE') or ""
47 machine_name = data.getVar('MACHINE') or ""
48 soc_family = data.getVar('SOC_FAMILY') or ""
49 qb_extra_args = ''
50 # Add kernel image and boot.scr to qemu boot command when initramfs_image supplied
51 kernel_name = ''
52 bootscr_image = '%s/boot.scr' % deploy_dir
53 if soc_family in ('zynqmp', 'versal'):
54 kernel_name = 'Image'
55 bootscr_loadaddr = '0x20000000'
56 if initramfs_image:
57 kernel_image = '%s/%s' % (deploy_dir, kernel_name)
58 if bundle_image == "1":
59 kernel_image = '%s/%s-initramfs-%s.bin' % (deploy_dir, kernel_name, machine_name)
60 kernel_loadaddr = '0x200000'
61 if kernel_name:
62 qb_extra_args = ' -device loader,file=%s,addr=%s,force-raw=on' % (kernel_image, kernel_loadaddr)
63 qb_extra_args += ' -device loader,file=%s,addr=%s,force-raw=on' % (bootscr_image, bootscr_loadaddr)
64 if soc_family == 'versal':
65 qb_extra_args += ' -boot mode=5'
66 else:
67 if soc_family in ('zynqmp', 'versal'):
68 qb_extra_args = ' -boot mode=5'
69 return qb_extra_args
70
71def qemu_rootfs_params(data, param):
72 initramfs_image = data.getVar('INITRAMFS_IMAGE') or ""
73 bundle_image = data.getVar('INITRAMFS_IMAGE_BUNDLE') or ""
74 soc_family = data.getVar('SOC_FAMILY') or ""
75 tune_features = (data.getVar('TUNE_FEATURES') or []).split()
76 if 'microblaze' in tune_features:
77 soc_family = 'microblaze'
78 soc_variant = data.getVar('SOC_VARIANT') or ""
79
80 if param == 'rootfs':
81 return 'none' if bundle_image == "1" else ''
82
83 elif param == 'fstype':
84 fstype_dict = {
85 "microblaze": "cpio.gz",
86 "zynq": "cpio.gz",
87 "zynqmp": "cpio.gz.u-boot",
88 "versal": "cpio.gz.u-boot.qemu-sd-fatimg"
89 }
90 if not initramfs_image:
91 image_fs = data.getVar('IMAGE_FSTYPES')
92 if 'wic.qemu-sd' in image_fs:
93 return 'wic.qemu-sd'
94 if soc_family not in fstype_dict:
95 return ""
96 return fstype_dict[soc_family]
97
98 elif param == 'rootfs-opt':
99 sd_index = "1"
100 if soc_family == 'zynq':
101 sd_index = "0"
102 if soc_family == 'versal' and soc_variant == 'net':
103 sd_index = "0"
104
105 # Device is using a disk
106 if not initramfs_image:
107 return ' -drive if=sd,index=%s,file=@ROOTFS@,format=raw' % (sd_index)
108
109 # Device is using a ramdisk
110 if soc_family not in ('zynq', 'microblaze'):
111 return ' -device loader,file=@ROOTFS@,addr=0x04000000,force-raw=on'
112
113 # Ramdisk must be compiled into the kernel
114 return ''
115
116def qemu_default_dtb(data):
117 if data.getVar("IMAGE_BOOT_FILES", True):
118 dtbs = data.getVar("IMAGE_BOOT_FILES", True).split(" ")
119 # IMAGE_BOOT_FILES has extra renaming info in the format '<source>;<target>'
120 # Note: Wildcard sources work here only because runqemu expands them at run time
121 dtbs = [f.split(";")[0] for f in dtbs]
122 dtbs = [f for f in dtbs if f.endswith(".dtb")]
123 if len(dtbs) != 0:
124 return dtbs[0]
125 return ""
126
127def qemu_default_serial(data):
128 if data.getVar("SERIAL_CONSOLES", True):
129 first_console = data.getVar("SERIAL_CONSOLES", True).split(" ")[0]
130 speed, console = first_console.split(";", 1)
131 # zynqmp uses earlycon and stdout (in dtb)
132 if "zynqmp" in data.getVar("MACHINEOVERRIDES", True).split(":"):
133 return ""
134 return "console=%s,%s earlyprintk" % (console, speed)
135 return ""
136
137def qemu_zynqmp_unhalt(data, multiarch):
138 if multiarch:
139 return "-global xlnx,zynqmp-boot.cpu-num=0 -global xlnx,zynqmp-boot.use-pmufw=true"
140 return "-device loader,addr=0xfd1a0104,data=0x8000000e,data-len=4 -device loader,addr=0xfd1a0104,data=0x8000000e,data-len=4"
diff --git a/meta-xilinx-core/classes-recipe/xilinx-fetch-restricted.bbclass b/meta-xilinx-core/classes-recipe/xilinx-fetch-restricted.bbclass
new file mode 100644
index 00000000..a778ec7d
--- /dev/null
+++ b/meta-xilinx-core/classes-recipe/xilinx-fetch-restricted.bbclass
@@ -0,0 +1,35 @@
1# This class is setup to override the default fetching for the target recipe.
2# When fetching it forces PREMIRROR only fetching so that no attempts are made
3# to fetch the Xilinx downloads that are restricted to authenticated users only.
4#
5# The purpose of this class is to allow for automatation with pre-downloaded
6# content or content that is available with curated/user defined pre-mirrors
7# and or pre-populated downloads/ directories.
8
9python do_fetch() {
10 xilinx_restricted_url = "xilinx.com/member/forms/download"
11
12 src_uri = (d.getVar('SRC_URI') or "").split()
13 if len(src_uri) == 0:
14 return
15
16 for i in src_uri:
17 if xilinx_restricted_url in i:
18 # force the use of premirrors only, do not attempt download from xilinx.com
19 d.setVar("BB_FETCH_PREMIRRORONLY", "1")
20 break
21
22 try:
23 fetcher = bb.fetch2.Fetch(src_uri, d)
24 fetcher.download()
25 except bb.fetch2.NetworkAccess as e:
26 if xilinx_restricted_url in e.url:
27 # fatal on access to xilinx.com restricted downloads, print the url for manual download
28 bb.fatal("The following download cannot be fetched automatically. " \
29 "Please manually download the file and place it in the 'downloads' directory (or on an available PREMIRROR).\n" \
30 " %s" % (e.url.split(";")[0]))
31 else:
32 bb.fatal(str(e))
33 except bb.fetch2.BBFetchException as e:
34 bb.fatal(str(e))
35}
diff --git a/meta-xilinx-core/classes-recipe/xilinx-platform-init.bbclass b/meta-xilinx-core/classes-recipe/xilinx-platform-init.bbclass
new file mode 100644
index 00000000..99f7863a
--- /dev/null
+++ b/meta-xilinx-core/classes-recipe/xilinx-platform-init.bbclass
@@ -0,0 +1,14 @@
1# This class should be included by any recipe that wants to access or provide
2# the platform init source files which are used to initialize a Zynq or ZynqMP
3# SoC.
4
5# Define the path to the xilinx platform init code/headers
6PLATFORM_INIT_DIR ?= "/usr/src/xilinx-platform-init"
7
8PLATFORM_INIT_STAGE_DIR = "${STAGING_DIR_HOST}${PLATFORM_INIT_DIR}"
9
10# Target files use for platform init
11PLATFORM_INIT_FILES ?= ""
12PLATFORM_INIT_FILES:zynq = "ps7_init_gpl.c ps7_init_gpl.h"
13PLATFORM_INIT_FILES:zynqmp = "psu_init_gpl.c psu_init_gpl.h"
14