1 /*
2  * Copyright (C) 2016 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 "storaged"
18 
19 #include <stdio.h>
20 #include <string.h>
21 
22 #include <android-base/file.h>
23 #include <android-base/parseint.h>
24 #include <android-base/logging.h>
25 #include <android-base/strings.h>
26 #include <log/log_event_list.h>
27 
28 #include "storaged.h"
29 
30 using namespace std;
31 using namespace android::base;
32 
report_storage_health()33 void report_storage_health()
34 {
35     emmc_info_t mmc;
36     ufs_info_t ufs;
37 
38     mmc.report();
39     ufs.report();
40 }
41 
publish()42 void storage_info_t::publish()
43 {
44     android_log_event_list(EVENTLOGTAG_EMMCINFO)
45         << version << eol << lifetime_a << lifetime_b
46         << LOG_ID_EVENTS;
47 }
48 
report()49 bool emmc_info_t::report()
50 {
51     if (!report_sysfs() && !report_debugfs())
52         return false;
53 
54     publish();
55     return true;
56 }
57 
report_sysfs()58 bool emmc_info_t::report_sysfs()
59 {
60     string buffer;
61     uint16_t rev = 0;
62 
63     if (!ReadFileToString(emmc_sysfs + "rev", &buffer)) {
64         return false;
65     }
66 
67     if (sscanf(buffer.c_str(), "0x%hx", &rev) < 1 ||
68         rev < 7 || rev > ARRAY_SIZE(emmc_ver_str)) {
69         return false;
70     }
71 
72     version = "emmc ";
73     version += emmc_ver_str[rev];
74 
75     if (!ReadFileToString(emmc_sysfs + "pre_eol_info", &buffer)) {
76         return false;
77     }
78 
79     if (sscanf(buffer.c_str(), "%hx", &eol) < 1 || eol == 0) {
80         return false;
81     }
82 
83     if (!ReadFileToString(emmc_sysfs + "life_time", &buffer)) {
84         return false;
85     }
86 
87     if (sscanf(buffer.c_str(), "0x%hx 0x%hx", &lifetime_a, &lifetime_b) < 2 ||
88         (lifetime_a == 0 && lifetime_b == 0)) {
89         return false;
90     }
91 
92     return true;
93 }
94 
95 const size_t EXT_CSD_FILE_MIN_SIZE = 1024;
96 /* 2 characters in string for each byte */
97 const size_t EXT_CSD_REV_IDX = 192 * 2;
98 const size_t EXT_PRE_EOL_INFO_IDX = 267 * 2;
99 const size_t EXT_DEVICE_LIFE_TIME_EST_A_IDX = 268 * 2;
100 const size_t EXT_DEVICE_LIFE_TIME_EST_B_IDX = 269 * 2;
101 
report_debugfs()102 bool emmc_info_t::report_debugfs()
103 {
104     string buffer;
105     uint16_t rev = 0;
106 
107     if (!ReadFileToString(emmc_debugfs, &buffer) ||
108         buffer.length() < (size_t)EXT_CSD_FILE_MIN_SIZE) {
109         return false;
110     }
111 
112     string str = buffer.substr(EXT_CSD_REV_IDX, 2);
113     if (!ParseUint(str, &rev) ||
114         rev < 7 || rev > ARRAY_SIZE(emmc_ver_str)) {
115         return false;
116     }
117 
118     version = "emmc ";
119     version += emmc_ver_str[rev];
120 
121     str = buffer.substr(EXT_PRE_EOL_INFO_IDX, 2);
122     if (!ParseUint(str, &eol)) {
123         return false;
124     }
125 
126     str = buffer.substr(EXT_DEVICE_LIFE_TIME_EST_A_IDX, 2);
127     if (!ParseUint(str, &lifetime_a)) {
128         return false;
129     }
130 
131     str = buffer.substr(EXT_DEVICE_LIFE_TIME_EST_B_IDX, 2);
132     if (!ParseUint(str, &lifetime_b)) {
133         return false;
134     }
135 
136     return true;
137 }
138 
report()139 bool ufs_info_t::report()
140 {
141     string buffer;
142     if (!ReadFileToString(health_file, &buffer)) {
143         return false;
144     }
145 
146     vector<string> lines = Split(buffer, "\n");
147     if (lines.empty()) {
148         return false;
149     }
150 
151     char rev[8];
152     if (sscanf(lines[0].c_str(), "ufs version: 0x%7s\n", rev) < 1) {
153         return false;
154     }
155 
156     version = "ufs " + string(rev);
157 
158     for (size_t i = 1; i < lines.size(); i++) {
159         char token[32];
160         uint16_t val;
161         int ret;
162         if ((ret = sscanf(lines[i].c_str(),
163                    "Health Descriptor[Byte offset 0x%*d]: %31s = 0x%hx",
164                    token, &val)) < 2) {
165             continue;
166         }
167 
168         if (string(token) == "bPreEOLInfo") {
169             eol = val;
170         } else if (string(token) == "bDeviceLifeTimeEstA") {
171             lifetime_a = val;
172         } else if (string(token) == "bDeviceLifeTimeEstB") {
173             lifetime_b = val;
174         }
175     }
176 
177     if (eol == 0 || (lifetime_a == 0 && lifetime_b == 0)) {
178         return false;
179     }
180 
181     publish();
182     return true;
183 }
184 
185