/* -*-C++-*-	$NetBSD: memory.h,v 1.8 2008/04/28 20:23:20 martin Exp $	*/

/*-
 * Copyright (c) 2001 The NetBSD Foundation, Inc.
 * All rights reserved.
 *
 * This code is derived from software contributed to The NetBSD Foundation
 * by UCHIYAMA Yasushi.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

#ifndef _HPCBOOT_MEMORY_H_
#define	_HPCBOOT_MEMORY_H_

#undef MEMORY_MAP_DEBUG		// super verbose.

#include <hpcboot.h>
class Console;

template <class T>
inline T
ROUND(const T v, const T x) {
	return((v + x - 1) / x) * x;
}

template <class T>
inline T
TRUNC(const T v, const T x) {
	return(v / x) * x;
}

#define	MAX_MEM_BANK	16
class MemoryManager {
private:
	struct bank {
		paddr_t addr;
		psize_t size;
	};
	// Windows CE application virtual memory size
	enum { WCE_VMEM_MAX = 32 * 1024 * 1024 };
	// Windows CE VirtualAlloc() boundary
	enum { WCE_REGION_SIZE = 64 * 1024 };

protected:
	// Console options.
	Console *&_cons;
	BOOL _debug;

	enum { BLOCK_SIZE = WCE_REGION_SIZE * 64 }; // 4MByte

	// Pagesize, D-RAM bank
	vsize_t _page_size;
	int _page_shift;
	int _page_per_region;
	struct bank _bank[MAX_MEM_BANK];
	int _nbank;

	// Allocated memory
	vaddr_t _memory;

	// Virtual <-> Physical table
	int _naddr_table;
	struct AddressTranslationTable {
		vaddr_t vaddr;
		paddr_t paddr;
	}
	*_addr_table;
	int _addr_table_idx;

public:
	vsize_t getPageSize(void) { return _page_size; }

	vsize_t getTaggedPageSize(void)
		{ return _page_size - sizeof(struct PageTag); }
	vsize_t estimateTaggedPageSize(vsize_t sz) {
		vsize_t tsz = getTaggedPageSize();
		return ((sz + tsz - 1) / tsz) * _page_size;
	}
	uint32_t roundPage(uint32_t v) { return ROUND(v, _page_size); }
	uint32_t truncPage(uint32_t v) { return TRUNC(v, _page_size); }
	uint32_t roundRegion(uint32_t v)
		{ return ROUND(v, uint32_t(WCE_REGION_SIZE)); }
	uint32_t truncRegion(uint32_t v)
		{ return TRUNC(v, uint32_t(WCE_REGION_SIZE)); }

	// Physical address ops.
	vaddr_t mapPhysicalPage(paddr_t paddr, psize_t size, uint32_t flags);
	void unmapPhysicalPage(vaddr_t vaddr);
	uint32_t readPhysical4(paddr_t paddr);
	// return physical address of coresspoing virtual address.
	virtual paddr_t searchPage(vaddr_t vaddr) = 0;

	MemoryManager(Console *&cons, size_t pagesize);
	virtual ~MemoryManager(void);
	BOOL &setDebug(void) { return _debug; }
	virtual BOOL init(void) { return TRUE; }

	// initialize page pool
	BOOL reservePage(vsize_t size, BOOL page_commit = FALSE);
	// register D-RAM banks
	void loadBank(paddr_t paddr, psize_t psize);
	// get 1 page from high address of pool.
	BOOL getPage(vaddr_t &vaddr, paddr_t &paddr);
	// get tagged page from low address of pool.
	BOOL getTaggedPage(vaddr_t &vaddr, paddr_t &paddr);
	BOOL getTaggedPage(vaddr_t &v, paddr_t &p, struct PageTag **pvec,
	    paddr_t &pvec_paddr);
};

//
//	Use LockPages()
//
class MemoryManager_LockPages : public MemoryManager {
private:
	BOOL(*_lock_pages)(LPVOID, DWORD, PDWORD, int);
	BOOL(*_unlock_pages)(LPVOID, DWORD);
	int _shift;

public:
	MemoryManager_LockPages(BOOL(*)(LPVOID, DWORD, PDWORD, int),
	    BOOL(*)(LPVOID, DWORD), Console *&, size_t, int = 0);
	virtual ~MemoryManager_LockPages(void);
	paddr_t searchPage(vaddr_t vaddr);
};

//
//	Use VirtualCopy()
//
class MemoryManager_VirtualCopy : public MemoryManager {
private:
	// search guess
	paddr_t _search_guess;

	// Memory marker
	uint32_t _magic0, _magic1;
	volatile uint32_t *_magic_addr;
	enum {
		MAGIC_CHECK_DONE	= 0xac1dcafe,
		MAGIC_CHECK_DUMMY	= 0xa5a5a5a5
	};
	void setMagic(vaddr_t v) {
		_magic_addr =(uint32_t *)v;
		while ((_magic0 = Random()) == MAGIC_CHECK_DONE)
			;
		while ((_magic1 = Random()) == MAGIC_CHECK_DONE)
			;
		_magic_addr[0] = _magic0;
		_magic_addr[1] = _magic1;
	}
	BOOL checkMagic(vaddr_t v) {
		volatile uint32_t *marker =(uint32_t *)v;
		return (marker[0] == _magic0) && (marker[1] == _magic1);
	}
	void clearMagic(void) {
		_magic_addr[0] = MAGIC_CHECK_DONE;
		_magic_addr[1] = MAGIC_CHECK_DONE;
	}
	vaddr_t checkMagicRegion(vaddr_t start, vsize_t size,
	    vsize_t step = 8) {
		for (vaddr_t ref = start; ref < start + size; ref += step)
			if (checkMagic(ref))
				return ref - start;
		return ~0;
	}
	paddr_t searchBank(int banknum);

public:
	MemoryManager_VirtualCopy(Console *&, size_t);
	virtual ~MemoryManager_VirtualCopy(void);
	paddr_t searchPage(vaddr_t vaddr);
};

#endif // _HPCBOOT_MEMORY_H_
