Skip to content

REQ-F-PTP-IOCTL-001: Production-Safe PTP Clock Control IOCTLs (ADJUST_FREQUENCY, GET_CLOCK_CONFIG) #326

Description

@zarfld

Requirement Type

Functional Requirement (IOCTL API)

Discovered Via

Reverse engineering from docs/SECURITY_IOCTL_RESTRICTION.md documentation (Section 2: Production IOCTLs Added)

Description

Driver MUST provide production-safe, high-level IOCTL abstractions (IOCTL_AVB_ADJUST_FREQUENCY and IOCTL_AVB_GET_CLOCK_CONFIG) to replace raw register access IOCTLs for PTP clock control in production builds.

Business Context

Functional Requirements

FR-1: IOCTL_AVB_ADJUST_FREQUENCY (Code 38)

Purpose: Adjust PTP clock frequency (replaces raw TIMINCA register writes)
IOCTL Definition:

#define IOCTL_AVB_ADJUST_FREQUENCY _NDIS_CONTROL_CODE(38, METHOD_BUFFERED)

Input/Output Structure:

typedef struct AVB_FREQUENCY_REQUEST {
avb_u32 increment_ns; /* in: Clock increment in nanoseconds per cycle (e.g., 8 for 8ns @ 125MHz) */
avb_u32 increment_frac; /* in: Fractional part (2^32 = 1ns, optional fine-tuning) */
avb_u32 current_increment; /* out: current TIMINCA value before change */
avb_u32 status; /* out: NDIS_STATUS value */
} AVB_FREQUENCY_REQUEST, *PAVB_FREQUENCY_REQUEST;

Behavior:

  1. Input Validation:
  • increment_ns must be reasonable for clock rate (e.g., 6-10ns for 125MHz)
  • Total increment = increment_ns + (increment_frac / 2^32)
  1. Hardware Access:
  • Read current TIMINCA register value (save to current_increment)
  • Calculate new TIMINCA value from increment_ns and increment_frac
  • Write new value to TIMINCA register (offset 0x0B608)
  1. Status Return:
  • status = NDIS_STATUS_SUCCESS on success
  • status = NDIS_STATUS_INVALID_PARAMETER if increment out of range
  • status = NDIS_STATUS_ADAPTER_NOT_READY if hardware not initialized
    Usage Example:
AVB_FREQUENCY_REQUEST freq_req;
freq_req.increment_ns = 8; // 8ns increment for 125MHz clock
freq_req.increment_frac = 0; // No fractional adjustment
DWORD bytesReturned;
if (DeviceIoControl(hDevice, IOCTL_AVB_ADJUST_FREQUENCY,
&freq_req, sizeof(freq_req),
&freq_req, sizeof(freq_req),
&bytesReturned, NULL)) {
printf("Frequency adjusted. Previous TIMINCA: 0x%08X\n", freq_req.current_increment);
} else {
printf("Error: %d\n", GetLastError());
}

FR-2: IOCTL_AVB_GET_CLOCK_CONFIG (Code 45)

Purpose: Query complete clock configuration (replaces raw register reads)
IOCTL Definition:

#define IOCTL_AVB_GET_CLOCK_CONFIG _NDIS_CONTROL_CODE(45, METHOD_BUFFERED)

Note: Originally code 39, changed to 45 due to suspected Windows blocking (per inline comment in code).
Output Structure:

typedef struct AVB_CLOCK_CONFIG {
avb_u64 systim; /* out: Current SYSTIM counter value (64-bit nanoseconds) */
avb_u32 timinca; /* out: Current clock increment configuration */
avb_u32 tsauxc; /* out: Auxiliary clock control register */
avb_u32 clock_rate_mhz; /* out: Base clock rate (125/156/200/250 MHz) */
avb_u32 status; /* out: NDIS_STATUS value */
} AVB_CLOCK_CONFIG, *PAVB_CLOCK_CONFIG;

Behavior:

  1. Hardware Access:
  • Read SYSTIML (offset 0x0B600) and SYSTIMH (offset 0x0B604) → Combine to 64-bit systim
  • Read TIMINCA (offset 0x0B608) → Store to timinca
  • Read TSAUXC (offset 0x0B640) → Store to tsauxc
  • Detect clock rate from device ID/configuration → Store to clock_rate_mhz
  1. Status Return:
  • status = NDIS_STATUS_SUCCESS on success
  • status = NDIS_STATUS_ADAPTER_NOT_READY if hardware not initialized
    Usage Example:
