1 /* Copyright 2012 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 <string.h>
7
8 #include "cgpt.h"
9 #include "cgptlib_internal.h"
10 #include "vboot_host.h"
11
12 //////////////////////////////////////////////////////////////////////////////
13 // We need a sorted list of priority groups, where each element in the list
14 // contains an unordered list of GPT partition numbers.
15
16 #define MAX_GROUPS 17 // 0-15, plus one "higher"
17
18 typedef struct {
19 int priority; // priority of this group
20 int num_parts; // number of partitions in this group
21 uint32_t *part; // array of partitions in this group
22 } group_t;
23
24 typedef struct {
25 int max_parts; // max number of partitions in any group
26 int num_groups; // number of non-empty groups
27 group_t group[MAX_GROUPS]; // array of groups
28 } group_list_t;
29
30
NewGroupList(int max_p)31 static group_list_t *NewGroupList(int max_p) {
32 int i;
33 group_list_t *gl = (group_list_t *)malloc(sizeof(group_list_t));
34 require(gl);
35 gl->max_parts = max_p;
36 gl->num_groups = 0;
37 // reserve space for the maximum number of partitions in every group
38 for (i=0; i<MAX_GROUPS; i++) {
39 gl->group[i].priority = -1;
40 gl->group[i].num_parts = 0;
41 gl->group[i].part = (uint32_t *)malloc(sizeof(uint32_t) * max_p);
42 require(gl->group[i].part);
43 }
44
45 return gl;
46 }
47
FreeGroups(group_list_t * gl)48 static void FreeGroups(group_list_t *gl) {
49 int i;
50 for (i=0; i<MAX_GROUPS; i++)
51 free(gl->group[i].part);
52 free(gl);
53 }
54
AddToGroup(group_list_t * gl,int priority,int partition)55 static void AddToGroup(group_list_t *gl, int priority, int partition) {
56 int i;
57 // See if I've already got a group with this priority
58 for (i=0; i<gl->num_groups; i++)
59 if (gl->group[i].priority == priority)
60 break;
61 if (i == gl->num_groups) {
62 // no, add a group
63 require(i < MAX_GROUPS);
64 gl->num_groups++;
65 gl->group[i].priority = priority;
66 }
67 // add the partition to it
68 int j = gl->group[i].num_parts;
69 gl->group[i].part[j] = partition;
70 gl->group[i].num_parts++;
71 }
72
ChangeGroup(group_list_t * gl,int old_priority,int new_priority)73 static void ChangeGroup(group_list_t *gl, int old_priority, int new_priority) {
74 int i;
75 for (i=0; i<gl->num_groups; i++)
76 if (gl->group[i].priority == old_priority) {
77 gl->group[i].priority = new_priority;
78 break;
79 }
80 }
81
SortGroups(group_list_t * gl)82 static void SortGroups(group_list_t *gl) {
83 int i, j;
84 group_t tmp;
85
86 // straight insertion sort is fast enough
87 for (i=1; i<gl->num_groups; i++) {
88 tmp = gl->group[i];
89 for (j=i; j && (gl->group[j-1].priority < tmp.priority); j--)
90 gl->group[j] = gl->group[j-1];
91 gl->group[j] = tmp;
92 }
93 }
94
CgptPrioritize(CgptPrioritizeParams * params)95 int CgptPrioritize(CgptPrioritizeParams *params) {
96 struct drive drive;
97
98 int priority;
99
100 int gpt_retval;
101 uint32_t index;
102 uint32_t max_part;
103 int num_kernels;
104 int i,j;
105 group_list_t *groups;
106
107 if (params == NULL)
108 return CGPT_FAILED;
109
110 if (CGPT_OK != DriveOpen(params->drive_name, &drive, O_RDWR,
111 params->drive_size))
112 return CGPT_FAILED;
113
114 if (GPT_SUCCESS != (gpt_retval = GptValidityCheck(&drive.gpt))) {
115 Error("GptValidityCheck() returned %d: %s\n",
116 gpt_retval, GptError(gpt_retval));
117 goto bad;
118 }
119
120 if (CGPT_OK != CheckValid(&drive)) {
121 Error("please run 'cgpt repair' before reordering the priority.\n");
122 (void) DriveClose(&drive, 0);
123 return CGPT_OK;
124 }
125
126 max_part = GetNumberOfEntries(&drive);
127
128 if (params->set_partition) {
129 if (params->set_partition < 1 || params->set_partition > max_part) {
130 Error("invalid partition number: %d (must be between 1 and %d\n",
131 params->set_partition, max_part);
132 goto bad;
133 }
134 index = params->set_partition - 1;
135 // it must be a kernel
136 if (!IsKernel(&drive, PRIMARY, index)) {
137 Error("partition %d is not a ChromeOS kernel\n", params->set_partition);
138 goto bad;
139 }
140 }
141
142 // How many kernel partitions do I have?
143 num_kernels = 0;
144 for (i = 0; i < max_part; i++) {
145 if (IsKernel(&drive, PRIMARY, i))
146 num_kernels++;
147 }
148
149 if (num_kernels) {
150 // Determine the current priority groups
151 groups = NewGroupList(num_kernels);
152 for (i = 0; i < max_part; i++) {
153 if (!IsKernel(&drive, PRIMARY, i))
154 continue;
155
156 priority = GetPriority(&drive, PRIMARY, i);
157
158 // Is this partition special?
159 if (params->set_partition && (i+1 == params->set_partition)) {
160 params->orig_priority = priority; // remember the original priority
161 if (params->set_friends)
162 AddToGroup(groups, priority, i); // we'll move them all later
163 else
164 AddToGroup(groups, 99, i); // move only this one
165 } else {
166 AddToGroup(groups, priority, i); // just remember
167 }
168 }
169
170 // If we're including friends, then change the original group priority
171 if (params->set_partition && params->set_friends) {
172 ChangeGroup(groups, params->orig_priority, 99);
173 }
174
175 // Sorting gives the new order. Now we just need to reassign the
176 // priorities.
177 SortGroups(groups);
178
179 // We'll never lower anything to zero, so if the last group is priority zero
180 // we can ignore it.
181 i = groups->num_groups;
182 if (groups->group[i-1].priority == 0)
183 groups->num_groups--;
184
185 // Where do we start?
186 if (params->max_priority)
187 priority = params->max_priority;
188 else
189 priority = groups->num_groups > 15 ? 15 : groups->num_groups;
190
191 // Figure out what the new values should be
192 for (i=0; i<groups->num_groups; i++) {
193 groups->group[i].priority = priority;
194 if (priority > 1)
195 priority--;
196 }
197
198 // Now apply the ranking to the GPT
199 for (i=0; i<groups->num_groups; i++)
200 for (j=0; j<groups->group[i].num_parts; j++)
201 SetPriority(&drive, PRIMARY,
202 groups->group[i].part[j], groups->group[i].priority);
203
204 FreeGroups(groups);
205 }
206
207 // Write it all out
208 UpdateAllEntries(&drive);
209
210 return DriveClose(&drive, 1);
211
212 bad:
213 (void) DriveClose(&drive, 0);
214 return CGPT_FAILED;
215 }
216