8 public cxx::Dyn_castable<Dmar_space, Task>
12 static void clean_dcache(T *p)
13 { Mem_unit::clean_dcache(p, p + 1); }
21 Dmar_ptr_val() = default;
22 Dmar_ptr_val(Unsigned64 v) : v(v) {}
23 CXX_BITFIELD_MEMBER(0, 1, present, v);
29 typedef Mem_space::Attr Attr;
33 Dmar_ptr(Unsigned64 *e, unsigned char l)
34 : e(reinterpret_cast<Dmar_ptr_val*>(e)), level(l) {}
36 bool is_valid() const { return e->present(); }
38 { return (level == Dmar_pt::Depth) || (e->v & (1 << 7)); }
39 Unsigned64 next_level() const
40 { return cxx::mask_lsb(e->v, Config::PAGE_SHIFT); }
42 void set(Unsigned64 v);
43 void clear() { set(0); }
45 unsigned char page_order() const;
46 Unsigned64 page_addr() const;
49 typedef L4_fpage::Rights R;
51 auto raw = access_once(&e->v);
54 if (raw & 2) r |= R::W();
56 return Attr(r, Page::Type::Normal());
59 bool add_attribs(Page::Attr attr)
61 typedef L4_fpage::Rights R;
63 if (attr.rights & R::W())
65 auto p = access_once(&e->v);
78 void set_next_level(Unsigned64 phys)
81 void write_back_if(bool) const {}
82 static void write_back(void *, void *) {}
84 L4_fpage::Rights access_flags() const
86 return L4_fpage::Rights(0);
89 void del_rights(L4_fpage::Rights r)
91 if (r & L4_fpage::Rights::W())
93 auto p = access_once(&e->v);
94 auto o = p & ~(Unsigned64)2;
104 * WARNING: The VT-d documentation says that the super page bit
105 * WARNING: is ignored in page table entries for 4k pages. However,
106 * WARNING: this is not true. The super page bit must be zero.
108 void create_page(Phys_mem_addr addr, Page::Attr attr)
110 typedef L4_fpage::Rights R;
112 assert(level <= Dmar_pt::Depth);
113 Unsigned64 r = (level == Dmar_pt::Depth) ? 0 : (Unsigned64)(1<<7);
115 if (attr.rights & R::W()) r |= 2;
117 set(cxx::int_value<Phys_mem_addr>(addr) | r);
121 typedef Ptab::Tupel<Ptab::Traits<Unsigned64, 39, 9, true>,
122 Ptab::Traits<Unsigned64, 30, 9, true>,
123 Ptab::Traits<Unsigned64, 21, 9, true>,
124 Ptab::Traits<Unsigned64, 12, 9, true> >::List Dmar_traits;
126 typedef Ptab::Shift<Dmar_traits, 12>::List Dmar_traits_vpn;
127 typedef Ptab::Page_addr_wrap<Page_number, 12> Dmar_va_vpn;
128 typedef Ptab::Base<Dmar_ptr, Dmar_traits_vpn, Dmar_va_vpn> Dmar_pt;
131 enum { Max_nr_did = 0x10000 };
132 virtual void *debug_dir() const { return (void *)_dmarpt; }
133 static void create_identity_map();
134 static Dmar_pt *identity_map;
136 virtual void tlb_flush(bool);
137 virtual bool global() const { return true; }
143 static bool _initialized;
145 typedef Bitmap<Max_nr_did> Did_map;
147 static Did_map *_free_dids;
148 static unsigned _max_did;
151 // -----------------------------------------------------------
152 IMPLEMENTATION [iommu]:
154 #include "boot_alloc.h"
155 #include "intel_iommu.h"
156 #include "kmem_slab.h"
159 Dmar_space::Dmar_pt *Dmar_space::identity_map;
160 bool Dmar_space::_initialized;
161 Dmar_space::Did_map *Dmar_space::_free_dids;
162 unsigned Dmar_space::_max_did;
167 Dmar_space::mem_space_map_max_address() const override
169 return Page_number(1UL << (Intel::Io_mmu::hw_addr_width - Mem_space::Page_shift));
174 Dmar_space::get_root(Dmar_pt *pt, unsigned aw_level)
177 if (aw_level == Dmar_pt::Depth + 1)
178 return Mem_layout::pmem_to_phys(pt);
180 assert(aw_level <= Dmar_pt::Depth);
182 auto i = pt->walk(Mem_space::V_pfn(0), Dmar_pt::Depth - aw_level);
183 assert(i.is_valid());
184 return i.next_level();
189 Dmar_space::get_root(int aw_level) const
191 return get_root(_dmarpt, aw_level);
196 Dmar_space::alloc_did()
198 /* DID 0 may be reserved by the architecture, DID 1 is identity map. */
199 for (unsigned did = 2; did < _max_did; ++did)
200 if (_free_dids->atomic_get_and_set(did) == false)
203 panic("DMAR: Out of DIDs");
208 Dmar_space::free_did(unsigned long did)
210 if (_free_dids->atomic_get_and_clear(did) != true)
211 panic("DMAR: Freeing free DID");
216 Dmar_space::init(unsigned max_did)
218 add_page_size(Mem_space::Page_order(Config::PAGE_SHIFT));
219 /* XXX CEH: Add additional page sizes based on CAP_REG[34:37] */
222 _free_dids = new Boot_object<Did_map>();
228 Dmar_space::initialize()
235 b = Kmem_alloc::allocator()->q_alloc(ram_quota(), Config::PAGE_SHIFT);
236 if (EXPECT_FALSE(!b))
239 _dmarpt = static_cast<Dmar_pt *>(b);
240 _dmarpt->clear(false);
243 * Make sure that the very first entry in a page table is valid and
244 * not a super page. This is neccessary if the hardware supports
245 * fewer levels than the current software implementation.
247 * Force allocation of two levels in entry 0, so get_root works
249 auto i = _dmarpt->walk(Mem_space::V_pfn(0), 2, false,
250 Kmem_alloc::q_allocator(ram_quota()));
253 // Got a page-table entry with the wrong level. That happens in the
254 // case of an out-of-memory situation. So free everything we already
255 // allocated and fail.
256 _dmarpt->destroy(Virt_addr(0UL), Virt_addr(~0UL), 0, Dmar_pt::Depth,
257 Kmem_alloc::q_allocator(ram_quota()));
258 Kmem_alloc::allocator()->q_free(ram_quota(), Config::PAGE_SHIFT, _dmarpt);
268 Dmar_space::Dmar_ptr::set(Unsigned64 v)
270 write_consistent(e, Dmar_ptr_val(v));
276 Dmar_space::get_did()
278 // XXX: possibly need a loop here
281 unsigned long ndid = alloc_did();
282 if (!mp_cas(&_did, (unsigned long)0, ndid))
290 Dmar_space::create_identity_map()
295 WARN("At least one IOMMU does not support passthrough.\n");
297 check (identity_map = Kmem_alloc::allocator()->alloc_array<Dmar_pt>(1));
298 identity_map->clear(false);
300 Unsigned64 max_phys = 0;
301 for (auto const &m: Kip::k()->mem_descs_a())
302 if (m.valid() && m.type() == Mem_desc::Conventional
303 && !m.is_virtual() && m.end() > max_phys)
307 epfn = min(1ULL << (Intel::Io_mmu::hw_addr_width - Config::PAGE_SHIFT),
308 (max_phys + Config::PAGE_SIZE - 1) >> Config::PAGE_SHIFT);
310 printf("IOMMU: identity map 0 - 0x%llx (%lldGB)\n", epfn << Config::PAGE_SHIFT,
311 (epfn << Config::PAGE_SHIFT) >> 30);
312 for (Unsigned64 pfn = 0; pfn <= epfn; ++pfn)
314 auto i = identity_map->walk(Mem_space::V_pfn(pfn),
315 Dmar_pt::Depth, false,
316 Kmem_alloc::q_allocator(Ram_quota::root));
317 if (i.page_order() != 12)
318 panic("IOMMU: cannot allocate identity IO page table, OOM\n");
320 i.set((pfn << Config::PAGE_SHIFT) | 3);
327 Dmar_space::resume_vcpu(Context *, Vcpu_state *, bool) override
329 return -L4_err::EInval;
335 Dmar_space::Dmar_ptr::page_order() const
336 { return Dmar_space::Dmar_pt::page_order_for_level(level); }
340 Dmar_space::Dmar_ptr::page_addr() const
342 unsigned char o = page_order();
343 return cxx::mask_lsb(e->v, o);
348 Dmar_space::tlb_flush(bool)
351 for (auto &mmu: Intel::Io_mmu::iommus)
352 mmu.flush_iotlb(_did);
357 Dmar_space::v_lookup(Mem_space::Vaddr virt, Mem_space::Phys_addr *phys,
358 Mem_space::Page_order *order,
359 Mem_space::Attr *page_attribs) override
361 auto i = _dmarpt->walk(virt);
362 // XXX CEH: Check if this hack is still needed!
364 *order = Mem_space::Page_order(i.page_order() > 30 ? 30 : i.page_order());
370 *phys = Mem_space::Phys_addr(i.page_addr());
373 *page_attribs = i.attribs();
380 Dmar_space::v_insert(Mem_space::Phys_addr phys, Mem_space::Vaddr virt,
381 Mem_space::Page_order order,
382 Mem_space::Attr page_attribs) override
384 assert (cxx::get_lsb(Mem_space::Phys_addr(phys), order) == 0);
385 assert (cxx::get_lsb(Virt_addr(virt), order) == 0);
388 for (level = 0; level < Dmar_pt::Depth; ++level)
389 if (Mem_space::Page_order(Dmar_pt::page_order_for_level(level)) <= order)
392 auto i = _dmarpt->walk(virt, level, false,
393 Kmem_alloc::q_allocator(ram_quota()));
395 if (EXPECT_FALSE(!i.is_valid() && i.level != level))
396 return Mem_space::Insert_err_nomem;
398 if (EXPECT_FALSE(i.is_valid()
399 && (i.level != level || Mem_space::Phys_addr(i.page_addr()) != phys)))
400 return Mem_space::Insert_err_exists;
404 if (EXPECT_FALSE(!i.add_attribs(page_attribs)))
405 return Mem_space::Insert_warn_exists;
407 return Mem_space::Insert_warn_attrib_upgrade;
411 i.create_page(phys, page_attribs);
412 return Mem_space::Insert_ok;
418 Dmar_space::v_delete(Mem_space::Vaddr virt, Mem_space::Page_order order,
419 L4_fpage::Rights page_attribs) override
421 assert(cxx::get_lsb(Virt_addr(virt), order) == 0);
423 auto i = _dmarpt->walk(virt);
425 if (EXPECT_FALSE(!i.is_valid()))
426 return L4_fpage::Rights(0);
428 if (EXPECT_FALSE(Mem_space::Page_order(i.page_order()) != order))
429 return L4_fpage::Rights(0);
431 L4_fpage::Rights ret = i.access_flags();
433 if (!(page_attribs & L4_fpage::Rights::R()))
434 i.del_rights(page_attribs);
443 Dmar_space::v_set_access_flags(Mem_space::Vaddr, L4_fpage::Rights) override
446 static Mem_space::Fit_size::Size_array __dmar_ps;
450 Dmar_space::mem_space_fitting_sizes() const override
451 { return Mem_space::Fit_size(__dmar_ps); }
455 Dmar_space::add_page_size(Mem_space::Page_order o)
457 add_global_page_size(o);
458 for (Mem_space::Page_order c = o; c < __dmar_ps.size(); ++c)
464 Dmar_space::operator new (size_t size, void *p) throw()
467 assert (size == sizeof (Dmar_space));
473 Dmar_space::operator delete (void *ptr)
475 Dmar_space *t = reinterpret_cast<Dmar_space *>(ptr);
476 Kmem_slab_t<Dmar_space>::q_free(t->ram_quota(), ptr);
480 Dmar_space::Dmar_space(Ram_quota *q)
481 : Dyn_castable_class(q, Caps::mem()),
487 Dmar_space::remove_from_all_iommus()
489 unsigned long did = access_once(&_did);
493 // someone else changed the did
494 if (!mp_cas(&_did, did, 0ul))
497 for (auto &mmu: Intel::Io_mmu::iommus)
499 for (unsigned bus = 0; bus < 255; ++bus)
500 for (unsigned df = 0; df < 255; ++df)
502 auto entryp = mmu.get_context_entry(bus, df, false);
504 break; // complete bus is empty
506 Intel::Io_mmu::Cte entry = access_once(entryp.unsafe_ptr());
507 // different space bound, skip
508 if (entry.slptptr() != get_root(mmu.aw()))
511 // when the CAS fails someone else already unbound this slot,
512 // so ignore that case
513 mmu.cas_context_entry(entryp, bus, df, entry, Intel::Io_mmu::Cte());
516 mmu.flush_iotlb(did);
524 Dmar_space::destroy(Kobject ***rl) override
527 remove_from_all_iommus();
531 Dmar_space::~Dmar_space()
533 remove_from_all_iommus();
537 _dmarpt->destroy(Virt_addr(0UL), Virt_addr(~0UL), 0, Dmar_pt::Depth,
538 Kmem_alloc::q_allocator(ram_quota()));
539 Kmem_alloc::allocator()->q_free(ram_quota(), Config::PAGE_SHIFT, _dmarpt);
545 static inline void __attribute__((constructor)) FIASCO_INIT
548 Kobject_iface::set_factory(L4_msg_tag::Label_dma_space,
549 &Task::generic_factory<Dmar_space>);