/*
 * Copyright (c) 2007, 2008 University of Tsukuba
 * All rights reserved.
 *
 * 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.
 * 3. Neither the name of the University of Tsukuba nor the names of its
 *    contributors may be used to endorse or promote products derived from
 *    this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT OWNER 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.
 */
/*
 * Copyright (c) 2010-2012 Yuichi Watanabe
 */

#ifndef _CORE_MM_H
#define _CORE_MM_H

#include <common/list.h>
#include <core/mm.h>
#include "constants.h"
#include <core/types.h>

/*
 * Virtual address space map on x86_64
 *
 * 0x100_0000_0000			+-------------------+
 *					| hphys		    | num_of_hphys_
 *					|		    | pages * 4096
 * 0x080_0000_0000 (HPHYS_ADDR)		+-------------------+
 *					|		    |
 * 0x000_A000_0000 (MAPMEM_ADDR_END)	+-------------------+
 *					| mapmem	    |
 * 0x000_8000_0000 (MAPMEM_ADDR_START)	+-------------------+
 *					|		    |
 * 0x000_4800_0000			+-------------------+
 *					|		    | VMMSIZE_ALL
 *		   (end)		| +---------------+ |
 *					| | text/data/bss | |
 * 0x000_4100_0000 (head)		| +---------------+ |
 *					| vmm		    |
 * 0x000_4000_0000 (VMM_START_VIRT)	+-------------------+
 *
 *
 * Virtual address space map on x86_32
 *
 * 0xA000_0000 (MAPMEM_ADDR_END)	--------------------
 *					| mapmem	    |
 * 0x8000_0000 (MAPMEM_ADDR_START)	+-------------------+
 *					|		    |
 * 0x4800_0000				+-------------------+
 *					|		    | VMMSIZE_ALL
 *	       (end)			| +---------------+ |
 *					| | text/data/bss | |
 * 0x4100_0000 (head)			| +---------------+ |
 *					| vmm		    |
 * 0x4000_0000 (VMM_START_VIRT)		+-------------------+

 */

#ifdef __x86_64__
#	define PDPE_ATTR		(PDE_P_BIT | PDE_RW_BIT | PDE_US_BIT)
#	define HPHYS_ADDR		(1ULL << (12 + 9 + 9 + 9))
#else
#	define PDPE_ATTR		PDE_P_BIT
#	define HPHYS_ADDR		0
#endif

#	define PMAP_LEVELS		(sizeof (ulong) == 4 ? 3 : 4)
#	define PDE_PS_OFFSET_MASK	PDE_2M_OFFSET_MASK
#	define PDE_PS_ADDR_MASK		PDE_2M_ADDR_MASK

#define VMM_START_VIRT			0x40000000
#define VMMSIZE_ALL		(128 * 1024 * 1024)
/* #define VMMSIZE_ALL		(256 * 1024 * 1024) */
#define NUM_OF_PAGES		(VMMSIZE_ALL >> PAGESIZE_SHIFT)
#define MAPMEM_ADDR_START	0x80000000
#define MAPMEM_ADDR_END		0xA0000000

enum page_type {
	PAGE_TYPE_FREE,
	PAGE_TYPE_NOT_HEAD,
	PAGE_TYPE_ALLOCATED,
	PAGE_TYPE_RESERVED,
};

struct page {
	LIST1_DEFINE (struct page);
	enum page_type type;
	int allocsize;
	phys_t phys;
	virt_t virt;
};

enum pmap_type {
	PMAP_TYPE_VMM,
	PMAP_TYPE_GUEST,
	PMAP_TYPE_GUEST_ATOMIC,
};

typedef struct {
	u64 entry[5];
	phys_t entryaddr[4];
	virt_t curaddr;
	int curlevel;
	int readlevel;
	int levels;
	enum pmap_type type;
} pmap_t;

phys_t sym_to_phys (void *sym);
bool phys_in_vmm (phys_t phys);
int num_of_available_pages (void);
phys_t mm_top_of_avail_mem();
phys32_t mm_top_of_low_avail_mem();

/* process */
int mm_process_alloc (phys_t *phys);
void mm_process_free (phys_t phys);
int mm_process_map_alloc (virt_t virt, uint len);
int mm_process_unmap (virt_t virt, uint len);
void mm_process_unmapall (void);
virt_t mm_process_map_stack (uint len);
int mm_process_unmapstack (virt_t virt, uint len);
int mm_process_map_shared_physpage (virt_t virt, phys_t phys, bool rw);
void *mm_process_map_shared (phys_t procphys, void *buf, uint len, bool rw);
phys_t mm_process_switch (phys_t switchto);

/* accessing page tables */
void pmap_open_vmm (pmap_t *m, ulong cr3, int levels);
void pmap_open_guest (pmap_t *m, ulong cr3, int levels, bool atomic);
void pmap_close (pmap_t *m);

int pmap_getreadlevel (pmap_t *m);
void pmap_setlevel (pmap_t *m, int level);
void pmap_setvirt (pmap_t *m, virt_t virtaddr, int level);
u64 pmap_read (pmap_t *m);
bool pmap_write (pmap_t *m, u64 e, uint attrmask);
void pmap_clear (pmap_t *m);
vmmerr_t pmap_autoalloc (pmap_t *m);
void *pmap_pointer (pmap_t *m);
void pmap_dump (pmap_t *m);

struct page *mm_page_alloc (int n);
void mm_page_free (struct page *p);

struct page *virt_to_page (virt_t virt);
struct page *phys_to_page (phys_t phys);
virt_t page_to_virt (struct page *p);
phys_t page_to_phys (struct page *p);

void mm_alloc_init (void);
u64 mm_cache_flag_to_pte_attr (int flags);

void mm_dump_mem_address_space(void);

#endif
