Restore correct ESP value on fault during IRET - fixes issues in at least Windows NT 3.51, NT 4.0, OS/2 2.0, OS/2 Warp. Thanks to SA1988.

Hack to mirror IDE register writes to both drives, fixes IDE issues on NT.
Fixed occasional PCem crash when accessing a faulting page, fixes crash in OS/2 2.0.
This commit is contained in:
TomW 2014-04-09 19:55:14 +01:00
commit 1493306d5e
3 changed files with 50 additions and 21 deletions

View file

@ -86,6 +86,8 @@ static inline uint8_t fastreadb(uint32_t a)
if ((a >> 12) == pccache)
return *((uint8_t *)&pccache2[a]);
pccache2 = getpccache(a);
if (abrt)
return;
pccache = a >> 12;
return *((uint8_t *)&pccache2[a]);
}
@ -101,6 +103,8 @@ static inline uint16_t fastreadw(uint32_t a)
}
if ((a>>12)==pccache) return *((uint16_t *)&pccache2[a]);
pccache2=getpccache(a);
if (abrt)
return;
pccache=a>>12;
return *((uint16_t *)&pccache2[a]);
}

View file

@ -645,6 +645,7 @@ void writeidel(int ide_board, uint32_t val)
void writeide(int ide_board, uint16_t addr, uint8_t val)
{
IDE *ide = &ide_drives[cur_ide[ide_board]];
IDE *ide_other = &ide_drives[cur_ide[ide_board] ^ 1];
#ifndef RPCEMU_IDE
/* if (ide_board && (cr0&1) && !(eflags&VM_FLAG))
{
@ -671,27 +672,35 @@ void writeide(int ide_board, uint16_t addr, uint8_t val)
return;
case 0x1F1: /* Features */
ide->cylprecomp=val;
ide->cylprecomp = val;
ide_other->cylprecomp = val;
return;
case 0x1F2: /* Sector count */
ide->secount=val;
ide->secount = val;
ide_other->secount = val;
return;
case 0x1F3: /* Sector */
ide->sector=val;
ide->lba_addr=(ide->lba_addr&0xFFFFF00)|val;
ide->sector = val;
ide->lba_addr = (ide->lba_addr & 0xFFFFF00) | val;
ide_other->sector = val;
ide_other->lba_addr = (ide_other->lba_addr & 0xFFFFF00) | val;
return;
case 0x1F4: /* Cylinder low */
ide->cylinder=(ide->cylinder&0xFF00)|val;
ide->lba_addr=(ide->lba_addr&0xFFF00FF)|(val<<8);
ide->cylinder = (ide->cylinder & 0xFF00) | val;
ide->lba_addr = (ide->lba_addr & 0xFFF00FF) | (val << 8);
ide_other->cylinder = (ide_other->cylinder&0xFF00) | val;
ide_other->lba_addr = (ide_other->lba_addr&0xFFF00FF) | (val << 8);
// pclog("Write cylinder low %02X\n",val);
return;
case 0x1F5: /* Cylinder high */
ide->cylinder=(ide->cylinder&0xFF)|(val<<8);
ide->lba_addr=(ide->lba_addr&0xF00FFFF)|(val<<16);
ide->cylinder = (ide->cylinder & 0xFF) | (val << 8);
ide->lba_addr = (ide->lba_addr & 0xF00FFFF) | (val << 16);
ide_other->cylinder = (ide_other->cylinder & 0xFF) | (val << 8);
ide_other->lba_addr = (ide_other->lba_addr & 0xF00FFFF) | (val << 16);
return;
case 0x1F6: /* Drive/Head */
@ -720,11 +729,14 @@ void writeide(int ide_board, uint16_t addr, uint8_t val)
ide = &ide_drives[cur_ide[ide_board]];
}
ide->head=val&0xF;
ide->lba=val&0x40;
ide->lba_addr=(ide->lba_addr&0x0FFFFFF)|((val&0xF)<<24);
ide->head = val & 0xF;
ide->lba = val & 0x40;
ide_other->head = val & 0xF;
ide_other->lba = val & 0x40;
ide->lba_addr = (ide->lba_addr & 0x0FFFFFF) | ((val & 0xF) << 24);
ide_other->lba_addr = (ide_other->lba_addr & 0x0FFFFFF)|((val & 0xF) << 24);
ide_irq_update(ide);
return;

View file

