New upstream version 2.0pre9.2
Some checks failed
Master / Scheduled (FULL) (push) Has been cancelled
Master / Triggered (push) Has been cancelled
Master / Triggered (ASAN) (push) Has been cancelled
Master / Triggered (FULL) (push) Has been cancelled

This commit is contained in:
geos_one
2025-08-10 12:35:43 +02:00
commit 91736529d5
1056 changed files with 370820 additions and 0 deletions

7
src/base/core/Makefile Normal file
View File

@@ -0,0 +1,7 @@
top_builddir=../../..
include $(top_builddir)/Makefile.conf
CFILES = dyndeb.c int.c hlt.c emu.c ports.c coopth.c dump.c lowmem.c priv.c \
vint.c
include $(REALTOPDIR)/src/Makefile.common

1541
src/base/core/coopth.c Normal file

File diff suppressed because it is too large Load Diff

161
src/base/core/dump.c Normal file
View File

@@ -0,0 +1,161 @@
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <ctype.h>
#include <string.h>
#include "memory.h"
#include "emu.h"
#include "cpu.h"
#include "port.h"
#include "emu-ldt.h"
#include "emudpmi.h"
#include "int.h"
#include "mapping.h"
#ifdef X86_EMULATOR
#include "cpu-emu.h"
#endif
#include "dis8086.h"
char *emu_disasm(unsigned int ip)
{
static char buf[2048];
#ifdef USE_MHPDBG
char frmtbuf[1024];
int rc, i;
unsigned int cp;
char *p;
unsigned int refseg;
unsigned int ref;
cp = SEGOFF2LINEAR(_CS, _IP);
refseg = SREG(cs);
rc = dis_8086(cp, frmtbuf, 0, &ref, refseg * 16);
p = buf;
for (i=0; i<rc && i<8; i++) {
p += sprintf(p, "%02x", READ_BYTE(cp+i));
}
sprintf(p,"%20s", " ");
sprintf(buf+20, "%04x:%04x %s", SREG(cs), LWORD(eip), frmtbuf);
#else
buf[0] = '\0';
#endif
return buf;
}
/* */
/* show_regs,show_ints @@@ 32768 MOVED_CODE_BEGIN @@@ 01/23/96, ./src/emu-i386/cpu.c --> src/base/misc/dump.c */
void show_regs(void)
{
int i;
unsigned int sp, cp;
cp = SEGOFF2LINEAR(_CS, _IP);
if (cp < 1024) {
dbug_printf("Ain't gonna do it, cs=0x%x, eip=0x%x\n",SREG(cs),LWORD(eip));
return;
}
if (!LWORD(esp))
sp = SEGOFF2LINEAR(_SS, _SP) + 0x8000;
else
sp = SEGOFF2LINEAR(_SS, _SP);
dbug_printf("Real-mode state dump:\n");
dbug_printf("EIP: %04x:%08x", SREG(cs), REG(eip));
dbug_printf(" ESP: %04x:%08x", SREG(ss), REG(esp));
#if 1
dbug_printf(" VFLAGS(b): ");
for (i = (1 << 0x14); i > 0; i = (i >> 1)) {
dbug_printf((vflags & i) ? "1" : "0");
if (i & 0x10100) dbug_printf(" ");
}
#else
dbug_printf(" VFLAGS(b): ");
for (i = (1 << 0x11); i > 0; i = (i >> 1))
dbug_printf((vflags & i) ? "1" : "0");
#endif
dbug_printf("\nEAX: %08x EBX: %08x ECX: %08x EDX: %08x VFLAGS(h): %08lx",
REG(eax), REG(ebx), REG(ecx), REG(edx), (unsigned long)vflags);
dbug_printf("\nESI: %08x EDI: %08x EBP: %08x",
REG(esi), REG(edi), REG(ebp));
dbug_printf(" DS: %04x ES: %04x FS: %04x GS: %04x\n",
SREG(ds), SREG(es), SREG(fs), SREG(gs));
/* display vflags symbolically...the #f "stringizes" the macro name */
#define PFLAG(f) if (REG(eflags)&(f)) dbug_printf(#f" ")
dbug_printf("FLAGS: ");
PFLAG(CF);
PFLAG(PF);
PFLAG(AF);
PFLAG(ZF);
PFLAG(SF);
PFLAG(TF);
PFLAG(IF);
PFLAG(DF);
PFLAG(OF);
PFLAG(NT);
PFLAG(RF);
PFLAG(VM);
PFLAG(AC);
PFLAG(VIF);
PFLAG(VIP);
dbug_printf(" IOPL: %u\n", (unsigned) ((vflags & IOPL_MASK) >> 12));
/* display the 10 bytes before and after CS:EIP. the -> points
* to the byte at address CS:EIP
*/
if (sp < 0xa0000 && sp > 10) {
dbug_printf("STACK: ");
sp -= 10;
for (i = 0; i < 10; i++)
dbug_printf("%02x ", READ_BYTE(sp++));
dbug_printf("-> ");
for (i = 0; i < 10; i++)
dbug_printf("%02x ", READ_BYTE(sp++));
dbug_printf("\n");
}
dbug_printf("OPS : ");
cp -= 10;
for (i = 0; i < 10; i++)
dbug_printf("%02x ", READ_BYTE(cp++));
dbug_printf("-> ");
for (i = 0; i < 10; i++)
dbug_printf("%02x ", READ_BYTE(cp++));
dbug_printf("\n\t%s\n", emu_disasm(0));
}
void
show_ints(int min, int max)
{
int i, b;
max = (max - min) / 3;
for (i = 0, b = min; i <= max; i++, b += 3) {
g_printf("%02x| %04x:%04x->%06x ", b, ISEG(b), IOFF(b),
IVEC(b));
g_printf("%02x| %04x:%04x->%06x ", b + 1, ISEG(b + 1), IOFF(b + 1),
IVEC(b + 1));
g_printf("%02x| %04x:%04x->%06x\n", b + 2, ISEG(b + 2), IOFF(b + 2),
IVEC(b + 2));
}
}
#if MAX_SELECTORS != 8192
#error MAX_SELECTORS needs to be 8192
#endif
#define IsSegment32(s) dpmi_segment_is32(s)
void dump_state(void)
{
cpuctx_t *scp = dpmi_get_scp();
show_regs();
if (scp)
dbug_printf("\nProtected-mode state dump:\n%s\n", DPMI_show_state(scp));
}

298
src/base/core/dyndeb.c Normal file
View File

