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