root/src/kits/debugger/value/ValueWriter.cpp
/*
 * Copyright 2009-2012, Ingo Weinhold, ingo_weinhold@gmx.de.
 * Copyright 2013-2015, Rene Gollent, rene@gollent.com.
 * Distributed under the terms of the MIT License.
 */


#include "ValueWriter.h"

#include "Architecture.h"
#include "BitBuffer.h"
#include "CpuState.h"
#include "DebuggerInterface.h"
#include "Register.h"
#include "TeamMemory.h"
#include "Tracing.h"
#include "ValueLocation.h"


ValueWriter::ValueWriter(Architecture* architecture,
        DebuggerInterface* interface, CpuState* cpuState, thread_id targetThread)
        :
        fArchitecture(architecture),
        fDebuggerInterface(interface),
        fCpuState(cpuState),
        fTargetThread(targetThread)
{
        fArchitecture->AcquireReference();
        fDebuggerInterface->AcquireReference();
        if (fCpuState != NULL)
                fCpuState->AcquireReference();
}


ValueWriter::~ValueWriter()
{
        fArchitecture->ReleaseReference();
        fDebuggerInterface->ReleaseReference();
        if (fCpuState != NULL)
                fCpuState->ReleaseReference();
}


status_t
ValueWriter::WriteValue(ValueLocation* location, BVariant& value)
{
        if (!location->IsWritable())
                return B_BAD_VALUE;

        int32 count = location->CountPieces();
        if (fCpuState == NULL) {
                for (int32 i = 0; i < count; i++) {
                        const ValuePieceLocation piece = location->PieceAt(i);
                        if (piece.type == VALUE_PIECE_LOCATION_REGISTER) {
                                TRACE_LOCALS("  -> asked to write value with register piece, "
                                        "but no CPU state to write to.\n");
                                return B_UNSUPPORTED;
                        }
                }
        }

        bool cpuStateWriteNeeded = false;
        size_t byteOffset = 0;
        bool bigEndian = fArchitecture->IsBigEndian();
        const Register* registers = fArchitecture->Registers();
        for (int32 i = 0; i < count; i++) {
                ValuePieceLocation piece = location->PieceAt(
                        bigEndian ? i : count - i - 1);
                uint32 bytesToWrite = piece.size;

                uint8* targetData = (uint8*)value.Bytes() + byteOffset;

                switch (piece.type) {
                        case VALUE_PIECE_LOCATION_MEMORY:
                        {
                                target_addr_t address = piece.address;

                                TRACE_LOCALS("  piece %" B_PRId32 ": memory address: %#"
                                        B_PRIx64 ", bits: %" B_PRIu32 "\n", i, address,
                                        bytesToWrite * 8);

                                ssize_t bytesWritten = fDebuggerInterface->WriteMemory(address,
                                        targetData, bytesToWrite);

                                if (bytesWritten < 0)
                                        return bytesWritten;
                                if ((uint32)bytesWritten != bytesToWrite)
                                        return B_BAD_ADDRESS;

                                break;
                        }
                        case VALUE_PIECE_LOCATION_REGISTER:
                        {
                                TRACE_LOCALS("  piece %" B_PRId32 ": register: %" B_PRIu32
                                        ", bits: %" B_PRIu64 "\n", i, piece.reg, piece.bitSize);

                                const Register* target = registers + piece.reg;
                                BVariant pieceValue;
                                switch (bytesToWrite) {
                                        case 1:
                                                pieceValue.SetTo(*(uint8*)targetData);
                                                break;
                                        case 2:
                                                pieceValue.SetTo(*(uint16*)targetData);
                                                break;
                                        case 4:
                                                pieceValue.SetTo(*(uint32*)targetData);
                                                break;
                                        case 8:
                                                pieceValue.SetTo(*(uint64*)targetData);
                                                break;
                                        default:
                                                TRACE_LOCALS("Asked to write unsupported piece size %"
                                                        B_PRId32 " to register\n", bytesToWrite);
                                                return B_UNSUPPORTED;
                                }

                                if (!fCpuState->SetRegisterValue(target, pieceValue))
                                        return B_NO_MEMORY;

                                cpuStateWriteNeeded = true;
                                break;
                        }
                        default:
                                return B_UNSUPPORTED;
                }

                byteOffset += bytesToWrite;
        }

        if (cpuStateWriteNeeded)
                return fDebuggerInterface->SetCpuState(fTargetThread, fCpuState);

        return B_OK;
}