]> rtime.felk.cvut.cz Git - l4.git/blob - l4/pkg/plr/server/src/fault_handlers/pagefault.cc
update
[l4.git] / l4 / pkg / plr / server / src / fault_handlers / pagefault.cc
1 /*
2  * pagefault.cc --
3  *
4  *     Implementation of page fault handling.
5  *
6  * (c) 2011-2012 Björn Döbel <doebel@os.inf.tu-dresden.de>,
7  *     economic rights: Technische Universität Dresden (Germany)
8  * This file is part of TUD:OS and distributed under the terms of the
9  * GNU General Public License 2.
10  * Please see the COPYING-GPL-2 file for details.
11  */
12
13 #include "../constants.h"
14 #include "../log"
15 #include "../exceptions"
16 #include "../memory"
17 #include "../emulation"
18 #include "../app_loading"
19 #include "../locking.h"
20 #include "../configuration"
21 #include "syscalls_handler.h"
22
23 #include "observers.h"
24
25 #include <l4/sys/kdebug.h>
26 #include <l4/util/bitops.h>
27 #include <algorithm>
28
29 extern l4_addr_t __libc_l4_gettime;
30
31 static int pf_write  = 0; // statistical counter
32 static int pf_mapped = 0; // statistical counter
33 static int pf_kip    = 0; // statistical counter
34
35 #define MSG() DEBUGf(Romain::Log::Memory) << "[" << i->id() << "] "
36 #define MSGt(t) DEBUGf(Romain::Log::Faults) << "[" << t->vcpu() << "] "
37
38 /*****************************************************************
39  *                  Page Fault Handling                          *
40  *****************************************************************/
41 DEFINE_EMPTY_STARTUP(PageFaultObserver)
42
43 void Romain::PageFaultObserver::status() const
44 {
45         INFO() << "[ pf ] Page faults so far: mapped " << pf_mapped << " write emu "
46                << pf_write << " kip " << pf_kip;
47 }
48
49
50 /*
51  * Prepare for a mapping by finding the largest possible alignment to map a
52  * flexpage that contains the address at (start + offset) from master to replica.
53  */
54 l4_umword_t Romain::PageFaultObserver::fit_alignment(Romain::Region const* local,
55                                                                                                          L4Re::Util::Region const * remote,
56                                                                                                          l4_umword_t offset,
57                                                                                                          Romain::App_thread *t)
58 {
59         MSGt(t) << "offs in region: " << std::hex << offset;
60         
61         l4_addr_t localbase  = local->start() + offset;
62         l4_addr_t remotebase = remote->start() + offset;
63         MSGt(t) << std::hex << localbase  << "("  << l4util_bsf(localbase) << ") <-> "
64                 << remotebase << "(" << l4util_bsf(remotebase) << ")";
65
66         /*
67          * The maximum possible alignment is the minimum of the zero bits in both
68          * addresses.
69          */
70         l4_umword_t align = std::min(l4util_bsf(localbase), l4util_bsf(remotebase));
71
72         /*
73          * The maximum alignment might lead to a mapping that is larger than the
74          * region itself. Therefore we shrink the mapping until it fits the region.
75          */
76         for (; align > 12; --align) {
77                 l4_umword_t mask    = (1 << align) - 1;
78                 l4_addr_t newlocal  = localbase - (localbase & mask);
79
80                 if ((newlocal >= local->start()) and
81                         (newlocal + (1<<align) - 1 <= local->end())) {
82                         break;
83                 }
84         }
85
86         // XXX: tweak here if you want < 4MB mappings
87         return std::min(align, 22UL);
88 }
89
90 Romain::Observer::ObserverReturnVal
91 Romain::PageFaultObserver::notify(Romain::App_instance *i, Romain::App_thread *t,
92                                   Romain::Thread_group* tg, Romain::App_model *a)
93 {
94         if (!t->vcpu()->is_page_fault_entry())
95                 return Romain::Observer::Ignored;
96
97         if (t->unhandled_pf()) {
98                 enter_kdebug("unhandled pf");
99         }
100
101         Romain::Observer::ObserverReturnVal retVal = Romain::Observer::Finished;
102         L4vcpu::Vcpu *vcpu = t->vcpu();
103
104         bool write_pf = vcpu->r()->err & 0x2;
105         l4_addr_t pfa = vcpu->r()->pfa;
106
107         Measurements::GenericEvent *ev = Romain::_the_instance_manager->logbuf()->next();
108         ev->header.tsc         = Romain::_the_instance_manager->logbuf()->getTime(Log::logLocalTSC);
109         ev->header.vcpu        = (l4_uint32_t)t->vcpu();
110         ev->header.type        = Measurements::Pagefault;
111         ev->data.pf.address    = pfa;
112         ev->data.pf.rw         = write_pf ? 1 : 0;
113         ev->data.pf.localbase  = 0;
114         ev->data.pf.remotebase = 0;
115
116         MSGt(t) << (write_pf ? RED "write" NOCOLOR : BOLD_BLUE "read" NOCOLOR)
117               << " page fault @ 0x" << std::hex << pfa;
118
119         Romain::Region_map::Base::Node n = a->rm()->find(pfa);
120         MSGt(t) << "rm_find(" << std::hex << pfa << ") = " << n;
121         if (n) {
122                 const_cast<Romain::Region_handler&>(n->second).count_pf();
123                 /*
124                  * Lazily establish region handlers for replicas
125                  */
126                 MSGt(t) << Romain::Region_map::print_mapping(n, i->id());
127
128                 if (write_pf && (always_readonly() ||
129                                 (n->second.writable() == Romain::Region_handler::Read_only_emulate_write))) {
130                         ++pf_write;
131                         /* XXX: can we use a static object here instead? */
132                         AppModelAddressTranslator *aat = new AppModelAddressTranslator(a, i);
133                         Romain::WriteEmulator(vcpu, aat).emulate();
134                         delete aat;
135                         // writes are emulated, no need to map here at all
136                         // -> XXX actually, we could also map() here, because if the client
137                         //        writes to memory, it's most probably going to read this
138                         //        data at a later point in time, too
139
140                         /*
141                          * For emulated operations single-stepping won't work, because
142                          * the real instruction is never executed in the target AS. Therefore,
143                          * we need to inject a "virtual" INT1 into the manager here.
144                          */
145                         if (t->vcpu()->r()->flags & TrapFlag)
146                                 t->set_pending_trap(1);
147                 } else {
148                         ++pf_mapped;
149
150                         Romain::Region_handler const * rh   = &n->second;
151                         L4Re::Util::Region const * remote   = &n->first;
152                         l4_addr_t offset_in_region          = l4_trunc_page(pfa - remote->start());
153                         l4_addr_t remotebase                = remote->start() + offset_in_region;
154                         unsigned pageflags                  = rh->is_ro() ? L4_FPAGE_RO : L4_FPAGE_RW;
155                         l4_umword_t last_align = 0;
156
157                         for (l4_umword_t instID = 0;
158                              instID < Romain::_the_instance_manager->instance_count();
159                              ++instID) {
160
161                                 a->rm()->lazy_map_region(n, instID, write_pf);
162
163                                 Romain::Region const * localregion  = &rh->local_region(instID);
164
165                                 MSGt(t) << Romain::Region_map::print_mapping(n, instID);
166                                 MSGt(t) << "rh.alignment() = " << std::dec << rh->alignment();
167                                 /*
168                                  * We try to map the largest possible flexpage to reduce the
169                                  * total number of mappings.
170                                  */
171                                 l4_umword_t align = fit_alignment(localregion, remote, offset_in_region, t);
172
173                                 /*
174                                  * For replicas we unfortunately only have the guarantee that they
175                                  * are aligned to rh->alignment(). Apart from this, the replica's region
176                                  * copies may be aligned differently in the master.
177                                  *
178                                  * Example:
179                                  *   - rh->alignment() = 64kB
180                                  *   - replica1.alignment = 128 kB
181                                  *   - replica2.alignment = 2 MB
182                                  *
183                                  * ==> Here, we may encounter a situation where a page fault can be
184                                  *     serviced with a 256 kB mapping for replica 1 and a 1 MB mapping
185                                  *     for replica 2. The replicas will thereafter diverge.
186                                  *
187                                  * Quickfix: we make sure that memory mappings are at most rh->alignment().
188                                  *
189                                  * Better fix: the PF observer should walk over all replicas and determine
190                                  *             the minimum possible alignment for this specific mapping.
191                                  */
192                                 if (align > rh->alignment()) {
193                                         align = rh->alignment();
194                                 }
195                                 MSGt(t) << "fitting align " << align;
196
197                                 if (instID > 0) {
198                                         if (align != last_align) {
199                                                 ERROR() << "Mapping with diverging alignments!";
200                                                 ERROR() << Romain::Region_map::print_mapping(n, instID);
201                                                 ERROR() << "align: " << align << " but last was " << last_align;
202                                                 enter_kdebug("Align!");
203                                         }
204                                 }
205                                 last_align = align;
206
207                                 /* Calculate the map base addresses for the given alignment */
208                                 l4_addr_t localbase = localregion->start() + offset_in_region;
209                                 localbase  = localbase  - (localbase  & ((1 << align) - 1));
210
211                                 remotebase = remotebase - (remotebase & ((1 << align) - 1));
212
213                                 MSGt(t) << std::hex << "map: " << localbase << " -> "
214                                                 << remotebase << " size " << (1 << align);
215
216                                 //enter_kdebug("pf");
217                                 
218                                 // set flags properly, only check ro(), because else we'd already ended
219                                 // up in the emulation branch above
220                                 ev->data.pf.localbase  = localregion->start() + offset_in_region;
221                                 ev->data.pf.remotebase = remote->start() + offset_in_region;
222
223                                 Romain::_the_instance_manager->instance(instID)->map_aligned(localbase,
224                                                                                                                                                          remotebase,
225                                                                                                                                                          align, pageflags);
226                         }
227                         retVal = Romain::Observer::Replicatable;
228                 }
229
230         } else if ((a->prog_info()->kip <= pfa) && (pfa < a->prog_info()->kip + L4_PAGESIZE)) {
231             ++pf_kip;
232             MSGt(t) << "KIP access ";
233             /* XXX ??? */
234                 MSGt(t) << std::hex << __libc_l4_gettime << " " <<  *(l4_addr_t*)__libc_l4_gettime;
235             t->set_unhandled_pf();
236         } else {
237                 ERROR() << "Unhandled page fault @ address 0x" << std::hex << pfa
238                         << " PC @ 0x" << vcpu->r()->ip;
239                 t->set_unhandled_pf();
240         }
241
242         /*
243          * XXX: For now, tell the manager to let all instances perform their fault handling
244          *      independently. In fact, we could however also handle all faults upon first
245          *      occurrence and return Replicatable here.
246          */
247         MSGt(t) << "Page faults so far: mapped " << pf_mapped << " write emu " << pf_write << " kip " << pf_kip;
248
249         return retVal;
250 }
251
252
253 Romain::PageFaultObserver::PageFaultObserver()
254 {
255         char const *ro = ConfigStringValue("general:page_fault_handling");
256
257         if (ro && (strcmp(ro, "ro") == 0)) {
258                 _readonly = true;
259         } else {
260                 _readonly = false;
261         }
262 }