photo.png

By RicardoYanez. This page describes a method to install Debian on Apple Intel Mac Pro computers. It has been tested in two computer systems with a Quad-Core and dual Quad-core Xeon 5300-series "Clovertown" processors.

(If you are trying to install onto anything other than the primary disk drive (i.e. the one that Mac OS X boots from), then read the "Booting from a second disk" section below (comments on that section to ?MatthewVernon at his Debian address, please).)

If you have already tried to install Debian on Intel Mac Pro, you may have noticed the Debian Installer CD doesn't mount the CD-ROM properly. Eventually, no media is available to continue with the installation.

For the inpatient, the next section explains how to install Debian with a custom Debian Installer mini CD and custom Linux kernel in few steps.

In the "Details" section I explain how I compiled the custom kernel and how I made the custom Debian Installer CD.

If you have a MacBook Pro, you may want to see MacBookPro instead.

Debian Installation

Download the Debian Installer Mini CD for Intel Mac Pro (MD5SUM 023dadbea2bb5bc064d81b4fc62f184f), burn it to a CD, for example,

$ wodim -v -eject dev=/dev/cdrw -data mini.iso

Being a mini CD, it contains no .deb packages. You will need a network connection to install the base system.

Under Mac OS X, install rEFIt, the EFI boot menu for Intel Mac. Easiest is to download the Mac disk image, double click on the icon and run the installer.

Still under Mac OS X, open the CD tray, insert the Debian Installer Mini CD and reboot. In the boot menu choose the penguin CD.

Boot the installer in expert mode. Install as you would in any system.

Choose to partition manually, then set the root partition bootable flag on.

During the installation of the base system, choose 'none' for the kernel to install.

Choose to continue without boot loader.

Just before finishing the installation, open a virtual terminal, <ctrl>-<alt>-<f2> for instance, and run a shell. Change the root directory with,

# chroot /target

Install grub and initramfs-tools with,

# apt-get install grub initramfs-tools

Run,

# grub-install /dev/sda

to install GRUB on the first disk drive.

Edit /etc/apt/sources.list and add line,

 deb http://apt.debianchile.org/macpro etch main

or

 deb http://apt.debianchile.org/macpro lenny main

(untested)

You may want to install the archive key as well,

# gpg --keyserver hkp://pgp.mit.edu --recv-keys EFD17969
# gpg --export EFD17969 | apt-key add -

Run 'apt-get update' and install the custom Etch kernel,

# apt-get install linux-image-2.6.20-2-macpro-amd64

or the custom Lenny kernel (untested),

# apt-get install linux-image-2.6.26-2-macpro-amd64

Say no to abort kernel installation.

Run update-grub and create /boot/grub/menu.lst.

Go back to the installation terminal, <ctrl>-<alt>-<f1> and finish the installation.

In the boot menu choose the penguin disk.

After boot, you may want to install the kernel headers,

# apt-get install linux-headers-2.6.20-2-macpro-amd64

or

# apt-get install linux-headers-2.6.26-2-macpro-amd64

If you like to make Debian the first boot device, under Mac OS X, edit /efi/refit/refit.conf and uncomment option legacyfirst.

If you feel uncomfortable using my custom kernel, compile your own using the mactel-linux patches as described below.

Details

(This part requires some familiarity with kernel compilation and debian packaging).

I have two 64-bit dual Quad-Core Xeon E5335 "Clovertown" machines at work. One is an HP server, the other an Apple Intel Mac Pro. Both are primarily used to run and develop Monte Carlo simulations in parallel, or otherwise.

I obviously had no trouble installing Debian Etch on the HP server. On the Mac, the first thing one needs to do is to install rEFIt under Mac OS X, open the CD tray to insert the installation CD, boot and choose the penguin.

The Debian installer boots, but it doesn't mount the CD-ROM. Eventually, no media is available to continue with the base-system installation. I tried the standard approaches given in several wikies on the subject, e.g. boot parameters like 'install noapic irqpoll acpi=force', and combinations thereof, but none really helped. I also tried a USB-key installation, only to learn Mac firmware doesn't support USB-key installations, at least in the machines I have.

Then a co-worker handed me an Ubuntu 7.04 Feisty server installation CD, which recognized the CD-ROM, mounted the CD, installed the base system, and went all the way to the finish line. After boot, the kernel would not load, though. I read several guides describing some very intricate recipes, which included the need of Live CDs and disk partition utilities. None of these approaches is really necessary, once you realize the only thing they are trying to do is to set the boot flag on the root partition.