AVB_CLOCK_CONFIG cfg;
DWORD bytesReturned;
if (DeviceIoControl(hDevice, IOCTL_AVB_GET_CLOCK_CONFIG,
&cfg, sizeof(cfg),
&cfg, sizeof(cfg),
&bytesReturned, NULL)) {
printf("SYSTIM: 0x%016llX (%llu ns)\n", cfg.systim, cfg.systim);
printf("TIMINCA: 0x%08X\n", cfg.timinca);
printf("TSAUXC: 0x%08X (bit 31 = %s)\n",
cfg.tsauxc, (cfg.tsauxc & 0x80000000) ? "DISABLED" : "ENABLED");
printf("Clock: %u MHz\n", cfg.clock_rate_mhz);
} else {
printf("Error: %d\n", GetLastError());
}

FR-3: Always Available in All Builds

MUST NOT be guarded by #ifndef NDEBUG or any conditional compilation.
Rationale:

  • Production builds (Release with NDEBUG) require clock control
  • Test applications must work in Release builds
  • Security-safe abstractions (no arbitrary register access)

FR-4: Handler Implementation

MUST implement handlers in avb_integration_fixed.c:
Location: Lines ~724-780 (per grep search showing AVB_FREQUENCY_REQUEST usage)
Handler Pattern:

case IOCTL_AVB_ADJUST_FREQUENCY:
{
if (inLen < sizeof(AVB_FREQUENCY_REQUEST) || outLen < sizeof(AVB_FREQUENCY_REQUEST)) {
status = NDIS_STATUS_INVALID_LENGTH;
bytesRet = 0;
break;
}
PAVB_FREQUENCY_REQUEST freq_req = (PAVB_FREQUENCY_REQUEST)buf;

// Validate increment range
if (freq_req->increment_ns < 4 || freq_req->increment_ns > 16) {
freq_req->status = NDIS_STATUS_INVALID_PARAMETER;
status = NDIS_STATUS_SUCCESS;
bytesRet = sizeof(AVB_FREQUENCY_REQUEST);
break;
}
// Read current TIMINCA
freq_req->current_increment = read32(hwctx, 0x0B608);
// Calculate and write new TIMINCA
UINT32 new_timinca = (freq_req->increment_ns << 24) | freq_req->increment_frac;
write32(hwctx, 0x0B608, new_timinca);
freq_req->status = NDIS_STATUS_SUCCESS;
status = NDIS_STATUS_SUCCESS;
bytesRet = sizeof(AVB_FREQUENCY_REQUEST);
break;
}
case IOCTL_AVB_GET_CLOCK_CONFIG:
{
if (inLen < sizeof(AVB_CLOCK_CONFIG) || outLen < sizeof(AVB_CLOCK_CONFIG)) {
status = NDIS_STATUS_INVALID_LENGTH;
bytesRet = 0;
break;
}
PAVB_CLOCK_CONFIG cfg = (PAVB_CLOCK_CONFIG)buf;
// Read SYSTIM (64-bit)
UINT32 systiml = read32(hwctx, 0x0B600);
UINT32 systimh = read32(hwctx, 0x0B604);
cfg->systim = ((UINT64)systimh << 32) | systiml;
// Read TIMINCA
cfg->timinca = read32(hwctx, 0x0B608);
// Read TSAUXC
cfg->tsauxc = read32(hwctx, 0x0B640);
// Detect clock rate (device-specific)
cfg->clock_rate_mhz = 125; // Default for I210/I226
cfg->status = NDIS_STATUS_SUCCESS;
status = NDIS_STATUS_SUCCESS;
bytesRet = sizeof(AVB_CLOCK_CONFIG);
break;
}

FR-5: IOCTL Routing in device.c

MUST add case labels in device.c IOCTL routing switch:
Location: Lines ~277-298 (per earlier reverse engineering)
Routing Pattern:

