Logo Search packages:      
Sourcecode: faumachine version File versions  Download package

mem.c

/* $Id: mem.c,v 1.97 2009-02-26 07:19:21 sand Exp $ 
 *
 * Copyright (C) 2004-2009 FAUmachine Team <info@faumachine.org>.
 * This program is free software. You can redistribute it and/or modify it
 * under the terms of the GNU General Public License, either version 2 of
 * the License, or (at your option) any later version. See COPYING.
 */

#include "build_config.h"

/* ==================== RUNTIME_RM ==================== */
#ifdef RUNTIME_RM

#include "compiler.h"
CODE16;

#include "traps.h"
#include "mem.h"
#include "assert.h"
#include "cmos.h"
#include "debug.h"
#include "stdio.h"
#include "io.h"
#include "var.h"
#include "ptrace.h"
#include "acpi-tables.h"

/*
 * A20 Gate Support.
 */
static ALWAYS_INLINE int
a20_fast(void)
{
      return 1;
}

static ALWAYS_INLINE void
a20_fast_on(void)
{
      uint8_t state;

      state = inb(0x92);
      state |= 1 << 1;
      outb(state, 0x92);
}

static ALWAYS_INLINE void
a20_fast_off(void)
{
      uint8_t state;

      state = inb(0x92);
      state &= ~(1 << 1);
      outb(state, 0x92);
}

static ALWAYS_INLINE int
a20_fast_state(void)
{
      uint8_t state;

      state = inb(0x92);

      return (state >> 1) & 1;
}

static ALWAYS_INLINE void
a20_kbd_on(void)
{
      outb(0xd1, 0x64); /* Send cmd: Write output port. */
      outb(1 << 1, 0x60);     /* Set A20 gate. */
}

static ALWAYS_INLINE void
a20_kbd_off(void)
{
      outb(0xd1, 0x64); /* Send cmd: Write output port. */
      outb(0 << 1, 0x60);     /* Disable A20 gate. */
}

static ALWAYS_INLINE int
a20_kbd_state(void)
{
      uint16_t flags;
      uint8_t state;

      /* Disable Interrupts */
      asm volatile (
            "pushfw\n\t"
            "popw %0\n\t"
            "cli\n\t"
            : "=r" (flags)
      );

      outb(0xd0, 0x64); /* Send cmd: Read output port. */
      while (! (inb(0x64) & 1)) {
            /* Wait... */
      }
      state = inb(0x60);      /* Read state. */

      /* Restore Interrupts */
      asm volatile (
            "pushw %0\n\t"
            "popfw\n\t"
            :
            : "r" (flags)
      );

      return (state >> 1) & 1;
}

static ALWAYS_INLINE unsigned char
set_enable_a20(unsigned char flag)
{
      int retval;

      retval = a20_fast_state();
      if (flag) {
            a20_fast_on();
      } else {
            a20_fast_off();
      }

      return retval;
}

static uint32_t
bios_mem_size(void)
{
      return (var_get(mem_size) - ACPI_MEM_SIZE - 1024L * 1024L) / 1024L;
}

/*
 * Get memory size
 *
 * In:
 *
 * Out:     AX    = number of contiguous KB starting at absolute address 0
 */
void
bios_12_xxxx(struct regs *regs)
{
      AX = var_get(mem_total);
}

void
bios_15_24xx(struct regs *regs)
{
      if (AL == 0x00) {
            /*
             * Disable A20 gate.
             */
            if (a20_fast()) {
                  a20_fast_off();
            } else {
                  a20_kbd_off();
            }

      } else if (AL == 0x01) {
            /*
             * Enable A20 gate.
             */
            if (a20_fast()) {
                  a20_fast_on();
            } else {
                  a20_kbd_on();
            }

      } else if (AL == 0x02) {
            /*
             * Get A20 gate state.
             *
             * AL:      Bit0: 1: A20 enabled.
             *          0: A20 disabled.
             */
            if (a20_fast()) {
                  AL = a20_fast_state();
            } else {
                  AL = a20_kbd_state();
            }

      } else if (AL == 0x03) {
            /*
             * Query A20 gate support.
             *
             * BX:      Bit0: Gate A20 support via kbd controller.
             *    Bit1: Gate A20 support via port 0x92.
             *    Bit2-14: Reserved.
             *    Bit15: Additional data available.
             */
            if (a20_fast()) {
                  BX = 1 << 1;
            } else {
                  BX = 1 << 0;
            }

      } else {
             dprintf("int $0x%02x: unknown sub-function 0x%04x\n",
                         0x15, AX);
             AH = 0x86; /* Function not supported. */
             F |= (1 << 0);   /* Set carry. */
             return;
      }

      AH = 0x00;  /* Success. */
      F &= ~(1 << 0);   /* Clear carry. */
}

/*
 * Memory Function 87: Copy extended memory
 *
 * In:  AH      = 87h
 *      CX  = number of words to copy (max 8000h)
 *    ES:SI = ptr to global descriptor table
 *
 *          offset   use     initially  comments
 *          ==============================================
 *          00..07   Unused  zeros      Null descriptor
 *          08..0f   GDT     zeros      filled in by BIOS
 *          10..17   source  ssssssss   source of data
 *          18..1f   dest    dddddddd   destination of data
 *          20..27   CS      zeros      filled in by BIOS
 *          28..2f   SS      zeros      filled in by BIOS
 *
 * Out: AH  = status
 *    CF    = clear if successful (returned AH=00h)
 *    CF    = set on error
 */
