1/*
2 * Copyright (C) 2015 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include <sys/types.h>
18#include <sys/stat.h>
19#include <sys/sysmacros.h>
20#include <unistd.h>
21#include <fcntl.h>
22
23#include <errno.h>
24#include <inttypes.h>
25#include <stdio.h>
26#include <string.h>
27
28#include <fs_mgr.h>
29#include <hardware/hardware.h>
30#include <hardware/boot_control.h>
31
32#include "bootinfo.h"
33
34void module_init(boot_control_module_t *module)
35{
36}
37
38unsigned module_getNumberSlots(boot_control_module_t *module)
39{
40  return 2;
41}
42
43static bool get_dev_t_for_partition(const char *name, dev_t *out_device)
44{
45  int fd;
46  struct stat statbuf;
47
48  fd = boot_info_open_partition(name, NULL, O_RDONLY);
49  if (fd == -1)
50    return false;
51  if (fstat(fd, &statbuf) != 0) {
52    fprintf(stderr, "WARNING: Error getting information about part %s: %s\n",
53            name, strerror(errno));
54    close(fd);
55    return false;
56  }
57  close(fd);
58  *out_device = statbuf.st_rdev;
59  return true;
60}
61
62unsigned module_getCurrentSlot(boot_control_module_t *module)
63{
64  struct stat statbuf;
65  dev_t system_a_dev, system_b_dev;
66
67  if (stat("/system", &statbuf) != 0) {
68    fprintf(stderr, "WARNING: Error getting information about /system: %s\n",
69            strerror(errno));
70    return 0;
71  }
72
73  if (!get_dev_t_for_partition("system_a", &system_a_dev) ||
74      !get_dev_t_for_partition("system_b", &system_b_dev))
75    return 0;
76
77  if (statbuf.st_dev == system_a_dev) {
78    return 0;
79  } else if (statbuf.st_dev == system_b_dev) {
80    return 1;
81  } else {
82    fprintf(stderr, "WARNING: Error determining current slot "
83            "(/system dev_t of %d:%d does not match a=%d:%d or b=%d:%d)\n",
84            major(statbuf.st_dev), minor(statbuf.st_dev),
85            major(system_a_dev), minor(system_a_dev),
86            major(system_b_dev), minor(system_b_dev));
87    return 0;
88  }
89}
90
91int module_markBootSuccessful(boot_control_module_t *module)
92{
93  return 0;
94}
95
96#define COPY_BUF_SIZE (1024*1024)
97
98static bool copy_data(int src_fd, int dst_fd, size_t num_bytes)
99{
100  char copy_buf[COPY_BUF_SIZE];
101  size_t remaining;
102
103  remaining = num_bytes;
104  while (remaining > 0) {
105    size_t num_to_read = remaining > COPY_BUF_SIZE ? COPY_BUF_SIZE : remaining;
106    ssize_t num_read;
107    do {
108      num_read = read(src_fd, copy_buf, num_to_read);
109    } while (num_read == -1 && errno == EINTR);
110    if (num_read <= 0) {
111      fprintf(stderr, "Error reading %zd bytes from source: %s\n",
112              num_to_read, strerror(errno));
113      return false;
114    }
115    size_t num_to_write = num_read;
116    while (num_to_write > 0) {
117      size_t offset = num_read - num_to_write;
118      ssize_t num_written;
119      do {
120        num_written = write(dst_fd, copy_buf + offset, num_to_write);
121      } while (num_written == -1 && errno == EINTR);
122      if (num_written <= 0) {
123        fprintf(stderr, "Error writing %zd bytes to destination: %s\n",
124                num_to_write, strerror(errno));
125        return false;
126      }
127      num_to_write -= num_written;
128    }
129    remaining -= num_read;
130  }
131
132  return true;
133}
134
135int module_setActiveBootSlot(boot_control_module_t *module, unsigned slot)
136{
137  BrilloBootInfo info;
138  int src_fd, dst_fd;
139  uint64_t src_size, dst_size;
140  char src_name[32];
141
142  if (slot >= 2)
143    return -EINVAL;
144
145  if (!boot_info_load(&info)) {
146    fprintf(stderr, "WARNING: Error loading boot-info. Resetting.\n");
147    boot_info_reset(&info);
148  } else {
149    if (!boot_info_validate(&info)) {
150      fprintf(stderr, "WARNING: boot-info is invalid. Resetting.\n");
151      boot_info_reset(&info);
152    }
153  }
154
155  info.active_slot = slot;
156  info.slot_info[slot].bootable = true;
157  snprintf(info.bootctrl_suffix,
158           sizeof(info.bootctrl_suffix),
159           "_%c", slot + 'a');
160
161  if (!boot_info_save(&info)) {
162    fprintf(stderr, "Error saving boot-info.\n");
163    return -errno;
164  }
165
166  // Finally copy the contents of boot_X into boot.
167  snprintf(src_name, sizeof(src_name), "boot_%c", slot + 'a');
168  src_fd = boot_info_open_partition(src_name, &src_size, O_RDONLY);
169  if (src_fd == -1) {
170    fprintf(stderr, "Error opening \"%s\" partition.\n", src_name);
171    return -errno;
172  }
173
174  dst_fd = boot_info_open_partition("boot", &dst_size, O_RDWR);
175  if (dst_fd == -1) {
176    fprintf(stderr, "Error opening \"boot\" partition.\n");
177    close(src_fd);
178    return -errno;
179  }
180
181  if (src_size != dst_size) {
182    fprintf(stderr,
183            "src (%" PRIu64 " bytes) and dst (%" PRIu64 " bytes) "
184            "have different sizes.\n",
185            src_size, dst_size);
186    close(src_fd);
187    close(dst_fd);
188    return -EINVAL;
189  }
190
191  if (!copy_data(src_fd, dst_fd, src_size)) {
192    close(src_fd);
193    close(dst_fd);
194    return -errno;
195  }
196
197  if (fsync(dst_fd) != 0) {
198    fprintf(stderr, "Error calling fsync on destination: %s\n",
199            strerror(errno));
200    return -errno;
201  }
202
203  close(src_fd);
204  close(dst_fd);
205  return 0;
206}
207
208int module_setSlotAsUnbootable(struct boot_control_module *module, unsigned slot)
209{
210  BrilloBootInfo info;
211
212  if (slot >= 2)
213    return -EINVAL;
214
215  if (!boot_info_load(&info)) {
216    fprintf(stderr, "WARNING: Error loading boot-info. Resetting.\n");
217    boot_info_reset(&info);
218  } else {
219    if (!boot_info_validate(&info)) {
220      fprintf(stderr, "WARNING: boot-info is invalid. Resetting.\n");
221      boot_info_reset(&info);
222    }
223  }
224
225  info.slot_info[slot].bootable = false;
226
227  if (!boot_info_save(&info)) {
228    fprintf(stderr, "Error saving boot-info.\n");
229    return -errno;
230  }
231
232  return 0;
233}
234
235int module_isSlotBootable(struct boot_control_module *module, unsigned slot)
236{
237  BrilloBootInfo info;
238
239  if (slot >= 2)
240    return -EINVAL;
241
242  if (!boot_info_load(&info)) {
243    fprintf(stderr, "WARNING: Error loading boot-info. Resetting.\n");
244    boot_info_reset(&info);
245  } else {
246    if (!boot_info_validate(&info)) {
247      fprintf(stderr, "WARNING: boot-info is invalid. Resetting.\n");
248      boot_info_reset(&info);
249    }
250  }
251
252  return info.slot_info[slot].bootable;
253}
254
255const char* module_getSuffix(boot_control_module_t *module, unsigned slot)
256{
257  static const char* suffix[2] = {"_a", "_b"};
258  if (slot >= 2)
259    return NULL;
260  return suffix[slot];
261}
262
263static struct hw_module_methods_t module_methods = {
264  .open  = NULL,
265};
266
267
268/* This boot_control HAL implementation emulates A/B by copying the
269 * contents of the boot partition of the requested slot to the boot
270 * partition. It hence works with bootloaders that are not yet aware
271 * of A/B. This code is only intended to be used for development.
272 */
273
274boot_control_module_t HAL_MODULE_INFO_SYM = {
275  .common = {
276    .tag                 = HARDWARE_MODULE_TAG,
277    .module_api_version  = BOOT_CONTROL_MODULE_API_VERSION_0_1,
278    .hal_api_version     = HARDWARE_HAL_API_VERSION,
279    .id                  = BOOT_CONTROL_HARDWARE_MODULE_ID,
280    .name                = "Copy Implementation of boot_control HAL",
281    .author              = "The Android Open Source Project",
282    .methods             = &module_methods,
283  },
284  .init                 = module_init,
285  .getNumberSlots       = module_getNumberSlots,
286  .getCurrentSlot       = module_getCurrentSlot,
287  .markBootSuccessful   = module_markBootSuccessful,
288  .setActiveBootSlot    = module_setActiveBootSlot,
289  .setSlotAsUnbootable  = module_setSlotAsUnbootable,
290  .isSlotBootable       = module_isSlotBootable,
291  .getSuffix            = module_getSuffix,
292};
293