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 PortProxy; 52class SerialDevice; 53class ThreadContext; 54 55/** 56 * Semihosting for AArch32 and AArch64 57 * 58 * This class implements the Arm semihosting interface. This interface 59 * allows baremetal code access service, such as IO, from the 60 * simulator. It is conceptually a simplified version of gem5's more 61 * general syscall emulation mode. 62 * 63 * Exits calls (SYS_EXIT, SYS_EXIT_EXTENDED) from the guest get 64 * translated into simualtion exits. Well-known exit codes are 65 * translated to messages on the form 'semi:ADP_.*' while unknown 66 * codes are returned in hex ('semi:0x..'). The subcode is reported in 67 * the gem5 exit event. 68 */ 69class ArmSemihosting : public SimObject 70{ 71 public: 72 ArmSemihosting(const ArmSemihostingParams *p); 73 74 /** Perform an Arm Semihosting call from aarch64 code. */ 75 uint64_t call64(ThreadContext *tc, uint32_t op, uint64_t param); 76 /** Perform an Arm Semihosting call from aarch32 code. */ 77 uint32_t call32(ThreadContext *tc, uint32_t op, uint32_t param); 78 79 public: // SimObject and related interfaces 80 void serialize(CheckpointOut &cp) const override; 81 void unserialize(CheckpointIn &cp) override; 82 83 protected: // Configuration 84 const std::string cmdLine; 85 const Addr memReserve; 86 const Addr stackSize; 87 88 /** 89 * Base time when the simulation started. This is used to 90 * calculate the time of date when the guest call SYS_TIME. 91 */ 92 const time_t timeBase; 93 94 /** Number of bits to right shift gem5 ticks to fit in a uint32_t */ 95 const unsigned tickShift; 96 97 protected: // Internal state 98 typedef uint64_t SemiErrno; 99 SemiErrno semiErrno; 100 101 protected: // File IO 102 /** 103 * Internal state for open files 104 * 105 * This class describes the internal state of a file opened 106 * through the semihosting interface. 107 * 108 * A file instance is normally created using one of the 109 * ArmSemihosting::FileBase::create() factory methods. These 110 * methods handle some the magic file names in the Arm Semihosting 111 * specification and instantiate the right implementation. For the 112 * same, when unserializing a checkpoint, the create method must 113 * be used to unserialize a new instance of a file descriptor. 114 */ 115 class FileBase : public Serializable 116 { 117 public: 118 FileBase(ArmSemihosting &_parent, const char *name, const char *_mode) 119 : parent(_parent), _name(name), mode(_mode) {} 120 virtual ~FileBase() {}; 121 122 FileBase() = delete; 123 FileBase(FileBase &) = delete; 124 125 static std::unique_ptr<FileBase> create( 126 ArmSemihosting &parent, const std::string &fname, 127 const char *mode); 128 static std::unique_ptr<FileBase> create( 129 ArmSemihosting &parent, CheckpointIn &cp, const std::string &sec); 130 131 void serialize(CheckpointOut &cp) const override; 132 void unserialize(CheckpointIn &cp) override; 133 134 const std::string &fileName() { return _name; } 135 136 public: 137 /** @{ 138 * Semihosting file IO interfaces 139 * 140 * These interfaces implement common IO functionality in the 141 * Semihosting interface. 142 * 143 * All functions return a negative value that corresponds to a 144 * UNIX errno value when they fail and >=0 on success. 145 */ 146 147 /** 148 * Open the the file. 149 * 150 * @return <0 on error (-errno), 0 on success. 151 */ 152 virtual int64_t open() { return 0; } 153 154 /** 155 * Close the file. 156 * 157 * @return <0 on error (-errno), 0 on success. 158 */ 159 virtual int64_t close() { return 0; } 160 161 /** 162 * Check if a file corresponds to a TTY device. 163 * 164 * @return True if the file is a TTY, false otherwise. 165 */ 166 virtual bool isTTY() const { return false; } 167 168 /** 169 * Read data from file. 170 * 171 * @return <0 on error (-errno), bytes read on success (0 for EOF). 172 */ 173 virtual int64_t read(uint8_t *buffer, uint64_t size); 174 175 /** 176 * Write data to file. 177 * 178 * @return <0 on error (-errno), bytes written on success. 179 */ 180 virtual int64_t write(const uint8_t *buffer, uint64_t size); 181 182 /** 183 * Seek to an absolute position in the file. 184 * 185 * @param pos Byte offset from start of file. 186 * @return <0 on error (-errno), 0 on success. 187 */ 188 virtual int64_t seek(uint64_t pos); 189 190 /** 191 * Get the length of a file in bytes. 192 * 193 * @return <0 on error (-errno), length on success 194 */ 195 virtual int64_t flen(); 196 197 /** @} */ 198 199 protected: 200 ArmSemihosting &parent; 201 std::string _name; 202 std::string mode; 203 }; 204 205 /** Implementation of the ':semihosting-features' magic file. */ 206 class FileFeatures : public FileBase 207 { 208 public: 209 FileFeatures(ArmSemihosting &_parent, 210 const char *name, const char *mode); 211 212 void serialize(CheckpointOut &cp) const override; 213 void unserialize(CheckpointIn &cp) override; 214 215 int64_t read(uint8_t *buffer, uint64_t size) override; 216 int64_t seek(uint64_t pos) override; 217 218 protected: 219 size_t pos; 220 }; 221 222 class File : public FileBase 223 { 224 public: 225 File(ArmSemihosting &_parent, const char *name, const char *mode); 226 ~File(); 227 228 void serialize(CheckpointOut &cp) const override; 229 void unserialize(CheckpointIn &cp) override; 230 231 int64_t open() override { return openImpl(false); } 232 int64_t close() override; 233 bool isTTY() const override; 234 int64_t read(uint8_t *buffer, uint64_t size) override; 235 int64_t write(const uint8_t *buffer, uint64_t size) override; 236 int64_t seek(uint64_t pos) override; 237 int64_t flen() override; 238 239 protected: 240 int64_t openImpl(bool unserialize); 241 bool needClose() const { return !isTTY(); } 242 243 FILE *file; 244 }; 245 246 std::vector<std::unique_ptr<FileBase>> files; 247 FILE *stdin; 248 FILE *stdout; 249 FILE *stderr; 250 251 protected: // Helper functions 252 unsigned calcTickShift() const { 253 int msb = findMsbSet(SimClock::Frequency); 254 return msb > 31 ? msb - 31 : 0; 255 } 256 uint64_t semiTick(Tick tick) const { 257 return tick >> tickShift; 258 } 259 void semiExit(uint64_t code, uint64_t subcode); 260 PortProxy &physProxy(ThreadContext *tc); 261 std::string readString(ThreadContext *tc, Addr ptr, size_t len); 262 263 std::unique_ptr<PortProxy> physProxyS; 264 265 private: 266 typedef std::pair<uint64_t, SemiErrno> RetErrno; 267 static RetErrno retError(SemiErrno e) { 268 return RetErrno((uint64_t)-1, e); 269 } 270 271 static RetErrno retOK(uint64_t r) { 272 return RetErrno(r, 0); 273 } 274 275 /** 276 * Semihosting call information structure. 277 * 278 * This structure describes how a semi-hosting call is 279 * implemented. It contains debug information (e.g., the name of 280 * the call), a pointer to the implementation, and information 281 * needed to read its parameters from guest memory. 282 */ 283 struct SemiCall 284 { 285 /** Call name */ 286 const char *name; 287 288 /** 289 * Pointer to call implementation 290 * 291 * @param tc ThreadContext pointer for caller 292 * @param aarch64 True if in aarc64 mode, false otherwise. 293 * @parma argv Argument vector. argv[0] always corresponds to 294 * the pointer to the argument list. Remaining 295 * entries are read as consecutive words starting 296 * at the address pointed to by argv[0]. 297 * @return a (return value, errno) pair 298 */ 299 RetErrno (ArmSemihosting::*call)(ThreadContext *tc, bool aarch64, 300 std::vector<uint64_t> &argv); 301 302 /** Number of aarch32 arguments to read from guest memory. -1 303 * if unimplemented.*/ 304 int argc32; 305 /** Number of aarch32 arguments to read from guest memory. -1 306 * if unimplemented.*/ 307 int argc64; 308 309 /** Is call implemented in aarch32? */ 310 bool implemented32() const { return call && argc32 >= 0; } 311 /** Is call implemented in aarch64? */ 312 bool implemented64() const { return call && argc64 >= 0; } 313 }; 314 315#define SEMI_CALL(N) \ 316 RetErrno call ## N (ThreadContext *tc, \ 317 bool aarch64, std::vector<uint64_t> &argv) 318 319 SEMI_CALL(Open); 320 SEMI_CALL(Close); 321 SEMI_CALL(WriteC); 322 SEMI_CALL(Write0); 323 SEMI_CALL(Write); 324 SEMI_CALL(Read); 325 SEMI_CALL(ReadC); 326 SEMI_CALL(IsError); 327 SEMI_CALL(IsTTY); 328 SEMI_CALL(Seek); 329 SEMI_CALL(FLen); 330 SEMI_CALL(TmpNam); 331 SEMI_CALL(Remove); 332 SEMI_CALL(Rename); 333 SEMI_CALL(Clock); 334 SEMI_CALL(Time); 335 SEMI_CALL(System); 336 SEMI_CALL(Errno); 337 SEMI_CALL(GetCmdLine); 338 SEMI_CALL(HeapInfo); 339 SEMI_CALL(Exit); 340 SEMI_CALL(ExitExtended); 341 342 SEMI_CALL(Elapsed); 343 SEMI_CALL(TickFreq); 344 345#undef SEMI_CALL 346 347 static const SemiCall *getCall(uint32_t op, bool aarch64); 348 static FILE *getSTDIO(const char *stream_name, 349 const std::string &name, const char *mode); 350 351 static const std::map<uint32_t, SemiCall> calls; 352 static const std::vector<const char *> fmodes; 353 static const std::map<uint64_t, const char *> exitCodes; 354 static const std::vector<uint8_t> features; 355 static const std::map<const std::string, FILE *> stdioMap; 356}; 357 358#endif // __ARCH_ARM_SEMIHOSTING_HH__ 359