1 /* Copyright (c) 2013 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 6 #include "sysincludes.h" 7 8 #include "cgptlib.h" 9 #include "cgptlib_internal.h" 10 #include "crc32.h" 11 #include "gpt.h" 12 #include "utility.h" 13 #include "vboot_api.h" 14 15 int GptInit(GptData *gpt) 16 { 17 int retval; 18 19 gpt->modified = 0; 20 gpt->current_kernel = CGPT_KERNEL_ENTRY_NOT_FOUND; 21 gpt->current_priority = 999; 22 23 retval = GptSanityCheck(gpt); 24 if (GPT_SUCCESS != retval) { 25 VBDEBUG(("GptInit() failed sanity check\n")); 26 return retval; 27 } 28 29 GptRepair(gpt); 30 return GPT_SUCCESS; 31 } 32 33 int GptNextKernelEntry(GptData *gpt, uint64_t *start_sector, uint64_t *size) 34 { 35 GptHeader *header = (GptHeader *)gpt->primary_header; 36 GptEntry *entries = (GptEntry *)gpt->primary_entries; 37 GptEntry *e; 38 int new_kernel = CGPT_KERNEL_ENTRY_NOT_FOUND; 39 int new_prio = 0; 40 uint32_t i; 41 42 /* 43 * If we already found a kernel, continue the scan at the current 44 * kernel's priority, in case there is another kernel with the same 45 * priority. 46 */ 47 if (gpt->current_kernel != CGPT_KERNEL_ENTRY_NOT_FOUND) { 48 for (i = gpt->current_kernel + 1; 49 i < header->number_of_entries; i++) { 50 e = entries + i; 51 if (!IsKernelEntry(e)) 52 continue; 53 VBDEBUG(("GptNextKernelEntry looking at same prio " 54 "partition %d\n", i+1)); 55 VBDEBUG(("GptNextKernelEntry s%d t%d p%d\n", 56 GetEntrySuccessful(e), GetEntryTries(e), 57 GetEntryPriority(e))); 58 if (!(GetEntrySuccessful(e) || GetEntryTries(e))) 59 continue; 60 if (GetEntryPriority(e) == gpt->current_priority) { 61 gpt->current_kernel = i; 62 *start_sector = e->starting_lba; 63 *size = e->ending_lba - e->starting_lba + 1; 64 VBDEBUG(("GptNextKernelEntry likes it\n")); 65 return GPT_SUCCESS; 66 } 67 } 68 } 69 70 /* 71 * We're still here, so scan for the remaining kernel with the highest 72 * priority less than the previous attempt. 73 */ 74 for (i = 0, e = entries; i < header->number_of_entries; i++, e++) { 75 int current_prio = GetEntryPriority(e); 76 if (!IsKernelEntry(e)) 77 continue; 78 VBDEBUG(("GptNextKernelEntry looking at new prio " 79 "partition %d\n", i+1)); 80 VBDEBUG(("GptNextKernelEntry s%d t%d p%d\n", 81 GetEntrySuccessful(e), GetEntryTries(e), 82 GetEntryPriority(e))); 83 if (!(GetEntrySuccessful(e) || GetEntryTries(e))) 84 continue; 85 if (current_prio >= gpt->current_priority) { 86 /* Already returned this kernel in a previous call */ 87 continue; 88 } 89 if (current_prio > new_prio) { 90 new_kernel = i; 91 new_prio = current_prio; 92 } 93 } 94 95 /* 96 * Save what we found. Note that if we didn't find a new kernel, 97 * new_prio will still be -1, so future calls to this function will 98 * also fail. 99 */ 100 gpt->current_kernel = new_kernel; 101 gpt->current_priority = new_prio; 102 103 if (CGPT_KERNEL_ENTRY_NOT_FOUND == new_kernel) { 104 VBDEBUG(("GptNextKernelEntry no more kernels\n")); 105 return GPT_ERROR_NO_VALID_KERNEL; 106 } 107 108 VBDEBUG(("GptNextKernelEntry likes partition %d\n", new_kernel + 1)); 109 e = entries + new_kernel; 110 *start_sector = e->starting_lba; 111 *size = e->ending_lba - e->starting_lba + 1; 112 return GPT_SUCCESS; 113 } 114 115 /* 116 * Func: GptUpdateKernelWithEntry 117 * Desc: This function updates the given kernel entry according to the provided 118 * update_type. 119 */ 120 int GptUpdateKernelWithEntry(GptData *gpt, GptEntry *e, uint32_t update_type) 121 { 122 int modified = 0; 123 124 if (!IsKernelEntry(e)) 125 return GPT_ERROR_INVALID_UPDATE_TYPE; 126 127 switch (update_type) { 128 case GPT_UPDATE_ENTRY_TRY: { 129 /* Used up a try */ 130 int tries; 131 if (GetEntrySuccessful(e)) { 132 /* 133 * Successfully booted this partition, so tries field 134 * is ignored. 135 */ 136 return GPT_SUCCESS; 137 } 138 tries = GetEntryTries(e); 139 if (tries > 1) { 140 /* Still have tries left */ 141 modified = 1; 142 SetEntryTries(e, tries - 1); 143 break; 144 } 145 /* Out of tries, so drop through and mark partition bad. */ 146 } 147 case GPT_UPDATE_ENTRY_BAD: { 148 /* Giving up on this partition entirely. */ 149 if (!GetEntrySuccessful(e)) { 150 /* 151 * Only clear tries and priority if the successful bit 152 * is not set. 153 */ 154 modified = 1; 155 SetEntryTries(e, 0); 156 SetEntryPriority(e, 0); 157 } 158 break; 159 } 160 case GPT_UPDATE_ENTRY_RESET: { 161 /* 162 * Used for fastboot mode. If image is written to kernel 163 * partition, its GPT entry is marked with S1,P1,T15 164 */ 165 modified = 1; 166 SetEntryTries(e, 15); 167 SetEntryPriority(e, 1); 168 SetEntrySuccessful(e, 1); 169 break; 170 } 171 case GPT_UPDATE_ENTRY_INVALID: { 172 /* 173 * Used for fastboot mode. If kernel partition is erased, its 174 * GPT entry is marked with S0,P0,T0 175 */ 176 modified = 1; 177 SetEntryTries(e, 0); 178 SetEntryPriority(e, 0); 179 SetEntrySuccessful(e, 0); 180 break; 181 } 182 default: 183 return GPT_ERROR_INVALID_UPDATE_TYPE; 184 } 185 186 if (modified) { 187 GptModified(gpt); 188 } 189 190 return GPT_SUCCESS; 191 } 192 193 /* 194 * Func: GptUpdateKernelEntry 195 * Desc: This function updates current_kernel entry with provided 196 * update_type. If current_kernel is not set, then it returns error. 197 */ 198 int GptUpdateKernelEntry(GptData *gpt, uint32_t update_type) 199 { 200 GptEntry *entries = (GptEntry *)gpt->primary_entries; 201 GptEntry *e = entries + gpt->current_kernel; 202 203 if (gpt->current_kernel == CGPT_KERNEL_ENTRY_NOT_FOUND) 204 return GPT_ERROR_INVALID_UPDATE_TYPE; 205 206 return GptUpdateKernelWithEntry(gpt, e, update_type); 207 } 208 209 /* 210 * Func: GptFindNthEntry 211 * Desc: This function returns the nth instance of parition entry matching the 212 * partition type guid from the gpt table. Instance value starts from 0. If the 213 * entry is not found it returns NULL. 214 */ 215 GptEntry *GptFindNthEntry(GptData *gpt, const Guid *guid, unsigned int n) 216 { 217 GptHeader *header = (GptHeader *)gpt->primary_header; 218 GptEntry *entries = (GptEntry *)gpt->primary_entries; 219 GptEntry *e; 220 int i; 221 222 for (i = 0, e = entries; i < header->number_of_entries; i++, e++) { 223 if (!Memcmp(&e->type, guid, sizeof(*guid))) { 224 if (n == 0) 225 return e; 226 n--; 227 } 228 } 229 230 return NULL; 231 } 232