/*
 *  linux/include/linux/mm.h
 *
 *  Copyright (C) 1991, 1992  Linus Torvalds
 *
 * This file is subject to the terms and conditions of the GNU General Public
 * License.  See the file README.legal in the main directory of this archive
 * for more details.
 */

/*
 * 680x0 support by Hamish Macdonald
 */

#ifndef _LINUX_MM_H
#define _LINUX_MM_H

#include <linux/page.h>
#include <linux/fs.h>
#include <linux/kernel.h>

/*
 * Linux kernel virtual memory manager primitives.
 * The idea being to have a "virtual" mm in the same way
 * we have a virtual fs - giving a cleaner interface to the
 * mm details, and allowing different kinds of memory mappings
 * (from shared memory to executable loading to arbitrary
 * mmap() functions).
 */

/*
 * This struct defines a memory VMM memory area. There is one of these
 * per VM-area/task.  A VM area is any part of the process virtual memory
 * space that has a special rule for the page-fault handlers (ie a shared
 * library, the executable area etc).
 */
struct vm_area_struct {
	struct task_struct * vm_task;		/* VM area parameters */
	unsigned long vm_start;
	unsigned long vm_end;
	unsigned short vm_page_prot;
	struct vm_area_struct * vm_next;	/* linked list */
	struct vm_area_struct * vm_share;	/* linked list */
	struct inode * vm_inode;
	unsigned long vm_offset;
	struct vm_operations_struct * vm_ops;
};

/*
 * These are the virtual MM functions - opening of an area, closing it (needed to
 * keep files on disk up-to-date etc), pointer to the functions called when a
 * no-page or a wp-page exception occurs, and the function which decides on sharing
 * of pages between different processes.
 */
struct vm_operations_struct {
	void (*open)(struct vm_area_struct * area);
	void (*close)(struct vm_area_struct * area);
	void (*nopage)(int error_code,
		       struct vm_area_struct * area, unsigned long address);
	void (*wppage)(struct vm_area_struct * area, unsigned long address);
	int (*share)(struct vm_area_struct * from, struct vm_area_struct * to, unsigned long address);
	int (*unmap)(struct vm_area_struct *area, unsigned long, size_t);
};

extern unsigned long __bad_page(void);
extern unsigned long __bad_pagetable(void);
extern unsigned long __zero_page(void);

#define BAD_PAGETABLE __bad_pagetable()
#define BAD_PAGE __bad_page()
#define ZERO_PAGE __zero_page()

extern volatile short free_page_ptr; /* used by malloc and tcp/ip. */

extern int nr_swap_pages;
extern int nr_free_pages;
extern int nr_secondary_pages;
extern unsigned long free_page_list;
extern unsigned long secondary_page_list;

#define MAX_SECONDARY_PAGES 10

/*
 * This is timing-critical - most of the time in getting a new page
 * goes to clearing the page. If you want a page without the clearing
 * overhead, just use __get_free_page() directly..
 */
extern unsigned long __get_free_page(int priority);
extern inline unsigned long get_free_page(int priority)
{
    unsigned long page;

    page = __get_free_page(priority);
    if (page)
	__asm__ __volatile__("movel %0,a0\n\t"
			     "movew #255,d4\n\t"
			     "moveq #0,d0\n\t"
			     "movel d0,d1\n\t"
			     "movel d0,d2\n\t"
			     "movel d0,d3\n"
			     "1:\n\tmoveml d0-d3,a0@-\n\t"
			     "dbra  d4,1b"
			     : /* no outputs */ : "a" (page+PAGE_SIZE)
			     : "d0","d1","d2","d3","d4","a0");
    return page;
}

/* memory.c */

extern void free_page(unsigned long addr);
extern unsigned long put_dirty_page(struct task_struct * tsk,unsigned long page,
	unsigned long address);
extern void free_page_tables(struct task_struct * tsk);
extern void clear_page_tables(struct task_struct * tsk);
extern int clone_page_tables(struct task_struct * to);
extern int copy_page_tables(struct task_struct * to);
extern int unmap_page_range(unsigned long from, unsigned long size);
extern int remap_page_range(unsigned long from, unsigned long to, unsigned long size, int mask);
extern int zeromap_page_range(unsigned long from, unsigned long size, int mask);

