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
15int 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
33int 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 */
120int 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 */
198int 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 */
215GptEntry *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