1use anyhow::{Context, Error, Result, anyhow, bail};
2use cmsis_pack::pdsc::{AccessPort, Algorithm, Core, Device, Package, Processor};
3use cmsis_pack::{pack_index::PdscRef, utils::FromElem};
4use futures::StreamExt;
5use jep106::JEP106Code;
6use probe_rs::config::Registry;
7use probe_rs::flashing::FlashAlgorithm;
8use probe_rs_target::{
9 Architecture, ArmCoreAccessOptions, Chip, ChipFamily, Core as ProbeCore, CoreAccessOptions,
10 CoreType, GenericRegion, MemoryAccess, MemoryRegion, NvmRegion, RamRegion, RawFlashAlgorithm,
11 RiscvCoreAccessOptions, TargetDescriptionSource, XtensaCoreAccessOptions,
12};
13use std::collections::HashMap;
14use std::io::BufReader;
15use std::{fs, io::Read, path::Path};
16
17pub enum Kind<'a, T>
18where
19 T: std::io::Seek + std::io::Read,
20{
21 Archive(&'a mut zip::ZipArchive<T>),
22 Directory(&'a Path),
23}
24
25impl<T> Kind<'_, T>
26where
27 T: std::io::Seek + std::io::Read,
28{
29 fn read_bytes(&mut self, path: &Path) -> Result<Vec<u8>> {
31 let buffer = match self {
32 Kind::Archive(archive) => {
33 let reader = BufReader::new(archive.by_name(&path.to_string_lossy())?);
34 reader.bytes().collect::<std::io::Result<Vec<u8>>>()?
35 }
36 Kind::Directory(dir) => fs::read(dir.join(path))?,
37 };
38
39 Ok(buffer)
40 }
41}
42
43fn process_flash_algo<T>(
44 flash_algorithm: &Algorithm,
45 kind: &mut Kind<T>,
46) -> Result<RawFlashAlgorithm>
47where
48 T: std::io::Seek + std::io::Read,
49{
50 let algo_bytes = kind.read_bytes(&flash_algorithm.file_name)?;
51 let mut algo = crate::parser::extract_flash_algo(
52 None,
53 &algo_bytes,
54 &flash_algorithm.file_name,
55 flash_algorithm.default,
56 false, )?;
58
59 algo.load_address = flash_algorithm
62 .ram_start
63 .map(|ram_start| ram_start + FlashAlgorithm::get_max_algorithm_header_size());
64
65 Ok(algo)
69}
70
71pub(crate) fn extract_families<T>(
72 pdsc: Package,
73 mut kind: Kind<T>,
74 families: &mut Vec<ChipFamily>,
75 only_supported_familes: bool,
76) -> Result<()>
77where
78 T: std::io::Seek + std::io::Read,
79{
80 let mut devices = pdsc.devices.0.into_iter().collect::<Vec<_>>();
82 devices.sort_by(|a, b| a.0.cmp(&b.0));
83
84 let registry = Registry::from_builtin_families();
86 let currently_supported_chip_families = registry.families();
87
88 for (device_name, device) in devices {
89 if only_supported_familes
90 && !currently_supported_chip_families
91 .iter()
92 .any(|supported_family| supported_family.name == device.family)
93 {
94 log::debug!("Unsupprted chip family {}. Skipping ...", device.family);
96 return Ok(());
97 }
98
99 let mut potential_family = families
101 .iter_mut()
102 .find(|family| family.name == device.family);
103
104 let family = if let Some(ref mut family) = potential_family {
105 family
106 } else {
107 families.push(ChipFamily {
108 name: device.family.clone(),
109 manufacturer: try_parse_vendor(device.vendor.as_deref()),
110 generated_from_pack: true,
111 chip_detection: vec![],
112 pack_file_release: Some(pdsc.releases.latest_release().version.clone()),
113 variants: Vec::new(),
114 flash_algorithms: Vec::new(),
115 source: TargetDescriptionSource::BuiltIn,
116 });
117 families.last_mut().unwrap()
119 };
120
121 let flash_algorithm_names = device
123 .algorithms
124 .iter()
125 .filter_map(|flash_algorithm| {
126 match process_flash_algo(flash_algorithm, &mut kind) {
127 Ok(algo) => {
128 let algo_name = algo.name.clone();
131 if !family.flash_algorithms.contains(&algo) {
132 family.flash_algorithms.push(algo);
133 }
134
135 Some(algo_name)
136 }
137 Err(e) => {
138 log::warn!(
139 "Failed to process flash algorithm {}.",
140 flash_algorithm.file_name.display()
141 );
142 log::warn!("Reason: {e:?}");
143 None
144 }
145 }
146 })
147 .collect::<Vec<_>>();
148
149 let flash_algorithm_names = flash_algorithm_names
152 .iter()
153 .enumerate()
154 .filter(|(i, s)| !flash_algorithm_names[..*i].contains(s))
155 .map(|(_, s)| s.clone())
156 .collect::<Vec<_>>();
157
158 let cores = device
159 .processors
160 .iter()
161 .map(create_core)
162 .collect::<Result<Vec<_>>>()?;
163
164 let mut memory_map = get_mem_map(&device, &cores);
165 patch_memmap(&mut memory_map);
166
167 family.variants.push(Chip {
168 name: device_name,
169 part: None,
170 svd: None,
171 documentation: HashMap::new(),
172 package_variants: vec![],
173 cores,
174 memory_map,
175 flash_algorithms: flash_algorithm_names,
176 rtt_scan_ranges: None,
177 jtag: None, default_binary_format: None,
179 });
180 }
181
182 Ok(())
183}
184
185fn try_parse_vendor(vendor: Option<&str>) -> Option<JEP106Code> {
186 let jep = match vendor? {
187 "Atmel:3" => JEP106Code::new(0, 0x1f),
188 "NXP:11" => JEP106Code::new(0, 0x15),
189 "STMicroelectronics:13" => JEP106Code::new(0, 0x20),
190 _ => return None,
191 };
192
193 Some(jep)
194}
195
196fn create_core(processor: &Processor) -> Result<ProbeCore> {
197 let core_type = core_to_probe_core(&processor.core)?;
198 Ok(ProbeCore {
199 name: processor
200 .name
201 .as_ref()
202 .map(|s| s.to_ascii_lowercase())
203 .unwrap_or_else(|| "main".to_string()),
204 core_type,
205 core_access_options: match core_type.architecture() {
206 Architecture::Arm => CoreAccessOptions::Arm(ArmCoreAccessOptions {
207 ap: match processor.ap {
208 AccessPort::Index(id) => probe_rs_target::ApAddress::V1(id),
209 AccessPort::Address(addr) => probe_rs_target::ApAddress::V2(addr),
210 },
211 targetsel: None,
212 debug_base: None,
213 cti_base: None,
214 jtag_tap: None,
215 }),
216 Architecture::Riscv => CoreAccessOptions::Riscv(RiscvCoreAccessOptions {
217 hart_id: None,
218 jtag_tap: None,
219 }),
220 Architecture::Xtensa => {
221 CoreAccessOptions::Xtensa(XtensaCoreAccessOptions { jtag_tap: None })
222 }
223 },
224 })
225}
226
227fn core_to_probe_core(value: &Core) -> Result<CoreType, Error> {
228 Ok(match value {
229 Core::CortexM0 => CoreType::Armv6m,
230 Core::CortexM0Plus => CoreType::Armv6m,
231 Core::CortexM4 => CoreType::Armv7em,
232 Core::CortexM3 => CoreType::Armv7m,
233 Core::CortexM23 => CoreType::Armv8m,
234 Core::CortexM33 => CoreType::Armv8m,
235 Core::CortexM55 => CoreType::Armv8m,
236 Core::CortexM85 => CoreType::Armv8m,
237 Core::CortexM7 => CoreType::Armv7em,
238 Core::StarMC1 => CoreType::Armv8m,
239 c => bail!("Core '{c:?}' is not yet supported for target generation."),
240 })
241}
242
243pub fn visit_dirs(path: &Path, families: &mut Vec<ChipFamily>) -> Result<()> {
245 walk_files(path, &mut |file_path| {
246 if has_extension(file_path, "pdsc") {
247 log::info!("Found .pdsc file: {}", file_path.display());
248
249 let package = Package::from_path(file_path).context(format!(
250 "Failed to open .pdsc file {}.",
251 file_path.display()
252 ))?;
253
254 extract_families::<fs::File>(package, Kind::Directory(path), families, false).context(
255 format!("Failed to process .pdsc file {}.", file_path.display()),
256 )?;
257 }
258
259 Ok(())
260 })
261}
262
263fn walk_files(path: &Path, callback: &mut impl FnMut(&Path) -> Result<()>) -> Result<()> {
264 for entry in fs::read_dir(path)? {
265 let entry_path = entry?.path();
266
267 if entry_path.is_dir() {
268 walk_files(&entry_path, callback)?;
269 } else {
270 callback(&entry_path)?;
271 }
272 }
273
274 Ok(())
275}
276
277fn has_extension(path: &Path, ext: &str) -> bool {
278 path.extension().is_some_and(|e| e == ext)
279}
280
281pub fn visit_file(path: &Path, families: &mut Vec<ChipFamily>) -> Result<()> {
282 log::info!("Trying to open pack file: {}.", path.display());
283 let file = fs::File::open(path)?;
285 let mut archive = zip::ZipArchive::new(file)?;
286
287 let mut pdsc_file = find_pdsc_in_archive(&mut archive)?
288 .ok_or_else(|| anyhow!("Failed to find .pdsc file in archive {}", path.display()))?;
289
290 let mut pdsc = String::new();
291 pdsc_file.read_to_string(&mut pdsc)?;
292
293 let package = Package::from_string(&pdsc).map_err(|e| {
294 anyhow!(
295 "Failed to parse pdsc file '{}' in CMSIS Pack {}: {}",
296 pdsc_file.name(),
297 path.display(),
298 e
299 )
300 })?;
301
302 drop(pdsc_file);
303
304 extract_families(package, Kind::Archive(&mut archive), families, false)
305}
306
307pub async fn visit_arm_files(families: &mut Vec<ChipFamily>, filter: Option<String>) -> Result<()> {
308 let packs = crate::fetch::get_vidx().await?;
310
311 let mut stream =
312 futures::stream::iter(packs.pdsc_index.iter().enumerate().filter_map(|(i, pack)| {
313 let only_supported_familes = if let Some(ref filter) = filter {
314 if !pack.name.contains(filter) {
316 log::debug!("Ignoring filtered {} ...", pack.name);
317 return None;
318 }
319
320 log::info!("Found matching chip family: {}", pack.name);
321
322 false
324 } else {
325 true
327 };
328 if pack.deprecated.is_none() {
329 log::info!("Working PACK {}/{} ...", i, packs.pdsc_index.len());
331 Some(visit_arm_file(pack, only_supported_familes))
332 } else {
333 log::warn!("Ignoring deprecated {} ...", pack.name);
334 None
335 }
336 }))
337 .buffer_unordered(32);
338 while let Some(result) = stream.next().await {
339 families.extend(result);
340 }
341
342 Ok(())
343}
344
345pub(crate) async fn visit_arm_file(
346 pack: &PdscRef,
347 only_supported_familes: bool,
348) -> Vec<ChipFamily> {
349 let url = format!(
350 "{url}/{vendor}.{name}.{version}.pack",
351 url = pack.url.trim_end_matches('/'),
352 vendor = pack.vendor,
353 name = pack.name,
354 version = pack.version
355 );
356
357 log::info!("Downloading {url}");
358
359 let response = match reqwest::get(&url).await {
360 Ok(response) => response,
361 Err(error) => {
362 log::error!("Failed to download pack '{url}': {error}");
363 return vec![];
364 }
365 };
366 let bytes = match response.bytes().await {
367 Ok(bytes) => bytes,
368 Err(error) => {
369 log::error!("Failed to get bytes from pack '{url}': {error}");
370 return vec![];
371 }
372 };
373
374 log::info!("Trying to open pack file: {url}.");
375 let zip = std::io::Cursor::new(bytes);
376 let mut archive = match zip::ZipArchive::new(zip) {
377 Ok(archive) => archive,
378 Err(error) => {
379 log::error!("Failed to open pack '{url}': {error}");
380 return vec![];
381 }
382 };
383
384 let mut pdsc_file = match find_pdsc_in_archive(&mut archive) {
385 Ok(Some(file)) => file,
386 Ok(None) => {
387 log::error!("Failed to find .pdsc file in archive {url}");
388 return vec![];
389 }
390 Err(error) => {
391 log::error!("Error handling archive {url}: {error}");
392 return vec![];
393 }
394 };
395
396 let mut pdsc = String::new();
397 if let Err(error) = pdsc_file.read_to_string(&mut pdsc) {
398 log::error!("Failed to read .pdsc file '{url}': {error}");
399 return vec![];
400 };
401
402 let package = match Package::from_string(&pdsc) {
403 Ok(package) => package,
404 Err(error) => {
405 log::error!(
406 "Failed to parse pdsc file '{}' in CMSIS Pack {url}: {error}",
407 pdsc_file.name(),
408 );
409 return vec![];
410 }
411 };
412
413 let pdsc_name = pdsc_file.name().to_owned();
414
415 drop(pdsc_file);
416
417 let mut families = vec![];
418
419 match extract_families(
420 package,
421 Kind::Archive(&mut archive),
422 &mut families,
423 only_supported_familes,
424 ) {
425 Ok(_) => log::info!("Processed package {pdsc_name}"),
426 Err(error) => log::error!("Something went wrong while handling pack {url}: {error}"),
427 };
428
429 families
430}
431
432pub(crate) fn find_pdsc_in_archive<T>(
434 archive: &mut zip::ZipArchive<T>,
435) -> Result<Option<zip::read::ZipFile<'_, T>>>
436where
437 T: std::io::Seek + std::io::Read,
438{
439 let mut index = None;
440 for i in 0..archive.len() {
441 let file = archive.by_index(i)?;
442 let outpath = file.enclosed_name().ok_or_else(|| {
443 anyhow!(
444 "Error handling the ZIP file content with path '{}': Path seems to be malformed",
445 file.name()
446 )
447 })?;
448
449 if has_extension(&outpath, "pdsc") {
450 index = Some(i);
454 break;
455 }
456 }
457
458 if let Some(index) = index {
459 let file = archive.by_index(index)?;
460
461 Ok(Some(file))
462 } else {
463 Ok(None)
464 }
465}
466
467#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
469enum MemoryType {
470 Ram,
472 Nvm,
474 Generic,
476}
477
478#[derive(Debug, Clone, PartialEq, Eq)]
482struct DeviceMemory {
483 memory_type: MemoryType,
484 p_name: Option<String>,
485 memory_start: u64,
486 memory_end: u64,
487 name: String,
488 access: MemoryAccess,
489}
490
491impl DeviceMemory {
492 fn access(&self) -> Option<MemoryAccess> {
493 fn is_default(access: &MemoryAccess) -> bool {
494 access == &MemoryAccess::default()
495 }
496
497 if is_default(&self.access) {
498 None
499 } else {
500 Some(self.access)
501 }
502 }
503}
504
505pub(crate) fn get_mem_map(device: &Device, cores: &[probe_rs_target::Core]) -> Vec<MemoryRegion> {
509 let mut device_memories: Vec<DeviceMemory> = device
510 .memories
511 .0
512 .iter()
513 .map(|(name, memory)| DeviceMemory {
514 name: name.clone(),
515 p_name: memory.p_name.clone(),
516 memory_type: if memory.default && memory.access.read && memory.access.write {
517 MemoryType::Ram
518 } else if memory.default
519 && memory.access.read
520 && memory.access.execute
521 && !memory.access.write
522 {
523 MemoryType::Nvm
524 } else {
525 MemoryType::Generic
526 },
527 memory_start: memory.start,
528 memory_end: memory.start + memory.size,
529 access: MemoryAccess {
530 read: memory.access.read,
531 write: memory.access.write,
532 execute: memory.access.execute,
533 boot: memory.startup,
534 },
535 })
536 .collect();
537
538 device_memories.sort_by_key(|memory| {
540 (
541 memory.memory_type,
542 memory.p_name.clone(),
543 memory.access.boot,
544 memory.memory_start,
545 )
546 });
547
548 let all_cores: Vec<_> = cores.iter().map(|core| core.name.clone()).collect();
549
550 let is_multi_core = cores.len() > 1;
551
552 let mut mem_map = vec![];
554 for region in device_memories {
555 if is_multi_core && region.p_name.is_none() {
556 log::warn!(
557 "Device {}, memory region {} has no processor name, but this is required for a multicore device. Assigning memory to all cores!",
558 device.name,
559 region.name
560 );
561 }
562
563 let cores = region
564 .p_name
565 .as_ref()
566 .map(|s| vec![s.to_ascii_lowercase()])
567 .unwrap_or_else(|| all_cores.clone());
568
569 match region.memory_type {
570 MemoryType::Ram => {
571 if let Some(MemoryRegion::Ram(existing_region)) = mem_map.iter_mut().find(|existing_region| {
572 matches!(existing_region, MemoryRegion::Ram(ram_region) if ram_region.name.as_deref() == Some(®ion.name) && ram_region.access == region.access())
573 })
574 {
575 existing_region.cores.extend_from_slice(&cores);
576 } else {
577 mem_map.push(MemoryRegion::Ram(RamRegion {
578 access: region.access(),
579 name: Some(region.name),
580 range: region.memory_start..region.memory_end,
581 cores,
582 }));
583 }
584 },
585 MemoryType::Nvm => {
586 if let Some(MemoryRegion::Nvm(existing_region)) = mem_map.iter_mut().find(|existing_region| {
587 matches!(existing_region, MemoryRegion::Nvm(nvm_region) if nvm_region.name.as_deref() == Some(®ion.name) && nvm_region.access == region.access())
588 })
589 {
590 existing_region.cores.extend_from_slice(&cores);
591 } else {
592 mem_map.push(MemoryRegion::Nvm(NvmRegion {
593 access: region.access(),
594 name: Some(region.name),
595 range: region.memory_start..region.memory_end,
596 cores,
597 is_alias: false,
598 }));
599 }
600 },
601 MemoryType::Generic => {
602 if let Some(MemoryRegion::Generic(existing_region)) = mem_map.iter_mut().find(|existing_region| {
603 matches!(existing_region, MemoryRegion::Generic(generic_region) if generic_region.name.as_deref() == Some(®ion.name) && generic_region.access == region.access())
604 })
605 {
606 existing_region.cores.extend_from_slice(&cores);
607 } else {
608 mem_map.push(MemoryRegion::Generic(GenericRegion {
609 access: region.access(),
610 name: Some(region.name),
611 range: region.memory_start..region.memory_end,
612 cores,
613 }));
614 }
615 },
616 };
617 }
618
619 mem_map
620}
621
622fn patch_memmap(mem_map: &mut [MemoryRegion]) {
623 ensure_single_ram_region_is_executable(mem_map);
624}
625
626fn ensure_single_ram_region_is_executable(mem_map: &mut [MemoryRegion]) {
628 let ram_regions = mem_map
632 .iter()
633 .filter_map(MemoryRegion::as_ram_region)
634 .count();
635
636 if ram_regions == 1
637 && let Some(MemoryRegion::Ram(ram_region)) = mem_map
638 .iter_mut()
639 .find(|region| matches!(region, MemoryRegion::Ram(_)))
640 && let Some(ref mut access) = ram_region.access
641 {
642 access.execute = true;
643 }
644}