void
bios_15_87xx(struct regs *regs)
{
      unsigned char prev_a20_enable;
      unsigned long base;
      uint16_t cx;
      uint16_t di;
      uint16_t si;
      unsigned long tmp;

      /* Turn off interrupts. */
      disable_ints();

      /* Enable A20 line. */
      prev_a20_enable = set_enable_a20(1);

      /* Initialize GDT descriptor (in GDT table!). */
      base = ((unsigned long) ES << 4) + (unsigned long) SI;
      put_word(ES, SI + 0x08 + 0, 47);    /* limit 15:00 = 6 * 8 byte */
      put_word(ES, SI + 0x08 + 2, base & 0xffff); /* base 15:00 */
      put_byte(ES, SI + 0x08 + 4, base >> 16);/* base 23:16 */
      put_byte(ES, SI + 0x08 + 5, 0x93);  /* access */
      put_word(ES, SI + 0x08 + 6, 0x0000);      /* base 31:24, limit 19:16 */

      /* Initialize CS descriptor. */
      base = (unsigned long) bios_15_87xx;
      put_word(ES, SI + 0x20 + 0, 0xffff);      /* limit 15:00 */
      put_word(ES, SI + 0x20 + 2, base & 0xffff); /* base 15:00 */
      put_byte(ES, SI + 0x20 + 4, base >> 16);/* base 23:16 */
      put_byte(ES, SI + 0x20 + 5, 0x9b);  /* access */
      put_word(ES, SI + 0x20 + 6, 0x0000);      /* base 31:24, limit 19:16 */

      /* Initialize SS descriptor. */
      base = (unsigned long) get_ss() << 4;
      put_word(ES, SI + 0x28 + 0, 0xffff);      /* limit 15:00 */
      put_word(ES, SI + 0x28 + 2, base & 0xffff); /* base 15:00 */
      put_byte(ES, SI + 0x28 + 4, base >> 16);/* base 23:16 */
      put_byte(ES, SI + 0x28 + 5, 0x93);  /* access */
      put_word(ES, SI + 0x28 + 6, 0x0000);      /* base 31:24, limit 19:16 */

      /* Load GDT descriptor. */
      asm volatile (
            "movw %0, %%es\n"
            "lgdt %%es:(%1)\n"
            : /* No output */
            : "r" ((uint16_t)ES), "S" ((uint16_t)(SI + 8))
      );

      /* Load IDT descriptor. */
      /* Not used... */
      /* FIXME VOSSI */
      /* Switch to protected mode and copy words. */
      asm volatile (
            "pushaw\n"
            "pushfw\n"
            /* set cmos shutdown status */
            "movb $0x0f, %%al\n"
            "outb %%al, $0x70\n"
            "movb $0x05, %%al\n"
            "outb %%al, $0x71\n"
            /* ax = ds, ds = 0x0040 */
            "movw $0x0040, %%bx\n"
            "movw %%ds, %%ax\n"
            "movw %%bx, %%ds\n"
            /* POST reset flag (1234) to bypass memory test (warm boot) */
            "movw $0x0072, %%bx\n"
            "movw $0x1234, (%%bx)\n"
            /* save ss:sp, ds(in ax), es */
            "movw $0x00f0, %%bx\n"
            "movw %%sp, (%%bx)\n"
            "movw $0x00f2, %%bx\n"
            "movw %%ss, (%%bx)\n"
            "movw $0x00f4, %%bx\n"
            "movw %%ax, (%%bx)\n"
            "movw $0x00f6, %%bx\n"
            "movw %%es, (%%bx)\n"
            /* save return adress */
            "movw $0x0067, %%bx\n"
            "movw $(leave_pmode - bios_15_87xx), %%ax\n"
            "addw $OFF_bios_15_87xx, %%ax\n"
            "movw %%ax, (%%bx)\n"
            "movw $0x0069, %%bx\n"
            "movw %%cs, (%%bx)\n"

            /* Enter protected mode. */
            "smsww %w0\n"
            "orw $0x0001, %w0\n"
            "lmsww %w0\n"

            "ljmp $0x0020, $(enter_pmode - bios_15_87xx)\n"
      "enter_pmode:\n"

            /* Copy words. */
            "mov $0x0010, %0\n"
            "mov %0, %%ds\n"
            "mov $0x0018, %0\n"
            "mov %0, %%es\n"
            "movw $0x0000, %%si\n"
            "movw $0x0000, %%di\n"
            "cld\n"
            "rep movsw\n"

            /* reset CPU to get to real mode */
            "movb $0x4, %%al\n"
            "movw $0xcf9, %%dx\n"
            "out  %%al, (%%dx)\n"
            /* wait until reset */
            "1:\n"
            "jmp 1b\n"
      "leave_pmode:\n"

            /* restore ss:sp, es, ds */
            "movw $0x0040, %%bx\n"
            "movw %%bx, %%ds\n"
            "movw $0x00f0, %%bx\n"
            "movw (%%bx), %%sp\n"
            "movw $0x00f2, %%bx\n"
            "movw (%%bx), %%ss\n"
            "movw $0x00f4, %%bx\n"
            "movw (%%bx), %%ds\n"
            "movw $0x00f6, %%bx\n"
            "movw (%%bx), %%es\n"
            "popfw\n"
            "popaw\n"

            : "=r" (tmp), "=D" (di), "=S" (si), "=c" (cx)
            : "3" (CX)
      );

      /* Restore A20 gate. */
      set_enable_a20(prev_a20_enable);

      /* Turn back on interrupts. */
      enable_ints();

      AH = 0x00;
      F &= ~(1 << 0);   /* clear CF */
}
      
/*
 * Memory Function 88: Get extended memory size
 *
 * In:  AH      = 88h
 *
 * Out: AX  = number of contiguous KB starting at absolute address 100000h
 *    CF    = clear if successful (returned AH=00h)
 *    CF    = set on error
 */
void
bios_15_88xx(struct regs *regs)
{
      AX = cmos_get(mem_1kb);
      F &= ~(1 << 0);   /* clear CF */
}

