Home | History | Annotate | Download | only in cgpt
      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