semihosting.hh revision 12531
1/* 2 * Copyright (c) 2018 ARM Limited 3 * All rights reserved 4 * 5 * The license below extends only to copyright in the software and shall 6 * not be construed as granting a license to any other intellectual 7 * property including but not limited to intellectual property relating 8 * to a hardware implementation of the functionality of the software 9 * licensed hereunder. You may use the software subject to the license 10 * terms below provided that you ensure that this notice is replicated 11 * unmodified and in its entirety in all distributions of the software, 12 * modified or unmodified, in source code or in binary form. 13 * 14 * Redistribution and use in source and binary forms, with or without 15 * modification, are permitted provided that the following conditions are 16 * met: redistributions of source code must retain the above copyright 17 * notice, this list of conditions and the following disclaimer; 18 * redistributions in binary form must reproduce the above copyright 19 * notice, this list of conditions and the following disclaimer in the 20 * documentation and/or other materials provided with the distribution; 21 * neither the name of the copyright holders nor the names of its 22 * contributors may be used to endorse or promote products derived from 23 * this software without specific prior written permission. 24 * 25 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 26 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 27 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 28 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 29 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 30 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 31 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 32 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 33 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 34 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 35 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 36 * 37 * Authors: Andreas Sandberg 38 */ 39#ifndef __ARCH_ARM_SEMIHOSTING_HH__ 40#define __ARCH_ARM_SEMIHOSTING_HH__ 41 42#include <cstdio> 43#include <map> 44#include <memory> 45#include <utility> 46#include <vector> 47 48#include "sim/sim_object.hh" 49 50struct ArmSemihostingParams; 51class SerialDevice; 52class ThreadContext; 53 54/** 55 * Semihosting for AArch32 and AArch64 56 * 57 * This class implements the Arm semihosting interface. This interface 58 * allows baremetal code access service, such as IO, from the 59 * simulator. It is conceptually a simplified version of gem5's more 60 * general syscall emulation mode. 61 * 62 * Exits calls (SYS_EXIT, SYS_EXIT_EXTENDED) from the guest get 63 * translated into simualtion exits. Well-known exit codes are 64 * translated to messages on the form 'semi:ADP_.*' while unknown 65 * codes are returned in hex ('semi:0x..'). The subcode is reported in 66 * the gem5 exit event. 67 */ 68class ArmSemihosting : public SimObject 69{ 70 public: 71 ArmSemihosting(const ArmSemihostingParams *p); 72 73 /** Perform an Arm Semihosting call from aarch64 code. */ 74 uint64_t call64(ThreadContext *tc, uint32_t op, uint64_t param); 75 /** Perform an Arm Semihosting call from aarch32 code. */ 76 uint32_t call32(ThreadContext *tc, uint32_t op, uint32_t param); 77 78 public: // SimObject and related interfaces 79 void serialize(CheckpointOut &cp) const override; 80 void unserialize(CheckpointIn &cp) override; 81 82 protected: // Configuration 83 const std::string cmdLine; 84 const Addr memReserve; 85 const Addr stackSize; 86 87 /** 88 * Base time when the simulation started. This is used to 89 * calculate the time of date when the guest call SYS_TIME. 90 */ 91 const time_t timeBase; 92 93 /** Number of bits to right shift gem5 ticks to fit in a uint32_t */ 94 const unsigned tickShift; 95 96 protected: // Internal state 97 typedef uint64_t SemiErrno; 98 SemiErrno semiErrno; 99 100 protected: // File IO 101 /** 102 * Internal state for open files 103 * 104 * This class describes the internal state of a file opened 105 * through the semihosting interface. 106 * 107 * A file instance is normally created using one of the 108 * ArmSemihosting::FileBase::create() factory methods. These 109 * methods handle some the magic file names in the Arm Semihosting 110 * specification and instantiate the right implementation. For the 111 * same, when unserializing a checkpoint, the create method must 112 * be used to unserialize a new instance of a file descriptor. 113 */ 114 class FileBase : public Serializable 115 { 116 public: 117 FileBase(ArmSemihosting &_parent, const char *name, const char *_mode) 118 : parent(_parent), _name(name), mode(_mode) {} 119 virtual ~FileBase() {}; 120 121 FileBase() = delete; 122 FileBase(FileBase &) = delete; 123 124 static std::unique_ptr<FileBase> create( 125 ArmSemihosting &parent, const std::string &fname, 126 const char *mode); 127 static std::unique_ptr<FileBase> create( 128 ArmSemihosting &parent, CheckpointIn &cp, const std::string &sec); 129 130 void serialize(CheckpointOut &cp) const override; 131 void unserialize(CheckpointIn &cp) override; 132 133 const std::string &fileName() { return _name; } 134 135 public: 136 /** @{ 137 * Semihosting file IO interfaces 138 * 139 * These interfaces implement common IO functionality in the 140 * Semihosting interface. 141 * 142 * All functions return a negative value that corresponds to a 143 * UNIX errno value when they fail and >=0 on success. 144 */ 145 146 /** 147 * Open the the file. 148 * 149 * @return <0 on error (-errno), 0 on success. 150 */ 151 virtual int64_t open() { return 0; } 152 153 /** 154 * Close the file. 155 * 156 * @return <0 on error (-errno), 0 on success. 157 */ 158 virtual int64_t close() { return 0; } 159 160 /** 161 * Check if a file corresponds to a TTY device. 162 * 163 * @return True if the file is a TTY, false otherwise. 164 */ 165 virtual bool isTTY() const { return false; } 166 167 /** 168 * Read data from file. 169 * 170 * @return <0 on error (-errno), bytes read on success (0 for EOF). 171 */ 172 virtual int64_t read(uint8_t *buffer, uint64_t size); 173 174 /** 175 * Write data to file. 176 * 177 * @return <0 on error (-errno), bytes written on success. 178 */ 179 virtual int64_t write(const uint8_t *buffer, uint64_t size); 180 181 /** 182 * Seek to an absolute position in the file. 183 * 184 * @param pos Byte offset from start of file. 185 * @return <0 on error (-errno), 0 on success. 186 */ 187 virtual int64_t seek(uint64_t pos); 188 189 /** 190 * Get the length of a file in bytes. 191 * 192 * @return <0 on error (-errno), length on success 193 */ 194 virtual int64_t flen(); 195 196 /** @} */ 197 198 protected: 199 ArmSemihosting &parent; 200 std::string _name; 201 std::string mode; 202 }; 203 204 /** Implementation of the ':semihosting-features' magic file. */ 205 class FileFeatures : public FileBase 206 { 207 public: 208 FileFeatures(ArmSemihosting &_parent, 209 const char *name, const char *mode); 210 211 void serialize(CheckpointOut &cp) const override; 212 void unserialize(CheckpointIn &cp) override; 213 214 int64_t read(uint8_t *buffer, uint64_t size) override; 215 int64_t seek(uint64_t pos) override; 216 217 protected: 218 size_t pos; 219 }; 220 221 class File : public FileBase 222 { 223 public: 224 File(ArmSemihosting &_parent, const char *name, const char *mode); 225 ~File(); 226 227 void serialize(CheckpointOut &cp) const override; 228 void unserialize(CheckpointIn &cp) override; 229 230 int64_t open() override { return openImpl(false); } 231 int64_t close() override; 232 bool isTTY() const override; 233 int64_t read(uint8_t *buffer, uint64_t size) override; 234 int64_t write(const uint8_t *buffer, uint64_t size) override; 235 int64_t seek(uint64_t pos) override; 236 int64_t flen() override; 237 238 protected: 239 int64_t openImpl(bool unserialize); 240 bool needClose() const { return !isTTY(); } 241 242 FILE *file; 243 }; 244 245 std::vector<std::unique_ptr<FileBase>> files; 246 247 protected: // Helper functions 248 unsigned calcTickShift() const { 249 int msb = findMsbSet(SimClock::Frequency); 250 return msb > 31 ? msb - 31 : 0; 251 } 252 uint64_t semiTick(Tick tick) const { 253 return tick >> tickShift; 254 } 255 void semiExit(uint64_t code, uint64_t subcode); 256 std::string readString(ThreadContext *tc, Addr ptr, size_t len); 257 258 private: 259 typedef std::pair<uint64_t, SemiErrno> RetErrno; 260 static constexpr RetErrno retError(SemiErrno e) { 261 return RetErrno((uint64_t)-1, e); 262 } 263 264 static constexpr RetErrno retOK(uint64_t r) { 265 return RetErrno(r, 0); 266 } 267 268 /** 269 * Semihosting call information structure. 270 * 271 * This structure describes how a semi-hosting call is 272 * implemented. It contains debug information (e.g., the name of 273 * the call), a pointer to the implementation, and information 274 * needed to read its parameters from guest memory. 275 */ 276 struct SemiCall 277 { 278 /** Call name */ 279 const char *name; 280 281 /** 282 * Pointer to call implementation 283 * 284 * @param tc ThreadContext pointer for caller 285 * @param aarch64 True if in aarc64 mode, false otherwise. 286 * @parma argv Argument vector. argv[0] always corresponds to 287 * the pointer to the argument list. Remaining 288 * entries are read as consecutive words starting 289 * at the address pointed to by argv[0]. 290 * @return a (return value, errno) pair 291 */ 292 RetErrno (ArmSemihosting::*call)(ThreadContext *tc, bool aarch64, 293 std::vector<uint64_t> &argv); 294 295 /** Number of aarch32 arguments to read from guest memory. -1 296 * if unimplemented.*/ 297 int argc32; 298 /** Number of aarch32 arguments to read from guest memory. -1 299 * if unimplemented.*/ 300 int argc64; 301 302 /** Is call implemented in aarch32? */ 303 bool implemented32() const { return call && argc32 >= 0; } 304 /** Is call implemented in aarch64? */ 305 bool implemented64() const { return call && argc64 >= 0; } 306 }; 307 308#define SEMI_CALL(N) \ 309 RetErrno call ## N (ThreadContext *tc, \ 310 bool aarch64, std::vector<uint64_t> &argv) 311 312 SEMI_CALL(Open); 313 SEMI_CALL(Close); 314 SEMI_CALL(WriteC); 315 SEMI_CALL(Write0); 316 SEMI_CALL(Write); 317 SEMI_CALL(Read); 318 SEMI_CALL(ReadC); 319 SEMI_CALL(IsError); 320 SEMI_CALL(IsTTY); 321 SEMI_CALL(Seek); 322 SEMI_CALL(FLen); 323 SEMI_CALL(TmpNam); 324 SEMI_CALL(Remove); 325 SEMI_CALL(Rename); 326 SEMI_CALL(Clock); 327 SEMI_CALL(Time); 328 SEMI_CALL(System); 329 SEMI_CALL(Errno); 330 SEMI_CALL(GetCmdLine); 331 SEMI_CALL(HeapInfo); 332 SEMI_CALL(Exit); 333 SEMI_CALL(ExitExtended); 334 335 SEMI_CALL(Elapsed); 336 SEMI_CALL(TickFreq); 337 338#undef SEMI_CALL 339 340 static const SemiCall *getCall(uint32_t op, bool aarch64); 341 342 static const std::map<uint32_t, SemiCall> calls; 343 static const std::vector<const char *> fmodes; 344 static const std::map<uint64_t, const char *> exitCodes; 345 static const std::vector<uint8_t> features; 346}; 347 348#endif // __ARCH_ARM_SEMIHOSTING_HH__ 349