Bidirectional test mode signaling over GPIO

* Implements a bidirectional synchronous handshake protocol over
  dut_flaga/b GPIOs. This protocol is designed to return true (test
  mode) only if the DUT is connected to a servo board which implements
  the remote end.

* Includes unit tests for the test mode signaling routine, complete with
  mock/fake implementation of the remote end.

Note that we still do not deploy GpioHandler in actual update
processing, which will be done later.

BUG=chromium-os:25397,chromium-os:27109,chromium-os:27672
TEST=Builds and passes unit tests (including new ones)

Change-Id: I265407ed735c3e1354e10782ac30566b16caeb20
Reviewed-on: https://gerrit.chromium.org/gerrit/23330
Reviewed-by: Gaurav Shah <gauravsh@chromium.org>
Tested-by: Gilad Arnold <garnold@chromium.org>
Commit-Ready: Gilad Arnold <garnold@chromium.org>
Reviewed-by: Gilad Arnold <garnold@chromium.org>
diff --git a/gpio_mock_file_descriptor.h b/gpio_mock_file_descriptor.h
new file mode 100644
index 0000000..a4fd4e8
--- /dev/null
+++ b/gpio_mock_file_descriptor.h
@@ -0,0 +1,255 @@
+// Copyright (c) 2012 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.
+
+#ifndef CHROMEOS_PLATFORM_UPDATE_ENGINE_GPIO_MOCK_FILE_DESCRIPTOR_H__
+#define CHROMEOS_PLATFORM_UPDATE_ENGINE_GPIO_MOCK_FILE_DESCRIPTOR_H__
+
+#include <base/rand_util.h>
+#include <base/time.h>
+
+#include "update_engine/file_descriptor.h"
+#include "update_engine/gpio_handler_unittest.h"
+
+// A set of mock file descriptors used for unit-testing. All classes here
+// inherit the FileDescriptor interface.
+
+namespace chromeos_update_engine {
+
+// An abstract classs implementing a common mock infrastructure for GPIO
+// reading/writing. This includes all the inherited interface methods, and basic
+// logic to manage the opening, reading, writing and closing of GPIO files. It
+// is up to concrete implementations to manage their internal state machine and
+// update the values to be read by clients. In most cases, this amounts to
+// adding internal state and overloading the UpdateState() method to change to
+// various GPIO "registers" accordingly.
+class GpioMockFileDescriptor : public FileDescriptor {
+ public:
+  GpioMockFileDescriptor();
+
+  // Interface methods.
+  virtual bool Open(const char* path, int flags, mode_t mode);
+  virtual bool Open(const char* path, int flags);
+  virtual ssize_t Read(void* buf, size_t count);
+  virtual ssize_t Write(const void* buf, size_t count);
+  virtual bool Close();
+  virtual void Reset();
+  virtual bool IsSettingErrno();
+  virtual bool IsOpen() {
+    return (gpio_id_ < kMockGpioIdMax && gpio_subdev_ < kMockGpioSubdevMax);
+  }
+
+
+  // Returns true iff all file resources were freed; otherwise, will fail the
+  // current test.
+  virtual bool ExpectAllResourcesDeallocated();
+
+  // Returns true iff all GPIOs have been restored to their default state;
+  // otherwise, will fail the current test.
+  virtual bool ExpectAllGpiosRestoredToDefault();
+
+ protected:
+  // A pair of write value and time at which it was written.
+  struct MockGpioWriteEvent {
+    MockGpioVal val;
+    base::Time time;
+  };
+
+  // Sets the last written value and timestamp of GPIO |gpio_id|.
+  inline MockGpioVal SetGpioLastWrite(MockGpioId gpio_id, MockGpioVal val,
+                                      base::Time time) {
+    gpio_last_writes_[gpio_id].time = time;
+    return (gpio_last_writes_[gpio_id].val = val);
+  }
+
+  inline MockGpioVal SetGpioLastWrite(MockGpioId gpio_id, MockGpioVal val) {
+    return SetGpioLastWrite(gpio_id, val, base::Time::Now());
+  }
+
+
+  // The current direction of each GPIO device. These are generally handled by
+  // Write(), but can be modified by concrete implementations of this class to
+  // simulate race conditions on GPIO devices.
+  MockGpioDir gpio_dirs_[kMockGpioIdMax];
+
+  // The current values to be read by the DUT. These can be modified by concrete
+  // implementations of this class, to reflect current GPIO values.
+  MockGpioVal gpio_read_vals_[kMockGpioIdMax];
+
+  // The last values and time they were written by the DUT to each GPIO device.
+  // These are generally handled by Write(), but can be modified by concrete
+  // implementations of this class to simulate race conditions on GPIO devices.
+  MockGpioWriteEvent gpio_last_writes_[kMockGpioIdMax];
+
+  // Override strings for GPIO value / direction readings. Initialized to null
+  // pointers by default, which means the default values will not be overridden.
+  const char* override_read_gpio_val_strings_[kMockGpioValMax];
+  const char* override_read_gpio_dir_strings_[kMockGpioDirMax];
+
+ private:
+  // GPIO subdevice identifiers.
+  enum MockGpioSubdev {
+    kMockGpioSubdevValue,
+    kMockGpioSubdevDirection,
+    kMockGpioSubdevMax  // marker, do not remove!
+  };
+
+  // Device name prefixes of the different GPIOs.
+  static const char* gpio_devname_prefixes_[kMockGpioIdMax];
+
+  // Strings to be written as GPIO values, corresponding to the abstract GPIO
+  // value.
+  static const char* gpio_val_strings_[kMockGpioValMax];
+
+  // Strings to be written as GPIO directions, corresponding to the abstract
+  // GPIO direction.
+  static const char* gpio_dir_strings_[kMockGpioDirMax];
+
+
+  // Compare a string |buf| of length |count| that is written to a GPIO device
+  // with an array of strings |strs| of length |num_strs|. Returns the index of
+  // the string entry that is the same as the written string, or |num_strs| if
+  // none was found. Requires that the the last character in |buf| is a newline.
+  size_t DecodeGpioString(const char* buf, size_t count, const char** strs,
+                          size_t num_strs) const;
+
+  // Decode a written GPIO value.
+  inline MockGpioVal DecodeGpioVal(const char* buf, size_t count) const {
+    return static_cast<MockGpioVal>(
+        DecodeGpioString(buf, count, gpio_val_strings_, kMockGpioValMax));
+  }
+
+  // Decodes a written GPIO direction.
+  inline MockGpioDir DecodeGpioDir(const char* buf, size_t count) const {
+    return static_cast<MockGpioDir>(
+        DecodeGpioString(buf, count, gpio_dir_strings_, kMockGpioDirMax));
+  }
+
+  // Simulate the Servo state transition, based on the last recorded state, the
+  // time it was recorded, and the current GPIO values. This is a pure virtual
+  // function that must be implemented by concrete subclasses.
+  virtual void UpdateState() = 0;
+
+  // The identifier of the currently accessed GPIO device.
+  MockGpioId gpio_id_;
+
+  // The identifier of the currently accessed GPIO sub-device.
+  MockGpioSubdev gpio_subdev_;
+};
+
+
+// A mock file descriptor that implements the GPIO test signaling protocol. In
+// doing so, it simulates the asynchronous behavior of a properly implemented
+// Servo test controller.
+class TestModeGpioMockFileDescriptor : public GpioMockFileDescriptor {
+ public:
+  TestModeGpioMockFileDescriptor(base::TimeDelta servo_poll_interval);
+  virtual ~TestModeGpioMockFileDescriptor() {};
+
+ protected:
+  // The state of the Servo-side GPIO signaling protocol. These do not include
+  // sub-state changes on the DUT side, which can be approximated by tracking
+  // read operations but otherwise cannot be observed by an ordinary Servo.
+  enum ServoState {
+    kServoStateInit,
+    kServoStateTriggerSent,
+    kServoStateChallengeUpReceived,
+    kServoStateChallengeDownReceived,
+    kServoStateMax  // marker, do not remove!
+  };
+
+  // Simulate the Servo state transition, based on the last recorded state, the
+  // time it was recorded, and the current GPIO values.
+  virtual void UpdateState();
+
+  // The last recorded state in the GPIO protocol.
+  ServoState last_state_;
+
+ private:
+  // Return a uniformly distributed random time delta within the Servo poll
+  // interval.
+  inline base::TimeDelta RandomServoPollFuzz() {
+    return base::TimeDelta::FromMicroseconds(
+        base::RandInt(0, servo_poll_interval_.InMicroseconds()));
+  }
+
+  // The Servo poll interval.
+  base::TimeDelta servo_poll_interval_;
+
+  // The current Servo poll fuzz, used for deciding when signals are (simulated
+  // to be) sensed within the poll interval. Must be between zero and
+  // servo_poll_interval_.
+  base::TimeDelta curr_servo_poll_fuzz_;
+};
+
+
+// A mock file descriptor that implements GPIO feedback when not conneced to a
+// Servo, on boards that include a pull-up resistor wiring for GPIOs. This is
+// the typical mode of operations for Chromebooks out in the field, and we need
+// to make sure that the client is not made to believe that it is in test mode.
+class NormalModeGpioMockFileDescriptor : public GpioMockFileDescriptor {
+ private:
+  // This is a no-op, as there's no Servo connected.
+  virtual void UpdateState() {};
+};
+
+// A mock file descriptor that implements GPIOs that are not pulled-up by
+// default, and whose idle reading might be zero. We've seen this problem on
+// Lumpy/Stumpy and need to make sure that the protocol doesn't allow these
+// boards to go into test mode without actually being told so by a Servo.
+class NonPulledUpNormalModeGpioMockFileDescriptor
+    : public GpioMockFileDescriptor {
+ private:
+  // Set the default value of dut_flaga to "down".
+  virtual void UpdateState() {
+    gpio_read_vals_[kMockGpioIdDutflaga] = kMockGpioValDown;
+  }
+};
+
+// A mock file descriptor that implements a bogus GPIO feedback. This includes
+// flipping GPIO directions, invalid value readings, and I/O errors on various
+// file operations. All of these instances must be ruled out by the protocol,
+// resulting in normal mode operation.
+class ErrorNormalModeGpioMockFileDescriptor :
+    public TestModeGpioMockFileDescriptor {
+ public:
+  enum GpioError {
+    kGpioErrorFlipInputDir,
+    kGpioErrorReadInvalidVal,
+    kGpioErrorReadInvalidDir,
+    kGpioErrorFailFileOpen,
+    kGpioErrorFailFileRead,
+    kGpioErrorFailFileWrite,
+    kGpioErrorFailFileClose,
+  };
+
+  ErrorNormalModeGpioMockFileDescriptor(base::TimeDelta servo_poll_interval,
+                                        GpioError error);
+  virtual ~ErrorNormalModeGpioMockFileDescriptor() {};
+
+  // Wrapper methods for the respectively inherited ones, which can fail the
+  // call as part of a test.
+  virtual bool Open(const char* path, int flags, mode_t mode);
+  virtual ssize_t Read(void* buf, size_t count);
+  virtual ssize_t Write(const void* buf, size_t count);
+  virtual bool Close();
+
+  // Wrapper which restores all state we might have tampered with.
+  virtual bool ExpectAllGpiosRestoredToDefault();
+
+ private:
+  // Wraps the ordinary test mode servo simulation with an error injecting
+  // behavior, which corresponds to the requested type of error.
+  virtual void UpdateState();
+
+  // The GPIO error to be injected into the protocol.
+  GpioError error_;
+
+  // A flag denoting whether the direction of dut_flaga was already maliciously
+  // flipped.
+  bool is_dutflaga_dir_flipped_;
+};
+
+}  // namespace chromeos_update_engine
+
+#endif  // CHROMEOS_PLATFORM_UPDATE_ENGINE_GPIO_MOCK_FILE_DESCRIPTOR_H__