Index: reboot_mode.cc |
diff --git a/reboot_mode.cc b/reboot_mode.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..57a5bc8bce049b34a544261af19bd3465b83faf3 |
--- /dev/null |
+++ b/reboot_mode.cc |
@@ -0,0 +1,298 @@ |
+// Copyright (c) 2010 The Chromium OS Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+// NOTE: this file is translated from reboot_mode.py |
+// |
+// A script to manipulate ChromeOS CMOS reboot Field. |
+// |
+// A few bits in a byte in CMOS (called RDB, short for 'Reboot Data Byte' |
+// hereafter) are dedicated to information exchange between Linux and BIOS. |
+// |
+// The CMOS contents are available through /dev/nvrom. The location of RDB is |
+// reported by BIOS through ACPI. The few bits in RDB operated on by this script |
+// are defined in the all_mode_fields dictionary below along with their |
+// functions. |
+// |
+// When invoked without parameters this script prints values of the fields. When |
+// invoked with any of the bit field names as parameter(s), the script sets the |
+// bit(s) as required. |
+// |
+// Additional parameters allow to specify alternative ACPI and NVRAM files for |
+// testing. |
+ |
+#include <stdio.h> |
+#include <stdlib.h> |
+#include <assert.h> |
+#include <unistd.h> |
+#include <sys/param.h> |
+#include <stdint.h> |
+#include <errno.h> |
+#include <stdarg.h> |
+#include <getopt.h> |
+ |
+#include <string> |
+#include <algorithm> |
+#include <vector> |
+#include <map> |
+ |
+#define DEFAULT_ACPI_FILE "/sys/devices/platform/chromeos_acpi/CHNV" |
+#define DEFAULT_NVRAM_FILE "/dev/nvram" |
+ |
+/////////////////////////////////////////////////////////////////////// |
+// Utilities for quick python-C translation |
+ |
+using std::string; |
+ |
+// Works like throwing an exception - directly exit here. |
+static void RebootModeError(const char *message, ...) { |
+ va_list args; |
+ va_start(args, message); |
+ vfprintf(stderr, message, args); |
+ fprintf(stderr, "\n"); |
+ va_end(args); |
+ exit(1); |
+} |
+ |
+// return a string which is upper case of input parameter. |
+static string str_to_upper(const string str) { |
+ string newstr = str; |
+ std::transform(str.begin(), str.end(), newstr.begin(), toupper); |
+ return newstr; |
+} |
+ |
+/////////////////////////////////////////////////////////////////////// |
+ |
+typedef std::map<string, uint8_t> RDB_BitField; |
+ |
+struct RebootModeConfig { |
+ // file path |
+ string acpi_file; |
+ string nvram_file; |
+ |
+ // Offset of RDB in NVRAM |
+ int rdb_offset; |
+ |
+ // NVRAM contents read on startup |
+ uint8_t rdb_value; |
+ |
+ // A dictionary of fields to be updated as requested by the command line |
+ // parameters |
+ RDB_BitField fields_to_set; |
+ |
+ // All bitfields in RDB this script provides access to. Field names prepended |
+ // by `--' become this script's command line options |
+ RDB_BitField all_mode_fields; |
+ |
+ // write constructure here to ease python translation |
+ RebootModeConfig() { |
+ acpi_file = DEFAULT_ACPI_FILE; |
+ nvram_file = DEFAULT_NVRAM_FILE; |
+ rdb_offset = 0; |
+ all_mode_fields["recovery"] = 0x80; |
+ all_mode_fields["debug_reset"] = 0x40; |
+ all_mode_fields["try_firmware_b"] = 0x20; |
+ } |
+} cfg; // this is a special global object to ease python translation. |
+ |
+// Process bit field name command line parameter. |
+// |
+// Verifies that the value is in range (0 or 1) and adds the appropriate |
+// element to fields_to_set dictionary. Should the same field specified in the |
+// command line more than once, only the last value will be used. |
+// |
+// Raises: |
+// RebootModeError in case the parameter value is out of range. |
+void OptionHandler(const char *opt_str, const char *value_str) { |
+ const char *key = opt_str; |
+ int value = atoi(value_str); |
+ |
+ if (!value && *value_str != '0') |
+ RebootModeError("--%s needs numeric argument value", key); |
+ |
+ if (value < 0 || value > 1) |
+ RebootModeError("--%s should be either 0 or 1", key); |
+ |
+ if (cfg.all_mode_fields.find(key) == cfg.all_mode_fields.end()) |
+ RebootModeError("INTERNAL ERROR: UNKNOWN FIELD: %s", key); |
+ |
+ cfg.fields_to_set[key] = value; |
+} |
+ |
+void GetRDBOffset() { |
+ const char *acpi_file = cfg.acpi_file.c_str(); |
+ |
+ FILE *f = fopen(acpi_file, "rb"); |
+ if (!f) { |
+ perror(acpi_file); |
+ RebootModeError("Cannot read acpi_file: %s", acpi_file); |
+ } |
+ |
+ char buffer[PATH_MAX]; // this should be large enough |
+ if (fgets(buffer, sizeof(buffer), f) == NULL) { |
+ perror(acpi_file); |
+ RebootModeError("Trying to read %s but failed.", acpi_file); |
+ } |
+ |
+ cfg.rdb_offset = atoi(buffer); |
+ if (!cfg.rdb_offset && *buffer != '0') { // consider this as error |
+ RebootModeError("%s contents are corrupted", acpi_file); |
+ } |
+} |
+ |
+void ReadNvram() { |
+ FILE *f = fopen(cfg.nvram_file.c_str(), "rb"); |
+ if (!f) { |
+ if (errno == 2) |
+ RebootModeError("%s does not exist. Is nvram module installed?", |
+ cfg.nvram_file.c_str()); |
+ if (errno == 13) |
+ RebootModeError("Access to %s denied. Are you root?", |
+ cfg.nvram_file.c_str()); |
+ // all other error |
+ perror(cfg.nvram_file.c_str()); |
+ } |
+ |
+ if (fseek(f, cfg.rdb_offset, SEEK_SET) == -1 || |
+ fread(&cfg.rdb_value, 1, 1, f) != 1) { |
+ perror(cfg.nvram_file.c_str()); |
+ RebootModeError("Failed reading mode byte from %s", cfg.nvram_file.c_str()); |
+ } |
+ fclose(f); |
+} |
+ |
+void PrintCurrentMode() { |
+ printf("Current reboot mode settings:\n"); |
+ for (RDB_BitField::iterator i = cfg.all_mode_fields.begin(); |
+ i != cfg.all_mode_fields.end(); |
+ ++i) { |
+ printf("%-15s: %d\n", i->first.c_str(), |
+ (cfg.rdb_value & i->second) ? 1 : 0); |
+ } |
+} |
+ |
+void SetNewMode() { |
+ uint8_t new_mode = 0; |
+ uint8_t updated_bits_mask = 0; |
+ |
+ for (RDB_BitField::iterator i = cfg.fields_to_set.begin(); |
+ i != cfg.fields_to_set.end(); |
+ ++i) { |
+ string opt = i->first; |
+ uint8_t value = i->second; |
+ assert(cfg.all_mode_fields.find(opt) != cfg.all_mode_fields.end()); |
+ |
+ uint8_t mask = cfg.all_mode_fields[opt]; |
+ if (value) |
+ new_mode |= mask; |
+ |
+ updated_bits_mask |= mask; |
+ } |
+ |
+ if ((cfg.rdb_value & updated_bits_mask) == new_mode) { |
+ printf("No update required\n"); |
+ return; |
+ } |
+ |
+ FILE *f = fopen(cfg.nvram_file.c_str(), "rb+"); |
+ fseek(f, cfg.rdb_offset, SEEK_SET); |
+ new_mode |= (cfg.rdb_value & ~updated_bits_mask); |
+ if (fwrite(&new_mode, 1, 1, f) != 1) { |
+ RebootModeError("Failed writing mode byte to %s", cfg.nvram_file.c_str()); |
+ } |
+ fclose(f); |
+} |
+ |
+const char *__name__; |
+ |
+void usage_help_exit(int err) { |
+ printf("Usage: %s [options]\n" |
+ "\n" |
+ "Options:\n" |
+ " -h, --help\t\tshow this help message and exit\n" |
+ " --acpi_file=ACPI_FILE\n" |
+ " --nvram_file=NVRAM_FILE\n" |
+ , __name__); |
+ |
+ // list all possible fields |
+ for (RDB_BitField::iterator i = cfg.all_mode_fields.begin(); |
+ i != cfg.all_mode_fields.end(); |
+ ++i) { |
+ printf(" --%s=%s\n", |
+ i->first.c_str(), |
+ str_to_upper(i->first).c_str()); |
+ } |
+ exit(err); |
+} |
+ |
+int main(int argc, char *argv[]) { |
+ __name__ = argv[0]; |
+ |
+ struct option optv = {0}; |
+ std::vector<struct option> longopts; |
+ int max_field_index = 0; |
+ |
+ optv.has_arg = 0; |
+ optv.val = 0; |
+ optv.name = "help"; // value = 0 |
+ longopts.push_back(optv); |
+ optv.has_arg = 1; |
+ |
+ optv.val++; |
+ optv.name = "acpi_file"; // value = 1 |
+ longopts.push_back(optv); |
+ optv.val++; |
+ optv.name = "nvram_file"; // value = 2 |
+ longopts.push_back(optv); |
+ |
+ // add all known RDB fields |
+ for (RDB_BitField::iterator i = cfg.all_mode_fields.begin(); |
+ i != cfg.all_mode_fields.end(); |
+ ++i) { |
+ optv.name = i->first.c_str(); |
+ optv.val++; |
+ longopts.push_back(optv); |
+ } |
+ max_field_index = optv.val; |
+ |
+ // add a zero for closing options |
+ optv.name = NULL; |
+ optv.val = optv.has_arg = 0; |
+ longopts.push_back(optv); |
+ |
+ int optc; |
+ while ((optc = getopt_long(argc, argv, "h", &longopts.front(), NULL)) != -1) { |
+ switch (optc) { |
+ case 1: |
+ cfg.acpi_file = optarg; |
+ break; |
+ |
+ case 2: |
+ cfg.nvram_file = optarg; |
+ break; |
+ |
+ default: |
+ // RDB fields? |
+ // printf("optc: %d %s %s\n", optc, longopts[optc].name, optarg); |
+ if (optc > 0 && optc <= max_field_index) |
+ OptionHandler(longopts[optc].name, optarg); |
+ else |
+ usage_help_exit(1); |
+ break; |
+ } |
+ } |
+ |
+ // currently no other non-dashed arguments allowed. |
+ if (optind != argc) |
+ usage_help_exit(1); |
+ |
+ GetRDBOffset(); |
+ ReadNvram(); |
+ |
+ if (!cfg.fields_to_set.empty()) { |
+ SetNewMode(); |
+ } else { |
+ PrintCurrentMode(); |
+ } |
+ return 0; |
+} |