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