diff --git a/docs/TIERED_ARM_TRACE.md b/docs/TIERED_ARM_TRACE.md new file mode 100644 index 0000000..5785b49 --- /dev/null +++ b/docs/TIERED_ARM_TRACE.md @@ -0,0 +1,14 @@ +# Explicit tiered ARM diagnostic parser + +The existing `parse_arm64_trace_configuration` entry keeps its profile maximum +of 64 instructions and absent-profile maximum of 8. The new +`parse_arm64_trace_configuration_with_limit` entry requires an explicit limit +of 64, 256, 1024 or 4096. Limits 0, 65 and 4097 are invalid. Budgets above 64 +must be one of 256, 1024 or 4096 and fit within that limit. Selecting a larger +limit never provisions a missing platform or raises the absent-profile +maximum of 8. Memory, handoff, platform and DT configuration checks still use +the same parser implementation. + +This entry is for an explicit diagnostic-only EFI feature. Default callers +continue using the original function. Original bytes remain unchanged; a +budget outcome proves only bounded instruction execution, not normal boot. diff --git a/repository-files.json b/repository-files.json index 13a9572..3cbb089 100644 --- a/repository-files.json +++ b/repository-files.json @@ -31,6 +31,11 @@ "bytes": 1200, "sha256": "d7ec3da6c39dcbcb8733c85016874e6f0cd982137c4f4c105a00ab71c9b45fa4" }, + { + "path": "docs/TIERED_ARM_TRACE.md", + "bytes": 820, + "sha256": "7b4ac7b1720869e8b6aa88f2f7a7cb3beb41ca1db8706c47355fc7f865ff45d1" + }, { "path": "examples/audit_kc.rs", "bytes": 3210, @@ -58,8 +63,8 @@ }, { "path": "repository.json", - "bytes": 1394, - "sha256": "64f2d11a45a5e2a9bab8a704d63a9757582d4069a1f32e6ec992363253ff912d" + "bytes": 1396, + "sha256": "fe17d7e0d4979fde4916c92631cb65fda8e2732aabd3d5237cf3af2392d86abd" }, { "path": "src/acpi.rs", @@ -73,8 +78,8 @@ }, { "path": "src/boot_config.rs", - "bytes": 30591, - "sha256": "c182ddc16b84501f032de336fb4c63ccd35c3b2f5eca6f3da8a4d5a4ea14a253" + "bytes": 31190, + "sha256": "be0e13ec360759c4605f16d34b53c904fb700246da3e7e08382b606069cc81c0" }, { "path": "src/boot_picker.rs", @@ -198,8 +203,8 @@ }, { "path": "tests/arm64_platform_config.rs", - "bytes": 5971, - "sha256": "fdd1c27a51a7f71ffb95303de7b2173b1843878836a3bc838fae3ae7a1d6e34e" + "bytes": 7880, + "sha256": "a6d5e6f56a801a639abdc71fe4e8698a38d904e382f0a6db2a5bd757d4de97bb" }, { "path": "tests/boot_config.rs", diff --git a/repository.json b/repository.json index 90aa7da..db75989 100644 --- a/repository.json +++ b/repository.json @@ -7,9 +7,9 @@ "crate": "nextcore-core", "description": "Public boot configuration and bounded format codecs, including APFS Jumpstart extraction.", "development": { - "branch": "codex/arm64e-boot-metal", + "branch": "codex/tiered-trace-budget", "base_commit": "6547be4bbf6b75cf706b89ade4d4a60d5441d223", - "updated_utc": "2026-09-08T14:42:36.688484+00:00", + "updated_utc": "2026-09-08T16:46:07.724853+00:00", "dependency_revisions": {}, "integration": "Git submodule with an immutable gitlink; workspace source patches for local development" }, diff --git a/src/boot_config.rs b/src/boot_config.rs index 132ea9b..eb67cbb 100644 --- a/src/boot_config.rs +++ b/src/boot_config.rs @@ -373,6 +373,18 @@ fn parse_named_kernel_target(input: &[u8], arm: bool) -> Result { /// provisioned. The prefix is bounded to eight instructions without a platform /// profile, or64 with the explicit software interrupt compatibility profile. pub fn parse_arm64_trace_configuration(input: &[u8]) -> Result { + parse_arm64_trace_configuration_with_limit(input, 64) +} + +/// Explicit diagnostic-only extension. Default callers retain the64/8 entry. +/// The limit and any budget above64 must be an approved bounded tier. +pub fn parse_arm64_trace_configuration_with_limit( + input: &[u8], + maximum_budget: u64, +) -> Result { + if !matches!(maximum_budget, 64 | 256 | 1024 | 4096) { + return Err(BootConfigError::InvalidTraceConfiguration); + } if parse_arm64_kernel_target(input)?.profile != KernelProfile::X86EfiArm64Trace { return Err(BootConfigError::UnsupportedKernelProfile); } @@ -440,7 +452,9 @@ pub fn parse_arm64_trace_configuration(input: &[u8]) -> Result= result.physical_base + result.memory_size || result.instruction_budget == 0 - || result.instruction_budget > if result.platform.is_some() { 64 } else { 8 } + || result.instruction_budget > if result.platform.is_some() { maximum_budget } else { 8 } + || (result.instruction_budget > 64 + && !matches!(result.instruction_budget, 256 | 1024 | 4096)) { return Err(BootConfigError::InvalidTraceConfiguration); } diff --git a/tests/arm64_platform_config.rs b/tests/arm64_platform_config.rs index 8487408..219fa02 100644 --- a/tests/arm64_platform_config.rs +++ b/tests/arm64_platform_config.rs @@ -1,5 +1,6 @@ use nextcore_core::boot_config::{ parse_arm64_trace_configuration as parse, Arm64PlatformProfile, BootConfigError as E, + parse_arm64_trace_configuration_with_limit as parse_with_limit, }; fn config(profile: Option<&str>, options: Option<&str>, budget: u64) -> Vec { @@ -23,6 +24,47 @@ fn integer(name: &str, value: u64) -> String { } const PROFILE: &str = "nextcore-irq-compat-v1"; +#[test] +fn explicit_tiers_require_a_valid_ceiling_and_do_not_change_the_default_entry() { + for maximum in [64, 256, 1024, 4096] { + for budget in [1, 8, 63, 64, 256, 1024, 4096] { + let input = config(Some(PROFILE), None, budget); + assert_eq!(parse_with_limit(&input, maximum).is_ok(), budget <= maximum); + assert_eq!(parse(&input).is_ok(), budget <= 64); + } + } + for maximum in [0, 8, 65, 255, 1023, 4097, u64::MAX] { + assert_eq!(parse_with_limit(&config(Some(PROFILE), None, 8), maximum), Err(E::InvalidTraceConfiguration)); + } +} + +#[test] +fn unsupported_extended_budgets_never_become_arbitrary_long_runs() { + for budget in [0, 65, 128, 255, 257, 512, 1023, 1025, 2048, 4095, 4097, u64::MAX] { + assert_eq!(parse_with_limit(&config(Some(PROFILE), None, budget), 4096), Err(E::InvalidTraceConfiguration)); + } +} + +#[test] +fn extended_ceiling_does_not_provision_a_profile_or_expand_its_absent_bound() { + for maximum in [64, 256, 1024, 4096] { + assert_eq!(parse_with_limit(&config(None, None, 8), maximum).unwrap().platform, None); + for budget in [9, 64, 256, 1024, 4096] { + assert_eq!(parse_with_limit(&config(None, None, budget), maximum), Err(E::InvalidTraceConfiguration)); + } + } +} + +#[test] +fn extended_configuration_retains_handoff_memory_and_platform_validation() { + let valid = String::from_utf8(config(Some(PROFILE), None, 256)).unwrap(); + for input in [valid.replace("unprovisioned-sptm-prefix", "automatic"), + valid.replace("0x40000000", "0x40000001")] { + assert_eq!(parse_with_limit(input.as_bytes(), 4096), Err(E::InvalidTraceConfiguration)); + } + assert_eq!(parse_with_limit(&config(Some(PROFILE), Some(&integer("IrqLevel", 2)), 256), 4096), Err(E::InvalidPlatformConfiguration)); +} + #[test] fn absent_profile_retains_old_bound_and_does_not_create_a_provider() { assert_eq!(parse(&config(None, None, 8)).unwrap().platform, None);