1 /* libs/diskconfig/diskconfig.c
2 *
3 * Copyright 2008, The Android Open Source Project
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18 #define LOG_TAG "config_mbr"
19
20 #include <stdint.h>
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24
25 #include <diskconfig/diskconfig.h>
26 #include <log/log.h>
27
28 /* start and len are in LBA units */
29 static void
cfg_pentry(struct pc_partition * pentry,uint8_t status,uint8_t type,uint32_t start,uint32_t len)30 cfg_pentry(struct pc_partition *pentry, uint8_t status, uint8_t type,
31 uint32_t start, uint32_t len)
32 {
33 if (len > 0) {
34 /* seems that somes BIOSens can get wedged on boot while verifying
35 * the mbr if these are 0 */
36 memset(&pentry->start, 0xff, sizeof(struct chs));
37 memset(&pentry->end, 0xff, sizeof(struct chs));
38 } else {
39 /* zero out the c/h/s entries.. they are not used */
40 memset(&pentry->start, 0, sizeof(struct chs));
41 memset(&pentry->end, 0, sizeof(struct chs));
42 }
43
44 pentry->status = status;
45 pentry->type = type;
46 pentry->start_lba = start;
47 pentry->len_lba = len;
48
49 ALOGI("Configuring pentry. status=0x%x type=0x%x start_lba=%u len_lba=%u",
50 pentry->status, pentry->type, pentry->start_lba, pentry->len_lba);
51 }
52
53
54 static inline uint32_t
kb_to_lba(uint32_t len_kb,uint32_t sect_size)55 kb_to_lba(uint32_t len_kb, uint32_t sect_size)
56 {
57 uint64_t lba;
58
59 lba = (uint64_t)len_kb * 1024;
60 /* bump it up to the next LBA boundary just in case */
61 lba = (lba + (uint64_t)sect_size - 1) & ~((uint64_t)sect_size - 1);
62 lba /= (uint64_t)sect_size;
63 if (lba >= 0xffffffffULL)
64 ALOGE("Error converting kb -> lba. 32bit overflow, expect weirdness");
65 return (uint32_t)(lba & 0xffffffffULL);
66 }
67
68
69 static struct write_list *
mk_pri_pentry(struct disk_info * dinfo,struct part_info * pinfo,int pnum,uint32_t * lba)70 mk_pri_pentry(struct disk_info *dinfo, struct part_info *pinfo, int pnum,
71 uint32_t *lba)
72 {
73 struct write_list *item;
74 struct pc_partition *pentry;
75
76 if (pnum >= PC_NUM_BOOT_RECORD_PARTS) {
77 ALOGE("Maximum number of primary partition exceeded.");
78 return NULL;
79 }
80
81 if (!(item = alloc_wl(sizeof(struct pc_partition)))) {
82 ALOGE("Unable to allocate memory for partition entry.");
83 return NULL;
84 }
85
86 {
87 /* DO NOT DEREFERENCE */
88 struct pc_boot_record *mbr = (void *)PC_MBR_DISK_OFFSET;
89 /* grab the offset in mbr where to write this partition entry. */
90 item->offset = (loff_t)((uintptr_t)((uint8_t *)(&mbr->ptable[pnum])));
91 }
92
93 pentry = (struct pc_partition *) &item->data;
94
95 /* need a standard primary partition entry */
96 if (pinfo) {
97 /* need this to be 64 bit in case len_kb is large */
98 uint64_t len_lba;
99
100 if (pinfo->len_kb != (uint32_t)-1) {
101 /* bump it up to the next LBA boundary just in case */
102 len_lba = ((uint64_t)pinfo->len_kb * 1024);
103 len_lba += ((uint64_t)dinfo->sect_size - 1);
104 len_lba &= ~((uint64_t)dinfo->sect_size - 1);
105 len_lba /= (uint64_t)dinfo->sect_size;
106 } else {
107 /* make it fill the rest of disk */
108 len_lba = dinfo->num_lba - *lba;
109 }
110
111 cfg_pentry(pentry, ((pinfo->flags & PART_ACTIVE_FLAG) ?
112 PC_PART_ACTIVE : PC_PART_NORMAL),
113 pinfo->type, *lba, (uint32_t)len_lba);
114
115 pinfo->start_lba = *lba;
116 *lba += (uint32_t)len_lba;
117 } else {
118 /* this should be made an extended partition, and should take
119 * up the rest of the disk as a primary partition */
120 cfg_pentry(pentry, PC_PART_NORMAL, PC_PART_TYPE_EXTENDED,
121 *lba, dinfo->num_lba - *lba);
122
123 /* note that we do not update the *lba because we now have to
124 * create a chain of extended partition tables, and first one is at
125 * *lba */
126 }
127
128 return item;
129 }
130
131
132 /* This function configures an extended boot record at the beginning of an
133 * extended partition. This creates a logical partition and a pointer to
134 * the next EBR.
135 *
136 * ext_lba == The start of the toplevel extended partition (pointed to by the
137 * entry in the MBR).
138 */
139 static struct write_list *
mk_ext_pentry(struct disk_info * dinfo,struct part_info * pinfo,uint32_t * lba,uint32_t ext_lba,struct part_info * pnext)140 mk_ext_pentry(struct disk_info *dinfo, struct part_info *pinfo, uint32_t *lba,
141 uint32_t ext_lba, struct part_info *pnext)
142 {
143 struct write_list *item;
144 struct pc_boot_record *ebr;
145 uint32_t len; /* in lba units */
146
147 if (!(item = alloc_wl(sizeof(struct pc_boot_record)))) {
148 ALOGE("Unable to allocate memory for EBR.");
149 return NULL;
150 }
151
152 /* we are going to write the ebr at the current LBA, and then bump the
153 * lba counter since that is where the logical data partition will start */
154 item->offset = ((loff_t)(*lba)) * dinfo->sect_size;
155 (*lba)++;
156
157 ebr = (struct pc_boot_record *) &item->data;
158 memset(ebr, 0, sizeof(struct pc_boot_record));
159 ebr->mbr_sig = PC_BIOS_BOOT_SIG;
160
161 if (pinfo->len_kb != (uint32_t)-1)
162 len = kb_to_lba(pinfo->len_kb, dinfo->sect_size);
163 else {
164 if (pnext) {
165 ALOGE("Only the last partition can be specified to fill the disk "
166 "(name = '%s')", pinfo->name);
167 goto fail;
168 }
169 len = dinfo->num_lba - *lba;
170 /* update the pinfo structure to reflect the new size, for
171 * bookkeeping */
172 pinfo->len_kb =
173 (uint32_t)(((uint64_t)len * (uint64_t)dinfo->sect_size) /
174 ((uint64_t)1024));
175 }
176
177 cfg_pentry(&ebr->ptable[PC_EBR_LOGICAL_PART], PC_PART_NORMAL,
178 pinfo->type, 1, len);
179
180 pinfo->start_lba = *lba;
181 *lba += len;
182
183 /* If this is not the last partition, we have to create a link to the
184 * next extended partition.
185 *
186 * Otherwise, there's nothing to do since the "pointer entry" is
187 * already zero-filled.
188 */
189 if (pnext) {
190 /* The start lba for next partition is an offset from the beginning
191 * of the top-level extended partition */
192 uint32_t next_start_lba = *lba - ext_lba;
193 uint32_t next_len_lba;
194 if (pnext->len_kb != (uint32_t)-1)
195 next_len_lba = 1 + kb_to_lba(pnext->len_kb, dinfo->sect_size);
196 else
197 next_len_lba = dinfo->num_lba - *lba;
198 cfg_pentry(&ebr->ptable[PC_EBR_NEXT_PTR_PART], PC_PART_NORMAL,
199 PC_PART_TYPE_EXTENDED, next_start_lba, next_len_lba);
200 }
201
202 return item;
203
204 fail:
205 free_wl(item);
206 return NULL;
207 }
208
209
210 static struct write_list *
mk_mbr_sig()211 mk_mbr_sig()
212 {
213 struct write_list *item;
214 if (!(item = alloc_wl(sizeof(uint16_t)))) {
215 ALOGE("Unable to allocate memory for MBR signature.");
216 return NULL;
217 }
218
219 {
220 /* DO NOT DEREFERENCE */
221 struct pc_boot_record *mbr = (void *)PC_MBR_DISK_OFFSET;
222 /* grab the offset in mbr where to write mbr signature. */
223 item->offset = (loff_t)((uintptr_t)((uint8_t *)(&mbr->mbr_sig)));
224 }
225
226 *((uint16_t*)item->data) = PC_BIOS_BOOT_SIG;
227 return item;
228 }
229
230 struct write_list *
config_mbr(struct disk_info * dinfo)231 config_mbr(struct disk_info *dinfo)
232 {
233 struct part_info *pinfo;
234 uint32_t cur_lba = dinfo->skip_lba;
235 uint32_t ext_lba = 0;
236 struct write_list *wr_list = NULL;
237 struct write_list *temp_wr = NULL;
238 int cnt = 0;
239 int extended = 0;
240
241 if (!dinfo->part_lst)
242 return NULL;
243
244 for (cnt = 0; cnt < dinfo->num_parts; ++cnt) {
245 pinfo = &dinfo->part_lst[cnt];
246
247 /* Should we create an extedned partition? */
248 if (cnt == (PC_NUM_BOOT_RECORD_PARTS - 1)) {
249 if (cnt + 1 < dinfo->num_parts) {
250 extended = 1;
251 ext_lba = cur_lba;
252 if ((temp_wr = mk_pri_pentry(dinfo, NULL, cnt, &cur_lba)))
253 wlist_add(&wr_list, temp_wr);
254 else {
255 ALOGE("Cannot create primary extended partition.");
256 goto fail;
257 }
258 }
259 }
260
261 /* if extended, need 1 lba for ebr */
262 if ((cur_lba + extended) >= dinfo->num_lba)
263 goto nospace;
264 else if (pinfo->len_kb != (uint32_t)-1) {
265 uint32_t sz_lba = (pinfo->len_kb / dinfo->sect_size) * 1024;
266 if ((cur_lba + sz_lba + extended) > dinfo->num_lba)
267 goto nospace;
268 }
269
270 if (!extended)
271 temp_wr = mk_pri_pentry(dinfo, pinfo, cnt, &cur_lba);
272 else {
273 struct part_info *pnext;
274 pnext = cnt + 1 < dinfo->num_parts ? &dinfo->part_lst[cnt+1] : NULL;
275 temp_wr = mk_ext_pentry(dinfo, pinfo, &cur_lba, ext_lba, pnext);
276 }
277
278 if (temp_wr)
279 wlist_add(&wr_list, temp_wr);
280 else {
281 ALOGE("Cannot create partition %d (%s).", cnt, pinfo->name);
282 goto fail;
283 }
284 }
285
286 /* fill in the rest of the MBR with empty parts (if needed). */
287 for (; cnt < PC_NUM_BOOT_RECORD_PARTS; ++cnt) {
288 struct part_info blank;
289 cur_lba = 0;
290 memset(&blank, 0, sizeof(struct part_info));
291 if (!(temp_wr = mk_pri_pentry(dinfo, &blank, cnt, &cur_lba))) {
292 ALOGE("Cannot create blank partition %d.", cnt);
293 goto fail;
294 }
295 wlist_add(&wr_list, temp_wr);
296 }
297
298 if ((temp_wr = mk_mbr_sig()))
299 wlist_add(&wr_list, temp_wr);
300 else {
301 ALOGE("Cannot set MBR signature");
302 goto fail;
303 }
304
305 return wr_list;
306
307 nospace:
308 ALOGE("Not enough space to add parttion '%s'.", pinfo->name);
309
310 fail:
311 wlist_free(wr_list);
312 return NULL;
313 }
314
315
316 /* Returns the device path of the partition referred to by 'name'
317 * Must be freed by the caller.
318 */
319 char *
find_mbr_part(struct disk_info * dinfo,const char * name)320 find_mbr_part(struct disk_info *dinfo, const char *name)
321 {
322 struct part_info *plist = dinfo->part_lst;
323 int num = 0;
324 char *dev_name = NULL;
325 int has_extended = (dinfo->num_parts > PC_NUM_BOOT_RECORD_PARTS);
326
327 for(num = 1; num <= dinfo->num_parts; ++num) {
328 if (!strcmp(plist[num-1].name, name))
329 break;
330 }
331
332 if (num > dinfo->num_parts)
333 return NULL;
334
335 if (has_extended && (num >= PC_NUM_BOOT_RECORD_PARTS))
336 num++;
337
338 if (!(dev_name = malloc(MAX_NAME_LEN))) {
339 ALOGE("Cannot allocate memory.");
340 return NULL;
341 }
342
343 num = snprintf(dev_name, MAX_NAME_LEN, "%s%d", dinfo->device, num);
344 if (num >= MAX_NAME_LEN) {
345 ALOGE("Device name is too long?!");
346 free(dev_name);
347 return NULL;
348 }
349
350 return dev_name;
351 }
352