Skip to content
vanhoefm edited this page Oct 6, 2013 · 7 revisions

Background

The radiotap header contains a MAC timestamp in microseconds. This is the time since the device has been brought online using "ifconfig wlanX up".

The wireless chip writes the transmission or reception time to the descriptor buffer of the packet. In particular the field status2 contains either AR_SendTimestamp or AR_RcvTimestamp (see Buffer Management for details). So it's not the firmware that writes this timestamp, the but actual wireless chip, making sure these timings are accurate.

Firmware

In the firmware the macro OS_DELAY(us) is used to delay execution for us microseconds. This macro has the following execution path: OS_DELAY(us) => ath_hal_delay(us) => adf_os_udelay(us) => adf_os_udelay(us) => __adf_os_udelay(us) => A_DELAY_USECS(us) => A_CMN(clock._delay_us(us)). In this last macro the clock structure can be initialized differently for Magpie and K2 chips. When grepping for _delay_us assignments we get three results. Two of them are:

  • ./magpie_fw_dev/target/cmnos/cmnos_clock.c
  • ./magpie_fw_dev/build/magpie_1_1/sboot/cmnos/clock/src/cmnos_clock.c

Strangely enough both files are not used (putting in garbage still makes it compile). Additionally both these files are identical (apart from indentation)! However the third file does get used: ./magpie_fw_dev/target/init/app_start.c. In this file the clock is patched. The 'new' function cmnos_delay_us_patch is now used. We have two implementations in two identical files, one used by Magpie and one used by K2:

  • Magpie ./magpie_fw_dev/target/rompatch/cmnos_clock_patch.c
  • K2: ./magpie_fw_dev/target/cmnos/k2_cmnos_clock_patch.c

Both use the NOW() macro and do a busy spin until enough microseconds have passed.

The NOW() Function

For both Magpie and K2 this is defined as xthal_get_ccount. In turn this is defined using "PROVIDE" to a location in the ROM??

The question is now, how do we properly call this function in the firmware? It appears that cmnos_tick_patch uses the function NOW() and takes into account a possible overflow of the counter. We can then use the function cmnos_milliseconds_patch, assigned to the field _milliseconds, from the firmware using the define A_MILLISECONDS().

For some reason all the timings are wrong. Executing A_CLOCK_INIT(10) on K2 and A_CLOCK_INIT(50) makes the function A_MILLISECONDS() work somewhat. Why do these constants work? What is happening? Even now these timings are not really accurate... that could be because the patch in cmmos_clock_patch.c has rounded errors for both K2 and Magpie.

Conclusion

It appears the patched clocks are still incorrect. There appears to be no functionality which depends on a true accurate clock anyway. We have found the following for the Magie and K2 chipsets:

  • Magpie: Has a configurable clockrate. Default firmware sets the speed to 200MHz. The current frequency is saved in register 0x56000 (do a right shift by two - that is divide by 4).
  • K2: Fixed clockrate. It runs at 117MHz.

In attack.c we have implemented our own clock based on these findings. The original implementation has trouble with overflow and I think it incorrectly uses the current frequency. Again, the implementation in attack.c works quite well ;)

Reverse Engineering

The xthal prefix stands for XTensa Hardware Abstraction Layer. The function xthal_get_ccount returns the CCOUNT register of the Xtensa: "The CCOUNT register increments every clock cycle and as many as three 32-bit comparison registers that can generate level-1 interrupts or high-priority interrupts when they match CCOUNT" [1]. So for a CPU at 200MHz this register is increased 200,000,000 times per second.

[1] 1.1.3 Architectural Building Blocks - Timer Interrupt Option, http://tensilica.com/uploads/pdf/xtensalx_overview_handbook.pdf