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