if (!static_cpu_has(X86_FEATURE_CONSTANT_TSC) ||
!vcpu->kvm->arch.use_master_clock) break;
/* *IfbothXenPVclocksareactive,arbitrarilytrytousethe *compatclockfirst,butalsotrytousethenon-compatclock *ifthecompatclockisunusable.ThetwoPVclocksholdthe *sameinformation,butit'spossibleone(orboth)isstale *and/orcurrentlyunreachable.
*/ if (xen->vcpu_info_cache.active)
r = xen_get_guest_pvclock(vcpu, &hv_clock, &xen->vcpu_info_cache,
offsetof(struct compat_vcpu_info, time)); if (r && xen->vcpu_time_info_cache.active)
r = xen_get_guest_pvclock(vcpu, &hv_clock, &xen->vcpu_time_info_cache, 0); if (r) break;
if (!IS_ENABLED(CONFIG_64BIT) ||
!kvm_get_monotonic_and_clockread(&kernel_now, &host_tsc)) { /* *Don'tfallbacktoget_kvmclock_ns()becauseit's *broken;ithasasystemicerrorinitsresults *becauseitscalesdirectlyfromhostTSCto *nanoseconds,anddoesn'tscalefirsttoguestTSC *and*then*tonanosecondsastheguestdoes. * *Thereisasmallerrorintroducedherebecausetime *continuestoelapsebetweenthektime_get()andthe *subsequentrdtsc().Butnotthesystemicdriftdue *toget_kvmclock_ns().
*/
kernel_now = ktime_get(); /* This is CLOCK_MONOTONIC */
host_tsc = rdtsc();
}
/* Calculate the guest kvmclock as the guest would do it. */
guest_tsc = kvm_read_l1_tsc(vcpu, host_tsc);
guest_now = __pvclock_read_cycles(&hv_clock, guest_tsc);
} while (0);
/* For the split case, we have to then copy it to the guest. */ if (user_len2) {
memcpy(gpc1->khva, rs_state, user_len1);
memcpy(gpc2->khva, ((void *)rs_state) + user_len1, user_len2);
}
smp_wmb();
/* Finally, clear the XEN_RUNSTATE_UPDATE bit. */ if (update_bit) {
entry_time &= ~XEN_RUNSTATE_UPDATE;
*update_bit = entry_time >> 56;
smp_wmb();
}
if (user_len2) {
kvm_gpc_mark_dirty_in_slot(gpc2);
read_unlock(&gpc2->lock);
}
if (kvm_gpc_refresh(gpc, sizeof(struct vcpu_info))) return;
read_lock_irqsave(&gpc->lock, flags);
}
/* Now gpc->khva is a valid kernel address for the vcpu_info */ if (IS_ENABLED(CONFIG_64BIT) && v->kvm->arch.xen.long_mode) { struct vcpu_info *vi = gpc->khva;
case KVM_XEN_ATTR_TYPE_SHARED_INFO: case KVM_XEN_ATTR_TYPE_SHARED_INFO_HVA: { int idx;
mutex_lock(&kvm->arch.xen.xen_lock);
idx = srcu_read_lock(&kvm->srcu);
if (data->type == KVM_XEN_ATTR_TYPE_SHARED_INFO) {
gfn_t gfn = data->u.shared_info.gfn;
if (gfn == KVM_XEN_INVALID_GFN) {
kvm_gpc_deactivate(&kvm->arch.xen.shinfo_cache);
r = 0;
} else {
r = kvm_gpc_activate(&kvm->arch.xen.shinfo_cache,
gfn_to_gpa(gfn), PAGE_SIZE);
}
} else { void __user * hva = u64_to_user_ptr(data->u.shared_info.hva);
if (!PAGE_ALIGNED(hva)) {
r = -EINVAL;
} elseif (!hva) {
kvm_gpc_deactivate(&kvm->arch.xen.shinfo_cache);
r = 0;
} else {
r = kvm_gpc_activate_hva(&kvm->arch.xen.shinfo_cache,
(unsignedlong)hva, PAGE_SIZE);
}
}
srcu_read_unlock(&kvm->srcu, idx);
if (!r && kvm->arch.xen.shinfo_cache.active)
r = kvm_xen_shared_info_init(kvm);
mutex_unlock(&kvm->arch.xen.xen_lock); break;
} case KVM_XEN_ATTR_TYPE_UPCALL_VECTOR: if (data->u.vector && data->u.vector < 0x10)
r = -EINVAL; else {
mutex_lock(&kvm->arch.xen.xen_lock);
kvm->arch.xen.upcall_vector = data->u.vector;
mutex_unlock(&kvm->arch.xen.xen_lock);
r = 0;
} break;
case KVM_XEN_ATTR_TYPE_EVTCHN:
r = kvm_xen_setattr_evtchn(kvm, data); break;
case KVM_XEN_ATTR_TYPE_XEN_VERSION:
mutex_lock(&kvm->arch.xen.xen_lock);
kvm->arch.xen.xen_version = data->u.xen_version;
mutex_unlock(&kvm->arch.xen.xen_lock);
r = 0; break;
case KVM_XEN_ATTR_TYPE_RUNSTATE_UPDATE_FLAG: if (!sched_info_on()) {
r = -EOPNOTSUPP; break;
}
mutex_lock(&kvm->arch.xen.xen_lock);
kvm->arch.xen.runstate_update_flag = !!data->u.runstate_update_flag;
mutex_unlock(&kvm->arch.xen.xen_lock);
r = 0; break;
default: break;
}
return r;
}
int kvm_xen_hvm_get_attr(struct kvm *kvm, struct kvm_xen_hvm_attr *data)
{ int r = -ENOENT;
mutex_lock(&kvm->arch.xen.xen_lock);
switch (data->type) { case KVM_XEN_ATTR_TYPE_LONG_MODE:
data->u.long_mode = kvm->arch.xen.long_mode;
r = 0; break;
case KVM_XEN_ATTR_TYPE_SHARED_INFO: if (kvm_gpc_is_gpa_active(&kvm->arch.xen.shinfo_cache))
data->u.shared_info.gfn = gpa_to_gfn(kvm->arch.xen.shinfo_cache.gpa); else
data->u.shared_info.gfn = KVM_XEN_INVALID_GFN;
r = 0; break;
case KVM_XEN_ATTR_TYPE_SHARED_INFO_HVA: if (kvm_gpc_is_hva_active(&kvm->arch.xen.shinfo_cache))
data->u.shared_info.hva = kvm->arch.xen.shinfo_cache.uhva; else
data->u.shared_info.hva = 0;
r = 0; break;
case KVM_XEN_ATTR_TYPE_UPCALL_VECTOR:
data->u.vector = kvm->arch.xen.upcall_vector;
r = 0; break;
case KVM_XEN_ATTR_TYPE_XEN_VERSION:
data->u.xen_version = kvm->arch.xen.xen_version;
r = 0; break;
case KVM_XEN_ATTR_TYPE_RUNSTATE_UPDATE_FLAG: if (!sched_info_on()) {
r = -EOPNOTSUPP; break;
}
data->u.runstate_update_flag = kvm->arch.xen.runstate_update_flag;
r = 0; break;
switch (data->type) { case KVM_XEN_VCPU_ATTR_TYPE_VCPU_INFO: case KVM_XEN_VCPU_ATTR_TYPE_VCPU_INFO_HVA: /* No compat necessary here. */
BUILD_BUG_ON(sizeof(struct vcpu_info) != sizeof(struct compat_vcpu_info));
BUILD_BUG_ON(offsetof(struct vcpu_info, time) !=
offsetof(struct compat_vcpu_info, time));
if (data->type == KVM_XEN_VCPU_ATTR_TYPE_VCPU_INFO) { if (data->u.gpa == KVM_XEN_INVALID_GPA) {
kvm_gpc_deactivate(&vcpu->arch.xen.vcpu_info_cache);
r = 0; break;
}
r = kvm_gpc_activate(&vcpu->arch.xen.vcpu_info_cache,
data->u.gpa, sizeof(struct vcpu_info));
} else { if (data->u.hva == 0) {
kvm_gpc_deactivate(&vcpu->arch.xen.vcpu_info_cache);
r = 0; break;
}
r = kvm_gpc_activate_hva(&vcpu->arch.xen.vcpu_info_cache,
data->u.hva, sizeof(struct vcpu_info));
}
if (!r)
kvm_make_request(KVM_REQ_CLOCK_UPDATE, vcpu);
break;
case KVM_XEN_VCPU_ATTR_TYPE_VCPU_TIME_INFO: if (data->u.gpa == KVM_XEN_INVALID_GPA) {
kvm_gpc_deactivate(&vcpu->arch.xen.vcpu_time_info_cache);
r = 0; break;
}
r = kvm_gpc_activate(&vcpu->arch.xen.vcpu_time_info_cache,
data->u.gpa, sizeof(struct pvclock_vcpu_time_info)); if (!r)
kvm_make_request(KVM_REQ_CLOCK_UPDATE, vcpu); break;
case KVM_XEN_VCPU_ATTR_TYPE_RUNSTATE_ADDR: {
size_t sz, sz1, sz2;
if (!sched_info_on()) {
r = -EOPNOTSUPP; break;
} if (data->u.gpa == KVM_XEN_INVALID_GPA) {
r = 0;
deactivate_out:
kvm_gpc_deactivate(&vcpu->arch.xen.runstate_cache);
kvm_gpc_deactivate(&vcpu->arch.xen.runstate2_cache); break;
}
/* How much fits in the (first) page? */
sz1 = PAGE_SIZE - (data->u.gpa & ~PAGE_MASK);
r = kvm_gpc_activate(&vcpu->arch.xen.runstate_cache,
data->u.gpa, sz1); if (r) goto deactivate_out;
/* Either map the second page, or deactivate the second GPC */ if (sz1 >= sz) {
kvm_gpc_deactivate(&vcpu->arch.xen.runstate2_cache);
} else {
sz2 = sz - sz1;
BUG_ON((data->u.gpa + sz1) & ~PAGE_MASK);
r = kvm_gpc_activate(&vcpu->arch.xen.runstate2_cache,
data->u.gpa + sz1, sz2); if (r) goto deactivate_out;
}
kvm_xen_update_runstate_guest(vcpu, false); break;
} case KVM_XEN_VCPU_ATTR_TYPE_RUNSTATE_CURRENT: if (!sched_info_on()) {
r = -EOPNOTSUPP; break;
} if (data->u.runstate.state > RUNSTATE_offline) {
r = -EINVAL; break;
}
kvm_xen_update_runstate(vcpu, data->u.runstate.state);
r = 0; break;
case KVM_XEN_VCPU_ATTR_TYPE_RUNSTATE_DATA: if (!sched_info_on()) {
r = -EOPNOTSUPP; break;
} if (data->u.runstate.state > RUNSTATE_offline) {
r = -EINVAL; break;
} if (data->u.runstate.state_entry_time !=
(data->u.runstate.time_running +
data->u.runstate.time_runnable +
data->u.runstate.time_blocked +
data->u.runstate.time_offline)) {
r = -EINVAL; break;
} if (get_kvmclock_ns(vcpu->kvm) <
data->u.runstate.state_entry_time) {
r = -EINVAL; break;
}
case KVM_XEN_VCPU_ATTR_TYPE_RUNSTATE_ADJUST: if (!sched_info_on()) {
r = -EOPNOTSUPP; break;
} if (data->u.runstate.state > RUNSTATE_offline &&
data->u.runstate.state != (u64)-1) {
r = -EINVAL; break;
} /* The adjustment must add up */ if (data->u.runstate.state_entry_time !=
(data->u.runstate.time_running +
data->u.runstate.time_runnable +
data->u.runstate.time_blocked +
data->u.runstate.time_offline)) {
r = -EINVAL; break;
}
if (get_kvmclock_ns(vcpu->kvm) <
(vcpu->arch.xen.runstate_entry_time +
data->u.runstate.state_entry_time)) {
r = -EINVAL; break;
}
if (data->u.runstate.state <= RUNSTATE_offline)
kvm_xen_update_runstate(vcpu, data->u.runstate.state); elseif (vcpu->arch.xen.runstate_cache.active)
kvm_xen_update_runstate_guest(vcpu, false);
r = 0; break;
case KVM_XEN_VCPU_ATTR_TYPE_VCPU_ID: if (data->u.vcpu_id >= KVM_MAX_VCPUS)
r = -EINVAL; else {
vcpu->arch.xen.vcpu_id = data->u.vcpu_id;
r = 0;
} break;
case KVM_XEN_VCPU_ATTR_TYPE_TIMER: if (data->u.timer.port &&
data->u.timer.priority != KVM_IRQ_ROUTING_XEN_EVTCHN_PRIO_2LEVEL) {
r = -EINVAL; break;
}
/* Stop the timer (if it's running) before changing the vector */
kvm_xen_stop_timer(vcpu);
vcpu->arch.xen.timer_virq = data->u.timer.port;
/* Start the timer if the new value has a valid vector+expiry. */ if (data->u.timer.port && data->u.timer.expires_ns)
kvm_xen_start_timer(vcpu, data->u.timer.expires_ns, false);
r = 0; break;
case KVM_XEN_VCPU_ATTR_TYPE_UPCALL_VECTOR: if (data->u.vector && data->u.vector < 0x10)
r = -EINVAL; else {
vcpu->arch.xen.upcall_vector = data->u.vector;
r = 0;
} break;
int kvm_xen_vcpu_get_attr(struct kvm_vcpu *vcpu, struct kvm_xen_vcpu_attr *data)
{ int r = -ENOENT;
mutex_lock(&vcpu->kvm->arch.xen.xen_lock);
switch (data->type) { case KVM_XEN_VCPU_ATTR_TYPE_VCPU_INFO: if (kvm_gpc_is_gpa_active(&vcpu->arch.xen.vcpu_info_cache))
data->u.gpa = vcpu->arch.xen.vcpu_info_cache.gpa; else
data->u.gpa = KVM_XEN_INVALID_GPA;
r = 0; break;
case KVM_XEN_VCPU_ATTR_TYPE_VCPU_INFO_HVA: if (kvm_gpc_is_hva_active(&vcpu->arch.xen.vcpu_info_cache))
data->u.hva = vcpu->arch.xen.vcpu_info_cache.uhva; else
data->u.hva = 0;
r = 0; break;
case KVM_XEN_VCPU_ATTR_TYPE_VCPU_TIME_INFO: if (vcpu->arch.xen.vcpu_time_info_cache.active)
data->u.gpa = vcpu->arch.xen.vcpu_time_info_cache.gpa; else
data->u.gpa = KVM_XEN_INVALID_GPA;
r = 0; break;
case KVM_XEN_VCPU_ATTR_TYPE_RUNSTATE_ADDR: if (!sched_info_on()) {
r = -EOPNOTSUPP; break;
} if (vcpu->arch.xen.runstate_cache.active) {
data->u.gpa = vcpu->arch.xen.runstate_cache.gpa;
r = 0;
} break;
case KVM_XEN_VCPU_ATTR_TYPE_RUNSTATE_CURRENT: if (!sched_info_on()) {
r = -EOPNOTSUPP; break;
}
data->u.runstate.state = vcpu->arch.xen.current_runstate;
r = 0; break;
case KVM_XEN_VCPU_ATTR_TYPE_RUNSTATE_DATA: if (!sched_info_on()) {
r = -EOPNOTSUPP; break;
}
data->u.runstate.state = vcpu->arch.xen.current_runstate;
data->u.runstate.state_entry_time =
vcpu->arch.xen.runstate_entry_time;
data->u.runstate.time_running =
vcpu->arch.xen.runstate_times[RUNSTATE_running];
data->u.runstate.time_runnable =
vcpu->arch.xen.runstate_times[RUNSTATE_runnable];
data->u.runstate.time_blocked =
vcpu->arch.xen.runstate_times[RUNSTATE_blocked];
data->u.runstate.time_offline =
vcpu->arch.xen.runstate_times[RUNSTATE_offline];
r = 0; break;
case KVM_XEN_VCPU_ATTR_TYPE_RUNSTATE_ADJUST:
r = -EINVAL; break;
case KVM_XEN_VCPU_ATTR_TYPE_VCPU_ID:
data->u.vcpu_id = vcpu->arch.xen.vcpu_id;
r = 0; break;
case KVM_XEN_VCPU_ATTR_TYPE_TIMER: /* *Ensureaconsistentsnapshotofstateiscaptured,witha *timereitherbeingpending,ortheeventchanneldelivered *tothecorrespondingbitintheshared_info.Notstill *lurkinginthetimer_pendingflagfordeferreddelivery. *Purelyasanoptimisation,ifthetimer_expiresfieldis *zero,thatmeansthetimerisn'tactive(oreveninthe *timer_pendingflag)andthereisnoneedtocancelit.
*/ if (vcpu->arch.xen.timer_expires) {
hrtimer_cancel(&vcpu->arch.xen.timer);
kvm_xen_inject_timer_irqs(vcpu);
}
/* int3 to pad */
memset(instructions + 9, 0xcc, sizeof(instructions) - 9);
for (i = 0; i < PAGE_SIZE / sizeof(instructions); i++) {
*(u32 *)&instructions[1] = i; if (kvm_vcpu_write_guest(vcpu,
page_addr + (i * sizeof(instructions)),
instructions, sizeof(instructions))) return1;
}
} else { /* *Note,truncationisanon-issueas'lm'isguaranteedtobe *falsefora32-bitkernel,i.e.whenhva_tisonly4bytes.
*/
hva_t blob_addr = lm ? kvm->arch.xen.hvm_config.blob_addr_64
: kvm->arch.xen.hvm_config.blob_addr_32;
u8 blob_size = lm ? kvm->arch.xen.hvm_config.blob_size_64
: kvm->arch.xen.hvm_config.blob_size_32;
u8 *page; int ret;
if (page_num >= blob_size) return1;
blob_addr += page_num * PAGE_SIZE;
page = memdup_user((u8 __user *)blob_addr, PAGE_SIZE); if (IS_ERR(page)) return PTR_ERR(page);
ret = kvm_vcpu_write_guest(vcpu, page_addr, page, PAGE_SIZE);
kfree(page); if (ret) return1;
} return0;
}
int kvm_xen_hvm_config(struct kvm *kvm, struct kvm_xen_hvm_config *xhc)
{ /* Only some feature flags need to be *enabled* by userspace */
u32 permitted_flags = KVM_XEN_HVM_CONFIG_INTERCEPT_HCALL |
KVM_XEN_HVM_CONFIG_EVTCHN_SEND |
KVM_XEN_HVM_CONFIG_PVCLOCK_TSC_UNSTABLE;
u32 old_flags;
if (xhc->flags & ~permitted_flags) return -EINVAL;
if (unlikely(sched_poll.nr_ports > 1)) { /* Xen (unofficially) limits number of pollers to 128 */ if (sched_poll.nr_ports > 128) {
*r = -EINVAL; returntrue;
}
if (!wait_pending_event(vcpu, sched_poll.nr_ports, ports)) {
kvm_set_mp_state(vcpu, KVM_MP_STATE_HALTED);
if (sched_poll.timeout)
mod_timer(&vcpu->arch.xen.poll_timer,
jiffies + nsecs_to_jiffies(sched_poll.timeout));
kvm_vcpu_halt(vcpu);
if (sched_poll.timeout)
timer_delete(&vcpu->arch.xen.poll_timer);
kvm_set_mp_state(vcpu, KVM_MP_STATE_RUNNABLE);
}
vcpu->arch.xen.poll_evtchn = 0;
*r = 0;
out: /* Really, this is only needed in case of timeout */
clear_bit(vcpu->vcpu_idx, vcpu->kvm->arch.xen.poll_mask);
if (unlikely(sched_poll.nr_ports > 1))
kfree(ports); returntrue;
}
/* *Ifthisportwasn'talreadyset,andifitisn'tmasked,then *wetrytosetthecorrespondingbitinthein-kernelshadowof *evtchn_pending_selforthetargetvCPU.Andif*that*wasn't *alreadyset,thenwekickthevCPUinquestiontowritetothe **real*evtchn_pending_selinitsownguestvcpu_infostruct.
*/ if (test_and_set_bit(xe->port, pending_bits)) {
rc = 0; /* It was already raised */
} elseif (test_bit(xe->port, mask_bits)) {
rc = -ENOTCONN; /* Masked */
kvm_xen_check_poller(vcpu, xe->port);
} else {
rc = 1; /* Delivered to the bitmap in shared_info. */ /* Now switch to the vCPU's vcpu_info to set the index and pending_sel */
read_unlock_irqrestore(&gpc->lock, flags);
gpc = &vcpu->arch.xen.vcpu_info_cache;
read_lock_irqsave(&gpc->lock, flags); if (!kvm_gpc_check(gpc, sizeof(struct vcpu_info))) { /* *Couldnotaccessthevcpu_info.Setthebitin-kernel *andprodthevCPUtodeliveritforitself.
*/ if (!test_and_set_bit(port_word_bit, &vcpu->arch.xen.evtchn_pending_sel))
kick_vcpu = true; goto out_rcu;
}
/* For the per-vCPU lapic vector, deliver it as MSI. */ if (kick_vcpu && vcpu->arch.xen.upcall_vector) {
kvm_xen_inject_vcpu_vector(vcpu);
kick_vcpu = false;
}
}
/* This is the version called from kvm_set_irq() as the .set function */ staticint evtchn_set_fn(struct kvm_kernel_irq_routing_entry *e, struct kvm *kvm, int irq_source_id, int level, bool line_status)
{ if (!level) return -EINVAL;
/* *UpdatetargetvCPUorpriorityforaregisteredsendingchannel.
*/ staticint kvm_xen_eventfd_update(struct kvm *kvm, struct kvm_xen_hvm_attr *data)
{
u32 port = data->u.evtchn.send_port; struct evtchnfd *evtchnfd; int ret;
/* Protect writes to evtchnfd as well as the idr lookup. */
mutex_lock(&kvm->arch.xen.xen_lock);
evtchnfd = idr_find(&kvm->arch.xen.evtchn_ports, port);
ret = -ENOENT; if (!evtchnfd) goto out_unlock;
/* For an UPDATE, nothing may change except the priority/vcpu */
ret = -EINVAL; if (evtchnfd->type != data->u.evtchn.type) goto out_unlock;
/* We only support 2 level event channels for now */ if (data->u.evtchn.deliver.port.priority != KVM_IRQ_ROUTING_XEN_EVTCHN_PRIO_2LEVEL) goto out_unlock;
/* *Configurethetarget(eventfdorlocalportdelivery)forsendingon *agiveneventchannel.
*/ staticint kvm_xen_eventfd_assign(struct kvm *kvm, struct kvm_xen_hvm_attr *data)
{
u32 port = data->u.evtchn.send_port; struct eventfd_ctx *eventfd = NULL; struct evtchnfd *evtchnfd; int ret = -EINVAL;
evtchnfd = kzalloc(sizeof(struct evtchnfd), GFP_KERNEL); if (!evtchnfd) return -ENOMEM;
switch(data->u.evtchn.type) { case EVTCHNSTAT_ipi: /* IPI must map back to the same port# */ if (data->u.evtchn.deliver.port.port != data->u.evtchn.send_port) goto out_noeventfd; /* -EINVAL */ break;
case EVTCHNSTAT_interdomain: if (data->u.evtchn.deliver.port.port) { if (data->u.evtchn.deliver.port.port >= max_evtchn_port(kvm)) goto out_noeventfd; /* -EINVAL */
} else {
eventfd = eventfd_ctx_fdget(data->u.evtchn.deliver.eventfd.fd); if (IS_ERR(eventfd)) {
ret = PTR_ERR(eventfd); goto out_noeventfd;
}
} break;
case EVTCHNSTAT_virq: case EVTCHNSTAT_closed: case EVTCHNSTAT_unbound: case EVTCHNSTAT_pirq: default: /* Unknown event channel type */ goto out; /* -EINVAL */
}
evtchnfd->send_port = data->u.evtchn.send_port;
evtchnfd->type = data->u.evtchn.type; if (eventfd) {
evtchnfd->deliver.eventfd.ctx = eventfd;
} else { /* We only support 2 level event channels for now */ if (data->u.evtchn.deliver.port.priority != KVM_IRQ_ROUTING_XEN_EVTCHN_PRIO_2LEVEL) goto out; /* -EINVAL; */
/* Sanity check: this structure is the same for 32-bit and 64-bit */
BUILD_BUG_ON(sizeof(send) != 4); if (kvm_read_guest_virt(vcpu, param, &send, sizeof(send), &e)) {
*r = -EFAULT; returntrue;
}
if (evtchnfd->deliver.port.port) { int ret = kvm_xen_set_evtchn(&evtchnfd->deliver.port, vcpu->kvm); if (ret < 0 && ret != -ENOTCONN) returnfalse;
} else {
eventfd_signal(evtchnfd->deliver.eventfd.ctx);
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.