@@ -0,0 +1,298 @@
/* dynamic debug handlers - by Tim Bird */
/* modified to support debug levels -- peak */
/* Rehash so we aren't changing the code all of the time. Eric Biederman */
#include <string.h>
#include <stdlib.h>
#include "emu.h"
#include "dosemu_debug.h"
#include "dosemu_config.h"
#include "init.h"
#include "int.h"
#include "port.h"
FILE *dbg_fd;
#ifdef DONT_DEBUG_BOOT
static struct debug_class debug_save[DEBUG_CLASSES];
#endif
static struct debug_class debug[DEBUG_CLASSES];
unsigned char debug_levels[DEBUG_CLASSES];
int shut_debug;
#ifndef NO_DEBUGPRINT_AT_ALL
int register_debug_class(int letter, void (*change_level)(int level), const char *help_text)
{
struct debug_class *cls;
if (letter >= DEBUG_CLASSES) {
abort();
}
if ((letter >= '0') && (letter <= '9')) {
abort();
}
cls = &debug[letter];
if (cls->letter) {
abort();
}
cls->letter = letter;
cls->change_level = change_level;
cls->help_text = help_text;
debug_levels[letter] = 0;
return 0;
}
int unregister_debug_class(int letter)
{
struct debug_class *cls;
if (letter >= DEBUG_CLASSES) {
return -1;
}
cls = &debug[letter];
if (!cls->letter) {
return -1;
}
memset(cls, 0, sizeof(*cls));
return 0;
}
int set_debug_level(int letter, int level)
{
struct debug_class *cls;
if (letter >= DEBUG_CLASSES) {
return -1;
}
cls = &debug[letter];
if (!cls->letter) {
return -1;
}
if (debug_levels[letter] != level) {
debug_levels[letter] = level;
if (cls->change_level) {
cls->change_level(level);
}
}
return 0;
}
/*
* DANG_BEGIN_FUNCTION parse_debugflags
*
* arguments:
* s - string of options.
*
* description:
* This part is fairly flexible...you specify the debugging
* flags you wish with -D string. The string consists of the following
* characters: + turns the following options on (initial state) -
* turns the following options off a turns all the options on/off,
* depending on whether +/- is set 0-9 sets debug levels (0 is off, 9 is
* most verbose) # where # is a letter from the valid option list (see
* docs), turns that option off/on depending on the +/- state.
*
* Any option letter can occur in any place. Even meaningless combinations,
* such as "01-a-1+0vk" will be parsed without error, so be careful. Some
* options are set by default, some are clear. This is subject to my whim.
* You can ensure which are set by explicitly specifying.
*
* DANG_END_FUNCTION
*/
int parse_debugflags(const char *s, unsigned char flag)
{
char c;
int ret = 0;
dbug_printf("debug flags: %s\n", s);
while ((c = *(s++)))
switch (c) {
case '+': /* begin options to turn on */
if (!flag)
flag = 1;
break;
case '-': /* begin options to turn off */
flag = 0;
break;
case '0'...'9': /* set debug level, 0 is off, 9 is most
* verbose */
flag = c - '0';
break;
default:
ret = set_debug_level(c, flag);
if (ret >= 0) {
ret = 0;
} else {
fprintf(stderr, "Unknown debug-msg mask: %c\n\r", c);
dbug_printf("Unknown debug-msg mask: %c\n", c);
ret = 1;
}
}
return ret;
}
int SetDebugFlagsHelper(char *debugStr)
{
return parse_debugflags(debugStr, 0);
}
static char DebugFlag(int f)
{
if (f == 0)
return '-';
else if (f >= 2 && f <= 9)
return f + '0';
else
return '+';
}
int GetDebugFlagsHelper(char *debugStr, int print)
{
int i;
struct debug_class *cls;
if (print) dbug_printf("GetDebugFlagsHelper\n");
if (print) dbug_printf("debugStr at %p\n", debugStr);
i = 0;
for(cls = debug; cls <= &debug[DEBUG_CLASSES-1]; cls++) {
if (!cls->letter) {
continue;
}
debugStr[i++] = DebugFlag(debug_levels[cls->letter]);
debugStr[i++] = cls->letter;
}
debugStr[i++] = '\0';
if (print) dbug_printf("debugStr is %s\n", debugStr);
return (0);
}
int GetDebugInfoHelper(char *buf, int bufsize)
{
struct debug_class *cls;
int num = 0, col;
char ws;
for (cls = debug, col = 0; cls <= &debug[DEBUG_CLASSES - 1]; cls++) {
if (!cls->letter)
continue;
if (col++ % 3 != 0)
ws = ' ';
else
ws = '\n';
num += snprintf(buf + num, bufsize - num, "%c%c%c: %-21s", ws,
DebugFlag(debug_levels[cls->letter]), cls->letter, cls->help_text);
if (num >= bufsize) // snprintf output was truncated
return 0;
}
num += snprintf(buf + num, bufsize - num, "\n");
if (num >= bufsize) // snprintf output was truncated
return 0;
return num;
}
void print_debug_usage(FILE *stream)
{
struct debug_class *cls;
int i;
fprintf(stream,
" -D set debug-msg mask to flags {+-}{0-9}{");
for(cls = debug; cls <= &debug[DEBUG_CLASSES-1]; cls++) {
if (cls->letter) {
putc(cls->letter, stream);
}
}
fprintf(stream, "}\n");
i = 0;
for(cls = debug; cls <= &debug[DEBUG_CLASSES-1]; cls++) {
if (!cls->letter) {
continue;
}
if ((i & 1) == 0) {
fprintf(stream, " ");
}
fprintf(stream, " %c=%-33.33s",
cls->letter, cls->help_text);
if ((i & 1) == 1) {
fprintf(stream, "\n");
}
i++;
}
if ((i & 1) == 1) {
fprintf(stream, "\n");
}
}
static void all_change_level(int level)
{
int c;
for (c = 0; c < DEBUG_CLASSES; c++) {
if (c != 'a') {
set_debug_level(c, level);
}
}
}
static void int21_change_level(int level)
{
}
static void port_trace_change_level(int level)
{
init_port_traceing();
}
static void config_change_level(int level)
{
if (config_check_only && !level) {
set_debug_level('c', 1);
}
}
CONSTRUCTOR(static void init(void))
{
register_debug_class('#', 0, "default int");
register_debug_class('A', 0, "ASPI");
register_debug_class('B', 0, "dosdebug trace");
register_debug_class('C', 0, "CDROM");
register_debug_class('D', int21_change_level, "dos int 21h");
register_debug_class('E', 0, "EMS");
register_debug_class('F', 0, "MMIO trace");
register_debug_class('I', 0, "IPC");
register_debug_class('N', 0, "NE2000 emulation");
register_debug_class('P', 0, "Packet driver");
register_debug_class('Q', 0, "Mapping driver");
register_debug_class('R', 0, "disk READ");
register_debug_class('S', 0, "SOUND");
register_debug_class('T', port_trace_change_level, "I/O trace");
register_debug_class('M', 0, "DPMI");
register_debug_class('W', 0, "disk WRITE");
register_debug_class('X', 0, "X support");
register_debug_class('Z', 0, "PCI");
register_debug_class('a', all_change_level, "Set all levels");
register_debug_class('c', config_change_level, "configuration");
register_debug_class('d', 0, "disk msgs");
#ifdef X86_EMULATOR
register_debug_class('e', 0, "cpu-emu");
#endif
register_debug_class('g', 0, "general messages");
register_debug_class('h', 0, "hardware");
register_debug_class('i', 0, "I/O instructions");
register_debug_class('j', 0, "joystick");
register_debug_class('k', 0, "keyboard");
register_debug_class('m', 0, "mouse");
register_debug_class('p', 0, "printer");
register_debug_class('n', 0, "IPX network");
register_debug_class('r', 0, "PIC request");
register_debug_class('s', 0, "serial");
#ifdef TRACE_DPMI
register_debug_class('t', 0, "dpmi trace");
#endif
register_debug_class('v', 0, "video");
register_debug_class('w', 0, "warnings");
register_debug_class('x', 0, "XMS");
};
#endif /* ! NO_DEBUGPRINT_AT_ALL */

