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

#define PAGE_SIZE 4096
#define PAGE_SHIFT 12

#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/traps.h>
#include <linux/fs.h>

/* The address of the kernel stack in kernel virtual space */
#define KSTACK_ADDR	(0xD0001000)

/* The address of the interrupt stack in kernel virtual space */
#define ISTACK_ADDR	(0xD0003000)

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

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

/*
 * Definitions for MMU descriptors
 */
#define BIT_WP		(2)
#define BIT_USED	(3)
#define BIT_DIRTY	(4)

#define PTE_READ_WRITE	(0)
#define PTE_WRITE_ONLY	(1)

#define PTE_WP		(1 << BIT_WP)
#define PTE_DIRTY	(1 << BIT_DIRTY)
#define PTE_USED	(1 << BIT_USED)

#define INVALID_DESC	(0x00)
#define PAGE_DESC	(0x01)
#define SHORT_DESC	(0x02)
#define LONG_DESC	(0x03)
#define DESC_MASK	(0x03)

#define PAGE_PRESENT	0x001
#define PAGE_RW 	0x000
#define PAGE_RONLY	0x004
#define PAGE_ACCESSED	0x010
#define PAGE_DIRTY	0x020
#define PAGE_COW	0x100	/* implemented in software */

#define PAGE_PRIVATE	(PAGE_PRESENT | PAGE_RW | PAGE_COW)
#define PAGE_SHARED	(PAGE_PRESENT | PAGE_RW)
#define PAGE_COPY	(PAGE_PRESENT | PAGE_RONLY | PAGE_COW)
#define PAGE_READONLY	(PAGE_PRESENT | PAGE_RONLY)
#define PAGE_TABLE	(SHORT_DESC   | PAGE_RW)

/*
 * This structure describes the virtual memory map for a task.
 *
 * It contains the following:
 *  - virtual pointer to the page directory for the task
 *  - physical pointer to the page directory for the task
 *  - virtual pointer to the kernel stack page for the task
 *  - physical pointer to the kernel stack page for the task
 *  - CPU root pointer for the task
 */
typedef struct {
    ulong *pagedir_v;
    ulong pagedir_p;
    ulong *kstack_v;
    ulong kstack_p;
    ulong crp[2];
} pmap_t;

#define NUM_L1_ENTRIES	(1024)    /* the number of entries in an L1 table
				   * (page directory)
				   */
#define NUM_L2_ENTRIES	(1024)    /* number of entries in an L2 table.
				   * (page table)
				   */

#define KERN_L1_ENTRY	(256*3)   /* index into L1 table for
				   * kernel virtual space */

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

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

/* macros to retrieve a page table index from an address */
#define L1_INDEX(addr) (((ulong)addr) >> L1_SHIFT)
#define L2_INDEX(addr) ((((ulong)addr) >> L2_SHIFT) \
			& (NUM_L2_ENTRIES-1))

/* For virtual address to physical address conversion */
extern ulong vpoffset;
#define VTOP(addr)  (((ulong)(addr)) + vpoffset)
#define PTOV(addr)  (((ulong)(addr)) - vpoffset)

/* index into L1 table for kernel stacks */
#define KSTACK_ENTRY	(832)

#define invalidate() \
__asm__ __volatile__("pflusha")

/*
 * This inline routine is used to switch process maps when context
 * switching.
 */
static inline void switch_pmap (pmap_t *pmap)
{
    /* The pointer to the page descriptor for the kernel stack */
    extern ulong *kstack_descp;

    /* switch the kernel stack */
    *kstack_descp = pmap->kstack_p | PAGE_DESC;

    /* switch the CPU root pointer */
    __asm__ __volatile__ ("pmove %0@,crp" :: "a" (pmap->crp));

    /*
     * flush atc
     * (probably not needed, since I think the above pmove does it)
     */
    __asm__ __volatile__ ("pflusha");
}

/* memory map definitions */
#define KSTART_ADDR  (0xC0000000)
#define MAP_NR(addr) ((addr-KSTART_ADDR) >> PAGE_SHIFT)
#define MAP_PAGE_RESERVED (1<<15)
extern unsigned short * mem_map;

extern ulong free_page_list;
extern ulong secondary_page_list;
extern int nr_free_pages;
extern int nr_secondary_pages;
extern volatile short free_page_ptr; /* used by malloc and tcp/ip. */

#define MAX_SECONDARY_PAGES 10

/* highest kernel memory location */
extern ulong high_memory;

/*
 * 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\t"
			     "1: moveml d0-d3,a0@-\n\t"
			     "dbra  d4,1b"
			     :: "a" (page+PAGE_SIZE)
			     : "d0","d1","d2","d3","d4","a0");
    return page;
}

/* MMU status register bits */

#define MMU_B	     (0x8000)    /* bus error */
#define MMU_L	     (0x4000)    /* limit violation */
#define MMU_S	     (0x2000)    /* supervisor violation */
#define MMU_WP	     (0x0800)    /* write-protected */
#define MMU_I	     (0x0400)    /* invalid descriptor */
#define MMU_M	     (0x0200)    /* ATC entry modified */
#define MMU_T	     (0x0040)    /* transparent translation */
#define MMU_NUM      (0x0007)    /* number of levels traversed */

/* memory.c */
extern void mem_init(ulong start_mem, ulong end_mem);
extern void do_page_fault(struct frame *, ulong, ulong);
extern int  copy_page_tables(struct task_struct * new);
extern void free_page_tables(struct task_struct * tsk);
extern void clear_page_tables(struct task_struct * tsk);
extern int remap_page_range(ulong from, ulong to, ulong size, int mask);
extern int unmap_page_range(ulong from, ulong size);
extern int zeromap_page_range(ulong from, ulong size, int mask);
extern void do_wp_page(ulong error_code, ulong address,
		       struct task_struct *tsk, ulong user_esp);
extern void do_no_page(ulong error_code, ulong address,
		       struct task_struct *tsk, ulong user_esp);
extern void oom(struct task_struct * task);
/*
 * 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(ulong addr, int count);
extern void si_meminfo(struct sysinfo * val);
extern ulong put_dirty_page(struct task_struct * tsk,ulong page,
	ulong address);

/* swap.c */
/* priority types for get_free_page */
#define GFP_BUFFER	0x00
#define GFP_ATOMIC	0x01
#define GFP_USER	0x02
#define GFP_KERNEL	0x03
extern ulong get_free_page(int priority);
extern void free_page(ulong addr);
extern void si_swapinfo(struct sysinfo * val);
extern void swap_free(ulong page_nr);
extern ulong swap_duplicate(ulong page_nr);
extern void swap_in(ulong *table_ptr);
extern void rw_swap_page(int rw, ulong nr, char * buf);
#define read_swap_page(nr,buf) \
	rw_swap_page(READ,(nr),(buf))
#define write_swap_page(nr,buf) \
	rw_swap_page(WRITE,(nr),(buf))

/* mmap.c */

#if 0
/*
 * Experimental... not in use
 */

/*
 * 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 */
	ulong vm_start;
	ulong vm_end;
	struct vm_area_struct * vm_next;	/* linked list */
	struct vm_area_struct * vm_share;	/* linked list */
	struct inode * vm_inode;
	ulong 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)(struct vm_area_struct * area, ulong address);
	void (*wppage)(struct vm_area_struct * area, ulong address);
	int (*share)(struct vm_area_struct * old, struct vm_area_struct * new, ulong address);
};
#endif /* experimental mm primitives */

#endif /* _LINUX_MM_H */
