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
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 }