case IOCTL_AVB_ADJUST_FREQUENCY:
case IOCTL_AVB_GET_CLOCK_CONFIG:
// Route to avb_integration_fixed.c handler
status = HandleAvbIoctl(FilterModuleContext, Request);
break;

Acceptance Criteria

AC-1: IOCTL_AVB_ADJUST_FREQUENCY Implementation

Given driver hardware is initialized
When user-mode application calls IOCTL_AVB_ADJUST_FREQUENCY
Then TIMINCA register is updated with new increment
And previous TIMINCA value is returned in current_increment
And status is NDIS_STATUS_SUCCESS

AC-2: IOCTL_AVB_GET_CLOCK_CONFIG Implementation

Given driver hardware is initialized
When user-mode application calls IOCTL_AVB_GET_CLOCK_CONFIG
Then current SYSTIM, TIMINCA, TSAUXC values are returned
And clock rate is correctly detected
And status is NDIS_STATUS_SUCCESS

AC-3: Works in Release Build

Given driver compiled in Release mode (NDEBUG defined)
When production test application is run
Then both IOCTLs are available and functional
And no raw register access IOCTLs are present

AC-4: Input Validation

Given user provides invalid increment_ns (e.g., 0 or 100)
When IOCTL_AVB_ADJUST_FREQUENCY is called
Then status is NDIS_STATUS_INVALID_PARAMETER
And TIMINCA register is NOT modified

AC-5: Test Coverage

Given production test file ptp_clock_control_production_test.c
When test is executed
Then all 4 test cases pass:

  • Test 1: Clock configuration query
  • Test 2: Frequency adjustment (5 different values)
  • Test 3: Timestamp setting and retrieval
  • Test 4: Clock stability measurement

Non-Functional Requirements

NFR-1: Performance (P1 - Important)

  • Latency: IOCTL handler completes in <1ms (register reads/writes are fast)
  • Throughput: Supports 100+ calls/second (unlikely to be performance bottleneck)

NFR-2: Reliability (P0 - Critical)

  • Atomicity: TIMINCA writes are atomic (single 32-bit write)
  • Error Handling: All error paths return meaningful status codes
  • Hardware State: Handlers verify hardware is initialized before access

NFR-3: Security (P0 - Critical)

  • No Arbitrary Access: Users cannot access arbitrary register offsets
  • Validated Input: All parameters validated before hardware writes
  • Production Safe: Available in Release builds without exposing raw registers

Implementation Status

✅ Completed (Per Documentation)

According to docs/SECURITY_IOCTL_RESTRICTION.md:

  • ✅ Structures defined in include/avb_ioctl.h
  • ✅ Handlers implemented in avb_integration_fixed.c
  • ✅ Routing added to device.c
  • ✅ Production test created: tools/avb_test/ptp_clock_control_production_test.c

⚠️ Verification Needed

Grep search found:

  • AVB_FREQUENCY_REQUEST used in avb_integration_fixed.c (lines 724, 734)
  • AVB_CLOCK_CONFIG used in multiple test files (20+ matches)
  • IOCTL_AVB_ADJUST_FREQUENCY defined in include/avb_ioctl.h (line 73)
  • ⚠️ IOCTL_AVB_GET_CLOCK_CONFIG changed from code 39 to 45 (line 74) - Why?
    Action Required: Verify IOCTL code 45 change reason and update all test files.

🔍 IOCTL Code Mismatch Investigation

Current Code: 45 (per include/avb_ioctl.h line 74)
Documented Code: 39 (per docs/SECURITY_IOCTL_RESTRICTION.md)
Possible Reason (per inline comment):

#define IOCTL_AVB_GET_CLOCK_CONFIG _NDIS_CONTROL_CODE(45, METHOD_BUFFERED)
/* Changed from 39 to 45 - testing if 0x9C blocked */

Impact: Test applications using code 39 will fail. This is Bug #4 continuation!

Test Requirements

Test-1: Frequency Adjustment

// Test various clock increments
AVB_FREQUENCY_REQUEST freq_req;
for (int ns = 6; ns <= 10; ns++) {
freq_req.increment_ns = ns;
freq_req.increment_frac = 0;
BOOL success = DeviceIoControl(h, IOCTL_AVB_ADJUST_FREQUENCY, ...);
assert(success && freq_req.status == NDIS_STATUS_SUCCESS);
}

