]>
Commit | Line | Data |
---|---|---|
3fa2d384 VP |
1 | /* |
2 | * Copyright (c) 2018 Virtuozzo International GmbH | |
3 | * | |
4 | * This work is licensed under the terms of the GNU GPL, version 2 or later. | |
5 | * | |
6 | */ | |
7 | ||
8 | #include "qemu/osdep.h" | |
9 | #include "err.h" | |
10 | #include "qemu_elf.h" | |
11 | ||
12 | #define QEMU_NOTE_NAME "QEMU" | |
13 | ||
14 | #ifndef ROUND_UP | |
15 | #define ROUND_UP(n, d) (((n) + (d) - 1) & -(0 ? (n) : (d))) | |
16 | #endif | |
17 | ||
18 | #ifndef DIV_ROUND_UP | |
19 | #define DIV_ROUND_UP(n, d) (((n) + (d) - 1) / (d)) | |
20 | #endif | |
21 | ||
22 | #define ELF_NOTE_SIZE(hdr_size, name_size, desc_size) \ | |
23 | ((DIV_ROUND_UP((hdr_size), 4) + \ | |
24 | DIV_ROUND_UP((name_size), 4) + \ | |
25 | DIV_ROUND_UP((desc_size), 4)) * 4) | |
26 | ||
27 | int is_system(QEMUCPUState *s) | |
28 | { | |
29 | return s->gs.base >> 63; | |
30 | } | |
31 | ||
32 | static char *nhdr_get_name(Elf64_Nhdr *nhdr) | |
33 | { | |
34 | return (char *)nhdr + ROUND_UP(sizeof(*nhdr), 4); | |
35 | } | |
36 | ||
37 | static void *nhdr_get_desc(Elf64_Nhdr *nhdr) | |
38 | { | |
39 | return nhdr_get_name(nhdr) + ROUND_UP(nhdr->n_namesz, 4); | |
40 | } | |
41 | ||
42 | static Elf64_Nhdr *nhdr_get_next(Elf64_Nhdr *nhdr) | |
43 | { | |
44 | return (void *)((uint8_t *)nhdr + ELF_NOTE_SIZE(sizeof(*nhdr), | |
45 | nhdr->n_namesz, nhdr->n_descsz)); | |
46 | } | |
47 | ||
48 | Elf64_Phdr *elf64_getphdr(void *map) | |
49 | { | |
50 | Elf64_Ehdr *ehdr = map; | |
51 | Elf64_Phdr *phdr = (void *)((uint8_t *)map + ehdr->e_phoff); | |
52 | ||
53 | return phdr; | |
54 | } | |
55 | ||
56 | Elf64_Half elf_getphdrnum(void *map) | |
57 | { | |
58 | Elf64_Ehdr *ehdr = map; | |
59 | ||
60 | return ehdr->e_phnum; | |
61 | } | |
62 | ||
63 | static int init_states(QEMU_Elf *qe) | |
64 | { | |
65 | Elf64_Phdr *phdr = elf64_getphdr(qe->map); | |
66 | Elf64_Nhdr *start = (void *)((uint8_t *)qe->map + phdr[0].p_offset); | |
67 | Elf64_Nhdr *end = (void *)((uint8_t *)start + phdr[0].p_memsz); | |
68 | Elf64_Nhdr *nhdr; | |
69 | size_t cpu_nr = 0; | |
70 | ||
71 | if (phdr[0].p_type != PT_NOTE) { | |
72 | eprintf("Failed to find PT_NOTE\n"); | |
73 | return 1; | |
74 | } | |
75 | ||
76 | qe->has_kernel_gs_base = 1; | |
77 | ||
78 | for (nhdr = start; nhdr < end; nhdr = nhdr_get_next(nhdr)) { | |
79 | if (!strcmp(nhdr_get_name(nhdr), QEMU_NOTE_NAME)) { | |
80 | QEMUCPUState *state = nhdr_get_desc(nhdr); | |
81 | ||
82 | if (state->size < sizeof(*state)) { | |
83 | eprintf("CPU #%zu: QEMU CPU state size %u doesn't match\n", | |
84 | cpu_nr, state->size); | |
85 | /* | |
86 | * We assume either every QEMU CPU state has KERNEL_GS_BASE or | |
87 | * no one has. | |
88 | */ | |
89 | qe->has_kernel_gs_base = 0; | |
90 | } | |
91 | cpu_nr++; | |
92 | } | |
93 | } | |
94 | ||
95 | printf("%zu CPU states has been found\n", cpu_nr); | |
96 | ||
97 | qe->state = malloc(sizeof(*qe->state) * cpu_nr); | |
98 | if (!qe->state) { | |
99 | return 1; | |
100 | } | |
101 | ||
102 | cpu_nr = 0; | |
103 | ||
104 | for (nhdr = start; nhdr < end; nhdr = nhdr_get_next(nhdr)) { | |
105 | if (!strcmp(nhdr_get_name(nhdr), QEMU_NOTE_NAME)) { | |
106 | qe->state[cpu_nr] = nhdr_get_desc(nhdr); | |
107 | cpu_nr++; | |
108 | } | |
109 | } | |
110 | ||
111 | qe->state_nr = cpu_nr; | |
112 | ||
113 | return 0; | |
114 | } | |
115 | ||
116 | static void exit_states(QEMU_Elf *qe) | |
117 | { | |
118 | free(qe->state); | |
119 | } | |
120 | ||
121 | int QEMU_Elf_init(QEMU_Elf *qe, const char *filename) | |
122 | { | |
bd4d0da7 | 123 | GError *gerr = NULL; |
3fa2d384 | 124 | int err = 0; |
3fa2d384 | 125 | |
bd4d0da7 VP |
126 | qe->gmf = g_mapped_file_new(filename, TRUE, &gerr); |
127 | if (gerr) { | |
128 | eprintf("Failed to map ELF dump file \'%s\'\n", filename); | |
3fa2d384 VP |
129 | return 1; |
130 | } | |
131 | ||
bd4d0da7 VP |
132 | qe->map = g_mapped_file_get_contents(qe->gmf); |
133 | qe->size = g_mapped_file_get_length(qe->gmf); | |
3fa2d384 VP |
134 | |
135 | if (init_states(qe)) { | |
136 | eprintf("Failed to extract QEMU CPU states\n"); | |
137 | err = 1; | |
138 | goto out_unmap; | |
139 | } | |
140 | ||
141 | return 0; | |
142 | ||
143 | out_unmap: | |
bd4d0da7 | 144 | g_mapped_file_unref(qe->gmf); |
3fa2d384 VP |
145 | |
146 | return err; | |
147 | } | |
148 | ||
149 | void QEMU_Elf_exit(QEMU_Elf *qe) | |
150 | { | |
151 | exit_states(qe); | |
bd4d0da7 | 152 | g_mapped_file_unref(qe->gmf); |
3fa2d384 | 153 | } |