/* * xemu XBE accessing * * Helper functions to get details about the currently running executable. * * Copyright (C) 2020-2021 Matt Borgerson * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "xemu-xbe.h" #include "qemu/osdep.h" #include "hw/hw.h" #include "hw/i386/pc.h" #include "hw/pci/pci.h" #include "sysemu/hw_accel.h" #include "cpu.h" static int virt_to_phys(vaddr vaddr, hwaddr *phys_addr) { MemTxAttrs attrs; CPUState *cs; hwaddr gpa; cs = qemu_get_cpu(0); if (!cs) { return 1; // No cpu } cpu_synchronize_state(cs); gpa = cpu_get_phys_page_attrs_debug(cs, vaddr & TARGET_PAGE_MASK, &attrs); if (gpa == -1) { return 1; // Unmapped } else { *phys_addr = gpa + (vaddr & ~TARGET_PAGE_MASK); } return 0; } static ssize_t virt_dma_memory_read(vaddr vaddr, void *buf, size_t len) { size_t num_bytes_read = 0; while (num_bytes_read < len) { // Get physical page for this offset hwaddr phys_addr = 0; if (virt_to_phys(vaddr + num_bytes_read, &phys_addr) != 0) { return -1; } // Read contents from the page size_t bytes_remaining_in_page = TARGET_PAGE_SIZE - (phys_addr & ~TARGET_PAGE_MASK); size_t num_bytes_to_read = MIN(len - num_bytes_read, bytes_remaining_in_page); // FIXME: Check return value dma_memory_read(&address_space_memory, phys_addr, buf + num_bytes_read, num_bytes_to_read, MEMTXATTRS_UNSPECIFIED); num_bytes_read += num_bytes_to_read; } return num_bytes_read; } struct xbe *xemu_get_xbe_info(void) { vaddr hdr_addr_virt = 0x10000; static struct xbe xbe = {0}; if (xbe.headers) { free(xbe.headers); xbe.headers = NULL; } // Get physical page of headers hwaddr hdr_addr_phys = 0; if (virt_to_phys(hdr_addr_virt, &hdr_addr_phys) != 0) { return NULL; } // Check `XBEH` signature uint32_t sig = ldl_le_phys(&address_space_memory, hdr_addr_phys); if (sig != 0x48454258) { return NULL; } // Determine full length of headers xbe.headers_len = ldl_le_phys(&address_space_memory, hdr_addr_phys + offsetof(struct xbe_header, m_sizeof_headers)); if (xbe.headers_len > 8*TARGET_PAGE_SIZE) { // Headers are unusually large return NULL; } xbe.headers = malloc(xbe.headers_len); assert(xbe.headers != NULL); // Read all XBE headers ssize_t bytes_read = virt_dma_memory_read(hdr_addr_virt, xbe.headers, xbe.headers_len); if (bytes_read != xbe.headers_len) { // Failed to read headers return NULL; } // Extract XBE header fields xbe.header = (struct xbe_header *)xbe.headers; // Get certificate vaddr cert_addr_virt = ldl_le_p(&xbe.header->m_certificate_addr); if ((cert_addr_virt == 0) || ((cert_addr_virt + sizeof(struct xbe_certificate)) > (hdr_addr_virt + xbe.headers_len))) { // Invalid certificate header (a valid certificate is expected for official titles) return NULL; } xbe.cert = (struct xbe_certificate *)(xbe.headers + cert_addr_virt - hdr_addr_virt); return &xbe; }