582
src/base/core/emu.c Normal file
View File

@@ -0,0 +1,582 @@
/*
* Extensions by Robert Sanders, 1992-93
*
* DANG_BEGIN_MODULE
*
* REMARK
* Here is where DOSEMU gets booted. From emu.c external calls are made to
* the specific I/O systems (video/keyboard/serial/etc...) to initialize
* them. Memory is cleared/set up and the boot sector is read from the
* boot drive. Many SIGNALS are set so that DOSEMU can exploit things like
* timers, I/O signals, illegal instructions, etc... When every system
* gives the green light, vm86() is called to switch into vm86 mode and
* start executing i86 code.
*
* The vm86() function will return to DOSEMU when certain `exceptions` occur
* as when some interrupt instructions occur (0xcd).
*
* The top level function emulate() is called from dos.c by way of a dll
* entry point.
*
* /REMARK
* DANG_END_MODULE
*
*/
/*
* DANG_BEGIN_REMARK
* DOSEMU must not work within the 1 meg DOS limit, so
* start of code is loaded at a higher address, at some time this could
* conflict with other shared libs. If DOSEMU is compiled statically
* (without shared libs), and org instruction is used to provide the jump
* above 1 meg.
* DANG_END_REMARK
*/
#include <stdio.h>
#include <termios.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdarg.h>
#include <errno.h>
#ifndef EDEADLOCK
#define EDEADLOCK EDEADLK
#endif
#include <string.h>
#include <ctype.h>
#include <fcntl.h>
#include <time.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <limits.h>
#include <assert.h>
#include <locale.h>
#include <pthread.h>
#include "version.h"
#include "memory.h"
#include "emu.h"
#include "mhpdbg.h"
#include "bios.h"
#include "video.h"
#include "timers.h"
#include "cmos.h"
#include "mouse.h"
#include "disks.h"
#include "xms.h"
#include "ipx.h" /* TRB - add support for ipx */
#include "serial.h"
#include "int.h"
#include "bitops.h"
#include "pic.h"
#include "emudpmi.h"
#include "priv.h" /* for priv_init */
#include "port.h" /* for port_init */
#include "pci.h"
#include "speaker.h"
#include "utilities.h"
#include "dos2linux.h"
#include "iodev.h"
#include "mapping.h"
#include "dosemu_config.h"
#include "libpacket.h"
#include "ne2000.h"
#include "dma.h"
#include "hlt.h"
#include "coopth.h"
#include "keyboard/keyb_server.h"
#include "sig.h"
#include "sound.h"
#include "ioselect.h"
#ifdef X86_EMULATOR
#include "cpu-emu.h"
#endif
#include "kvm.h"
static int ld_tid;
static int can_leavedos;
static int leavedos_code;
static int leavedos_called;
static pthread_mutex_t ld_mtx = PTHREAD_MUTEX_INITIALIZER;
__TLS union vm86_union vm86u;
volatile __thread int fault_cnt;
volatile int in_vm86;
int terminal_pipe;
int terminal_fd = -1;
int kernel_version_code;
int console_fd = -1;
int mem_fd = -1;
int fatalerr;
int in_leavedos;
pthread_t dosemu_pthread_self;
char * const *dosemu_envp;
FILE *real_stderr;
#define MAX_EXIT_HANDLERS 5
struct exit_hndl {
void (*handler)(void);
};
static struct exit_hndl exit_hndl[MAX_EXIT_HANDLERS];
static int exit_hndl_num;
static void __leavedos_main(int code, int sig);
static void leavedos_thr(void *arg);
static int find_boot_drive(void)
{
int i;
for (i = 0; i < config.fdisks; i++) {
if (disktab[i].boot)
return i;
}
FOR_EACH_HDISK(i,
if (disk_is_bootable(&hdisktab[i]))
return HDISK_NUM(i);
);
return -1;
}
void boot(void)
{
unsigned buffer;
struct disk *dp = NULL;
if (config.try_freedos && config.hdiskboot == -1 &&
config.hdisks > 0 && !disk_is_bootable(&hdisktab[0])) {
c_printf("Applying freedos boot work-around\n");
config.swap_bootdrv = 1;
}
if (config.hdiskboot == -1)
config.hdiskboot = find_boot_drive();
switch (config.hdiskboot) {
case -1:
error("Bootable drive not found, exiting\n");
leavedos(16);
return;
case 0:
if (config.fdisks > 0)
dp = &disktab[0];
else {
error("Drive A: not defined, can't boot!\n");
leavedos(71);
}
break;
case 1:
{
int d = 1;
if (config.fdisks > 1) {
if (config.swap_bootdrv) {
struct disk tmp = disktab[1];
disktab[1] = disktab[0];
disktab[0] = tmp;
disktab[0].drive_num = disktab[1].drive_num;
disktab[1].drive_num = tmp.drive_num;
d = 0;
disk_reset();
}
dp = &disktab[d];
} else if (config.fdisks == 1) {
dp = &disktab[0];
} else {
error("Drive B: not defined, can't boot!\n");
leavedos(71);
}
break;
}
default:
{
int d = config.hdiskboot - 2;
struct disk *dd = hdisk_find(d | 0x80);
struct disk *cc = hdisk_find(0x80);
if (config.swap_bootdrv && d && dd) {
dd->drive_num = 0x80;
cc->drive_num = d | 0x80;
config.hdiskboot = 2;
d = 0;
disk_reset();
}
if (dd)
dp = dd;
else {
error("Drive %c not defined, can't boot!\n", d + 'C');
leavedos(71);
}
if (dp->type != DIR_TYPE && dp->drive_num != 0x80) {
error("Boot from drive %c is not possible.\n", d + 'C');
error("@Fix the $_hdimage setting or enable $_swap_bootdrive.\n");
leavedos(72);
}
break;
}
}
disk_close();
disk_open(dp);
buffer = 0x7c00;
if (dp->type == PARTITION) {/* we boot partition boot record, not MBR! */
d_printf("Booting partition boot record from part=%s....\n", dp->dev_name);
if (dos_read(dp->fdesc, buffer, SECTOR_SIZE) != SECTOR_SIZE) {
error("reading partition boot sector using partition %s.\n", dp->dev_name);
leavedos(16);
}
} else if (dp->floppy) {
if (read_sectors(dp, buffer, 0, 1) != SECTOR_SIZE) {
error("can't boot from %s, using harddisk\n", dp->dev_name);
dp = hdisktab;
goto mbr;
}
} else {
if (dp->type == DIR_TYPE) {
if (!disk_is_bootable(dp) || !disk_validate_boot_part(dp)) {
error("Drive unbootable, exiting\n");
leavedos(16);
}
}
mbr:
if (read_mbr(dp, buffer) != SECTOR_SIZE) {
error("can't boot from hard disk\n");
leavedos(16);
}
}
disk_close();
/* put boot drive to dl */
_DX = dp->drive_num;
}
static int c_chk(void)
{
/* return 1 if the context is safe for coopth to do a thread switch */
return !in_dpmi_pm();
}
/*
* DANG_BEGIN_FUNCTION emulate
*
* arguments:
* argc - Argument count.
* argv - Arguments.
*
* description:
* Emulate gets called from dos.c. It initializes DOSEMU to
* prepare it for running in vm86 mode. This involves catching signals,
* preparing memory, calling all the initialization functions for the I/O
* subsystems (video/serial/etc...), getting the boot sector instructions
* and calling vm86().
*
* DANG_END_FUNCTION
*
*/
int main(int argc, char **argv, char * const *envp)
{
dosemu_envp = envp;
setlocale(LC_ALL,"");
srand(time(NULL));
memset(&config, 0, sizeof(config));
/* NOW! it is safe to touch the priv code. */
priv_init(); /* This must come first! */
/* Before we even try to give options to the parser,
* we pre-filter some dangerous options and delete them
* from the arguments list
*/
secure_option_preparse(&argc, argv);
/* the transposal of (config_|stdio_)init allows the addition of -o */
/* to specify a debug out filename, if you're wondering */
port_init(); /* setup port structures, before config! */
version_init(); /* Check the OS version */
config_init(argc, argv); /* parse the commands & config file(s) */
#ifdef X86_EMULATOR
#ifdef DONT_DEBUG_BOOT /* cpuemu only */
memcpy(&debug_save, &debug, sizeof(debug));
set_debug_level('e', 0);
#ifdef TRACE_DPMI
set_debug_level('t', 0);
#endif
#endif
#endif
get_time_init();
print_version(); /* log version information */
memcheck_init();
/* threads can be created only after signal_pre_init() so
* it should be above device_init(), iodev_init(), cpu_setup() etc */
signal_pre_init(); /* initialize sig's & sig handlers */
cpu_setup(); /* setup the CPU */
pci_setup();
device_init(); /* priv initialization of video etc. */
extra_port_init(); /* setup ports dependent on config */
SIG_init(); /* Silly Interrupt Generator */
LibpacketInit(); /* initialize network packet interfaces */
mapping_init(); /* initialize mapping drivers */
if (can_do_root_stuff && !under_root_login) {
g_printf("dropping root privileges\n");
open_kmem();
}
priv_drop();
map_memory_space(); /* maps all DOS memory (low, dpmi, xms...) */
init_hardware_ram(); /* map the direct hardware ram */
map_video_bios(); /* map (really: copy) the video bios */
close_kmem();
/* the following duo have to be done before others who use hlt or coopth */
vm86_hlt_state = hlt_init(BIOS_HLT_BLK_SIZE);
coopth_init();
coopth_set_ctx_checker_vm86(c_chk);
ld_tid = coopth_create("leavedos", leavedos_thr);
coopth_set_ctx_handlers(ld_tid, sig_ctx_prepare, sig_ctx_restore, NULL);
vm86_init();
cputime_late_init();
HMA_init(); /* HMA can only be done now after mapping
is initialized*/
memory_init(); /* initialize the memory contents */
ioselect_init();
/* iodev_init() can load plugins, like SDL, that can spawn a thread.
* This must be done before initializing signals, or problems ensue.
* This also must be done when the signals are blocked, so after
* the signal_pre_init(), which right now blocks the signals. */
iodev_init(); /* initialize devices */
init_all_DOS_tables(); /* longest init function! needs to be optimized */
signal_init(); /* initialize sig's & sig handlers */
if (config.exitearly) {
dbug_printf("Leaving DOS before booting\n");
leavedos(0);
}
g_printf("EMULATE\n");
fflush(stdout);
#ifdef USE_MHPDBG
mhp_debug(DBG_INIT, 0, 0);
#endif
timer_interrupt_init(); /* start sending int 8h int signals */
/* map KVM memory */
if (config.cpu_vm == CPUVM_KVM || config.cpu_vm_dpmi == CPUVM_KVM)
set_kvm_memory_regions();
cpu_reset();
if (config.cpu_vm == CPUVM_KVM)
kvm_enter(0);
can_leavedos = 1;
while (!fatalerr && !config.exitearly) {
loopstep_run_vm86();
}
if (fatalerr) {
sync();
fprintf(stderr, "Not a good day to die!!!!!\n");
}
leavedos(99);
return 0; /* just to make gcc happy */
}
void
dos_ctrl_alt_del(void)
{
SETIVEC(0x19, BIOSSEG, INT_OFF(0x19));
dbug_printf("DOS ctrl-alt-del requested. Rebooting!\n");
if(in_dpmi_pm())
fake_pm_int();
real_run_int(0x19);
}
int register_exit_handler(void (*handler)(void))
{
assert(exit_hndl_num < MAX_EXIT_HANDLERS);
exit_hndl[exit_hndl_num].handler = handler;
exit_hndl_num++;
return 0;
}
static void leavedos_thr(void *arg)
{
dbug_printf("leavedos thread started\n");
/* this may require working vm86() */
video_early_close();
dbug_printf("leavedos thread ended\n");
}
/* "graceful" shutdown */
void __leavedos(int code, int sig, const char *s, int num)
{
int tmp;
dbug_printf("leavedos(%s:%i|%i) called - shutting down\n", s, num, sig);
if (in_leavedos)
{
error("leavedos called recursively, forgetting the graceful exit!\n");
_exit(1);
}
if (!can_leavedos) {
config.exitearly = 1;
return;
}
in_leavedos++;
if (fault_cnt > 0) {
dosemu_error("leavedos() called from within a signal context!\n");
leavedos_main(sig);
return;
}
#ifdef USE_MHPDBG
/* try to notify dosdebug */
mhp_exit_intercept(sig);
#endif
/* try to regain control of keyboard and video first */
keyb_close();
/* abandon current thread if any */
coopth_abandon();
/* switch to RM before closing coopthreads-related stuff */
dpmi_done0();
if (!config.exitearly) { // in exitearly case nothing to join
/* try to clean up threads */
tmp = coopth_flush_vm86();
if (tmp)
dbug_printf("%i threads still active\n", tmp);
coopth_start(ld_tid, NULL);
/* vc switch may require vm86() so call it while waiting for thread */
coopth_join_vm86(ld_tid);
}
__leavedos_main(code, sig);
}
static void __leavedos_main(int code, int sig)
{
int i;
/* async signals must be disabled first or pthread_cancel() hangs on arm */
signal_done();
dpmi_done();
/* now safe to stop io thread */
ioselect_done();
/* then stop device threads, which also stops any remaining vm86() uses */
iodev_term();
#ifdef USE_MHPDBG
g_printf("closing debugger pipes\n");
/* after vm86() is no longer used, we can do this */
mhp_close();
#endif
/* now it is safe to shut down coopth. Can be done any later, if need be */
coopth_done();
dbug_printf("coopthreads stopped\n");
video_close();
if (config.cpu_vm == CPUVM_KVM || config.cpu_vm_dpmi == CPUVM_KVM)
kvm_done();
if (config.speaker == SPKR_EMULATED) {
g_printf("SPEAKER: sound off\n");
speaker_off(); /* turn off any sound */
}
else if (config.speaker==SPKR_NATIVE) {
g_printf("SPEAKER: sound off\n");
/* Since the speaker is native hardware use port manipulation,
* we don't know what is actually implementing the kernel's
* ioctls.
* My port logic is actually stolen from kd_nosound in the kernel.
* --EB 21 September 1997
*/
port_outb(0x61, port_inb(0x61)&0xFC); /* turn off any sound */
}
free(vm86_hlt_state);
SIG_close();
#if defined(X86_EMULATOR)
/* if we are here with config.cpuemu>1 something went wrong... */
if (IS_EMU()) {
leave_cpu_emu();
}
#endif
show_ints(0, 0x33);
g_printf("calling disk_close_all\n");
disk_close_all();
if (config.emuretrace) {
do_r3da_pending ();
set_ioperm (0x3da, 1, 1);
set_ioperm (0x3c0, 1, 1);
config.emuretrace = 0;
}
/* terminate port server */
port_exit();
g_printf("releasing ports and blocked devices\n");
release_ports();
g_printf("calling shared memory exit\n");
g_printf("calling HMA exit\n");
hma_exit();
g_printf("calling mapping_close()\n");
mapping_close();
g_printf("calling close_all_printers\n");
close_all_printers();
for (i = 0; i < exit_hndl_num; i++)
exit_hndl[i].handler();
flush_log();
if (sig < 0)
code = -sig;
else if (sig > 0)
code = sig + 128;
else
code &= 0x7f;
/* We don't need to use _exit() here; this is the graceful exit path. */
exit(code);
}
void __leavedos_main_wrp(int code, int sig, const char *s, int num)
{
dbug_printf("leavedos_main(%s:%i|%i) called - shutting down\n", s, num, sig);
__leavedos_main(code, sig);
}
void leavedos_from_thread(int code)
{
pthread_mutex_lock(&ld_mtx);
leavedos_code = code;
leavedos_called++;
pthread_mutex_unlock(&ld_mtx);
}
void check_leavedos(void)
{
int ld_code, ld_called;
pthread_mutex_lock(&ld_mtx);
ld_code = leavedos_code;
ld_called = leavedos_called;
leavedos_called = 0;
pthread_mutex_unlock(&ld_mtx);
if (ld_called)
leavedos(ld_code);
}
void hardware_run(void)
{
run_sb(); /* Beat Karcher to this one .. 8-) - AM */
keyb_server_run();
rtc_run();
}

