/* -*-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_