This exercise made me realize the installer needs to attach the CD-ROM not as ATAPI IDE (ide-cd) as the Debian Installer does, but under SATA (not sure here why, nor if this statement is absolutely true.)

My plan of action was then to compile a custom kernel a la Ubuntu (which has the Mactel-linux patches included) that attaches the CD-ROM as the hardware requires, then make a new Debian Installer CD with it.

I followed closely DebianInstaller/Modify/CustomKernel and DebianInstaller/BuildEtch.

In short, kernel 2.6.20 is compiled with the mactel-linux patches, then debian-installer is used to create the mini installation image.

Custom kernel 2.6

I compiled the kernel in the HP server running Debian Etch. I used kernel version 2.6.20, downloaded from kernel.org. Untar the tarball in /usr/src.

Install subversion and get the mactel-linux kernel patches,

# svn co https://mactel-linux.svn.sourceforge.net/svnroot/mactel-linux mactel-linux

Go to trunk/kernel/mactel-patches-[kernel-version] and apply patches with,

# ./apply /usr/src/[linux-source-directory]

Install kernel-package and libncurses5-dev. Go the the kernel source directory and 'make menuconfig'.

This is the current .config file.

Before compiling, edit Makefile and remove the EXTRAVERSION introduced by the Mactel-linux patching. Leave it blank.

Compile with,

# make-kpkg --initrd --append-to-version=-1-macpro-amd64 --revision=2.6.20 kernel_image kernel_headers

Install the custom kernel. No need to boot it in what follows.

Kernel udebs

I'm repeating here some, if not most, of DebianInstaller/Modify/CustomKernel.

Install devscripts and kernel-wedge.

Unpack the source package linux-kernel-di-amd64-2.6,

# apt-get source linux-kernel-di-amd64-2.6

Go to the directory and edit kernel-versions to match the new version, 2.6.20-1-macpro-amd64. Comment out or delete the existing line.

Check for build dependencies with dpkg-checkbuilddeps, and install dependencies as needed.

If you have a GPG signature you can do 'debchange -i' to edit the changelog with your e-mail address and some comments. The directory name will change, reflecting the new version number. You will have to go to the parent directory, then back into the new directory. You may also want to change the Maintainer: field in debian/control.stub, and define the EMAIL and GNUPGHOME environment variables in the user's .bashrc, then source it.

Build the package with debuild, or 'debuild -rfakeroot' if not root.

If you have no GPG signature, build with 'debuild binary', or 'debuild -rfakeroot binary' if not root instead.

The building may fail due to missing kernel modules. Edit module listings in modules/amd64 needed be. I copied some of the include files into the directory and put a ? at the end of the missing module. See kernel-wedge documentation for more options. I had to delete some module-list files that generated empty udebs.

If you have a GPG signature, sign the files.

You should end up with a bunch of udebs in the parent directory.

Debian Installer

Get the source,

# apt-get source debian-installer

Go to directory and check for dependencies.

Edit build/config/amd64.cfg and change KERNELVERSION. Uncomment monolithic image type.

Copy all kernel udebs into build/localudebs.

Go to build/ and build the monolithic image,

# make build_monolithic

The image is placed in dest/monolithic.

Now you have the CD image and the kernel image. Install as described in the installation section.

Booting from a second disk

By ?MatthewVernon

rEFIt assumes that you have only one disk drive. If you try and install linux onto a secondary drive, you will probably have found that rEFIt lets you try and boot your newly-minted linux partition/drive, only for you to get a "Missing operating system" error message. This is actually a Syslinux error message. What happens is that rEFIt looks on the primary disk for an MBR record, fails to find one (obviously!), so sticks the syslinux MBR onto the primary disk, and tries to boot that. You can confirm that this has happened, with some runes like the following:

# dd if=/dev/sda of=/tmp/mbr bs=512 count=1
# file /tmp/mbr

Hopefully this rEFIt bug will be fixed, but in the mean-time, the best work-around is to install GRUB or LILO onto the MBR of the primary hard-disk, and use that to boot your partition. This means you'll end up having to select linux at the rEFIt menu, and then again in the GRUB or LILO menu, but you can always turn the timeout down very low in LILO ;-) Something like this in lilo.conf:

boot=/dev/sda
root=/dev/sdb2
image=/boot/vmlinuz-2.6.20-2-macpro-amd64
        label="Lin 2.6.20img0"
        initrd=/boot/initrd.img-2.6.20-2-macpro-amd64
        read-only

Should do the trick.

Fan speed control

By ?AlexeyAnikeenko