180
src/base/core/hlt.c Normal file
View File

@@ -0,0 +1,180 @@
/*
* 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; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include <assert.h>
#include "emu.h"
#include "emudpmi.h"
#include "int.h"
#include "utilities.h"
#include "hlt.h"
#define CONFIG_HLT_TRACE 1
/*
* maximum number of halt handlers.
* you can increase this to anything below 256 since an 8-bit handle
* is used for each device
*/
#define MAX_HLT_HANDLERS 50
struct hlt_handler {
emu_hlt_t h;
Bit16u start_addr;
};
#define MAX_HLT_BLK_SIZE 4096
struct hlt_struct {
struct hlt_handler hlt_handler[MAX_HLT_HANDLERS];
int hlt_handler_id[MAX_HLT_BLK_SIZE];
int hlt_handler_count;
int hlt_block_size;
};
/*
* This is the default HLT handler for the HLT block -- assume that
* someone did a CALLF to get to us.
*/
static void hlt_default(Bit16u addr, HLT_ARG(arg))
{
if (in_dpmi_pm()) {
dosemu_error("HLT: DPMI hlt_default(0x%04x) called, exiting\n", addr);
leavedos(2);
} else {
/* 32rtm doesn't shut down mouse driver properly, so it may
* execute the no longer valid realmode callback.
* See https://github.com/dosemu2/dosemu2/issues/1563 */
error_once("HLT: vm86 hlt_default(0x%04x) called, trying retf\n", addr);
warn("HLT: vm86 hlt_default(0x%04x) called, trying retf\n", addr);
fake_retf();
}
}
/*
* DANG_BEGIN_FUNCTION hlt_init(void)
*
* description:
* Resets all the HLT handlers
*
* DANG_END_FUNCTION
*/
void *hlt_init(int size)
{
struct hlt_struct *state;
int i;
state = malloc(sizeof(*state));
state->hlt_handler[0].h.func = hlt_default;
state->hlt_handler[0].h.name = "Unmapped HLT instruction";
state->hlt_handler_count = 1;
assert(size <= MAX_HLT_BLK_SIZE);
for (i = 0; i < size; i++)
state->hlt_handler_id[i] = 0; /* unmapped HLT handler */
state->hlt_block_size = size;
return state;
}
/*
* DANG_BEGIN_FUNCTION hlt_handle()
*
* description:
* Handles a HLT instruction reached inside the dos emulator.
*
* DANG_END_FUNCTION
*/
int hlt_handle(void *arg, Bit16u offs, void *arg2)
{
struct hlt_struct *state = arg;
struct hlt_handler *hlt = &state->hlt_handler[state->hlt_handler_id[offs]];
#if CONFIG_HLT_TRACE > 0
h_printf("HLT: fcn 0x%04x called in HLT block, handler: %s +%#x\n", offs,
hlt->h.name, offs - hlt->start_addr);
#endif
hlt->h.func(offs - hlt->start_addr, arg2, hlt->h.arg);
return hlt->h.ret;
}
/*
* Register a HLT handler.
*/
Bit16u hlt_register_handler(void *arg, emu_hlt_t handler)
{
struct hlt_struct *state = arg;
int handle, i, j;
Bit16u start_addr = -1;
/* initialization check */
assert(state->hlt_handler_count);
if (state->hlt_handler_count >= MAX_HLT_HANDLERS) {
error("HLT: too many HLT handlers, increase MAX_HLT_HANDLERS\n");
config.exitearly = 1;
return -1;
}
for (i = 0; i + handler.len <= state->hlt_block_size; i++) {
for (j = 0; j < handler.len; j++) {
if (state->hlt_handler_id[i + j]) {
i += j;
break;
}
}
/* see if found free block */
if (j == handler.len) {
start_addr = i;
break;
}
}
if (start_addr == (Bit16u)-1) {
error("HLT: Cannot find free block of len %i\n", handler.len);
config.exitearly = 1;
return -1;
}
handle = state->hlt_handler_count++;
state->hlt_handler[handle].h = handler;
state->hlt_handler[handle].start_addr = start_addr;
/* change table to reflect new handler id for that address */
for (j = 0; j < handler.len; j++)
state->hlt_handler_id[start_addr + j] = handle;
h_printf("HLT: registered %s at %#x,%i\n",
handler.name, start_addr, handler.len);
return start_addr;
}
int hlt_unregister_handler(void *arg, Bit16u start_addr)
{
struct hlt_struct *state = arg;
int handle, i;
emu_hlt_t *h;
assert(start_addr < state->hlt_block_size);
handle = state->hlt_handler_id[start_addr];
if (!handle)
return -1;
h = &state->hlt_handler[handle].h;
for (i = 0; i < h->len; i++)
state->hlt_handler_id[start_addr + i] = 0;
h->func = hlt_default;
while (state->hlt_handler_count &&
state->hlt_handler[state->hlt_handler_count - 1].h.func == hlt_default)
state->hlt_handler_count--;
return 0;
}