Test-2: Clock Configuration Query

AVB_CLOCK_CONFIG cfg;
BOOL success = DeviceIoControl(h, IOCTL_AVB_GET_CLOCK_CONFIG, ...);
assert(success && cfg.status == NDIS_STATUS_SUCCESS);
assert(cfg.systim > 0); // Clock is running
assert(cfg.clock_rate_mhz == 125 || cfg.clock_rate_mhz == 156 || ...);

Test-3: Clock Stability

// Measure clock drift over 1 second
AVB_CLOCK_CONFIG cfg1, cfg2;
DeviceIoControl(h, IOCTL_AVB_GET_CLOCK_CONFIG, &cfg1, ...);
Sleep(1000);
DeviceIoControl(h, IOCTL_AVB_GET_CLOCK_CONFIG, &cfg2, ...);
UINT64 elapsed_ns = cfg2.systim - cfg1.systim;
double drift_ppm = fabs(1000000000.0 - elapsed_ns) / 1000.0;
assert(drift_ppm < 100); // <100ppm drift acceptable

Test-4: Release Build Compatibility

# Compile test in Release mode
cl /nologo /W4 /O2 /DNDEBUG /Zi /I include ptp_clock_control_production_test.c
# Should compile and run successfully
.\ptp_clock_control_production_test.exe

Traceability

Traces to (Parent Requirements)

Traces to: #23

Verified by (Test Cases)

Verified by: #319 (TEST-PTP-CTRL-001: Verify PTP Clock Control IOCTLs)

  • TEST-PTP-FREQ-001: Frequency adjustment functionality
  • TEST-PTP-CONFIG-001: Clock configuration query functionality
  • TEST-PTP-STABLE-001: Clock stability measurement
  • TEST-PTP-RELEASE-001: Works in Release build

Related Requirements

Implements

  • IEEE 1588-2008 Section 7.1.2: Clock adjustment requirements

Architecture Impact

  • Abstraction Layer: High-level IOCTL API decouples user-mode from register details
  • Security Boundary: Production builds cannot bypass validated abstractions
  • Maintainability: Register layout changes isolated to driver internals

Migration Impact

Medium Impact on Test Code:

  • Files using IOCTL_AVB_READ/WRITE_REGISTER for TIMINCA/SYSTIM/TSAUXC must migrate
  • Migration guide provided in docs/SECURITY_IOCTL_RESTRICTION.md Section 4
    Affected Test Files (from Section 4):
  • tools/avb_test/tsauxc_toggle_test.c - Uses raw TSAUXC writes
  • tools/avb_test/ptp_clock_control_test.c - Uses raw TIMINCA writes
    Migration Effort: 1-2 hours per test file (replace raw register patterns with IOCTL calls)

References

  • IEEE 1588-2008: PTP clock control specifications
  • Intel I210 Datasheet:
  • Section 8.12.25 - SYSTIM registers (0x0B600/0x0B604)
  • Section 8.12.27 - TIMINCA register (0x0B608)
  • Section 8.12.36 - TSAUXC register (0x0B640)
  • Documentation: docs/SECURITY_IOCTL_RESTRICTION.md

Priority Justification

P0 (Critical) because:

  • Required for production PTP functionality (cannot use debug-only raw register access)
  • Complements Issue REQ-NF-SEC-DEBUG-001: Debug-Only Raw Register Access (NDEBUG Guards) #23 (security requirement) - must provide alternative to raw access
  • Documented as completed but requires verification (possible IOCTL code mismatch)
  • Test infrastructure depends on these IOCTLs (ptp_clock_control_production_test.c)

Open Issues

  1. IOCTL Code Mismatch: Why was IOCTL_AVB_GET_CLOCK_CONFIG changed from 39 to 45? ("testing if 0x9C blocked")
  2. Test Compatibility: Are existing tests using code 39 or 45?
  3. Windows Blocking: Is there evidence that Windows blocks certain IOCTL codes?

Issue Created By: Reverse Engineering Analysis
Discovery Date: 2025-12-07
Standards: IEEE 1588-2008 (PTP), ISO/IEC/IEEE 29148:2018 (Requirements Engineering)

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions