Skip to content

Commit

Permalink
Begin adding ACPI emulation. Start with an independent memory region …
Browse files Browse the repository at this point in the history
…at top of 4GB RAM where the tables will go. The general design of DOSBox-X does not allow just taking from top of RAM like some BIOSes do
  • Loading branch information
joncampbell123 committed Mar 12, 2024
1 parent af0ec20 commit bfb1bfb
Show file tree
Hide file tree
Showing 3 changed files with 160 additions and 11 deletions.
11 changes: 11 additions & 0 deletions include/bios.h
Original file line number Diff line number Diff line change
Expand Up @@ -325,6 +325,17 @@ class ISAPnPDevice {
size_t alloc_sz = 0;
};

extern bool ACPI_enabled;
extern uint32_t ACPI_BASE;
extern uint32_t ACPI_REGION_SIZE; // power of 2
extern uint32_t ACPI_version;
extern unsigned char *ACPI_buffer;
extern size_t ACPI_buffer_size;

void ACPI_mem_enable(const bool enable);
void ACPI_free();
bool ACPI_init();

/* abc = ASCII letters of the alphabet
* defg = hexadecimal digits */
#define ISAPNP_ID(a,b,c,d,e,f,g) \
Expand Down
100 changes: 99 additions & 1 deletion src/hardware/memory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,103 @@
#include "../gamelink/gamelink.h"
#endif // C_GAMELINK

// ACPI memory region allocation.
// Most ACPI BIOSes actually use some region at top of memory, but the
// design of DOSBox-X doesn't make that possible, so the ACPI tables are
// written to a high memory region just below the top 4GB region and the
// RSD PTR in the legacy BIOS region (0xE0000-0xFFFFF) will point at that.
// A memory address is chosen, which must be maintained once tables are
// generated because tables point at each other by physical memory address.
// A fixed size region is chosen within which the tables are written.
//
// NTS: ACPI didn't happen until the Pentium era when it became quite rare
// for CPUs to have less than 32 address bits. No 26-bit 486SX limits
// here. For this reason, ACPI is not supported unless all 32 address
// bits are enabled.
bool ACPI_enabled = false;
bool acpi_mem_setup = false;
uint32_t ACPI_BASE=0;
uint32_t ACPI_REGION_SIZE=0; // power of 2
uint32_t ACPI_version=0;
unsigned char *ACPI_buffer=NULL;
size_t ACPI_buffer_size=0;

class ACPIPageHandler : public PageHandler {
public:
ACPIPageHandler() : PageHandler(PFLAG_NOCODE) {}
ACPIPageHandler(Bitu flags) : PageHandler(flags) {}
uint8_t readb(PhysPt addr) {
if (ACPI_buffer != NULL) {
const PhysPt rel = addr - ACPI_BASE;
if (rel < ACPI_buffer_size) return ACPI_buffer[rel];
}

return 0xFF;
}
void writeb(PhysPt addr,uint8_t val){
LOG(LOG_CPU,LOG_ERROR)("Write %x to acpi at %x",(int)val,(int)addr);
}
void writew(PhysPt addr,uint16_t val){
LOG(LOG_CPU,LOG_ERROR)("Write %x to acpi at %x",(int)val,(int)addr);
}
void writed(PhysPt addr,uint32_t val){
LOG(LOG_CPU,LOG_ERROR)("Write %x to acpi at %x",(int)val,(int)addr);
}
};

static ACPIPageHandler acpi_mem_handler;

PageHandler* acpi_memio_cb(MEM_CalloutObject &co,Bitu phys_page) {
(void)co;//UNUSED
(void)phys_page;//UNUSED

if (ACPI_buffer != NULL && ACPI_REGION_SIZE != 0 && phys_page >= (ACPI_BASE/4096) && phys_page < ((ACPI_BASE+ACPI_REGION_SIZE)/4096))
return &acpi_mem_handler;

return NULL;
}

void MEM_ResetPageHandler_Unmapped(Bitu phys_page, Bitu pages);

void ACPI_mem_enable(const bool enable) {
if (enable && !acpi_mem_setup) {
if (ACPI_BASE != 0 && ACPI_REGION_SIZE != 0) {
MEM_SetPageHandler( ACPI_BASE/4096, ACPI_REGION_SIZE/4096, &acpi_mem_handler );
acpi_mem_setup = true;
PAGING_ClearTLB();
}
}
else if (!enable && acpi_mem_setup) {
if (ACPI_BASE != 0 && ACPI_REGION_SIZE != 0) {
MEM_ResetPageHandler_Unmapped( ACPI_BASE/4096, ACPI_REGION_SIZE/4096 );
acpi_mem_setup = false;
PAGING_ClearTLB();
}
}
}