3916
src/base/core/int.c Normal file

File diff suppressed because it is too large Load Diff

178
src/base/core/lowmem.c Normal file
View File

@@ -0,0 +1,178 @@
/*
* 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; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
/*
* Author: Stas Sergeev <stsp@users.sourceforge.net>
*
* Management for the static 32K heap in a low memory.
* Used by various dosemu internal subsystems.
*/
#include <signal.h>
#include <assert.h>
#include "emu.h"
#include "memory.h"
#include "smalloc.h"
#include "utilities.h"
#include "emudpmi.h"
#include "lowmem.h"
static smpool mp;
unsigned char *dosemu_lmheap_base;
static void *rm_stack;
#define RM_STACK_SIZE 0x200
static void do_sm_error(int prio, const char *fmt, ...)
{
char buf[1024];
va_list al;
va_start(al, fmt);
vsnprintf(buf, sizeof(buf), fmt, al);
va_end(al);
if (prio)
dosemu_error("%s\n", buf);
else
dbug_printf("%s\n", buf);
}
int lowmem_init(void)
{
dosemu_lmheap_base = MK_FP32(DOSEMU_LMHEAP_SEG, DOSEMU_LMHEAP_OFF);
sminit(&mp, dosemu_lmheap_base, DOSEMU_LMHEAP_SIZE);
smregister_error_notifier(&mp, do_sm_error);
return 1;
}
void * lowmem_alloc(int size)
{
char *ptr = smalloc(&mp, size);
if (!ptr) {
error("lowmem_heap: OOM, size=%i\n", size);
leavedos(86);
}
return ptr;
}
void * lowmem_alloc_aligned(int align, int size)
{
char *ptr = smalloc_aligned(&mp, align, size);
if (!ptr) {
error("lowmem_heap: OOM, size=%i\n", size);
leavedos(86);
}
return ptr;
}
void lowmem_free(void *p)
{
smfree(&mp, p);
}
void lowmem_reset(void)
{
lowmem_free(rm_stack);
smfree_all(&mp);
rm_stack = lowmem_alloc(RM_STACK_SIZE);
}
static int in_rm_stack;
static uint16_t rm_sp;
#define MAX_RM_STACKS 10
static uint64_t userval[MAX_RM_STACKS];
int get_rm_stack(Bit16u *ss_p, Bit16u *sp_p, uint64_t cookie)
{
int ret = 0;
assert(in_rm_stack < MAX_RM_STACKS);
userval[in_rm_stack] = cookie;
if (!(in_rm_stack++)) {
rm_sp = DOSEMU_LMHEAP_OFFS_OF(rm_stack) + RM_STACK_SIZE;
*ss_p = DOSEMU_LMHEAP_SEG;
*sp_p = rm_sp;
ret = 1;
}
return ret;
}
uint16_t put_rm_stack(uint64_t *cookie)
{
int ret = 0;
assert(in_rm_stack > 0);
if (!(--in_rm_stack)) {
ret = rm_sp;
}
if (cookie)
*cookie = userval[in_rm_stack];
return ret;
}
/* recursion is _very_unlikely, but define an array */
#define MAX_SAVED_REGS 5
static struct vm86_regs rm_regs_stack[MAX_SAVED_REGS];
static void switch_stack(struct vm86_regs *regs)
{
Bit16u new_ss, new_sp;
int stk;
stk = get_rm_stack(&new_ss, &new_sp, 0);
if (stk) {
regs->ss = new_ss;
regs->esp = new_sp;
}
}
void get_rm_stack_regs(struct vm86_regs *regs, struct vm86_regs *saved_regs)
{
*saved_regs = REGS;
switch_stack(regs);
}
void rm_stack_enter(void)
{
assert(in_rm_stack < MAX_SAVED_REGS);
get_rm_stack_regs(&REGS, &rm_regs_stack[in_rm_stack]);
}
void rm_stack_leave(void)
{
int old_tf = isset_TF();
put_rm_stack(NULL);
REGS = rm_regs_stack[in_rm_stack];
if (old_tf)
set_TF();
}
#define LMHEAP_OFF 0xa000
#define LMHEAP_SIZE 0x4000
static uint16_t lmheap_add(void)
{
return (config.dos_up == 2 ? FDPP_LMHEAP_ADD : 0);
}
uint16_t lmheap_off(void)
{
return LMHEAP_OFF + lmheap_add();
}
uint16_t lmheap_size(void)
{
return LMHEAP_SIZE - lmheap_add();
}

