1 //
2 // Copyright (c) 2010-2024 Antmicro
3 //
4 // This file is licensed under the MIT License.
5 // Full license text is available in 'licenses/MIT.txt'.
6 //
7 using System.Linq;
8 using System;
9 using Antmicro.Renode.Peripherals.CPU;
10 using Antmicro.Renode.Logging;
11 using ELFSharp.ELF;
12 
13 namespace Antmicro.Renode.Utilities.GDB.Commands
14 {
15     internal class WriteRegisterCommand : Command
16     {
WriteRegisterCommand(CommandsManager manager)17         public WriteRegisterCommand(CommandsManager manager) : base(manager)
18         {
19         }
20 
21         [Execute("P")]
Execute( [Argument(Encoding = ArgumentAttribute.ArgumentEncoding.HexNumber, Separator = B)]int registerNumber, [Argument(Encoding = ArgumentAttribute.ArgumentEncoding.HexBytesString)]byte[] value)22         public PacketData Execute(
23             [Argument(Encoding = ArgumentAttribute.ArgumentEncoding.HexNumber, Separator = '=')]int registerNumber,
24             [Argument(Encoding = ArgumentAttribute.ArgumentEncoding.HexBytesString)]byte[] value)
25         {
26             var isLittleEndian = manager.Cpu.Endianness == Endianess.LittleEndian;
27             var reg = manager.Cpu.GetRegisters().SingleOrDefault(x => x.Index == registerNumber);
28             if(reg.Width == 0)
29             {
30                 manager.Cpu.Log(LogLevel.Warning, "Writing to register #{0} failed, register doesn't exit.", registerNumber);
31                 return PacketData.ErrorReply();
32             }
33 
34             // register may have been reported with bigger Width, try to truncate the value
35             var width = reg.Width / 8;
36             if(value.Length > width)
37             {
38                 // split value into excess and proper value
39                 var excess = value.Skip(isLittleEndian ? width : 0).Take(value.Length - width);
40                 value = value.Skip(isLittleEndian ? 0 : value.Length - width).Take(width).ToArray();
41 
42                 // Allow excess to be filled with zeros and (for two's complement support) 0xff when msb is set.
43                 // All bytes in excess need to be the same and checked,
44                 // use sample byte to test value and check if all bytes equal to the sample.
45                 var sampleByte = excess.First();
46                 var msb = value[isLittleEndian ? value.Length - 1 : 0] >> 7;
47                 if(!(sampleByte == 0 || (sampleByte == 0xff && msb == 1)) || excess.Any(b => b != sampleByte))
48                 {
49                     manager.Cpu.Log(LogLevel.Warning, "Writing to register #{0} failed, sent value doesn't fit in {1} bits.", registerNumber, reg.Width);
50                     return PacketData.ErrorReply();
51                 }
52             }
53 
54             manager.Cpu.SetRegister(registerNumber, reg.ValueFromBytes(value, manager.Cpu.Endianness));
55             return PacketData.Success;
56         }
57     }
58 }
59