@ -1996,7 +1996,7 @@ void pmodeiret(int is32)
// pclog("POP\n");
newpc=POPL();
seg=POPL();
tempflags=POPL(); if (abrt) return;
tempflags=POPL(); if (abrt) { ESP = oldsp; return; }
// if (output) pclog("IRETD pop %08X %08X %08X\n",newpc,seg,tempflags);
if (is386 && ((tempflags>>16)&VM_FLAG))
{
@ -2007,7 +2007,7 @@ void pmodeiret(int is32)
segs[0]=POPL();
segs[1]=POPL();
segs[2]=POPL();
segs[3]=POPL(); if (abrt) return;
segs[3]=POPL(); if (abrt) { ESP = oldsp; return; }
// pclog("Pop stack %04X:%04X\n",newss,newsp);
eflags=tempflags>>16;
loadseg(segs[0],&_es);
@ -2043,13 +2043,14 @@ void pmodeiret(int is32)
{
newpc=POPW();
seg=POPW();
tempflags=POPW(); if (abrt) return;
tempflags=POPW(); if (abrt) { ESP = oldsp; return; }
}
// if (!is386) tempflags&=0xFFF;
// pclog("Returned to %04X:%08X %04X %04X %i\n",seg,newpc,flags,tempflags, ins);
if (!(seg&~3))
{
pclog("IRET CS=0\n");
ESP = oldsp;
// dumpregs();
// exit(-1);
x86gpf(NULL,0);
@ -2063,6 +2064,7 @@ void pmodeiret(int is32)
if (addr>=ldt.limit)
{
pclog("Bigger than LDT limit %04X %04X IRET\n",seg,gdt.limit);
ESP = oldsp;
x86gpf(NULL,seg&~3);
return;
}
@ -2073,6 +2075,7 @@ void pmodeiret(int is32)
if (addr>=gdt.limit)
{
pclog("Bigger than GDT limit %04X %04X IRET\n",seg,gdt.limit);
ESP = oldsp;
x86gpf(NULL,seg&~3);
return;
}
@ -2081,6 +2084,7 @@ void pmodeiret(int is32)
if ((seg&3) < CPL)
{
pclog("IRET to lower level\n");
ESP = oldsp;
x86gpf(NULL,seg&~3);
return;
}
@ -2088,7 +2092,7 @@ void pmodeiret(int is32)
segdat[0]=readmemw(0,addr);
segdat[1]=readmemw(0,addr+2);
segdat[2]=readmemw(0,addr+4);
segdat[3]=readmemw(0,addr+6); cpl_override=0; if (abrt) { ESP=oldsp; return; }
segdat[3]=readmemw(0,addr+6); cpl_override=0; if (abrt) { ESP = oldsp; return; }
// pclog("Seg type %04X %04X\n",segdat[2]&0x1F00,segdat[2]);
switch (segdat[2]&0x1F00)
@ -2097,6 +2101,7 @@ void pmodeiret(int is32)
if ((seg&3) != DPL)
{
pclog("IRET NC DPL %04X %04X %04X %04X %04X\n", seg, segdat[0], segdat[1], segdat[2], segdat[3]);
ESP = oldsp;
// dumpregs();
// exit(-1);
x86gpf(NULL,seg&~3);
@ -2107,12 +2112,14 @@ void pmodeiret(int is32)
if ((seg&3) < DPL)
{
pclog("IRET C DPL\n");
ESP = oldsp;
x86gpf(NULL,seg&~3);
return;
}
break;
default:
pclog("IRET CS != code seg\n");
ESP = oldsp;
x86gpf(NULL,seg&~3);
// dumpregs();
// exit(-1);
@ -2121,7 +2128,7 @@ void pmodeiret(int is32)
if (!(segdat[2]&0x8000))
{
pclog("IRET CS not present %i %04X %04X %04X\n",ins, segdat[0], segdat[1], segdat[2]);
ESP=oldsp;
ESP = oldsp;
x86np("IRET CS not present\n", seg & 0xfffc);
return;
}
@ -2152,12 +2159,12 @@ void pmodeiret(int is32)
if (is32)
{
newsp=POPL();
newss=POPL(); if (abrt) return;
newss=POPL(); if (abrt) { ESP = oldsp; return; }
}
else
{
newsp=POPW();
newss=POPW(); if (abrt) return;
newss=POPW(); if (abrt) { ESP = oldsp; return; }
}
if (output) pclog("IRET load stack %04X:%04X\n",newss,newsp);
@ -2165,6 +2172,7 @@ void pmodeiret(int is32)
if (!(newss&~3))
{
pclog("IRET loading null SS\n");
ESP = oldsp;
x86gpf(NULL,newss&~3);
return;
}
@ -2174,6 +2182,7 @@ void pmodeiret(int is32)
if (addr>=ldt.limit)
{
pclog("Bigger than LDT limit %04X %04X PMODEIRET SS\n",newss,gdt.limit);
ESP = oldsp;
x86gpf(NULL,newss&~3);
return;
}
@ -2184,6 +2193,7 @@ void pmodeiret(int is32)
if (addr>=gdt.limit)
{
pclog("Bigger than GDT limit %04X %04X PMODEIRET\n",newss,gdt.limit);
ESP = oldsp;
x86gpf(NULL,newss&~3);
return;
}
@ -2193,12 +2203,13 @@ void pmodeiret(int is32)
segdat2[0]=readmemw(0,addr);
segdat2[1]=readmemw(0,addr+2);
segdat2[2]=readmemw(0,addr+4);
segdat2[3]=readmemw(0,addr+6); cpl_override=0; if (abrt) { ESP=oldsp; return; }
segdat2[3]=readmemw(0,addr+6); cpl_override=0; if (abrt) { ESP = oldsp; return; }
// pclog("IRET SS sd2 %04X\n",segdat2[2]);
// if (((newss & 3) != DPL) || (DPL2 != DPL))
if ((newss & 3) != (seg & 3))
{
pclog("IRET loading SS with wrong permissions %04X %04X\n", newss, seg);
ESP = oldsp;
// dumpregs();
// exit(-1);
x86gpf(NULL,newss&~3);
@ -2207,19 +2218,21 @@ void pmodeiret(int is32)
if ((segdat2[2]&0x1A00)!=0x1200)
{
pclog("IRET loading SS wrong type\n");
ESP = oldsp;
x86gpf(NULL,newss&~3);
return;
}
if (DPL2 != (seg & 3))
{
pclog("IRET loading SS with wrong permissions2 %i %i %04X %04X\n", DPL2, seg & 3, newss, seg);
ESP=oldsp;
ESP = oldsp;
x86gpf(NULL,newss&~3);
return;
}
if (!(segdat2[2]&0x8000))
{
pclog("IRET loading SS not present\n");
ESP = oldsp;
x86np("IRET loading SS not present\n", newss & 0xfffc);
return;
}