Software APIs
bootstrap_fuzzer_util.h
1 // Copyright lowRISC contributors (OpenTitan project).
2 // Licensed under the Apache License, Version 2.0, see LICENSE for details.
3 // SPDX-License-Identifier: Apache-2.0
4 
5 #ifndef OPENTITAN_SW_DEVICE_SILICON_CREATOR_LIB_BOOTSTRAP_FUZZER_UTIL_H_
6 #define OPENTITAN_SW_DEVICE_SILICON_CREATOR_LIB_BOOTSTRAP_FUZZER_UTIL_H_
7 
8 #include <iostream>
9 #include <stddef.h>
10 #include <stdint.h>
11 
12 #include "absl/types/optional.h"
13 #include "absl/types/span.h"
14 #include "sw/device/silicon_creator/lib/drivers/mock_flash_ctrl.h"
15 #include "sw/device/silicon_creator/lib/drivers/mock_otp.h"
16 #include "sw/device/silicon_creator/lib/drivers/mock_rstmgr.h"
17 #include "sw/device/silicon_creator/lib/drivers/mock_spi_device.h"
18 
19 namespace bootstrap_fuzzer {
20 
21 /**
22  * Consumes values on demand from a non-owning span.
23  */
24 class StreamParser {
25  public:
26  /**
27  * For speed, the constructor does not make a copy of `data`. Ensure that
28  * `data` points to memory that outlives `this`.
29  *
30  * @param data Non-owning view into fuzzer-generated data.
31  * @param verbose Whether verbose logging is enabled.
32  */
33  StreamParser(absl::Span<const uint8_t> data, bool verbose)
34  : data_(data), verbose_(verbose) {}
35 
36  StreamParser(StreamParser &&) = default;
37 
38  StreamParser(StreamParser &) = delete;
39 
40  /**
41  * Attempts to parse a `spi_device_cmd_t`. If there are not enough bytes, it
42  * returns `default_value`. When `verbose_` is true, it describes the returned
43  * value in a human-readable string written to stdout
44  */
46  const spi_device_cmd_t cmd = ParseCmd().value_or(default_value);
47  if (verbose_) {
48  std::cout << std::hex << "Parsed spi_device_cmd_t: {"
49  << "opcode=0x" << cmd.opcode << ", address=0x" << cmd.address
50  << ", payload_byte_count=0x" << cmd.payload_byte_count << "}"
51  << std::endl;
52  }
53  return cmd;
54  }
55 
56  /**
57  * Attempts to parse an `IntType` from the stream. If there are not enough
58  * bytes, it returns `default_value`. When `verbose_` is true, it uses
59  * `log_label` to write a human-readable message to stdout.
60  */
61  template <typename IntType>
62  IntType ParseIntOr(const char *log_label, IntType default_value) {
63  const IntType value = ParseInt<IntType>().value_or(default_value);
64  if (verbose_) {
65  std::cout << std::hex << "Parsed " << log_label << ": 0x" << value
66  << std::endl;
67  }
68  return value;
69  }
70 
71  private:
72  template <typename IntType>
73  absl::optional<IntType> ParseInt() {
74  static_assert(
75  std::is_integral<IntType>::value || std::is_enum<IntType>::value,
76  "IntType must be an integral or enum type");
77  if (data_.length() < sizeof(IntType)) {
78  return absl::nullopt;
79  }
80  IntType n;
81  memcpy(&n, data_.data(), sizeof(IntType));
82  data_ = data_.subspan(sizeof(IntType));
83  return n;
84  }
85 
86  absl::optional<spi_device_cmd_t> ParseCmd() {
87  auto opcode = ParseInt<uint8_t>();
88  auto address = ParseInt<uint32_t>();
89  auto payload_byte_count = ParseInt<size_t>();
90  if (!opcode || !address || !payload_byte_count) {
91  return absl::nullopt;
92  }
93  const spi_device_cmd_t cmd{
94  .opcode = static_cast<spi_device_opcode_t>(*opcode & UINT8_MAX),
95  .address = *address,
96  .payload_byte_count =
97  *payload_byte_count % (kSpiDevicePayloadAreaNumBytes + 1),
98  };
99  return cmd;
100  }
101 
102  absl::Span<const uint8_t> data_;
103  bool verbose_{false};
104 };
105 
106 /**
107  * This class is responsible for one-time startup tasks. Instantiate it as a
108  * static local variable in `LLVMFuzzerTestOneInput()`.
109  */
111  public:
113 
116 };
117 
118 /**
119  * This class configures mock objects with reasonable defaults. Some mocks will
120  * enable the fuzzing engine to drive control flow by getting values from a
121  * `StreamParser`. Instantiate this class once per call to
122  * `LLVMFuzzerTestOneInput()`.
123  */
125  public:
126  /**
127  * @param data A chunk of data generated by the fuzzing engine.
128  * @param verbose Whether to enable verbose logging.
129  */
130  AbstractBootstrapMockGroup(StreamParser stream, bool verbose);
131 
132  AbstractBootstrapMockGroup() = delete;
135 
136  /**
137  * This method configures the mocks owned by `AbstractBootstrapMockGroup`. If
138  * you add custom mocks in a derived class and override this method, be sure
139  * to call `AbstractBootstrapMockGroup::ConfigureMocks()`.
140  */
141  virtual void ConfigureMocks();
142 
143  protected:
144  bool verbose_{false};
145  bool flash_status_override_{false};
146  size_t max_spi_cmd_count_{0};
147  size_t spi_cmd_count_{0};
148  StreamParser stream_;
149  ::rom_test::NiceMockSpiDevice spi_device_;
150  ::rom_test::NiceMockRstmgr rstmgr_;
151  ::rom_test::NiceMockFlashCtrl flash_ctrl_;
152  ::rom_test::NiceMockOtp otp_;
153 };
154 
155 } // namespace bootstrap_fuzzer
156 
157 #endif // OPENTITAN_SW_DEVICE_SILICON_CREATOR_LIB_BOOTSTRAP_FUZZER_UTIL_H_