void ACPI_free() {
if (ACPI_buffer != NULL) {
delete[] ACPI_buffer;
ACPI_buffer = NULL;
}
ACPI_buffer_size = 0;
}

bool ACPI_init() {
if (ACPI_buffer == NULL) {
if (ACPI_REGION_SIZE == 0 || ACPI_REGION_SIZE > (8ul << 20ull))
return false;

ACPI_buffer_size = ACPI_REGION_SIZE;
ACPI_buffer = new unsigned char [ACPI_buffer_size];
if (ACPI_buffer == NULL)
return false;
}

return (ACPI_buffer != NULL);
}

static MEM_Callout_t lfb_mem_cb = MEM_Callout_t_none;
static MEM_Callout_t lfb_mmio_cb = MEM_Callout_t_none;

Expand Down Expand Up @@ -156,7 +253,7 @@ class IllegalPageHandler : public PageHandler {

class RAMPageHandler : public PageHandler {
public:
RAMPageHandler() : PageHandler(PFLAG_READABLE|PFLAG_WRITEABLE) {}
RAMPageHandler() : PageHandler(PFLAG_READABLE|PFLAG_WRITEABLE|PFLAG_NOCODE) {}
RAMPageHandler(Bitu flags) : PageHandler(flags) {}
HostPt GetHostReadPt(Bitu phys_page) {
if (!a20_fast_changeable || (phys_page & (~0xFul/*64KB*/)) == 0x100ul/*@1MB*/)
Expand Down Expand Up @@ -1770,6 +1867,7 @@ void ShutDownRAM(Section * sec) {
#endif
MemBase = NULL;
}
ACPI_free();
}

void MEM_InitCallouts(void) {
Expand Down
60 changes: 50 additions & 10 deletions src/ints/bios.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7970,24 +7970,51 @@ class BIOS:public Module_base{
# endif
#endif

ACPI_mem_enable(false);
ACPI_REGION_SIZE = 0;
ACPI_BASE = 0;
ACPI_enabled = false;
ACPI_version = 0;
ACPI_free();

/* If we're here because of a JMP to F000:FFF0 from a DOS program, then
* an actual reset is needed to prevent reentrancy problems with the DOS
* kernel shell. The WINNT.EXE install program for Windows NT/2000/XP
* likes to restart the program by JMPing to F000:FFF0 */
if (!dos_kernel_disabled && first_shell != NULL) {
LOG(LOG_MISC,LOG_DEBUG)("BIOS POST: JMP to F000:FFF0 detected, initiating proper reset");
throw int(9);
}

{
Section_prop * section=static_cast<Section_prop *>(control->GetSection("dosbox"));
int val = section->Get_int("reboot delay");

if (val < 0)
val = IS_PC98_ARCH ? 1000 : 500;
}

reset_post_delay = (unsigned int)val;
}
{
Section_prop * section=static_cast<Section_prop *>(control->GetSection("dosbox"));
int val = section->Get_int("reboot delay");

if (val < 0)
val = IS_PC98_ARCH ? 1000 : 500;

reset_post_delay = (unsigned int)val;

/* Read the ACPI setting and decide on a ACPI region to use */
{
std::string s = section->Get_string("acpi");

if (IS_PC98_ARCH) {
/* do not enable ACPI, PC-98 does not have it */
}
else if (MEM_get_address_bits() < 32) {
/* I doubt any 486DX systems with less than 32 address bits has ACPI */
}
else if (s == "1.0") {
ACPI_version = 0x100;
ACPI_REGION_SIZE = (256u << 10u); // 256KB
}
else if (s == "1.0b") {
ACPI_version = 0x10B;
ACPI_REGION_SIZE = (256u << 10u); // 256KB
}
}
}

if (bios_post_counter != 0 && reset_post_delay != 0) {
/* reboot delay, in case the guest OS/application had something to day before hitting the "reset" signal */
Expand Down Expand Up @@ -8261,6 +8288,19 @@ class BIOS:public Module_base{
bios_has_exec_vga_bios = false;
LOG(LOG_MISC,LOG_DEBUG)("BIOS: executing POST routine");

if (ACPI_REGION_SIZE != 0) {
// place it just below the mirror of the BIOS at FFFF0000
ACPI_BASE = 0xFFFF0000 - ACPI_REGION_SIZE;

LOG(LOG_MISC,LOG_DEBUG)("ACPI: Setting up version %u.%02x at 0x%lx-0x%lx",
ACPI_version>>8,ACPI_version&0xFF,
(unsigned long)ACPI_BASE,(unsigned long)(ACPI_BASE+ACPI_REGION_SIZE-1lu));

ACPI_init();
ACPI_mem_enable(true);
memset(ACPI_buffer,0,ACPI_buffer_size);
}

// TODO: Anything we can test in the CPU here?

// initialize registers
Expand Down

0 comments on commit bfb1bfb

Please sign in to comment.