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_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; }
static int parse_at(struct envy_bios *bios, struct envy_bios_mem *mem, int idx, int offset, const char ** name) { struct M_known_tables m1_tbls[] = { /* { 0x00, &mem->trestrict, "RESET" }, */ /* 0x01 Memory Strap Data Count */ { 0x03, &mem->trestrict, "RESTRICT" }, /* { 0x05, &mem->, "DATA VREF" }, */ /* { 0x07, &mem->, "DATA DQS" }, */ /* { 0x09, &mem->, "DATA DLCELL ON" }, */ /* { 0x0B, &mem->, "DATA DLCELL OFF" }, */ }; struct M_known_tables m2_tbls[] = { { 0x01, &mem->trestrict, "RESTRICT" }, { 0x03, &mem->type.offset, "TYPE" }, { 0x05, &mem->train.offset, "TRAIN" }, { 0x09, &mem->train_ptrn.offset, "TRAIN PATTERN" }, { 0x0d, &mem->unk0d.offset, "UNK0D" }, }; struct M_known_tables *tbls; int entries_count = 0; if (mem->bit->version == 0x1) { tbls = m1_tbls; entries_count = (sizeof(m1_tbls) / sizeof(struct M_known_tables)); } else if (mem->bit->version == 0x2) { tbls = m2_tbls; entries_count = (sizeof(m2_tbls) / sizeof(struct M_known_tables)); } else return -EINVAL; /* either we address by offset or idx */ if (idx != -1 && offset != -1) return -EINVAL; /* lookup the index by the table's offset */ if (offset > -1) { idx = 0; while (idx < entries_count && tbls[idx].offset != offset) idx++; } /* check the index */ if (idx < 0 || idx >= entries_count) return -ENOENT; /* check the table has the right size */ if (tbls[idx].offset + 2 > mem->bit->t_len) return -ENOENT; if (name) *name = tbls[idx].name; return bios_u16(bios, mem->bit->t_offset + tbls[idx].offset, tbls[idx].ptr); }
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_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_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; }
void envy_bios_print_bit_P (struct envy_bios *bios, FILE *out, unsigned mask) { struct envy_bios_power *power = &bios->power; const char *name; uint16_t addr; int ret = 0, i = 0; if (!power->bit || !(mask & ENVY_BIOS_PRINT_PERF)) return; fprintf(out, "BIT table 'P' at 0x%x, version %i\n", power->bit->offset, power->bit->version); for (i = 0; i < power->bit->t_len; i+=2) { ret = bios_u16(bios, power->bit->t_offset + i, &addr); if (!ret && addr) { name = "UNKNOWN"; ret = parse_at(bios, power, -1, i, &name); fprintf(out, "0x%02x: 0x%x => %s TABLE\n", i, addr, name); } } fprintf(out, "\n"); }
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; } }
static int parse_at(struct envy_bios *bios, struct envy_bios_power *power, int idx, int offset, const char ** name) { struct P_known_tables p1_tbls[] = { { 0x00, &power->perf.offset, "PERFORMANCE" }, { 0x04, &power->timing.offset, "MEMORY TIMINGS" }, { 0x0c, &power->therm.offset, "THERMAL" }, { 0x10, &power->volt.offset, "VOLTAGE" }, { 0x15, &power->unk18.offset, "POWER UNK18" } }; struct P_known_tables p2_tbls[] = { { 0x00, &power->perf.offset, "PERFORMANCE" }, { 0x04, &power->timing_map.offset, "MEMORY TIMINGS MAPPING" }, { 0x08, &power->timing.offset, "MEMORY TIMINGS" }, { 0x0c, &power->volt.offset, "VOLTAGE" }, { 0x10, &power->therm.offset, "THERMAL" }, { 0x14, &power->unk14.offset, "UNK14" }, { 0x18, &power->unk18.offset, "POWER UNK18" }, { 0x1c, &power->unk1c.offset, "POWER UNK1C" }, { 0x20, &power->volt_map.offset, "VOLT MAPPING" }, { 0x24, &power->unk24.offset, "POWER UNK24" }, { 0x28, &power->sense.offset, "POWER SENSE" }, { 0x2c, &power->budget.offset, "POWER BUDGET" }, { 0x30, &power->boost.offset, "BOOST" }, { 0x34, &power->cstep.offset, "CSTEP" }, { 0x38, &power->base_clock.offset, "POWER BASE CLOCK" }, { 0x3c, &power->unk3c.offset, "POWER UNK3C" }, { 0x40, &power->unk40.offset, "POWER UNK40" }, { 0x44, &power->unk44.offset, "POWER UNK44" }, { 0x48, &power->unk48.offset, "POWER UNK48" }, { 0x4c, &power->unk4c.offset, "POWER UNK4C" }, { 0x50, &power->unk50.offset, "POWER UNK50" }, { 0x54, &power->unk54.offset, "POWER UNK54" }, { 0x58, &power->fan.offset, "POWER FAN" }, { 0x5c, &power->unk5c.offset, "POWER UNK5C" }, { 0x60, &power->unk60.offset, "POWER UNK60" }, { 0x64, &power->unk64.offset, "POWER UNK64" } }; struct P_known_tables *tbls; int entries_count = 0; if (power->bit->version == 0x1) { tbls = p1_tbls; entries_count = (sizeof(p1_tbls) / sizeof(struct P_known_tables)); } else if (power->bit->version == 0x2) { tbls = p2_tbls; entries_count = (sizeof(p2_tbls) / sizeof(struct P_known_tables)); } else return -EINVAL; /* either we address by offset or idx */ if (idx != -1 && offset != -1) return -EINVAL; /* lookup the index by the table's offset */ if (offset > -1) { idx = 0; while (idx < entries_count && tbls[idx].offset != offset) idx++; } /* check the index */ if (idx < 0 || idx >= entries_count) return -ENOENT; /* check the table has the right size */ if (tbls[idx].offset + 2 > power->bit->t_len) return -ENOENT; if (name) *name = tbls[idx].name; return bios_u16(bios, power->bit->t_offset + tbls[idx].offset, tbls[idx].ptr); }