Home | History | Annotate | Download | only in cgptlib
      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