4 * Implementation of page fault handling.
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.
13 #include "../constants.h"
15 #include "../exceptions"
17 #include "../emulation"
18 #include "../app_loading"
19 #include "../locking.h"
20 #include "../configuration"
21 #include "syscalls_handler.h"
23 #include "observers.h"
25 #include <l4/sys/kdebug.h>
26 #include <l4/util/bitops.h>
29 extern l4_addr_t __libc_l4_gettime;
31 static int pf_write = 0; // statistical counter
32 static int pf_mapped = 0; // statistical counter
33 static int pf_kip = 0; // statistical counter
35 #define MSG() DEBUGf(Romain::Log::Memory) << "[" << i->id() << "] "
36 #define MSGt(t) DEBUGf(Romain::Log::Faults) << "[" << t->vcpu() << "] "
38 /*****************************************************************
39 * Page Fault Handling *
40 *****************************************************************/
41 DEFINE_EMPTY_STARTUP(PageFaultObserver)
43 void Romain::PageFaultObserver::status() const
45 INFO() << "[ pf ] Page faults so far: mapped " << pf_mapped << " write emu "
46 << pf_write << " kip " << pf_kip;
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.
54 l4_umword_t Romain::PageFaultObserver::fit_alignment(Romain::Region const* local,
55 L4Re::Util::Region const * remote,
57 Romain::App_thread *t)
59 MSGt(t) << "offs in region: " << std::hex << offset;
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) << ")";
67 * The maximum possible alignment is the minimum of the zero bits in both
70 l4_umword_t align = std::min(l4util_bsf(localbase), l4util_bsf(remotebase));
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.
76 for (; align > 12; --align) {
77 l4_umword_t mask = (1 << align) - 1;
78 l4_addr_t newlocal = localbase - (localbase & mask);
80 if ((newlocal >= local->start()) and
81 (newlocal + (1<<align) - 1 <= local->end())) {
86 // XXX: tweak here if you want < 4MB mappings
87 return std::min(align, 22UL);
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)
94 if (!t->vcpu()->is_page_fault_entry())
95 return Romain::Observer::Ignored;
97 if (t->unhandled_pf()) {
98 enter_kdebug("unhandled pf");
101 Romain::Observer::ObserverReturnVal retVal = Romain::Observer::Finished;
102 L4vcpu::Vcpu *vcpu = t->vcpu();
104 bool write_pf = vcpu->r()->err & 0x2;
105 l4_addr_t pfa = vcpu->r()->pfa;
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;
116 MSGt(t) << (write_pf ? RED "write" NOCOLOR : BOLD_BLUE "read" NOCOLOR)
117 << " page fault @ 0x" << std::hex << pfa;
119 Romain::Region_map::Base::Node n = a->rm()->find(pfa);
120 MSGt(t) << "rm_find(" << std::hex << pfa << ") = " << n;
122 const_cast<Romain::Region_handler&>(n->second).count_pf();
124 * Lazily establish region handlers for replicas
126 MSGt(t) << Romain::Region_map::print_mapping(n, i->id());
128 if (write_pf && (always_readonly() ||
129 (n->second.writable() == Romain::Region_handler::Read_only_emulate_write))) {
131 /* XXX: can we use a static object here instead? */
132 AppModelAddressTranslator *aat = new AppModelAddressTranslator(a, i);
133 Romain::WriteEmulator(vcpu, aat).emulate();
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
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.
145 if (t->vcpu()->r()->flags & TrapFlag)
146 t->set_pending_trap(1);
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;
157 for (l4_umword_t instID = 0;
158 instID < Romain::_the_instance_manager->instance_count();
161 a->rm()->lazy_map_region(n, instID, write_pf);
163 Romain::Region const * localregion = &rh->local_region(instID);
165 MSGt(t) << Romain::Region_map::print_mapping(n, instID);
166 MSGt(t) << "rh.alignment() = " << std::dec << rh->alignment();
168 * We try to map the largest possible flexpage to reduce the
169 * total number of mappings.
171 l4_umword_t align = fit_alignment(localregion, remote, offset_in_region, t);
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.
179 * - rh->alignment() = 64kB
180 * - replica1.alignment = 128 kB
181 * - replica2.alignment = 2 MB
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.
187 * Quickfix: we make sure that memory mappings are at most rh->alignment().
189 * Better fix: the PF observer should walk over all replicas and determine
190 * the minimum possible alignment for this specific mapping.
192 if (align > rh->alignment()) {
193 align = rh->alignment();
195 MSGt(t) << "fitting align " << align;
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!");
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));
211 remotebase = remotebase - (remotebase & ((1 << align) - 1));
213 MSGt(t) << std::hex << "map: " << localbase << " -> "
214 << remotebase << " size " << (1 << align);
216 //enter_kdebug("pf");
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;
223 Romain::_the_instance_manager->instance(instID)->map_aligned(localbase,
227 retVal = Romain::Observer::Replicatable;
230 } else if ((a->prog_info()->kip <= pfa) && (pfa < a->prog_info()->kip + L4_PAGESIZE)) {
232 MSGt(t) << "KIP access ";
234 MSGt(t) << std::hex << __libc_l4_gettime << " " << *(l4_addr_t*)__libc_l4_gettime;
235 t->set_unhandled_pf();
237 ERROR() << "Unhandled page fault @ address 0x" << std::hex << pfa
238 << " PC @ 0x" << vcpu->r()->ip;
239 t->set_unhandled_pf();
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.
247 MSGt(t) << "Page faults so far: mapped " << pf_mapped << " write emu " << pf_write << " kip " << pf_kip;
253 Romain::PageFaultObserver::PageFaultObserver()
255 char const *ro = ConfigStringValue("general:page_fault_handling");
257 if (ro && (strcmp(ro, "ro") == 0)) {