forked from xemu-project/xemu
-
Notifications
You must be signed in to change notification settings - Fork 2
/
xemu-xbe.c
136 lines (113 loc) · 3.9 KB
/
xemu-xbe.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#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;
}