| OLD | NEW |
| (Empty) |
| 1 #!/usr/bin/env python | |
| 2 | |
| 3 # Copyright (c) 2009 The Chromium OS Authors. All rights reserved. | |
| 4 # Use of this source code is governed by a BSD-style license that can be | |
| 5 # found in the LICENSE file. | |
| 6 | |
| 7 """Script to generate ARM beagleboard SD card image from kernel, root fs | |
| 8 | |
| 9 This script must be passed a uImage file and a tarred up root filesystem. | |
| 10 It also needs EITHER an output device or a file + size. If you use a real | |
| 11 device, the entire device will be used. if you specify a file, the file | |
| 12 will be truncated to the given length and be formatted as a disk image. | |
| 13 | |
| 14 To copy a disk image to a device (e.g. /dev/sdb): | |
| 15 # dd if=disk_image.img of=/dev/sdb bs=4M | |
| 16 """ | |
| 17 | |
| 18 from optparse import OptionParser | |
| 19 import math | |
| 20 import os | |
| 21 import re | |
| 22 import shutil | |
| 23 import subprocess | |
| 24 import sys | |
| 25 | |
| 26 def DieWithUsage(exec_path): | |
| 27 print 'usage:', exec_path, ' [-f file] [-s filesize] [-d device] ', \ | |
| 28 'path/to/uImage path/to/armel-rootfs.tgz' | |
| 29 print 'You must pass either -d or both -f and -s' | |
| 30 print 'size may end in k, m, or g for kibibyte, mebibytes, gibibytes.' | |
| 31 print 'This will erase all data on the device or in the file passed.' | |
| 32 print 'This script must be run as root.' | |
| 33 sys.exit(1) | |
| 34 | |
| 35 def ParseFilesize(size): | |
| 36 if size == '': | |
| 37 return -1 | |
| 38 multiplier = 1 | |
| 39 number_part = size[:-1] | |
| 40 last_char = size[-1] | |
| 41 if (last_char == 'k') or (last_char == 'K'): | |
| 42 multiplier = 1024 | |
| 43 elif (last_char == 'm') or (last_char == 'M'): | |
| 44 multiplier = 1024 * 1024 | |
| 45 elif (last_char == 'g') or (last_char == 'G'): | |
| 46 multiplier = 1024 * 1024 * 1024 | |
| 47 else: | |
| 48 number_part = size | |
| 49 return long(number_part) * multiplier | |
| 50 | |
| 51 def ParseArgs(argv): | |
| 52 use_file = False | |
| 53 file_size = 0 | |
| 54 device_path = '' | |
| 55 uimage_path = '' | |
| 56 rootfs_path = '' | |
| 57 | |
| 58 parser = OptionParser() | |
| 59 parser.add_option('-f', action='store', type='string', dest='filename') | |
| 60 parser.add_option('-s', action='store', type='string', dest='filesize') | |
| 61 parser.add_option('-d', action='store', type='string', dest='devname') | |
| 62 (options, args) = parser.parse_args() | |
| 63 | |
| 64 # check for valid arg presence | |
| 65 if len(args) != 2: | |
| 66 DieWithUsage(argv[0]) | |
| 67 if (options.filename != None) != (options.filesize != None): | |
| 68 DieWithUsage(argv[0]) | |
| 69 if not (bool((options.filename != None) and (options.filesize != None)) ^ | |
| 70 bool(options.devname != None)): | |
| 71 DieWithUsage(argv[0]) | |
| 72 | |
| 73 # check the device isn't a partition | |
| 74 if options.devname != None: | |
| 75 if (options.devname[-1] >= '0') and (options.devname[-1] <= '9'): | |
| 76 print 'Looks like you specified a partition device, rather than the ' \ | |
| 77 'entire device. try using -d',options.devname[:-1] | |
| 78 DieWithUsage(argv[0]) | |
| 79 | |
| 80 # if size passed, parse size | |
| 81 if options.filesize != None: | |
| 82 file_size = ParseFilesize(options.filesize) | |
| 83 if file_size < 0: | |
| 84 DieWithUsage(argv[0]) | |
| 85 if options.devname != None: | |
| 86 device_path = options.devname | |
| 87 if options.filename != None: | |
| 88 use_file = True | |
| 89 device_path = options.filename | |
| 90 uimage_path = args[0] | |
| 91 rootfs_path = args[1] | |
| 92 | |
| 93 # print args | |
| 94 if use_file: | |
| 95 print "file size:", file_size | |
| 96 print "dev path:", device_path | |
| 97 print "uimage:", uimage_path | |
| 98 print 'rootfs:', rootfs_path | |
| 99 return use_file, file_size, device_path, uimage_path, rootfs_path | |
| 100 | |
| 101 def CreateSparseFile(path, size): | |
| 102 fd = os.open(path, os.O_CREAT | os.O_WRONLY | os.O_TRUNC, 0644) | |
| 103 if (fd < 0): | |
| 104 print 'os.open() failed' | |
| 105 exit(1) | |
| 106 os.ftruncate(fd, size) | |
| 107 os.close(fd) | |
| 108 | |
| 109 # creates the partion table with the first partition having enough | |
| 110 # space for the uimage, the second partition takingn the rest of the space | |
| 111 def CreatePartitions(uimage_path, device_path): | |
| 112 # get size of first partition in mebibytes | |
| 113 statinfo = os.stat(uimage_path) | |
| 114 first_part_size = int(math.ceil(statinfo.st_size / (1024.0 * 1024.0)) + 1) | |
| 115 System('echo -e ",' + str(first_part_size) \ | |
| 116 + ',c,*\\n,,83,-" | sfdisk -uM \'' + device_path + '\'') | |
| 117 | |
| 118 # uses losetup to set up two loopback devices for the two partitions | |
| 119 # returns the two loopback device paths | |
| 120 def SetupLoopbackDevices(device_path): | |
| 121 sector_size = 512 # bytes | |
| 122 # get size of partitons | |
| 123 output = subprocess.Popen(['sfdisk', '-d', device_path], | |
| 124 stdout=subprocess.PIPE).communicate()[0] | |
| 125 m = re.search('start=\\s+(\\d+), size=\\s+(\\d+),.*?start=\\s+(\\d+), size=\\s
+(\\d+),', output, re.DOTALL) | |
| 126 part1_start = long(m.group(1)) * sector_size | |
| 127 part1_size = long(m.group(2)) * sector_size | |
| 128 part2_start = long(m.group(3)) * sector_size | |
| 129 part2_size = long(m.group(4)) * sector_size | |
| 130 if part1_start < 1 or part1_size < 1 or part2_start < 1 or part2_size < 1: | |
| 131 print 'failed to read partition table' | |
| 132 sys.exit(1) | |
| 133 return SetupLoopbackDevice(device_path, part1_start, part1_size), \ | |
| 134 SetupLoopbackDevice(device_path, part2_start, part2_size) | |
| 135 | |
| 136 # returns loopback device path | |
| 137 def SetupLoopbackDevice(path, start, size): | |
| 138 # get a device | |
| 139 device = subprocess.Popen(['losetup', '-f'], | |
| 140 stdout=subprocess.PIPE).communicate()[0].rstrip() | |
| 141 if device == '': | |
| 142 print 'can\'t get device' | |
| 143 sys.exit(1) | |
| 144 System('losetup -o ' + str(start) + ' --sizelimit ' + str(size) + ' ' + device
+ ' ' + path) | |
| 145 return device | |
| 146 | |
| 147 def DeleteLoopbackDevice(dev): | |
| 148 System('losetup -d ' + dev) | |
| 149 | |
| 150 def FormatDevices(first, second): | |
| 151 System('mkfs.msdos -F 32 ' + first) | |
| 152 System('mkfs.ext3 ' + second) | |
| 153 | |
| 154 # returns mounted paths | |
| 155 def MountFilesystems(paths): | |
| 156 i = 0 | |
| 157 ret = [] | |
| 158 for path in paths: | |
| 159 i = i + 1 | |
| 160 mntpoint = 'mnt' + str(i) | |
| 161 System('mkdir ' + mntpoint) | |
| 162 System('mount ' + path + ' ' + mntpoint) | |
| 163 ret.append(mntpoint) | |
| 164 return ret | |
| 165 | |
| 166 def UnmountFilesystems(mntpoints): | |
| 167 for mntpoint in mntpoints: | |
| 168 System('umount ' + mntpoint) | |
| 169 os.rmdir(mntpoint) | |
| 170 | |
| 171 def System(cmd): | |
| 172 print 'system(' + cmd + ')' | |
| 173 p = subprocess.Popen(cmd, shell=True) | |
| 174 return os.waitpid(p.pid, 0) | |
| 175 | |
| 176 def main(argv): | |
| 177 (use_file, file_size, device_path, uimage_path, rootfs_path) = ParseArgs(argv) | |
| 178 if use_file: | |
| 179 CreateSparseFile(device_path, file_size) | |
| 180 CreatePartitions(uimage_path, device_path) | |
| 181 if use_file: | |
| 182 (dev1, dev2) = SetupLoopbackDevices(device_path) | |
| 183 else: | |
| 184 dev1 = device_path + '1' | |
| 185 dev2 = device_path + '2' | |
| 186 | |
| 187 FormatDevices(dev1, dev2) | |
| 188 (mnt1, mnt2) = MountFilesystems([dev1, dev2]) | |
| 189 | |
| 190 # copy data in | |
| 191 shutil.copy(uimage_path, mnt1 + '/uImage') | |
| 192 System('tar xzpf ' + rootfs_path + ' -C ' + mnt2) | |
| 193 | |
| 194 UnmountFilesystems([mnt1, mnt2]) | |
| 195 | |
| 196 if use_file: | |
| 197 DeleteLoopbackDevice(dev1) | |
| 198 DeleteLoopbackDevice(dev2) | |
| 199 print 'all done!' | |
| 200 if use_file: | |
| 201 print 'you may want to run dd if=' + device_path + ' of=/some/device bs=4M' | |
| 202 | |
| 203 if __name__ == '__main__': | |
| 204 main(sys.argv) | |
| OLD | NEW |