1 // 2 // Copyright (c) 2010-2022 Antmicro 3 // Copyright (c) 2011-2015 Realtime Embedded 4 // 5 // This file is licensed under the MIT License. 6 // Full license text is available in 'licenses/MIT.txt'. 7 // 8 using System; 9 using System.Collections.Concurrent; 10 using System.Collections.Generic; 11 using System.Diagnostics; 12 using System.IO; 13 using System.Linq; 14 using System.Text; 15 using System.Threading; 16 using Antmicro.Migrant; 17 using Antmicro.Migrant.Hooks; 18 using Antmicro.Renode.Core; 19 using Antmicro.Renode.Logging; 20 using Antmicro.Renode.Peripherals.Bus; 21 using Antmicro.Renode.Utilities; 22 #if !PLATFORM_WINDOWS 23 using Mono.Unix; 24 using Mono.Unix.Native; 25 #endif 26 using Antmicro.Renode.UserInterface; 27 28 namespace Antmicro.Renode.Peripherals.Miscellaneous 29 { 30 [Icon("controller")] 31 [AllowedTranslations(AllowedTranslation.ByteToDoubleWord)] 32 public class EmulatorController : IDoubleWordPeripheral, IKnownSize 33 { EmulatorController(IMachine machine)34 public EmulatorController(IMachine machine) 35 { 36 this.machine = machine; 37 stopwatch = new Stopwatch(); 38 stringRegister = new byte[StringRegisterSize]; 39 fileRegister = new byte[FileRegisterSize]; 40 dictionary = new ConcurrentDictionary<string, string>(); 41 } 42 ReadDoubleWord(long offset)43 public uint ReadDoubleWord(long offset) 44 { 45 if((Register)offset == Register.Activate) 46 { 47 return Magic + Version; 48 } 49 if(activated) 50 { 51 return HandleRead(offset); 52 } 53 this.Log(LogLevel.Warning, "Trying to read without prior activation, offset {0}.", offset); 54 return 0; 55 } 56 WriteDoubleWord(long offset, uint value)57 public void WriteDoubleWord(long offset, uint value) 58 { 59 if((Register)offset == Register.Activate) 60 { 61 TryActivate(value); 62 return; 63 } 64 if(activated) 65 { 66 HandleWrite(offset, value); 67 } 68 else 69 { 70 this.Log(LogLevel.Warning, "Trying to write without prior activation, value {0} @ 0x{1:X}.", value, offset); 71 } 72 } 73 Reset()74 public void Reset() 75 { 76 activated = false; 77 state = State.Usual; 78 } 79 80 public long Size 81 { 82 get 83 { 84 return FileRegisterEnd; 85 } 86 } 87 88 public string this[string key] 89 { 90 get 91 { 92 string value; 93 if(!dictionary.TryGetValue(key, out value)) 94 { 95 throw new KeyNotFoundException(); 96 } 97 return value; 98 } 99 set 100 { 101 dictionary.AddOrUpdate(key, x => value, (x, y) => value); 102 } 103 } 104 TryActivate(uint value)105 private void TryActivate(uint value) 106 { 107 if(value == Magic + Version) 108 { 109 activated = true; 110 this.Log(LogLevel.Info, "Activated."); 111 } 112 else 113 { 114 this.Log(LogLevel.Warning, 115 "Write at activate register, but incorrent MAGIC value 0x{0:X}, should be 0x{1:X}.", 116 value, Magic + Version); 117 } 118 } 119 HandleRead(long offset)120 private uint HandleRead(long offset) 121 { 122 switch((Register)offset) 123 { 124 #if !PLATFORM_WINDOWS 125 case Register.ReceiveFileFromEmulator: 126 return HandleReceiveFile(); 127 #endif 128 case Register.SendFileToEmulator: 129 return HandleSendFile(); 130 case Register.SendReceiveController: 131 return HandleReadPacket(); 132 case Register.GetOrSet: 133 return HandleGet(); 134 case Register.List: 135 return HandleList(); 136 default: 137 if(offset >= StringRegisterStart && offset < StringRegisterEnd) 138 { 139 return HandleArrayRead(offset - StringRegisterStart, stringRegister); 140 } 141 if(offset >= FileRegisterStart && offset < FileRegisterEnd) 142 { 143 return HandleArrayRead(offset - FileRegisterStart, fileRegister); 144 } 145 this.LogUnhandledRead(offset); 146 return 0; 147 } 148 } 149 HandleWrite(long offset, uint value)150 private void HandleWrite(long offset, uint value) 151 { 152 switch((Register)offset) 153 { 154 case Register.Save: 155 HandleSave(value); 156 break; 157 case Register.Load: 158 HandleLoad(value); 159 break; 160 case Register.SendReceiveController: 161 HandleWritePacket(value); 162 break; 163 case Register.Date: 164 HandleDate(); 165 break; 166 case Register.MeasureTime: 167 HandleTimeMeasure((TimeMeasurementOperation)value); 168 break; 169 case Register.GetOrSet: 170 HandleSet(); 171 break; 172 default: 173 if(offset >= StringRegisterStart && offset < StringRegisterEnd) 174 { 175 HandleArrayWrite(offset - StringRegisterStart, value, stringRegister); 176 return; 177 } 178 if(offset >= FileRegisterStart && offset < FileRegisterEnd) 179 { 180 HandleArrayWrite(offset - FileRegisterStart, value, fileRegister); 181 return; 182 } 183 this.LogUnhandledWrite(offset, value); 184 break; 185 } 186 } 187 HandleList()188 private uint HandleList() 189 { 190 if(!keyListPosition.HasValue) 191 { 192 keyListPosition = 0; 193 keys = dictionary.Keys.ToArray(); 194 } 195 if(keyListPosition.Value >= keys.Length) 196 { 197 keyListPosition = null; 198 keys = null; 199 return 0; 200 } 201 SetCurrentStringRegister(keys[keyListPosition.Value]); 202 keyListPosition++; 203 return 1; 204 } 205 HandleGet()206 private uint HandleGet() 207 { 208 var key = GetCurrentStringRegister(); 209 string value; 210 if(!dictionary.TryGetValue(key, out value)) 211 { 212 return 0; 213 } 214 SetCurrentStringRegister(value); 215 return 1; 216 } 217 HandleSet()218 private void HandleSet() 219 { 220 switch(state) 221 { 222 case State.Usual: 223 currentKeyToSet = GetCurrentStringRegister(); 224 state = State.SetValue; 225 break; 226 case State.SetValue: 227 this[currentKeyToSet] = GetCurrentStringRegister(); 228 state = State.Usual; 229 break; 230 default: 231 throw new InvalidOperationException("Improper state while setting a value."); 232 } 233 } 234 HandleDate()235 private void HandleDate() 236 { 237 SetCurrentStringRegister(string.Format("{0:yyyy.MM.dd-HH:mm:ss}", machine.RealTimeClockDateTime)); 238 } 239 HandleTimeMeasure(TimeMeasurementOperation operation)240 private void HandleTimeMeasure(TimeMeasurementOperation operation) 241 { 242 switch(operation) 243 { 244 case TimeMeasurementOperation.Start: 245 stopwatch.Start(); 246 this.Log(LogLevel.Info, "Time measurement started."); 247 break; 248 case TimeMeasurementOperation.Stop: 249 stopwatch.Stop(); 250 this.Log(LogLevel.Info, "Time measurement finished. Elapsed {0}s = {1}", Misc.NormalizeDecimal(stopwatch.Elapsed.TotalSeconds), stopwatch.Elapsed); 251 break; 252 case TimeMeasurementOperation.Reset: 253 if(stopwatch.IsRunning) 254 { 255 stopwatch.Restart(); 256 } 257 else 258 { 259 stopwatch.Reset(); 260 } 261 this.Log(LogLevel.Info, "Time measurement reseted."); 262 break; 263 default: 264 this.Log(LogLevel.Warning, "Invalid value written to time measurement register, ignoring."); 265 break; 266 } 267 } 268 #if !PLATFORM_WINDOWS HandleReceiveFile()269 private uint HandleReceiveFile() 270 { 271 var transferFileName = GetCurrentStringRegister(); 272 try 273 { 274 OpenStreamForReading(transferFileName); 275 state = State.FileReceive; 276 this.Log(LogLevel.Info, "Sending file {0} to emulation started.", transferStream.Name); 277 var info = new UnixFileInfo(transferStream.Name).FileAccessPermissions; 278 return (uint)info; 279 } 280 catch(IOException e) 281 { 282 HandleException(e); 283 return 0; 284 } 285 } 286 #endif 287 HandleSendFile()288 private uint HandleSendFile() 289 { 290 var transferFileName = GetCurrentStringRegister(); 291 try 292 { 293 OpenStreamForWriting(transferFileName); 294 state = State.FileSend; 295 this.Log(LogLevel.Info, "Receiving file {0} from emulation started.", transferStream.Name); 296 return 0; 297 } 298 catch(IOException e) 299 { 300 HandleException(e); 301 return 1; 302 } 303 } 304 HandleReadPacket()305 private uint HandleReadPacket() 306 { 307 try 308 { 309 if(state != State.FileReceive) 310 { 311 this.Log(LogLevel.Error, "HandleReadPacket called in an improper state.", state); 312 return 0; 313 } 314 var retValue = (uint)transferStream.Read(fileRegister, 0, (int)(FileRegisterEnd - FileRegisterStart)); 315 if(retValue == 0) 316 { 317 state = State.Usual; 318 this.Log(LogLevel.Info, 319 "Sending file {0} to emulation ended, {1}B transmitted.", 320 transferStream.Name, Misc.NormalizeBinary(transferStream.Position)); 321 transferStream.Close(); 322 } 323 this.NoisyLog("Prepared packet of data to read by guest of size {0}B.", 324 Misc.NormalizeBinary(retValue)); 325 return retValue; 326 327 } 328 catch(IOException e) 329 { 330 HandleException(e); 331 return 0; 332 } 333 } 334 HandleWritePacket(uint value)335 private void HandleWritePacket(uint value) 336 { 337 try 338 { 339 if(state == State.ReceivePermisions) 340 { 341 #if !PLATFORM_WINDOWS 342 Syscall.chmod(transferStream.Name, (FilePermissions)value); 343 #else 344 this.Log(LogLevel.Warning, "Setting file permissions in not supported in Windows."); 345 #endif 346 state = State.Usual; 347 return; 348 } 349 if(state != State.FileSend) 350 { 351 this.Log(LogLevel.Error, "HandleWritePacket called in improper state.", 352 state); 353 return; 354 } 355 if(value == 0) 356 { 357 state = State.ReceivePermisions; 358 this.Log(LogLevel.Info, 359 "Receiving file {0} from emulation ended, {1}B transmitted.", 360 transferStream.Name, Misc.NormalizeBinary(transferStream.Position)); 361 transferStream.Close(); 362 return; 363 } 364 this.NoisyLog("Received packet of data to write of size {0}B.", Misc.NormalizeBinary(value)); 365 transferStream.Write(fileRegister, 0, (int)value); 366 } 367 catch(IOException e) 368 { 369 HandleException(e); 370 } 371 } 372 GetCurrentStringRegister()373 private string GetCurrentStringRegister() 374 { 375 var count = stringRegister.IndexOf(x => x == 0); 376 if(count == -1) 377 { 378 count = stringRegister.Length; 379 } 380 return Encoding.ASCII.GetString(stringRegister, 0, count); 381 } 382 SetCurrentStringRegister(string value)383 private void SetCurrentStringRegister(string value) 384 { 385 var bytes = Encoding.ASCII.GetBytes(value); 386 if(bytes.Length - 1 > StringRegisterEnd - StringRegisterStart) 387 { 388 throw new ArgumentException(string.Format("String size cannot exceed {0} bytes.", StringRegisterEnd - StringRegisterStart - 1)); 389 } 390 bytes.CopyTo(stringRegister, 0); 391 stringRegister[bytes.Length] = 0; 392 } 393 HandleException(IOException e)394 private void HandleException(IOException e) 395 { 396 this.Log(LogLevel.Error, "IOException: {0}.", e.Message); 397 } 398 OpenStreamForReading(string transferFileName)399 private void OpenStreamForReading(string transferFileName) 400 { 401 transferStream = new FileStream(transferFileName, FileMode.Open, FileAccess.Read, FileShare.Read); 402 } 403 OpenStreamForWriting(string transferFileName)404 private void OpenStreamForWriting(string transferFileName) 405 { 406 transferStream = new FileStream(transferFileName, FileMode.Create, FileAccess.ReadWrite); 407 } 408 HandleArrayWrite(long offset, uint value, byte[] array)409 private static void HandleArrayWrite(long offset, uint value, byte[] array) 410 { 411 var index = (int)(offset); 412 var bytes = BitConverter.GetBytes(value); 413 for(var i = 0; i < 4; i++) 414 { 415 array[index + i] = bytes[i]; 416 } 417 } 418 HandleArrayRead(long offset, byte[] array)419 private static uint HandleArrayRead(long offset, byte[] array) 420 { 421 var index = (int)(offset); 422 var bytes = new byte[4]; 423 for(var i = 0; i < 4; i++) 424 { 425 bytes[i] = array[index + i]; 426 } 427 return BitConverter.ToUInt32(bytes, 0); 428 } 429 HandleLoad(uint value)430 private static void HandleLoad(uint value) 431 { 432 ThreadPool.QueueUserWorkItem(delegate { EmulationManager.Instance.Load(string.Format(SavepointName, value)); }); 433 } 434 HandleSave(uint value)435 private static void HandleSave(uint value) 436 { 437 ThreadPool.QueueUserWorkItem(delegate { EmulationManager.Instance.Save(string.Format(SavepointName, value)); }); 438 } 439 440 [PostDeserialization] AfterDeserialization()441 private void AfterDeserialization() 442 { 443 if(state == State.FileSend || state == State.FileReceive) 444 { 445 // we don't know whether the file is still available etc. 446 // therefore we go back to the usual state 447 state = State.Usual; 448 } 449 } 450 451 private bool activated; 452 private State state; 453 454 [Transient] 455 private FileStream transferStream; 456 private string[] keys; 457 private string currentKeyToSet; 458 private int? keyListPosition; 459 460 private readonly IMachine machine; 461 private readonly Stopwatch stopwatch; 462 private readonly byte[] stringRegister; 463 private readonly byte[] fileRegister; 464 private readonly ConcurrentDictionary<string, string> dictionary; 465 466 private const uint Magic = 0xDEADBEEF; 467 private const uint Version = 3; 468 private const string SavepointName = "ckpt{0}.dat"; 469 470 private enum State 471 { 472 Usual, 473 FileReceive, 474 FileSend, 475 ReceivePermisions, 476 SetValue 477 } 478 479 private enum Register : uint 480 { 481 Activate = 0x00, 482 Save = 0x04, 483 Load = 0x08, 484 ReceiveFileFromEmulator = 0x14, 485 SendFileToEmulator = 0x18, 486 SendReceiveController = 0x1C, 487 GetOrSet = 0x20, 488 List = 0x24, 489 Date = 0x28, 490 MeasureTime = 0x2C 491 } 492 493 private enum TimeMeasurementOperation : uint 494 { 495 Start = 0, 496 Stop = 1, 497 Reset = 2 498 } 499 500 private const uint StringRegisterStart = 0x100; 501 private const uint StringRegisterSize = 0x100; 502 private const uint StringRegisterEnd = StringRegisterStart + StringRegisterSize; 503 private const uint FileRegisterStart = StringRegisterEnd; 504 private const uint FileRegisterSize = 0x10000; 505 private const uint FileRegisterEnd = FileRegisterStart + FileRegisterSize; 506 } 507 } 508 509