2011-05-09 14:45:55 +00:00
|
|
|
#!/usr/bin/python
|
|
|
|
# mkefiboot - a tool to make EFI boot images
|
|
|
|
# Copyright (C) 2011 Red Hat, Inc.
|
|
|
|
#
|
|
|
|
# This program is free software; you can redistribute it and/or modify
|
|
|
|
# it under the terms of the GNU General Public License as published by
|
|
|
|
# the Free Software Foundation; either version 2 of the License, or
|
|
|
|
# (at your option) any later version.
|
|
|
|
#
|
|
|
|
# This program is distributed in the hope that it will be useful,
|
|
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
# GNU General Public License for more details.
|
|
|
|
#
|
|
|
|
# You should have received a copy of the GNU General Public License
|
|
|
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
#
|
|
|
|
# Red Hat Author(s): Will Woods <wwoods@redhat.com>
|
|
|
|
|
2011-10-25 20:04:09 +00:00
|
|
|
import os, tempfile, argparse
|
2011-05-09 14:45:55 +00:00
|
|
|
from subprocess import check_call, PIPE
|
2011-05-10 02:56:14 +00:00
|
|
|
from pylorax.imgutils import mkdosimg, round_to_blocks, LoopDev, DMDev, dm_detach
|
2011-09-06 16:46:41 +00:00
|
|
|
from pylorax.imgutils import mkhfsimg, Mount
|
2011-09-07 16:05:29 +00:00
|
|
|
import struct, shutil, glob
|
2011-05-09 14:45:55 +00:00
|
|
|
|
|
|
|
def mkefiboot(bootdir, outfile, label):
|
|
|
|
'''Make an EFI boot image with the contents of bootdir in EFI/BOOT'''
|
2011-05-10 02:56:14 +00:00
|
|
|
mkdosimg(None, outfile, label=label, graft={'EFI/BOOT':bootdir})
|
2011-05-09 14:45:55 +00:00
|
|
|
|
2011-09-06 16:46:41 +00:00
|
|
|
def mkmacboot(bootdir, outfile, label, icon=None):
|
|
|
|
'''Make an EFI boot image for Apple's EFI implementation'''
|
|
|
|
graft = {'EFI/BOOT':bootdir}
|
|
|
|
if icon:
|
|
|
|
graft['.VolumeIcon.icns'] = icon
|
|
|
|
mkhfsimg(None, outfile, label=label, graft=graft)
|
|
|
|
macbless(outfile)
|
|
|
|
|
|
|
|
# To make an HFS+ image bootable, we need to fill in parts of the
|
|
|
|
# HFSPlusVolumeHeader structure - specifically, finderInfo[0,1,5].
|
|
|
|
# For details, see Technical Note TN1150: HFS Plus Volume Format
|
|
|
|
# http://developer.apple.com/library/mac/#technotes/tn/tn1150.html
|
|
|
|
def macbless(imgfile):
|
|
|
|
'''"bless" the EFI bootloader inside the given Mac EFI boot image, by
|
|
|
|
writing its inode info into the HFS+ volume header.'''
|
|
|
|
# Get the inode number for the boot image and its parent directory
|
2011-10-25 21:23:24 +00:00
|
|
|
with LoopDev(imgfile) as loopdev:
|
2011-09-06 16:46:41 +00:00
|
|
|
with Mount(loopdev) as mnt:
|
|
|
|
loader = glob.glob(os.path.join(mnt,'EFI/BOOT/BOOT*.efi'))[0]
|
|
|
|
blessnode = os.stat(loader).st_ino
|
|
|
|
dirnode = os.stat(os.path.dirname(loader)).st_ino
|
|
|
|
# format data properly (big-endian UInt32)
|
|
|
|
nodedata = struct.pack(">i", blessnode)
|
|
|
|
dirdata = struct.pack(">i", dirnode)
|
|
|
|
# Write it to the volume header
|
|
|
|
with open(imgfile, "wb") as img:
|
|
|
|
img.seek(0x450) # HFSPlusVolumeHeader->finderInfo
|
|
|
|
img.write(dirdata) # finderInfo[0]
|
|
|
|
img.write(nodedata) # finderInfo[1]
|
|
|
|
img.seek(0x464) #
|
|
|
|
img.write(dirdata) # finderInfo[5]
|
|
|
|
|
2011-05-09 14:45:55 +00:00
|
|
|
def mkefidisk(efiboot, outfile):
|
|
|
|
'''Make a bootable EFI disk image out of the given EFI boot image.'''
|
|
|
|
partsize = os.path.getsize(efiboot) + 17408
|
|
|
|
disksize = round_to_blocks(17408 + partsize, 512)
|
|
|
|
with LoopDev(outfile, disksize) as loopdev:
|
|
|
|
with DMDev(loopdev, disksize) as dmdev:
|
|
|
|
check_call(["parted", "--script", "/dev/mapper/%s" % dmdev,
|
|
|
|
"mklabel", "gpt",
|
|
|
|
"unit", "b",
|
|
|
|
"mkpart", "'EFI System Partition'", "fat32", "17408", str(partsize),
|
|
|
|
"set", "1", "boot", "on"], stdout=PIPE, stderr=PIPE)
|
2011-08-30 17:10:13 +00:00
|
|
|
partdev = "/dev/mapper/{0}p1".format(dmdev)
|
2011-05-09 14:45:55 +00:00
|
|
|
with open(efiboot, "rb") as infile:
|
2011-08-30 17:10:13 +00:00
|
|
|
with open(partdev, "wb") as outfile:
|
2011-05-09 14:45:55 +00:00
|
|
|
outfile.write(infile.read())
|
|
|
|
dm_detach(dmdev+"p1")
|
|
|
|
|
|
|
|
if __name__ == '__main__':
|
2011-10-25 20:04:09 +00:00
|
|
|
parser = argparse.ArgumentParser(description="Make an EFI boot image from the given directory.")
|
|
|
|
parser.add_argument("-d", "--disk", action="store_true",
|
|
|
|
help="make a full EFI disk image (including partition table)")
|
|
|
|
parser.add_argument("-a", "--apple", action="store_const", const="apple",
|
|
|
|
dest="imgtype", default="default",
|
|
|
|
help="make an Apple EFI image (use hfs+, bless bootloader)")
|
|
|
|
parser.add_argument("-l", "--label", default="EFI",
|
|
|
|
help="filesystem label to use (default: %(default)s)")
|
|
|
|
parser.add_argument("-i", "--icon", metavar="ICONFILE",
|
|
|
|
help="icon file to include (for Apple EFI image)")
|
|
|
|
parser.add_argument("bootdir", metavar="EFIBOOTDIR",
|
|
|
|
help="input directory (will become /EFI/BOOT in the image)")
|
|
|
|
parser.add_argument("outfile", metavar="OUTPUTFILE",
|
|
|
|
help="output file to write")
|
|
|
|
opt = parser.parse_args()
|
2011-05-09 14:45:55 +00:00
|
|
|
# sanity checks
|
2011-10-25 20:04:09 +00:00
|
|
|
if not os.path.isdir(opt.bootdir):
|
|
|
|
parser.error("%s is not a directory" % opt.bootdir)
|
2011-05-09 14:45:55 +00:00
|
|
|
if os.getuid() > 0:
|
|
|
|
parser.error("need root permissions")
|
2011-09-06 16:46:41 +00:00
|
|
|
if opt.icon and not opt.imgtype == "apple":
|
|
|
|
print "Warning: --icon is only useful for Apple EFI images"
|
2011-05-09 14:45:55 +00:00
|
|
|
# do the thing!
|
2011-09-07 16:05:29 +00:00
|
|
|
if opt.imgtype == "apple":
|
2011-10-25 20:04:09 +00:00
|
|
|
mkmacboot(opt.bootdir, opt.outfile, opt.label, opt.icon)
|
2011-09-06 16:46:41 +00:00
|
|
|
else:
|
2011-10-25 20:04:09 +00:00
|
|
|
mkefiboot(opt.bootdir, opt.outfile, opt.label)
|
2011-05-09 14:45:55 +00:00
|
|
|
if opt.disk:
|
|
|
|
efiboot = tempfile.NamedTemporaryFile(prefix="mkefiboot.").name
|
2011-10-25 20:04:09 +00:00
|
|
|
shutil.move(opt.outfile, efiboot)
|
|
|
|
mkefidisk(efiboot, opt.outfile)
|