/*
 * Memory Function E801: Get memory size (for machines with >64MB)
 *
 * In:      AX    = e801h
 *
 * Out:     AX    = extended memory between 1M and 16M, in K (max 3C00h = 15MB)
 *    BX    = extended memory above 16M, in 64K blocks
 *    CX    = configured memory 1M to 16M, in K
 *    DX    = configured memory above 16M, in 64K blocks
 *    CF    = clear if successful
 *    CF    = set on error
 */
void
bios_15_e801(struct regs *regs)
{
      uint32_t kb;

      kb = bios_mem_size();

      if (15 * 1024 <= kb) {
            AX = 15 * 1024;
            kb -= 15 * 1024;
      } else {
            AX = kb;
            kb = 0;
      }
      BX = kb / 64;
      CX = AX;
      DX = BX;

      F &= ~(1 << 0);   /* clear CF */
}

/*
 * Memory Function E820: Get memory size (for machines with >64MB)
 *
 * In:      AX    = e820h
 *    BX    = index of data block, 0: first block
 *    EDX   = 0x534D4150
 *
 * Out:     EAX   = 0x534D4150
 *    EBX   = 0: finished, other value: index of next data block
 *    ECX   = size of returned info (0x14)
 *    CF    = clear if successful
 *    CF    = set on error
 *
 *    memory map is copied to es:di
 */
