On the 2003 April 1st (no joke!) I got an IBM ThinkPad R31 and
I've yet to find a better system for running Debian GNU/Linux on.
Everything works including the LinModem (binary only...) and
hardware APM hibernation (this document); in addition the keyboard
is better than most and there are 3 Mouse buttons. The bummer is
that you have to install Windows 98 to run the Acer/IBM
SleepMgr.exe
program. It claims to run under some form
of DOS aswell, but I've failed. Oh, sorry one thing
doesn't work—The S-Video TV Out!
Aftering installing Windows 98 over the remains of the IBM Rescue partition, I initially did some work on reverse-engerineering the system during June 2003. Then, at the start of May 2004 I decided I wanted to repartition my laptop disk and so I needed to write a program that could produce the magic file that I depend on for hibernation—my laptop has uptimes of over 30-days of being powered on between reboots (about 3-4 months in real-time).
The SleepMgr (Sleep Manager) program creates a magic file in the
root of one of your FAT16 or FAT32 partitions name
`acr_0v.dat
' which stands for ``Acer Zero-Volt
Suspend'' (ie, it draws no voltage when powered off). Some other
BIOS hibernate systems depend on it being a FAT file-system because
they intelligently read the filesystem. With the Acer/IBM
filesystem this does not appear to be the case.
When creating the hibernation file SleepMgr does lots of work to ensure the file is contiguous. There is a reason for this; a value is stored in the CMOS which is 28-bit pointer to the start of the file on the physical disk. This is the number of sectors from the start of the hard-disk which means that copying or duplicating the `acr_0v.dat' file does not work—it is the contents on the physical disk that matter.
There are copies of SleepMgr.exe at the location below (there are about 3 versions):
There is code that I have written for GNU/Linux called
`acrdisk
', which can setup up the NVRAM and create the
necesary partition headers (md5sum the same as SleepMgr!) But it does not configure the BIOS yet, I'm currently stuck with
that!
Key: Offset Generate-able Copy-able Dunno Diff'ed Hex Description Ascii
The CMOS (Non-Volatile RAM) is exposed under Linux via
`/dev/nvram
' driver. Dumping the CMOS via I/O commands
yields 128-bytes of data but the `nvram
' device is only
114-bytes long, this is because the first 14-bytes map to the
Real-Time Clock (RTC) and are hidden by the driver. The offsets in
the dump below are from reading out the offsets before I realised that
`/dev/nvram' accomplished the same thing alot more easily (and with
locking and automatic checksumming!).
An advantage of writing and reading from `/dev/nvram' is that I can
replace it with a normal file for testing purposes—this has
proved very useful. After various bits of ignoring how other people
believed the system worked, I dug the following reference in the CMOS
(subtract 14 for the offset in `/dev/nvram
'):
-00000050 xx xx xx xx b3 4b 50 00 00 08 xx xx xx xx xx xx |................| +00000050 7b 77 37 00 1c b1 5a 03 aa 2c 80 08 01 42 20 31 |................| ^^^^^^^^^^^ -- ^^ le: LBA Off Checksum? [matches file]
So the `/dev/nvram
' offsets that matter are:
0x46
→0x49
(4 bytes LE)
28-bit LBA offset (Logical Block Address) to start of
hibernation block on the first system disk.0x4a
(1 byte)
Flag: `0xaa
' means created, `0x00
' means
not created.0x4b
(1 byte)
Negative of the Sum of the previous 5 bytes.I finally understand the checksum and why it is so. The full
128-byte of the CMOS is checksummed by adding up each byte and the
result must equal zero. The checksum at 0x4a
is exactly
the same and has the cunning effect that across the 6 bytes used for
hibernate the net-difference is still zero meaning that the global
CMOS checksum need not be updated!
So, following that pointer in the CMOS we can find the start of the hibernation block; to make sure that we've found it, the first 512-bytes are a 1-sector header block. This can be used to double check that the BIOS has found the right place and it is correctly setup. In addition is contains information on the amount of space allocated.
00000000 41 43 45 52 20 30 56 20 53 55 53 50 45 4e 44 2e |ACER 0V SUSPEND.| ------------------------------------------------ A C E R 0 V S U S P E N D . -00000010 00 00 00 92 10 00 00 00 aa 63 ce 8a 00 41 43 52 |........?c?..ACR| +00000010 00 00 00 92 18 00 00 00 aa 1c b1 5a 03 41 43 52 |........?.?Z.ACR| -- ^^^^^^^^^^^ -------- ^^ ^^^^^^^^^^^ -------- \0 RAM bytes Flag LBA offset A C R 00000020 5f 30 56 20 20 44 41 54 ff ff 00 00 ff ff 00 00 |_0V DAT??..??..| ----------------------- ----------- ----------- _ 0 V D A T -00000030 00 00 ed f1 d4 3e 9b 00 00 00 00 00 00 00 00 00 |..???>..........| +00000030 00 00 03 67 ff 3e 2c 00 00 00 00 00 00 00 00 00 |...g?>,.........| ^^^^^^^^^^^ ^^ Timestamp Checksum [Same as BIOS?. 512-sum(0x32..0x35)] 00000xxx 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| ----------------------- ----------------------- [rest of header is padded with nulls] 000001f0 00 00 00 00 00 00 00 00 00 00 00 00 00 01 00 62 |...............b| ----------------------- ----------- ----- ----- Version Num?
After the on-disk header, there is initially a piece of firmware dumped...
00000000 eb 36 jmp short 0x38 00000002 00 00 38 00 00 01 86 ad 94 06 00 41 43 52 |.6..8........ACR| ^^^^^ ^^^^^^^^^^^ ^^^^^^^^ Header? Length A C R 00000010 30 30 31 30 30 ad 94 06 00 00 38 00 05 01 02 00 |00100.....8.....| ^^^^^^^^^^^^^^ ^^^^^^^^^^^^ ^^^^^ ^^ 0 0 1 0 0 Length Start? Digits? 00000020 00 03 00 04 e8 5d 00 cf 00 03 00 04 e0 0d ae 0b |.....]..........| 00000030 00 00 00 00 00 00 00 00 |........`....t =| 00000038 60 pusha 00000039 1e push ds 0000003a 06 push es 0000003b 0b c0 or ax,ax 0000003d 74 20 jz 0x5f 0000003f 3d 01 00 cmp ax,0x1 00000042 74 20 jz 0x64 00000044 3d 02 00 cmp ax,0x2 00000047 74 20 jz 0x69 00000049 3d 03 00 cmp ax,0x3 0000004c 74 20 jz 0x6e 0000004e 3d 04 00 cmp ax,0x4 00000051 74 20 jz 0x73 00000053 3d 80 00 cmp ax,0x80 00000056 74 20 jz 0x78 00000058 3d 81 00 cmp ax,0x81 0000005b 74 20 jz 0x7d 0000005d eb 21 jmp short 0x80 0000005f e8 23 00 call 0x85 00000062 eb 1c jmp short 0x80 00000064 e8 e1 01 call 0x248 00000067 eb 17 jmp short 0x80 00000069 e8 4e 02 call 0x2ba 0000006c eb 12 jmp short 0x80 0000006e e8 73 02 call 0x2e4 00000071 eb 0d jmp short 0x80 00000073 e8 98 05 call 0x60e 00000076 eb 08 jmp short 0x80 00000078 e8 59 07 call 0x7d4 0000007b eb 03 jmp short 0x80 0000007d e8 86 07 call 0x806 00000080 07 pop es 00000081 1f pop ds 00000082 61 popa 00000083 cb retf
import time time.localtime(0x3eff6703) time.localtime(0x3ed4faed) (2003, 6, 29, 23, 24, 3, 6, 180, 1) (2003, 5, 28, 19, 7, 41, 2, 148, 1) 0x3e 0011 1110 0xf 1111 0xe 1110 0xd 1101 0xc 1100 0xb 1011 0xa 1010 0x9 1001 0x8 1000 0x7 0111 0x6 0110 0x5 0101 0x4 0100 0x3 0011 0x2 0010 0x1 0001 0x0 0000 print hex((0x0 - 0x63 - 0xce - 0x8a - 0x00 - 0xaa) & 0xff) 0x print 0x00edf1d4 15593940 print 0xd4f1ed00 -722342656 print 0x000367ff 223231 print 0xff670300 -10026240 >>> hex(0xaa-0x92) '0x18' print hex(512-0xb3+0x4b+0x50+0xaa+0x08) >>> hex(0x1c+0xb1+0x5a+0x03+0xaa+0x2c) '0x200' >>> hex(512-0x1c-0xb1-0x5a-0x03-0xaa) '0x2c' >>> 0x367ff3e-0x35ab11c >>> 0x367ff3e-0x35ab11c 871970 >>> (0x367ff3e-0x35ab11c)*512 446448640From some Googling, it looks like this hibernation setup may well be the same as the IBM ThinkPad 310/310E/315/315E/i1400/i1100. These are presumably other models that have come via the same Acer/Twainese OEM supply chain in the past.
>>> hex(0x1c+0xb1+0x5a+0x03) '0x12a' >>> hex(0x1c+0xb1+0x5a+0x03-0x2c) '0xfe' >>> hex(0x1c+0xb1+0x5a+0x03-0x2c^0xff) '0x1' >>> iSeries (IBM) Thinkpad 1161 IBM THINKPAD i 1450 TP 310E/ED,315E/ED ThinkPad 310E ``Upon restoring system from 0V suspend, you need to wait a while (about 10 seconds)''