1263
src/base/core/ports.c Normal file

File diff suppressed because it is too large Load Diff

257
src/base/core/priv.c Normal file
View File

@@ -0,0 +1,257 @@
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <grp.h>
#ifdef HAVE_SYS_IO_H
#include <sys/io.h>
#endif
#include "emu.h"
#include "priv.h"
#include "dosemu_config.h"
#include "mapping.h"
#include "utilities.h"
#ifdef X86_EMULATOR
#include "cpu-emu.h"
#endif
#if 0
#define PRIV_TESTING
#endif
/* Some handy information to have around */
static uid_t uid,euid;
static gid_t gid,egid;
static uid_t cur_uid, cur_euid;
static gid_t cur_gid, cur_egid;
static int skip_priv_setting = 0;
int can_do_root_stuff;
int under_root_login;
int using_sudo;
int current_iopl;
#define PRIVS_ARE_ON (euid == cur_euid)
#define PRIVS_ARE_OFF (uid == cur_euid)
#define PRIVS_WERE_ON(privs) (pop_priv(privs))
static void push_priv(saved_priv_status *privs)
{
if (!privs || *privs != PRIV_MAGIC) {
error("Aiiiee... not in-sync saved priv status on push_priv\n");
leavedos(99);
}
*privs = PRIVS_ARE_ON;
#ifdef PRIV_TESTING
c_printf("PRIV: pushing %d privs_ptr=%p\n", *privs, privs);
#endif
}
static int pop_priv(saved_priv_status *privs)
{
int ret;
if (!privs || *privs == PRIV_MAGIC) {
error("Aiiiee... not in-sync saved priv status on pop_priv\n");
leavedos(99);
}
#ifdef PRIV_TESTING
c_printf("PRIV: popping %d privs_ptr=%p\n", *privs, privs);
#endif
ret = (int)*privs;
*privs = PRIV_MAGIC;
return ret;
}
static int _priv_on(void)
{
if (PRIVS_ARE_OFF) { /* make sure the privs need to be changed */
#ifdef PRIV_TESTING
c_printf("PRIV: on-in %d\n", cur_euid);
#endif
if (setreuid(uid,euid)) {
error("Cannot turn privs on!\n");
return 0;
}
cur_uid = uid;
cur_euid = euid;
if (setregid(gid,egid)) {
error("Cannot turn privs on!\n");
return 0;
}
cur_gid = gid;
cur_egid = egid;
}
#ifdef PRIV_TESTING
c_printf("PRIV: on-ex %d\n", cur_euid);
#endif
return 1;
}
static int _priv_off(void)
{
if (PRIVS_ARE_ON) { /* make sure the privs need to be changed */
#ifdef PRIV_TESTING
c_printf("PRIV: off-in %d\n", cur_euid);
#endif
if (setreuid(euid,uid)) {
error("Cannot turn privs off!\n");
return 0;
}
cur_uid = euid;
cur_euid = uid;
if (setregid(egid,gid)) {
error("Cannot turn privs off!\n");
return 0;
}
cur_gid = egid;
cur_egid = gid;
}
#ifdef PRIV_TESTING
c_printf("PRIV: off-ex %d\n", cur_euid);
#endif
return 1;
}
int real_enter_priv_on(saved_priv_status *privs)
{
if (skip_priv_setting) return 1;
push_priv(privs);
return _priv_on();
}
int real_enter_priv_off(saved_priv_status *privs)
{
if (skip_priv_setting) return 1;
push_priv(privs);
return _priv_off();
}
int real_leave_priv_setting(saved_priv_status *privs)
{
if (skip_priv_setting) return 1;
if (PRIVS_WERE_ON(privs)) return _priv_on();
return _priv_off();
}
int priv_iopl(int pl)
{
#ifdef HAVE_SYS_IO_H
int ret;
if (PRIVS_ARE_OFF) {
_priv_on();
ret = iopl(pl);
_priv_off();
}
else ret = iopl(pl);
#ifdef X86_EMULATOR
if (config.cpu_vm == CPUVM_EMU) e_priv_iopl(pl);
#endif
if (ret == 0)
current_iopl = pl;
return ret;
#else
return -1;
#endif
}
uid_t get_cur_uid(void)
{
return cur_uid;
}
uid_t get_cur_euid(void)
{
return cur_euid;
}
gid_t get_cur_egid(void)
{
return cur_egid;
}
uid_t get_orig_uid(void)
{
return uid;
}
uid_t get_orig_euid(void)
{
return euid;
}
gid_t get_orig_gid(void)
{
return gid;
}
int priv_drop(void)
{
if (setreuid(uid,uid) || setregid(gid,gid))
{
error("Cannot drop root uid or gid!\n");
return 0;
}
cur_euid = euid = cur_uid = uid;
cur_egid = egid = cur_gid = gid;
skip_priv_setting = 1;
if (uid) can_do_root_stuff = 0;
return 1;
}
void priv_init(void)
{
const char *sh = getenv("SUDO_HOME"); // theoretical future var
const char *h = getenv("HOME");
uid = cur_uid = getuid();
/* suid bit only sets euid & suid but not uid, sudo sets all 3 */
if (!uid) under_root_login = 1;
euid = cur_euid = geteuid();
if (!euid) can_do_root_stuff = 1;
if (!uid && !euid) skip_priv_setting = 1;
gid = cur_gid = getgid();
egid = cur_egid = getegid();
/* must store the /proc/self/exe symlink contents before dropping
privs! */
dosemu_proc_self_exe = readlink_malloc("/proc/self/exe");
/* For Fedora we must also save a file descriptor to /proc/self/maps */
dosemu_proc_self_maps_fd = open("/proc/self/maps", O_RDONLY | O_CLOEXEC);
if (!sh)
sh = getenv("DOSEMU_SUDO_HOME");
/* see if -E was used */
if (under_root_login && sh && h && strcmp(sh, h) == 0)
{
/* check for sudo and set to original user */
char *s = getenv("SUDO_GID");
if (s) {
gid = cur_gid = atoi(s);
if (gid) {
setregid(gid, egid);
}
}
s = getenv("SUDO_UID");
if (s) {
uid = cur_uid = atoi(s);
if (uid) {
skip_priv_setting = under_root_login = 0;
using_sudo = 1;
s = getenv("SUDO_USER");
if (s) {
initgroups(s, gid);
setenv("USER", s, 1);
}
setreuid(uid, euid);
}
}
}
if (!can_do_root_stuff)
{
skip_priv_setting = 1;
}
if (!skip_priv_setting) _priv_off();
}

