:p
atchew
Login
On architectures that run guests in dom0less mode without the PV ABI (currently RISC-V), no shared_info page is allocated and d->shared_info remains NULL throughout the domain lifetime. Several places in common code access d->shared_info through the shared_info() macro or directly, causing UBSAN null-pointer errors on such architectures. Rather than adding runtime NULL guards that are logically unreachable on x86 and Arm (where shared_info is always allocated), introduce a new Kconfig symbol CONFIG_HAS_SHARED_INFO selected by x86 and Arm. On !HAS_SHARED_INFO the shared_info() macro expands to a dereference of a pointer returned by shared_info_absent(), which is declared but intentionally never defined. Any use of shared_info() that is not dead-code-eliminated will therefore cause a link-time failure, making missed guards impossible to overlook. The 2L event-channel ops call shared_info() and must not be compiled on architectures without a shared_info page, so event_2l.o is gated on CONFIG_HAS_SHARED_INFO. On such architectures evtchn_init() installs the FIFO ops as a placeholder instead; evtchn_fifo_word_from_port() is guarded against uninitialised d->evtchn_fifo so the FIFO ops are safe before evtchn_fifo_init_control() is called by the guest. With CONFIG_HAS_SHARED_INFO=n all vCPUs fall back to the global dummy_vcpu_info, so writes through vcpu_info() could leak data between vCPUs. Reviewing the write paths in common code: the write in map_guest_area() stores the constant ~0 so nothing serious would happen if it were leaked; the event_2l.c paths are unreachable because the preceding shared_info() call would trap first; the write in vcpu_info_populate() targets the new mapping buffer, not dummy_vcpu_info. Outside common code, the remaining writes are x86 PV-specific, for which CONFIG_HAS_SHARED_INFO=y. No code changes are needed. Signed-off-by: Oleksii Kurochko <oleksii.kurochko@gmail.com> --- Changes in v4: - event_channel.c: drop the redundant evtchn_fifo_init_ops() in the else branch of evtchn_reset(); evtchn_fifo_destroy() does not undo the ops installed by evtchn_init(), so only the switch back to 2-level ABI needs an explicit call. - shared.h: simplify the !HAS_SHARED_INFO shared_info() definition to use an undefined "extern struct shared_info *shared_info_absent" instead of shared_info_absent() with a typeof cast. - Extend the commit description to note that vcpu_info()/__vcpu_info() uses were also audited: on !HAS_SHARED_INFO vcpu_info_area.map points at dummy_vcpu_info, reads are harmless, and writes in common code do not open a cross-domain info-leak side channel, so no code changes are needed on that path. --- Changes in v3: - Introduce CONFIG_HAS_SHARED_INFO Kconfig symbol selected by x86 and Arm; RISC-V does not select it. - Gate shared_info() macro on CONFIG_HAS_SHARED_INFO; on !HAS_SHARED_INFO it calls shared_info_absent() (declared, never defined) so any unguarded use produces a link-time error. - Replace runtime if (!d->shared_info) guards with IS_ENABLED() at call sites so both branches type-check and dead code is eliminated. - Guard shared_info_frame assignment in domctl.c. - Gate event_2l.o on CONFIG_HAS_SHARED_INFO; use FIFO ops as placeholder on !HAS_SHARED_INFO archs instead of dedicated stub ops; guard evtchn_fifo_word_from_port() against uninitialised d->evtchn_fifo. - Add static inline stubs for evtchn_2l_init() (!HAS_SHARED_INFO) and evtchn_fifo_init_ops() (!EVTCHN_FIFO) so call sites can use IS_ENABLED() without #ifdef. - Drop inaccurate changelog entry about "only FIFO ABI" migration. - Update the commit message. - Drop R-by: Baptiste ... as some extra checks are added. --- Changes in v2: - Update commit message + subject. - Drop Fixes tag. --- xen/arch/arm/Kconfig | 1 + xen/arch/x86/Kconfig | 1 + xen/common/Kconfig | 3 +++ xen/common/Makefile | 2 +- xen/common/domain.c | 6 +++--- xen/common/domctl.c | 11 ++++++++--- xen/common/event_channel.c | 12 +++++++++--- xen/common/event_channel.h | 6 ++++++ xen/common/event_fifo.c | 11 ++++++++++- xen/common/time.c | 2 ++ xen/include/xen/shared.h | 8 +++++++- xen/include/xen/time.h | 4 ++++ 12 files changed, 55 insertions(+), 12 deletions(-) diff --git a/xen/arch/arm/Kconfig b/xen/arch/arm/Kconfig index XXXXXXX..XXXXXXX 100644 --- a/xen/arch/arm/Kconfig +++ b/xen/arch/arm/Kconfig @@ -XXX,XX +XXX,XX @@ config ARM select HAS_DEVICE_TREE_DISCOVERY select HAS_DOM0LESS select HAS_GRANT_CACHE_FLUSH if GRANT_TABLE + select HAS_SHARED_INFO select HAS_STACK_PROTECTOR select HAS_UBSAN diff --git a/xen/arch/x86/Kconfig b/xen/arch/x86/Kconfig index XXXXXXX..XXXXXXX 100644 --- a/xen/arch/x86/Kconfig +++ b/xen/arch/x86/Kconfig @@ -XXX,XX +XXX,XX @@ config X86 select HAS_PCI_MSI select HAS_PIRQ select HAS_SCHED_GRANULARITY + select HAS_SHARED_INFO imply HAS_SOFT_RESET select HAS_UBSAN select HAS_VMAP diff --git a/xen/common/Kconfig b/xen/common/Kconfig index XXXXXXX..XXXXXXX 100644 --- a/xen/common/Kconfig +++ b/xen/common/Kconfig @@ -XXX,XX +XXX,XX @@ config HAS_PMAP config HAS_SCHED_GRANULARITY bool +config HAS_SHARED_INFO + bool + config HAS_SOFT_RESET bool diff --git a/xen/common/Makefile b/xen/common/Makefile index XXXXXXX..XXXXXXX 100644 --- a/xen/common/Makefile +++ b/xen/common/Makefile @@ -XXX,XX +XXX,XX @@ obj-$(CONFIG_DEVICE_TREE_PARSE) += device-tree/ obj-$(CONFIG_IOREQ_SERVER) += dm.o obj-y += domain.o obj-y += domid.o -obj-y += event_2l.o +obj-$(CONFIG_HAS_SHARED_INFO) += event_2l.o obj-y += event_channel.o obj-$(CONFIG_EVTCHN_FIFO) += event_fifo.o obj-$(CONFIG_GRANT_TABLE) += grant_table.o diff --git a/xen/common/domain.c b/xen/common/domain.c index XXXXXXX..XXXXXXX 100644 --- a/xen/common/domain.c +++ b/xen/common/domain.c @@ -XXX,XX +XXX,XX @@ void vcpu_info_reset(struct vcpu *v) struct domain *d = v->domain; v->vcpu_info_area.map = - ((v->vcpu_id < XEN_LEGACY_MAX_VCPUS) - ? (vcpu_info_t *)&shared_info(d, vcpu_info[v->vcpu_id]) - : &dummy_vcpu_info); + IS_ENABLED(CONFIG_HAS_SHARED_INFO) && v->vcpu_id < XEN_LEGACY_MAX_VCPUS + ? (vcpu_info_t *)&shared_info(d, vcpu_info[v->vcpu_id]) + : &dummy_vcpu_info; } static struct domain *alloc_domain_struct(void) diff --git a/xen/common/domctl.c b/xen/common/domctl.c index XXXXXXX..XXXXXXX 100644 --- a/xen/common/domctl.c +++ b/xen/common/domctl.c @@ -XXX,XX +XXX,XX @@ void getdomaininfo(struct domain *d, struct xen_domctl_getdomaininfo *info) #ifdef CONFIG_MEM_PAGING info->paged_pages = atomic_read(&d->paged_pages); #endif - info->shared_info_frame = - gfn_x(mfn_to_gfn(d, _mfn(virt_to_mfn(d->shared_info)))); - BUG_ON(SHARED_M2P(info->shared_info_frame)); + if ( IS_ENABLED(CONFIG_HAS_SHARED_INFO) ) + { + info->shared_info_frame = + gfn_x(mfn_to_gfn(d, _mfn(virt_to_mfn(d->shared_info)))); + BUG_ON(SHARED_M2P(info->shared_info_frame)); + } + else + info->shared_info_frame = INVALID_GFN_RAW; info->cpupool = cpupool_get_id(d); diff --git a/xen/common/event_channel.c b/xen/common/event_channel.c index XXXXXXX..XXXXXXX 100644 --- a/xen/common/event_channel.c +++ b/xen/common/event_channel.c @@ -XXX,XX +XXX,XX @@ int evtchn_reset(struct domain *d, bool resuming) rc = -EAGAIN; else if ( d->evtchn_fifo ) { - /* Switching back to 2-level ABI. */ evtchn_fifo_destroy(d); - evtchn_2l_init(d); + + if ( IS_ENABLED(CONFIG_HAS_SHARED_INFO) ) + /* Switching back to 2-level ABI. */ + evtchn_2l_init(d); } write_unlock(&d->event_lock); @@ -XXX,XX +XXX,XX @@ void evtchn_check_pollers(struct domain *d, unsigned int port) int evtchn_init(struct domain *d, unsigned int max_port) { - evtchn_2l_init(d); + if ( IS_ENABLED(CONFIG_HAS_SHARED_INFO) ) + evtchn_2l_init(d); + else + evtchn_fifo_init_ops(d); + d->max_evtchn_port = min_t(unsigned int, max_port, INT_MAX); d->evtchn = alloc_evtchn_bucket(d, 0); diff --git a/xen/common/event_channel.h b/xen/common/event_channel.h index XXXXXXX..XXXXXXX 100644 --- a/xen/common/event_channel.h +++ b/xen/common/event_channel.h @@ -XXX,XX +XXX,XX @@ static inline void evtchn_port_print_state(struct domain *d, /* 2-level */ +#ifdef CONFIG_HAS_SHARED_INFO void evtchn_2l_init(struct domain *d); +#else +static inline void evtchn_2l_init(struct domain *d) {} +#endif /* FIFO */ @@ -XXX,XX +XXX,XX @@ struct evtchn_expand_array; int evtchn_fifo_init_control(struct evtchn_init_control *init_control); int evtchn_fifo_expand_array(const struct evtchn_expand_array *expand_array); void evtchn_fifo_destroy(struct domain *d); +void evtchn_fifo_init_ops(struct domain *d); #else static inline int evtchn_fifo_init_control(struct evtchn_init_control *init_control) { @@ -XXX,XX +XXX,XX @@ static inline void evtchn_fifo_destroy(struct domain *d) { return; } +static inline void evtchn_fifo_init_ops(struct domain *d) {} #endif /* CONFIG_EVTCHN_FIFO */ #endif /* EVENT_CHANNEL_H */ diff --git a/xen/common/event_fifo.c b/xen/common/event_fifo.c index XXXXXXX..XXXXXXX 100644 --- a/xen/common/event_fifo.c +++ b/xen/common/event_fifo.c @@ -XXX,XX +XXX,XX @@ static inline event_word_t *evtchn_fifo_word_from_port(const struct domain *d, */ smp_rmb(); + if ( unlikely(!d->evtchn_fifo) ) + return NULL; + if ( unlikely(port >= d->evtchn_fifo->num_evtchns) ) return NULL; @@ -XXX,XX +XXX,XX @@ static const struct evtchn_port_ops evtchn_port_ops_fifo = .print_state = evtchn_fifo_print_state, }; +void evtchn_fifo_init_ops(struct domain *d) +{ + d->evtchn_port_ops = &evtchn_port_ops_fifo; +} + static int map_guest_page(struct domain *d, uint64_t gfn, void **virt) { struct page_info *p; @@ -XXX,XX +XXX,XX @@ static void setup_ports(struct domain *d, unsigned int prev_evtchns) evtchn = evtchn_from_port(d, port); - if ( guest_test_bit(d, port, &shared_info(d, evtchn_pending)) ) + if ( IS_ENABLED(CONFIG_HAS_SHARED_INFO) && + guest_test_bit(d, port, &shared_info(d, evtchn_pending)) ) evtchn->pending = true; evtchn_fifo_set_priority(d, evtchn, EVTCHN_FIFO_PRIORITY_DEFAULT); diff --git a/xen/common/time.c b/xen/common/time.c index XXXXXXX..XXXXXXX 100644 --- a/xen/common/time.c +++ b/xen/common/time.c @@ -XXX,XX +XXX,XX @@ struct tm gmtime(unsigned long t) return tbuf; } +#ifdef CONFIG_HAS_SHARED_INFO void update_domain_wallclock_time(struct domain *d) { uint32_t *wc_version; @@ -XXX,XX +XXX,XX @@ void update_domain_wallclock_time(struct domain *d) spin_unlock(&wc_lock); } +#endif /* CONFIG_HAS_SHARED_INFO */ /* Set clock to <secs,usecs> after 00:00:00 UTC, 1 January, 1970. */ void do_settime(u64 secs, unsigned int nsecs, u64 system_time_base) diff --git a/xen/include/xen/shared.h b/xen/include/xen/shared.h index XXXXXXX..XXXXXXX 100644 --- a/xen/include/xen/shared.h +++ b/xen/include/xen/shared.h @@ -XXX,XX +XXX,XX @@ typedef struct vcpu_info vcpu_info_t; extern vcpu_info_t dummy_vcpu_info; -#define shared_info(d, field) __shared_info(d, (d)->shared_info, field) +#ifdef CONFIG_HAS_SHARED_INFO +#define shared_info(d, field) __shared_info(d, (d)->shared_info, field) +#else +extern struct shared_info *shared_info_absent; +#define shared_info(d, field) (((void)(d), shared_info_absent)->field) +#endif /* CONFIG_HAS_SHARED_INFO */ + #define vcpu_info(v, field) \ __vcpu_info(v, (vcpu_info_t *)(v)->vcpu_info_area.map, field) diff --git a/xen/include/xen/time.h b/xen/include/xen/time.h index XXXXXXX..XXXXXXX 100644 --- a/xen/include/xen/time.h +++ b/xen/include/xen/time.h @@ -XXX,XX +XXX,XX @@ struct tm wallclock_time(uint64_t *ns); #define version_update_begin(v) (((v) + 1) | 1) #define version_update_end(v) ((v) + 1) extern void update_vcpu_system_time(struct vcpu *v); +#ifdef CONFIG_HAS_SHARED_INFO extern void update_domain_wallclock_time(struct domain *d); +#else +static inline void update_domain_wallclock_time(struct domain *d) {} +#endif extern void do_settime( u64 secs, unsigned int nsecs, u64 system_time_base); -- 2.54.0
On architectures that run guests in dom0less mode without the PV ABI (currently RISC-V), no shared_info page is allocated and d->shared_info remains NULL throughout the domain lifetime. Several places in common code access d->shared_info through the shared_info() macro or directly, causing UBSAN null-pointer errors on such architectures. Rather than adding runtime NULL guards that are logically unreachable on x86 and Arm (where shared_info is always allocated), introduce a new Kconfig symbol CONFIG_HAS_SHARED_INFO selected by x86 and Arm. On !HAS_SHARED_INFO the shared_info() macro expands to a dereference of shared_info_absent, an extern pointer that is declared but intentionally never defined. Any use of shared_info() that is not dead-code-eliminated will therefore cause a link-time failure, making missed guards impossible to overlook. The 2L event-channel ops call shared_info() and must not be compiled on architectures without a shared_info page, so event_2l.o is gated on CONFIG_HAS_SHARED_INFO. On such architectures evtchn_init() installs the FIFO ops as a placeholder instead, so that a later guest opt-in to the FIFO ABI via EVTCHNOP_init_control has no special-casing to do; if FIFO support itself is also unavailable (!CONFIG_EVTCHN_FIFO), a dedicated no-op evtchn_port_ops_none table is installed instead, so that d->evtchn_port_ops is never NULL. evtchn_fifo_word_from_port() is guarded against uninitialised d->evtchn_fifo so the FIFO ops are safe before evtchn_fifo_init_control() is called by the guest. With CONFIG_HAS_SHARED_INFO=n all vCPUs fall back to the global dummy_vcpu_info, so writes through vcpu_info() could leak data between vCPUs. Reviewing the write paths in common code: the write in map_guest_area() stores the constant ~0 so nothing serious would happen if it were leaked; the event_2l.c paths are not compiled on !HAS_SHARED_INFO, as event_2l.o is gated on CONFIG_HAS_SHARED_INFO; the write in vcpu_info_populate() targets the new mapping buffer, not dummy_vcpu_info. Outside common code, the remaining writes are x86 PV-specific, for which CONFIG_HAS_SHARED_INFO=y. No code changes are needed. Signed-off-by: Oleksii Kurochko <oleksii.kurochko@gmail.com> --- Changes in v5: - drop the static inline evtchn_2l_init() stub for !HAS_SHARED_INFO; a plain declaration is enough since the only call sites are guarded by IS_ENABLED(CONFIG_HAS_SHARED_INFO) and the dead call is eliminated before linking. - fix a NULL d->evtchn_port_ops dereference when CONFIG_HAS_SHARED_INFO=n and CONFIG_EVTCHN_FIFO=n: evtchn_init() was unconditionally calling evtchn_fifo_init_ops(), whose !EVTCHN_FIFO stub leaves d->evtchn_port_ops unset. Gate the FIFO branch on IS_ENABLED(CONFIG_EVTCHN_FIFO) and add a dedicated evtchn_port_ops_none table for the remaining case. Stubs are shared where signatures permit: evtchn_none_noop covers both clear_pending and unmask; evtchn_none_false covers both is_pending and is_masked. evtchn_none_init() is called only from event_channel.c, so its declaration is kept there rather than in event_channel.h. - gate evtchn_fifo_init_ops() on !CONFIG_HAS_SHARED_INFO; its only call site is in the IS_ENABLED(CONFIG_EVTCHN_FIFO) dead branch of evtchn_init(), which is never reached on HAS_SHARED_INFO=y builds. --- Changes in v4: - event_channel.c: drop the redundant evtchn_fifo_init_ops() in the else branch of evtchn_reset(); evtchn_fifo_destroy() does not undo the ops installed by evtchn_init(), so only the switch back to 2-level ABI needs an explicit call. - shared.h: simplify the !HAS_SHARED_INFO shared_info() definition to use an undefined "extern struct shared_info *shared_info_absent" instead of shared_info_absent() with a typeof cast. - Extend the commit description to note that vcpu_info()/__vcpu_info() uses were also audited: on !HAS_SHARED_INFO vcpu_info_area.map points at dummy_vcpu_info, reads are harmless, and writes in common code do not open a cross-domain info-leak side channel, so no code changes are needed on that path. --- Changes in v3: - Introduce CONFIG_HAS_SHARED_INFO Kconfig symbol selected by x86 and Arm; RISC-V does not select it. - Gate shared_info() macro on CONFIG_HAS_SHARED_INFO; on !HAS_SHARED_INFO it calls shared_info_absent() (declared, never defined) so any unguarded use produces a link-time error. - Replace runtime if (!d->shared_info) guards with IS_ENABLED() at call sites so both branches type-check and dead code is eliminated. - Guard shared_info_frame assignment in domctl.c. - Gate event_2l.o on CONFIG_HAS_SHARED_INFO; use FIFO ops as placeholder on !HAS_SHARED_INFO archs instead of dedicated stub ops; guard evtchn_fifo_word_from_port() against uninitialised d->evtchn_fifo. - Add static inline stubs for evtchn_2l_init() (!HAS_SHARED_INFO) and evtchn_fifo_init_ops() (!EVTCHN_FIFO) so call sites can use IS_ENABLED() without #ifdef. - Drop inaccurate changelog entry about "only FIFO ABI" migration. - Update the commit message. - Drop R-by: Baptiste ... as some extra checks are added. --- Changes in v2: - Update commit message + subject. - Drop Fixes tag. --- xen/arch/arm/Kconfig | 1 + xen/arch/x86/Kconfig | 1 + xen/common/Kconfig | 3 +++ xen/common/Makefile | 2 +- xen/common/domain.c | 6 ++--- xen/common/domctl.c | 11 ++++++--- xen/common/event_channel.c | 49 +++++++++++++++++++++++++++++++++++--- xen/common/event_channel.h | 2 ++ xen/common/event_fifo.c | 18 +++++++++++++- xen/common/time.c | 2 ++ xen/include/xen/shared.h | 8 ++++++- xen/include/xen/time.h | 4 ++++ 12 files changed, 95 insertions(+), 12 deletions(-) diff --git a/xen/arch/arm/Kconfig b/xen/arch/arm/Kconfig index XXXXXXX..XXXXXXX 100644 --- a/xen/arch/arm/Kconfig +++ b/xen/arch/arm/Kconfig @@ -XXX,XX +XXX,XX @@ config ARM select HAS_DEVICE_TREE_DISCOVERY select HAS_DOM0LESS select HAS_GRANT_CACHE_FLUSH if GRANT_TABLE + select HAS_SHARED_INFO select HAS_STACK_PROTECTOR select HAS_UBSAN diff --git a/xen/arch/x86/Kconfig b/xen/arch/x86/Kconfig index XXXXXXX..XXXXXXX 100644 --- a/xen/arch/x86/Kconfig +++ b/xen/arch/x86/Kconfig @@ -XXX,XX +XXX,XX @@ config X86 select HAS_PCI_MSI select HAS_PIRQ select HAS_SCHED_GRANULARITY + select HAS_SHARED_INFO imply HAS_SOFT_RESET select HAS_UBSAN select HAS_VMAP diff --git a/xen/common/Kconfig b/xen/common/Kconfig index XXXXXXX..XXXXXXX 100644 --- a/xen/common/Kconfig +++ b/xen/common/Kconfig @@ -XXX,XX +XXX,XX @@ config HAS_PMAP config HAS_SCHED_GRANULARITY bool +config HAS_SHARED_INFO + bool + config HAS_SOFT_RESET bool diff --git a/xen/common/Makefile b/xen/common/Makefile index XXXXXXX..XXXXXXX 100644 --- a/xen/common/Makefile +++ b/xen/common/Makefile @@ -XXX,XX +XXX,XX @@ obj-$(CONFIG_DEVICE_TREE_PARSE) += device-tree/ obj-$(CONFIG_IOREQ_SERVER) += dm.o obj-y += domain.o obj-y += domid.o -obj-y += event_2l.o +obj-$(CONFIG_HAS_SHARED_INFO) += event_2l.o obj-y += event_channel.o obj-$(CONFIG_EVTCHN_FIFO) += event_fifo.o obj-$(CONFIG_GRANT_TABLE) += grant_table.o diff --git a/xen/common/domain.c b/xen/common/domain.c index XXXXXXX..XXXXXXX 100644 --- a/xen/common/domain.c +++ b/xen/common/domain.c @@ -XXX,XX +XXX,XX @@ void vcpu_info_reset(struct vcpu *v) struct domain *d = v->domain; v->vcpu_info_area.map = - ((v->vcpu_id < XEN_LEGACY_MAX_VCPUS) - ? (vcpu_info_t *)&shared_info(d, vcpu_info[v->vcpu_id]) - : &dummy_vcpu_info); + IS_ENABLED(CONFIG_HAS_SHARED_INFO) && v->vcpu_id < XEN_LEGACY_MAX_VCPUS + ? (vcpu_info_t *)&shared_info(d, vcpu_info[v->vcpu_id]) + : &dummy_vcpu_info; } static struct domain *alloc_domain_struct(void) diff --git a/xen/common/domctl.c b/xen/common/domctl.c index XXXXXXX..XXXXXXX 100644 --- a/xen/common/domctl.c +++ b/xen/common/domctl.c @@ -XXX,XX +XXX,XX @@ void getdomaininfo(struct domain *d, struct xen_domctl_getdomaininfo *info) #ifdef CONFIG_MEM_PAGING info->paged_pages = atomic_read(&d->paged_pages); #endif - info->shared_info_frame = - gfn_x(mfn_to_gfn(d, _mfn(virt_to_mfn(d->shared_info)))); - BUG_ON(SHARED_M2P(info->shared_info_frame)); + if ( IS_ENABLED(CONFIG_HAS_SHARED_INFO) ) + { + info->shared_info_frame = + gfn_x(mfn_to_gfn(d, _mfn(virt_to_mfn(d->shared_info)))); + BUG_ON(SHARED_M2P(info->shared_info_frame)); + } + else + info->shared_info_frame = INVALID_GFN_RAW; info->cpupool = cpupool_get_id(d); diff --git a/xen/common/event_channel.c b/xen/common/event_channel.c index XXXXXXX..XXXXXXX 100644 --- a/xen/common/event_channel.c +++ b/xen/common/event_channel.c @@ -XXX,XX +XXX,XX @@ #define consumer_is_xen(e) (!!(e)->xen_consumer) +/* Defined below when !CONFIG_HAS_SHARED_INFO; call is DCE'd otherwise. */ +void evtchn_none_init(struct domain *d); + /* * Lock an event channel exclusively. This is allowed only when the channel is * free or unbound either when taking or when releasing the lock, as any @@ -XXX,XX +XXX,XX @@ int evtchn_reset(struct domain *d, bool resuming) rc = -EAGAIN; else if ( d->evtchn_fifo ) { - /* Switching back to 2-level ABI. */ evtchn_fifo_destroy(d); - evtchn_2l_init(d); + + if ( IS_ENABLED(CONFIG_HAS_SHARED_INFO) ) + /* Switching back to 2-level ABI. */ + evtchn_2l_init(d); + else + evtchn_none_init(d); } write_unlock(&d->event_lock); @@ -XXX,XX +XXX,XX @@ void evtchn_check_pollers(struct domain *d, unsigned int port) } } +#ifndef CONFIG_HAS_SHARED_INFO +/* + * Placeholder ops for domains with neither a shared_info page nor (yet) + * a FIFO control block. None of these are ever reachable in practice; + * they only exist to keep d->evtchn_port_ops non-NULL. + */ +static void cf_check evtchn_none_set_pending( + struct vcpu *v, struct evtchn *evtchn) {} +static void cf_check evtchn_none_noop( + struct domain *d, struct evtchn *evtchn) {} +static bool cf_check evtchn_none_false( + const struct domain *d, const struct evtchn *evtchn) { return false; } +static void cf_check evtchn_none_print_state( + struct domain *d, const struct evtchn *evtchn) {} + +static const struct evtchn_port_ops evtchn_port_ops_none = { + .set_pending = evtchn_none_set_pending, + .clear_pending = evtchn_none_noop, + .unmask = evtchn_none_noop, + .is_pending = evtchn_none_false, + .is_masked = evtchn_none_false, + .print_state = evtchn_none_print_state, +}; + +void evtchn_none_init(struct domain *d) +{ + d->evtchn_port_ops = &evtchn_port_ops_none; +} +#endif /* !CONFIG_HAS_SHARED_INFO */ + int evtchn_init(struct domain *d, unsigned int max_port) { - evtchn_2l_init(d); + if ( IS_ENABLED(CONFIG_HAS_SHARED_INFO) ) + evtchn_2l_init(d); + else if ( IS_ENABLED(CONFIG_EVTCHN_FIFO) ) + evtchn_fifo_init_ops(d); + else + evtchn_none_init(d); + d->max_evtchn_port = min_t(unsigned int, max_port, INT_MAX); d->evtchn = alloc_evtchn_bucket(d, 0); diff --git a/xen/common/event_channel.h b/xen/common/event_channel.h index XXXXXXX..XXXXXXX 100644 --- a/xen/common/event_channel.h +++ b/xen/common/event_channel.h @@ -XXX,XX +XXX,XX @@ struct evtchn_expand_array; int evtchn_fifo_init_control(struct evtchn_init_control *init_control); int evtchn_fifo_expand_array(const struct evtchn_expand_array *expand_array); void evtchn_fifo_destroy(struct domain *d); +void evtchn_fifo_init_ops(struct domain *d); #else static inline int evtchn_fifo_init_control(struct evtchn_init_control *init_control) { @@ -XXX,XX +XXX,XX @@ static inline void evtchn_fifo_destroy(struct domain *d) { return; } +static inline void evtchn_fifo_init_ops(struct domain *d) {} #endif /* CONFIG_EVTCHN_FIFO */ #endif /* EVENT_CHANNEL_H */ diff --git a/xen/common/event_fifo.c b/xen/common/event_fifo.c index XXXXXXX..XXXXXXX 100644 --- a/xen/common/event_fifo.c +++ b/xen/common/event_fifo.c @@ -XXX,XX +XXX,XX @@ static inline event_word_t *evtchn_fifo_word_from_port(const struct domain *d, */ smp_rmb(); + if ( unlikely(!d->evtchn_fifo) ) + return NULL; + if ( unlikely(port >= d->evtchn_fifo->num_evtchns) ) return NULL; @@ -XXX,XX +XXX,XX @@ static const struct evtchn_port_ops evtchn_port_ops_fifo = .print_state = evtchn_fifo_print_state, }; +/* + * evtchn_fifo_init_ops() only call site is in the + * IS_ENABLED(CONFIG_EVTCHN_FIFO) dead branch of evtchn_init(), which is never + * reached on HAS_SHARED_INFO=y builds because of DCE. + */ +#ifndef CONFIG_HAS_SHARED_INFO +void evtchn_fifo_init_ops(struct domain *d) +{ + d->evtchn_port_ops = &evtchn_port_ops_fifo; +} +#endif + static int map_guest_page(struct domain *d, uint64_t gfn, void **virt) { struct page_info *p; @@ -XXX,XX +XXX,XX @@ static void setup_ports(struct domain *d, unsigned int prev_evtchns) evtchn = evtchn_from_port(d, port); - if ( guest_test_bit(d, port, &shared_info(d, evtchn_pending)) ) + if ( IS_ENABLED(CONFIG_HAS_SHARED_INFO) && + guest_test_bit(d, port, &shared_info(d, evtchn_pending)) ) evtchn->pending = true; evtchn_fifo_set_priority(d, evtchn, EVTCHN_FIFO_PRIORITY_DEFAULT); diff --git a/xen/common/time.c b/xen/common/time.c index XXXXXXX..XXXXXXX 100644 --- a/xen/common/time.c +++ b/xen/common/time.c @@ -XXX,XX +XXX,XX @@ struct tm gmtime(unsigned long t) return tbuf; } +#ifdef CONFIG_HAS_SHARED_INFO void update_domain_wallclock_time(struct domain *d) { uint32_t *wc_version; @@ -XXX,XX +XXX,XX @@ void update_domain_wallclock_time(struct domain *d) spin_unlock(&wc_lock); } +#endif /* CONFIG_HAS_SHARED_INFO */ /* Set clock to <secs,usecs> after 00:00:00 UTC, 1 January, 1970. */ void do_settime(u64 secs, unsigned int nsecs, u64 system_time_base) diff --git a/xen/include/xen/shared.h b/xen/include/xen/shared.h index XXXXXXX..XXXXXXX 100644 --- a/xen/include/xen/shared.h +++ b/xen/include/xen/shared.h @@ -XXX,XX +XXX,XX @@ typedef struct vcpu_info vcpu_info_t; extern vcpu_info_t dummy_vcpu_info; -#define shared_info(d, field) __shared_info(d, (d)->shared_info, field) +#ifdef CONFIG_HAS_SHARED_INFO +#define shared_info(d, field) __shared_info(d, (d)->shared_info, field) +#else +extern struct shared_info *shared_info_absent; +#define shared_info(d, field) (((void)(d), shared_info_absent)->field) +#endif /* CONFIG_HAS_SHARED_INFO */ + #define vcpu_info(v, field) \ __vcpu_info(v, (vcpu_info_t *)(v)->vcpu_info_area.map, field) diff --git a/xen/include/xen/time.h b/xen/include/xen/time.h index XXXXXXX..XXXXXXX 100644 --- a/xen/include/xen/time.h +++ b/xen/include/xen/time.h @@ -XXX,XX +XXX,XX @@ struct tm wallclock_time(uint64_t *ns); #define version_update_begin(v) (((v) + 1) | 1) #define version_update_end(v) ((v) + 1) extern void update_vcpu_system_time(struct vcpu *v); +#ifdef CONFIG_HAS_SHARED_INFO extern void update_domain_wallclock_time(struct domain *d); +#else +static inline void update_domain_wallclock_time(struct domain *d) {} +#endif extern void do_settime( u64 secs, unsigned int nsecs, u64 system_time_base); -- 2.54.0