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