Ejemplo n.º 1
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;
}
Ejemplo n.º 2
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;
}
Ejemplo n.º 3
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;
}
Ejemplo n.º 4
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;
}
Ejemplo n.º 5
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;
}
Ejemplo n.º 6
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;
}