From 5115214d01398150e01d1cf4e0ebc75f66e86ea9 Mon Sep 17 00:00:00 2001 From: RichardG867 Date: Fri, 26 Jun 2020 18:05:27 -0300 Subject: [PATCH] DRB locking implementation --- src/chipset/intel_420ex.c | 33 +++++++++++- src/chipset/intel_4x0.c | 103 +++++++++++++++++++++++++++++++++++--- src/include/86box/spd.h | 3 ++ src/mem/spd.c | 103 ++++++++++++++++++++++---------------- 4 files changed, 191 insertions(+), 51 deletions(-) diff --git a/src/chipset/intel_420ex.c b/src/chipset/intel_420ex.c index ca872599d..9aa41adad 100644 --- a/src/chipset/intel_420ex.c +++ b/src/chipset/intel_420ex.c @@ -32,6 +32,7 @@ #include <86box/hdc.h> #include <86box/machine.h> #include <86box/chipset.h> +#include <86box/spd.h> #define MEM_STATE_SHADOW_R 0x01 @@ -179,6 +180,33 @@ i420ex_smram_handler_phase1(i420ex_t *dev) } +static void +i420ex_write_drbs(i420ex_t *dev) +{ + uint8_t row, dimm; + uint16_t size, vslots[SPD_MAX_SLOTS]; + + /* No SPD: let the SPD code split SIMMs into pairs as if they were "DIMM"s. */ + dimm = (4 + 1) >> 1; /* amount of "DIMM"s, also used to determine the maximum "DIMM" size */ + spd_populate(vslots, dimm, (mem_size >> 10), 1, 1 << (log2_ui16(machines[machine].max_ram / dimm)), 0); + + /* Write DRBs for each row. */ + i420ex_log("Writing DRBs...\n"); + for (row = 0; row <= 4; row++) { + dimm = (row >> 1); + + /* No SPD: use the values calculated above. */ + size = (vslots[dimm] >> 1); + + /* Populate DRB register, adding the previous DRB's value.. */ + dev->regs[0x60 | row] = ((row > 0) ? dev->regs[0x60 | (row - 1)] : 0); + if (size) + dev->regs[0x60 | row] += size; + i420ex_log("DRB[%d] = %d MB (%02Xh raw)\n", row, size, dev->regs[0x60 | row]); + } +} + + static void i420ex_write(int func, int addr, uint8_t val, void *priv) { @@ -232,8 +260,6 @@ i420ex_write(int func, int addr, uint8_t val, void *priv) break; case 0x4c: case 0x51: case 0x57: - case 0x60: case 0x61: case 0x62: case 0x63: - case 0x64: case 0x68: case 0x69: dev->regs[addr] = val; if (addr == 0x4c) { @@ -306,6 +332,9 @@ i420ex_write(int func, int addr, uint8_t val, void *priv) i420ex_map(0xec000, 0x04000, val >> 4); dev->regs[0x5f] = val; break; + case 0x60: case 0x61: case 0x62: case 0x63: case 0x64: + i420ex_write_drbs(dev); + break; case 0x66: case 0x67: i420ex_log("Set IRQ routing: INT %c -> %02X\n", 0x41 + (addr & 0x01), val); dev->regs[addr] = val & 0x8f; diff --git a/src/chipset/intel_4x0.c b/src/chipset/intel_4x0.c index a65e06dd3..e6009107d 100644 --- a/src/chipset/intel_4x0.c +++ b/src/chipset/intel_4x0.c @@ -6,7 +6,7 @@ * * This file is part of the 86Box distribution. * - * Implementation of the Intel PCISet chips from 420TX to 440BX. + * Implementation of the Intel PCISet chips from 420TX to 440GX. * * * @@ -28,6 +28,8 @@ #include <86box/device.h> #include <86box/keyboard.h> #include <86box/chipset.h> +#include <86box/spd.h> +#include <86box/machine.h> enum @@ -52,12 +54,32 @@ typedef struct { uint8_t pm2_cntrl, max_func, smram_locked, max_drb, - drb_default; + drb_unit, drb_default; uint8_t regs[2][256], regs_locked[2][256]; int type; } i4x0_t; +#ifdef ENABLE_I4X0_LOG +int i4x0_do_log = ENABLE_I4X0_LOG; + + +static void +i4x0_log(const char *fmt, ...) +{ + va_list ap; + + if (i4x0_do_log) { + va_start(ap, fmt); + pclog_ex(fmt, ap); + va_end(ap); + } +} +#else +#define i4x0_log(fmt, ...) +#endif + + static void i4x0_map(uint32_t addr, uint32_t size, int state) { @@ -247,6 +269,47 @@ pm2_cntrl_write(uint16_t addr, uint8_t val, void *p) } +static void +i4x0_write_drbs(i4x0_t *dev) +{ + uint8_t row, dimm; + uint16_t size, vslots[SPD_MAX_SLOTS]; + + /* No SPD: let the SPD code split SIMMs into pairs as if they were "DIMM"s. */ + if (!spd_present) { + dimm = (dev->max_drb + 1) >> 1; /* amount of "DIMM"s, also used to determine the maximum "DIMM" size */ + spd_populate(vslots, dimm, (mem_size >> 10), dev->drb_unit, 1 << (log2_ui16(machines[machine].max_ram / dimm)), 0); + } + + /* Write DRBs for each row. */ + i4x0_log("Writing DRBs... unit=%d max=%d\n", dev->drb_unit, dev->max_drb); + for (row = 0; row <= dev->max_drb; row++) { + dimm = (row >> 1); + size = 0; + + if (spd_present) { + /* SPD enabled: use SPD info for this slot, if present. */ + if (spd_devices[dimm]) { + if (spd_devices[dimm]->row1 < dev->drb_unit) /* hack within a hack: turn a double-sided DIMM that is too small into a single-sided one */ + size = ((row & 1) ? 0 : dev->drb_unit); + else + size = ((row & 1) ? spd_devices[dimm]->row2 : spd_devices[dimm]->row1); + } + } else { + /* No SPD: use the values calculated above. */ + size = (vslots[dimm] >> 1); + } + + /* Populate DRB register, adding the previous DRB's value. + This will intentionally overflow on 440GX with 2 GB. */ + dev->regs[0][0x60 | row] = ((row > 0) ? dev->regs[0][0x60 | (row - 1)] : 0); + if (size) + dev->regs[0][0x60 | row] += (size / dev->drb_unit); + i4x0_log("DRB[%d] = %d MB (%02Xh raw)\n", row, size, dev->regs[0][0x60 | row]); + } +} + + static void i4x0_write(int func, int addr, uint8_t val, void *priv) { @@ -627,13 +690,17 @@ i4x0_write(int func, int addr, uint8_t val, void *priv) regs[0x5f] = val & 0x77; break; case 0x60: case 0x61: case 0x62: case 0x63: case 0x64: + if ((addr & 0x7) <= dev->max_drb) { + i4x0_write_drbs(dev); + break; + } switch (dev->type) { case INTEL_420TX: case INTEL_420ZX: case INTEL_430LX: case INTEL_430NX: case INTEL_430HX: case INTEL_440FX: case INTEL_440LX: case INTEL_440EX: - case INTEL_440BX: case INTEL_440ZX: + case INTEL_440BX: case INTEL_440ZX: default: regs[addr] = val; break; @@ -646,13 +713,17 @@ i4x0_write(int func, int addr, uint8_t val, void *priv) } break; case 0x65: + if ((addr & 0x7) <= dev->max_drb) { + i4x0_write_drbs(dev); + break; + } switch (dev->type) { case INTEL_420TX: case INTEL_420ZX: case INTEL_430LX: case INTEL_430NX: case INTEL_430HX: case INTEL_440FX: case INTEL_440LX: case INTEL_440EX: - case INTEL_440GX: + case INTEL_440GX: case INTEL_440BX: case INTEL_440ZX: regs[addr] = val; break; @@ -665,6 +736,10 @@ i4x0_write(int func, int addr, uint8_t val, void *priv) } break; case 0x66: + if ((addr & 0x7) <= dev->max_drb) { + i4x0_write_drbs(dev); + break; + } switch (dev->type) { case INTEL_430NX: case INTEL_430HX: case INTEL_440FX: case INTEL_440LX: @@ -675,12 +750,16 @@ i4x0_write(int func, int addr, uint8_t val, void *priv) } break; case 0x67: + if ((addr & 0x7) <= dev->max_drb) { + i4x0_write_drbs(dev); + break; + } switch (dev->type) { case INTEL_430NX: case INTEL_430HX: case INTEL_440FX: case INTEL_440LX: case INTEL_440EX: case INTEL_440BX: case INTEL_440GX: - case INTEL_440ZX: + case INTEL_440ZX: regs[addr] = val; break; case INTEL_430VX: @@ -1305,6 +1384,7 @@ static void regs[0x59] = 0x0f; regs[0x60] = regs[0x61] = regs[0x62] = regs[0x63] = regs[0x64] = regs[0x65] = 0x02; dev->max_drb = 5; + dev->drb_unit = 4; dev->drb_default = 0x02; break; case INTEL_430LX: @@ -1323,6 +1403,7 @@ static void regs[0x59] = 0x0f; regs[0x60] = regs[0x61] = regs[0x62] = regs[0x63] = regs[0x64] = regs[0x65] = 0x02; dev->max_drb = 5; + dev->drb_unit = 4; dev->drb_default = 0x02; break; case INTEL_430NX: @@ -1343,6 +1424,7 @@ static void regs[0x59] = 0x0f; regs[0x60] = regs[0x61] = regs[0x62] = regs[0x63] = regs[0x64] = regs[0x65] = regs[0x66] = regs[0x67] = 0x02; dev->max_drb = 7; + dev->drb_unit = 4; dev->drb_default = 0x02; break; case INTEL_430FX: @@ -1358,6 +1440,7 @@ static void regs[0x60] = regs[0x61] = regs[0x62] = regs[0x63] = regs[0x64] = 0x02; regs[0x72] = 0x02; dev->max_drb = 4; + dev->drb_unit = 4; dev->drb_default = 0x02; break; case INTEL_430HX: @@ -1372,6 +1455,7 @@ static void regs[0x60] = regs[0x61] = regs[0x62] = regs[0x63] = regs[0x64] = regs[0x65] = regs[0x66] = regs[0x67] = 0x02; regs[0x72] = 0x02; dev->max_drb = 7; + dev->drb_unit = 4; dev->drb_default = 0x02; break; case INTEL_430VX: @@ -1393,6 +1477,7 @@ static void regs[0x74] = 0x0e; regs[0x78] = 0x23; dev->max_drb = 4; + dev->drb_unit = 4; dev->drb_default = 0x02; break; case INTEL_430TX: @@ -1410,6 +1495,7 @@ static void regs[0x70] = 0x20; regs[0x72] = 0x02; dev->max_drb = 5; + dev->drb_unit = 4; dev->drb_default = 0x02; break; case INTEL_440FX: @@ -1426,6 +1512,7 @@ static void regs[0x71] = 0x10; regs[0x72] = 0x02; dev->max_drb = 7; + dev->drb_unit = 8; dev->drb_default = 0x02; break; case INTEL_440LX: @@ -1450,12 +1537,13 @@ static void regs[0xa5] = 0x02; regs[0xa7] = 0x1f; dev->max_drb = 7; + dev->drb_unit = 8; dev->drb_default = 0x01; break; case INTEL_440EX: dev->max_func = 1; - regs[0x02] = 0x80; regs[0x03] = 0x71; /* 82443EX. Same Vendor ID as 440LX*/ + regs[0x02] = 0x80; regs[0x03] = 0x71; /* 82443EX. Same Vendor ID as 440LX */ regs[0x06] = 0x90; regs[0x10] = 0x08; regs[0x34] = 0xa0; @@ -1474,6 +1562,7 @@ static void regs[0xa5] = 0x02; regs[0xa7] = 0x1f; dev->max_drb = 7; + dev->drb_unit = 8; dev->drb_default = 0x01; break; case INTEL_440BX: case INTEL_440ZX: @@ -1502,6 +1591,7 @@ static void regs[0xa5] = 0x02; regs[0xa7] = 0x1f; dev->max_drb = 7; + dev->drb_unit = 8; dev->drb_default = 0x01; break; case INTEL_440GX: @@ -1527,6 +1617,7 @@ static void regs[0xa5] = 0x02; regs[0xa7] = 0x1f; dev->max_drb = 7; + dev->drb_unit = 8; dev->drb_default = 0x01; break; } diff --git a/src/include/86box/spd.h b/src/include/86box/spd.h index 098fb3ed7..cec4e9c18 100644 --- a/src/include/86box/spd.h +++ b/src/include/86box/spd.h @@ -100,9 +100,12 @@ typedef struct _spd_sdram_ { } spd_sdram_t; +extern int spd_present; extern spd_t *spd_devices[SPD_MAX_SLOTS]; +extern uint8_t log2_ui16(uint16_t i); +extern void spd_populate(uint16_t *vslots, uint8_t slot_count, uint16_t total_size, uint16_t min_module_size, uint16_t max_module_size, uint8_t enable_asym); extern void spd_register(uint8_t ram_type, uint8_t slot_mask, uint16_t max_module_size); diff --git a/src/mem/spd.c b/src/mem/spd.c index 84ba3eb84..5d26d7364 100644 --- a/src/mem/spd.c +++ b/src/mem/spd.c @@ -31,6 +31,7 @@ #define SPD_ROLLUP(x) ((x) >= 16 ? ((x) - 15) : (x)) +int spd_present = 0; spd_t *spd_devices[SPD_MAX_SLOTS]; uint8_t spd_data[SPD_MAX_SLOTS][SPD_DATA_SIZE]; @@ -156,41 +157,13 @@ comp_ui16_rev(const void *elem1, const void *elem2) void -spd_register(uint8_t ram_type, uint8_t slot_mask, uint16_t max_module_size) +spd_populate(uint16_t *vslots, uint8_t slot_count, uint16_t total_size, uint16_t min_module_size, uint16_t max_module_size, uint8_t enable_asym) { - uint8_t slot, slot_count, vslot, next_empty_vslot, i, split; - uint16_t min_module_size, total_size, vslots[SPD_MAX_SLOTS], asym; - device_t *info; - spd_edo_t *edo_data; - spd_sdram_t *sdram_data; - - /* determine the minimum module size for this RAM type */ - switch (ram_type) { - case SPD_TYPE_FPM: - case SPD_TYPE_EDO: - min_module_size = SPD_MIN_SIZE_EDO; - break; - - case SPD_TYPE_SDRAM: - min_module_size = SPD_MIN_SIZE_SDRAM; - break; - - default: - spd_log("SPD: unknown RAM type 0x%02X\n", ram_type); - return; - } - - /* count how many (real) slots are enabled */ - slot_count = 0; - for (slot = 0; slot < SPD_MAX_SLOTS; slot++) { - vslots[slot] = 0; - if (slot_mask & (1 << slot)) { - slot_count++; - } - } + uint8_t vslot, next_empty_vslot, split, i; + uint16_t asym; /* populate vslots with modules in power-of-2 capacities */ - total_size = (mem_size >> 10); + memset(vslots, 0x00, SPD_MAX_SLOTS << 1); for (vslot = 0; vslot < slot_count && total_size; vslot++) { /* populate slot */ vslots[vslot] = (1 << log2_ui16(MIN(total_size, max_module_size))); @@ -205,15 +178,17 @@ spd_register(uint8_t ram_type, uint8_t slot_mask, uint16_t max_module_size) /* did we populate all the RAM? */ if (total_size) { - /* work backwards to add the missing RAM as asymmetric modules */ - vslot = slot_count - 1; - do { - asym = (1 << log2_ui16(MIN(total_size, vslots[vslot]))); - if (vslots[vslot] + asym <= max_module_size) { - vslots[vslot] += asym; - total_size -= asym; - } - } while (vslot-- > 0 && total_size); + /* work backwards to add the missing RAM as asymmetric modules if possible */ + if (enable_asym) { + vslot = slot_count - 1; + do { + asym = (1 << log2_ui16(MIN(total_size, vslots[vslot]))); + if (vslots[vslot] + asym <= max_module_size) { + vslots[vslot] += asym; + total_size -= asym; + } + } while ((vslot-- > 0) && total_size); + } if (total_size) /* still not enough */ spd_log("SPD: not enough RAM slots (%d) to cover memory (%d MB short)\n", slot_count, total_size); @@ -241,12 +216,52 @@ spd_register(uint8_t ram_type, uint8_t slot_mask, uint16_t max_module_size) spd_log("SPD: splitting vslot %d (%d MB) into %d and %d (%d MB each)\n", vslot, vslots[vslot], vslot, next_empty_vslot, (vslots[vslot] >> 1)); vslots[vslot] = vslots[next_empty_vslot] = (vslots[vslot] >> 1); split = 1; + break; } - /* re-sort vslots by descending capacity if any modules were split */ + /* sort vslots by descending capacity if any were split */ if (split) qsort(vslots, slot_count, sizeof(uint16_t), comp_ui16_rev); } +} + + +void +spd_register(uint8_t ram_type, uint8_t slot_mask, uint16_t max_module_size) +{ + uint8_t slot, slot_count, vslot, i; + uint16_t min_module_size, vslots[SPD_MAX_SLOTS], asym; + device_t *info; + spd_edo_t *edo_data; + spd_sdram_t *sdram_data; + + /* determine the minimum module size for this RAM type */ + switch (ram_type) { + case SPD_TYPE_FPM: + case SPD_TYPE_EDO: + min_module_size = SPD_MIN_SIZE_EDO; + break; + + case SPD_TYPE_SDRAM: + min_module_size = SPD_MIN_SIZE_SDRAM; + break; + + default: + spd_log("SPD: unknown RAM type 0x%02X\n", ram_type); + return; + } + + /* count how many (real) slots are enabled */ + slot_count = 0; + for (slot = 0; slot < SPD_MAX_SLOTS; slot++) { + vslots[slot] = 0; + if (slot_mask & (1 << slot)) { + slot_count++; + } + } + + /* populate vslots */ + spd_populate(vslots, slot_count, (mem_size >> 10), min_module_size, max_module_size, 1); /* register SPD devices and populate their data according to the vslots */ vslot = 0; @@ -377,7 +392,9 @@ spd_register(uint8_t ram_type, uint8_t slot_mask, uint16_t max_module_size) break; } - device_add(info); + //device_add(info); vslot++; } + + spd_present = 1; }