ppc64le on x86_64: cross compiling the Linux kernel
Let’s use the ppc64le
cross-compilers on your x86_64
computer to build the Linux kernel!
And, obviously, boot-test it with QEMU full-sytem emulation!
Build the Linux kernel
To cross compile the Linux kernel, just set the ARCH
and CROSS_COMPILE
variables for make
.
For example:
ARCH=powerpc CROSS_COMPILE=powerpc64le-linux-gnu- make -j $(nproc)
Note: you can specify the path to the cross compiler if it’s not in $PATH
.
Note: there is a trailing dash after the target-platform triplet.
You can also export
the variables and not specify them in make
commands.
For example, this is equivalent to the command line above:
$ export ARCH=powerpc
$ export CROSS_COMPILE=powerpc64le-linux-gnu-
$ make -j $(nproc)
So, let’s build the Linux kernel with that.
Note: These variables can be set either before make
(as environment variables) or after it (as variable overrides), as in this documentation – but apparently not all build variables can be set in both ways.
Download and extract:
$ wget https://cdn.kernel.org/pub/linux/kernel/v4.x/linux-4.15.10.tar.xz
$ tar xf linux-4.15.10.tar.xz
Configure (this doesn’t need CROSS_COMPILE
– no target code involved – but it won’t hurt):
$ make ARCH=powerpc pseries_le_defconfig
Build:
-
Ubuntu 16.04 LTS, Fedora 27:
$ ARCH=powerpc CROSS_COMPILE=powerpc64le-linux-gnu- make -j $(nproc)
-
RHEL/CentOS 7:
$ ARCH=powerpc CROSS_COMPILE=powerpc64-linux-gnu- make -j $(nproc)
-
Any Distro (Tarball):
$ ARCH=powerpc CROSS_COMPILE=~/bin/powerpc64le-power8--glibc--stable-2018.02-2/bin/powerpc64le-linux- make -j $(nproc)
Check:
$ file vmlinux
vmlinux: ELF 64-bit LSB executable, 64-bit PowerPC [...]
Cool.
Well, it doesn’t make sense to make install
that on your local system.
But we’ll need the modules_install
target to get the kernel modules to test.
So let’s use the INSTALL_MOD_PATH
variable.
Install modules:
$ ARCH=powerpc CROSS_COMPILE=powerpc64-linux-gnu- \
INSTALL_MOD_PATH=mod-install-dir \
make -j $(nproc) modules_install
...
DEPMOD 4.15.10
And the usual /lib/modules/<version>
directory structure is there:
$ find mod-install-dir/ -name 4.15.10
mod-install-dir/lib/modules/4.15.10
Nice, let’s test it.
Install Alpine Linux
Let’s install Alpine Linux to a disk image with QEMU full-system emulation.
Download the ISO image:
$ wget http://dl-cdn.alpinelinux.org/alpine/v3.7/releases/ppc64le/alpine-vanilla-3.7.0-ppc64le.iso
Create an empty disk image:
$ dd if=/dev/zero of=alpine-disk.img bs=1 seek=1G count=0
Start QEMU with the
$ qemu-system-ppc64 \
-machine pseries \
-cpu power8 \
-m 1G \
-nodefaults \
-nographic \
-serial stdio \
-cdrom alpine-vanilla-3.7.0-ppc64le.iso \
-drive file=alpine-disk.img \
-net nic \
-net user
The devices sda
and eth0
should be available:
localhost login: root
Welcome to Alpine!
...
localhost:~# uname -srm
Linux 4.9.65 ppc64le
localhost:~# ls /dev/sd*
/dev/sda
localhost:~# ip link list
...
2: eth0: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN qlen 1000
link/ether 52:54:00:12:34:56 brd ff:ff:ff:ff:ff:ff
Run setup-alpine
to perform the installation to disk:
localhost:~# setup-alpine
Available keyboard layouts:
...
Select keyboard layout [none]: us
Available variants: <...>
Select variant []: us-intl
* Caching service dependencies ... [ ok ]
* Setting keymap ... [ ok ]
Enter system hostname (short form, e.g. 'foo') [localhost]: alpine
...
Which one do you want to initialize? (or '?' or 'done') [eth0]
Ip address for eth0? (or 'dhcp', 'none', '?') [dhcp]
Do you want to do any manual network configuration? [no]
...
New password:
...
passwd: password for root changed by root
Which timezone are you in? ('?' for list) [UTC]
...
HTTP/FTP proxy URL? (e.g. 'http://proxy:8080', or 'none') [none]
Available mirrors:
1) dl-cdn.alpinelinux.org
...
Enter mirror number (1-28) or URL to add (or r/f/e/done) [f]: 1
...
Which SSH server? ('openssh', 'dropbear' or 'none') [openssh]
...
Which NTP client to run? ('busybox', 'openntpd', 'chrony' or 'none') [chrony]
...
Available disks are:
sda (1.1 GB QEMU QEMU HARDDISK )
Which disk(s) would you like to use? (or '?' for help or 'none') [none] sda
...
The following disk is selected:
sda (1.1 GB QEMU QEMU HARDDISK )
How would you like to use it? ('sys', 'data', 'lvm' or '?' for help) [?] sys
WARNING: The following disk(s) will be erased:
sda (1.1 GB QEMU QEMU HARDDISK )
WARNING: Erase the above disk(s) and continue? [y/N]: y
Creating file systems...
Installing system on /dev/sda4:
Installing grub on /dev/sda1
Installing for powerpc-ieee1275 platform.
...
Installation is complete. Please reboot.
alpine:~# reboot
...
reboot: Restarting system
And it boots from the disk image:
alpine login: root
Password:
Welcome to Alpine!
...
alpine:~# mount | grep ' / '
/dev/sda4 on / type ext4 (rw,relatime,data=ordered)
alpine:~# poweroff
Install the Kernel
Setup the disk image as loop device, load partitions, mount the root partition, and copy kernel files.
$ sudo losetup --find --show alpine-disk.img
/dev/loop1
$ sudo kpartx -av /dev/loop1
add map loop1p1 (253:3): 0 16384 linear /dev/loop1 2048
add map loop1p2 (253:4): 0 204800 linear /dev/loop1 18432
add map loop1p3 (253:5): 0 524288 linear /dev/loop1 223232
add map loop1p4 (253:6): 0 1349632 linear /dev/loop1 747520
$ mkdir disk
$ sudo mount /dev/mapper/loop1p4 disk/
$ sudo mkdir disk/linux
$ sudo cp -r \
linux-4.15.10/vmlinux \
linux-4.15.10/System.map \
linux-4.15.10/mod-install-dir/ \
disk/linux/
$ sudo umount disk/
$ sudo kpartx -dv /dev/loop1
del devmap : loop1p4
del devmap : loop1p3
del devmap : loop1p2
del devmap : loop1p1
$ sudo losetup -d /dev/loop1
Now boot Alpine again with QEMU again (the ISO image and more RAM is not required anymore):
$ qemu-system-ppc64 \
-machine pseries \
-cpu power8 \
-nodefaults \
-nographic \
-serial stdio \
-drive file=alpine-disk.img \
-net nic \
-net user
Copy the kernel files to /boot
and /lib/modules
, create an initramfs, and update grub config:
alpine:~# mv /linux/vmlinux /boot/vmlinux-4.15.10
alpine:~# mv /linux/System.map /boot/System.map-4.15.10
alpine:~# mv /linux/mod-install-dir/lib/modules/4.15.10/ /lib/modules/
alpine:~# mkinitfs -o /boot/initramfs-4.15.10 4.15.10
==> initramfs: creating /boot/initramfs-4.15.10
alpine:~# touch /etc/update-extlinux.conf # only once.
alpine:~# grub-mkconfig -o /boot/grub/grub.cfg
Generating grub configuration file ...
Found linux image: /boot/vmlinuz-4.15.10
done
alpine:~# reboot
And it goes on to boot the new kernel:
GNU GRUB version 2.02
+----------------------------------------------------------------------------+
|*GNU/Linux |
| Advanced options for GNU/Linux |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
+----------------------------------------------------------------------------+
...
Booting `GNU/Linux'
Loading Linux 4.15.10 ...
Loading initial ramdisk ...
OF stdout device is: /vdevice/vty@71000000
Preparing to boot Linux version 4.15.10 (mauricfo@t470.localdomain) (gcc version 4.8.5 20150623 (Red Hat 4.8.5-16) (GCC)) #1 SMP Sun Mar 18 13:56:26 -03 2018
And there it is!
alpine:~# uname -a
Linux alpine 4.15.10 #1 SMP Sun Mar 18 13:56:26 -03 2018 ppc64le Linux
The ppc64le
kernel cross-compiled on x86_64
just booted! Cheers!