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 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 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 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 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 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 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