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>
28 extern l4_addr_t __libc_l4_gettime;
30 static int pf_write = 0; // statistical counter
31 static int pf_mapped = 0; // statistical counter
32 static int pf_kip = 0; // statistical counter
34 #define MSG() DEBUGf(Romain::Log::Memory) << "[" << i->id() << "] "
35 #define MSGt(t) DEBUGf(Romain::Log::Faults) << "[" << t->vcpu() << "] "
37 /*****************************************************************
38 * Page Fault Handling *
39 *****************************************************************/
40 DEFINE_EMPTY_STARTUP(PageFaultObserver)
42 void Romain::PageFaultObserver::status() const
44 INFO() << "[ pf ] Page faults so far: mapped " << pf_mapped << " write emu "
45 << pf_write << " kip " << pf_kip;
50 * Prepare for a mapping by finding the largest possible alignment to map a
51 * flexpage that contains the address at (start + offset) from master to replica.
53 l4_umword_t Romain::PageFaultObserver::fit_alignment(Romain::Region const* local,
54 L4Re::Util::Region const * remote,
56 Romain::App_thread *t)
58 MSGt(t) << "offs in region: " << std::hex << offset;
60 l4_addr_t localbase = local->start() + offset;
61 l4_addr_t remotebase = remote->start() + offset;
62 MSGt(t) << std::hex << localbase << "(" << l4util_bsf(localbase) << ") <-> "
63 << remotebase << "(" << l4util_bsf(remotebase) << ")";
66 * The maximum possible alignment is the minimum of the zero bits in both
69 l4_umword_t align = std::min(l4util_bsf(localbase), l4util_bsf(remotebase));
72 * The maximum alignment might lead to a mapping that is larger than the
73 * region itself. Therefore we shrink the mapping until it fits the region.
75 for (; align > 12; --align) {
76 l4_umword_t mask = (1 << align) - 1;
77 l4_addr_t newlocal = localbase - (localbase & mask);
79 if ((newlocal >= local->start()) and
80 (newlocal + (1<<align) - 1 <= local->end())) {
87 /* XXX: Use this if you want to test the single-page-mapping version. */
88 //return L4_PAGESHIFT;
91 Romain::Observer::ObserverReturnVal
92 Romain::PageFaultObserver::notify(Romain::App_instance *i, Romain::App_thread *t,
93 Romain::Thread_group* tg, Romain::App_model *a)
95 if (!t->vcpu()->is_page_fault_entry())
96 return Romain::Observer::Ignored;
98 if (t->unhandled_pf()) {
99 enter_kdebug("unhandled pf");
102 Romain::Observer::ObserverReturnVal retVal = Romain::Observer::Finished;
103 L4vcpu::Vcpu *vcpu = t->vcpu();
105 bool write_pf = vcpu->r()->err & 0x2;
106 l4_addr_t pfa = vcpu->r()->pfa;
108 Measurements::GenericEvent *ev = Romain::_the_instance_manager->logbuf()->next();
109 ev->header.tsc = Romain::_the_instance_manager->logbuf()->getTime(Log::logLocalTSC);
110 ev->header.vcpu = (l4_uint32_t)t->vcpu();
111 ev->header.type = Measurements::Pagefault;
112 ev->data.pf.address = pfa;
113 ev->data.pf.rw = write_pf ? 1 : 0;
114 ev->data.pf.localbase = 0;
115 ev->data.pf.remotebase = 0;
117 MSGt(t) << (write_pf ? RED "write" NOCOLOR : BOLD_BLUE "read" NOCOLOR)
118 << " page fault @ 0x" << std::hex << pfa;
120 Romain::Region_map::Base::Node n = a->rm()->find(pfa);
121 MSGt(t) << "rm_find(" << std::hex << pfa << ") = " << n;
123 const_cast<Romain::Region_handler&>(n->second).count_pf();
125 * Lazily establish region handlers for replicas
127 MSGt(t) << "[" << std::hex
128 << n->second.local_region(i->id()).start() << " - "
129 << n->second.local_region(i->id()).end() << "] ==> "
130 << "[" << n->first.start() << " - "
131 << n->first.end() << "]";
132 MSGt(t) << " DS " << std::hex << n->second.memory(i->id()).cap();
134 if (write_pf && (always_readonly() ||
135 (n->second.writable() == Romain::Region_handler::Read_only_emulate_write))) {
137 /* XXX: can we use a static object here instead? */
138 AppModelAddressTranslator *aat = new AppModelAddressTranslator(a, i);
139 Romain::WriteEmulator(vcpu, aat).emulate();
141 // writes are emulated, no need to map here at all
142 // -> XXX actually, we could also map() here, because if the client
143 // writes to memory, it's most probably going to read this
144 // data at a later point in time, too
147 * For emulated operations single-stepping won't work, because
148 * the real instruction is never executed in the target AS. Therefore,
149 * we need to inject a "virtual" INT1 into the manager here.
151 if (t->vcpu()->r()->flags & TrapFlag)
152 t->set_pending_trap(1);
156 Romain::Region_handler const * rh = &n->second;
157 L4Re::Util::Region const * remote = &n->first;
158 l4_addr_t offset_in_region = l4_trunc_page(pfa - remote->start());
159 l4_addr_t remotebase = remote->start() + offset_in_region;
160 unsigned pageflags = rh->is_ro() ? L4_FPAGE_RO : L4_FPAGE_RW;
162 for (l4_umword_t instID = 0;
163 instID < Romain::_the_instance_manager->instance_count();
166 a->rm()->lazy_map_region(n, instID, write_pf);
168 Romain::Region const * localregion = &rh->local_region(instID);
170 * We try to map the largest possible flexpage to reduce the
171 * total number of mappings.
173 l4_umword_t align = fit_alignment(localregion, remote, offset_in_region, t);
176 * For replicas we unfortunately only have the guarantee that they
177 * are aligned to rh->alignment(). Apart from this, the replica's region
178 * copies may be aligned differently in the master.
181 * - rh->alignment() = 64kB
182 * - replica1.alignment = 128 kB
183 * - replica2.alignment = 2 MB
185 * ==> Here, we may encounter a situation where a page fault can be
186 * serviced with a 256 kB mapping for replica 1 and a 1 MB mapping
187 * for replica 2. The replicas will thereafter diverge.
189 * Quickfix: we make sure that memory mappings are at most rh->alignment().
191 * Better fix: the PF observer should walk over all replicas and determine
192 * the minimum possible alignment for this specific mapping.
194 if (align > rh->alignment()) {
195 align = rh->alignment();
197 MSGt(t) << "fitting align " << align;
199 /* Calculate the map base addresses for the given alignment */
200 l4_addr_t localbase = localregion->start() + offset_in_region;
201 localbase = localbase - (localbase & ((1 << align) - 1));
203 remotebase = remotebase - (remotebase & ((1 << align) - 1));
205 MSGt(t) << std::hex << "map: " << localbase << " -> "
206 << remotebase << " size " << (1 << align);
208 //enter_kdebug("pf");
210 // set flags properly, only check ro(), because else we'd already ended
211 // up in the emulation branch above
212 ev->data.pf.localbase = localregion->start() + offset_in_region;
213 ev->data.pf.remotebase = remote->start() + offset_in_region;
215 Romain::_the_instance_manager->instance(instID)->map_aligned(localbase,
219 retVal = Romain::Observer::Replicatable;
222 } else if ((a->prog_info()->kip <= pfa) && (pfa < a->prog_info()->kip + L4_PAGESIZE)) {
224 MSGt(t) << "KIP access ";
226 MSGt(t) << std::hex << __libc_l4_gettime << " " << *(l4_addr_t*)__libc_l4_gettime;
227 t->set_unhandled_pf();
229 ERROR() << "Unhandled page fault @ address 0x" << std::hex << pfa
230 << " PC @ 0x" << vcpu->r()->ip;
231 t->set_unhandled_pf();
235 * XXX: For now, tell the manager to let all instances perform their fault handling
236 * independently. In fact, we could however also handle all faults upon first
237 * occurrence and return Replicatable here.
239 MSGt(t) << "Page faults so far: mapped " << pf_mapped << " write emu " << pf_write << " kip " << pf_kip;
245 Romain::PageFaultObserver::PageFaultObserver()
247 char const *ro = ConfigStringValue("general:page_fault_handling");
249 if (ro && (strcmp(ro, "ro") == 0)) {