/* ----------------------------------------------------------------------- *
 *
 *   Copyright 2009 Intel Corporation; author: H. Peter Anvin
 *
 *   This program is free software; you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
 *   Boston MA 02110-1301, USA; either version 2 of the License, or
 *   (at your option) any later version; incorporated herein by reference.
 *
 *   Based on code from the Linux kernel:
 *
 *   Copyright (C) 1991, 1992 Linus Torvalds
 *   Copyright 2007 rPath, Inc. - All Rights Reserved
 *
 *   Original APM BIOS checking by Stephen Rothwell, May 1994
 *   (sfr@canb.auug.org.au)
 *
 *   This file is part of the Linux kernel, and is made available under
 *   the terms of the GNU General Public License version 2.
 *
 * ----------------------------------------------------------------------- */

/*
 * apm.c
 *
 * APM information for Multiboot
 */

#include "mboot.h"
#include <com32.h>

void mboot_apm(void)
{
    static struct apm_info apm;
    com32sys_t ireg, oreg;

    memset(&ireg, 0, sizeof ireg);

    ireg.eax.w[0] = 0x5300;
    __intcall(0x15, &ireg, &oreg);

    if (oreg.eflags.l & EFLAGS_CF)
	return;			/* No APM BIOS */

    if (oreg.ebx.w[0] != 0x504d)
	return;			/* No "PM" signature */

    if (!(oreg.ecx.w[0] & 0x02))
	return;			/* 32 bits not supported */

    /* Disconnect first, just in case */
    ireg.eax.b[0] = 0x04;
    __intcall(0x15, &ireg, &oreg);

    /* 32-bit connect */
    ireg.eax.b[0] = 0x03;
    __intcall(0x15, &ireg, &oreg);

    apm.cseg = oreg.eax.w[0];
    apm.offset = oreg.ebx.l;
    apm.cseg_16 = oreg.ecx.w[0];
    apm.dseg_16 = oreg.edx.w[0];
    apm.cseg_len = oreg.esi.w[0];
    apm.cseg_16_len = oreg.esi.w[1];
    apm.dseg_16_len = oreg.edi.w[0];

    /* Redo the installation check as the 32-bit connect;
       some BIOSes return different flags this way... */

    ireg.eax.b[0] = 0x00;
    __intcall(0x15, &ireg, &oreg);

    if ((oreg.eflags.l & EFLAGS_CF) || (oreg.ebx.w[0] != 0x504d)) {
	/* Failure with 32-bit connect, try to disconect and ignore */
	ireg.eax.b[0] = 0x04;
	__intcall(0x15, &ireg, NULL);
	return;
    }

    apm.version = oreg.eax.w[0];

    mbinfo.apm_table = map_data(&apm, sizeof apm, 4, false);
    if (mbinfo.apm_table)
	mbinfo.flags |= MB_INFO_APM_TABLE;
}
