1 /*
2 * Copyright (c) 2016 Oracle and/or its affiliates. All rights reserved.
3 */
4
5 #include <xen/init.h>
6 #include <xen/kernel.h>
7 #include <xen/rcupdate.h>
8 #include <xen/spinlock.h>
9 #include <xen/virtual_region.h>
10
11 static struct virtual_region core = {
12 .list = LIST_HEAD_INIT(core.list),
13 .start = _stext,
14 .end = _etext,
15 };
16
17 /* Becomes irrelevant when __init sections are cleared. */
18 static struct virtual_region core_init __initdata = {
19 .list = LIST_HEAD_INIT(core_init.list),
20 .start = _sinittext,
21 .end = _einittext,
22 };
23
24 /*
25 * RCU locking. Additions are done either at startup (when there is only
26 * one CPU) or when all CPUs are running without IRQs.
27 *
28 * Deletions are bit tricky. We do it when Live Patch (all CPUs running
29 * without IRQs) or during bootup (when clearing the init).
30 *
31 * Hence we use list_del_rcu (which sports an memory fence) and a spinlock
32 * on deletion.
33 *
34 * All readers of virtual_region_list MUST use list_for_each_entry_rcu.
35 */
36 static LIST_HEAD(virtual_region_list);
37 static DEFINE_SPINLOCK(virtual_region_lock);
38 static DEFINE_RCU_READ_LOCK(rcu_virtual_region_lock);
39
find_text_region(unsigned long addr)40 const struct virtual_region *find_text_region(unsigned long addr)
41 {
42 const struct virtual_region *region;
43
44 rcu_read_lock(&rcu_virtual_region_lock);
45 list_for_each_entry_rcu( region, &virtual_region_list, list )
46 {
47 if ( (void *)addr >= region->start && (void *)addr < region->end )
48 {
49 rcu_read_unlock(&rcu_virtual_region_lock);
50 return region;
51 }
52 }
53 rcu_read_unlock(&rcu_virtual_region_lock);
54
55 return NULL;
56 }
57
register_virtual_region(struct virtual_region * r)58 void register_virtual_region(struct virtual_region *r)
59 {
60 ASSERT(!local_irq_is_enabled());
61
62 list_add_tail_rcu(&r->list, &virtual_region_list);
63 }
64
remove_virtual_region(struct virtual_region * r)65 static void remove_virtual_region(struct virtual_region *r)
66 {
67 unsigned long flags;
68
69 spin_lock_irqsave(&virtual_region_lock, flags);
70 list_del_rcu(&r->list);
71 spin_unlock_irqrestore(&virtual_region_lock, flags);
72 /*
73 * We do not need to invoke call_rcu.
74 *
75 * This is due to the fact that on the deletion we have made sure
76 * to use spinlocks (to guard against somebody else calling
77 * unregister_virtual_region) and list_deletion spiced with
78 * memory barrier.
79 *
80 * That protects us from corrupting the list as the readers all
81 * use list_for_each_entry_rcu which is safe against concurrent
82 * deletions.
83 */
84 }
85
unregister_virtual_region(struct virtual_region * r)86 void unregister_virtual_region(struct virtual_region *r)
87 {
88 /* Expected to be called from Live Patch - which has IRQs disabled. */
89 ASSERT(!local_irq_is_enabled());
90
91 remove_virtual_region(r);
92 }
93
unregister_init_virtual_region(void)94 void __init unregister_init_virtual_region(void)
95 {
96 BUG_ON(system_state != SYS_STATE_active);
97
98 remove_virtual_region(&core_init);
99 }
100
setup_virtual_regions(const struct exception_table_entry * start,const struct exception_table_entry * end)101 void __init setup_virtual_regions(const struct exception_table_entry *start,
102 const struct exception_table_entry *end)
103 {
104 size_t sz;
105 unsigned int i;
106 static const struct bug_frame *const __initconstrel bug_frames[] = {
107 __start_bug_frames,
108 __stop_bug_frames_0,
109 __stop_bug_frames_1,
110 __stop_bug_frames_2,
111 #ifdef CONFIG_X86
112 __stop_bug_frames_3,
113 #endif
114 NULL
115 };
116
117 for ( i = 1; bug_frames[i]; i++ )
118 {
119 const struct bug_frame *s;
120
121 s = bug_frames[i - 1];
122 sz = bug_frames[i] - s;
123
124 core.frame[i - 1].n_bugs = sz;
125 core.frame[i - 1].bugs = s;
126
127 core_init.frame[i - 1].n_bugs = sz;
128 core_init.frame[i - 1].bugs = s;
129 }
130
131 core_init.ex = core.ex = start;
132 core_init.ex_end = core.ex_end = end;
133
134 register_virtual_region(&core_init);
135 register_virtual_region(&core);
136 }
137
138 /*
139 * Local variables:
140 * mode: C
141 * c-file-style: "BSD"
142 * c-basic-offset: 4
143 * tab-width: 4
144 * indent-tabs-mode: nil
145 * End:
146 */
147