1 /*
2 * Copyright (C) 2022 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 #include "include/fastboot/Fastboot_aidl.h"
17
18 #include <android-base/file.h>
19 #include <android-base/logging.h>
20 #include <android-base/strings.h>
21 #include <android-base/unique_fd.h>
22 #include <dlfcn.h>
23 #include <endian.h>
24
25 #include <map>
26 #include <string>
27 #include <unordered_map>
28 #include <vector>
29
30 // FS headers
31 #include <ext4_utils/wipe.h>
32 #include <fs_mgr.h>
33 #include <fs_mgr/roots.h>
34
35 // Nugget headers
36 #include <app_nugget.h>
37 #include <nos/NuggetClient.h>
38 #include <nos/debug.h>
39
40 using ndk::ScopedAStatus;
41
42 namespace aidl {
43 namespace android {
44 namespace hardware {
45 namespace fastboot {
46
47 constexpr const char *BRIGHTNESS_FILE = "/sys/class/backlight/panel0-backlight/brightness";
48 constexpr int DISPLAY_BRIGHTNESS_DIM_THRESHOLD = 20;
49
50 bool WipeDigitalCarKeys(void);
51
52 using OEMCommandHandler =
53 std::function<ScopedAStatus(const std::vector<std::string> &, std::string *)>;
54
getPartitionType(const std::string & in_partitionName,FileSystemType * _aidl_return)55 ScopedAStatus Fastboot::getPartitionType(const std::string &in_partitionName,
56 FileSystemType *_aidl_return) {
57 if (in_partitionName.empty()) {
58 return ScopedAStatus::fromExceptionCodeWithMessage(EX_ILLEGAL_ARGUMENT,
59 "Invalid partition name");
60 }
61 // For bluecross devices, all partitions need to return raw.
62 *_aidl_return = FileSystemType::RAW;
63 return ScopedAStatus::ok();
64 }
65
getVariant(std::string * _aidl_return)66 ScopedAStatus Fastboot::getVariant(std::string *_aidl_return) {
67 *_aidl_return = "MSM USF";
68 return ScopedAStatus::ok();
69 }
70
getOffModeChargeState(bool * _aidl_return)71 ScopedAStatus Fastboot::getOffModeChargeState(bool *_aidl_return) {
72 constexpr const char *kDevinfoPath = "/dev/block/by-name/devinfo";
73 constexpr int kDevInfoOffModeChargeOffset = 15;
74
75 uint8_t off_mode_charge_status = 0;
76 ::android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(kDevinfoPath, O_RDONLY | O_BINARY)));
77 if (fd < 0) {
78 std::string message = "Unable to open devinfo " + std::to_string(errno);
79 return ScopedAStatus::fromServiceSpecificErrorWithMessage(BnFastboot::FAILURE_UNKNOWN,
80 message.c_str());
81 }
82 auto ret = ::android::base::ReadFullyAtOffset(fd, &off_mode_charge_status, 1 /* byte count */,
83 kDevInfoOffModeChargeOffset);
84 if (!ret) {
85 std::string message = "Reading devifo failed errno:" + std::to_string(errno) +
86 " Unable to read off-mode-charge state";
87 return ScopedAStatus::fromServiceSpecificErrorWithMessage(BnFastboot::FAILURE_UNKNOWN,
88 message.c_str());
89 } else {
90 *_aidl_return = (off_mode_charge_status != 0);
91 }
92
93 return ScopedAStatus::ok();
94 }
95
getBatteryVoltageFlashingThreshold(int32_t * _aidl_return)96 ScopedAStatus Fastboot::getBatteryVoltageFlashingThreshold(int32_t *_aidl_return) {
97 constexpr int kMinVoltageForFlashing = 3500;
98 *_aidl_return = kMinVoltageForFlashing;
99 return ScopedAStatus::ok();
100 }
101
SetBrightnessLevel(const std::vector<std::string> & args,std::string * _aidl_return)102 ScopedAStatus SetBrightnessLevel(const std::vector<std::string> &args, std::string *_aidl_return) {
103 if (!args.size()) {
104 return ScopedAStatus::fromExceptionCodeWithMessage(EX_ILLEGAL_ARGUMENT,
105 "Brightness level unspecified");
106 }
107
108 auto level = std::stoi(args[0]);
109
110 if (level < 0 || level > 100) {
111 return ScopedAStatus::fromExceptionCodeWithMessage(
112 EX_ILLEGAL_ARGUMENT, "Brighness level must be between 0 and 100");
113 }
114
115 // Avoid screen being dimmed too much.
116 if (level < DISPLAY_BRIGHTNESS_DIM_THRESHOLD) {
117 level = DISPLAY_BRIGHTNESS_DIM_THRESHOLD;
118 }
119
120 if (::android::base::WriteStringToFile(std::to_string(level), BRIGHTNESS_FILE)) {
121 *_aidl_return = "";
122 return ScopedAStatus::ok();
123 }
124 std::string message = "Writing to brightness file failed errno: " + std::to_string(errno) +
125 " Unable to set display brightness";
126
127 return ScopedAStatus::fromServiceSpecificErrorWithMessage(BnFastboot::FAILURE_UNKNOWN,
128 message.c_str());
129 }
130
DckWipe(const std::vector<std::string> & args,std::string * _aidl_return)131 ScopedAStatus DckWipe(const std::vector<std::string> &args, std::string *_aidl_return) {
132 if (args.size()) {
133 return ScopedAStatus::fromExceptionCodeWithMessage(EX_ILLEGAL_ARGUMENT,
134 "extraneois parameters for dck_wipe");
135 }
136
137 if (WipeDigitalCarKeys()) {
138 *_aidl_return = "";
139 return ScopedAStatus::ok();
140 }
141
142 return ScopedAStatus::fromServiceSpecificErrorWithMessage(BnFastboot::FAILURE_UNKNOWN,
143 "clearing digital car keys failed");
144 }
145
doOemCommand(const std::string & in_oemCmd,std::string * _aidl_return)146 ScopedAStatus Fastboot::doOemCommand(const std::string &in_oemCmd, std::string *_aidl_return) {
147 const std::unordered_map<std::string, OEMCommandHandler> kOEMCmdMap = {
148 {FB_OEM_SET_BRIGHTNESS, SetBrightnessLevel},
149 {FB_OEM_DCK_WIPE, DckWipe},
150 };
151
152 auto args = ::android::base::Split(in_oemCmd, " ");
153 if (args.size() < 2) {
154 return ScopedAStatus::fromExceptionCodeWithMessage(EX_ILLEGAL_ARGUMENT,
155 "Invalid OEM command");
156 }
157
158 // args[0] will be "oem", args[1] will be the command name
159 auto cmd_handler = kOEMCmdMap.find(args[1]);
160 if (cmd_handler != kOEMCmdMap.end()) {
161 return cmd_handler->second(std::vector<std::string>(args.begin() + 2, args.end()),
162 _aidl_return);
163 } else {
164 return ScopedAStatus::fromServiceSpecificErrorWithMessage(BnFastboot::FAILURE_UNKNOWN,
165 "Unknown OEM Command");
166 }
167
168 return ScopedAStatus::ok();
169 }
170
171 static ::android::fs_mgr::Fstab fstab;
172 enum WipeVolumeStatus {
173 WIPE_OK = 0,
174 VOL_FSTAB,
175 VOL_UNKNOWN,
176 VOL_MOUNTED,
177 VOL_BLK_DEV_OPEN,
178 WIPE_ERROR_MAX = 0xffffffff,
179 };
180 std::map<enum WipeVolumeStatus, std::string> wipe_vol_ret_msg{
181 {WIPE_OK, ""},
182 {VOL_FSTAB, "Unknown FS table"},
183 {VOL_UNKNOWN, "Unknown volume"},
184 {VOL_MOUNTED, "Fail to unmount volume"},
185 {VOL_BLK_DEV_OPEN, "Fail to open block device"},
186 {WIPE_ERROR_MAX, "Unknown wipe error"}};
187
wipe_volume(const std::string & volume)188 enum WipeVolumeStatus wipe_volume(const std::string &volume) {
189 if (!::android::fs_mgr::ReadDefaultFstab(&fstab)) {
190 return VOL_FSTAB;
191 }
192 const ::android::fs_mgr::FstabEntry *v = ::android::fs_mgr::GetEntryForPath(&fstab, volume);
193 if (v == nullptr) {
194 return VOL_UNKNOWN;
195 }
196 if (::android::fs_mgr::EnsurePathUnmounted(&fstab, volume) != true) {
197 return VOL_MOUNTED;
198 }
199
200 int fd = open(v->blk_device.c_str(), O_WRONLY | O_CREAT, 0644);
201 if (fd == -1) {
202 return VOL_BLK_DEV_OPEN;
203 }
204 wipe_block_device(fd, get_block_device_size(fd));
205 close(fd);
206
207 return WIPE_OK;
208 }
209
210 // Attempt to reuse a WipeKeys function that might be found in the recovery
211 // library in order to clear any digital car keys on the secure element.
WipeDigitalCarKeys(void)212 bool WipeDigitalCarKeys(void) {
213 static constexpr const char *kDefaultLibRecoveryUIExt = "librecovery_ui_ext.so";
214 void *librecovery_ui_ext = dlopen(kDefaultLibRecoveryUIExt, RTLD_NOW);
215 if (librecovery_ui_ext == nullptr) {
216 // Dynamic library not found. Returning true since this likely
217 // means target does not support DCK.
218 return true;
219 }
220
221 bool *(*WipeKeysFunc)(void *const);
222 reinterpret_cast<void *&>(WipeKeysFunc) = dlsym(librecovery_ui_ext, "WipeKeys");
223 if (WipeKeysFunc == nullptr) {
224 // No WipeKeys implementation found. Returning true since this likely
225 // means target does not support DCK.
226 return true;
227 }
228
229 return (*WipeKeysFunc)(nullptr);
230 }
231
doOemSpecificErase()232 ScopedAStatus Fastboot::doOemSpecificErase() {
233 // Erase metadata partition along with userdata partition.
234 // Keep erasing Titan M even if failing on this case.
235 auto wipe_status = wipe_volume("/metadata");
236
237 bool dck_wipe_success = WipeDigitalCarKeys();
238
239 // Connect to Titan M
240 ::nos::NuggetClient client;
241 client.Open();
242 if (!client.IsOpen()) {
243 return ScopedAStatus::fromServiceSpecificErrorWithMessage(BnFastboot::FAILURE_UNKNOWN,
244 "open Titan M fail");
245 }
246
247 // Tell Titan M to wipe user data
248 const uint32_t magicValue = htole32(ERASE_CONFIRMATION);
249 std::vector<uint8_t> magic(sizeof(magicValue));
250 memcpy(magic.data(), &magicValue, sizeof(magicValue));
251 const uint8_t retry_count = 5;
252 uint32_t nugget_status;
253 for (uint8_t i = 0; i < retry_count; i++) {
254 nugget_status = client.CallApp(APP_ID_NUGGET, NUGGET_PARAM_NUKE_FROM_ORBIT, magic, nullptr);
255 if (nugget_status == APP_SUCCESS && wipe_status == WIPE_OK) {
256 return ScopedAStatus::ok();
257 }
258 }
259
260 // Return exactly what happened
261 if (nugget_status != APP_SUCCESS && wipe_status != WIPE_OK && !dck_wipe_success) {
262 return ScopedAStatus::fromServiceSpecificErrorWithMessage(
263 BnFastboot::FAILURE_UNKNOWN, "Fail on wiping metadata, Titan M user data, and DCK");
264 } else if (nugget_status != APP_SUCCESS && wipe_status != WIPE_OK) {
265 return ScopedAStatus::fromServiceSpecificErrorWithMessage(
266 BnFastboot::FAILURE_UNKNOWN, "Fail on wiping metadata and Titan M user data");
267 } else if (nugget_status != APP_SUCCESS && !dck_wipe_success) {
268 return ScopedAStatus::fromServiceSpecificErrorWithMessage(
269 BnFastboot::FAILURE_UNKNOWN, "Titan M user data and DCK wipe failed");
270 } else if (nugget_status != APP_SUCCESS) {
271 return ScopedAStatus::fromServiceSpecificErrorWithMessage(BnFastboot::FAILURE_UNKNOWN,
272 "Titan M user data wipe failed");
273 } else if (wipe_status != WIPE_OK && !dck_wipe_success) {
274 return ScopedAStatus::fromServiceSpecificErrorWithMessage(
275 BnFastboot::FAILURE_UNKNOWN, "Fail on wiping metadata and DCK");
276 } else if (!dck_wipe_success) {
277 return ScopedAStatus::fromServiceSpecificErrorWithMessage(BnFastboot::FAILURE_UNKNOWN,
278 "DCK wipe failed");
279 } else {
280 if (wipe_vol_ret_msg.find(wipe_status) != wipe_vol_ret_msg.end())
281 return ScopedAStatus::fromServiceSpecificErrorWithMessage(
282 BnFastboot::FAILURE_UNKNOWN, wipe_vol_ret_msg[wipe_status].c_str());
283 else // Should not reach here, but handle it anyway
284 return ScopedAStatus::fromServiceSpecificErrorWithMessage(BnFastboot::FAILURE_UNKNOWN,
285 "Unknown failure");
286 }
287
288 // Return exactly what happened
289 if (nugget_status != APP_SUCCESS && wipe_status != WIPE_OK) {
290 return ScopedAStatus::fromServiceSpecificErrorWithMessage(
291 BnFastboot::FAILURE_UNKNOWN, "Fail on wiping metadata and Titan M user data");
292
293 } else if (nugget_status != APP_SUCCESS) {
294 return ScopedAStatus::fromServiceSpecificErrorWithMessage(BnFastboot::FAILURE_UNKNOWN,
295 "Titan M user data wipe failed");
296 } else {
297 if (wipe_vol_ret_msg.find(wipe_status) != wipe_vol_ret_msg.end())
298 return ScopedAStatus::fromServiceSpecificErrorWithMessage(
299 BnFastboot::FAILURE_UNKNOWN, wipe_vol_ret_msg[wipe_status].c_str());
300 else // Should not reach here, but handle it anyway
301 return ScopedAStatus::fromServiceSpecificErrorWithMessage(BnFastboot::FAILURE_UNKNOWN,
302 "Unknown failure");
303 }
304
305 return ScopedAStatus::ok();
306 }
307
308 } // namespace fastboot
309 } // namespace hardware
310 } // namespace android
311 } // namespace aidl
312