#include #if defined(__i386__) || defined(__x86_64__) || defined(__ia64__) #include #endif #include #include #include #include #include #include #include #include #include #include #include "get-edid.h" #include "int10/vbios.h" #ifdef __i386__ /* some regressions can occur. e.g. Shuttle S754 with built-in VIA VGA, in that case default to previous lrmi code */ #define LRMI #endif #include int box_is_xbox(); /* also used by libint10 */ void log_err(char *format, ...) { if (verbose) { va_list args; va_start(args, format); vfprintf(stderr, format, args); va_end(args); } } /* * Create a 'canonical' version, i.e. no spaces at start and end. * * Note: removes chars >= 0x80 as well (due to (char *))! This * is currently considered a feature. */ static char *canon_str(char *s, int len) { char *start, *end; for (start = s; start < s + len; start++) if (start[0] > ' ') break; for (end = &s[len - 1]; end >= start; end--) { if (end[0] > ' ') break; end[0] = 0; } return start; } static unsigned segofs2addr(unsigned char *segofs) { return segofs[0] + (segofs[1] << 8) + (segofs[2] << 4)+ (segofs[3] << 12); } static unsigned get_data(unsigned char *buf, unsigned buf_size, unsigned addr) { unsigned bufferaddr = 0x7e00; unsigned len; *buf = 0; len = 0; if(addr >= bufferaddr && addr < bufferaddr + 0x200) { len = bufferaddr + 0x200 - addr; if(len >= buf_size) len = buf_size - 1; memcpy(buf, addr + (char *) 0, len); } else if(addr >= 0x0c0000 && addr < 0x100000) { len = 0x100000 - addr; if(len >= buf_size) len = buf_size - 1; memcpy(buf, addr + (char *) 0, len); } buf[len] = 0; return len; } static char *get_str(unsigned char *buf, unsigned buf_size, char *v) { int len = get_data(buf, buf_size, segofs2addr(v)); return canon_str(buf, len); } #define GET_WORD(ADDR, OFS) ((ADDR)[OFS] + ((ADDR)[(OFS) + 1] << 8)) static void parse_vbe_info(unsigned char *v) { unsigned char tmp[1024]; /* Parse VBE block */ int version = GET_WORD(v, 0x04); int oem_version = GET_WORD(v, 0x14); int memory_size = GET_WORD(v, 0x12) << 16; log_err("VBE version: %u.%u, oem version = %u.%u\n", version >> 8, version & 0xff, oem_version >> 8, oem_version & 0xff); log_err("Memory: %uk\n", memory_size >> 10); log_err("OEM name: %s\n", get_str(tmp, sizeof tmp, v + 0x06)); log_err("Vendor name: %s\n", get_str(tmp, sizeof tmp, v + 0x16)); log_err("Product name: %s\n", get_str(tmp, sizeof tmp, v + 0x1a)); log_err("Product revision: %s\n", get_str(tmp, sizeof tmp, v + 0x1e)); } static int vbe_check_vbe_info(hd_data_t *hd_data) { int i; unsigned char v[0x200]; int ax, bx, cx; /* Setup registers for the interrupt call */ ax = 0x4f00; bx = 0; cx = 0; memset(v, 0, sizeof(v)); strcpy(v, "VBE2"); /* Get VBE block */ i = CallInt10(&ax, &bx, &cx, v, sizeof(v), hd_data->flags.cpuemu) & 0xffff; if (i != 0x4f) { log_err("VBE: Error (0x4f00): 0x%04x\n", i); return 0; } parse_vbe_info(v); return 1; } /* Get EDID info. */ int vbe_get_edid_info(hd_data_t *hd_data, char *edid, int port) { int i; int ax, bx, cx; /* Setup registers for the interrupt call */ ax = 0x4f15; bx = 1; cx = port; /* Get EDID block */ i = CallInt10(&ax, &bx, &cx, edid, 256, hd_data->flags.cpuemu) & 0xffff; if (i != 0x4f) { log_err("EDID: Error (0x4f15): 0x%04x\n", i); return 0; } return 1; } #ifdef LRMI #include static int vbe_check_vbe_info__old(void) { int i; unsigned char *mem; unsigned char v[0x200]; struct LRMI_regs regs; /* initialize LRMI */ if (LRMI_init() == 0) { log_err("VBE: could not initialize LRMI\n"); return 0; } /* allocate a chunk of memory */ mem = LRMI_alloc_real(sizeof(v)); if (mem == NULL) { log_err("VBE: could allocate real memory\n"); return 0; } memset(mem, 0, sizeof(v)); /* set up registers for the interrupt call. */ memset(®s, 0, sizeof(regs)); regs.eax = 0x4f00; regs.es = ((u_int32_t)mem) >> 4; regs.edi = ((u_int32_t)mem) & 0x0f; memcpy(mem, "VBE2", 4); /* do it. */ iopl(3); ioperm(0, 0x400, 1); if (LRMI_int(0x10, ®s) == 0) { LRMI_free_real(mem); return 0; } /* check for successful return code */ i = regs.eax & 0xffff; if (i != 0x4f) { LRMI_free_real(mem); log_err("VBE: Error (0x4f00): 0x%04x\n", i); return 0; } /* get memory to return the information */ memcpy(v, mem, sizeof(v)); LRMI_free_real(mem); parse_vbe_info(v); return 1; } static int vbe_get_edid_info__old(char *edid, int port) { int i; unsigned char *mem; const int EDID_BLOCK_SIZE = 256; struct LRMI_regs regs; /* initialize LRMI */ if (LRMI_init() == 0) { log_err("EDID: could not initialize LRMI\n"); return 0; } /* allocate a chunk of memory */ mem = LRMI_alloc_real(EDID_BLOCK_SIZE); if (mem == NULL) { log_err("EDID: could allocate real memory\n"); return 0; } memset(mem, 0, EDID_BLOCK_SIZE); /* set up registers for the interrupt call. */ memset(®s, 0, sizeof(regs)); regs.eax = 0x4f15; regs.ebx = 0x0001; regs.ecx = port; regs.es = ((u_int32_t)mem) >> 4; regs.edi = ((u_int32_t)mem) & 0x0f; /* do it. */ iopl(3); ioperm(0, 0x400, 1); if (LRMI_int(0x10, ®s) == 0) { LRMI_free_real(mem); return 0; } /* check for successful return code */ i = regs.eax & 0xffff; if (i != 0x4f) { LRMI_free_real(mem); log_err("EDID: Error (0x4f15): 0x%04x\n", i); return 0; } /* get memory to return the information */ memcpy(edid, mem, EDID_BLOCK_SIZE); LRMI_free_real(mem); return 1; } int get_edid__old(char *edid, int port) { int ok; /* try with older lrmi interface, we can assume a failure for one is also bogus for the other */ log_err("Retrying with old LRMI interface\n"); ok = (box_is_xbox() || vbe_check_vbe_info__old()) && vbe_get_edid_info__old(edid, port); return ok; } #endif int get_edid(hd_data_t *hd_data, char *edid, int port) { int ok = 0; if (getuid() != 0) { fprintf(stderr, "you must be root to run this program\n"); return 0; } if (!box_is_xbox()) { if (InitInt10(hd_data) == 0) { ok = vbe_check_vbe_info(hd_data) && vbe_get_edid_info(hd_data, edid, port); FreeInt10(); } } #ifdef LRMI if (!ok) ok = get_edid__old(edid, port); #endif return ok ? 128 : 0; } int box_is_xbox() { int is_xbox = 0; int result = -1; int fd; size_t rd; char *xbox_id = "0000\t10de02a5"; char id[13]; if (!(fd = open("/proc/bus/pci/devices", O_RDONLY))) { printf("Unable to open /proc/bus/pci/devices\n"); } if (!(rd = read(fd, id, sizeof(id)))) { printf("Unable to read /proc/bus/pci/devices\n"); } if (fd > 0) close(fd); result = strncmp(id, xbox_id, 13); if (result == 0) is_xbox = 1; return is_xbox; }