// SCE CONFIDENTIAL
// PLAYSTATION(R)3 Programmer Tool Runtime Library 250.001
// Copyright (C) 2007 Sony Computer Entertainment Inc.
// All Rights Reserved.

#include <cellUtilSpursTaskElf.h>

__CELL_UTIL_SPURS_BEGIN

struct Ehdr {
	uint64_t e_ident0;					// unsigned char e_ident[0-7]
	uint64_t e_ident1;					// unsigned char e_ident[8-15]

	uint16_t e_type;
	enum { // e_type
		ET_NONE		= 0,				// No file type
		ET_REL		= 1,				// Relocatable file
		ET_EXEC		= 2,				// Executable file
		ET_DYN		= 3,				// Shared object file
		ET_CORE		= 4					// Core file
	};
	uint16_t e_machine;
	enum { // e_machine
		EM_PPC		= 20,				// PowerPC 32-bit
		EM_PPC64	= 21,				// PowerPC 64-bit
		EM_SPU		= 23				// SPU
	};
	uint32_t e_version;
	uint32_t e_entry;
	uint32_t e_phoff;
	uint32_t e_shoff;
	uint32_t e_flags;
	uint16_t e_ehsize;
	uint16_t e_phentsize;
	uint16_t e_phnum;
	uint16_t e_shentsize;
	uint16_t e_shnum;
	uint16_t e_shstrndx;

	bool isSpu() const
		{
			static const uint64_t e_ident_spu = 0x7f454c4601020100LL;
			return e_ident0 == e_ident_spu && e_machine == EM_SPU;
		}
};

struct Phdr {
	uint32_t p_type;
	enum { // p_type
		PT_NULL		= 0,
		PT_LOAD		= 1,
		PT_DYNAMIC	= 2,
		PT_INTERP	= 3,
		PT_NOTE		= 4,
		PT_SHLIB	= 5,
		PT_PHDR		= 6
	};
	uint32_t p_offset;
	uint32_t p_vaddr;
	uint32_t p_paddr;
	uint32_t p_filesz;
	uint32_t p_memsz;
	uint32_t p_flags;
	enum { // p_flags
		PF_X		= (1<<0),
		PF_W		= (1<<1),
		PF_R		= (1<<2)
	};
	uint32_t p_align;

	bool isLoad() const
		{
			return p_type == PT_LOAD;
		}
	bool isReadOnly() const
		{
			return isLoad() && !(p_flags & PF_W);
		}
	bool isWritable() const
		{
			return isLoad() && (p_flags & PF_W);
		}
};

unsigned TaskElf::saveBufferSize() const
{
	uint64_t upper = m_upper;
	uint64_t lower = m_lower;
	upper = (upper & 0x5555555555555555ull) + (upper >> 1 & 0x5555555555555555ull);
	lower = (lower & 0x5555555555555555ull) + (lower >> 1 & 0x5555555555555555ull);
	upper = (upper & 0x3333333333333333ull) + (upper >> 2 & 0x3333333333333333ull);
	lower = (lower & 0x3333333333333333ull) + (lower >> 2 & 0x3333333333333333ull);
	upper = (upper + (upper >>  4)) & 0x0f0f0f0f0f0f0f0full;
	lower = (lower + (lower >>  4)) & 0x0f0f0f0f0f0f0f0full;
	upper = (upper + (upper >>  8)) & 0x00ff00ff00ff00ffull;
	lower = (lower + (lower >>  8)) & 0x00ff00ff00ff00ffull;
	upper = (upper + (upper >> 16)) & 0x0000ffff0000ffffull;
	lower = (lower + (lower >> 16)) & 0x0000ffff0000ffffull;
	upper += upper >> 32;
	lower += lower >> 32;

	return (((unsigned)upper + (unsigned)lower) << 11)
		+ CELL_SPURS_TASK_EXECUTION_CONTEXT_SIZE;
}

void TaskElf::setRange(unsigned start, unsigned size)
{
	if (size == 0) {
		return;
	}

	unsigned mbegin = CELL_SPURS_CONTEXT_SIZE2BITS(start);
	unsigned mend   = CELL_SPURS_CONTEXT_SIZE2BITS(start + size + 2047);
	mend = mend < 128 ? mend : 128;

	static const uint64_t mask_base = 0xffffffffffffffffull;
	uint64_t upper_begin = mask_base >> mbegin;
	uint64_t upper_end   = mask_base << (mend   < 64 ? 64 - mend : 0);
	uint64_t lower_begin = mask_base >> (mbegin < 64 ? 0 : mbegin - 64);
	uint64_t lower_end   = mask_base << 128 - mend;
	m_upper |= upper_begin & upper_end;
	m_lower |= lower_begin & lower_end;
}

void TaskElf::unsetRange(unsigned start, unsigned size)
{
	if (size == 0) {
		return;
	}

	unsigned mbegin = CELL_SPURS_CONTEXT_SIZE2BITS(start + 2047);
	unsigned mend   = CELL_SPURS_CONTEXT_SIZE2BITS(start + size);
	mend = mend < 128 ? mend : 128;

	static const uint64_t mask_base = 0xffffffffffffffffull;
	uint64_t upper_begin = mask_base << (mbegin < 64 ? 64 - mbegin : 0);
	uint64_t upper_end   = mask_base >> mend;
	uint64_t lower_begin = mask_base << 128 - mbegin;
	uint64_t lower_end   = mask_base >> (mend < 64 ? 0 : mend - 64);
	m_upper &= upper_begin | upper_end;
	m_lower &= lower_begin | lower_end;
}

void TaskElf::setWritableSegment()
{
	const Ehdr* ehdr = static_cast<const Ehdr*>(m_elf);
	if (!ehdr->isSpu()) {
		return;
	}

	uintptr_t _phdr = (uintptr_t)ehdr + ehdr->e_phoff;
	for (unsigned i = 0; i < ehdr->e_phnum; ++i, _phdr += ehdr->e_phentsize) {
		const Phdr* phdr = (const Phdr*)_phdr;
		if (phdr->isWritable()) {
			setRange(phdr->p_vaddr, phdr->p_memsz);
		}
	}
}

void TaskElf::unsetReadOnlySegment()
{
	const Ehdr* ehdr = static_cast< const Ehdr* >(m_elf);
	if (!ehdr->isSpu()) {
		return;
	}

	uintptr_t _phdr = (uintptr_t)ehdr + ehdr->e_phoff;
	for (unsigned i = 0; i < ehdr->e_phnum; ++i, _phdr += ehdr->e_phentsize) {
		const Phdr* phdr = (const Phdr*)_phdr;
		if (phdr->isReadOnly()) {
			unsetRange(phdr->p_vaddr, phdr->p_memsz);
		}
	}
}

__CELL_UTIL_SPURS_END

// Local Variables:
// mode: C++
// c-file-style: "stroustrup"
// tab-width: 4
// End:
// vim:sw=4:sts=4:ts=4
