added PCI code in anticipation of porting the S3 driver
authorJohn Tsiombikas <nuclear@member.fsf.org>
Sun, 15 May 2022 03:18:53 +0000 (06:18 +0300)
committerJohn Tsiombikas <nuclear@member.fsf.org>
Sun, 15 May 2022 03:18:53 +0000 (06:18 +0300)
Makefile
src/dos/cdpmi.h
src/dos/dosutil.h
src/dos/main.c
src/dos/pci.c [new file with mode: 0644]
src/dos/pci.h [new file with mode: 0644]

index c1a8cad..c862ea9 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -1,7 +1,8 @@
 !ifdef __UNIX__
 dosobj = src/dos/audos.obj src/dos/djdpmi.obj src/dos/gfx.obj src/dos/keyb.obj &
        src/dos/logger.obj src/dos/main.obj src/dos/sball.obj src/dos/timer.obj &
-       src/dos/vbe.obj src/dos/vga.obj src/dos/watdpmi.obj src/dos/mouse.obj
+       src/dos/vbe.obj src/dos/vga.obj src/dos/watdpmi.obj src/dos/mouse.obj &
+       src/dos/pci.obj
 3dobj = src/3dgfx/3dgfx.obj src/3dgfx/mesh.obj src/3dgfx/meshload.obj &
        src/3dgfx/polyclip.obj src/3dgfx/polyfill.obj
 srcobj = src/bsptree.obj src/cfgopt.obj src/console.obj src/demo.obj &
@@ -23,7 +24,8 @@ libpath = libpath libs/imago libpath libs/anim libpath libs/midas
 
 dosobj = src\dos\audos.obj src\dos\djdpmi.obj src\dos\gfx.obj src\dos\keyb.obj &
        src\dos\logger.obj src\dos\main.obj src\dos\sball.obj src\dos\timer.obj &
-       src\dos\vbe.obj src\dos\vga.obj src\dos\watdpmi.obj src\dos\mouse.obj
+       src\dos\vbe.obj src\dos\vga.obj src\dos\watdpmi.obj src\dos\mouse.obj &
+       src\dos\pci.obj
 3dobj = src\3dgfx\3dgfx.obj src\3dgfx\mesh.obj src\3dgfx\meshload.obj &
        src\3dgfx\polyclip.obj src\3dgfx\polyfill.obj
 srcobj = src\bsptree.obj src\cfgopt.obj src\console.obj src\demo.obj &
index 71138b6..e8a6950 100644 (file)
@@ -29,6 +29,17 @@ struct dpmi_regs {
 } PACKED;
 #pragma pack (pop)
 
+enum {
+       FLAGS_CF        = 0x000001,
+       FLAGS_PF        = 0x000004,
+       FLAGS_ZF        = 0x000040,
+       FLAGS_SF        = 0x000080,
+       FLAGS_IF        = 0x000020,
+       FLAGS_DF        = 0x000040,
+       FLAGS_VM        = 0x020000,
+       FLAGS_ID        = 0x200000,
+};
+
 uint16_t dpmi_alloc(unsigned int par, uint16_t *sel);
 void dpmi_free(uint16_t sel);
 void dpmi_int(int inum, struct dpmi_regs *regs);
index 9894251..71af365 100644 (file)
@@ -9,9 +9,11 @@
 
 #define outp(p, v)     outportb(p, v)
 #define outpw(p, v)    outportw(p, v)
+#define outpd(p, v)    outportd(p, v)
 
 #define inp(p)         inportb(p)
 #define inpw(p)                inportw(p)
+#define inpd(p)                inportd(p)
 #endif
 
 #endif /* DOSUTIL_H_ */
index ee28dc9..7f67e85 100644 (file)
@@ -62,6 +62,10 @@ int main(int argc, char **argv)
        init_timer(100);
        kb_init(32);
 
+       if(init_pci() != -1) {
+               /* TODO detect and initialize S3 virge */
+       }
+
        if(init_video() == -1) {
                return 1;
        }