If your Mac Pro does not change the fan speeds automatically, it can lead to overheating under heavy loads. This daemon monitors current core temperatures and sets minimal rotation speed for fans under control. You should check MIN/MAX values and temperature_to_speed_step() function before use.

   1 /*
   2  *  smcfancontrol.c : fan control daemon for Apple Intel Mac Pro 
   3  *  based on cmp-daemon 0.21 from http://aur.archlinux.org/packages.php?ID=21391
   4  *
   5  *  Copyright (C) 2009 Alexey Anikeenko
   6  *
   7  *  This program is free software; you can redistribute it and/or modify
   8  *  it under the terms of the GNU General Public License as published by
   9  *  the Free Software Foundation; either version 2 of the License, or
  10  *  (at your option) any later version.
  11  *
  12  *  This program is distributed in the hope that it will be useful, 
  13  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  14  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  15  *  GNU General Public License for more details.
  16  *
  17  *  You should have received a copy of the GNU General Public License
  18  *  along with this program; if not, write to the Free Software
  19  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
  20  */
  21 
  22 #include <stdio.h>
  23 #include <stdlib.h>
  24 #include <unistd.h>
  25 #include <sys/types.h>
  26 #include <sys/stat.h>
  27 #include <errno.h>
  28 #include <fcntl.h>
  29 #include <time.h>
  30 #include <signal.h>
  31 #include <string.h>
  32 #include <syslog.h>
  33 
  34 #define SYSFS_PATH_MAX 256
  35 #define SPEED_STEP_MAX 20
  36 
  37 static const char *PIDFILE = "/var/run/smcfancontrol.pid";
  38 static const char *SMCDIR = "/sys/devices/platform/applesmc.768";
  39 static const char *CORETEMP_PREFIX = "/sys/devices/platform/coretemp";
  40 
  41 static const struct timespec smc_write_delay = { 0, 5000000 }; /* 5 ms delay between writes to smc controller */ 
  42 
  43 /* Reference data for dual-Xeon_X5482 Mac Pro:
  44 const int FANS_EFI_MIN[] = { 500, 800, 600, 600 };
  45 const int FANS_EFI_MAX[] = { 2900, 2900, 2900, 2800 };
  46 const char *FANS_LABEL[] = { "CPU_MEM", "IO", "EXHAUST", "PS" };
  47 */ 
  48 
  49 const int FANS[] = { 1, 2, 3 };  /* suffixes of fans under control */
  50 const int FANS_MIN[] = { 800, 800, 600 };
  51 const int FANS_MAX[] = { 2200, 2200, 2200 };
  52 const int FANS_NUM = sizeof(FANS)/sizeof(FANS[0]);
  53 
  54 const int SENSORS[] = { 0, 1, 2, 3, 4, 5, 6, 7 };  /* suffixes of used coretemp sensors */
  55 const int SENSORS_NUM = sizeof(SENSORS)/sizeof(SENSORS[0]);
  56 
  57 static int get_sensors_temp(void);
  58 static void set_fans_manual(int);
  59 static void set_fans_min(int);
  60 
  61 
  62 /*
  63  * Fan speed depends linearly on the speed step and changes 
  64  * between FAN_MIN and FAN_MAX for steps in range [0, SPEED_STEP_MAX]. 
  65  * This function converts current temperature to speed step 
  66  * and should be calibrated for your box! 
  67  */
  68 int temperature_to_speed_step(int t)
  69 {
  70     int step = ((t-45)*SPEED_STEP_MAX)/(75-45);  
  71 
  72     if (step < 0 ) step = 0;
  73     if (step > SPEED_STEP_MAX ) step = SPEED_STEP_MAX;
  74 
  75     return step;
  76 }
  77 
  78 
  79 /* clean up and exit */
  80 static void clean_exit_with_status(int status)
  81 {
  82     set_fans_manual(0); /* set fans to automatic control */
  83     syslog(LOG_NOTICE, "exiting\n");
  84     unlink(PIDFILE);    /* remove pidfile */
  85     exit(status);
  86 }
  87 
  88 /* signal handler for clean exit */
  89 static void clean_exit(int sig __attribute__((unused)))
  90 {
  91     clean_exit_with_status(EXIT_SUCCESS);
  92 }
  93 
  94 
  95 /* set fanX_min (min rotation speed) according to current speed step */
  96 static void set_fans_min(int speed_step)
  97 {
  98     int i;
  99     FILE *file;
 100     char fan_path[SYSFS_PATH_MAX];
 101 
 102     /* for fans under control write rpm to fanX_min */
 103     for(i=0; i < FANS_NUM; i++ )
 104     {
 105         /* fan speed depends linearly on the speed step and changes 
 106          * between FAN_MIN and FAN_MAX for steps in range [0, SPEED_STEP_MAX] */
 107         int speed = FANS_MIN[i] + speed_step*(FANS_MAX[i]-FANS_MIN[i])/SPEED_STEP_MAX;
 108 
 109         sprintf(fan_path, "%s/fan%d_min", SMCDIR, FANS[i]);
 110         nanosleep(&smc_write_delay, NULL);
 111         if ((file=fopen(fan_path, "w")) != NULL) {
 112             fprintf(file, "%d", speed);
 113             fclose(file);
 114         }
 115         else {
 116             syslog(LOG_WARNING, "Error writing to %s, check if applesmc module loaded", fan_path);
 117         }
 118     }
 119 }
 120 
 121 
 122 /* write value to fanX_manual */
 123 static void set_fans_manual(int value)
 124 {
 125     int i;
 126     FILE *file;
 127     char fan_path[SYSFS_PATH_MAX];
 128 
 129     /* for fans under control write value to fanX_manual */
 130     for(i=0; i < FANS_NUM; i++ )
 131     {
 132         sprintf(fan_path, "%s/fan%d_manual", SMCDIR, FANS[i]);
 133         nanosleep(&smc_write_delay, NULL);
 134         if ((file=fopen(fan_path, "w")) != NULL) {
 135             fprintf(file, "%d", value);
 136             fclose(file);
 137         }
 138         else {
 139             syslog(LOG_WARNING, "Error writing to %s, check if applesmc module loaded", fan_path);
 140         }
 141     }
 142 }
 143 
 144 
 145 /* sort function for qsort: integers descending order */
 146 static int compare_int_desc(const void *a, const void *b) {
 147     int *da, *db;
 148     da = (int *)a;
 149     db = (int *)b;
 150 
 151     return *db - *da;
 152 }
 153 
 154 
 155 /* return average of two largest temperatures, degrees  */
 156 static int get_sensors_temp(void)
 157 {
 158     int i;
 159     int t[SENSORS_NUM];
 160     char sensor_path[SYSFS_PATH_MAX];
 161     FILE *file;
 162 
 163     /* for all sensors read temp1_input */
 164     for(i=0; i < SENSORS_NUM; i++ )
 165     {
 166         sprintf(sensor_path, "%s.%d/temp1_input", CORETEMP_PREFIX, SENSORS[i]);
 167         if ((file=fopen(sensor_path, "r")) != NULL) {
 168             fscanf(file, "%d", &(t[i])); /* read temperature in millidegrees */ 
 169             fclose(file);
 170         }
 171         else {
 172             syslog(LOG_ERR, "Error reading %s, check if coretemp module loaded and number of sensors", sensor_path);
 173             clean_exit_with_status(EXIT_FAILURE);
 174         }
 175     }
 176 
 177     /* sort temperature values, descending order */
 178     qsort(t, SENSORS_NUM, sizeof(t[0]), compare_int_desc);
 179 
 180     /* calculate average of two largest temps (if have more than one sensor) */
 181     int avg = 0, cnt = 0;
 182     for(i=0; (i < 2) && (i < SENSORS_NUM); i++) {
 183         avg += t[i];
 184         cnt ++;
 185     }
 186     avg = avg/cnt;
 187 
 188     /* return temperature in degrees */
 189     return avg/1000;
 190 }
 191 
 192 
 193 /* lock entire file */
 194 static int lock_fd(int fd)
 195 {
 196     struct flock lock;
 197 
 198     lock.l_type = F_WRLCK;
 199     lock.l_start = 0;
 200     lock.l_whence = SEEK_SET;
 201     lock.l_len = 0;
 202 
 203     return fcntl(fd, F_SETLK, &lock);
 204 }
 205 
 206 
 207 /* creates and locks pidfile */
 208 static void create_pidfile(void)
 209 {
 210     int fd;
 211     FILE *f;
 212 
 213     /* open the pidfile */
 214     fd = open(PIDFILE, O_RDWR|O_CREAT, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
 215     if (fd < 0) {
 216         syslog(LOG_ERR, "can't open %s: %s\n", PIDFILE, strerror(errno));
 217         exit(EXIT_FAILURE);
 218     }
 219     /* lock pid file */
 220     if (lock_fd(fd) < 0) {
 221         if (errno == EACCES || errno == EAGAIN) {
 222             syslog(LOG_ERR, "daemon already running");
 223         } else {
 224             syslog(LOG_ERR, "can't lock %s: %s", PIDFILE, strerror(errno));
 225         }
 226         exit(EXIT_FAILURE);
 227     }
 228 
 229     /* write pid */
 230     ftruncate(fd, 0);
 231     f = fdopen(fd, "w");
 232     fprintf(f, "%d\n", getpid());
 233     fflush(f);
 234     /* keep file open and locked */
 235 }
 236 
 237 
 238 /* become a daemon and open log */
 239 void daemonize(void)
 240 {
 241     int i, maxfd;
 242     int fd0, fd1, fd2;
 243     pid_t pid;
 244 
 245     /* clear file creation mask */
 246     umask(0);
 247 
 248     /* fork and become a session leader */
 249     if ((pid=fork()) < 0) { 
 250         fprintf(stderr, "fork failed\n");
 251         exit(EXIT_FAILURE);
 252     }
 253     else if (pid != 0) { /* parent */
 254         exit(EXIT_SUCCESS);
 255     }
 256     setsid();
 257 
 258     /* change current working directory to the root */
 259     if (chdir("/") < 0) {
 260         fprintf(stderr, "can't change directory to /");
 261         exit(EXIT_FAILURE);
 262     }
 263 
 264     /* close all open file descriptors */
 265     maxfd = sysconf(_SC_OPEN_MAX);
 266     for (i=0; i < maxfd; i++)
 267         close(i); 
 268 
 269     /* set up stdin, stdout, stderr to /dev/null */
 270     fd0 = open("/dev/null", O_RDWR);
 271     fd1 = dup(fd0);
 272     fd2 = dup(fd0);
 273 
 274     /* open log */
 275     openlog("smcfancontrol", LOG_PID, LOG_DAEMON);
 276     if (fd0 != STDIN_FILENO || fd1 != STDOUT_FILENO || fd2 != STDERR_FILENO) {
 277         syslog(LOG_ERR, "unexpected file descriptors %d %d %d", fd0, fd1, fd2);
 278         exit(EXIT_FAILURE);
 279     }
 280 }
 281 
 282 
 283 int main(int argc, char *argv[])
 284 {
 285     struct timespec sleep_period;   /* delay between sensor polls */
 286     int t, t_old;                   /* current and previous temperature */
 287     int speed_step, old_speed_step; /* current and old speed step */
 288     int cold, hot;                  /* number of consecutive temperature (de/in)creases */
 289 
 290     /* become a daemon */
 291     daemonize();
 292 
 293     syslog(LOG_INFO, "starting up\n");
 294 
 295     /* trap key signals */
 296     signal(SIGTERM, clean_exit);
 297     signal(SIGQUIT, clean_exit);
 298     signal(SIGINT,  clean_exit);
 299     signal(SIGHUP,  SIG_IGN);
 300 
 301     /* create pidfile and lock it */
 302     create_pidfile();
 303 
 304     /* set fans to automatic control */
 305     set_fans_manual(0);
 306 
 307     /* initial temperatures */
 308     t = t_old = get_sensors_temp();
 309     cold = hot = 1;
 310 
 311     /* init speed step and set fans */
 312     speed_step = temperature_to_speed_step(t);
 313     set_fans_min(speed_step);
 314     old_speed_step = speed_step;
 315 
 316     /* set sleep timer to 0.5 sec */
 317     sleep_period.tv_sec = 0;
 318     sleep_period.tv_nsec = 500000000;
 319     
 320     /* main loop */
 321     while(1)
 322     {
 323         t = get_sensors_temp();
 324 
 325         if ( t < t_old ) { /* it's getting colder */
 326             cold++;
 327             hot = 0;
 328         }
 329         if ( t > t_old ) { /* it's getting hotter */
 330             hot++;
 331             cold = 0;
 332         }
 333 
 334         if ( (cold==2) || (hot==2) ) 
 335         {
 336             speed_step = temperature_to_speed_step(t);
 337 
 338             if ( speed_step != old_speed_step) {
 339                 set_fans_min(speed_step);
 340                 syslog(LOG_INFO, "changed to speed step %d (of %d) at temperature %d", speed_step, SPEED_STEP_MAX, t);
 341                 old_speed_step = speed_step;
 342             }
 343 
 344             cold = 0;
 345             hot = 0;
 346         }
 347 
 348         if( nanosleep(&sleep_period, NULL) < 0 && errno != EINTR )
 349         {
 350             clean_exit_with_status(EXIT_FAILURE);
 351         }
 352 
 353         t_old = t;
 354     }
 355 
 356     clean_exit_with_status(EXIT_SUCCESS);
 357 
 358     return 0;
 359 }

smcfancontrol.c smcfancontrol.sh