void
bios_15_e820(struct regs *regs)
{
      struct mem_map {
            uint32_t addr0;         /* Base address. */
            uint32_t addr1;
            uint32_t size0;         /* Length in bytes. */
            uint32_t size1;
            enum {
                  AVAILABLE_MEMORY = 1,   /* Available to OS. */
                  RESERVED = 2,           /* ROM, mapped devices,... */
                  ACPI_RECLAIM = 3, /* Memory available after */
                                    /* ACPI tables have be read. */
                  ACPI_NVS = 4,           /* Memory which has to be */
                                    /* saved between NVS sessions.*/
                  /* Others are reserved. */
            } type;
      };
      uint32_t addr;
      uint32_t size;

      if (EDX != 0x534D4150) {
            AH = 0x86;  /* Function not supported. */
            F |= (1 << 0);    /* Set carry. */
            return;
      }

      DEBUGPRINT(80, "called e820 (memory map) with BX %04xh ES:DI %04x:%04x\n",
                  BX, ES, DI);

      if (BX == 0) {
            /* Report low memory. */
            addr = 0x00000000;
            size = var_get(mem_total) * 1024;

            put_long(ES, DI + offsetof(struct mem_map, addr0), addr);
            put_long(ES, DI + offsetof(struct mem_map, addr1), 0x00000000);
            put_long(ES, DI + offsetof(struct mem_map, size0), size);
            put_long(ES, DI + offsetof(struct mem_map, size1), 0x00000000);
            put_long(ES, DI + offsetof(struct mem_map, type), AVAILABLE_MEMORY);

            EAX = 0x534D4150;
            EBX = 1;          /* More to read. */
            ECX = sizeof(struct mem_map);

            F &= ~(1 << 0);         /* Clear carry. */

      } else if (BX == 1) {
            /* Reserved BIOS memory */
            addr = 0x000e0000;
            size = 0x00100000 - addr;

            put_long(ES, DI + offsetof(struct mem_map, addr0), addr);
            put_long(ES, DI + offsetof(struct mem_map, addr1), 0x00000000);
            put_long(ES, DI + offsetof(struct mem_map, size0), size);
            put_long(ES, DI + offsetof(struct mem_map, size1), 0x00000000);
            put_long(ES, DI + offsetof(struct mem_map, type), RESERVED);

            EAX = 0x534D4150;
            EBX = 2;                /* More to read. */
            ECX = sizeof(struct mem_map);

            F &= ~(1 << 0);         /* Clear carry. */

      } else if (BX == 2) {
            /* Report high memory. */
            addr = 0x00100000;
            size = var_get(mem_size);
            size -= addr;
            size -= ACPI_MEM_SIZE; /* space for some ACPI stuff */

            put_long(ES, DI + offsetof(struct mem_map, addr0), addr);
            put_long(ES, DI + offsetof(struct mem_map, addr1), 0x00000000);
            put_long(ES, DI + offsetof(struct mem_map, size0), size);
            put_long(ES, DI + offsetof(struct mem_map, size1), 0x00000000);
            put_long(ES, DI + offsetof(struct mem_map, type), AVAILABLE_MEMORY);

            EAX = 0x534D4150;
#ifdef CONFIG_ACPI_SUPPORT
            EBX = 3;          /* More to read. */
#else
            EBX = 0;          /* Nothing more to read. */
#endif
            ECX = sizeof(struct mem_map);

            F &= ~(1 << 0);         /* Clear Carry. */

#ifdef CONFIG_ACPI_SUPPORT
      } else if (BX == 3) {
            /* Report ACPI NVS memory */
            addr = var_get(mem_size);
            addr -= ACPI_MEM_SIZE;
            size = ACPI_NVS_SIZE;

            put_long(ES, DI + offsetof(struct mem_map, addr0), addr);
            put_long(ES, DI + offsetof(struct mem_map, addr1), 0x00000000);
            put_long(ES, DI + offsetof(struct mem_map, size0), size);
            put_long(ES, DI + offsetof(struct mem_map, size1), 0x00000000);
            put_long(ES, DI + offsetof(struct mem_map, type), ACPI_NVS);

            EAX = 0x534D4150;
            EBX = 4;          /* More to read. */
            ECX = sizeof(struct mem_map);

            F &= ~(1 << 0);         /* Clear Carry. */

      } else if (BX == 4) {
            /* Report ACPI table memory */
            addr = var_get(mem_size);
            addr -= ACPI_MEM_SIZE;
            addr += ACPI_NVS_SIZE;
            size = var_get(mem_size);
            size -= addr;

            put_long(ES, DI + offsetof(struct mem_map, addr0), addr);
            put_long(ES, DI + offsetof(struct mem_map, addr1), 0x00000000);
            put_long(ES, DI + offsetof(struct mem_map, size0), size);
            put_long(ES, DI + offsetof(struct mem_map, size1), 0x00000000);
            put_long(ES, DI + offsetof(struct mem_map, type), ACPI_RECLAIM);

            EAX = 0x534D4150;
            EBX = 5;          /* More to read. */
            ECX = sizeof(struct mem_map);

            F &= ~(1 << 0);         /* Clear Carry. */

      } else if (BX == 5) {
            /* Report Mapped BIOS ROM at end of address space */
            addr = 0xFFF00000;
            size = 0x00000000 - addr;

            put_long(ES, DI + offsetof(struct mem_map, addr0), addr);
            put_long(ES, DI + offsetof(struct mem_map, addr1), 0x00000000);
            put_long(ES, DI + offsetof(struct mem_map, size0), size);
            put_long(ES, DI + offsetof(struct mem_map, size1), 0x00000000);
            put_long(ES, DI + offsetof(struct mem_map, type), RESERVED);

            EAX = 0x534D4150;
            EBX = 0;                /* Nothing more to read. */
            ECX = sizeof(struct mem_map);

            F &= ~(1 << 0);         /* Clear Carry. */
#endif /* CONFIG_ACPI_SUPPORT */
      } else {
            AH = 0x86;  /* Function not supported. */
            F |= (1 << 0);    /* Set carry. */
      }
}

/*
 * Memory Function E881: Get memory size (for machines with >64MB)
 * (Phoenix BIOS extension)
 *
 * In:      AX    = e881h
 *
 * Out:     AX    = extended memory between 1M and 16M, in K (max 3C00h = 15MB)
 *    BX    = extended memory above 16M, in 64K blocks
 *    CX    = configured memory 1M to 16M, in K
 *    DX    = configured memory above 16M, in 64K blocks
 *    CF    = clear if successful
 *    CF    = set on error
 */