extern void do_wp_page(unsigned long error_code, unsigned long address,
       struct task_struct *tsk, unsigned long user_esp);
extern void do_no_page(unsigned long error_code, unsigned long address,
       struct task_struct *tsk, unsigned long user_esp);

extern void paging_init(unsigned long *start_mem, unsigned long *end_mem);
extern void mem_init(unsigned long low_start_mem,
		     unsigned long start_mem, unsigned long end_mem);
extern void show_mem(void);
extern void oom(struct task_struct * task);
extern void si_meminfo(struct sysinfo * val);

/* vmalloc.c */

extern void * vmalloc(unsigned long size);
extern void vfree(void * addr);
extern int vread(char *buf, char *addr, int count);

/* swap.c */

extern void swap_free(unsigned long page_nr);
extern unsigned long swap_duplicate(unsigned long page_nr);
extern void swap_in(unsigned long *table_ptr);
extern void si_swapinfo(struct sysinfo * val);
extern void rw_swap_page(int rw, unsigned long nr, char * buf);

/* mmap.c */
extern int do_mmap(struct file * file, unsigned long addr, unsigned long len,
	unsigned long prot, unsigned long flags, unsigned long off);
typedef int (*map_mergep_fnp)(const struct vm_area_struct *,
			      const struct vm_area_struct *, void *);
extern void merge_segments(struct vm_area_struct *, map_mergep_fnp, void *);
extern void insert_vm_struct(struct task_struct *, struct vm_area_struct *);
extern int ignoff_mergep(const struct vm_area_struct *,
			 const struct vm_area_struct *, void *);
extern int do_munmap(unsigned long, size_t);

#define read_swap_page(nr,buf) \
	rw_swap_page(READ,(nr),(buf))
#define write_swap_page(nr,buf) \
	rw_swap_page(WRITE,(nr),(buf))

#define invalidate() \
{ \
	if (boot_info.cputype & CPU_68040) \
		__asm__ __volatile__(".word 0xf510\n"::); /* pflushan */ \
	else \
		__asm__ __volatile__("pflusha\n"::); \
}

extern unsigned long high_memory;

#if defined(__i386__)
# define KSTART_ADDR  (0UL)
#elif defined(__mc68000__)
# define KSTART_ADDR  (0xC0000000UL)
#else
# error Architecture not supported
#endif
#define MAP_NR(addr) ((addr-KSTART_ADDR) >> PAGE_SHIFT)
#define MAP_PAGE_RESERVED (1<<15)

extern unsigned short * mem_map;

#if defined(__i386__)

#define PAGE_PRESENT	0x001
#define PAGE_RW 	0x002
#define PAGE_USER	0x004
#define PAGE_PWT	0x008	/* 486 only - not used currently */
#define PAGE_PCD	0x010	/* 486 only - not used currently */
#define PAGE_ACCESSED	0x020
#define PAGE_DIRTY	0x040
#define PAGE_COW	0x200	/* implemented in software (one of the AVL bits) */

#define PAGE_PRIVATE	(PAGE_PRESENT | PAGE_RW | PAGE_USER | PAGE_ACCESSED | PAGE_COW)
#define PAGE_SHARED	(PAGE_PRESENT | PAGE_RW | PAGE_USER | PAGE_ACCESSED)
#define PAGE_COPY	(PAGE_PRESENT | PAGE_USER | PAGE_ACCESSED | PAGE_COW)
#define PAGE_READONLY	(PAGE_PRESENT | PAGE_USER | PAGE_ACCESSED)
#define PAGE_TABLE	(PAGE_PRESENT | PAGE_RW | PAGE_USER | PAGE_ACCESSED)

#elif defined(__mc68000__)

/*
 * Definitions for MMU descriptors
 */
#define PAGE_PRESENT	0x001
#define PAGE_SHORT	0x002
#define PAGE_RW 	0x000
#define PAGE_RONLY	0x004
#define PAGE_ACCESSED	0x008
#define PAGE_DIRTY	0x010
#define PAGE_COW	0x800	/* implemented in software */
#define PAGE_NOCACHE	0x040
#define PAGE_GLOBAL040	0x400	/* 68040 global bit, used for kva descs */
#define PAGE_CACHE040	0x020	/* 68040 cache mode, cachable, copyback */

#define CACHEMASK040	(~0x060)