181
src/base/core/vint.c Normal file
View File

@@ -0,0 +1,181 @@
/*
* 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; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
/*
* Purpose: virtual interrupt router (another one)
*
* Author: Stas Sergeev.
*
*/
#include <stdint.h>
#include <assert.h>
#include "cpu.h"
#include "int.h"
#include "hlt.h"
#include "memory.h"
#include "port.h"
#include "chipset.h"
#include "emu.h"
#include "vint.h"
#define VINT_MAX 2
static int vi_used;
static uint16_t vint_hlt;
#define ON_PIC1(n) (vih[n].orig_irq >= 8)
struct vihandler {
void (*handler)(int, int);
void (*mask)(int, int);
uint8_t irq;
uint8_t orig_irq;
uint8_t interrupt;
int tweaked;
unsigned tw_flags;
};
struct vihandler vih[VINT_MAX];
static void poll_pic0(uint8_t irq)
{
port_outb(0x20, 0x0c); // OCW3, enter poll mode
port_outb(0x20, irq); // extension, may not work on real PIC
/* see if it worked */
assert(pic_get_isr() & (1 << irq));
}
static void poll_pic1(uint8_t irq)
{
port_outb(0x20, 0x0c); // OCW3, enter poll mode
port_outb(0x20, 2); // extension, may not work on real PIC
port_outb(0xa0, 0x0c);
port_outb(0xa0, irq - 8);
/* see if it worked */
assert((pic_get_isr() & ((1 << irq) | 4)) == ((1 << irq) | 4));
}
static void full_eoi(void)
{
port_outb(0xa0, 0x20);
port_outb(0x20, 0x20);
}
static void do_ret(int vi_num)
{
clear_IF();
vih[vi_num].mask(vi_num, 0);
do_iret();
}
int vint_is_masked(int vi_num, uint8_t *imr)
{
uint16_t real_imr = (imr[1] << 8) | imr[0];
return !!(real_imr & (1 << vih[vi_num].orig_irq));
}
static void vint_handler(uint16_t idx, HLT_ARG(arg))
{
uint8_t imr[2];
int masked;
int vi_num = idx >> 1;
if (idx & 1) {
do_ret(vi_num);
return;
}
imr[0] = port_inb(0x21);
imr[1] = port_inb(0xa1);
masked = vint_is_masked(vi_num, imr);
if (masked) {
h_printf("vint: masked, iret\n");
do_eoi2_iret();
} else {
uint8_t irq = vih[vi_num].orig_irq;
uint16_t port = (irq >= 8 ? PIC1_VECBASE_PORT : PIC0_VECBASE_PORT);
uint8_t inum = port_inb(port) + (irq & 7);
full_eoi();
if (ON_PIC1(vi_num))
poll_pic1(irq);
else
poll_pic0(irq);
if (vih[vi_num].tweaked) {
_IP++; // skip hlt
h_printf("vint: call to inum %x\n", inum);
real_run_int(inum);
vih[vi_num].mask(vi_num, 1);
} else {
h_printf("vint: jump to inum %x\n", inum);
jmp_to(ISEG(inum), IOFF(inum));
}
}
if (vih[vi_num].handler)
vih[vi_num].handler(vi_num, masked);
}
void vint_post_irq_dpmi(int vi_num, int masked)
{
full_eoi();
if (!masked) {
uint8_t irq = vih[vi_num].orig_irq;
if (ON_PIC1(vi_num))
poll_pic1(irq);
else
poll_pic0(irq);
}
}
void vint_init(void)
{
emu_hlt_t hlt_hdlr = HLT_INITIALIZER;
hlt_hdlr.name = "vint";
hlt_hdlr.func = vint_handler;
hlt_hdlr.len = VINT_MAX * 2;
vint_hlt = hlt_register_handler_vm86(hlt_hdlr);
}
void vint_setup(void)
{
int i;
for (i = 0; i < VINT_MAX; i++) {
if (vih[i].interrupt)
SETIVEC(vih[i].interrupt, BIOS_HLT_BLK_SEG, vint_hlt + 2 * i);
}
}
int vint_register(void (*ack_handler)(int, int),
void (*mask_handler)(int, int),
int irq, int orig_irq, int inum)
{
struct vihandler *vi = &vih[vi_used];
assert(vi_used < VINT_MAX);
vi->handler = ack_handler;
vi->mask = mask_handler;
vi->irq = irq;
vi->orig_irq = orig_irq;
vi->interrupt = inum;
return vi_used++;
}
void vint_set_tweaked(int vi_num, int on, unsigned flags)
{
struct vihandler *vi = &vih[vi_num];
assert(vi_num < VINT_MAX);
vi->tweaked = on;
vi->tw_flags = flags;
}