void
bios_15_e881(struct regs *regs)
{
      uint32_t kb;

      kb = bios_mem_size();

      if (15 * 1024 <= kb) {
            AX = 15 * 1024;
            kb -= 15 * 1024;
      } else {
            AX = kb;
            kb = 0;
      }
      BX = kb / 64;
      CX = AX;
      DX = BX;

      F &= ~(1 << 0);   /* clear CF */
}

#endif /* RUNTIME */
/* =================== REAL-MODE INIT ========================= */
#ifdef INIT_RM

#include "compiler.h"
CODE16;

#include "io.h"
#include "cmos.h"
#include "var.h"
#include "stdio.h"
#include "mem.h"
#include "video.h"

/*
 * This subroutines must be called in protected mode!
 */

static int
mem_exists(volatile unsigned char *p)
{
      unsigned char c;

      c = *p;
      *p = 0xaa;
      if (*p != 0xaa) return 0;
      *p = 0x55;
      if (*p != 0x55) return 0;
      *p = c;

      return 1;
}

/*
 * This subroutines must be called in real mode!
 */

static uint32_t
mem_check(void)
{
      volatile unsigned char *p;
      uint32_t memsize;

      /*
       * determine size of memory
       */
#ifndef CONFIG_80286_SUPPORT
      /* Assume more than 1Mbyte of RAM - FIXME VOSSI */
      for (p = (volatile unsigned char *) ((uint32_t) 1024 * 1024); ;) {
            if (! mem_exists(p)) {
                  break;
            } else {
                  p += (uint32_t) 64 * 1024; /* increase in 64kB steps */
                  memsize = (long) p;
                  memsize /= 1024;     /* in kBytes */
                  gotoxy(14, 9, 0);
                  bprintf("%7lu", (unsigned long) memsize);
            }
      }
      memsize = (uint32_t) p;
#else
      /* Assume less(equal) than 640kbytes of RAM (all in real mode), start at 64KB */
      for (p = (volatile unsigned char *) ((uint32_t) 0x10000000); (uint32_t)p<(uint32_t)0xa0000000;) {
            if (! mem_exists(p)) {
                  break;
            } else {
                  p += (uint32_t) 0x10000000; /* increase in 64kB steps */
                  memsize = (uint16_t) p + ((p >> 12)&0xf0000);
                  memsize /= 1024;     /* in kBytes */
                  gotoxy(14, 9, 0);
                  bprintf("%7lu", (unsigned long) memsize);
            }
      }
      memsize = (uint32_t) p;
      memsize = (uint16_t) memsize + (memsize >> 12);
#endif
      return memsize;
}

void
mem_init(void)
{
      uint32_t size;
      uint16_t basemem;
      uint16_t extmem;

      /*
       * Determine Memory Size
       */
      size = mem_check();

      /*
       * Write memory size to BDA and CMOS RAM.
       */
      var_put(mem_size, size); /* FIXME */

      size /= 1024; /* in KByte */

      /* Base Memory */
      if (640 <= size) {
            basemem = 640;
      } else {
            basemem = size;
      }
      basemem -= 1; /* EBDA */
      cmos_put(basemem_1kb, basemem);
      var_put(mem_total, basemem);
      size -= basemem + 1; /* EBDA */

      /* Skip BIOS Shadow (if available) */
      if (1024 - 640 <= size) {
            size -= 1024 - 640;
      } else {
            size = 0;
      }

      /* Extended Memory (1..16MB) */
      if (15 * 1024 <= size) {
            extmem = 15 * 1024;
      } else {
            extmem = size;
      }
      cmos_put(extmem_1kb, extmem);
      cmos_put(mem_1kb, extmem);
      size -= extmem;

      /* Extended Memory (>16MB) */
      size /= 64;
      cmos_put(mem_64kb, size);
}

#endif /* INIT_RM */

Generated by  Doxygen 1.6.0   Back to index