diff --git a/src/dos/pci.c b/src/dos/pci.c
new file mode 100644 (file)
index 0000000..d3a1e7d
--- /dev/null
@@ -0,0 +1,421 @@
+/*
+S3 Virge driver hack
+Copyright (C) 2021 John Tsiombikas <nuclear@member.fsf.org>
+
+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 3 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, see <https://www.gnu.org/licenses/>.
+*/
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <conio.h>
+#include <i86.h>
+#include "inttypes.h"
+#include "pci.h"
+#include "cdpmi.h"
+
+#define CONFIG_ADDR_PORT       0xcf8
+#define CONFIG_DATA_PORT       0xcfc
+
+#define ADDR_ENABLE            0x80000000
+#define ADDR_BUSID(x)  (((uint32_t)(x) & 0xff) << 16)
+#define ADDR_DEVID(x)  (((uint32_t)(x) & 0x1f) << 11)
+#define ADDR_FUNC(x)   (((uint32_t)(x) & 3) << 8)
+
+/* signature returned in edx by the PCI BIOS present function: FOURCC "PCI " */
+#define PCI_SIG                0x20494350
+
+#define TYPE_MULTIFUNC 0x80
+
+
+static struct pci_device *pcidev;
+static int num_pcidevs, max_pcidevs;
+
+
+static int enum_bus(int busid);
+static int enum_dev(int busid, int dev);
+static int read_dev_info(struct pci_config_data *res, int bus, int dev, int func);
+static void print_dev_info(struct pci_config_data *info, int bus, int dev, int func);
+
+static uint32_t cfg_read32_m1(int bus, int dev, int func, int reg);
+static uint32_t cfg_read32_m2(int bus, int dev, int func, int reg);
+static const char *class_str(int cc);
+static const char *subclass_str(int cc, int sub);
+
+static uint32_t (*cfg_read32)(int, int, int, int);
+
+static void clear_devices(void);
+static void add_device(struct pci_device *dev);
+
+int init_pci(void)
+{
+       int i, count = 0;
+       struct dpmi_regs regs = {0};
+
+       clear_devices();
+
+       regs.eax = 0xb101;
+       dpmi_int(0x1a, &regs);
+
+       /* PCI BIOS present if CF=0, AH=0, and EDX has the "PCI " sig FOURCC */
+       if((regs.flags & FLAGS_CF)  || (regs.eax & 0xff00) || regs.edx != PCI_SIG) {
+               fprintf(stderr, "No PCI BIOS present\n");
+               return -1;
+       }
+
+       printf("PCI BIOS v%x.%x found\n", (regs.ebx & 0xff00) >> 8, regs.ebx & 0xff);
+       if(regs.eax & 1) {
+               cfg_read32 = cfg_read32_m1;
+       } else {
+               if(!(regs.eax & 2)) {
+                       fprintf(stderr, "Failed to find supported PCI mess mechanism\n");
+                       return -1;
+               }
+               printf("PCI mess mechanism #1 unsupported, falling back to mechanism #2\n");
+               cfg_read32 = cfg_read32_m2;
+       }
+
+       for(i=0; i<256; i++) {
+               count += enum_bus(i);
+       }
+       printf("found %d PCI devices\n\n", count);
+       return 0;
+}
+
+static int enum_bus(int busid)
+{
+       int i, count = 0;
+
+       for(i=0; i<32; i++) {
+               count += enum_dev(busid, i);
+       }
+
+       return count;
+}
+
+static int enum_dev(int busid, int devid)
+{
+       int i, count;
+       struct pci_device dev;
+
+       dev.bus = busid;
+       dev.dev = devid;
+       dev.func = 0;
+
+       /* vendor id ffff is invalid */
+       if((cfg_read32(busid, devid, 0, 0) & 0xffff) == 0xffff) {
+               return 0;
+       }
+       if(read_dev_info(&dev.cfg, busid, devid, 0) == -1) {
+               return 0;
+       }
+       print_dev_info(&dev.cfg, busid, devid, 0);
+       add_device(&dev);
+
+       count = 1;
+
+       if(dev.cfg.hdr_type & TYPE_MULTIFUNC) {
+               for(i=1; i<8; i++) {
+                       if(read_dev_info(&dev.cfg, busid, devid, i) == -1) {
+                               continue;
+                       }
+                       print_dev_info(&dev.cfg, busid, devid, i);
+                       dev.func = i;
+                       add_device(&dev);
+                       count++;
+               }
+       }
+       return count;
+}
+
+static int read_dev_info(struct pci_config_data *res, int bus, int dev, int func)
+{
+       int i;
+       uint32_t *ptr = (uint32_t*)res;
+
+       *ptr++ = cfg_read32(bus, dev, func, 0);
+       if(res->vendor == 0xffff) {
+               return -1;
+       }
+
+       for(i=1; i<16; i++) {
+               *ptr++ = cfg_read32(bus, dev, func, i * 4);
+       }
+       return 0;
+}
+
+static void print_dev_info(struct pci_config_data *info, int bus, int dev, int func)
+{
+       printf("- (%d:%d,%d) Device %04x:%04x: ", bus, dev, func, info->vendor, info->device);
+       printf("\"%s\" (%d) - %s-func\n", class_str(info->class), info->class,
+                       info->hdr_type & TYPE_MULTIFUNC ? "multi" : "single");
+       printf("    subclass: \"%s\" (%d), iface: %d\n", subclass_str(info->class, info->subclass),
+                       info->subclass, info->iface);
+}
+
+static uint32_t cfg_read32_m1(int bus, int dev, int func, int reg)
+{
+       uint32_t addr = ADDR_ENABLE | ADDR_BUSID(bus) | ADDR_DEVID(dev) |
+               ADDR_FUNC(func) | reg;
+
+       outpd(CONFIG_ADDR_PORT, addr);
+       return inpd(CONFIG_DATA_PORT);
+}
+
+static uint32_t cfg_read32_m2(int bus, int dev, int func, int reg)
+{
+       fprintf(stderr, "BUG: PCI mess mechanism #2 not implemented yet!");
+       demo_abort();
+       return 0;
+}
+
+static const char *class_names[] = {
+       "unknown",
+       "mass storage controller",
+       "network controller",
+       "display controller",
+       "multimedia device",
+       "memory controller",
+       "bridge device",
+       "simple communication controller",
+       "base system peripheral",
+       "input device",
+       "docking station",
+       "processor",
+       "serial bus controller",
+       "wireless controller",
+       "intelligent I/O controller",
+       "satellite communication controller",
+       "encryption/decryption controller",
+       "data acquisition & signal processing controller"
+};
+
+static const char *class_mass_names[] = {
+       "SCSI bus controller",
+       "IDE controller",
+       "floppy disk controller",
+       "IPI bus controller",
+       "RAID controller"
+};
+
+static const char *class_net_names[] = {
+       "ethernet controller",
+       "token ring controller",
+       "FDDI controller",
+       "ATM controller",
+       "ISDN controller"
+};
+
+static const char *class_disp_names[] = {
+       "VGA-compatible controller",
+       "XGA controller",
+       "3D controller"
+};
+
+static const char *class_mm_names[] = {
+       "video device",
+       "audio device",
+       "telephony device"
+};
+
+static const char *class_bridge_names[] = {
+       "host bridge",
+       "ISA bridge",
+       "EISA bridge",
+       "MCA bridge",
+       "PCI-to-PCI bridge",
+       "Subtractive decode PCI-to-PCI bridge",
+       "PCMCIA bridge",
+       "NuBus bridge",
+       "CardBus bridge",
+       "RACEway bridge"
+};
+
+static const char *class_comm_names[] = {
+       "serial controller",
+       "parallel/IEEE1284",
+       "multiport serial controller",
+       "modem"
+};
+
+static const char *class_base_names[] = {
+       "interrupt controller",
+       "DMA controller",
+       "timer",
+       "RTC",
+       "PCI hot-plug controller"
+};
+
+static const char *class_input_names[] = {
+       "keyboard controller",
+       "digitizer",
+       "mouse controller",
+       "scanner controller",
+       "gameport controller"
+};
+
+static const char *class_ser_names[] = {
+       "firewire",
+       "ACCESS.bus",
+       "SSA",
+       "USB",
+       "Fibre Channel",
+       "SMBus"
+};
+
+static const char *class_sat_names[] = {
+       "TV",
+       "audio",
+       "voice",
+       "data"
+};
+
+
+static const char *class_str(int cc)
+{
+       if(cc == 0xff) {
+               return "other";
+       }
+       if(cc >= 0x12) {
+               return "unknown";
+       }
+       return class_names[cc];
+}
+
+static const char *subclass_str(int cc, int sub)
+{
+       if(sub == 0x80) return "other";
+
+       switch(cc) {
+       case 0:
+               if(sub == 1) return "VGA-compatible device";
+               return "unknown";
+
+       case 1:
+               if(sub > 4) return "unknown";
+               return class_mass_names[sub];
+
+       case 2:
+               if(sub > 4) return "unknown";
+               return class_net_names[sub];
+
+       case 3:
+               if(sub > 2) return "unknown";
+               return class_disp_names[sub];
+
+       case 4:
+               if(sub > 2) return "unknown";
+               return class_mm_names[sub];
+
+       case 5:
+               if(sub == 0) return "RAM";
+               if(sub == 1) return "flash";
+               return "unknown";
+
+       case 6:
+               if(sub > 8) return "unknown";
+               return class_bridge_names[sub];
+
+       case 7:
+               if(sub > 3) return "unknown";
+               return class_comm_names[sub];
+
+       case 8:
+               if(sub > 4) return "unknown";
+               return class_base_names[sub];
+
+       case 9:
+               if(sub > 4) return "unknown";
+               return class_input_names[sub];
+
+       case 10:
+               if(sub == 0) return "generic docking station";
+               return "unknown";
+
+       case 11:
+               switch(sub) {
+               case 0: return "386";
+               case 1: return "486";
+               case 2: return "pentium";
+               case 0x10: return "alpha";
+               case 0x20: return "powerpc";
+               case 0x30: return "mips";
+               case 0x40: return "co-processor";
+               default:
+                       break;
+               }
+               return "unknown";
+
+       case 12:
+               if(sub > 5) return "unknown";
+               return class_ser_names[sub];
+
+       case 13:
+               if(sub == 0) return "irda controller";
+               if(sub == 1) return "IR controller";
+               if(sub == 0x10) return "RF controller";
+               return "unknonw";
+
+       case 15:
+               if(sub > 4) return "unknown";
+               return class_sat_names[sub];
+
+       case 16:
+               if(sub == 0) return "network & computing crypto";
+               if(sub == 1) return "entertainment crypto";
+               return "unknown";
+
+       case 17:
+               if(sub == 0) return "DPIO module";
+               return "unknown";
+
+       default:
+               break;
+       }
+       return "unknown";
+}
+
+static void clear_devices(void)
+{
+       free(pcidev);
+       pcidev = 0;
+       num_pcidevs = max_pcidevs = 0;
+}
+
+static void add_device(struct pci_device *dev)
+{
+       if(num_pcidevs >= max_pcidevs) {
+               void *newarr;
+               int newsz = max_pcidevs ? max_pcidevs << 1 : 8;
+
+               if(!(newarr = realloc(pcidev, newsz * sizeof *pcidev))) {
+                       fprintf(stderr, "failed to resize PCI device array (%d)\n", newsz);
+                       return;
+               }
+               pcidev = newarr;
+               max_pcidevs = newsz;
+       }
+
+       pcidev[num_pcidevs++] = *dev;
+}
+
+struct pci_device *find_pci_dev(uint16_t vendorid, uint16_t devid)
+{
+       int i;
+       for(i=0; i<num_pcidevs; i++) {
+               if(pcidev[i].cfg.vendor == vendorid && pcidev[i].cfg.device == devid) {
+                       return pcidev + i;
+               }
+       }
+       return 0;
+}
diff --git a/src/dos/pci.h b/src/dos/pci.h
new file mode 100644 (file)
index 0000000..6025831
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+S3 Virge driver hack
+Copyright (C) 2021 John Tsiombikas <nuclear@member.fsf.org>
+
+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 3 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, see <https://www.gnu.org/licenses/>.
+*/
+#ifndef PCI_H_
+#define PCI_H_
+
+#include "inttypes.h"
+#include "util.h"
+
+#pragma pack (push, 1)
+struct pci_config_data {
+       uint16_t vendor, device;
+       uint16_t cmd, status;
+       uint8_t rev, iface, subclass, class;
+       uint8_t cacheline_size;
+       uint8_t latency_timer;
+       uint8_t hdr_type;
+       uint8_t bist;
+       uint32_t base_addr[6];
+       uint32_t cardbus_cis;
+       uint16_t subsys_vendor;
+       uint16_t subsys;
+       uint32_t rom_addr;
+       uint32_t reserved1, reserved2;
+       uint8_t intr_line, intr_pin;
+       uint8_t min_grant, max_latency;
+} PACKED;
+#pragma pop (push)
+
+struct pci_device {
+       int bus, dev, func;
+       struct pci_config_data cfg;
+};
+
+int init_pci(void);
+
+struct pci_device *find_pci_dev(uint16_t vendorid, uint16_t devid);
+
+#endif /* PCI_H_ */