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