This documentation refers to HaRET version "0.5.2". Please see HaRET for information on obtaining the latest version.
There is older documentation (out of date) at:
http://handhelds.org/cgi-bin/cvsweb.cgi/~checkout~/haret/docs/haret.html
Overview
HaRET is typically used for one of two purposes:
-
To boot a Linux kernel from WinCE
-
To explore a device to gather information on a device's hardware. (Often so that one can create a Linux kernel capable of running on that machine.)
HaRET commands
HaRET is fundamentally a command-line driven application. There are several ways to run HaRET commands.
-
Place commands in a file called "default.txt". When HaRET starts, there will be a button "run" - clicking the button will cause HaRET to load and run the commands from "default.txt". The output from the commands will be written to the screen.
-
Place commands in a file called "startup.txt". When HaRET starts it will automatically run these commands.
-
Click the "listen" button and connect to HaRET from a host PC. The best way to do this is to install haretconsole (see Obtain haretconsole) on the host PC. However, if that is not available, one may use telnet (eg, "telnet <deviceIP> 9999").
Booting Linux
HaRET is capable of replacing WinCE with a Linux kernel. In order for this to work, one must have a Linux kernel customized for the device. (For example, an HTC Universal kernel will not boot on an HTC Magician phone.) When trying to boot a kernel downloaded from the internet, please try to also locate the device specific booting instructions - most developers that publish kernels will also publish instructions (or even "boot bundles" that automate the boot).
In the simplest case, one can launch Linux by running the HaRET command:
boot
The above will cause HaRET to load a kernel from the file "kernel" into memory, shutdown critical hardware, and then jump into the kernel. During the process, HaRET will write status messages to the LCD screen.
Additional boot options
One may use a file other than "kernel" to store the Linux kernel. To do so, run
set kernel <filename>
One may load an initrd in addition to a kernel image by running:
set initrd <filename>
One may specify a custom kernel command line:
set cmdline "<cmdline>"
On many devices, HaRET can autodetect the proper Linux kernel machine type (mtype). If HaRET does not detect it, one may run:
set mtype <mtype>To enhance HaRET to autodetect a device, please enable early logging (see Early logging), and email the results along with the proper mtype to us (see Contact Us).
HaRET can double check that the kernel (and optional initrd) are correctly copied to ram prior to jumping to the kernel. It can do this by CRCing the kernel as it is read from disk and again just before jumping to it. To enable, run:
set kernelcrc 1
Some additional variables that control booting are "ramaddr", "ramsize", and "fbduringboot". Please run "help vars" for more info on these settings.
Boot bundles
It is possible to link a Linux kernel, an optional initrd, and command script into a single WinCE program. Doing so makes booting easy. To do this, one will need the HaRET exe, a kernel, an optional initrd, a HaRET command file, and the script at:
http://handhelds.org/cgi-bin/cvsweb.cgi/~checkout~/haret/tools/make-bootbundle.py
Exploring a device
The HaRET application is a powerful tool for researching how the hardware on a WinCE machine works. Indeed, HaRET is primarily a "Handhelds Reverse Engineering Tool".
This section details common commands that developers use to explore a new device - most frequently as a prelude to porting Linux to that device.
Please note - THERE IS NO WARRANTY so USE AT YOUR OWN RISK. WinCE is flaky at the best of times - running HaRET and exploring a device has been known to make WinCE even more flaky. Typing in a wrong command can easily lock up WinCE and require a hard reset. We haven't yet ruined a device using HaRET - but, hey, there is a first for everything..
Obtain haretconsole
In order to fully use HaRET for device inspection one must connect to HaRET via the haretconsole scripts. These python programs can be found in the package at:
http://www.handhelds.org/~koconnor/haret/haretconsole-0.5.2.tar.gz
To run haretconsole, expand the above package on a host PC, make sure the target PDA is available by IP, start HaRET on the PDA, click the "listen" button in HaRET, and run the following on the host PC:
./console <pdaIP>
Once remotely connected to HaRET via haretconsole, one may run commands and interactively receive their responses. The haretconsole program will decode certain raw memory dumps provided by the main HaRET application. All of the following examples will assume one is using haretconsole.
As a useful feature, the haretconsole program keeps logs on the host PC of all data received from HaRET. These files are stored in a file (one per session) with name "haretlog-YYYYMMDD_HHMMSS.log".
Most developers choose to run haretconsole on a Linux host PC. However, it is also possible to use Windows. In this case, get HaretconsolePackageForWin_20090121_NetRipper_2.zip (Haretconsole + Python 2.6) or download
python for Windows, expand the above package, open a command window (Start->Run->cmd.exe), change to the directory haretconsole was expanded to (cd \path\to\haretconsole), and then run "python console <pdaIP>".
Built in command reference
Although this document describes a number of HaRET commands - the application will have many more. To get a list of all commands HaRET supports run the following:
helpAdditionally, running "help dump" will show a handful of additional "hardware dumping" commands, and running "help vars" will show all variables that HaRET is aware of.
Polling GPIOs
When porting Linux to a new device, one of the first things one needs to do is to compile a list of all
GPIO functions. This is done by running the command:
watch GPIOS 1This causes HaRET to repeatedly check the status of GPIOs on the device for one second - reporting any changes in GPIO settings. One may extend the time by replacing the "1" with a longer interval.
After running the above, one might see output like:
HaRET(5)# watch gpios 1 Beginning memory tracing. Watching GPIOS(00): Addr b6600004(@56000004) Watching GPIOS(01): Addr b6600014(@56000014) ... 000.000 GPIOS GPADAT=00000001: GPA0=1 000.000 GPIOS GPBDAT=0000004e: GPB1=1 GPB2=1 GPB3=1 GPB6=1 000.000 GPIOS GPCDAT=00003621: GPC0=1 GPC5=1 GPC9=1 GPC10=1 GPC12=1 GPC13=1 000.000 GPIOS GPDDAT=00000000: ... 000.971 GPIOS GPEDAT: GPE5(133)=0 000.975 GPIOS GPEDAT: GPE5(133)=1 000.979 GPIOS GPEDAT: GPE5(133)=0 000.986 GPIOS GPBDAT: GPB0(32)=1 000.986 GPIOS GPEDAT: GPE5(133)=1 000.991 GPIOS GPBDAT: GPB0(32)=0 000.998 GPIOS GPBDAT: GPB0(32)=1 000.998 GPIOS GPEDAT: GPE5(133)=0 HaRET(6)#Note that the names of the GPIO registers depends on the CPU architecture in use. HaRET starts its report by displaying all the memory addresses it is "watching". Most of the time, this info can be ignored. It then provides a list of the state of the GPIO registers at the start of the watch period - a useful reference. Finally it reports all changed GPIO settings - the most important information.
In each report, the first column (eg, 000.998) is the time, in seconds since command start, that the change was noticed. The second column (eg, GPIOS) is a reminder that one is watching gpios. The third column (eg, GPEDAT) is the name of the architecture register where the change was noticed. The fourth column (eg, GPE5(133)=0) has the individual GPIO name, a HaRET id for it in parenthesis, and the value it has changed to.
When running the above command for the first time, it is common for the screen to constantly scroll with changing GPIO settings. To get to useful information, one needs to ignore the "spamming" gpio registers. This is done by ignoring the unwanted GPIOs:
ibit GPIOS 133 32The "ibit GPIOS" command takes a list of HaRET ids (the numbers reported in parenthesis during "watch GPIOS").
One typically repeatedly runs "watch gpios 1" followed by "ibit gpios x" until "watch gpios 1" produces little or no gpio change reports.
Once one has "watch gpios" into a useful steady state, one can generally figure out most gpio functions by noting how the gpios change during an event. For example, pressing the "camera" button may show:
HaRET(11)# watch gpios 30 Beginning memory tracing. Watching GPIOS(00): Addr b6600004(@56000004) Watching GPIOS(01): Addr b6600014(@56000014) ... 002.269 GPIOS GPGDAT: GPG3(195)=0 <----- button press 002.481 GPIOS GPGDAT: GPG3(195)=1 <----- button release HaRET(12)#The above example indicates GPG3 is attached to the "camera" button.
The key to mapping GPIOs is taking detailed notes and trying many different actions. See Invoking events for things to try.
IMPORTANT LIMITATION - the "watch" command works by repeatedly reading chip settings - generally once every millisecond. Actions on the device may occur too quickly for HaRET to notice them. For example, a gpio may be enabled and disabled within a micro-second - the small change from zero to one may never be seen by HaRET when it polls the registers once every millisecond. In order to explore these types of changes one must use HaRET's Low-level event watching mechanism.
Low-level event watching
The
ARM CPU will invoke exception handlers during special events. Normally, WinCE implements these exception handlers and controls the responses to these events. However, HaRET can replace the WinCE handlers with its own exception handlers. This feature allows HaRET to report the asynchronous events that are occurring on the CPU - this can be a powerful investigation tool.
The exception handler support is installed by running the "wirq" command. This command replaces the WinCE handlers at the start of the command and restores them when the command finishes. For example:
wirq 1Will cause HaRET to handle all exceptions for 1 second. (Replace the "1" with a larger number to support a longer duration.)
Watching interrupts
As with GPIO watching (see Polling GPIOs), porting Linux to a device requires knowing what all the device
IRQs are. The procedure for doing this is similar to the procedure for investigating GPIOs.
Start by running wirq:
wirq 1
After running the above, one might see output like:
HaRET(3)# wirq 1 ... Finished installing exception handlers. 000.000 IRQS INTPND=00000010: EINT4_7=1 000.000 IRQS EINTPEND=00000040: EINT6=1 000.000 IRQS cpldirq=00000000: 000.000 IRQS EINTPEND: EINT6(38)=1 000.000 IRQS EINTPEND: EINT6(38)=1 000.000 IRQS EINTPEND: EINT6(38)=1 ... 000.997 IRQS EINTPEND: EINT6(38)=1 000.997 IRQS EINTPEND: EINT6(38)=1 Restoring windows exception handlers... Finished restoring windows exception handlers. Handled 1862 irq, 4199 abort, 1433 prefetch, 0 lost, 0 errors HaRET(4)#Note that the names of the IRQ registers depends on the CPU architecture in use. The initial output contains diagnostic and state information - in most cases it can be safely ignored. After these startup messages, one will receive a log of irq messages. Finally, at the end of the command, the exception handlers are restored and a brief summary message is provided.
One will notice that the reports from "wirq" are very similar to the reports from "watch gpios". Please see Polling GPIOs for a description of the output.
As with GPIO watching, in order to effectively use "wirq" one may need to ignore the "spamming" irqs. This is done with:
ibit IRQS 38The "ibit IRQS" command takes a list of HaRET ids (the numbers reported in parenthesis during "wirq").
Once all "spamming" irqs have been silenced, one can determine how the device interrupts are used. This is done by invoking an action while in "wirq" and then seeing what interrupts were raised as a result of that action. For example one might see:
HaRET(2)# wi 10 ... 002.299 IRQS INTPND: INT_ADC(31)=1 <=== press touchscreen 002.350 IRQS INTPND: INT_ADC(31)=1 ... HaRET(3)#The above shows that touchscreen events are handled via the "INT_ADC" interrupt.
Note that on PXA machines, the time column will have an extra parameter. For example:
... 041.160(0020377) IRQS ICIP: USBh1(3)=1 041.160(0004156) IRQS ICIP: USBh1(3)=1 041.160(0020057) IRQS ICIP: USBh1(3)=1 ...The event time on PXA machines includes a clock delta in parenthesis. This clock delta represents the number of CPU cycles since the last report - it can be a useful tool in gauging the relative times between events that occur within a millisecond.
The key to mapping IRQs is taking detailed notes and trying many different actions. See Invoking events for things to try.
Catching reads/writes via mmutrace
Probably HaRET's most powerful feature is its ability to trap WinCE reads and writes to memory locations. With this feature, one can monitor exactly how the OS (and its applications, drivers, programs, etc.) interacts with hardware. HaRET can provide a complete log of these accesses - including what was accessed, when it was accessed, and what was sent or received.
The "mmutrace" feature of HaRET achieves this by instructing the ARM CPU to cause an exception when certain memory addresses are accessed. It does this by altering the CPU's
Memory Management Unit. Once HaRET catches one of these artificially generated exceptions it logs the event, completes the memory access, and then seamlessly returns back to the code that made the access.
To use this feature, determine the virtual memory address range that is to be watched. For example:
addlist mmutrace 0xb4700000 0x20 wirq 10The above will report on all accesses to addresses between 0xb4700000 and 0xb4700020. The output might look like:
... 000.158 094f5820: ldrh r3, [r3, #8] # b4700008==0000f500 000.359 094f5ea0: ldrh r3, [r3, #8] # b4700008==00000500 000.990 IRQS INTPND: INT_TICK(8)=1 ...The first column reports the time of the event, the second column shows the address of the instruction that caused the access, the next section shows an assembler representation of the instruction, and the final section shows the address of the memory accessed along with the value that was either read or written.
Note that memory tracing is done while in "wirq", so normal interrupt messages will be interleaved with reports of memory accesses.
HaRET can provide information on the address of the instruction that made the memory access. The command "addr2mod" will report the wince module that contains the instruction. For example:
HaRET(5)# addr2mod 0x094f5820 Address 094f5820 in process: device.exe (08000000 - 0a000000) in module: tiacxwln.dll (094f0000 - 09546000)
It is possible to monitor many different areas of memory simultaneously. To do this, run multiple "addlist mmutrace ..." commands. HaRET will extend the list with each addlist command. Each subsequent call to "wirq" will monitor the complete list. To reset the list, run "clear mmutrace". To see all the addresses currently on the list, run "show mmutrace".
In addition to monitoring multiple ranges of memory, mmutrace supports filtering out just reads or just writes. It also has rudimentary support for ignoring reads/writes of duplicate data. To get info on how to use these features, run "help vars" and read the reference for the MMUTRACE variable. Also, there are some advanced examples at ApachePhoneTrace.
IMPORTANT LIMITATION - the mmutrace feature only supports monitoring accesses from the main CPU. If the system uses
DMA to access a chip, then the HaRET reports will be incomplete or empty. Further, HaRET only supports watching
Virtual Memory addresses - if a given chip is located at a specific physical memory address, then one must take care to monitor all virtual mappings to that physical address range. If WinCE reads/writes to a chip via a virtual mapping that is not monitored then HaRET will not show a complete log. See the output of "dump mmu" to review the WinCE virtual to physical address mapping.
Think you are not seeing all the accesses? There is some additional information at: HaRET Tracing Details
Catching reads/writes on pxa
Haret can configure the Intel PXA debug registers. Users of PXA devices can use this mechanism as an alternative to the mmutrace mechanism. However, this system is less flexible, so it is generally preferable to use mmutrace.
If one is interested in using pxa tracing, run "help vars" and read the descriptions for the following variables: trace, tracemask, trace2, tracetype, trace2type, traceforwatch
High frequency polling
Sometimes it is useful to repeatedly read chip settings and report when they change. The "watch gpios" command does exactly this for the gpio registers. However, it does so only at a rate of 1000 times a second. One can use the TRACES variable to poll for changes during every exception event that wirq catches. This will always be more than 1000 times a second, and it may be much larger depending on how many exceptions the ARM CPU raises.
One easy way to check for all GPIO settings during each exception is to run:
joinlist TRACES GPIOS wirq 10This will append all the GPIOS (along with ibit settings) to the TRACES list. One can clear the TRACES list by running "clear TRACES", and one can view the TRACES list by running "show TRACES".
In addition to polling gpios, one may poll any memory address. For example, one might run:
clear TRACES addlist TRACES p2v(0x4C000000) wirq 10to repeatedly check for changes in the register at physical address 0x4C000000. Note the use of the p2v function - the TRACES variable takes virtual addresses, but one can use the p2v function to convert physical addresses to virtual addresses.
The TRACES variable supports a mechanism for ignoring some reports. Run "help vars" and read the info for the GPIOS variable. (The TRACES and GPIOS variable have the same format.)
Tracing during suspend and resume
It is possible to suspend and resume a device while running "wirq". This can be useful to see what kind of actions WinCE does during a suspend and resume - all of the above "wirq" features should work seamlessly. (Note, aggressively tracing things during a suspend/resume has been known to cause lock ups.)
It is also possible to have HaRET poll memory and chip registers right after the hardware resumes (and before WinCE starts). In order for this to work, the device must have WinCE 4.x or later and the variable RESUMEADDR must be set. (The RESUMEADDR stores the physical location of the WinCE resume vector - it varies by device.) If the above is set, one can add addresses to the RESUMETRACES variable.
An example:
addlist resumetraces 0x48000008 wirq 60Note that RESUMETRACES takes physical addresses - the addresses are polled before the mmu table is initialized.
The RESUMETRACES variable supports many options - run "help vars" and read the help for the GPIOS variable. (The RESUMETRACES and GPIOS variable have the same format.)
Instruction breakpoints
On PXA processors, it is possible to setup instruction breakpoints in HaRET. This can be useful if one knows where a code function is located in memory, and one wishes to be be notified every time that code is run.
For example:
set insn 0x9058eb74 set insn2 0x9058eaac wirq 10The above sets two breakpoints at the specified virtual addresses. When those code addresses are executed, output like the following will occur:
... 001.825(0000053) break 9058eb74: 00000003 00000000 001.825(0000019) break 9058eb78: 00000003 00000000 001.825(0000150) break 9058eaac: 00000004 ffffc730 001.825(0000007) break 9058eab0: 00000004 ffffc730 ...The first column shows the time of the event, the third column shows the address the breakpoint occurred at, the forth column shows the contents of the arm register r0 during the breakpoint, and the fifth column shows the contents of r1.
Note that when tracing on PXA, the time column also shows a "clock delta" in parenthesis. This clock delta can provide more insight into time differences between reports.
Interestingly, both the requested address (9058eb74) and the following address (9058eb78) are reported. This is due to the way the PXA instruction debugging registers work. When a breakpoint is hit, HaRET must disable the breakpoint in order to execute the instruction. In order for future breakpoints to trigger, HaRET enables a breakpoint for the following instruction before returning - when that breakpoint triggers, HaRET disables the secondary breakpoint and reenables the original breakpoint. This implements a "poor man's" single step mode. As a result of this, one should avoid breakpointing instructions that can change the PC. Finally, note that one can instruct HaRET to use an address other than the next instruction as the reenable point - the variables "INSNREENABLE" and "INSN2REENABLE" support this.
By default, HaRET will report the r0 and r1 registers during a breakpoint, but any two registers may be reported. The variables "INSNREG1" and "INSNREG2" control the registers reported during the "insn" breakpoint. The variables "INSN2REG1" and "INSN2REG2" control "insn2" breakpoints.
Invoking events
A common practice is to monitor a series of GPIOs, IRQS, and/or memory addresses while invoking an action. Common actions include:
-
pressing buttons
-
pressing the touch screen
-
inserting/removing sd card
-
enabling/disabling wifi/phone/bluetooth/irda
-
playing sound
-
enabling vibra
-
taking pictures; turning on flashlight
-
plugging in / unplugging USB
-
plugging in / unplugging head phones
In addition to external events (eg, pressing a button) it is also possible to instruct HaRET to invoke an event. To do this, one must log into HaRET twice. This is generally done by pressing the "listen" button and connecting to the device via haretconsole. Then one can press the "listen" button a second time and connect via haretconsole a second time.
Use the first window to monitor actions using "wirq" or "watch gpios". In the second window, one can run HaRET commands. Some sample commands include:
-
NLEDSET - a command that can turn LEDs on and off. The vibra is also usually controlled by this.
-
PLAYSOUND - activates the speaker output
-
SETLCD - turn the LCD on and off.
Please see the HaRET "help" output for more info on how to use these commands.
Misc. commands
This section is a work in progress.
-
Dumping memory
-
dump mmu
-
Get CPU info (dump cp)
-
Obtaining WinCE info (ps, lsmod, powermon)
Extending HaRET
This section is a work in progress.
-
addlist GPIOS
-
addlist IRQS
-
http://handhelds.org/cgi-bin/cvsweb.cgi/~checkout~/haret/src/mach/machlist.txt
Diagnosing problems
HaRET is intended to run on a wide variety of ARM processors running the WinCE software. HaRET has been known to boot on WinCE v2.11 through v6.0. If you run into an issue, please produce a log file of the event and forward it to the HaRET mailing list.
Early logging
The easiest way to enable logging in HaRET is to create a file "earlyharetlog.txt" in the same directory as the haret.exe program. The next time one runs HaRET, it will create a file "haretlog.txt" with logging information.
The resulting "haretlog.txt" file contains information on the device and the actions that HaRET performs during startup. It will also record all commands executed by HaRET.
When reporting an issue, please create the "haretlog.txt" file as described above and forward it (with a description of the problem) to the HaRET mailing list.
Contact Us
Please send any inquiries to the the
HaRET Mailing list. When reporting an issue, please provide the "haretlog.txt" file produced when early logging is enabled (see Early logging).