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
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
|
/* SPDX-License-Identifier: GPL-2.0 */
#include <xen/init.h>
#include <xen/mm.h>
#include <asm/setup.h>
/* Override macros from asm/page.h to make them work with mfn_t */
#undef virt_to_mfn
#define virt_to_mfn(va) _mfn(__virt_to_mfn(va))
static DEFINE_PAGE_TABLE(xen_first_id);
static DEFINE_PAGE_TABLE(xen_second_id);
static DEFINE_PAGE_TABLE(xen_third_id);
/*
* The identity mapping may start at physical address 0. So we don't want
* to keep it mapped longer than necessary.
*
* When this is called, we are still using the boot_pgtable.
*
* We need to prepare the identity mapping for both the boot page tables
* and runtime page tables.
*
* The logic to create the entry is slightly different because Xen may
* be running at a different location at runtime.
*/
static void __init prepare_boot_identity_mapping(void)
{
paddr_t id_addr = virt_to_maddr(_start);
lpae_t pte;
DECLARE_OFFSETS(id_offsets, id_addr);
/*
* We will be re-using the boot ID tables. They may not have been
* zeroed but they should be unlinked. So it is fine to use
* clear_page().
*/
clear_page(boot_first_id);
clear_page(boot_second_id);
clear_page(boot_third_id);
if ( id_offsets[0] >= IDENTITY_MAPPING_AREA_NR_L0 )
panic("Cannot handle ID mapping above 2TB\n");
/* Link first ID table */
pte = mfn_to_xen_entry(virt_to_mfn(boot_first_id), MT_NORMAL);
pte.pt.table = 1;
pte.pt.xn = 0;
write_pte(&boot_pgtable[id_offsets[0]], pte);
/* Link second ID table */
pte = mfn_to_xen_entry(virt_to_mfn(boot_second_id), MT_NORMAL);
pte.pt.table = 1;
pte.pt.xn = 0;
write_pte(&boot_first_id[id_offsets[1]], pte);
/* Link third ID table */
pte = mfn_to_xen_entry(virt_to_mfn(boot_third_id), MT_NORMAL);
pte.pt.table = 1;
pte.pt.xn = 0;
write_pte(&boot_second_id[id_offsets[2]], pte);
/* The mapping in the third table will be created at a later stage */
}
static void __init prepare_runtime_identity_mapping(void)
{
paddr_t id_addr = virt_to_maddr(_start);
lpae_t pte;
DECLARE_OFFSETS(id_offsets, id_addr);
if ( id_offsets[0] >= IDENTITY_MAPPING_AREA_NR_L0 )
panic("Cannot handle ID mapping above 2TB\n");
/* Link first ID table */
pte = pte_of_xenaddr((vaddr_t)xen_first_id);
pte.pt.table = 1;
pte.pt.xn = 0;
write_pte(&xen_pgtable[id_offsets[0]], pte);
/* Link second ID table */
pte = pte_of_xenaddr((vaddr_t)xen_second_id);
pte.pt.table = 1;
pte.pt.xn = 0;
write_pte(&xen_first_id[id_offsets[1]], pte);
/* Link third ID table */
pte = pte_of_xenaddr((vaddr_t)xen_third_id);
pte.pt.table = 1;
pte.pt.xn = 0;
write_pte(&xen_second_id[id_offsets[2]], pte);
/* The mapping in the third table will be created at a later stage */
}
void __init arch_setup_page_tables(void)
{
prepare_boot_identity_mapping();
prepare_runtime_identity_mapping();
}
void update_identity_mapping(bool enable)
{
paddr_t id_addr = virt_to_maddr(_start);
int rc;
if ( enable )
rc = map_pages_to_xen(id_addr, maddr_to_mfn(id_addr), 1,
PAGE_HYPERVISOR_RX);
else
rc = destroy_xen_mappings(id_addr, id_addr + PAGE_SIZE);
BUG_ON(rc);
}
extern void switch_ttbr_id(uint64_t ttbr);
typedef void (switch_ttbr_fn)(uint64_t ttbr);
void __init switch_ttbr(uint64_t ttbr)
{
vaddr_t id_addr = virt_to_maddr(switch_ttbr_id);
switch_ttbr_fn *fn = (switch_ttbr_fn *)id_addr;
lpae_t pte;
/* Enable the identity mapping in the boot page tables */
update_identity_mapping(true);
/* Enable the identity mapping in the runtime page tables */
pte = pte_of_xenaddr((vaddr_t)switch_ttbr_id);
pte.pt.table = 1;
pte.pt.xn = 0;
pte.pt.ro = 1;
write_pte(&xen_third_id[third_table_offset(id_addr)], pte);
/* Switch TTBR */
fn(ttbr);
/*
* Disable the identity mapping in the runtime page tables.
* Note it is not necessary to disable it in the boot page tables
* because they are not going to be used by this CPU anymore.
*/
update_identity_mapping(false);
}
/*
* Local variables:
* mode: C
* c-file-style: "BSD"
* c-basic-offset: 4
* indent-tabs-mode: nil
* End:
*/
|