OLD | NEW |
---|---|
(Empty) | |
1 # Copyright (c) 2010 The Chromium OS Authors. All rights reserved. | |
2 # Use of this source code is governed by a BSD-style license that can be | |
3 # found in the LICENSE file. | |
4 # | |
5 # This contains common constants and functions for installer scripts. This must | |
6 # evaluate properly for both /bin/bash and /bin/sh, since it's used both to | |
7 # create the initial image at compile time and to install or upgrade a running | |
8 # image. | |
9 | |
10 # Here are the GUIDs we'll be using to identify various partitions. | |
11 STATEFUL_GUID='ebd0a0a2-b9e5-4433-87c0-68b6b72699c7' | |
12 KERN_GUID='fe3a2a5d-4f32-41a7-b725-accc3285a309' | |
13 ROOTFS_GUID='3cb8e202-3b7e-47dd-8a3c-7ff2a13cfcec' | |
14 ESP_GUID='28732ac1-1ff8-d211-ba4b-00a0c93ec93b' | |
15 | |
16 | |
17 # The GPT tables describe things in terms of 512-byte sectors, but some | |
18 # filesystems prefer 4096-byte blocks. These functions help with alignment | |
19 # issues. | |
20 | |
21 # This returns the size of a file or device in 512-byte sectors, rounded up if | |
22 # needed. | |
23 # Invoke as: subshell | |
24 # Args: FILENAME | |
25 # Return: whole number of sectors needed to fully contain FILENAME | |
26 numsectors() { | |
27 case $1 in | |
28 /dev/*[0-9]) | |
29 dnum=${1##*/} | |
30 dev=${dnum%%[0-9]*} | |
31 cat /sys/block/$dev/$dnum/size | |
32 ;; | |
33 /dev/*) | |
34 dev=${1##*/} | |
35 cat /sys/block/$dev/size | |
36 ;; | |
37 *) | |
38 local bytes=$(stat -c%s "$1") | |
39 local sectors=$(( $bytes / 512 )) | |
40 local rem=$(( $bytes % 512 )) | |
41 if [ $rem -ne 0 ]; then | |
42 sectors=$(( $sectors + 1 )) | |
43 fi | |
44 echo $sectors | |
45 ;; | |
46 esac | |
47 } | |
48 | |
49 # Round a number of 512-byte sectors up to an integral number of 4096-byte | |
50 # blocks. | |
51 # Invoke as: subshell | |
52 # Args: SECTORS | |
53 # Return: Next largest multiple-of-8 sectors (ex: 4->8, 33->40, 32->32) | |
54 roundup() { | |
55 local num=$1 | |
56 local rem=$(( $num % 8 )) | |
57 | |
58 if (( $rem )); then | |
59 num=$(($num + 8 - $rem)) | |
60 fi | |
61 echo $num | |
62 } | |
63 | |
64 # Truncate a number of 512-byte sectors down to an integral number of 4096-byte | |
65 # blocks. | |
66 # Invoke as: subshell | |
67 # Args: SECTORS | |
68 # Return: Next smallest multiple-of-8 sectors (ex: 4->0, 33->32, 32->32) | |
69 rounddown() { | |
70 local num=$1 | |
71 local rem=$(( $num % 8 )) | |
72 | |
73 if (( $rem )); then | |
74 num=$(($num - $rem)) | |
75 fi | |
76 echo $num | |
77 } | |
78 | |
79 | |
80 # We need to locate the gpt tool. It should already be installed in the build | |
81 # chroot, but some of these functions may be invoked outside the chroot (by | |
82 # image_to_usb or similar), so we need to find it. | |
83 GPT=$(which gpt 2>/dev/null) || /bin/true | |
84 if [ -z "$GPT" ]; then | |
85 if [ -x "${DEFAULT_CHROOT_DIR:-}/usr/bin/gpt" ]; then | |
86 GPT="${DEFAULT_CHROOT_DIR:-}/usr/bin/gpt" | |
87 else | |
88 echo "can't find gpt tool" 1>&2 | |
89 exit 1 | |
90 fi | |
91 fi | |
92 | |
93 | |
94 # This installs a GPT into the specified device or file, using the given | |
95 # components. If the target is a block device we'll do a full install. | |
96 # Otherwise, it'll be just enough to boot. | |
97 # Invoke as: command (not subshell) | |
98 # Args: TARGET ROOTFS_IMG KERNEL_IMG STATEFUL_IMG PMBRCODE | |
99 # Return: nothing | |
100 # Side effects: Sets these global variables describing the GPT partitions | |
101 # (all untis are 512-byte sectors): | |
102 # NUM_KERN_SECTORS | |
103 # NUM_ROOTFS_SECTORS | |
104 # NUM_STATEFUL_SECTORS | |
105 # NUM_RESERVED_SECTORS | |
106 # START_KERN_A | |
107 # START_STATEFUL | |
108 # START_ROOTFS_A | |
109 # START_KERN_B | |
110 # START_ROOTFS_B | |
111 # START_RESERVED | |
112 install_gpt() { | |
113 local outdev=$1 | |
114 local rootfs_img=$2 | |
115 local kernel_img=$3 | |
116 local stateful_img=$4 | |
117 local pmbrcode=$5 | |
118 | |
119 # The gpt tool requires a fixed-size target to work on, so we may have to | |
120 # create a file of the appropriate size. Let's figure out what that size is | |
121 # now. The partition layout is gonna look something like this: | |
122 # | |
123 # PMBR (512 bytes) | |
124 # Primary GPT Header (512 bytes) | |
125 # Primary GPT Table (16K) | |
126 # Kernel A partition 2 | |
127 # Kernel B partition 4 | |
128 # reserved space for additional partitions partition 6,7,8,... | |
129 # Stateful partition (as large as possible) partition 1 | |
130 # Rootfs B partition 5 | |
131 # Rootfs A partition 3 | |
132 # Secondary GPT Table (16K) | |
133 # Secondary GPT Header (512 bytes) | |
134 # | |
135 # Please refer to the official ChromeOS documentation for the details and | |
136 # explanation behind the layout and partition numbering scheme. The short | |
137 # version is that 1) we want everything above partition 3 to be optional when | |
138 # we're creating a bootable USB key, 2) we want to be able to add new | |
139 # partitions later without breaking current scripts, and 3) we may need to | |
140 # increase the size of the rootfs during an upgrade, which means shrinking | |
141 # the size of the stateful partition. | |
142 # | |
143 # One nonobvious contraint is that the ext2-based filesystems typically use | |
144 # 4096-byte blocks. We'll need a little padding at each end of the disk to | |
145 # align the useable space to that size boundary. | |
146 | |
147 # Here are the size limits that we're currently requiring | |
148 local max_kern_sectors=32768 # 16M | |
149 local max_rootfs_sectors=2097152 # 1G | |
150 local max_reserved_sectors=131072 # 64M | |
151 local min_stateful_sectors=262144 # 128M, expands to fill available space | |
152 | |
153 local num_pmbr_sectors=1 | |
154 local num_gpt_hdr_sectors=1 | |
155 local num_gpt_table_sectors=32 # 16K | |
156 local num_footer_sectors=$(($num_gpt_hdr_sectors + $num_gpt_table_sectors)) | |
157 local num_header_sectors=$(($num_pmbr_sectors + $num_footer_sectors)) | |
158 | |
159 local start_useful=$(roundup $num_header_sectors) | |
160 | |
161 # What are we doing? | |
162 if [ -b $outdev ]; then | |
163 # Block device, need to be root. | |
164 local sudo=sudo | |
165 | |
166 # Full install, use max sizes and create both A & B images. | |
167 NUM_KERN_SECTORS=$max_kern_sectors | |
168 NUM_ROOTFS_SECTORS=$max_rootfs_sectors | |
169 NUM_RESERVED_SECTORS=$max_reserved_sectors | |
170 | |
171 # Where do things go? | |
172 START_KERN_A=$start_useful | |
173 START_KERN_B=$(($START_KERN_A + $NUM_KERN_SECTORS)) | |
174 START_RESERVED=$(($START_KERN_B + $NUM_KERN_SECTORS)) | |
175 START_STATEFUL=$(($START_RESERVED + $NUM_RESERVED_SECTORS)) | |
176 | |
177 local total_sectors=$(numsectors $outdev) | |
178 local start_gpt_footer=$(($total_sectors - $num_footer_sectors)) | |
179 local end_useful=$(rounddown $start_gpt_footer) | |
180 | |
181 START_ROOTFS_A=$(($end_useful - $NUM_ROOTFS_SECTORS)) | |
182 START_ROOTFS_B=$(($START_ROOTFS_A - $NUM_ROOTFS_SECTORS)) | |
183 | |
184 NUM_STATEFUL_SECTORS=$(($START_ROOTFS_B - $START_STATEFUL)) | |
185 else | |
186 # Just a local file. | |
187 local sudo= | |
188 | |
189 # We'll only populate partitions 1, 2, 3. Image B isn't required for this, | |
190 # and the others are still theoretical. | |
191 NUM_KERN_SECTORS=$(roundup $(numsectors $kernel_img)) | |
192 NUM_ROOTFS_SECTORS=$(roundup $(numsectors $rootfs_img)) | |
193 NUM_STATEFUL_SECTORS=$(roundup $(numsectors $stateful_img)) | |
194 NUM_RESERVED_SECTORS=0 | |
195 | |
196 START_KERN_A=$start_useful | |
197 START_STATEFUL=$(($START_KERN_A + $NUM_KERN_SECTORS)) | |
198 START_ROOTFS_A=$(($START_STATEFUL + $NUM_STATEFUL_SECTORS)) | |
199 START_KERN_B="" | |
200 START_ROOTFS_B="" | |
201 START_RESERVED="" | |
202 | |
203 # For minimal install, we're not worried about the secondary GPT header | |
204 # being at the end of the device because we're almost always writing to a | |
205 # file. If that's not true, the secondary will just be invalid. | |
206 local start_gpt_footer=$(($START_ROOTFS_A + $NUM_ROOTFS_SECTORS)) | |
207 local end_useful=$start_gpt_footer | |
208 | |
209 local total_sectors=$(($start_gpt_footer + $num_footer_sectors)) | |
210 | |
211 # Create the image file if it doesn't exist. | |
212 if [ ! -e ${outdev} ]; then | |
213 dd if=/dev/zero of=${outdev} bs=512 count=1 seek=$(($total_sectors - 1)) | |
214 fi | |
215 fi | |
216 | |
217 echo "Creating partition tables..." | |
218 | |
219 # Zap any old partitions (otherwise gpt complains). | |
220 $sudo dd if=/dev/zero of=${outdev} conv=notrunc bs=512 \ | |
221 count=$num_header_sectors | |
222 $sudo dd if=/dev/zero of=${outdev} conv=notrunc bs=512 \ | |
223 seek=${start_gpt_footer} count=$num_footer_sectors | |
224 | |
225 # Create the new GPT partitions. The order determines the partition number. | |
226 # Note that the partition label is in the GPT only. The filesystem label is | |
227 # what's used to populate /dev/disk/by-label/, and this is not that. | |
228 $sudo $GPT create ${outdev} | |
229 | |
230 $sudo $GPT add -b ${START_STATEFUL} -s ${NUM_STATEFUL_SECTORS} \ | |
231 -t ${STATEFUL_GUID} ${outdev} | |
232 $sudo $GPT label -i 1 -l "STATE" ${outdev} | |
233 | |
234 $sudo $GPT add -b ${START_KERN_A} -s ${NUM_KERN_SECTORS} \ | |
235 -t ${KERN_GUID} ${outdev} | |
236 $sudo $GPT label -i 2 -l "KERN-A" ${outdev} | |
237 | |
238 $sudo $GPT add -b ${START_ROOTFS_A} -s ${NUM_ROOTFS_SECTORS} \ | |
239 -t ${ROOTFS_GUID} ${outdev} | |
240 $sudo $GPT label -i 3 -l "ROOT-A" ${outdev} | |
241 | |
242 # add the rest of the partitions for a full install | |
243 if [ -n "$START_KERN_B" ]; then | |
244 $sudo $GPT add -b ${START_KERN_B} -s ${NUM_KERN_SECTORS} \ | |
245 -t ${KERN_GUID} ${outdev} | |
246 $sudo $GPT label -i 4 -l "KERN-B" ${outdev} | |
247 | |
248 $sudo $GPT add -b ${START_ROOTFS_B} -s ${NUM_ROOTFS_SECTORS} \ | |
249 -t ${ROOTFS_GUID} ${outdev} | |
250 $sudo $GPT label -i 5 -l "ROOT-B" ${outdev} | |
251 fi | |
252 | |
253 # Create the PMBR and instruct it to boot ROOT-A | |
254 $sudo $GPT boot -i 3 -b ${pmbrcode} ${outdev} | |
255 | |
256 # Display what we've got | |
257 $sudo $GPT -r show -l ${outdev} | |
258 | |
259 sync | |
260 } | |
261 | |
262 | |
263 # Helper function, please ignore and look below. | |
264 _partinfo() { | |
265 local device=$1 | |
266 local partnum=$2 | |
267 local start size part x n | |
268 sudo $GPT -r -S show $device \ | |
269 | grep 'GPT part -' \ | |
270 | while read start size part x x x n x; do \ | |
271 if [ "${part}" -eq "${partnum}" ]; then \ | |
272 echo $start $size; \ | |
273 fi; \ | |
274 done | |
275 # The 'while' is a subshell, so there's no way to indicate success. | |
276 } | |
277 | |
278 # Read GPT table to find information about a specific partition. | |
279 # Invoke as: subshell | |
280 # Args: DEVICE PARTNUM | |
281 # Returns: offset and size (in sectors) of partition PARTNUM | |
282 partinfo() { | |
283 # get string | |
284 local X=$(_partinfo $1 $2) | |
285 # detect success or failure here | |
286 [ -n "$X" ] && echo $X | |
287 } | |
288 | |
289 # Read GPT table to find the starting location of a specific partition. | |
290 # Invoke as: subshell | |
291 # Args: DEVICE PARTNUM | |
292 # Returns: offset (in sectors) of partition PARTNUM | |
293 partoffset() { | |
294 # get string | |
295 local X=$(_partinfo $1 $2) | |
296 # detect success or failure here | |
297 [ -n "$X" ] && echo ${X% *} | |
adlr
2010/03/30 19:50:20
same here?
| |
298 } | |
299 | |
300 # Read GPT table to find the size of a specific partition. | |
301 # Invoke as: subshell | |
302 # Args: DEVICE PARTNUM | |
303 # Returns: size (in sectors) of partition PARTNUM | |
304 partsize() { | |
305 # get string | |
306 local X=$(_partinfo $1 $2) | |
307 # detect success or failure here | |
308 [ -n "$X" ] && echo ${X#* } | |
adlr
2010/03/30 19:50:20
same here?
| |
309 } | |
310 | |
OLD | NEW |