Saturday 27 June 2009

SATA AHCI mode on systems without BIOS option

Background:
I've got a Dell Poweredge SC430 I bought a couple of years back.
I recently put 4 new WD 1TB drives in it which support native command queuing and installed the latest Ubuntu 9.04 whilst I was rebuilding my server.
Anyway, I noticed linux was only using the ata_piix module for my onboard SATA ICH7 family controller (integrated in the Intel Chipset). As some background, many SATA controllers offer a legacy mode (ATA/IDE emulation mode) and native mode (AHCI or RAID). Normally you can change which mode the controller is in via the BIOS hoever for whatever reason Dell chose not to include this option in my system.

Problem: The hardware ID that the controller reports to the system is dependant on which mode the controller is in. This means that the OS can load the appropriate driver depending on the mode of the controller. For Windows users the solution is aparently easy, you can force Windows to use the AHCI driver when the controller is still in the legacy mode. I presume the driver then detects that the controller is in legacy mode and instructs it to change to AHCI mode on the fly.

Unfortunately for linux this isn't exactly easy to do. To force linux to use a particular module with hardware that it doesn't have it must have it's hardware ID listed in the driver when the module is built. This means compiling the kernel from source. This also means re-compiling the kernel from source whenever you want to update your kernel, not something I want to have to deal with.

Fortunately some clever Mac Pro users have found another way. Instead of modifying the linux kernel, they edited the stage1 part of the grub bootloader to add some assembly commands to instruct the controller to switch modes. This is a fantastic hack for several reasons:
  1. As grub can boot virtually any OS, this hack will ensure AHCI mode is available for any OS, not just linux
  2. Grub, and especially the stage1 file, is relatively static and doesn't get updated too often, so no need to worry about patching new versions continually
  3. No need to worry about kernel updates clobbering the patch and no need to compile the kernel from source every time there is an update
Solution: Note, at the time of writing this, I used grub 0.97, you might need to change some directory paths appropriately if you use a different version.

This patch, whilst originally written for a Mac Pro with an Intel 631xESB/632xESB Controller just happens to work perfectly on my Dell with an ICH7 Controller. Note that I did indeed verify that the commands to switch SATA mode were identical by comparing the Intel documentation here: http://www.intel.com/Assets/PDF/datasheet/313082.pdf Port Mapping Register (SATA–D31:F2) on page 764 and here: http://www.intel.com/Assets/PDF/datasheet/307013.pdf Sub Class Code Register (SATA–D31:F2) on page 495.

You should verify at least two things before trying this patch:
  • ensure your chipset supports AHCI (not all ICH7 variants do)
  • ensure your chipset has the same format command at the same offset to switch modes (if it is an Intel, I'd hope that they're consistent, they seem to be so far)
If you're happy to try this out, here's how you do it:
  1. Get the source of grub, in ubuntu:
    sudo apt-get source grub
  2. If you're using and x64 OS you'll need the 32bit libc dev files:
    sudo apt-get install libc6-dev-i386
  3. Get the patch:
    http://boeglin.org/static/macpro/grub-0.97_macpro_esb2_ahci_stage1_new.patch
  4. Apply the patch
    patch grub-0.97/stage1/stage1.S grub-0.97_macpro_esb2_ahci_stage1_new.patch
  5. Configure grub
    cd grub-0.97; ./configure
  6. Build grub:
    make all
  7. Find your grub installed libraries. eg:
    cd /usr/lib/grub/x86_64-pc
  8. Backup your grub existing grub stage1 file
  9. Copy the compiled stage1 to your grub library directory
  10. Update your grub bootloader. eg:
    grub-install hd0
  11. Note: grub-install will overwrite /boot/grub/stage1 with the stage1 from your library directory so you'll need to make sure you're putting the patched stage1 in the library directory NOT the /boot/grub or equivalent directory
  12. Reboot and verify your controller is now using the ahci module!