int envy_bios_parse_power_unk4c(struct envy_bios *bios) { struct envy_bios_power_unk4c *unk4c = &bios->power.unk4c; int i, err = 0; if (!unk4c->offset) return -EINVAL; bios_u8(bios, unk4c->offset + 0x0, &unk4c->version); switch(unk4c->version) { case 0x10: err |= bios_u8(bios, unk4c->offset + 0x1, &unk4c->hlen); err |= bios_u8(bios, unk4c->offset + 0x2, &unk4c->rlen); err |= bios_u8(bios, unk4c->offset + 0x3, &unk4c->entriesnum); unk4c->valid = !err; break; default: ENVY_BIOS_ERR("Unknown UNK4c table version 0x%x\n", unk4c->version); return -EINVAL; }; err = 0; unk4c->entries = malloc(unk4c->entriesnum * sizeof(struct envy_bios_power_unk4c_entry)); for (i = 0; i < unk4c->entriesnum; i++) { uint16_t data = unk4c->offset + unk4c->hlen + i * unk4c->rlen; unk4c->entries[i].offset = data; } return 0; }
int envy_bios_parse_iunk21 (struct envy_bios *bios) { struct envy_bios_iunk21 *iunk21 = &bios->iunk21; if (!iunk21->offset) return 0; int err = 0; err |= bios_u8(bios, iunk21->offset, &iunk21->version); err |= bios_u8(bios, iunk21->offset+1, &iunk21->hlen); err |= bios_u8(bios, iunk21->offset+2, &iunk21->rlen); err |= bios_u8(bios, iunk21->offset+3, &iunk21->entriesnum); if (err) return -EFAULT; envy_bios_block(bios, iunk21->offset, iunk21->hlen + iunk21->rlen * iunk21->entriesnum, "IUNK21", -1); int wanthlen = 0; int wantrlen = 0; switch (iunk21->version) { case 0x10: wanthlen = 4; wantrlen = 3; break; default: ENVY_BIOS_ERR("Unknown IUNK21 table version %d.%d\n", iunk21->version >> 4, iunk21->version & 0xf); return -EINVAL; } if (iunk21->hlen < wanthlen) { ENVY_BIOS_ERR("IUNK21 table header too short [%d < %d]\n", iunk21->hlen, wanthlen); return -EINVAL; } if (iunk21->rlen < wantrlen) { ENVY_BIOS_ERR("IUNK21 table record too short [%d < %d]\n", iunk21->rlen, wantrlen); return -EINVAL; } if (iunk21->hlen > wanthlen) { ENVY_BIOS_WARN("IUNK21 table header longer than expected [%d > %d]\n", iunk21->hlen, wanthlen); } if (iunk21->rlen > wantrlen) { ENVY_BIOS_WARN("IUNK21 table record longer than expected [%d > %d]\n", iunk21->rlen, wantrlen); } iunk21->entries = calloc(iunk21->entriesnum, sizeof *iunk21->entries); if (!iunk21->entries) return -ENOMEM; int i; for (i = 0; i < iunk21->entriesnum; i++) { struct envy_bios_iunk21_entry *entry = &iunk21->entries[i]; entry->offset = iunk21->offset + iunk21->hlen + iunk21->rlen * i; /* XXX */ if (err) return -EFAULT; } iunk21->valid = 1; return 0; }
int envy_bios_parse_power_cstep(struct envy_bios *bios) { struct envy_bios_power_cstep *cstep = &bios->power.cstep; int i, err = 0; if (!cstep->offset) return -EINVAL; bios_u8(bios, cstep->offset + 0x0, &cstep->version); switch(cstep->version) { case 0x10: err |= bios_u8(bios, cstep->offset + 0x1, &cstep->hlen); err |= bios_u8(bios, cstep->offset + 0x2, &cstep->rlen); err |= bios_u8(bios, cstep->offset + 0x3, &cstep->entriesnum); err |= bios_u8(bios, cstep->offset + 0x4, &cstep->ssz); err |= bios_u8(bios, cstep->offset + 0x5, &cstep->snr); cstep->valid = !err; break; default: ENVY_BIOS_ERR("Unknown CSTEP table version 0x%x\n", cstep->version); return -EINVAL; }; assert(cstep->entriesnum <= (sizeof(cstep->ent1) / sizeof(struct envy_bios_power_cstep_entry1))); for (i = 0; i < cstep->entriesnum; i++) { uint16_t data = cstep->offset + cstep->hlen + i * cstep->rlen; uint16_t tmp; err |= bios_u16(bios, data + 0x0, &tmp); cstep->ent1[i].offset = data; cstep->ent1[i].pstate = (tmp & 0x01e0) >> 5; bios_u8(bios, data + 0x3, &cstep->ent1[i].index); } cstep->ent2 = malloc(cstep->snr * sizeof(struct envy_bios_power_cstep_entry2)); memset(cstep->ent2, 0x0, cstep->snr * sizeof(struct envy_bios_power_cstep_entry2)); for (i = 0; i < cstep->snr; i++) { uint16_t data = cstep->offset + cstep->hlen + (cstep->entriesnum * cstep->rlen) + (i * cstep->ssz); cstep->ent2[i].offset = data; bios_u16(bios, data + 0x0, &cstep->ent2[i].freq); bios_u8(bios, data + 0x2, &cstep->ent2[i].unkn[0]); bios_u8(bios, data + 0x3, &cstep->ent2[i].unkn[1]); bios_u8(bios, data + 0x4, &cstep->ent2[i].voltage); cstep->ent2[i].valid = (cstep->ent2[i].freq > 0); } return 0; }
int envy_bios_parse_power_base_clock(struct envy_bios *bios) { struct envy_bios_power_base_clock *bc = &bios->power.base_clock; int i, j, err = 0; if (!bc->offset) return -EINVAL; bios_u8(bios, bc->offset + 0x0, &bc->version); switch(bc->version) { case 0x10: err |= bios_u8(bios, bc->offset + 0x1, &bc->hlen); err |= bios_u8(bios, bc->offset + 0x2, &bc->rlen); err |= bios_u8(bios, bc->offset + 0x3, &bc->selen); err |= bios_u8(bios, bc->offset + 0x4, &bc->secount); err |= bios_u8(bios, bc->offset + 0x5, &bc->entriesnum); bc->valid = !err; if (bc->valid) { bios_u8(bios, bc->offset + 0x0f, &bc->base_entry); bios_u8(bios, bc->offset + 0x10, &bc->boost_entry); bios_u8(bios, bc->offset + 0x11, &bc->tdp_entry); } else { return -EINVAL; } break; default: ENVY_BIOS_ERR("BASE CLOCKS table version 0x%x\n", bc->version); return -EINVAL; }; err = 0; bc->entries = malloc(bc->entriesnum * sizeof(struct envy_bios_power_base_clock_entry)); for (i = 0; i < bc->entriesnum; i++) { struct envy_bios_power_base_clock_entry *bce = &bc->entries[i]; bce->offset = bc->offset + bc->hlen + i * (bc->rlen + (bc->selen * bc->secount)); bios_u8(bios, bce->offset, &bce->pstate); bios_u16(bios, bce->offset + 0x1, &bce->unk0); bios_u16(bios, bce->offset + 0x3, &bce->unk1); bce->clock = malloc(bc->secount * sizeof(uint16_t)); for (j = 0; j < bc->secount; j++) bios_u16(bios, bce->offset + 0x5 + j, &bce->clock[j]); } return 0; }
int envy_bios_parse_i2c (struct envy_bios *bios) { struct envy_bios_i2c *i2c = &bios->i2c; if (!i2c->offset) return 0; int err = 0; int wanthlen = 5; int wantrlen = 4; uint8_t defs; if (bios->dcb.version >= 0x30) { err |= bios_u8(bios, i2c->offset, &i2c->version); err |= bios_u8(bios, i2c->offset+1, &i2c->hlen); err |= bios_u8(bios, i2c->offset+2, &i2c->entriesnum); err |= bios_u8(bios, i2c->offset+3, &i2c->rlen); err |= bios_u8(bios, i2c->offset+4, &defs); i2c->def[0] = defs & 0xf; i2c->def[1] = defs >> 4; } else {
int envy_bios_parse_power_budget(struct envy_bios *bios) { struct envy_bios_power_budget *budget = &bios->power.budget; int i, err = 0; if (!budget->offset) return -EINVAL; bios_u8(bios, budget->offset + 0x0, &budget->version); switch(budget->version) { case 0x10: case 0x20: case 0x30: err |= bios_u8(bios, budget->offset + 0x1, &budget->hlen); err |= bios_u8(bios, budget->offset + 0x2, &budget->rlen); err |= bios_u8(bios, budget->offset + 0x3, &budget->entriesnum); budget->valid = !err; break; default: ENVY_BIOS_ERR("Unknown POWER BUDGET table version 0x%x\n", budget->version); return -EINVAL; }; err = 0; budget->entries = malloc(budget->entriesnum * sizeof(struct envy_bios_power_budget_entry)); memset(budget->entries, 0x0, budget->entriesnum * sizeof(struct envy_bios_power_budget_entry)); for (i = 0; i < budget->entriesnum; i++) { uint16_t data = budget->offset + budget->hlen + i * budget->rlen; budget->entries[i].offset = data; if (budget->rlen == 0x6) { err |= bios_u32(bios, data + 0x02, &budget->entries[i].avg); } else { err |= bios_u32(bios, data + 0x02, &budget->entries[i].min); err |= bios_u32(bios, data + 0x06, &budget->entries[i].avg); err |= bios_u32(bios, data + 0x0a, &budget->entries[i].peak); err |= bios_u32(bios, data + 0x12, &budget->entries[i].unkn12); } budget->entries[i].valid = !err; } return 0; }
int envy_bios_parse_power_boost(struct envy_bios *bios) { struct envy_bios_power_boost *boost = &bios->power.boost; int i, j, err = 0; if (!boost->offset) return -EINVAL; bios_u8(bios, boost->offset + 0x0, &boost->version); switch(boost->version) { case 0x11: err |= bios_u8(bios, boost->offset + 0x1, &boost->hlen); err |= bios_u8(bios, boost->offset + 0x2, &boost->rlen); err |= bios_u8(bios, boost->offset + 0x3, &boost->ssz); err |= bios_u8(bios, boost->offset + 0x4, &boost->snr); err |= bios_u8(bios, boost->offset + 0x5, &boost->entriesnum); boost->valid = !err; break; default: ENVY_BIOS_ERR("Unknown BOOST table version 0x%x\n", boost->version); return -EINVAL; }; boost->entries = malloc(boost->entriesnum * sizeof(struct envy_bios_power_boost_entry)); for (i = 0; i < boost->entriesnum; i++) { uint16_t data = boost->offset + boost->hlen + i * (boost->rlen + (boost->snr * boost->ssz)); uint16_t tmp; err |= bios_u16(bios, data + 0x0, &tmp); err |= bios_u16(bios, data + 0x2, &boost->entries[i].min); err |= bios_u16(bios, data + 0x4, &boost->entries[i].max); boost->entries[i].offset = data; boost->entries[i].pstate = (tmp & 0x01e0) >> 5; boost->entries[i].entries = malloc(boost->snr * sizeof(struct envy_bios_power_boost_subentry)); for (j = 0; j < boost->snr; j++) { struct envy_bios_power_boost_subentry *sub = &boost->entries[i].entries[j]; uint16_t sdata = data + boost->rlen + j * boost->ssz; sub->offset = sdata; bios_u8(bios, sdata + 0x0, &sub->domain); bios_u8(bios, sdata + 0x1, &sub->percent); bios_u16(bios, sdata + 0x2, &sub->min); bios_u16(bios, sdata + 0x4, &sub->max); } } return 0; }
int envy_bios_parse_mux (struct envy_bios *bios) { struct envy_bios_mux *mux = &bios->mux; if (!mux->offset) return 0; int err = 0; err |= bios_u8(bios, mux->offset, &mux->version); err |= bios_u8(bios, mux->offset+1, &mux->hlen); err |= bios_u8(bios, mux->offset+2, &mux->entriesnum); err |= bios_u8(bios, mux->offset+3, &mux->rlen); if (err) return -EFAULT; envy_bios_block(bios, mux->offset, mux->hlen + mux->rlen * mux->entriesnum, "MUX", -1); int wanthlen = 0; int wantrlen = 0; switch (mux->version) { case 0x10: wanthlen = 4; wantrlen = 5; break; default: ENVY_BIOS_ERR("Unknown MUX table version %x.%x\n", mux->version >> 4, mux->version & 0xf); return -EINVAL; } if (mux->hlen < wanthlen) { ENVY_BIOS_ERR("MUX table header too short [%d < %d]\n", mux->hlen, wanthlen); return -EINVAL; } if (mux->rlen < wantrlen) { ENVY_BIOS_ERR("MUX table record too short [%d < %d]\n", mux->rlen, wantrlen); return -EINVAL; } if (mux->hlen > wanthlen) { ENVY_BIOS_WARN("MUX table header longer than expected [%d > %d]\n", mux->hlen, wanthlen); } if (mux->rlen > wantrlen) { ENVY_BIOS_WARN("MUX table record longer than expected [%d > %d]\n", mux->rlen, wantrlen); } mux->entries = calloc(mux->entriesnum, sizeof *mux->entries); if (!mux->entries) return -ENOMEM; int i; for (i = 0; i < mux->entriesnum; i++) { struct envy_bios_mux_entry *entry = &mux->entries[i]; entry->offset = mux->offset + mux->hlen + mux->rlen * i; uint8_t sub[4]; err |= bios_u8(bios, entry->offset, &entry->idx); int j; for (j = 0; j < 4; j++) { err |= bios_u8(bios, entry->offset+j+1, &sub[j]); entry->sub_loc[j] = sub[j] & 1; entry->sub_line[j] = sub[j] >> 1 & 0x1f; entry->sub_val[j] = sub[j] >> 6 & 1; entry->sub_unk7[j] = sub[j] >> 7 & 1; } if (err) return -EFAULT; } mux->valid = 1; return 0; }
int envy_bios_parse_power_unk44(struct envy_bios *bios) { struct envy_bios_power_unk44 *unk44 = &bios->power.unk44; int err = 0; if (!unk44->offset) return -EINVAL; bios_u8(bios, unk44->offset + 0x0, &unk44->version); switch(unk44->version) { case 0x10: err |= bios_u8(bios, unk44->offset + 0x1, &unk44->hlen); /* it doesn't appear as if this table has entries at all */ unk44->valid = !err; break; default: ENVY_BIOS_ERR("Unknown UNK44 table version 0x%x\n", unk44->version); return -EINVAL; }; return 0; }
int envy_bios_parse_mem_train_ptrn(struct envy_bios *bios) { struct envy_bios_mem_train_ptrn *mtp = &bios->mem.train_ptrn; if (!mtp->offset) return 0; int err = 0; err |= bios_u8(bios, mtp->offset, &mtp->version); err |= bios_u8(bios, mtp->offset+1, &mtp->hlen); err |= bios_u8(bios, mtp->offset+2, &mtp->rlen); err |= bios_u8(bios, mtp->offset+3, &mtp->subentrylen); err |= bios_u8(bios, mtp->offset+4, &mtp->entriesnum); mtp->subentries = 1; if (err) return -EFAULT; envy_bios_block(bios, mtp->offset, mtp->hlen + mtp->rlen * mtp->entriesnum, "MEM TRAIN PATTERN", -1); mtp->entries = calloc(mtp->entriesnum, sizeof *mtp->entries); if (!mtp->entries) return -ENOMEM; int i; for (i = 0; i < mtp->entriesnum; i++) { struct envy_bios_mem_train_ptrn_entry *entry = &mtp->entries[i]; entry->offset = mtp->offset + mtp->hlen + ((mtp->rlen + mtp->subentries * mtp->subentrylen) * i); err |= bios_u8(bios, entry->offset, &entry->bits); entry->bits &= 0x3f; err |= bios_u8(bios, entry->offset+1, &entry->modulo); if (mtp->rlen >= 4) { err |= bios_u8(bios, entry->offset+2, &entry->indirect); entry->indirect = (entry->indirect & 0x7) == 0x2; err |= bios_u8(bios, entry->offset+3, &entry->indirect_entry); } else { entry->indirect = 0; entry->indirect_entry = i; } } mtp->valid = 1; return 0; }
int envy_bios_parse_power_sense(struct envy_bios *bios) { struct envy_bios_power_sense *sense = &bios->power.sense; int i, err = 0; if (!sense->offset) return -EINVAL; bios_u8(bios, sense->offset + 0x0, &sense->version); switch(sense->version) { case 0x10: case 0x20: err |= bios_u8(bios, sense->offset + 0x1, &sense->hlen); err |= bios_u8(bios, sense->offset + 0x2, &sense->rlen); err |= bios_u8(bios, sense->offset + 0x3, &sense->entriesnum); sense->valid = !err; break; default: ENVY_BIOS_ERR("Unknown SENSE table version 0x%x\n", sense->version); return -EINVAL; }; err = 0; sense->entries = malloc(sense->entriesnum * sizeof(struct envy_bios_power_sense_entry)); for (i = 0; i < sense->entriesnum; i++) { uint16_t data = sense->offset + sense->hlen + i * sense->rlen; sense->entries[i].offset = data; switch(sense->version) { case 0x10: err |= bios_u8(bios, data + 0x2, &sense->entries[i].extdev_id); err |= bios_u8(bios, data + 0x3, &sense->entries[i].resistor_mohm); break; case 0x20: err |= bios_u8(bios, data + 0x1, &sense->entries[i].extdev_id); err |= bios_u8(bios, data + 0x5, &sense->entries[i].resistor_mohm); break; }; } return 0; }
int envy_bios_parse_mem_train (struct envy_bios *bios) { struct envy_bios_mem_train *mt = &bios->mem.train; if (!mt->offset) return 0; int err = 0; err |= bios_u8(bios, mt->offset, &mt->version); err |= bios_u8(bios, mt->offset+1, &mt->hlen); err |= bios_u8(bios, mt->offset+2, &mt->rlen); err |= bios_u8(bios, mt->offset+3, &mt->subentrylen); err |= bios_u8(bios, mt->offset+4, &mt->subentries); err |= bios_u8(bios, mt->offset+5, &mt->entriesnum); if (err) return -EFAULT; bios_u16(bios, mt->offset+6, &mt->mclk); envy_bios_block(bios, mt->offset, mt->hlen + mt->rlen * mt->entriesnum, "MEM TRAIN", -1); mt->entries = calloc(mt->entriesnum, sizeof *mt->entries); if (!mt->entries) return -ENOMEM; int i; for (i = 0; i < mt->entriesnum; i++) { struct envy_bios_mem_train_entry *entry = &mt->entries[i]; entry->offset = mt->offset + mt->hlen + ((mt->rlen + mt->subentries * mt->subentrylen) * i); err |= bios_u8(bios, entry->offset, &entry->u00); if (mt->subentries > sizeof(entry->subentry)) { mt->subentries = sizeof(entry->subentry); ENVY_BIOS_ERR("Error when parsing mem train: subentries = %d > %zu\n", mt->subentries, sizeof(entry->subentry)); return -EFAULT; } int j; for (j = 0; j < mt->subentries; j++) { err |= bios_u8(bios, entry->offset+j+1, &entry->subentry[j]); } if (err) return -EFAULT; } mt->valid = 1; return 0; }
int envy_bios_parse_power_fan(struct envy_bios *bios) { struct envy_bios_power_fan *fan = &bios->power.fan; uint16_t data; int err = 0; if (!fan->offset) return -EINVAL; bios_u8(bios, fan->offset + 0x0, &fan->version); switch(fan->version) { case 0x10: err |= bios_u8(bios, fan->offset + 0x1, &fan->hlen); err |= bios_u8(bios, fan->offset + 0x2, &fan->rlen); err |= bios_u8(bios, fan->offset + 0x3, &fan->entriesnum); fan->valid = !err; break; default: ENVY_BIOS_ERR("Unknown FAN table version 0x%x\n", fan->version); return -EINVAL; }; /* go to the first entry */ data = fan->offset + fan->hlen; bios_u8(bios, data + 0x00, &fan->type); bios_u8(bios, data + 0x02, &fan->duty_min); bios_u8(bios, data + 0x03, &fan->duty_max); /* 0x10 == constant to 9? */ bios_u32(bios, data + 0x0b, &fan->divisor); fan->divisor &= 0xffffff; bios_u16(bios, data + 0x0e, &fan->unk0e); /* looks like the fan bump delay */ bios_u16(bios, data + 0x10, &fan->unk10); /* looks like the fan slow down delay */ bios_u16(bios, data + 0x14, &fan->unboost_unboost_ms); bios_u8(bios, data + 0x17, &fan->duty_boosted); /* threshold = 96 °C */ /* temp fan bump min = 45°C */ /* temp fan max = 95°C */ return 0; }
int envy_bios_parse_bit (struct envy_bios *bios) { struct envy_bios_bit *bit = &bios->bit; if (!bit->offset) return 0; int err = 0; err |= bios_u8(bios, bit->offset+7, &bit->version); err |= bios_u8(bios, bit->offset+8, &bit->hlen); err |= bios_u8(bios, bit->offset+9, &bit->rlen); err |= bios_u8(bios, bit->offset+10, &bit->entriesnum); if (err) return -EFAULT; envy_bios_block(bios, bit->offset, bit->hlen + bit->rlen * bit->entriesnum, "BIT", -1); uint8_t checksum = 0; int i; for (i = 0; i < bit->hlen; i++) { uint8_t byte; err |= bios_u8(bios, bit->offset+i, &byte); if (err) return -EFAULT; checksum += byte; } if (checksum) { ENVY_BIOS_ERR("BIT table checksum mismatch\n"); return -EINVAL; } int wanthlen = 12; int wantrlen = 6; switch (bit->version) { case 1: break; default: ENVY_BIOS_ERR("Unknown BIT table version %d\n", bit->version); return -EINVAL; } if (bit->hlen < wanthlen) { ENVY_BIOS_ERR("BIT table header too short [%d < %d]\n", bit->hlen, wanthlen); return -EINVAL; } if (bit->rlen < wantrlen) { ENVY_BIOS_ERR("BIT table record too short [%d < %d]\n", bit->rlen, wantrlen); return -EINVAL; } if (bit->hlen > wanthlen) { ENVY_BIOS_WARN("BIT table header longer than expected [%d > %d]\n", bit->hlen, wanthlen); } if (bit->rlen > wantrlen) { ENVY_BIOS_WARN("BIT table record longer than expected [%d > %d]\n", bit->rlen, wantrlen); } bit->entries = calloc(bit->entriesnum, sizeof *bit->entries); if (!bit->entries) return -ENOMEM; for (i = 0; i < bit->entriesnum; i++) { struct envy_bios_bit_entry *entry = &bit->entries[i]; entry->offset = bit->offset + bit->hlen + bit->rlen * i; err |= bios_u8(bios, entry->offset+0, &entry->type); err |= bios_u8(bios, entry->offset+1, &entry->version); err |= bios_u16(bios, entry->offset+2, &entry->t_len); err |= bios_u16(bios, entry->offset+4, &entry->t_offset); if (err) return -EFAULT; entry->is_unk = 1; if (entry->type != 'b' && entry->t_len) envy_bios_block(bios, entry->t_offset, entry->t_len, "BIT", entry->type); } int j; /* iterate over BIT tables by type first - some types of tables have to be parsed before others, notably 'i'. */ for (j = 0; bit_types[j].parse; j++) { for (i = 0; i < bit->entriesnum; i++) { struct envy_bios_bit_entry *entry = &bit->entries[i]; if (entry->type == bit_types[j].type && entry->version == bit_types[j].version) { if (bit_types[j].parse(bios, entry)) ENVY_BIOS_ERR("Failed to parse BIT table '%c' at 0x%04x version %d\n", entry->type, entry->t_offset, entry->version); else entry->is_unk = 0; } } } bit->valid = 1; return 0; }
static void print_nv01_init_script(struct envy_bios *bios, FILE *out, unsigned offset, unsigned mask) { unsigned len; uint8_t op; uint8_t arg8_0, arg8_1, arg8_2; uint16_t arg16_0; uint32_t arg32_0, arg32_1, arg32_2; int err = 0; if (!(mask & ENVY_BIOS_PRINT_SCRIPTS)) return; fprintf(out, "Init script at 0x%x:\n", offset); while (1) { if (bios_u8(bios, offset, &op)) { ENVY_BIOS_ERR("Init script out of bounds!\n"); return; } switch (op) { case 0x6e: /* NV01+ */ len = 13; err |= bios_u32(bios, offset+1, &arg32_0); err |= bios_u32(bios, offset+5, &arg32_1); err |= bios_u32(bios, offset+9, &arg32_2); if (err) { ENVY_BIOS_ERR("Init script out of bounds!\n"); return; } dump_hex_script(bios, out, offset, len); fprintf(out, "\tMMIO_MASK 0x%06x &= 0x%08x |= 0x%08x\n", arg32_0, arg32_1, arg32_2); break; case 0x7a: /* NV03+ */ len = 9; err |= bios_u32(bios, offset+1, &arg32_0); err |= bios_u32(bios, offset+5, &arg32_1); if (err) { ENVY_BIOS_ERR("Init script out of bounds!\n"); return; } dump_hex_script(bios, out, offset, len); fprintf(out, "\tMMIO_WR 0x%06x <= 0x%08x\n", arg32_0, arg32_1); break; case 0x77: /* NV03+ */ len = 7; err |= bios_u32(bios, offset+1, &arg32_0); err |= bios_u16(bios, offset+5, &arg16_0); if (err) { ENVY_BIOS_ERR("Init script out of bounds!\n"); return; } dump_hex_script(bios, out, offset, len); fprintf(out, "\tMMIO_WR16 0x%06x <= 0x%04x\n", arg32_0, arg16_0); break; case 0x79: /* NV03+ */ len = 13; err |= bios_u32(bios, offset+1, &arg32_0); err |= bios_u32(bios, offset+5, &arg32_1); err |= bios_u32(bios, offset+9, &arg32_2); if (err) { ENVY_BIOS_ERR("Init script out of bounds!\n"); return; } dump_hex_script(bios, out, offset, len); fprintf(out, "\tMMIO_WR_CRYSTAL 0x%06x <= 0x%08x / 0x%08x\n", arg32_0, arg32_1, arg32_2); break; case 0x74: /* NV03+ */ len = 3; err |= bios_u16(bios, offset+1, &arg16_0); if (err) { ENVY_BIOS_ERR("Init script out of bounds!\n"); return; } dump_hex_script(bios, out, offset, len); fprintf(out, "\tUSLEEP %d\n", arg16_0); break; case 0x69: /* NV03+ */ len = 5; err |= bios_u16(bios, offset+1, &arg16_0); err |= bios_u8(bios, offset+3, &arg8_0); err |= bios_u8(bios, offset+4, &arg8_1); if (err) { ENVY_BIOS_ERR("Init script out of bounds!\n"); return; } dump_hex_script(bios, out, offset, len); fprintf(out, "\tIO_MASK 0x%04x &= 0x%02x |= 0x%02x\n", arg16_0, arg8_0, arg8_1); break; case 0x78: /* NV03+ */ len = 6; err |= bios_u16(bios, offset+1, &arg16_0); err |= bios_u8(bios, offset+3, &arg8_0); err |= bios_u8(bios, offset+4, &arg8_1); err |= bios_u8(bios, offset+5, &arg8_2); if (err) { ENVY_BIOS_ERR("Init script out of bounds!\n"); return; } dump_hex_script(bios, out, offset, len); fprintf(out, "\tIOIDX_MASK 0x%04x[0x%02x] &= 0x%02x |= 0x%02x\n", arg16_0, arg8_0, arg8_1, arg8_2); break; case 0x6d: /* NV03+ */ len = 2; err |= bios_u8(bios, offset+1, &arg8_0); if (err) { ENVY_BIOS_ERR("Init script out of bounds!\n"); return; } dump_hex_script(bios, out, offset, len); fprintf(out, "\tIF_MEM_SIZE 0x%02x\n", arg8_0); break; case 0x73: /* NV03+ */ len = 9; err |= bios_u32(bios, offset+1, &arg32_0); err |= bios_u32(bios, offset+5, &arg32_1); if (err) { ENVY_BIOS_ERR("Init script out of bounds!\n"); return; } dump_hex_script(bios, out, offset, len); fprintf(out, "\tIF_STRAPS & 0x%08x == 0x%08x\n", arg32_0, arg32_1); break; case 0x72: /* NV03+ */ len = 1; dump_hex_script(bios, out, offset, len); fprintf(out, "\tRESUME\n"); break; case 0x63: /* NV03+ */ len = 1; dump_hex_script(bios, out, offset, len); fprintf(out, "\tCOMPUTE_MEM\n"); break; case 0x71: /* NV01+ */ len = 1; dump_hex_script(bios, out, offset, len); fprintf(out, "\tQUIT\n"); fprintf(out, "\n"); return; case 0x70: /* NV01:NV03 */ len = 7; err |= bios_u16(bios, offset+1, &arg16_0); err |= bios_u32(bios, offset+3, &arg32_0); if (err) { ENVY_BIOS_ERR("Init script out of bounds!\n"); return; } dump_hex_script(bios, out, offset, len); fprintf(out, "\tDAC_PLL 0x%04x <= 0x%08x\n", arg16_0, arg32_0); break; case 0x64: /* NV01:NV03 */ len = 5; err |= bios_u16(bios, offset+1, &arg16_0); err |= bios_u8(bios, offset+3, &arg8_0); err |= bios_u8(bios, offset+4, &arg8_1); if (err) { ENVY_BIOS_ERR("Init script out of bounds!\n"); return; } dump_hex_script(bios, out, offset, len); fprintf(out, "\tDAC_MASK 0x%04x &= 0x%02x |= 0x%02x\n", arg16_0, arg8_0, arg8_1); break; case 0xff: /* NV01:NV03 */ len = 1; dump_hex_script(bios, out, offset, len); fprintf(out, "\tQUIT\n"); fprintf(out, "\n"); return; default: len = 1; dump_hex_script(bios, out, offset, len); fprintf(out, "\n"); ENVY_BIOS_ERR("Unknown op 0x%02x in init script!\n", op); return; } offset += len; } }
int envy_bios_parse_conn (struct envy_bios *bios) { struct envy_bios_conn *conn = &bios->conn; if (!conn->offset) return 0; int err = 0; err |= bios_u8(bios, conn->offset, &conn->version); err |= bios_u8(bios, conn->offset+1, &conn->hlen); err |= bios_u8(bios, conn->offset+2, &conn->entriesnum); err |= bios_u8(bios, conn->offset+3, &conn->rlen); if (err) return -EFAULT; envy_bios_block(bios, conn->offset, conn->hlen + conn->rlen * conn->entriesnum, "CONN", -1); int wanthlen = 5; int wantrlen = 4; if (conn->rlen < 4) wantrlen = 2; switch (conn->version) { case 0x30: case 0x40: break; default: ENVY_BIOS_ERR("Unknown CONN table version %d.%d\n", conn->version >> 4, conn->version & 0xf); return -EINVAL; } if (conn->hlen < wanthlen) { ENVY_BIOS_ERR("CONN table header too short [%d < %d]\n", conn->hlen, wanthlen); return -EINVAL; } if (conn->rlen < wantrlen) { ENVY_BIOS_ERR("CONN table record too short [%d < %d]\n", conn->rlen, wantrlen); return -EINVAL; } if (conn->hlen > wanthlen) { ENVY_BIOS_WARN("CONN table header longer than expected [%d > %d]\n", conn->hlen, wanthlen); } if (conn->rlen > wantrlen) { ENVY_BIOS_WARN("CONN table record longer than expected [%d > %d]\n", conn->rlen, wantrlen); } conn->entries = calloc(conn->entriesnum, sizeof *conn->entries); if (!conn->entries) return -ENOMEM; int i; for (i = 0; i < conn->entriesnum; i++) { struct envy_bios_conn_entry *entry = &conn->entries[i]; entry->offset = conn->offset + conn->hlen + conn->rlen * i; uint8_t bytes[4] = { 0 }; uint32_t val = 0; int j; static const int hpds[7] = { 12, 13, 16, 17, 24, 25, 26 }; entry->hpd = -1; entry->dp_ext = -1; for (j = 0; j < 4 && j < conn->rlen; j++) { err |= bios_u8(bios, entry->offset+j, &bytes[j]); if (err) return -EFAULT; val |= bytes[j] << j * 8; } entry->type = bytes[0]; entry->tag = bytes[1] & 0xf; for (j = 0; j < 7; j++) { if (val & 1 << hpds[j]) { if (entry->hpd == -1) entry->hpd = j; else ENVY_BIOS_ERR("CONN %d: duplicate HPD bits\n", i); } } for (j = 0; j < 2; j++) { if (val & 1 << (j+14)) { if (entry->dp_ext == -1) entry->dp_ext = j; else ENVY_BIOS_ERR("CONN %d: duplicate DP_AUX bits\n", i); } } entry->unk02_2 = bytes[2] >> 2 & 3; entry->unk02_4 = bytes[2] >> 4 & 7; entry->unk02_7 = bytes[2] >> 7 & 1; entry->unk03_3 = bytes[3] >> 3 & 0x1f; } conn->valid = 1; return 0; }