static int regions_are_sane(struct regions *regions) { struct region *cur = regions->head; if (!cur) return 1; if (cur->prev) { mmt_error("%s", "start->prev != NULL\n"); return 0; } while (cur) { if (cur->start >= cur->end) { mmt_error("cur->start >= cur->end 0x%x 0x%x\n", cur->start, cur->end); return 0; } if (cur->next) { if (cur->end >= cur->next->start) { mmt_error("cur->end >= cur->next->start 0x%x 0x%x\n", cur->end, cur->next->start); return 0; } } cur = cur->next; } return 1; }
static void handle_nvrm_ioctl_vspace_unmap(uint32_t fd, struct nvrm_ioctl_vspace_unmap *s) { if (s->status != NVRM_STATUS_SUCCESS) return; check_cid(s->cid); struct gpu_object *obj = gpu_object_find(s->cid, s->handle); if (!obj) { mmt_error("nvrm_ioctl_vspace_unmap: cannot find object 0x%08x 0x%08x\n", s->cid, s->handle); return; } struct gpu_mapping *gpu_mapping; for (gpu_mapping = obj->gpu_mappings; gpu_mapping != NULL; gpu_mapping = gpu_mapping->next) if (gpu_mapping->address == s->addr) { gpu_mapping->address = 0; gpu_mapping_destroy(gpu_mapping); return; } mmt_error("can't find matching gpu_mappings%s\n", ""); demmt_abort(); }
static void handle_nvrm_ioctl_host_unmap(uint32_t fd, struct nvrm_ioctl_host_unmap *s) { if (s->status != NVRM_STATUS_SUCCESS) return; check_cid(s->cid); struct gpu_object *obj = gpu_object_find(s->cid, s->handle); if (!obj) { mmt_error("nvrm_ioctl_host_unmap: cannot find object 0x%08x 0x%08x\n", s->cid, s->handle); return; } struct cpu_mapping *cpu_mapping; for (cpu_mapping = obj->cpu_mappings; cpu_mapping != NULL; cpu_mapping = cpu_mapping->next) if (cpu_mapping->mmap_offset == (s->foffset & ~0xfffUL)) { cpu_mapping->mmap_offset = 0; disconnect_cpu_mapping_from_gpu_object(cpu_mapping); return; } // weird, host_unmap accepts cpu addresses in foffset field for (cpu_mapping = obj->cpu_mappings; cpu_mapping != NULL; cpu_mapping = cpu_mapping->next) if (cpu_mapping->cpu_addr == (s->foffset & ~0xfffUL)) { //mmt_error("host_unmap with cpu address as offset, wtf?%s\n", ""); cpu_mapping->mmap_offset = 0; disconnect_cpu_mapping_from_gpu_object(cpu_mapping); return; } mmt_error("can't find matching mapping%s\n", ""); demmt_abort(); }
static void nvrm_add_object(uint32_t cid, uint32_t parent, uint32_t handle, uint32_t class_) { int bucket = (handle * (handle + 3)) % NVRM_OBJECT_CACHE_SIZE; struct nvrm_object *entry = nvrm_object_cache[bucket]; while (entry && (entry->cid != cid || entry->handle != handle)) entry = entry->next; if (entry) { mmt_error("object 0x%08x / 0x%08x already exists!\n", cid, handle); if (entry->class_ != class_ || entry->parent != parent) { mmt_error("... and its class or parent differ! 0x%04x != 0x%04x || 0x%08x != 0x%08x\n", entry->class_, class_, entry->parent, parent); fflush(stdout); abort(); } } else { if (0 && nvrm_object_cache[bucket]) mmt_error("collision%s\n", ""); entry = malloc(sizeof(struct nvrm_object)); entry->cid = cid; entry->parent = parent; entry->handle = handle; entry->class_ = class_; entry->next = nvrm_object_cache[bucket]; nvrm_object_cache[bucket] = entry; } }
static void handle_nvrm_ioctl_host_map(uint32_t fd, struct nvrm_ioctl_host_map *s) { if (s->status != NVRM_STATUS_SUCCESS) return; check_cid(s->cid); struct gpu_object *obj = gpu_object_find(s->cid, s->handle); if (!obj) { mmt_error("nvrm_ioctl_host_map: cannot find object 0x%08x 0x%08x\n", s->cid, s->handle); return; } // NOTE: s->subdev may not be object's parent and may not even be an ancestor of its parent /* if (obj->parent != s->subdev) { struct gpu_object *subdev = find_object(s->cid, s->subdev); while (subdev && subdev->handle != obj->parent) subdev = subdev->parent_object; if (!subdev) { mmt_error("nvrm_ioctl_host_map: subdev 0x%08x is not an ancestor of object's parent (0x%08x)\n", s->subdev, obj->parent); return; } }*/ host_map(obj, fd, s->foffset, s->base, s->limit + 1, s->subdev, -1); }
static void __demmt_ioctl_post(uint32_t fd, uint32_t id, struct mmt_buf *data, uint64_t ret, uint64_t err, void *state, struct mmt_memory_dump *args, int argc) { uint8_t dir, type, nr; uint16_t size; decode_ioctl_id(id, &dir, &type, &nr, &size); int print_raw = 0; enum mmt_fd_type fdtype = demmt_get_fdtype(fd); if (fdtype == FDDRM) print_raw = demmt_drm_ioctl_post(fd, id, dir, nr, size, data, ret, err, state, args, argc); else if (fdtype == FDNVIDIA) print_raw = nvrm_ioctl_post(fd, id, dir, nr, size, data, ret, err, state, args, argc); else if (fdtype == FDFGLRX) print_raw = fglrx_ioctl_post(fd, id, dir, nr, size, data, ret, err, state, args, argc); else mmt_error("ioctl 0x%x called for unknown type of file [%d, %d]\n", id, fd, fdtype); print_raw = print_raw || dump_raw_ioctl_data; if (print_raw) { mmt_log("ioctl post 0x%02x (0x%08x), fd: %d, dir: %2s, size: %4d", nr, id, fd, dir_desc[dir], size); if (ret) mmt_log_cont(", ret: 0x%" PRIx64 "", ret); if (err) mmt_log_cont(", err: 0x%" PRIx64 "", err); if (size != data->len) mmt_log_cont(", data.len: %d", data->len); ioctl_data_print(data); mmt_log_cont_nl(); } }
void nvrm_mmap(uint32_t id, uint32_t fd, uint64_t mmap_addr, uint64_t len, uint64_t mmap_offset) { struct gpu_object *obj; struct cpu_mapping *cpu_mapping; for (obj = gpu_objects; obj != NULL; obj = obj->next) for (cpu_mapping = obj->cpu_mappings; cpu_mapping != NULL; cpu_mapping = cpu_mapping->next) //can't validate fd if (cpu_mapping->mmap_offset == mmap_offset) { cpu_mapping->cpu_addr = mmap_addr; cpu_mapping->id = id; set_cpu_mapping(id, cpu_mapping); if (dump_sys_mmap) { if (cpu_mapping->fdtype == FDNVIDIA) { mmt_log_cont(", cid: 0x%08x, handle: 0x%08x", obj->cid, obj->handle); describe_nvrm_object(obj->cid, obj->handle, ""); } mmt_log_cont_nl(); } return; } if (dump_sys_mmap) mmt_log_cont_nl(); if (demmt_get_fdtype(fd) == FDNVIDIA) mmt_error("nvrm_mmap: couldn't find object/space offset: 0x%016" PRIx64 "\n", mmap_offset); buffer_mmap(id, fd, mmap_addr, len, mmap_offset); }
static void handle_nvrm_ioctl_vspace_map(uint32_t fd, struct nvrm_ioctl_vspace_map *s) { if (s->status != NVRM_STATUS_SUCCESS) return; check_cid(s->cid); struct gpu_object *obj = gpu_object_find(s->cid, s->handle); if (!obj) { mmt_error("nvrm_ioctl_vspace_map: cannot find object 0x%08x 0x%08x\n", s->cid, s->handle); return; } struct gpu_mapping *mapping = calloc(sizeof(struct gpu_mapping), 1); mapping->fd = fd; mapping->dev = s->dev; mapping->vspace = s->vspace; mapping->address = s->addr; mapping->object_offset = s->base; mapping->length = s->size; if (s->size > obj->length) { obj->data = realloc(obj->data, s->size); memset(obj->data + obj->length, 0, s->size - obj->length); obj->length = s->size; } mapping->object = obj; mapping->next = obj->gpu_mappings; obj->gpu_mappings = mapping; }
static void nvrm_del_object(uint32_t cid, uint32_t parent, uint32_t handle) { int bucket = (handle * (handle + 3)) % NVRM_OBJECT_CACHE_SIZE; struct nvrm_object *entry = nvrm_object_cache[bucket]; struct nvrm_object *preventry = NULL; while (entry && (entry->cid != cid || entry->handle != handle)) { preventry = entry; entry = entry->next; } if (!entry) { mmt_error("trying to delete object 0x%08x / 0x%08x which does not exist!\n", cid, handle); return; } if (preventry) preventry->next = entry->next; else nvrm_object_cache[bucket] = entry->next; free(entry); }
static void handle_nvrm_ioctl_host_unmap(uint32_t fd, struct nvrm_ioctl_host_unmap *s) { if (s->status != NVRM_STATUS_SUCCESS) return; check_cid(s->cid); struct gpu_object *obj = gpu_object_find(s->cid, s->handle); if (!obj) { mmt_error("nvrm_ioctl_host_unmap: cannot find object 0x%08x 0x%08x\n", s->cid, s->handle); return; } struct cpu_mapping *cpu_mapping; /* it seems, depending on blob version, foffset have different meaning :/ */ for (cpu_mapping = obj->cpu_mappings; cpu_mapping != NULL; cpu_mapping = cpu_mapping->next) if (cpu_mapping->map_id == s->foffset) { cpu_mapping->mmap_offset = -1; cpu_mapping->map_id = -1; disconnect_cpu_mapping_from_gpu_object(cpu_mapping); return; } for (cpu_mapping = obj->cpu_mappings; cpu_mapping != NULL; cpu_mapping = cpu_mapping->next) if (cpu_mapping->mmap_offset == (s->foffset & ~0xfffUL)) { cpu_mapping->mmap_offset = -1; cpu_mapping->map_id = -1; disconnect_cpu_mapping_from_gpu_object(cpu_mapping); return; } for (cpu_mapping = obj->cpu_mappings; cpu_mapping != NULL; cpu_mapping = cpu_mapping->next) if (cpu_mapping->cpu_addr == (s->foffset & ~0xfffUL)) { //mmt_error("host_unmap with cpu address as offset, wtf?%s\n", ""); cpu_mapping->mmap_offset = -1; cpu_mapping->map_id = -1; disconnect_cpu_mapping_from_gpu_object(cpu_mapping); return; } mmt_error("can't find matching mapping%s\n", ""); demmt_abort(); }
void nvrm_mmap(uint32_t id, uint32_t fd, uint64_t mmap_addr, uint64_t len, uint64_t mmap_offset) { struct gpu_object *obj; struct cpu_mapping *cpu_mapping; for (obj = gpu_objects; obj != NULL; obj = obj->next) for (cpu_mapping = obj->cpu_mappings; cpu_mapping != NULL; cpu_mapping = cpu_mapping->next) //can't validate fd if (cpu_mapping->mmap_offset == mmap_offset) { cpu_mapping->cpu_addr = mmap_addr; uint32_t old_id = cpu_mapping->id; cpu_mapping->id = id; set_cpu_mapping(id, cpu_mapping); if (dump_sys_mmap) { if (cpu_mapping->fdtype == FDNVIDIA) { mmt_log_cont(", cid: 0x%08x, handle: 0x%08x", obj->cid, obj->handle); describe_nvrm_object(obj->cid, obj->handle, ""); } mmt_log_cont_nl(); } if (old_id) mmt_error("%d -> %d, mapping reuse, expect crash soon\n", old_id, id); /* * On newer blob, where mmap_offset is 0 for * all mappings (WTF?), clobber the value to * prevent the next nvrm_mmap from finding this * mapping. */ if (cpu_mapping->mmap_offset == 0) cpu_mapping->mmap_offset = -1; return; } if (dump_sys_mmap) mmt_log_cont_nl(); if (demmt_get_fdtype(fd) == FDNVIDIA) mmt_error("nvrm_mmap: couldn't find object/space offset: 0x%016" PRIx64 "\n", mmap_offset); buffer_mmap(id, fd, mmap_addr, len, mmap_offset); }
struct gpu_object *nvrm_get_fifo(struct gpu_object *obj, uint64_t gpu_addr, int strict) { struct gpu_object *last = NULL; while (obj) { if (is_fifo(obj, 0)) last = obj; if (is_fifo_and_addr_belongs(obj, gpu_addr)) return obj; if (obj->class_ == NVRM_DEVICE_0) { struct gpu_object *fifo = nvrm_find_object_by_func(obj, is_fifo_and_addr_belongs, gpu_addr); if (!fifo && !strict) // fallback, for traces without ioctl_create args { fifo = nvrm_find_object_by_func(obj, is_fifo, 0); struct gpu_object *dev = nvrm_get_device(obj); int fifos = 0; if (dev && dev->class_data) fifos = nvrm_dev(dev)->fifos; static int warned = 0; if (fifos > 1 && !warned) { int chipset = nvrm_get_chipset(dev); if (chipset > 0x80 || chipset == 0x50) mmt_error("This trace may not be decoded accurately because there are multiple fifo objects " "and ioctl_creates for some of them were not captured with argument data%s\n", ""); else mmt_error("This trace may not be decoded accurately because there are multiple fifo objects " "and USER buffer detection is not implemented yet%s\n", ""); warned = 1; } } return fifo; } obj = obj->parent_object; } return last; }
int nvrm_get_chipset(struct gpu_object *obj) { struct gpu_object *dev = nvrm_get_device(obj); if (dev && dev->class_data) return nvrm_dev(dev)->chipset; if (chipset) return chipset; mmt_error("Can't detect chipset, you need to use -m option or regenerate trace with newer mmt (> Sep 7 2014)%s\n", ""); demmt_abort(); }
static void demmt_memread(struct mmt_read *w, void *state) { char comment[50]; struct cpu_mapping *mapping = get_cpu_mapping(w->id); if (mapping == NULL) { mmt_error("invalid buffer id: %d\n", w->id); demmt_abort(); } uint64_t gpu_addr = cpu_mapping_to_gpu_addr(mapping, w->offset); if (print_gpu_addresses && gpu_addr) sprintf(comment, " (gpu=0x%08" PRIx64 ")", gpu_addr); else comment[0] = 0; if (dump_memory_reads) { unsigned char *data = &w->data[0]; if (w->len == 1) mmt_printf("r %d:0x%04x%s, 0x%02x\n", w->id, w->offset, comment, data[0]); else if (w->len == 2) mmt_printf("r %d:0x%04x%s, 0x%04x\n", w->id, w->offset, comment, *(uint16_t *)&data[0]); else if (w->len == 4 || w->len == 8 || w->len == 16 || w->len == 32) { mmt_printf("r %d:0x%04x%s, ", w->id, w->offset, comment); int i; for (i = 0; i < w->len; i += 4) mmt_printf("0x%08x ", *(uint32_t *)&data[i]); mmt_printf("%s\n", ""); } else { mmt_error("unhandled size: %d\n", w->len); demmt_abort(); } } }
static void demmt_munmap(struct mmt_unmap *mm, void *state) { if (get_cpu_mapping(mm->id) == NULL) { mmt_error("invalid buffer id: %d\n", mm->id); demmt_abort(); } if (dump_sys_munmap) mmt_log("munmap: address: 0x%" PRIx64 ", length: 0x%08" PRIx64 ", id: %d, offset: 0x%08" PRIx64 "", mm->start, mm->len, mm->id, mm->offset); nvrm_munmap(mm->id, mm->start, mm->len, mm->offset); }
static void demmt_mremap(struct mmt_mremap *mm, void *state) { if (get_cpu_mapping(mm->id) == NULL) { mmt_error("invalid buffer id: %d\n", mm->id); demmt_abort(); } if (dump_sys_mremap) mmt_log("mremap: old_address: 0x%" PRIx64 ", new_address: 0x%" PRIx64 ", old_length: 0x%08" PRIx64 ", new_length: 0x%08" PRIx64 ", id: %d, offset: 0x%08" PRIx64 "\n", mm->old_start, mm->start, mm->old_len, mm->len, mm->id, mm->offset); buffer_mremap(mm); }
static void handle_nvrm_ioctl_create(uint32_t fd, struct nvrm_ioctl_create *s, struct mmt_memory_dump *args, int argc) { if (s->status != NVRM_STATUS_SUCCESS) return; uint32_t cid = s->cid; uint32_t parent = s->parent; uint32_t handle = s->handle; struct mmt_buf *data = NULL; if (s->ptr) data = find_ptr(s->ptr, args, argc); if (handle == 0) { if (!data || data->len < 4) { mmt_error("\"create cid\" without data - probably because this trace was obtained by old mmt version (before Sep 6 2014)%s\n", ""); cid_not_found++; return; } cid = parent = handle = ((uint32_t *)data->data)[0]; } check_cid(cid); struct gpu_object *obj = nvrm_add_object(fd, cid, parent, handle, s->cls); pushbuf_add_object(handle, s->cls, obj); if (is_fifo_ib_class(s->cls)) { if (data) { struct fifo_state *state = get_fifo_state(obj); struct nvrm_create_fifo_ib *create_data = (void *)data->data; state->ib.addr = create_data->ib_addr; state->ib.entries = create_data->ib_entries; } } else if (is_fifo_dma_class(s->cls)) { if (data) { struct fifo_state *state = get_fifo_state(obj); struct nvrm_create_fifo_dma *create_data = (void *)data->data; state->user.addr = create_data->user_addr; } } }
int regions_add_range(struct regions *regions, uint32_t start, uint32_t len) { __regions_add_range(regions, start, len); if (!regions_are_sane(regions)) return 0; if (!range_in_regions(regions, start, len)) { mmt_error("region <0x%08x, 0x%08x> was not added!\n", start, start + len); return 0; } return 1; }
static void handle_nvrm_ioctl_host_map56(uint32_t fd, struct nvrm_ioctl_host_map56 *s) { if (s->status != NVRM_STATUS_SUCCESS) return; check_cid(s->cid); struct gpu_object *obj = gpu_object_find(s->cid, s->handle); if (!obj) { mmt_error("nvrm_ioctl_host_map56: cannot find object 0x%08x 0x%08x\n", s->cid, s->handle); return; } host_map(obj, fd, s->foffset, 0, s->length, s->subdev, s->map_id); }
static void handle_nvrm_ioctl_create_vspace56(uint32_t fd, struct nvrm_ioctl_create_vspace56 *s) { if (s->status != NVRM_STATUS_SUCCESS) return; check_cid(s->cid); struct gpu_object *obj = nvrm_add_object(fd, s->cid, s->parent, s->handle, s->cls); if (!obj) { mmt_error("nvrm_ioctl_create_vspace56: cannot find object 0x%08x 0x%08x\n", s->cid, s->handle); return; } if (s->map_id) host_map(obj, fd, 0, 0, s->limit + 1, 0, s->map_id); }
void demmt_memwrite2(struct mmt_write2 *w2, void *state) { uint32_t i; struct cpu_mapping *m = NULL; for (i = 0; i < max_id + 1; ++i) { m = get_cpu_mapping(i); if (m && w2->addr >= m->cpu_addr && w2->addr < m->cpu_addr + m->length) break; } if (i != max_id + 1) { if (!mem2_buffer) mem2_buffer = malloc(4096); struct mmt_write *w1 = mem2_buffer; w1->msg_type = w2->msg_type; w1->id = i; w1->offset = w2->addr - m->cpu_addr; w1->len = w2->len; memcpy(w1->data, w2->data, w1->len); demmt_memwrite(w1, state); } if (dump_memory_writes) { unsigned char *data = &w2->data[0]; if (w2->len == 1) mmt_printf("@w 0x%" PRIx64 ", 0x%02x\n", w2->addr, data[0]); else if (w2->len == 2) mmt_printf("@w 0x%" PRIx64 ", 0x%04x\n", w2->addr, *(uint16_t *)&data[0]); else if (w2->len == 4 || w2->len == 8 || w2->len == 16 || w2->len == 32) { mmt_printf("@w 0x%" PRIx64 ", ", w2->addr); int i; for (i = 0; i < w2->len; i += 4) mmt_printf("0x%08x ", *(uint32_t *)&data[i]); mmt_printf("%s\n", ""); } else { mmt_error("unhandled size: %d\n", w2->len); demmt_abort(); } } }
static void nvrm_destroy_gpu_object(uint32_t fd, uint32_t cid, uint32_t parent, uint32_t handle) { struct gpu_object *obj = gpu_object_find(cid, handle); if (obj == NULL) { uint16_t cl = handle & 0xffff; // userspace deletes objects which it didn't create and kernel returns SUCCESS :/ // just ignore known offenders switch (cl) { case 0x0014: case 0x0202: case 0x0301: case 0x0308: case 0x0360: case 0x0371: case 0x1e00: case 0x1e01: case 0x1e10: case 0x1e20: break; default: mmt_error("trying to destroy object 0x%08x / 0x%08x which does not exist!\n", cid, handle); } return; } if (dump_object_tree_on_create_destroy) { mmt_log("Object tree before destroy: %s\n", ""); dump_object_trees(obj); } if (is_fifo_ib_class(obj->class_) || is_fifo_dma_class(obj->class_)) { struct gpu_object *dev = nvrm_get_device(obj); if (dev && dev->class_data) nvrm_dev(dev)->fifos--; } gpu_object_destroy(obj); }
static void handle_nvrm_ioctl_create(struct nvrm_ioctl_create *s, struct mmt_memory_dump *args, int argc) { if (s->status != NVRM_STATUS_SUCCESS) return; uint32_t cid = s->cid; uint32_t parent = s->parent; uint32_t handle = s->handle; if (handle == 0) { struct mmt_buf *data = find_ptr(s->ptr, args, argc); if (!data || data->len < 4) { mmt_error("\"create cid\" without data - probably because of old mmt (before Sep 6 2014) was used%s\n", ""); return; } cid = parent = handle = ((uint32_t *)data->data)[0]; } pushbuf_add_object(handle, s->cls); nvrm_add_object(cid, parent, handle, s->cls); }
static void __demmt_ioctl_pre(uint32_t fd, uint32_t id, struct mmt_buf *data, void *state, struct mmt_memory_dump *args, int argc) { uint8_t dir, type, nr; uint16_t size; decode_ioctl_id(id, &dir, &type, &nr, &size); int print_raw = 1; enum mmt_fd_type fdtype = demmt_get_fdtype(fd); if (fdtype == FDUNK) { if (type == 0x64) // DRM fdtype = undetected_fdtype = FDDRM; else if (type == 0x46) // nvidia fdtype = undetected_fdtype = FDNVIDIA; } if (fdtype == FDDRM) print_raw = demmt_drm_ioctl_pre(fd, id, dir, nr, size, data, state, args, argc); else if (fdtype == FDNVIDIA) print_raw = nvrm_ioctl_pre(fd, id, dir, nr, size, data, state, args, argc); else if (fdtype == FDFGLRX) print_raw = fglrx_ioctl_pre(fd, id, dir, nr, size, data, state, args, argc); else mmt_error("ioctl 0x%x called for unknown type of file [%d, %d]\n", id, fd, fdtype); print_raw = print_raw || dump_raw_ioctl_data; if (print_raw) { mmt_log("ioctl pre 0x%02x (0x%08x), fd: %d, dir: %2s, size: %4d", nr, id, fd, dir_desc[dir], size); if (size != data->len) mmt_log_cont(", data.len: %d", data->len); ioctl_data_print(data); mmt_log_cont_nl(); } }