OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2010 The Chromium OS Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 // NOTE: this file is translated from reboot_mode.py |
| 6 // |
| 7 // A script to manipulate ChromeOS CMOS reboot Field. |
| 8 // |
| 9 // A few bits in a byte in CMOS (called RDB, short for 'Reboot Data Byte' |
| 10 // hereafter) are dedicated to information exchange between Linux and BIOS. |
| 11 // |
| 12 // The CMOS contents are available through /dev/nvrom. The location of RDB is |
| 13 // reported by BIOS through ACPI. The few bits in RDB operated on by this script |
| 14 // are defined in the all_mode_fields dictionary below along with their |
| 15 // functions. |
| 16 // |
| 17 // When invoked without parameters this script prints values of the fields. When |
| 18 // invoked with any of the bit field names as parameter(s), the script sets the |
| 19 // bit(s) as required. |
| 20 // |
| 21 // Additional parameters allow to specify alternative ACPI and NVRAM files for |
| 22 // testing. |
| 23 |
| 24 #include <stdio.h> |
| 25 #include <stdlib.h> |
| 26 #include <assert.h> |
| 27 #include <unistd.h> |
| 28 #include <sys/param.h> |
| 29 #include <stdint.h> |
| 30 #include <errno.h> |
| 31 #include <stdarg.h> |
| 32 #include <getopt.h> |
| 33 |
| 34 #include <string> |
| 35 #include <algorithm> |
| 36 #include <vector> |
| 37 #include <map> |
| 38 |
| 39 #define DEFAULT_ACPI_FILE "/sys/devices/platform/chromeos_acpi/CHNV" |
| 40 #define DEFAULT_NVRAM_FILE "/dev/nvram" |
| 41 |
| 42 /////////////////////////////////////////////////////////////////////// |
| 43 // Utilities for quick python-C translation |
| 44 |
| 45 using std::string; |
| 46 |
| 47 // Works like throwing an exception - directly exit here. |
| 48 static void RebootModeError(const char *message, ...) { |
| 49 va_list args; |
| 50 va_start(args, message); |
| 51 vfprintf(stderr, message, args); |
| 52 fprintf(stderr, "\n"); |
| 53 va_end(args); |
| 54 exit(1); |
| 55 } |
| 56 |
| 57 // return a string which is upper case of input parameter. |
| 58 static string str_to_upper(const string str) { |
| 59 string newstr = str; |
| 60 std::transform(str.begin(), str.end(), newstr.begin(), toupper); |
| 61 return newstr; |
| 62 } |
| 63 |
| 64 /////////////////////////////////////////////////////////////////////// |
| 65 |
| 66 typedef std::map<string, uint8_t> RDB_BitField; |
| 67 |
| 68 struct RebootModeConfig { |
| 69 // file path |
| 70 string acpi_file; |
| 71 string nvram_file; |
| 72 |
| 73 // Offset of RDB in NVRAM |
| 74 int rdb_offset; |
| 75 |
| 76 // NVRAM contents read on startup |
| 77 uint8_t rdb_value; |
| 78 |
| 79 // A dictionary of fields to be updated as requested by the command line |
| 80 // parameters |
| 81 RDB_BitField fields_to_set; |
| 82 |
| 83 // All bitfields in RDB this script provides access to. Field names prepended |
| 84 // by `--' become this script's command line options |
| 85 RDB_BitField all_mode_fields; |
| 86 |
| 87 // write constructure here to ease python translation |
| 88 RebootModeConfig() { |
| 89 acpi_file = DEFAULT_ACPI_FILE; |
| 90 nvram_file = DEFAULT_NVRAM_FILE; |
| 91 rdb_offset = 0; |
| 92 all_mode_fields["recovery"] = 0x80; |
| 93 all_mode_fields["debug_reset"] = 0x40; |
| 94 all_mode_fields["try_firmware_b"] = 0x20; |
| 95 } |
| 96 } cfg; // this is a special global object to ease python translation. |
| 97 |
| 98 // Process bit field name command line parameter. |
| 99 // |
| 100 // Verifies that the value is in range (0 or 1) and adds the appropriate |
| 101 // element to fields_to_set dictionary. Should the same field specified in the |
| 102 // command line more than once, only the last value will be used. |
| 103 // |
| 104 // Raises: |
| 105 // RebootModeError in case the parameter value is out of range. |
| 106 void OptionHandler(const char *opt_str, const char *value_str) { |
| 107 const char *key = opt_str; |
| 108 int value = atoi(value_str); |
| 109 |
| 110 if (!value && *value_str != '0') |
| 111 RebootModeError("--%s needs numeric argument value", key); |
| 112 |
| 113 if (value < 0 || value > 1) |
| 114 RebootModeError("--%s should be either 0 or 1", key); |
| 115 |
| 116 if (cfg.all_mode_fields.find(key) == cfg.all_mode_fields.end()) |
| 117 RebootModeError("INTERNAL ERROR: UNKNOWN FIELD: %s", key); |
| 118 |
| 119 cfg.fields_to_set[key] = value; |
| 120 } |
| 121 |
| 122 void GetRDBOffset() { |
| 123 const char *acpi_file = cfg.acpi_file.c_str(); |
| 124 |
| 125 FILE *f = fopen(acpi_file, "rb"); |
| 126 if (!f) { |
| 127 perror(acpi_file); |
| 128 RebootModeError("Cannot read acpi_file: %s", acpi_file); |
| 129 } |
| 130 |
| 131 char buffer[PATH_MAX]; // this should be large enough |
| 132 if (fgets(buffer, sizeof(buffer), f) == NULL) { |
| 133 perror(acpi_file); |
| 134 RebootModeError("Trying to read %s but failed.", acpi_file); |
| 135 } |
| 136 |
| 137 cfg.rdb_offset = atoi(buffer); |
| 138 if (!cfg.rdb_offset && *buffer != '0') { // consider this as error |
| 139 RebootModeError("%s contents are corrupted", acpi_file); |
| 140 } |
| 141 } |
| 142 |
| 143 void ReadNvram() { |
| 144 FILE *f = fopen(cfg.nvram_file.c_str(), "rb"); |
| 145 if (!f) { |
| 146 if (errno == 2) |
| 147 RebootModeError("%s does not exist. Is nvram module installed?", |
| 148 cfg.nvram_file.c_str()); |
| 149 if (errno == 13) |
| 150 RebootModeError("Access to %s denied. Are you root?", |
| 151 cfg.nvram_file.c_str()); |
| 152 // all other error |
| 153 perror(cfg.nvram_file.c_str()); |
| 154 } |
| 155 |
| 156 if (fseek(f, cfg.rdb_offset, SEEK_SET) == -1 || |
| 157 fread(&cfg.rdb_value, 1, 1, f) != 1) { |
| 158 perror(cfg.nvram_file.c_str()); |
| 159 RebootModeError("Failed reading mode byte from %s", cfg.nvram_file.c_str()); |
| 160 } |
| 161 fclose(f); |
| 162 } |
| 163 |
| 164 void PrintCurrentMode() { |
| 165 printf("Current reboot mode settings:\n"); |
| 166 for (RDB_BitField::iterator i = cfg.all_mode_fields.begin(); |
| 167 i != cfg.all_mode_fields.end(); |
| 168 ++i) { |
| 169 printf("%-15s: %d\n", i->first.c_str(), |
| 170 (cfg.rdb_value & i->second) ? 1 : 0); |
| 171 } |
| 172 } |
| 173 |
| 174 void SetNewMode() { |
| 175 uint8_t new_mode = 0; |
| 176 uint8_t updated_bits_mask = 0; |
| 177 |
| 178 for (RDB_BitField::iterator i = cfg.fields_to_set.begin(); |
| 179 i != cfg.fields_to_set.end(); |
| 180 ++i) { |
| 181 string opt = i->first; |
| 182 uint8_t value = i->second; |
| 183 assert(cfg.all_mode_fields.find(opt) != cfg.all_mode_fields.end()); |
| 184 |
| 185 uint8_t mask = cfg.all_mode_fields[opt]; |
| 186 if (value) |
| 187 new_mode |= mask; |
| 188 |
| 189 updated_bits_mask |= mask; |
| 190 } |
| 191 |
| 192 if ((cfg.rdb_value & updated_bits_mask) == new_mode) { |
| 193 printf("No update required\n"); |
| 194 return; |
| 195 } |
| 196 |
| 197 FILE *f = fopen(cfg.nvram_file.c_str(), "rb+"); |
| 198 fseek(f, cfg.rdb_offset, SEEK_SET); |
| 199 new_mode |= (cfg.rdb_value & ~updated_bits_mask); |
| 200 if (fwrite(&new_mode, 1, 1, f) != 1) { |
| 201 RebootModeError("Failed writing mode byte to %s", cfg.nvram_file.c_str()); |
| 202 } |
| 203 fclose(f); |
| 204 } |
| 205 |
| 206 const char *__name__; |
| 207 |
| 208 void usage_help_exit(int err) { |
| 209 printf("Usage: %s [options]\n" |
| 210 "\n" |
| 211 "Options:\n" |
| 212 " -h, --help\t\tshow this help message and exit\n" |
| 213 " --acpi_file=ACPI_FILE\n" |
| 214 " --nvram_file=NVRAM_FILE\n" |
| 215 , __name__); |
| 216 |
| 217 // list all possible fields |
| 218 for (RDB_BitField::iterator i = cfg.all_mode_fields.begin(); |
| 219 i != cfg.all_mode_fields.end(); |
| 220 ++i) { |
| 221 printf(" --%s=%s\n", |
| 222 i->first.c_str(), |
| 223 str_to_upper(i->first).c_str()); |
| 224 } |
| 225 exit(err); |
| 226 } |
| 227 |
| 228 int main(int argc, char *argv[]) { |
| 229 __name__ = argv[0]; |
| 230 |
| 231 struct option optv = {0}; |
| 232 std::vector<struct option> longopts; |
| 233 int max_field_index = 0; |
| 234 |
| 235 optv.has_arg = 0; |
| 236 optv.val = 0; |
| 237 optv.name = "help"; // value = 0 |
| 238 longopts.push_back(optv); |
| 239 optv.has_arg = 1; |
| 240 |
| 241 optv.val++; |
| 242 optv.name = "acpi_file"; // value = 1 |
| 243 longopts.push_back(optv); |
| 244 optv.val++; |
| 245 optv.name = "nvram_file"; // value = 2 |
| 246 longopts.push_back(optv); |
| 247 |
| 248 // add all known RDB fields |
| 249 for (RDB_BitField::iterator i = cfg.all_mode_fields.begin(); |
| 250 i != cfg.all_mode_fields.end(); |
| 251 ++i) { |
| 252 optv.name = i->first.c_str(); |
| 253 optv.val++; |
| 254 longopts.push_back(optv); |
| 255 } |
| 256 max_field_index = optv.val; |
| 257 |
| 258 // add a zero for closing options |
| 259 optv.name = NULL; |
| 260 optv.val = optv.has_arg = 0; |
| 261 longopts.push_back(optv); |
| 262 |
| 263 int optc; |
| 264 while ((optc = getopt_long(argc, argv, "h", &longopts.front(), NULL)) != -1) { |
| 265 switch (optc) { |
| 266 case 1: |
| 267 cfg.acpi_file = optarg; |
| 268 break; |
| 269 |
| 270 case 2: |
| 271 cfg.nvram_file = optarg; |
| 272 break; |
| 273 |
| 274 default: |
| 275 // RDB fields? |
| 276 // printf("optc: %d %s %s\n", optc, longopts[optc].name, optarg); |
| 277 if (optc > 0 && optc <= max_field_index) |
| 278 OptionHandler(longopts[optc].name, optarg); |
| 279 else |
| 280 usage_help_exit(1); |
| 281 break; |
| 282 } |
| 283 } |
| 284 |
| 285 // currently no other non-dashed arguments allowed. |
| 286 if (optind != argc) |
| 287 usage_help_exit(1); |
| 288 |
| 289 GetRDBOffset(); |
| 290 ReadNvram(); |
| 291 |
| 292 if (!cfg.fields_to_set.empty()) { |
| 293 SetNewMode(); |
| 294 } else { |
| 295 PrintCurrentMode(); |
| 296 } |
| 297 return 0; |
| 298 } |
OLD | NEW |