diff options
Diffstat (limited to 'meta-xilinx-core/classes-recipe')
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 | |||
7 | QSPI_SIZE ?= "0x2280000" | ||
8 | |||
9 | # Register values | ||
10 | IDN_REG ?= "0x4D554241" | ||
11 | VERSION_REG ?= "0x1" | ||
12 | LENGTH_REG ?= "0x4" | ||
13 | PERSISTENT_REG ?= "0x01010000" | ||
14 | |||
15 | # QSPI Offsets | ||
16 | IMAGE_SELECTOR_OFFSET ?= "0x0" | ||
17 | IMAGE_SELECTOR_BACKUP_OFFSET ?= "0x80000" | ||
18 | PERSISTENT_REG_OFFSET ?= "0x100000" | ||
19 | PERSISTENT_REG_BACKUP_OFFSET ?= "0x120000" | ||
20 | IMAGE_A_OFFSET ?= "0x200000" | ||
21 | IMAGE_A_IMGSEL_OFFSET ?= "0xF00000" | ||
22 | IMAGE_B_OFFSET ?= "0xF80000" | ||
23 | IMAGE_B_IMGSEL_OFFSET ?= "0x1C80000" | ||
24 | IMAGE_RCVRY_OFFSET ?= "0x1E00000" | ||
25 | IMAGE_RCVRY_BACKUP_OFFSET ?= "0x2000000" | ||
26 | VERSION_OFFSET ?= "0x2240000" | ||
27 | CHECKSUM_OFFSET ?= "0x2250000" | ||
28 | |||
29 | def 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 | |||
136 | do_compile[depends] += "virtual/boot-bin:do_deploy virtual/imgsel:do_deploy virtual/imgrcry:do_deploy" | ||
137 | |||
138 | python amd_spi_image_do_compile() { | ||
139 | generate_spi_image(d) | ||
140 | } | ||
141 | |||
142 | EXPORT_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 | |||
10 | inherit devicetree | ||
11 | |||
12 | DEPENDS = "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 | ||
17 | COMPATIBLE_MACHINE ?= "^$" | ||
18 | COMPATIBLE_MACHINE:microblaze = "^$" | ||
19 | |||
20 | PACKAGE_ARCH = "${MACHINE_ARCH}" | ||
21 | |||
22 | PROVIDES = "" | ||
23 | |||
24 | do_fetch[cleandirs] = "${B}" | ||
25 | |||
26 | DT_PADDING_SIZE = "0x1000" | ||
27 | BOOTGEN_FLAGS ?= " -arch ${SOC_FAMILY} -w ${@bb.utils.contains('SOC_FAMILY','zynqmp','','-process_bitstream bin',d)}" | ||
28 | |||
29 | S ?= "${WORKDIR}" | ||
30 | FW_DIR ?= "" | ||
31 | DTSI_PATH ?= "" | ||
32 | DTBO_PATH ?= "" | ||
33 | BIT_PATH ?= "" | ||
34 | BIN_PATH ?= "" | ||
35 | PDI_PATH ?= "" | ||
36 | JSON_PATH ?= "" | ||
37 | XCl_PATH ?= "" | ||
38 | DT_FILES_PATH = "${S}/${DTSI_PATH}" | ||
39 | FIRMWARE_NAME_DT_FILE ?= "" | ||
40 | USER_DTS_FILE ?= "" | ||
41 | |||
42 | FIRMWARE_NAME_DT_FILE[doc] = "DT file which has firmware-name device-tree property" | ||
43 | USER_DTS_FILE[doc] = "Final DTSI or DTS file which is used for packaging final DT overlay" | ||
44 | DTSI_PATH[doc] = "Absolute '.dtsi' or ''.dts' file path as input to SRC_URI" | ||
45 | DTBO_PATH[doc] = "Absolute '.dtbo' file path as input to SRC_URI" | ||
46 | BIT_PATH[doc] = "Absolute '.bit' file path as input to SRC_URI" | ||
47 | BIN_PATH[doc] = "Absolute '.bin' file path as input to SRC_URI" | ||
48 | JSON_PATH[doc] = "Absolute '.json' file path as input to SRC_URI" | ||
49 | XCL_PATH[doc] = "Absolute '.xclbin' file path as input to SRC_URI" | ||
50 | |||
51 | python() { | ||
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. | ||
140 | python 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 | |||
156 | do_configure[prefuncs] += "find_firmware_file" | ||
157 | |||
158 | python 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 | |||
180 | do_compile[prefuncs] += "find_firmware_file" | ||
181 | |||
182 | python 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. | ||
224 | python 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 | |||
245 | do_install[prefuncs] += "find_user_dts_overlay_file" | ||
246 | |||
247 | do_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 | |||
318 | do_deploy[noexec] = "1" | ||
319 | |||
320 | FILES:${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 | |||
10 | inherit deploy | ||
11 | |||
12 | INHERIT_DEFAULT_DEPENDS = "1" | ||
13 | |||
14 | # Since we're just copying, we can run any config. | ||
15 | COMPATIBLE_HOST = ".*" | ||
16 | |||
17 | PACKAGE_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. | ||
21 | FW_NAME ??= "" | ||
22 | TARGET_MC ??= "" | ||
23 | FW_DEPENDS ??= "" | ||
24 | FW_MCDEPENDS ??= "" | ||
25 | FW_DEPLOY_DIR ??= "${DEPLOY_DIR_IMAGE}" | ||
26 | FW_DEPLOY_DIR[vardepsexclude] += "TOPDIR" | ||
27 | FW_IMAGE_NAME ??= "${FW_NAME}-${MACHINE}-${TARGET_MC}" | ||
28 | |||
29 | # Default is for the multilib case (without the extension .elf/.bin) | ||
30 | FW_FILE ??= "${FW_DEPLOY_DIR}/${FW_IMAGE_NAME}" | ||
31 | FW_FILE[vardepsexclude] = "FW_DEPLOY_DIR" | ||
32 | |||
33 | do_fetch[depends] += "${FW_DEPENDS}" | ||
34 | do_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. | ||
38 | DESTDIR ??= "${nonarch_base_libdir}/firmware/xilinx" | ||
39 | SYSROOT_DIRS += "/boot" | ||
40 | |||
41 | INSANE_SKIP:${PN} = "arch" | ||
42 | INSANE_SKIP:${PN}-dbg = "arch" | ||
43 | |||
44 | # Disable buildpaths QA check warnings. | ||
45 | INSANE_SKIP:${PN} += "buildpaths" | ||
46 | |||
47 | do_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! | ||
57 | SHOULD_DEPLOY = "${@'false' if (d.getVar('FW_FILE')).startswith(d.getVar('DEPLOY_DIR_IMAGE')) else 'true'}" | ||
58 | do_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 | |||
66 | FILES:${PN} += "${DESTDIR}/${FW_IMAGE_NAME}*" | ||
67 | |||
68 | def 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 | |||
91 | python() { | ||
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 | |||
8 | def 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 | |||
30 | def 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 | |||
48 | def 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 @@ | |||
1 | python __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 | |||
12 | do_prep_simpleimage[dirs] += "${B}" | ||
13 | do_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 | |||
24 | do_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 | ||
3 | OVERRIDES .= ":qemuboot-xilinx" | ||
4 | |||
5 | # Default machine targets for Xilinx QEMU (FDT Generic) | ||
6 | # Allow QB_MACHINE to be overridden by a BSP config | ||
7 | QB_MACHINE ?= "${QB_MACHINE_XILINX}" | ||
8 | QB_RNG="" | ||
9 | QB_MACHINE_XILINX:aarch64 = "-machine arm-generic-fdt" | ||
10 | QB_MACHINE_XILINX:arm = "-M arm-generic-fdt-7series" | ||
11 | QB_MACHINE_XILINX:microblaze = "-M microblaze-fdt-plnx" | ||
12 | |||
13 | QB_SYSTEM_NAME ?= "${@qemu_target_binary(d)}" | ||
14 | QB_DEFAULT_FSTYPE ?= "${@qemu_rootfs_params(d,'fstype')}" | ||
15 | QB_ROOTFS ?= "${@qemu_rootfs_params(d,'rootfs')}" | ||
16 | QB_ROOTFS_OPT ?= "${@qemu_rootfs_params(d,'rootfs-opt')}" | ||
17 | QB_DTB ?= "${@qemu_default_dtb(d)}" | ||
18 | |||
19 | # defaults | ||
20 | QB_DEFAULT_KERNEL ?= "none" | ||
21 | QB_DEFAULT_KERNEL:zynq ?= "${@'zImage' if \ | ||
22 | d.getVar('INITRAMFS_IMAGE_BUNDLE') != '1' else 'zImage-initramfs-${MACHINE}.bin'}" | ||
23 | QB_DEFAULT_KERNEL:microblaze ?= "${@'simpleImage.mb' if \ | ||
24 | d.getVar('INITRAMFS_IMAGE_BUNDLE') != '1' else 'simpleImage.mb-initramfs-${MACHINE}.bin'}" | ||
25 | |||
26 | inherit qemuboot | ||
27 | |||
28 | def 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 | |||
43 | def 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 | |||
71 | def 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 | |||
116 | def 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 | |||
127 | def 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 | |||
137 | def 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 | |||
9 | python 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 | ||
6 | PLATFORM_INIT_DIR ?= "/usr/src/xilinx-platform-init" | ||
7 | |||
8 | PLATFORM_INIT_STAGE_DIR = "${STAGING_DIR_HOST}${PLATFORM_INIT_DIR}" | ||
9 | |||
10 | # Target files use for platform init | ||
11 | PLATFORM_INIT_FILES ?= "" | ||
12 | PLATFORM_INIT_FILES:zynq = "ps7_init_gpl.c ps7_init_gpl.h" | ||
13 | PLATFORM_INIT_FILES:zynqmp = "psu_init_gpl.c psu_init_gpl.h" | ||
14 | |||