Debian GNU/Linux on an IBM Thinkpad X40

I bit the proverbial bullet recently and bought a new laptop. I'm not one for shopping around, and a quick survey suggested that IBM Thinkpad X40s were lightweight, sufficiently powerful and well-supported by Linux. I ordered mine directly from IBM and it landed on my doorstep just under a fortnight later, mummified in several cardboard boxes and lots of tape.

I'll give away the ending now and say that I'm extremely happy with this laptop. Despite not having a cdrom or floppy drive, installing Debian was a breeze, all of my hardware (except for the wireless card) was detected and worked out of the box (for the wireless card, I had to download a separate driver, but that was simple), it's lightweight, has a nice keyboard and it's quiet.


Processor: Intel(R) Pentium(R) M processor 1.50GHz
Video: Intel 855GME
Sound: Intel 82801DB/DBL/DBM (ICH4/ICH4-L/ICH4-M) AC'97 Audio Controller
Built-in ethernet: Intel 82541GI Gigabit Ethernet Controller
Built-in wireless: Intel PRO/Wireless 2200BG

Installing Debian

I decided to boot the Debian installer off my network (with no cdrom or floppy drives I didn't have many options). I'd never tried this before, but it turned out to be pretty straightforward.

There are basically three things you need to boot off the network: a DHCP server to tell the laptop its IP address and the location of the boot image, a TFTP daemon to allow the laptop to download that boot image, and the boot image itself.

I was already running a DHCP server (apt-get install dhcp) on one of my machines, so I just added a new entry for my laptop:

option domain-name "telefunken.dyn.ml.org";
option domain-name-servers;
option routers;
option subnet-mask;
default-lease-time 172800;
max-lease-time 172800;

host thinkpad {
  filename "/boot/pxelinux.0";
  hardware ethernet XX:XX:XX:XX:XX:XX;

The above config gives the laptop the IP address and tells it to retrieve the boot image from /boot/pxelinux.0. Next I installed atftpd (apt-get install atftpd) and enabled it in /etc/inetd.conf with (all on one line):

tftp  dgram  udp  wait  nobody  /usr/sbin/tcpd /usr/sbin/in.tftpd
--tftpd-timeout 300 --retry-timeout 5 --mcast-port 1758
--mcast-ttl 1 --maxthread 100 --verbose=5  /boot

Finally, I downloaded netboot.tar.gz from my local Debian mirror and unpacked the tarball into /boot/. This gives you the /boot/pxelinux.0 file that was referenced in dhcpd.conf.

With all of that set up, I connected my laptop to the network and told it to boot off the network (by tapping F12 as it powered on and selecting the appropriate device from the boot menu). A moment later I was staring at the Debian installer. Hurrah!


I expected setting up the build-in wireless card to be difficult, but it wasn't. The wireless card needs the modules and firmware from http://ipw2200.sf.net/, which in turn requires the 802.11 wireless network stack from http://ieee80211.sourceforge.net/. In my case, I apt-get'd (apt-got?) the Debian packages ipw2200-source and ieee80211-source. To build the modules, you will also need the linux-headers package for the kernel you are running. I used linux-headers-2.6.12-1-686.

To build a deb of the ieee80211 module, I ran the following commands:

# cd /usr/src/
# tar xvzf ieee80211-source.tar.gz
# cd modules/ieee80211/
# debian/rules binary-modules KSRC=/usr/src/linux-headers-2.6.12-1-686/ KVERS=2.6.12-1-686
# dpkg -i ../ieee80211-modules-2.6.12-1-686_1.0.3-2_i386.deb

and to build the ipw2200 module:

# cd /usr/src/
# tar xvzf ipw2200-source.tar.gz
# cd modules/ipw2200/
# debian/rules binary-modules KSRC=/usr/src/linux-headers-2.6.12-1-686/ KVERS=2.6.12-1-686
# dpkg -i ../ipw2200-modules-2.6.12-1-686_1.0.6-2_i386.deb

Finally, I grabbed the latest firmware linked from http://ipw2200.sf.net/ (which was ipw2200-fw-2.3.tgz), and unpacked it into /usr/lib/hotplug/firmware/:

# cd /usr/lib/hotplug/firmware/
# tar xvzf ~/ipw2200-fw-2.3.tgz
# /etc/init.d/hotplug restart

My wireless card was assigned device eth1, and I added the following entry to my /etc/network/interfaces file:

auto eth1
iface eth1 inet static
  pre-up iwconfig eth1 essid "mark" channel 6 mode ad-hoc

(iwconfig is available in the wireless-tools package in Debian.)


To encrypt data in transit over the wireless link, I set up OpenVPN. My setup is fairly low-tech:

# apt-get install openvpn
# mkdir /etc/openvpn/
# openvpn --genkey --secret /etc/openvpn/key
# chmod 600 /etc/openvpn/key

and the init script that starts the OpenVPN link:


case $1 in


openvpn --remote --dev tun0 --ifconfig \ --verb 0 --secret /etc/openvpn/key --daemon \
        --port 5000
route add default gw



pkill -f 'openvpn.*tun'
route del default gw



In the above, the 192.168.3.x subnet is OpenVPN, 192.168.2.x is the wireless link. My laptop's IP address is .2, while my other machine's IP is .1.


I didn't have to do anything special to get X set up. I apt-get install'd the xserver-xorg package, which politely offered to auto-detect my hardware. It picked up my video card and mouse with no problems.

Just for reference, here's my xorg.conf file:

Section "Files"
        FontPath    "unix/:7100"            # local font server
        # if the local font server has problems, we can fall back on these
        FontPath    "/usr/lib/X11/fonts/misc"
        FontPath    "/usr/lib/X11/fonts/cyrillic"
        FontPath    "/usr/lib/X11/fonts/100dpi/:unscaled"
        FontPath    "/usr/lib/X11/fonts/75dpi/:unscaled"
        FontPath    "/usr/lib/X11/fonts/Type1"
        FontPath    "/usr/lib/X11/fonts/CID"
        FontPath    "/usr/lib/X11/fonts/100dpi"
        FontPath    "/usr/lib/X11/fonts/75dpi"

Section "Module"
        Load    "bitmap"
        Load    "dbe"
        Load    "ddc"
        Load    "dri"
        Load    "extmod"
        Load    "freetype"
        Load    "glx"
        Load    "int10"
        Load    "record"
        Load    "type1"
        Load    "v4l"
        Load    "vbe"

Section "InputDevice"
        Identifier  "Generic Keyboard"
        Driver      "keyboard"
        Option      "CoreKeyboard"
        Option      "XkbRules"  "xorg"
        Option      "XkbModel"  "pc104"
        Option      "XkbLayout" "us"

Section "InputDevice"
        Identifier  "Configured Mouse"
        Driver      "mouse"
        Option      "CorePointer"
        Option      "Device"        "/dev/input/mice"
        Option      "Protocol"      "ImPS/2"
        Option      "Emulate3Buttons"   "true"
        Option      "ZAxisMapping"      "4 5"

Section "Device"
        Identifier  "Intel Corporation 82852/855GM Integrated Graphics Device"
        Driver      "i810"
        BusID       "PCI:0:2:0"

Section "Monitor"
        Identifier  "Generic Monitor"
        Option      "DPMS"
        HorizSync   30-65
        VertRefresh 30-60

Section "Screen"
        Identifier  "Default Screen"
        Device      "Intel Corporation 82852/855GM Integrated Graphics Device"
        Monitor     "Generic Monitor"
        DefaultDepth    24
        SubSection "Display"
                Depth       1
                Modes       "1280x768" "1024x768" "800x600" "640x480"
        SubSection "Display"
                Depth       4
                Modes       "1280x768" "1024x768" "800x600" "640x480"
        SubSection "Display"
                Depth       8
                Modes       "1280x768" "1024x768" "800x600" "640x480"
        SubSection "Display"
                Depth       15
                Modes       "1280x768" "1024x768" "800x600" "640x480"
        SubSection "Display"
                Depth       16
                Modes       "1280x768" "1024x768" "800x600" "640x480"
        SubSection "Display"
                Depth       24
                Modes       "1280x768" "1024x768" "800x600" "640x480"

Section "ServerLayout"
        Identifier  "Default Layout"
        Screen      "Default Screen"
        InputDevice "Generic Keyboard"
        InputDevice "Configured Mouse"

Section "DRI"
        Mode    0666


My sound card was supported by the standard modules that ship with the Debian kernel package, so I didn't have to do any setting up. It appears that I'm using the i810_audio module.

Power management

Suspend to memory

To make suspending work, I compiled and installed the ibm-acpi module from http://ibm-acpi.sourceforge.net/. While this ships with kernel 2.6.10 and later, I thought I'd be adventurous and install the latest version. The steps for installing this were:

# cd /usr/src/
# tar xvzf ibm-acpi-0.11.tar.gz
# cd ibm-acpi-0.11
# make
# make install
# modprobe ibm_acpi

Next I installed acpid and modified it to ignore power button events (normally it would init 0). This just required commenting out everything in /etc/acpi/events/powerbtn.

Finally, I had to pass the kernel the acpi_sleep=s3_bios parameter, which I added to my /boot/grub/menu.lst file.

After rebooting, I was able to suspend my laptop with a command like:

# echo 3 > /proc/acpi/sleep

and wake it up again by pressing the power button.

CPU speedstep

Making CPU speedstep work was trivial. I just ensured that the cpufreq_userspace and speedstep-centrino modules were loaded at boot time, and installed the powernowd package (apt-get install powernowd).