extern unsigned long mm_cachebits;
#define PAGE_PRIVATE	(PAGE_PRESENT | PAGE_RW | PAGE_COW | mm_cachebits)
#define PAGE_SHARED	(PAGE_PRESENT | PAGE_RW | mm_cachebits)
#define PAGE_COPY	(PAGE_PRESENT | PAGE_RONLY | PAGE_COW | mm_cachebits)
#define PAGE_READONLY	(PAGE_PRESENT | PAGE_RONLY | mm_cachebits)
#define PAGE_TABLE	(PAGE_SHORT   | PAGE_RW)

#define PAGE_IS_RW(prot) (!((prot) & PAGE_RONLY))

#define NUM_L1_ENTRIES	(128)    /* the number of entries in an L1 table
				  * (root table)
				  */
#define NUM_L2_ENTRIES	(8)      /* number of entries in an L2 table.
				  * (pointer table).  Each "entry" is
				  * actually 16 descriptors.  The descriptors
				  * refer to the page table.
				  */
#define NUM_L3_ENTRIES	(1024)   /* number of entries in an L3 table.
				  * (page table)
				  */

/* these constants define how many bytes are mapped by a single pt entry */
#define NBP_L2E 	(NUM_L3_ENTRIES * PAGE_SIZE)
#define NBP_L1E 	(NUM_L2_ENTRIES * NPB_L2E)

/* size of a pointer (or root) table */
#define PTABLE_SIZE	(sizeof(long)*NUM_L1_ENTRIES)

#define L1_SHIFT	(25)      /* to retrieve L1 index */
#define L2_SHIFT	(22)      /* to retrieve L2 index */
#define L3_SHIFT	(12)      /* to retrieve L3 index */

/* macros to retrieve a page table index from an address */
#define L1_INDEX(addr) (((unsigned long)addr) >> L1_SHIFT)
#define L2_INDEX(addr) (((((unsigned long)addr) >> L2_SHIFT) \
			& (NUM_L2_ENTRIES-1))*16)
#define L3_INDEX(addr) ((((unsigned long)addr) >> L3_SHIFT) \
			& (NUM_L3_ENTRIES-1))

#define TABLE_MASK	(0xfffffe00)

/* page table routines */
unsigned long *get_pointer_table (void);
void free_pointer_table (unsigned long *);
unsigned long *get_page_table (unsigned long *);
void free_page_table (unsigned long *ptr);

/* For virtual address to physical address conversion */
extern unsigned long mm_vtop(unsigned long addr);
extern unsigned long mm_ptov(unsigned long addr);
#define VTOP(addr)  (mm_vtop((unsigned long)(addr)))
#define PTOV(addr)  (mm_ptov((unsigned long)(addr)))

#define flush_icache() \
{ \
	if (boot_info.cputype & CPU_68040) \
		asm (".word 0xf498"); /* CINVA I */ \
	else \
		asm ("movec cacr,d0;" \
		     "andiw %0,d0;" \
		     "movec d0,cacr" \
		     : /* no outputs */ \
		     : "i" (FLUSH_I) \
		     : "d0"); \
}

/*
 * invalidate the cache for the specified memory range.
 * It starts at the physical address specified for
 * the given number of bytes.
 */
extern void cache_clear (unsigned long paddr, int len);
/*
 * push any dirty cache in the specified memory range.
 * It starts at the physical address specified for
 * the given number of bytes.
 */
extern void cache_push (unsigned long paddr, int len);

/*
 * push and invalidate pages in the specified user virtual
 * memory range.
 */
extern void cache_push_v (unsigned long vaddr, int len);

/* cache code */
#define FLUSH_I_AND_D	(0x00000808)
#define FLUSH_I 	(0x00000008)

/*
 * Return the number of valid bytes at 'addr', up to a maximum of 'count'
 * A byte is valid if /dev/mem can read/write it.
 */
extern int valid_addr(unsigned long addr, int count);

#else
# error Architecture not supported
#endif

#define GFP_BUFFER	0x00
#define GFP_ATOMIC	0x01
#define GFP_USER	0x02
#define GFP_KERNEL	0x03

/* vm_ops not present page codes */
#define SHM_SWP_TYPE 0x41
extern void shm_no_page (unsigned long *);

#endif /* _LINUX_MM_H */
