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 |