/* 
 * Copyright (c) 1995-1994 The University of Utah and
 * the Computer Systems Laboratory at the University of Utah (CSL).
 * All rights reserved.
 *
 * Permission to use, copy, modify and distribute this software is hereby
 * granted provided that (1) source code retains these copyright, permission,
 * and disclaimer notices, and (2) redistributions including binaries
 * reproduce the notices in supporting documentation, and (3) all advertising
 * materials mentioning features or use of this software display the following
 * acknowledgement: ``This product includes software developed by the
 * Computer Systems Laboratory at the University of Utah.''
 *
 * THE UNIVERSITY OF UTAH AND CSL ALLOW FREE USE OF THIS SOFTWARE IN ITS "AS
 * IS" CONDITION.  THE UNIVERSITY OF UTAH AND CSL DISCLAIM ANY LIABILITY OF
 * ANY KIND FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
 *
 * CSL requests users of this software to return to csl-dist@cs.utah.edu any
 * improvements that they make and grant CSL redistribution rights.
 *
 *      Author: Bryan Ford, University of Utah CSL
 */

#include <mach/machine/code16.h>
#include <mach/machine/vm_types.h>
#include <mach/machine/far_ptr.h>
#include <mach/machine/proc_reg.h>

#include "i16_bios.h"
#include "phys_mem.h"
#include "vm_param.h"
#include "debug.h"


static vm_offset_t ext_mem_phys_free_mem;
static vm_size_t ext_mem_phys_free_size;


CODE32

int ext_mem_collect(void)
{
	if (ext_mem_phys_free_mem)
	{
		phys_mem_add(ext_mem_phys_free_mem, ext_mem_phys_free_size);
		ext_mem_phys_free_mem = 0;
	}
}

CODE16

void i16_ext_mem_check()
{
	vm_offset_t ext_mem_top, ext_mem_bot;
	unsigned short ext_mem_k;

	/* Find the top of available extended memory.  */
	asm volatile("
		int	$0x15
		jnc	1f
		xorw	%%ax,%%ax
	1:
	" : "=a" (ext_mem_k)
	  : "a" (0x8800));
	ext_mem_top = 0x100000 + (vm_offset_t)ext_mem_k * 1024;

	/* XXX check for >16MB memory using function 0xc7 */

	ext_mem_bot = 0x100000;

	/* Check for extended memory allocated bottom-up: method 1.
	   This uses the technique (and, loosely, the code)
	   described in the VCPI spec, version 1.0.  */
	if (ext_mem_top > ext_mem_bot)
	{
		asm volatile("
			pushw	%%es

			xorw	%%ax,%%ax
			movw	%%ax,%%es
			movw	%%es:0x19*4+2,%%ax
			movw	%%ax,%%es

			movw	$0x12,%%di
			movw	$7,%%cx
			rep
			cmpsb
			jne	1f

			xorl	%%edx,%%edx
			movb	%%es:0x2e,%%dl
			shll	$16,%%edx
			movw	%%es:0x2c,%%dx

		1:
			popw	%%es
		" : "=d" (ext_mem_bot)
		  : "d" (ext_mem_bot),
		    "S" ((unsigned short)(vm_offset_t)"VDISK V")
		  : "eax", "ecx", "esi", "edi");
	}
	i16_assert(ext_mem_bot >= 0x100000);

	/* Check for extended memory allocated bottom-up: method 2.
	   This uses the technique (and, loosely, the code)
	   described in the VCPI spec, version 1.0.  */
	if (ext_mem_top > ext_mem_bot)
	{
		struct {
			char pad1[3];
			char V;
			long DISK;
			char pad2[30-8];
			unsigned short addr;
		} buf;
		unsigned char rc;

		i16_assert(sizeof(buf) == 0x20);
		rc = i16_bios_copy_ext_mem(0x100000, kvtolin((vm_offset_t)&buf), sizeof(buf)/2);
		if ((rc == 0) && (buf.V == 'V') && (buf.DISK == 'DISK'))
		{
			vm_offset_t new_bot = (vm_offset_t)buf.addr << 10;
			i16_assert(new_bot > 0x100000);
			if (new_bot > ext_mem_bot)
				ext_mem_bot = new_bot;
		}
	}
	i16_assert(ext_mem_bot >= 0x100000);

	if (ext_mem_top > ext_mem_bot)
	{
		ext_mem_phys_free_mem = ext_mem_bot;
		ext_mem_phys_free_size = ext_mem_top - ext_mem_bot;

		/* We need to update phys_mem_max here
		   instead of just letting phys_mem_add() do it
		   when the memory is collected with phys_mem_collect(),
		   because VCPI initialization needs to know the top of physical memory
		   before phys_mem_collect() is called.
		   See i16_vcpi.c for the gross details.  */
		if (ext_mem_top > phys_mem_max)
			phys_mem_max = ext_mem_top;
	}
}

void i16_ext_mem_shutdown()
{
	/* We didn't actually allocate the memory,
	   so no need to deallocate it... */
}