Committed the two missing files.
This commit is contained in:
515
src/acpi.c
Normal file
515
src/acpi.c
Normal file
@@ -0,0 +1,515 @@
|
||||
/*
|
||||
* 86Box A hypervisor and IBM PC system emulator that specializes in
|
||||
* running old operating systems and software designed for IBM
|
||||
* PC systems and compatibles from 1981 through fairly recent
|
||||
* system designs based on the PCI bus.
|
||||
*
|
||||
* This file is part of the 86Box distribution.
|
||||
*
|
||||
* ACPI emulation.
|
||||
*
|
||||
*
|
||||
*
|
||||
* Authors: Miran Grca, <mgrca8@gmail.com>
|
||||
*
|
||||
* Copyright 2020 Miran Grca.
|
||||
*/
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <wchar.h>
|
||||
#define HAVE_STDARG_H
|
||||
#include <86box/86box.h>
|
||||
#include "cpu.h"
|
||||
#include <86box/device.h>
|
||||
#include <86box/mem.h>
|
||||
#include <86box/io.h>
|
||||
#include <86box/pci.h>
|
||||
#include <86box/pic.h>
|
||||
#include <86box/timer.h>
|
||||
#include <86box/keyboard.h>
|
||||
#include <86box/nvr.h>
|
||||
#include <86box/pit.h>
|
||||
#include <86box/acpi.h>
|
||||
|
||||
|
||||
#ifdef ENABLE_ACPI_LOG
|
||||
int acpi_do_log = ENABLE_ACPI_LOG;
|
||||
|
||||
|
||||
static void
|
||||
acpi_log(const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
if (acpi_do_log) {
|
||||
va_start(ap, fmt);
|
||||
pclog_ex(fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
}
|
||||
#else
|
||||
#define acpi_log(fmt, ...)
|
||||
#endif
|
||||
|
||||
|
||||
static void
|
||||
acpi_update_irq(void *priv)
|
||||
{
|
||||
acpi_t *dev = (acpi_t *) priv;
|
||||
int sci_level;
|
||||
|
||||
sci_level = (dev->pmsts & dev->pmen) & (RTC_EN | PWRBTN_EN | GBL_EN | TMROF_EN);
|
||||
|
||||
if (sci_level) {
|
||||
if (dev->irq_mode == 1)
|
||||
pci_set_irq(dev->slot, dev->irq_pin);
|
||||
else
|
||||
picintlevel(1 << 9);
|
||||
} else {
|
||||
if (dev->irq_mode == 1)
|
||||
pci_clear_irq(dev->slot, dev->irq_pin);
|
||||
else
|
||||
picintc(1 << 9);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static uint32_t
|
||||
acpi_reg_read_common(int size, uint16_t addr, void *p)
|
||||
{
|
||||
acpi_t *dev = (acpi_t *) p;
|
||||
uint32_t ret = 0x00000000;
|
||||
int shift16, shift32;
|
||||
|
||||
addr &= 0x3f;
|
||||
shift16 = (addr & 1) << 3;
|
||||
shift32 = (addr & 3) << 3;
|
||||
|
||||
switch (addr) {
|
||||
case 0x00: case 0x01:
|
||||
/* PMSTS - Power Management Status Register (IO) */
|
||||
ret = (dev->pmsts >> shift16) & 0xff;
|
||||
break;
|
||||
case 0x02: case 0x03:
|
||||
/* PMEN - Power Management Resume Enable Register (IO) */
|
||||
ret = (dev->pmen >> shift16) & 0xff;
|
||||
break;
|
||||
case 0x04: case 0x05:
|
||||
/* PMCNTRL - Power Management Control Register (IO) */
|
||||
ret = (dev->pmcntrl >> shift16) & 0xff;
|
||||
break;
|
||||
case 0x08: case 0x09: case 0x0a: case 0x0b:
|
||||
/* PMTMR - Power Management Timer Register (IO) */
|
||||
ret = (dev->timer_val >> shift32) & 0xff;
|
||||
break;
|
||||
case 0x0c: case 0x0d:
|
||||
/* GPSTS - General Purpose Status Register (IO) */
|
||||
ret = (dev->gpsts >> shift16) & 0xff;
|
||||
break;
|
||||
case 0x0e: case 0x0f:
|
||||
/* GPEN - General Purpose Enable Register (IO) */
|
||||
ret = (dev->gpen >> shift16) & 0xff;
|
||||
break;
|
||||
case 0x10: case 0x11: case 0x12: case 0x13:
|
||||
/* PCNTRL - Processor Control Register (IO) */
|
||||
ret = (dev->pcntrl >> shift32) & 0xff;
|
||||
break;
|
||||
case 0x14:
|
||||
/* PLVL2 - Processor Level 2 Register (IO) */
|
||||
if (size == 1)
|
||||
ret = dev->plvl2;
|
||||
break;
|
||||
case 0x15:
|
||||
/* PLVL3 - Processor Level 3 Register (IO) */
|
||||
if (size == 1)
|
||||
ret = dev->plvl3;
|
||||
break;
|
||||
case 0x18: case 0x19:
|
||||
/* GLBSTS - Global Status Register (IO) */
|
||||
ret = (dev->glbsts >> shift16) & 0xff;
|
||||
if (addr == 0x18) {
|
||||
ret &= 0x05;
|
||||
if (dev->gpsts != 0x0000)
|
||||
ret |= 0x80;
|
||||
if (dev->pmsts != 0x0000)
|
||||
ret |= 0x40;
|
||||
if (in_smm)
|
||||
ret |= 0x20;
|
||||
if (dev->devsts != 0x00000000)
|
||||
ret |= 0x10;
|
||||
}
|
||||
break;
|
||||
case 0x1c: case 0x1d: case 0x1e: case 0x1f:
|
||||
/* DEVSTS - Device Status Register (IO) */
|
||||
ret = ((dev->devsts | 0x10000000) >> shift32) & 0xff;
|
||||
break;
|
||||
case 0x20: case 0x21:
|
||||
/* GLBEN - Global Enable Register (IO) */
|
||||
ret = (dev->glben >> shift16) & 0xff;
|
||||
break;
|
||||
case 0x28: case 0x29: case 0x2a: case 0x2b:
|
||||
/* GLBCTL - Global Control Register (IO) */
|
||||
ret = (dev->glbctl >> shift32) & 0xff;
|
||||
break;
|
||||
case 0x2c: case 0x2d: case 0x2e: case 0x2f:
|
||||
/* DEVCTL - Device Control Register (IO) */
|
||||
ret = (dev->devctl >> shift32) & 0xff;
|
||||
break;
|
||||
case 0x30: case 0x31: case 0x32:
|
||||
/* GPIREG - General Purpose Input Register (IO) */
|
||||
if (size == 1)
|
||||
ret = dev->gpireg[addr & 3];
|
||||
break;
|
||||
case 0x34: case 0x35: case 0x36: case 0x37:
|
||||
/* GPOREG - General Purpose Output Register (IO) */
|
||||
if (size == 1)
|
||||
ret = dev->gporeg[addr & 3];
|
||||
break;
|
||||
}
|
||||
|
||||
acpi_log("(%i) ACPI Read (%i) %02X: %02X\n", in_smm, size, addr, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
acpi_reg_write_common(int size, uint16_t addr, uint8_t val, void *p)
|
||||
{
|
||||
acpi_t *dev = (acpi_t *) p;
|
||||
int shift16, shift32;
|
||||
int sus_typ;
|
||||
|
||||
addr &= 0x3f;
|
||||
acpi_log("(%i) ACPI Write (%i) %02X: %02X\n", in_smm, size, addr, val);
|
||||
shift16 = (addr & 1) << 3;
|
||||
shift32 = (addr & 3) << 3;
|
||||
|
||||
switch (addr) {
|
||||
case 0x00: case 0x01:
|
||||
/* PMSTS - Power Management Status Register (IO) */
|
||||
dev->pmsts &= ~((val << shift16) & 0x8d31);
|
||||
acpi_update_irq(dev);
|
||||
break;
|
||||
case 0x02: case 0x03:
|
||||
/* PMEN - Power Management Resume Enable Register (IO) */
|
||||
dev->pmen = ((dev->pmen & ~(0xff << shift16)) | (val << shift16)) & 0x0521;
|
||||
acpi_update_irq(dev);
|
||||
break;
|
||||
case 0x04: case 0x05:
|
||||
/* PMCNTRL - Power Management Control Register (IO) */
|
||||
dev->pmcntrl = ((dev->pmcntrl & ~(0xff << shift16)) | (val << shift16)) & 0x3c07;
|
||||
if (dev->pmcntrl & 0x2000) {
|
||||
sus_typ = (dev->pmcntrl >> 10) & 7;
|
||||
switch (sus_typ) {
|
||||
case 0:
|
||||
/* Soft power off. */
|
||||
exit(-1);
|
||||
break;
|
||||
case 1:
|
||||
/* Suspend to RAM. */
|
||||
nvr_reg_write(0x000f, 0xff, dev->nvr);
|
||||
|
||||
/* Do a hard reset. */
|
||||
device_reset_all_pci();
|
||||
|
||||
cpu_alt_reset = 0;
|
||||
|
||||
pci_reset();
|
||||
keyboard_at_reset();
|
||||
|
||||
mem_a20_alt = 0;
|
||||
mem_a20_recalc();
|
||||
|
||||
flushmmucache();
|
||||
|
||||
resetx86();
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 0x0c: case 0x0d:
|
||||
/* GPSTS - General Purpose Status Register (IO) */
|
||||
dev->gpsts &= ~((val << shift16) & 0x0f81);
|
||||
break;
|
||||
case 0x0e: case 0x0f:
|
||||
/* GPEN - General Purpose Enable Register (IO) */
|
||||
dev->gpen = ((dev->gpen & ~(0xff << shift16)) | (val << shift16)) & 0x0f01;
|
||||
break;
|
||||
case 0x10: case 0x11: case 0x12: case 0x13:
|
||||
/* PCNTRL - Processor Control Register (IO) */
|
||||
dev->pcntrl = ((dev->pcntrl & ~(0xff << shift32)) | (val << shift32)) & 0x00023e1e;
|
||||
break;
|
||||
case 0x14:
|
||||
/* PLVL2 - Processor Level 2 Register (IO) */
|
||||
if (size == 1)
|
||||
dev->plvl2 = val;
|
||||
break;
|
||||
case 0x15:
|
||||
/* PLVL3 - Processor Level 3 Register (IO) */
|
||||
if (size == 1)
|
||||
dev->plvl3 = val;
|
||||
break;
|
||||
case 0x18: case 0x19:
|
||||
/* GLBSTS - Global Status Register (IO) */
|
||||
dev->glbsts &= ~((val << shift16) & 0x0df7);
|
||||
break;
|
||||
case 0x1c: case 0x1d: case 0x1e: case 0x1f:
|
||||
/* DEVSTS - Device Status Register (IO) */
|
||||
dev->devsts &= ~((val << shift32) & 0x3fff0fff);
|
||||
break;
|
||||
case 0x20: case 0x21:
|
||||
/* GLBEN - Global Enable Register (IO) */
|
||||
dev->glben = ((dev->glben & ~(0xff << shift16)) | (val << shift16)) & 0x8d1f;
|
||||
break;
|
||||
case 0x28: case 0x29: case 0x2a: case 0x2b:
|
||||
/* GLBCTL - Global Control Register (IO) */
|
||||
// dev->glbctl = ((dev->glbctl & ~(0xff << shift32)) | (val << shift32)) & 0x0701ff07;
|
||||
dev->glbctl = ((dev->glbctl & ~(0xff << shift32)) | (val << shift32)) & 0x0700ff07;
|
||||
break;
|
||||
case 0x2c: case 0x2d: case 0x2e: case 0x2f:
|
||||
/* DEVCTL - Device Control Register (IO) */
|
||||
dev->devctl = ((dev->devctl & ~(0xff << shift32)) | (val << shift32)) & 0x0fffffff;
|
||||
break;
|
||||
case 0x34: case 0x35: case 0x36: case 0x37:
|
||||
/* GPOREG - General Purpose Output Register (IO) */
|
||||
if (size == 1)
|
||||
dev->gporeg[addr & 3] = val;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static uint32_t
|
||||
acpi_reg_readl(uint16_t addr, void *p)
|
||||
{
|
||||
uint32_t ret = 0x00000000;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 4; i++) {
|
||||
ret <<= 8;
|
||||
ret |= acpi_reg_read_common(4, addr + i, p);
|
||||
}
|
||||
|
||||
acpi_log("ACPI: Read L %08X from %04X\n", ret, addr);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
static uint16_t
|
||||
acpi_reg_readw(uint16_t addr, void *p)
|
||||
{
|
||||
uint16_t ret = 0x0000;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 2; i++) {
|
||||
ret <<= 8;
|
||||
ret |= acpi_reg_read_common(2, addr + i, p);
|
||||
}
|
||||
|
||||
acpi_log("ACPI: Read W %08X from %04X\n", ret, addr);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
static uint8_t
|
||||
acpi_reg_read(uint16_t addr, void *p)
|
||||
{
|
||||
uint8_t ret = 0x00;
|
||||
|
||||
ret = acpi_reg_read_common(1, addr, p);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
acpi_reg_writel(uint16_t addr, uint32_t val, void *p)
|
||||
{
|
||||
int i = 0;
|
||||
|
||||
for (i = 0; i < 4; i++) {
|
||||
acpi_reg_write_common(4, addr + i, val & 0xff, p);
|
||||
val >>= 8;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
acpi_reg_writew(uint16_t addr, uint16_t val, void *p)
|
||||
{
|
||||
int i = 0;
|
||||
|
||||
for (i = 0; i < 2; i++) {
|
||||
acpi_reg_write_common(2, addr + i, val & 0xff, p);
|
||||
val >>= 8;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
acpi_reg_write(uint16_t addr, uint8_t val, void *p)
|
||||
{
|
||||
acpi_reg_write_common(1, addr, val, p);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
acpi_update_io_mapping(acpi_t *dev, uint32_t base, int chipset_en)
|
||||
{
|
||||
if (dev->io_base != 0x0000) {
|
||||
io_removehandler(dev->io_base, 0x40,
|
||||
acpi_reg_read, acpi_reg_readw, acpi_reg_readl,
|
||||
acpi_reg_write, acpi_reg_writew, acpi_reg_writel, dev);
|
||||
}
|
||||
|
||||
dev->io_base = base;
|
||||
|
||||
if (chipset_en && (dev->io_base != 0x0000)) {
|
||||
io_sethandler(dev->io_base, 0x40,
|
||||
acpi_reg_read, acpi_reg_readw, acpi_reg_readl,
|
||||
acpi_reg_write, acpi_reg_writew, acpi_reg_writel, dev);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
acpi_timer_count(void *priv)
|
||||
{
|
||||
acpi_t *dev = (acpi_t *) priv;
|
||||
int overflow;
|
||||
uint32_t old;
|
||||
|
||||
old = dev->timer_val;
|
||||
dev->timer_val++;
|
||||
|
||||
if (dev->timer32)
|
||||
overflow = (old ^ dev->timer_val) & 0x80000000;
|
||||
else {
|
||||
dev->timer_val &= 0x00ffffff;
|
||||
overflow = (old ^ dev->timer_val) & 0x00800000;
|
||||
}
|
||||
|
||||
if (overflow) {
|
||||
dev->pmsts |= TMROF_EN;
|
||||
acpi_update_irq(dev);
|
||||
}
|
||||
|
||||
timer_advance_u64(&dev->timer, ACPICONST);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
acpi_init_gporeg(acpi_t *dev, uint8_t val0, uint8_t val1, uint8_t val2, uint8_t val3)
|
||||
{
|
||||
dev->gporeg[0] = val0;
|
||||
dev->gporeg[1] = val1;
|
||||
dev->gporeg[2] = val2;
|
||||
dev->gporeg[3] = val3;
|
||||
acpi_log("acpi_init_gporeg(): %02X %02X %02X %02X\n", dev->gporeg[0], dev->gporeg[1], dev->gporeg[2], dev->gporeg[3]);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
acpi_set_timer32(acpi_t *dev, uint8_t timer32)
|
||||
{
|
||||
dev->timer32 = timer32;
|
||||
|
||||
if (!dev->timer32)
|
||||
dev->timer_val &= 0x00ffffff;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
acpi_set_slot(acpi_t *dev, int slot)
|
||||
{
|
||||
dev->slot = slot;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
acpi_set_irq_mode(acpi_t *dev, int irq_mode)
|
||||
{
|
||||
dev->irq_mode = irq_mode;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
acpi_set_irq_pin(acpi_t *dev, int irq_pin)
|
||||
{
|
||||
dev->irq_pin = irq_pin;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
acpi_set_nvr(acpi_t *dev, nvr_t *nvr)
|
||||
{
|
||||
dev->nvr = nvr;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
acpi_reset(void *priv)
|
||||
{
|
||||
acpi_t *dev = (acpi_t *) priv;
|
||||
|
||||
dev->timer_val = 0;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
acpi_speed_changed(void *priv)
|
||||
{
|
||||
acpi_t *dev = (acpi_t *) priv;
|
||||
|
||||
timer_disable(&dev->timer);
|
||||
timer_set_delay_u64(&dev->timer, ACPICONST);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
acpi_close(void *priv)
|
||||
{
|
||||
acpi_t *dev = (acpi_t *) priv;
|
||||
|
||||
timer_disable(&dev->timer);
|
||||
|
||||
free(dev);
|
||||
}
|
||||
|
||||
|
||||
static void *
|
||||
acpi_init(const device_t *info)
|
||||
{
|
||||
acpi_t *dev;
|
||||
|
||||
dev = (acpi_t *)malloc(sizeof(acpi_t));
|
||||
if (dev == NULL) return(NULL);
|
||||
memset(dev, 0x00, sizeof(acpi_t));
|
||||
|
||||
timer_add(&dev->timer, acpi_timer_count, dev, 0);
|
||||
timer_set_delay_u64(&dev->timer, ACPICONST);
|
||||
|
||||
dev->pmsts |= 0x0100;
|
||||
|
||||
return dev;
|
||||
}
|
||||
|
||||
|
||||
const device_t acpi_device =
|
||||
{
|
||||
"ACPI",
|
||||
DEVICE_PCI,
|
||||
0,
|
||||
acpi_init,
|
||||
acpi_close,
|
||||
acpi_reset,
|
||||
NULL,
|
||||
acpi_speed_changed,
|
||||
NULL,
|
||||
NULL
|
||||
};
|
88
src/include/86box/acpi.h
Normal file
88
src/include/86box/acpi.h
Normal file
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
* 86Box A hypervisor and IBM PC system emulator that specializes in
|
||||
* running old operating systems and software designed for IBM
|
||||
* PC systems and compatibles from 1981 through fairly recent
|
||||
* system designs based on the PCI bus.
|
||||
*
|
||||
* This file is part of the 86Box distribution.
|
||||
*
|
||||
* Implementation of the ISA Bus (de)Bugger expansion card
|
||||
* sold as a DIY kit in the late 1980's in The Netherlands.
|
||||
* This card was a assemble-yourself 8bit ISA addon card for
|
||||
* PC and AT systems that had several tools to aid in low-
|
||||
* level debugging (mostly for faulty BIOSes, bootloaders
|
||||
* and system kernels...)
|
||||
*
|
||||
* Definitions for the ACPI emulation.
|
||||
*
|
||||
*
|
||||
*
|
||||
* Authors: Miran Grca, <mgrca8@gmail.com>
|
||||
*
|
||||
* Copyright 2020 Miran Grca.
|
||||
*/
|
||||
#ifndef ACPI_H
|
||||
# define ACPI_H
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define ACPI_TIMER_FREQ 3579545
|
||||
#define PM_FREQ ACPI_TIMER_FREQ
|
||||
|
||||
#define RSM_STS (1 << 15)
|
||||
#define PWRBTN_STS (1 << 8)
|
||||
|
||||
#define RTC_EN (1 << 10)
|
||||
#define PWRBTN_EN (1 << 8)
|
||||
#define GBL_EN (1 << 5)
|
||||
#define TMROF_EN (1 << 0)
|
||||
|
||||
#define SCI_EN (1 << 0)
|
||||
#define SUS_EN (1 << 13)
|
||||
|
||||
#define ACPI_ENABLE 0xf1
|
||||
#define ACPI_DISABLE 0xf0
|
||||
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint8_t plvl2, plvl3,
|
||||
timer32,
|
||||
gpireg[3], gporeg[4];
|
||||
uint16_t pmsts, pmen,
|
||||
pmcntrl, gpsts,
|
||||
gpen, io_base;
|
||||
int slot,
|
||||
irq_mode, irq_pin;
|
||||
uint32_t pcntrl, glbsts,
|
||||
devsts, glben,
|
||||
glbctl, devctl,
|
||||
timer_val;
|
||||
uint64_t tmr_overflow_time;
|
||||
pc_timer_t timer;
|
||||
nvr_t *nvr;
|
||||
} acpi_t;
|
||||
|
||||
|
||||
/* Global variables. */
|
||||
extern const device_t acpi_device;
|
||||
|
||||
|
||||
/* Functions. */
|
||||
extern void acpi_update_io_mapping(acpi_t *dev, uint32_t base, int chipset_en);
|
||||
extern void acpi_init_gporeg(acpi_t *dev, uint8_t val0, uint8_t val1, uint8_t val2, uint8_t val3);
|
||||
extern void acpi_set_timer32(acpi_t *dev, uint8_t timer32);
|
||||
extern void acpi_set_slot(acpi_t *dev, int slot);
|
||||
extern void acpi_set_irq_mode(acpi_t *dev, int irq_mode);
|
||||
extern void acpi_set_irq_pin(acpi_t *dev, int irq_pin);
|
||||
extern void acpi_set_nvr(acpi_t *dev, nvr_t *nvr);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#endif /*ACPI_H*/
|
Reference in New Issue
Block a user