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_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_bit_2 (struct envy_bios *bios, struct envy_bios_bit_entry *bit) { struct envy_bios_i2cscript *i2cscript = &bios->i2cscript; i2cscript->bit = bit; int wantlen = 4; if (bit->t_len < wantlen) { ENVY_BIOS_ERR("I2C script table too short: %d < %d\n", bit->t_len, wantlen); return -EINVAL; } if (bit->t_len > wantlen) ENVY_BIOS_WARN("I2C script table longer than expected: %d > %d\n", bit->t_len, wantlen); int err = 0; err |= bios_u16(bios, bit->t_offset+0, &i2cscript->unk00); err |= bios_u16(bios, bit->t_offset+2, &i2cscript->script); if (err) return -EFAULT; i2cscript->valid = 1; 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; }
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; }