diff options
author | San Mehat <san@google.com> | 2010-03-10 12:46:00 -0800 |
---|---|---|
committer | San Mehat <san@google.com> | 2010-03-10 15:19:06 -0800 |
commit | a6391f1006b961ca89d1c79a826375380684a4de (patch) | |
tree | 50f5a2b2179d52cf24cb6ad09c4842d0404610e2 /libdiskconfig/config_mbr.c | |
parent | 732799eca9f2767996bc423503937c4c935bb630 (diff) | |
download | system_core-a6391f1006b961ca89d1c79a826375380684a4de.zip system_core-a6391f1006b961ca89d1c79a826375380684a4de.tar.gz system_core-a6391f1006b961ca89d1c79a826375380684a4de.tar.bz2 |
system: libdiskconfig: Add libdiskconfig
Change-Id: Ie7a7b5d8016dec60cdfb17228c3f519789c98564
Signed-off-by: San Mehat <san@google.com>
Diffstat (limited to 'libdiskconfig/config_mbr.c')
-rw-r--r-- | libdiskconfig/config_mbr.c | 325 |
1 files changed, 325 insertions, 0 deletions
diff --git a/libdiskconfig/config_mbr.c b/libdiskconfig/config_mbr.c new file mode 100644 index 0000000..825ba60 --- /dev/null +++ b/libdiskconfig/config_mbr.c @@ -0,0 +1,325 @@ +/* libs/diskconfig/diskconfig.c + * + * Copyright 2008, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "config_mbr" +#include <stdint.h> +#include <stdlib.h> +#include <string.h> +#include <stdio.h> + +#include <cutils/log.h> + +#include <diskconfig/diskconfig.h> + + +/* start and len are in LBA units */ +static void +cfg_pentry(struct pc_partition *pentry, uint8_t status, uint8_t type, + uint32_t start, uint32_t len) +{ + if (len > 0) { + /* seems that somes BIOSens can get wedged on boot while verifying + * the mbr if these are 0 */ + memset(&pentry->start, 0xff, sizeof(struct chs)); + memset(&pentry->end, 0xff, sizeof(struct chs)); + } else { + /* zero out the c/h/s entries.. they are not used */ + memset(&pentry->start, 0, sizeof(struct chs)); + memset(&pentry->end, 0, sizeof(struct chs)); + } + + pentry->status = status; + pentry->type = type; + pentry->start_lba = start; + pentry->len_lba = len; + + LOGI("Configuring pentry. status=0x%x type=0x%x start_lba=%u len_lba=%u", + pentry->status, pentry->type, pentry->start_lba, pentry->len_lba); +} + + +static inline uint32_t +kb_to_lba(uint32_t len_kb, uint32_t sect_size) +{ + uint64_t lba; + + lba = (uint64_t)len_kb * 1024; + /* bump it up to the next LBA boundary just in case */ + lba = (lba + (uint64_t)sect_size - 1) & ~((uint64_t)sect_size - 1); + lba /= (uint64_t)sect_size; + if (lba >= 0xffffffffULL) + LOGE("Error converting kb -> lba. 32bit overflow, expect weirdness"); + return (uint32_t)(lba & 0xffffffffULL); +} + + +static struct write_list * +mk_pri_pentry(struct disk_info *dinfo, struct part_info *pinfo, int pnum, + uint32_t *lba) +{ + struct write_list *item; + struct pc_partition *pentry; + + if (pnum >= PC_NUM_BOOT_RECORD_PARTS) { + LOGE("Maximum number of primary partition exceeded."); + return NULL; + } + + if (!(item = alloc_wl(sizeof(struct pc_partition)))) { + LOGE("Unable to allocate memory for partition entry."); + return NULL; + } + + { + /* DO NOT DEREFERENCE */ + struct pc_boot_record *mbr = (void *)PC_MBR_DISK_OFFSET; + /* grab the offset in mbr where to write this partition entry. */ + item->offset = (loff_t)((uint32_t)((uint8_t *)(&mbr->ptable[pnum]))); + } + + pentry = (struct pc_partition *) &item->data; + + /* need a standard primary partition entry */ + if (pinfo) { + /* need this to be 64 bit in case len_kb is large */ + uint64_t len_lba; + + if (pinfo->len_kb != (uint32_t)-1) { + /* bump it up to the next LBA boundary just in case */ + len_lba = ((uint64_t)pinfo->len_kb * 1024); + len_lba += ((uint64_t)dinfo->sect_size - 1); + len_lba &= ~((uint64_t)dinfo->sect_size - 1); + len_lba /= (uint64_t)dinfo->sect_size; + } else { + /* make it fill the rest of disk */ + len_lba = dinfo->num_lba - *lba; + } + + cfg_pentry(pentry, ((pinfo->flags & PART_ACTIVE_FLAG) ? + PC_PART_ACTIVE : PC_PART_NORMAL), + pinfo->type, *lba, (uint32_t)len_lba); + + pinfo->start_lba = *lba; + *lba += (uint32_t)len_lba; + } else { + /* this should be made an extended partition, and should take + * up the rest of the disk as a primary partition */ + cfg_pentry(pentry, PC_PART_NORMAL, PC_PART_TYPE_EXTENDED, + *lba, dinfo->num_lba - *lba); + + /* note that we do not update the *lba because we now have to + * create a chain of extended partition tables, and first one is at + * *lba */ + } + + return item; +} + + +/* This function configures an extended boot record at the beginning of an + * extended partition. This creates a logical partition and a pointer to + * the next EBR. + * + * ext_lba == The start of the toplevel extended partition (pointed to by the + * entry in the MBR). + */ +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) +{ + struct write_list *item; + struct pc_boot_record *ebr; + uint32_t len; /* in lba units */ + + if (!(item = alloc_wl(sizeof(struct pc_boot_record)))) { + LOGE("Unable to allocate memory for EBR."); + return NULL; + } + + /* we are going to write the ebr at the current LBA, and then bump the + * lba counter since that is where the logical data partition will start */ + item->offset = (*lba) * dinfo->sect_size; + (*lba)++; + + ebr = (struct pc_boot_record *) &item->data; + memset(ebr, 0, sizeof(struct pc_boot_record)); + ebr->mbr_sig = PC_BIOS_BOOT_SIG; + + if (pinfo->len_kb != (uint32_t)-1) + len = kb_to_lba(pinfo->len_kb, dinfo->sect_size); + else { + if (pnext) { + LOGE("Only the last partition can be specified to fill the disk " + "(name = '%s')", pinfo->name); + goto fail; + } + len = dinfo->num_lba - *lba; + /* update the pinfo structure to reflect the new size, for + * bookkeeping */ + pinfo->len_kb = + (uint32_t)(((uint64_t)len * (uint64_t)dinfo->sect_size) / + ((uint64_t)1024)); + } + + cfg_pentry(&ebr->ptable[PC_EBR_LOGICAL_PART], PC_PART_NORMAL, + pinfo->type, 1, len); + + pinfo->start_lba = *lba; + *lba += len; + + /* If this is not the last partition, we have to create a link to the + * next extended partition. + * + * Otherwise, there's nothing to do since the "pointer entry" is + * already zero-filled. + */ + if (pnext) { + /* The start lba for next partition is an offset from the beginning + * of the top-level extended partition */ + uint32_t next_start_lba = *lba - ext_lba; + uint32_t next_len_lba; + if (pnext->len_kb != (uint32_t)-1) + next_len_lba = 1 + kb_to_lba(pnext->len_kb, dinfo->sect_size); + else + next_len_lba = dinfo->num_lba - *lba; + cfg_pentry(&ebr->ptable[PC_EBR_NEXT_PTR_PART], PC_PART_NORMAL, + PC_PART_TYPE_EXTENDED, next_start_lba, next_len_lba); + } + + return item; + +fail: + free_wl(item); + return NULL; +} + + +struct write_list * +config_mbr(struct disk_info *dinfo) +{ + struct part_info *pinfo; + uint32_t cur_lba = dinfo->skip_lba; + uint32_t ext_lba = 0; + struct write_list *wr_list = NULL; + struct write_list *temp_wr = NULL; + int cnt = 0; + int extended = 0; + + if (!dinfo->part_lst) + return NULL; + + for (cnt = 0; cnt < dinfo->num_parts; ++cnt) { + pinfo = &dinfo->part_lst[cnt]; + + /* Should we create an extedned partition? */ + if (cnt == (PC_NUM_BOOT_RECORD_PARTS - 1)) { + if (cnt + 1 < dinfo->num_parts) { + extended = 1; + ext_lba = cur_lba; + if ((temp_wr = mk_pri_pentry(dinfo, NULL, cnt, &cur_lba))) + wlist_add(&wr_list, temp_wr); + else { + LOGE("Cannot create primary extended partition."); + goto fail; + } + } + } + + /* if extended, need 1 lba for ebr */ + if ((cur_lba + extended) >= dinfo->num_lba) + goto nospace; + else if (pinfo->len_kb != (uint32_t)-1) { + uint32_t sz_lba = (pinfo->len_kb / dinfo->sect_size) * 1024; + if ((cur_lba + sz_lba + extended) > dinfo->num_lba) + goto nospace; + } + + if (!extended) + temp_wr = mk_pri_pentry(dinfo, pinfo, cnt, &cur_lba); + else { + struct part_info *pnext; + pnext = cnt + 1 < dinfo->num_parts ? &dinfo->part_lst[cnt+1] : NULL; + temp_wr = mk_ext_pentry(dinfo, pinfo, &cur_lba, ext_lba, pnext); + } + + if (temp_wr) + wlist_add(&wr_list, temp_wr); + else { + LOGE("Cannot create partition %d (%s).", cnt, pinfo->name); + goto fail; + } + } + + /* fill in the rest of the MBR with empty parts (if needed). */ + for (; cnt < PC_NUM_BOOT_RECORD_PARTS; ++cnt) { + struct part_info blank; + cur_lba = 0; + memset(&blank, 0, sizeof(struct part_info)); + if (!(temp_wr = mk_pri_pentry(dinfo, &blank, cnt, &cur_lba))) { + LOGE("Cannot create blank partition %d.", cnt); + goto fail; + } + wlist_add(&wr_list, temp_wr); + } + + return wr_list; + +nospace: + LOGE("Not enough space to add parttion '%s'.", pinfo->name); + +fail: + wlist_free(wr_list); + return NULL; +} + + +/* Returns the device path of the partition referred to by 'name' + * Must be freed by the caller. + */ +char * +find_mbr_part(struct disk_info *dinfo, const char *name) +{ + struct part_info *plist = dinfo->part_lst; + int num = 0; + char *dev_name = NULL; + int has_extended = (dinfo->num_parts > PC_NUM_BOOT_RECORD_PARTS); + + for(num = 1; num <= dinfo->num_parts; ++num) { + if (!strcmp(plist[num-1].name, name)) + break; + } + + if (num > dinfo->num_parts) + return NULL; + + if (has_extended && (num >= PC_NUM_BOOT_RECORD_PARTS)) + num++; + + if (!(dev_name = malloc(MAX_NAME_LEN))) { + LOGE("Cannot allocate memory."); + return NULL; + } + + num = snprintf(dev_name, MAX_NAME_LEN, "%s%d", dinfo->device, num); + if (num >= MAX_NAME_LEN) { + LOGE("Device name is too long?!"); + free(dev_name); + return NULL; + } + + return dev_name; +} |