1/*
2 * Copyright 2009-2012, Ingo Weinhold, ingo_weinhold@gmx.de.
3 * Copyright 2013-2015, Rene Gollent, rene@gollent.com.
4 * Distributed under the terms of the MIT License.
5 */
6
7
8#include "ValueLoader.h"
9
10#include "Architecture.h"
11#include "BitBuffer.h"
12#include "CpuState.h"
13#include "Register.h"
14#include "TeamMemory.h"
15#include "Tracing.h"
16#include "ValueLocation.h"
17
18
19ValueLoader::ValueLoader(Architecture* architecture, TeamMemory* teamMemory,
20	CpuState* cpuState)
21	:
22	fArchitecture(architecture),
23	fTeamMemory(teamMemory),
24	fCpuState(cpuState)
25{
26	fArchitecture->AcquireReference();
27	fTeamMemory->AcquireReference();
28	if (fCpuState != NULL)
29		fCpuState->AcquireReference();
30}
31
32
33ValueLoader::~ValueLoader()
34{
35	fArchitecture->ReleaseReference();
36	fTeamMemory->ReleaseReference();
37	if (fCpuState != NULL)
38		fCpuState->ReleaseReference();
39}
40
41
42status_t
43ValueLoader::LoadValue(ValueLocation* location, type_code valueType,
44	bool shortValueIsFine, BVariant& _value)
45{
46	static const size_t kMaxPieceSize = 16;
47	uint64 totalBitSize = 0;
48	int32 count = location->CountPieces();
49	for (int32 i = 0; i < count; i++) {
50		ValuePieceLocation piece = location->PieceAt(i);
51		switch (piece.type) {
52			case VALUE_PIECE_LOCATION_INVALID:
53			case VALUE_PIECE_LOCATION_UNKNOWN:
54				return B_ENTRY_NOT_FOUND;
55			case VALUE_PIECE_LOCATION_MEMORY:
56			case VALUE_PIECE_LOCATION_REGISTER:
57			case VALUE_PIECE_LOCATION_IMPLICIT:
58				break;
59		}
60
61		if (piece.size > kMaxPieceSize) {
62			TRACE_LOCALS("  -> overly long piece size (%" B_PRIu64 " bytes)\n",
63				piece.size);
64			return B_UNSUPPORTED;
65		}
66
67		totalBitSize += piece.bitSize;
68	}
69
70	TRACE_LOCALS("  -> totalBitSize: %" B_PRIu64 "\n", totalBitSize);
71
72	if (totalBitSize == 0) {
73		TRACE_LOCALS("  -> no size\n");
74		return B_ENTRY_NOT_FOUND;
75	}
76
77	if (totalBitSize > 64) {
78		TRACE_LOCALS("  -> longer than 64 bits: unsupported\n");
79		return B_UNSUPPORTED;
80	}
81
82	uint64 valueBitSize = BVariant::SizeOfType(valueType) * 8;
83	if (!shortValueIsFine && totalBitSize < valueBitSize) {
84		TRACE_LOCALS("  -> too short for value type (%" B_PRIu64 " vs. %"
85			B_PRIu64 " bits)\n", totalBitSize, valueBitSize);
86		return B_BAD_VALUE;
87	}
88
89	// Load the data. Since the BitBuffer class we're using only supports big
90	// endian bit semantics, we convert all data to big endian before pushing
91	// them to the buffer. For later conversion to BVariant we need to make sure
92	// the final buffer has the size of the value type, so we pad the most
93	// significant bits with zeros.
94	BitBuffer valueBuffer;
95	if (totalBitSize < valueBitSize)
96		valueBuffer.AddZeroBits(valueBitSize - totalBitSize);
97
98	bool bigEndian = fArchitecture->IsBigEndian();
99	const Register* registers = fArchitecture->Registers();
100	for (int32 i = 0; i < count; i++) {
101		ValuePieceLocation piece = location->PieceAt(
102			bigEndian ? i : count - i - 1);
103		uint32 bytesToRead = piece.size;
104		uint32 bitSize = piece.bitSize;
105		uint8 bitOffset = piece.bitOffset;
106			// TODO: the offset's ordinal position and direction aren't
107			// specified by DWARF, and simply follow the target language.
108			// To handle non C/C++ languages properly, the corresponding
109			// SourceLanguage will need to be passed in and extended to
110			// return the relevant information.
111
112		switch (piece.type) {
113			case VALUE_PIECE_LOCATION_INVALID:
114			case VALUE_PIECE_LOCATION_UNKNOWN:
115				return B_ENTRY_NOT_FOUND;
116			case VALUE_PIECE_LOCATION_MEMORY:
117			case VALUE_PIECE_LOCATION_IMPLICIT:
118			{
119				target_addr_t address = piece.address;
120
121				if (piece.type == VALUE_PIECE_LOCATION_MEMORY) {
122					TRACE_LOCALS("  piece %" B_PRId32 ": memory address: %#"
123						B_PRIx64 ", bits: %" B_PRIu32 "\n", i, address,
124						bitSize);
125				} else {
126					TRACE_LOCALS("  piece %" B_PRId32 ": implicit value, "
127						"bits: %" B_PRIu32 "\n", i, bitSize);
128				}
129
130				uint8 pieceBuffer[kMaxPieceSize];
131				ssize_t bytesRead;
132				if (piece.type == VALUE_PIECE_LOCATION_MEMORY) {
133					bytesRead = fTeamMemory->ReadMemory(address,
134						pieceBuffer, bytesToRead);
135				} else {
136					memcpy(pieceBuffer, piece.value, piece.size);
137					bytesRead = piece.size;
138				}
139
140				if (bytesRead < 0)
141					return bytesRead;
142				if ((uint32)bytesRead != bytesToRead)
143					return B_BAD_ADDRESS;
144
145				TRACE_LOCALS_ONLY(
146					TRACE_LOCALS("  -> read: ");
147					for (ssize_t k = 0; k < bytesRead; k++)
148						TRACE_LOCALS("%02x", pieceBuffer[k]);
149					TRACE_LOCALS("\n");
150				)
151
152				// convert to big endian
153				if (!bigEndian) {
154					for (int32 k = bytesRead / 2 - 1; k >= 0; k--) {
155						std::swap(pieceBuffer[k],
156							pieceBuffer[bytesRead - k - 1]);
157					}
158				}
159
160				valueBuffer.AddBits(pieceBuffer, bitSize, bitOffset);
161				break;
162			}
163			case VALUE_PIECE_LOCATION_REGISTER:
164			{
165				TRACE_LOCALS("  piece %" B_PRId32 ": register: %" B_PRIu32
166					", bits: %" B_PRIu32 "\n", i, piece.reg, bitSize);
167
168				if (fCpuState == NULL) {
169					WARNING("ValueLoader::LoadValue(): register piece, but no "
170						"CpuState\n");
171					return B_UNSUPPORTED;
172				}
173
174				BVariant registerValue;
175				if (!fCpuState->GetRegisterValue(registers + piece.reg,
176						registerValue)) {
177					return B_ENTRY_NOT_FOUND;
178				}
179				if (registerValue.Size() < bytesToRead)
180					return B_ENTRY_NOT_FOUND;
181
182				if (!bigEndian) {
183					registerValue.SwapEndianess();
184					bitOffset = registerValue.Size() * 8 - bitOffset - bitSize;
185				}
186				valueBuffer.AddBits(registerValue.Bytes(), bitSize, bitOffset);
187				break;
188			}
189		}
190	}
191
192	// If we don't have enough bits in the buffer apparently adding some failed.
193	if (valueBuffer.BitSize() < valueBitSize)
194		return B_NO_MEMORY;
195
196	// convert the bits into something we can work with
197	BVariant value;
198	status_t error = value.SetToTypedData(valueBuffer.Bytes(), valueType);
199	if (error != B_OK) {
200		TRACE_LOCALS("  -> failed to set typed data: %s\n", strerror(error));
201		return error;
202	}
203
204	// convert to host endianess
205	#if B_HOST_IS_LENDIAN
206		value.SwapEndianess();
207	#endif
208
209	_value = value;
210	return B_OK;
211}
212
213
214status_t
215ValueLoader::LoadRawValue(BVariant& location, size_t bytesToRead, void* _value)
216{
217	ssize_t bytesRead = fTeamMemory->ReadMemory(location.ToUInt64(),
218		_value, bytesToRead);
219	if (bytesRead < 0)
220		return bytesRead;
221	if ((uint32)bytesRead != bytesToRead)
222		return B_BAD_ADDRESS;
223	return B_OK;
224}
225
226
227status_t
228ValueLoader::LoadStringValue(BVariant& location, size_t maxSize, BString& _value)
229{
230	static const size_t kMaxStringSize = 255;
231
232	return fTeamMemory->ReadMemoryString(location.ToUInt64(),
233		std::min(maxSize, kMaxStringSize), _value);
234}
235