1 /*
2  * Copyright (C) 2019 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 #define LOG_TAG "bootcontrolhal"
18 
19 #include "GptUtils.h"
20 
21 #include <android-base/file.h>
22 #include <errno.h>
23 #include <linux/fs.h>
24 #include <log/log.h>
25 #include <zlib.h>
26 
27 namespace android {
28 namespace hardware {
29 namespace boot {
30 namespace V1_2 {
31 namespace implementation {
32 
33 namespace {
34 
ValidateGptHeader(gpt_header * gpt)35 static int ValidateGptHeader(gpt_header *gpt) {
36     if (gpt->signature != GPT_SIGNATURE) {
37         ALOGE("invalid gpt signature 0x%lx\n", gpt->signature);
38         return -1;
39     }
40 
41     if (gpt->header_size != sizeof(gpt_header)) {
42         ALOGE("invalid gpt header size %u\n", gpt->header_size);
43         return -1;
44     }
45 
46     if (gpt->entry_size != sizeof(gpt_entry)) {
47         ALOGE("invalid gpt entry size %u\n", gpt->entry_size);
48         return -1;
49     }
50 
51     return 0;
52 }
53 
54 }  // namespace
55 
GptUtils(const std::string dev_path)56 GptUtils::GptUtils(const std::string dev_path) : dev_path(dev_path), fd(0) {}
57 
Load(void)58 int GptUtils::Load(void) {
59     fd = open(dev_path.c_str(), O_RDWR);
60     if (fd < 0) {
61         ALOGE("failed to open block dev %s, %d\n", dev_path.c_str(), errno);
62         return -1;
63     }
64 
65     int ret = ioctl(fd, BLKSSZGET, &block_size);
66     if (ret < 0) {
67         ALOGE("failed to get block size %d\n", errno);
68         return -1;
69     }
70 
71     // read primary header
72     lseek64(fd, block_size, SEEK_SET);
73     ret = read(fd, &gpt_primary, sizeof gpt_primary);
74     if (ret < 0) {
75         ALOGE("failed to read gpt primary header %d\n", errno);
76         return -1;
77     }
78 
79     if (ValidateGptHeader(&gpt_primary)) {
80         ALOGE("error validating gpt header\n");
81         return -1;
82     }
83 
84     // read partition entries
85     entry_array.resize(gpt_primary.entry_count);
86     uint32_t entries_size = gpt_primary.entry_size * gpt_primary.entry_count;
87     lseek64(fd, block_size * gpt_primary.start_lba, SEEK_SET);
88     ret = read(fd, entry_array.data(), entries_size);
89     if (ret < 0) {
90         ALOGE("failed to read gpt partition entries %d\n", errno);
91         return -1;
92     }
93 
94     // read gpt back header
95     lseek64(fd, block_size * gpt_primary.backup_lba, SEEK_SET);
96     ret = read(fd, &gpt_backup, sizeof gpt_backup);
97     if (ret < 0) {
98         ALOGE("failed to read gpt backup header %d\n", errno);
99         return -1;
100     }
101 
102     if (ValidateGptHeader(&gpt_backup)) {
103         ALOGW("error validating gpt backup\n");  // just warn about it, not fail
104     }
105 
106     // Create map <partition name, gpt_entry pointer>
107     auto get_name = [](const uint16_t *efi_name) {
108         char name[37] = {};
109         for (int i = 0; efi_name[i] && i < sizeof name - 1; ++i) name[i] = efi_name[i];
110         return std::string(name);
111     };
112 
113     for (auto const &e : entry_array) {
114         if (e.name[0] == 0)
115             break;  // stop at the first partition with no name
116         std::string s = get_name(e.name);
117         entries[s] = const_cast<gpt_entry *>(&e);
118     }
119 
120     return 0;
121 }
122 
GetPartitionEntry(std::string name)123 gpt_entry *GptUtils::GetPartitionEntry(std::string name) {
124     return entries.find(name) != entries.end() ? entries[name] : nullptr;
125 }
126 
Sync(void)127 int GptUtils::Sync(void) {
128     if (!fd)
129         return -1;
130 
131     // calculate crc and check if we need to update gpt
132     gpt_primary.entries_crc32 = crc32(0, reinterpret_cast<uint8_t *>(entry_array.data()),
133                                       entry_array.size() * sizeof(gpt_entry));
134 
135     // save old crc
136     uint32_t crc = gpt_primary.crc32;
137     gpt_primary.crc32 = 0;
138 
139     gpt_primary.crc32 = crc32(0, reinterpret_cast<uint8_t *>(&gpt_primary), sizeof gpt_primary);
140     if (crc == gpt_primary.crc32)
141         return 0;  // nothing to do (no changes)
142 
143     ALOGI("updating GPT\n");
144 
145     lseek64(fd, block_size * gpt_primary.current_lba, SEEK_SET);
146     int ret = write(fd, &gpt_primary, sizeof gpt_primary);
147     if (ret < 0) {
148         ALOGE("failed to write gpt primary header %d\n", errno);
149         return -1;
150     }
151 
152     lseek64(fd, block_size * gpt_primary.start_lba, SEEK_SET);
153     ret = write(fd, entry_array.data(), entry_array.size() * sizeof(gpt_entry));
154     if (ret < 0) {
155         ALOGE("failed to write gpt partition entries %d\n", errno);
156         return -1;
157     }
158 
159     // update GPT backup entries and backup
160     lseek64(fd, block_size * gpt_backup.start_lba, SEEK_SET);
161     ret = write(fd, entry_array.data(), entry_array.size() * sizeof(gpt_entry));
162     if (ret < 0) {
163         ALOGE("failed to write gpt backup partition entries %d\n", errno);
164         return -1;
165     }
166 
167     gpt_backup.entries_crc32 = gpt_primary.entries_crc32;
168     gpt_backup.crc32 = 0;
169     gpt_backup.crc32 = crc32(0, reinterpret_cast<uint8_t *>(&gpt_backup), sizeof gpt_backup);
170     lseek64(fd, block_size * gpt_primary.backup_lba, SEEK_SET);
171     ret = write(fd, &gpt_backup, sizeof gpt_backup);
172     if (ret < 0) {
173         ALOGE("failed to write gpt backup header %d\n", errno);
174         return -1;
175     }
176 
177     fsync(fd);
178 
179     return 0;
180 }
181 
~GptUtils()182 GptUtils::~GptUtils() {
183     if (fd) {
184         Sync();
185         close(fd);
186     }
187 }
188 
189 }  // namespace implementation
190 }  // namespace V1_2
191 }  // namespace boot
192 }  // namespace hardware
193 }  // namespace android
194