This article describes the over-clocking process of an Android phone (specifically the Sony Arc running an MSM chip at the 1Ghz default speed). It can be done without the need of recompiling the kernel, meaning that you don’t have to unlock the bootloader.

The frequencies / voltages / PLL rates are hardcoded into the kernel upon compiling time – so that the operating system could benefit from CPU governors that change the phone speed upon demand, thus helping battery life but still allowing fast operation when needed.
I decided to write this article to describe the step by step process of allowing the phone to go to a higher frequency by altering these hardcoded values by the use of a kernel module at runtime.

The first person (that I’ve found) to successfully do this documented the process here – it is meant for OMAP2 processors. His insight was right, it can be done for other type of processors also!

DISCLAIMER OF LIABILITIES: I have posted this tutorial for academic purposes – over-clocking can lead to many bad things and I shall not be held responsible for any damage direct or indirect if you proceed.

Identifing the kernel structures

The first thing when trying to overclock is to write a CPU governor that tries to set a higher than maximum frequency. However, as it will be seen below, there are validations in place that forbid values higher than what the kernel knows. Our goal is to manipulate kernel limits within kmem (the kernel memory) so that … the validation passes.


Having compiled the cpufreq_smartass.c governor for different Sony phones (X10 / Arc) I already had an idea of at least one check:

As you can see, there’s this policy object that seems to know about the maximum frequency of the phone. Upon checking over governors and searching kernel source code there’s a method that returns the policy object for each CPU, method is called cpufreq_cpu_get and luckily, the kernel exposes that method:

We write a simple module that retrieves the global pointer and then changes the MAX values to allow higher validations of frequencies. Checking the structure cpufreq_policy we can identify its members involved into the msm_cpufreq_verify

Module snippet:

However, this is not enough. It only modifies the global values used in validations, there’s still a long way to go until the frequency really changes.


Going back to the governor – we can identify which method applies the new frequencies (policy):

Since now the governors will be allowed higher frequencies, let’s begin at __cpufreq_driver_target.

The see the call invocation path, please check kernel source code by clicking on the method above:
a) governor computes new frequencies and calls __cpufreq_driver_target to set the new speed.
b) which passes the values to cpufreq_driver->target(...)
c) target(...) is a pointer to method msm_cpufreq_target
d) which then calls cpufreq_frequency_table_target to retrieve the frequency indexes in the global frequency table.


There is thus a global frequency *table, and cpufreq_frequency_table_target job is to match the policy against this table and return an index into it.

*table is passed as argument and is retrieved just before the call to cpufreq_frequency_table_target within the msm_cpufreq_target method. It is retrieved using method cpufreq_frequency_get_table.

We’re lucky again, the kernel exposes this method too:

It means we can also get a reference to the struct cpufreq_frequency_table *table and patch the heck out of it :). I’ll skip the boring details – technically the table should be processed and displayed to see what values we have, also taking into account that some of the values could be invalid. To see a real example of how to iterate over the table, the method show_available_freqs could be checked.

On Sony Arc phone index holding the 1024000Hz frequency (that we want updated to 1200000Hz) has index number 5 in the global table of frequencies, thus … we can patch this value also:

One word of advice.

We covered the policy and global table of frequencies, however only now to fun begins since the kernel on the phone doesn’t expose any more methods (as you’ll see below) – the only solution will be to hunt for them using disassembly, double pointer dereferences and writing directly back into the kernel memory at specific addresses that have to be identified.
Have a break and return when ready.


If we go back to the msm_cpufreq_target and we go pass everything that looks uninteresting, we find the method acpuclk_set_rate. To find it, we need to search the kernel sources in a file named acpuclock-7x30.c:

The for above looks like is doing another validation. If the specified rate doesn’t match any of the frequencies with the acpu_freq_tbl, it refuses to apply the new rates. Which is annoying, another table with frequencies to patch.

This time the kernel is not helpful – table is not exported …

Upon closer examination, the variable is statically defined within the kernel:

The biggest trouble is how to identify its location in memory, and for this … the only way to find out is to decompile the source code.

acpu_freq_tbl hunt

Since the variable is static and the acpuclk_set_rate makes use of it, for sure there have to be a reference to it within the binary version of acpuclk_set_rate. This is where things become kernel specific, a different kernel could have the static defined at a different address …

But anyway – there is a glimmer of hope:

The method is exposed by the kernel and it has 0x800bb694 – 0x800bb324 = 880 (in decimal) binary size.

We have to grab it from kmem and disassemble it on a PC with a cross compiler:

First on the phone:

Then on the PC:

and the result is this .asm file which I’ve tried to annotate with the C code above:

Now we know where acpu_freq_tbl is in memory 🙂 at location 0x80750948.
Here’s the type of each entry in the acpu_freq_tbl table:

We could just initialize a variable of type struct clkctl_acpu_speed * and … in fact why talking and not doing it ?

If you have the kernel source code, you can see that in this table, the 1024000Hz frequency is at index 9 instead of 5. We just have to replace the frequency as well as the PLL rates with the correct values for frequency 1200000Hz.
We could also activate ALL frequencies (by setting use_for_scaling = 1 for all of the entries) but we could run into issues with the final kernel object that needs to patched …. that is the cpufreq_stats_table table …

cpufreq_stats_table hunt

The cat /sys/devices/system/cpu/cpu0/cpufreq/stats/time_in_state which shows how much the system spent in each frequency is useful for tracking battery usage and is also used by SetCPU / CpuSpy etc… applications to inform the user.

Tracking it down is very similar to the steps above – the place to look for (that uses the table and creates the sysfs file) is the method show_time_in_state:

there are still few things to notice:

a) this table is not static, but is allocated at runtime based on the frequencies enabled in the acpu_freq_tbl table.

What you get when decompiling is in fact the address where the address of the cpufreq_stats_table table is stored.

What it means is that we need two pointers now to land on its space in kmem:

Now the times will also be reported correctly. Unfortunately, the code related to keeping track of the time in the states makes use of the frequency itself and not any index. Thus, if this is not correctly set, the time in state for 1200000 won’t be updated, since value 1200000 is not in the table.

b) although it could be interesting of overwriting the pointer with a newly allocated table that keeps track of all possible frequencies, it would make the code less clear …

Maybe in the future I will work on doing just this ..

I hope it helps, and my impression is that this could be applied to any type of phones, just like the Milestone-Overclock team noticed 🙂 hats off to them!