@@ -346,6 +346,37 @@ def get_total_cpus_from_system() -> Optional[int]:
346346 return None
347347
348348
349+ def get_valid_apic_ids_from_system () -> Optional [set ]:
350+ """
351+ Get set of valid APIC IDs from the system via sysfs.
352+ Reads /sys/devices/system/cpu/cpuN/topology/apic_id for each CPU.
353+ Returns set of valid APIC IDs or None if not available.
354+ """
355+ try :
356+ cpu_dir = Path ('/sys/devices/system/cpu' )
357+ if not cpu_dir .exists ():
358+ return None
359+
360+ apic_ids = set ()
361+ cpu_files = [f for f in cpu_dir .iterdir () if f .name .startswith ('cpu' ) and f .name [3 :].isdigit ()]
362+
363+ for cpu_path in cpu_files :
364+ apic_id_file = cpu_path / 'topology' / 'apic_id'
365+ if apic_id_file .exists ():
366+ try :
367+ with open (apic_id_file , 'r' , encoding = 'utf-8' ) as f :
368+ apic_id = int (f .read ().strip ())
369+ apic_ids .add (apic_id )
370+ except (ValueError , IOError ):
371+ pass
372+
373+ return apic_ids if apic_ids else None
374+ except (OSError , ValueError ):
375+ pass
376+
377+ return None
378+
379+
349380def build_baseline_from_cmdline (
350381 cpus : str ,
351382 devices : Optional [str ] = None ,
@@ -371,27 +402,30 @@ def build_baseline_from_cmdline(
371402 except ValueError as e :
372403 raise ValueError (f"Invalid CPU specification '{ cpus } ': { e } " ) from e
373404
374- system_total_cpus = get_total_cpus_from_system ()
375- if system_total_cpus is None :
376- max_cpu = max (cpu_list ) if cpu_list else 0
377- total_cpus = max_cpu + 1
378- if verbose :
379- click .echo (f"Warning: Could not determine total CPUs from system, using max from specification: { total_cpus } " , err = True )
380- else :
381- total_cpus = system_total_cpus
382- max_specified = max (cpu_list ) if cpu_list else - 1
383- if max_specified >= total_cpus :
384- raise ValueError (
385- f"CPU { max_specified } specified but system only has { total_cpus } CPUs (0-{ total_cpus - 1 } )"
386- )
405+ # Validate against valid APIC IDs on the system
406+ valid_apic_ids = get_valid_apic_ids_from_system ()
407+ if valid_apic_ids is None :
408+ raise KernelInterfaceError (
409+ "Could not read APIC IDs from /sys/devices/system/cpu/*/topology/apic_id. "
410+ "Ensure the system exposes CPU topology information."
411+ )
412+
413+ invalid_cpus = set (cpu_list ) - valid_apic_ids
414+ if invalid_cpus :
415+ raise ValueError (
416+ f"Invalid APIC ID(s) specified: { sorted (invalid_cpus )} . "
417+ f"Valid APIC IDs on this system: { sorted (valid_apic_ids )} "
418+ )
387419
420+ # Total CPUs is based on the max APIC ID + 1 for sizing purposes
421+ total_cpus = max (valid_apic_ids ) + 1
422+ # Host reserved are all valid APIC IDs not in the available list
388423 available_cpus = set (cpu_list )
389- all_cpus = set (range (total_cpus ))
390- host_reserved_cpus = sorted (list (all_cpus - available_cpus ))
424+ host_reserved_cpus = sorted (list (valid_apic_ids - available_cpus ))
391425
392426 if 0 in available_cpus and len (host_reserved_cpus ) == 0 :
393427 if verbose :
394- click .echo ("Warning: CPU 0 is in available list but no host-reserved CPUs. Moving CPU 0 to host-reserved." , err = True )
428+ click .echo ("Warning: APIC ID 0 is in available list but no host-reserved CPUs. Moving APIC ID 0 to host-reserved." , err = True )
395429 available_cpus .discard (0 )
396430 host_reserved_cpus = [0 ]
397431 cpu_list = sorted (list (available_cpus ))
@@ -407,10 +441,10 @@ def build_baseline_from_cmdline(
407441 total_bytes = memory_pool_base + memory_pool_bytes
408442 host_reserved_bytes = memory_pool_base
409443 if verbose :
410- click .echo (f"Parsed CPU specification: { cpus } " )
411- click .echo (f" Total CPUs : { total_cpus } " )
412- click .echo (f" Host-reserved CPUs : { host_reserved_cpus } " )
413- click .echo (f" Available CPUs : { cpu_list } " )
444+ click .echo (f"Parsed APIC ID specification: { cpus } " )
445+ click .echo (f" Valid APIC IDs on system : { sorted ( valid_apic_ids ) } " )
446+ click .echo (f" Host-reserved APIC IDs : { host_reserved_cpus } " )
447+ click .echo (f" Available APIC IDs : { cpu_list } " )
414448 click .echo ("Memory pool from /proc/iomem:" )
415449 click .echo (f" Base: { hex (memory_pool_base )} " )
416450 click .echo (f" Size: { memory_pool_bytes } bytes ({ memory_pool_bytes / (1024 ** 3 ):.2f} GB)" )
@@ -473,7 +507,7 @@ def build_baseline_from_cmdline(
473507@click .command ()
474508@click .pass_context
475509@click .option ('--input' , '-i' , help = 'Input DTS or DTB file containing all resources. Mutually exclusive with --cpus and --devices. When used, all resources must come from the file.' )
476- @click .option ('--cpus' , '-c' , help = 'CPU specification for baseline (e.g., "4-7 " or "4,5,6,7") . Mutually exclusive with --input. Memory will be parsed from /proc/iomem.' )
510+ @click .option ('--cpus' , '-c' , help = 'APIC ID specification for baseline (e.g., "128-134 " or "128,130,132"). Use physical APIC IDs, not logical CPU numbers . Mutually exclusive with --input. Memory will be parsed from /proc/iomem.' )
477511@click .option ('--devices' , '-d' , help = 'Device names (comma-separated, e.g., "enp9s0_dev,nvme0"). Mutually exclusive with --input. Creates minimal device entries in baseline.' )
478512@click .option ('--dry-run' , is_flag = True , help = 'Validate without applying' )
479513@click .option ('--report' , is_flag = True , help = 'Generate detailed validation report' )
@@ -501,11 +535,11 @@ def init(ctx: click.Context, input: Optional[str], cpus: Optional[str], devices:
501535 # Initialize from DTS file (all resources from file)
502536 kerf init --input=hardware.dts
503537
504- # Initialize from command line (CPUs 4-7 , memory from /proc/iomem)
505- kerf init --cpus=4-7
538+ # Initialize from command line (APIC IDs 128-134 , memory from /proc/iomem)
539+ kerf init --cpus=128-134
506540
507- # Initialize with CPUs and devices
508- kerf init --cpus=4-7 --devices=enp9s0_dev,nvme0
541+ # Initialize with APIC IDs and devices
542+ kerf init --cpus=128,130,132 --devices=enp9s0_dev,nvme0
509543
510544 # Validate baseline without applying
511545 kerf init --input=hardware.dts --dry-run
0 commit comments