From 883120b9a7184cace402e8a0069ac68dc3ad5d00 Mon Sep 17 00:00:00 2001 From: crazii Date: Fri, 19 Jan 2024 21:22:45 +0800 Subject: [PATCH] IRQ assignment: bugfix and add full switch for 32 bit protected mode. --- mpxplay/au_cards/pcibios.c | 418 ++++++++++++++++++++++--------------- sbemu/platform.h | 45 ++-- 2 files changed, 274 insertions(+), 189 deletions(-) diff --git a/mpxplay/au_cards/pcibios.c b/mpxplay/au_cards/pcibios.c index 9ffa04b1..e828deba 100644 --- a/mpxplay/au_cards/pcibios.c +++ b/mpxplay/au_cards/pcibios.c @@ -347,15 +347,25 @@ void pcibios_enable_interrupt(pci_config_s* ppkey) pcibios_WriteConfig_Word(ppkey, PCIR_PCICMD, cmd); } +#define USE_P32_CALL 1 //32bit protected mode call. if this doesn't work for some PCs, then the final solution should be programming the interrupt router. + +#pragma pack(1) + typedef struct { uint16_t size; + #if USE_P32_CALL + uint32_t off; + #else uint16_t off; + #endif uint16_t seg; + #if !USE_P32_CALL uint16_t padding; + #endif }IRQRoutingOptionBuffer; +_Static_assert(sizeof(IRQRoutingOptionBuffer) == 8, "size error"); -#pragma pack(1) typedef struct { uint8_t bus; @@ -373,11 +383,197 @@ _Static_assert(sizeof(IRQRoutingTable) == 16, "size error"); #include "../../sbemu/dpmi/dbgutil.h" #include "../../sbemu/pic.h" +#ifdef DJGPP #include +#include +#endif //copied & modified from usbddos //https://people.freebsd.org/~jhb/papers/bsdcan/2007/article/node4.html //https://people.freebsd.org/~jhb/papers/bsdcan/2007/article/node5.html //PCI BIOS SPECIFICATION Revision 2.1 +static int pcibios32cs = -1; +static int pcibios32ds = -1; +static uint32_t pcibios32entry = -1; +static int pcibios_inited = 0; + +static void pcibios_FreeLDT(void) +{ + if(pcibios32cs != -1) + __dpmi_free_ldt_descriptor(pcibios32cs); + if(pcibios32ds != -1) + __dpmi_free_ldt_descriptor(pcibios32ds); +} + +void pcibios_CallFunction32(rminfo* r) +{ + if(!pcibios_inited) + { + pcibios_inited = TRUE; + + typedef struct + { + char signature[4]; + uint32_t entry; //physical addr + uint8_t reivision; + uint8_t len; //in paragraph + uint8_t checksum; + uint8_t zero[5]; + }BIOS32SD; + _Static_assert(sizeof(BIOS32SD) == 16, "size error"); + + char* scanarea = (char*)malloc(0xFFFFF-0xE0000); + dosget(scanarea, 0xE0000, 0xFFFFF-0xE0000); + const BIOS32SD* sd = NULL; + int count = (0xFFFFF-0xE0000)/sizeof(BIOS32SD); + for(int i = 0; i < count; ++i) + { + const BIOS32SD* test = (const BIOS32SD*)(scanarea + i * sizeof(BIOS32SD)); + if(memcmp(test->signature,"_32_", 4) == 0 && memcmp(test->zero,"\0\0\0\0\0", 5) == 0 && test->len == 1) + { + const char* bytes = (const char*)test; + uint8_t checksum = 0; + for(int i = 0; i < 16; i++) + checksum += bytes[i]; + if(checksum == 0) + { + sd = test; + break; + } + } + } + + if(sd == NULL) //not found + { + free(scanarea); + r->EAX = PCI_NOT_SUPPORTED<<8; + return; + } + _LOG("Found BIOS32 Service Directory at %x, entry point %x\n", (uintptr_t)sd-(uintptr_t)scanarea + 0xE0000, sd->entry); + + int bios32cs = __dpmi_create_alias_descriptor(_my_cs()); + int bios32ds = __dpmi_create_alias_descriptor(_my_ds()); + __dpmi_set_descriptor_access_rights(bios32cs, __dpmi_get_descriptor_access_rights(bios32cs)|0x08); //code/exec + + uint32_t base = sd->entry&~0xFFF; //although it is physical addr, should be under 1M, phyiscal mapping not needed + __dpmi_set_segment_base_address(bios32cs, base); + __dpmi_set_segment_base_address(bios32ds, base); + __dpmi_set_segment_limit(bios32cs, 8192-1); //spec required 2 pages + __dpmi_set_segment_limit(bios32ds, 8192-1); + uint32_t pci32entry = 0; + uint32_t size = 0; + uint32_t eip = sd->entry - base; + free(scanarea); + + _ASM_BEGINP + _ASM(pushad) + _ASM(push ds) + + _ASM_MOVP(eax, %4) //bios32cs + _ASM(push eax) + _ASM_MOVP(eax, %5) //eip + _ASM(push eax) + + _ASM(mov eax, 0x049435024) //$PCI + _ASM(xor ebx, ebx) + + _ASM_MOVP(edx, %3) //bios32ds + _ASM(mov ds, edx) + _ASM(mov edx, esp) + _ASM(call far ptr ss:[edx]) + _ASM(add esp, 8) + _ASM(pop ds) + + _ASM(test al, al) + _ASM(jnz failed) + _ASM_MOVP(%0, ebx) + _ASM_MOVP(%1, ecx) + _ASM_MOVP(%2, edx) + _ASMLBL(failed:) + _ASM(popad) + + _ASM_PLIST(base, size, pci32entry, bios32ds, bios32cs, eip) + _ASM_ENDP + + if(base == 0 || size == 0) + { + _LOG("32bit PCI BIOS not found.\n"); + r->EAX = PCI_NOT_SUPPORTED<<8; + return; + } + + _LOG("Found 32bit PCI BIOS, base: %x, entry %x, size: %x\n", base, pci32entry, size); + //const int CS_BASE = 0xF000; //spec required CS base //I think it's a typo in the spec + const int CS_BASE = 0xF0000; + assert(base >= CS_BASE); + if(base < CS_BASE) //spec require DS=CS=0xF0000 + { + r->EAX = PCI_NOT_SUPPORTED<<8; + return; + } + size += max(0, (int)(base - CS_BASE)); + size = (size+0xFFF)&~0xFFF; + pci32entry += base; + base = CS_BASE; + pci32entry -= base; + __dpmi_set_segment_base_address(bios32cs, base); + __dpmi_set_segment_base_address(bios32ds, 0xF0000); //spec required DS base + __dpmi_set_segment_limit(bios32cs, size-1); + __dpmi_set_segment_limit(bios32ds, 64*1024-1); //spec required DS size + + pcibios32cs = bios32cs; + pcibios32ds = bios32ds; + pcibios32entry = pci32entry; + atexit(&pcibios_FreeLDT); + } + + if(pcibios32cs == -1 || pcibios32ds == -1 || pcibios32entry == -1) + { + r->EAX = PCI_NOT_SUPPORTED<<8; + return; + } + + rminfo reg = *r; //access using EBP + _LOG("EAX: %08lx, ECX: %08lx, EBX: %08lx EDX: %08lx\n", reg.EAX, reg.ECX, reg.EBX, reg.EDX); + _LOG("ES: %04lx, EDI: %08lx\n", reg.ES, reg.EDI); + _ASM_BEGINP + _ASM(pushad) + _ASM(push ds) + _ASM(push es) + + _ASM_MOVP(eax, %7) //pcibios32cs + _ASM(push eax) + _ASM_MOVP(eax, %8) //pcibios32entry + _ASM(push eax) + + _ASM_MOVP(eax, %0) + _ASM_MOVP(ebx, %1) + _ASM_MOVP(ecx, %2) + _ASM_MOVP(edx, %3) + _ASM_MOVP(edi, %4) + _ASM_MOVP(si, %5) + _ASM_MOVP(es, si) + + _ASM_MOVP(esi, %6) //pcibios32ds + _ASM(mov ds, esi) + _ASM(mov esi, esp) + _ASM(call far ptr ss:[esi]) + _ASM(add esp, 8) + + _ASM_MOVP(%0, eax) + _ASM_MOVP(%1, ebx) + _ASM_MOVP(%2, ecx) + _ASM_MOVP(%3, edx) + + _ASM(pop es) + _ASM(pop ds) + _ASM(popad) + + _ASM_PLIST(reg.EAX, reg.EBX, reg.ECX, reg.EDX, reg.EDI, reg.ES, + pcibios32ds, pcibios32cs, pcibios32entry) + _ASM_ENDP + *r = reg; +} + uint8_t pcibios_AssignIRQ(pci_config_s* ppkey) { if(!pcibios_GetBus()) @@ -403,63 +599,83 @@ uint8_t pcibios_AssignIRQ(pci_config_s* ppkey) return 0xFF; } - IRQRoutingOptionBuffer buf; + IRQRoutingOptionBuffer buf = {0}; buf.size = 0; //get actually size with size=0 - buf.off = sizeof(buf); - buf.seg = dosmem.segment; - dosput(dosmem.linearptr, &buf, sizeof(buf)); + dosput(dosmem.linearptr, &buf, sizeof(buf)); + _LOG("PCI_GET_ROUTING: get size\n"); rminfo r = {0}; r.EAX = (PCI_FUNCTION_ID<<8)|PCI_GET_ROUTING; r.EBX = 0; - r.DS = 0xF000; - r.ES = dosmem.segment; +#if USE_P32_CALL + #if 0 //32 bit offset not working, some BIOS used DI not EDI + r.ES = _my_ds(); + r.EDI = (uintptr_t)&buf; + #else + r.ES = dosmem.selector; r.EDI = 0; + #endif + pcibios_CallFunction32(&r); +#else + r.DS = 0xF000; r.SS = rmstack.segment; r.SP = STACK_SIZE; - _LOG("PCI_GET_ROUTING: get size\n"); + r.ES = dosmem.segment; + r.EDI = 0; pds_dpmi_realmodeint_call(PCI_SERVICE, &r); //get required size +#endif + dosget(&buf, dosmem.linearptr, sizeof(buf)); + IRQRoutingTable* table = NULL; if(((r.EAX>>8)&0xFF) == PCI_BUFFER_SMALL) //intended, should return this { - dosget(&buf, dosmem.linearptr, sizeof(buf)); + _LOG("PCI_GET_ROUTING: get data %d\n", buf.size); if(buf.size == 0 || !pds_dpmi_dos_allocmem(&dosmem, sizeof(buf) + buf.size)) //buf.size==0 means no PCI devices in the system { pds_dpmi_dos_freemem(&rmstack); return 0xFF; } - buf.off = sizeof(buf); - buf.seg = dosmem.segment; - dosput(dosmem.linearptr, &buf, sizeof(buf)); + table = (IRQRoutingTable*)malloc(buf.size); + if(!table) + { + pds_dpmi_dos_freemem(&rmstack); + pds_dpmi_dos_freemem(&dosmem); + return 0xFF; + } r.EAX = (PCI_FUNCTION_ID<<8)|PCI_GET_ROUTING; r.EBX = 0; - r.DS = 0xF000; +#if USE_P32_CALL + #if 0 //32 bit offset not working, some BIOS used DI not EDI + buf.seg = _my_ds(); + buf.off = (uintptr_t)table; + #else + buf.seg = dosmem.selector; + buf.off = sizeof(buf); + dosput(dosmem.linearptr, &buf, sizeof(buf)); + r.ES = dosmem.selector; + #endif + pcibios_CallFunction32(&r); +#else + buf.off = sizeof(buf); + buf.seg = dosmem.segment; + dosput(dosmem.linearptr, &buf, sizeof(buf)); r.ES = dosmem.segment; - r.EDI = 0; - r.SS = rmstack.segment; - r.SP = STACK_SIZE; - _LOG("PCI_GET_ROUTING: get data\n"); pds_dpmi_realmodeint_call(PCI_SERVICE, &r); //get data +#endif + dosget(table, dosmem.linearptr+sizeof(buf), buf.size); } else r.EAX = 0xFFFFFFFF; //make sure not successful if(((r.EAX>>8)&0xFF) != PCI_SUCCESSFUL) { + free(table); pds_dpmi_dos_freemem(&rmstack); pds_dpmi_dos_freemem(&dosmem); return 0xFF; } - IRQRoutingTable* table = (IRQRoutingTable*)malloc(buf.size); - if(!table) - { - pds_dpmi_dos_freemem(&rmstack); - pds_dpmi_dos_freemem(&dosmem); - return 0xFF; - } - dosget(table, dosmem.linearptr+sizeof(buf), buf.size); int count = buf.size/sizeof(IRQRoutingTable); uint16_t map = 0; @@ -554,158 +770,14 @@ uint8_t pcibios_AssignIRQ(pci_config_s* ppkey) r.EAX = (PCI_FUNCTION_ID<<8)|PCI_SET_INTERRUPT; r.ECX = (((uint32_t)irq)<<8) | (0xA + INTPIN - 1); //cl=INTPIN (0xA~0xD), ch=IRQ r.EBX = (((uint32_t)ppkey->bBus)<<8) | ((ppkey->bDev<<3)&0xFF) | (ppkey->bFunc&0x7); //bh=bus, bl=dev|func - r.DS = 0xF000; - r.SS = rmstack.segment; - r.SP = STACK_SIZE; _LOG("PCI_SET_INTERRUPT INT%c#->%d\n", 'A'+INTPIN-1, irq); -#define USE_P32_CALL 1 //32bit protected mode call. if this doesn't work for some PCs, then the final solution should be programming the interrupt router. - typedef struct - { - char signature[4]; - uint32_t entry; //physical addr - uint8_t reivision; - uint8_t len; //in paragraph - uint8_t checksum; - uint8_t zero[5]; - }BIOS32SD; - _Static_assert(sizeof(BIOS32SD) == 16, "size error"); - - char* scanarea = (char*)malloc(0xFFFFF-0xE0000); - dosget(scanarea, 0xE0000, 0xFFFFF-0xE0000); - const BIOS32SD* sd = NULL; - int count = (0xFFFFF-0xE0000)/sizeof(BIOS32SD); - for(int i = 0; i < count; ++i) - { - const BIOS32SD* test = (const BIOS32SD*)(scanarea + i * sizeof(BIOS32SD)); - if(memcmp(test->signature,"_32_", 4) == 0 && memcmp(test->zero,"\0\0\0\0\0", 5) == 0 && test->len == 1) - { - const char* bytes = (const char*)test; - uint8_t checksum = 0; - for(int i = 0; i < 16; i++) - checksum += bytes[i]; - if(checksum == 0) - { - sd = test; - break; - } - } - } - if(sd == NULL) //not found - { - free(scanarea); - pds_dpmi_dos_freemem(&rmstack); - pds_dpmi_dos_freemem(&dosmem); - free(table); - return 0xFF; - } - _LOG("Found BIOS32 Service Directory at %x, entry point %x\n", (uintptr_t)sd-(uintptr_t)scanarea + 0xE0000, sd->entry); - - int bios32cs = __dpmi_create_alias_descriptor(_my_cs()); - int bios32ds = __dpmi_create_alias_descriptor(_my_ds()); - __dpmi_set_descriptor_access_rights(bios32cs, __dpmi_get_descriptor_access_rights(bios32cs)|0x08); //code/exec - - uint32_t base = sd->entry&~0xFFF; //although it is physical addr, should be under 1M, phyiscal mapping not needed - __dpmi_set_segment_base_address(bios32cs, base); - __dpmi_set_segment_base_address(bios32ds, base); - __dpmi_set_segment_limit(bios32cs, 8192-1); //spec required 2 pages - __dpmi_set_segment_limit(bios32ds, 8192-1); - uint32_t pci32entry = 0; - uint32_t size = 0; - uint32_t eip = sd->entry - base; - free(scanarea); - - _ASM_BEGINP - _ASM(pushad) - _ASM(push ds) - - _ASM_MOVP(eax, %4) //bios32cs - _ASM(push eax) - _ASM_MOVP(eax, %5) //eip - _ASM(push eax) - - _ASM(mov eax, 0x049435024) //$PCI - _ASM(xor ebx, ebx) - - _ASM_MOVP(edx, %3) //bios32ds - _ASM(mov ds, edx) - _ASM(mov edx, esp) - _ASM(call far ptr ss:[edx]) - _ASM(add esp, 8) - _ASM(pop ds) - - _ASM(test al, al) - _ASM(jnz failed) - _ASM_MOVP(%0, ebx) - _ASM_MOVP(%1, ecx) - _ASM_MOVP(%2, edx) - _ASMLBL(failed:) - _ASM(popad) - - _ASM_PLIST - _ASM_P _ASM_OP(base) _ASM_OP2(size) _ASM_OP2(pci32entry) - _ASM_P _ASM_IP(bios32ds) _ASM_IP2(bios32cs) _ASM_IP2(eip) - _ASM_P _ASM_CLOBBER(eax) _ASM_CLOBBER2(ebx) _ASM_CLOBBER2(ecx) _ASM_CLOBBER2(edx) - _ASM_ENDP - if(base == 0 || size == 0) - { - _LOG("32bit PCI BIOS not found.\n"); - pds_dpmi_dos_freemem(&rmstack); - pds_dpmi_dos_freemem(&dosmem); - free(table); - return 0xFF; - } - - _LOG("Found 32bit PCI BIOS, base: %x, entry %x, size: %x\n", base, pci32entry, size); - //const int CS_BASE = 0xF000; //spec required CS base //I think it's a typo in the spec - const int CS_BASE = 0xF0000; - size += max(CS_BASE - base, base - CS_BASE); - pci32entry += base; - base = CS_BASE; - pci32entry -= base; - __dpmi_set_segment_base_address(bios32cs, base); - __dpmi_set_segment_base_address(bios32ds, 0xF0000); //spec required DS base - __dpmi_set_segment_limit(bios32cs, size-1); - __dpmi_set_segment_limit(bios32ds, 64*1024-1); //spec required DS size - uint8_t errorcode = 0; - - r.EAX = (PCI_FUNCTION_ID<<8)|PCI_SET_INTERRUPT; - r.ECX = (((uint32_t)irq)<<8) | (0xA + INTPIN - 1); //cl=INTPIN (0xA~0xD), ch=IRQ - r.EBX = (((uint32_t)ppkey->bBus)<<8) | ((ppkey->bDev<<3)&0xFF) | (ppkey->bFunc&0x7); //bh=bus, bl=dev|func - _LOG("EAX: %08lx, ECX: %08lx, EBX: %08lx\n", r.EAX, r.ECX, r.EBX); - _ASM_BEGINP - _ASM(pushad) - _ASM(push ds) - - _ASM_MOVP(eax, %2) //bios32cs - _ASM(push eax) - _ASM_MOVP(eax, %3) //pci32entry - _ASM(push eax) - - _ASM_MOVP(eax, %4) - _ASM_MOVP(ebx, %5) - _ASM_MOVP(ecx, %6) - - _ASM_MOVP(edx, %1) //bios32ds - _ASM(mov ds, edx) - _ASM(mov edx, esp) - _ASM(call far ptr ss:[edx]) - _ASM(add esp, 8) - _ASM(pop ds) - - _ASM_MOVP(%0, ah) - _ASM(popad) - - _ASM_PLIST - _ASM_P _ASM_OP(errorcode) - _ASM_P _ASM_IP(bios32ds) _ASM_IP2(bios32cs) _ASM_IP2(pci32entry) _ASM_IP2(r.EAX) _ASM_IP2(r.EBX) _ASM_IP2(r.ECX) - _ASM_P _ASM_CLOBBER(eax) _ASM_CLOBBER2(ebx) _ASM_CLOBBER2(ecx) _ASM_CLOBBER2(edx) - _ASM_ENDP - r.EAX = errorcode<<8; - __dpmi_free_ldt_descriptor(bios32ds); - __dpmi_free_ldt_descriptor(bios32cs); #if USE_P32_CALL + pcibios_CallFunction32(&r); #else + r.DS = 0xF000; + r.SS = rmstack.segment; + r.SP = STACK_SIZE; pds_dpmi_realmodeint_call(PCI_SERVICE, &r); #endif diff --git a/sbemu/platform.h b/sbemu/platform.h index 3cde9df1..039be4e5 100644 --- a/sbemu/platform.h +++ b/sbemu/platform.h @@ -96,17 +96,33 @@ static uint32_t PLTFM_BSF(uint32_t x) { uint32_t i; __asm {bsf eax, x; mov i, ea #define _ASM_BEGIN32 _ASM_BEGIN #define _ASM_END32 _ASM_END #define _ASM_MOVP(x,y) ".att_syntax noprefix\n\t" "mov "#y","#x"\n\t" ".intel_syntax noprefix\n\t" -#define _ASM_P : -#define _ASM_OP(x) "=m"(x) -#define _ASM_IP(x) "m"(x) -#define _ASM_OP2(x) ,"=m"(x) -#define _ASM_IP2(x) ,"m"(x) -#define _ASM_CLOBBER(x) #x -#define _ASM_CLOBBER2(x) ,#x -#define _ASM_PLIST ".att_syntax noprefix\n\t" #define _ASM_BEGINP _ASM_BEGIN #define _ASM_ENDP ); +#define _ASM_CONCAT2(arg1, arg2) arg1##arg2 +#define _ASM_CONCAT1(arg1, arg2) _ASM_CONCAT2(arg1, arg2) +#define _ASM_CONCAT(arg1, arg2) _ASM_CONCAT1(arg1, arg2) + +#define _ASM_P1(x, ...) "+m"(x) +#define _ASM_P2(x, ...) "+m"(x), _ASM_P1(__VA_ARGS__) +#define _ASM_P3(x, ...) "+m"(x), _ASM_P2(__VA_ARGS__) +#define _ASM_P4(x, ...) "+m"(x), _ASM_P3(__VA_ARGS__) +#define _ASM_P5(x, ...) "+m"(x), _ASM_P4(__VA_ARGS__) +#define _ASM_P6(x, ...) "+m"(x), _ASM_P5(__VA_ARGS__) +#define _ASM_P7(x, ...) "+m"(x), _ASM_P6(__VA_ARGS__) +#define _ASM_P8(x, ...) "+m"(x), _ASM_P7(__VA_ARGS__) +#define _ASM_P9(x, ...) "+m"(x), _ASM_P8(__VA_ARGS__) +#define _ASM_P10(x, ...) "+m"(x), _ASM_P9(__VA_ARGS__) + +#define _ASM_LIST_ARG_N(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, N, ...) N +#define _ASM_LIST_RSEQ_N() 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 +#define _ASM_LIST_NARG_(...) _ASM_LIST_ARG_N(__VA_ARGS__) +#define _ASM_LIST_NARG(...) _ASM_LIST_NARG_(__VA_ARGS__, _ASM_LIST_RSEQ_N()) +#define _ASM_PLIST_(N, ...) _ASM_CONCAT(_ASM_P, N)(__VA_ARGS__) +#define _ASM_PLIST(...) ".att_syntax noprefix\n\t" : _ASM_PLIST_(_ASM_LIST_NARG(__VA_ARGS__), __VA_ARGS__) + +//#define _ASM_PLIST ".att_syntax noprefix\n\t" : + #define NOP() asm __volatile__("nop") #define CLI() asm __volatile__("cli") #define STI() asm __volatile__("sti") @@ -180,14 +196,7 @@ static inline uint16_t PLTFM_CPU_FLAGS() { uint16_t (* volatile VFN)(void) = &PL #define _ASM_SIDT(x) #define _ASM_SGDT(x) -#define _ASM_PLIST -#define _ASM_P -#define _ASM_OP(x) -#define _ASM_IP(x) -#define _ASM_OP2(x) -#define _ASM_IP2(x) -#define _ASM_CLOBBER(x) -#define _ASM_CLOBBER2(x) +#define _ASM_PLIST(...) #define _ASM_MOVP(x,y) #define _ASM_BEGINP { #define _ASM_ENDP } @@ -209,6 +218,10 @@ extern void outpd(uint16_t port, uint32_t val); extern int _dos_open(const char* file, int mode, int* fd); extern int _dos_close(int fd); extern int ioctl(int, int, int, void*); +extern int _my_cs(); +extern int _my_es(); +extern int _my_ds(); +#define _dos_ds 0 #define DOS_RCVDATA 2 #define DOS_SNDDATA 3