1 /* Copyright 2013 The ChromiumOS Authors
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 "2common.h"
7 #include "2sysincludes.h"
8 #include "cgptlib.h"
9 #include "cgptlib_internal.h"
10 #include "crc32.h"
11 #include "gpt.h"
12 #include "vboot_api.h"
13
GptInit(GptData * gpt)14 int GptInit(GptData *gpt)
15 {
16 int retval;
17
18 gpt->modified = 0;
19 gpt->current_kernel = CGPT_KERNEL_ENTRY_NOT_FOUND;
20 gpt->current_priority = 999;
21
22 retval = GptValidityCheck(gpt);
23 if (GPT_SUCCESS != retval) {
24 VB2_DEBUG("GptInit() failed validity check\n");
25 return retval;
26 }
27
28 GptRepair(gpt);
29 return GPT_SUCCESS;
30 }
31
GptNextKernelEntry(GptData * gpt,uint64_t * start_sector,uint64_t * size)32 int GptNextKernelEntry(GptData *gpt, uint64_t *start_sector, uint64_t *size)
33 {
34 GptHeader *header = (GptHeader *)gpt->primary_header;
35 GptEntry *entries = (GptEntry *)gpt->primary_entries;
36 GptEntry *e;
37 int new_kernel = CGPT_KERNEL_ENTRY_NOT_FOUND;
38 int new_prio = 0;
39 uint32_t i;
40
41 /*
42 * If we already found a kernel, continue the scan at the current
43 * kernel's priority, in case there is another kernel with the same
44 * priority.
45 */
46 if (gpt->current_kernel != CGPT_KERNEL_ENTRY_NOT_FOUND) {
47 for (i = gpt->current_kernel + 1;
48 i < header->number_of_entries; i++) {
49 e = entries + i;
50 if (!IsKernelEntry(e))
51 continue;
52 VB2_DEBUG("GptNextKernelEntry looking at same prio "
53 "partition %d\n", i+1);
54 VB2_DEBUG("GptNextKernelEntry s%d t%d p%d\n",
55 GetEntrySuccessful(e), GetEntryTries(e),
56 GetEntryPriority(e));
57 if (!(GetEntrySuccessful(e) || GetEntryTries(e)))
58 continue;
59 if (GetEntryPriority(e) == gpt->current_priority) {
60 gpt->current_kernel = i;
61 *start_sector = e->starting_lba;
62 *size = e->ending_lba - e->starting_lba + 1;
63 VB2_DEBUG("GptNextKernelEntry likes it\n");
64 return GPT_SUCCESS;
65 }
66 }
67 }
68
69 /*
70 * We're still here, so scan for the remaining kernel with the highest
71 * priority less than the previous attempt.
72 */
73 for (i = 0, e = entries; i < header->number_of_entries; i++, e++) {
74 int current_prio = GetEntryPriority(e);
75 if (!IsKernelEntry(e))
76 continue;
77 VB2_DEBUG("GptNextKernelEntry looking at new prio "
78 "partition %d\n", i+1);
79 VB2_DEBUG("GptNextKernelEntry s%d t%d p%d\n",
80 GetEntrySuccessful(e), GetEntryTries(e),
81 GetEntryPriority(e));
82 if (!(GetEntrySuccessful(e) || GetEntryTries(e)))
83 continue;
84 if (current_prio >= gpt->current_priority) {
85 /* Already returned this kernel in a previous call */
86 continue;
87 }
88 if (current_prio > new_prio) {
89 new_kernel = i;
90 new_prio = current_prio;
91 }
92 }
93
94 /*
95 * Save what we found. Note that if we didn't find a new kernel,
96 * new_prio will still be -1, so future calls to this function will
97 * also fail.
98 */
99 gpt->current_kernel = new_kernel;
100 gpt->current_priority = new_prio;
101
102 if (CGPT_KERNEL_ENTRY_NOT_FOUND == new_kernel) {
103 VB2_DEBUG("GptNextKernelEntry no more kernels\n");
104 return GPT_ERROR_NO_VALID_KERNEL;
105 }
106
107 VB2_DEBUG("GptNextKernelEntry likes partition %d\n", new_kernel + 1);
108 e = entries + new_kernel;
109 *start_sector = e->starting_lba;
110 *size = e->ending_lba - e->starting_lba + 1;
111 return GPT_SUCCESS;
112 }
113
114 /*
115 * Func: GptUpdateKernelWithEntry
116 * Desc: This function updates the given kernel entry according to the provided
117 * update_type.
118 */
GptUpdateKernelWithEntry(GptData * gpt,GptEntry * e,uint32_t update_type)119 int GptUpdateKernelWithEntry(GptData *gpt, GptEntry *e, uint32_t update_type)
120 {
121 int modified = 0;
122
123 if (!IsKernelEntry(e))
124 return GPT_ERROR_INVALID_UPDATE_TYPE;
125
126 switch (update_type) {
127 case GPT_UPDATE_ENTRY_TRY: {
128 /* Used up a try */
129 int tries;
130 if (GetEntrySuccessful(e)) {
131 /*
132 * Successfully booted this partition, so tries field
133 * is ignored.
134 */
135 return GPT_SUCCESS;
136 }
137 tries = GetEntryTries(e);
138 if (tries > 1) {
139 /* Still have tries left */
140 modified = 1;
141 SetEntryTries(e, tries - 1);
142 break;
143 }
144 /* Out of tries, so drop through and mark partition bad. */
145 VBOOT_FALLTHROUGH;
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_ACTIVE: {
161 /*
162 * Used for fastboot mode. If kernel partition slot is marked
163 * active, its GPT entry is marked with S1,P2,T0.
164 */
165 modified = 1;
166 SetEntryTries(e, 0);
167 SetEntryPriority(e, 2);
168 SetEntrySuccessful(e, 1);
169 break;
170 }
171 case GPT_UPDATE_ENTRY_INVALID: {
172 /*
173 * Used for fastboot mode. If kernel partition slot is marked
174 * invalid, its 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 */
GptUpdateKernelEntry(GptData * gpt,uint32_t update_type)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 */
GptFindNthEntry(GptData * gpt,const Guid * guid,unsigned int n)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