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
446448640
From 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)''