diff --git a/Makefile b/Makefile
index 0b06b9cacda6c2301e253d349c7aad054c369371..ca51abfd8ce8b8607376afdcb2ba898614e98969 100644
--- a/Makefile
+++ b/Makefile
@@ -178,10 +178,6 @@ ifneq ($(wildcard config-host.mak),)
 include $(SRC_PATH)/Makefile.objs
 endif
 
-dummy := $(call unnest-vars,, \
-                common-obj-y \
-                common-obj-m)
-
 include $(SRC_PATH)/tests/Makefile.include
 
 all: $(DOCS) $(if $(BUILD_DOCS),sphinxdocs) recurse-all modules
diff --git a/Makefile.objs b/Makefile.objs
index c332323b81601fc6f36d81412148b199179444a3..c9720a92df6356dfa467656a0bff0a264bbc08c5 100644
--- a/Makefile.objs
+++ b/Makefile.objs
@@ -61,18 +61,10 @@ common-obj-$(if $(CONFIG_RBD),m) += block-rbd$(DSOSUF)
 common-obj-$(if $(CONFIG_LZFSE),m) += block-dmg-lzfse$(DSOSUF)
 common-obj-$(if $(and $(CONFIG_BZIP2),$(CONFIG_DMG)),m) += block-dmg-bz2$(DSOSUF)
 
-common-obj-y += hw/
-common-obj-m += hw/
-
 common-obj-y += libqmp.fa
 
 endif # CONFIG_SOFTMMU
 
-#######################################################################
-# Target-independent parts used in system and user emulation
-
-common-obj-y += hw/
-
 ######################################################################
 # Resource file for Windows executables
 version-obj-$(CONFIG_WIN32) += $(BUILD_DIR)/version.o
diff --git a/Makefile.target b/Makefile.target
index 3534ece38a8ad6c6ff8cdebe1627eecbb04b2532..c95e0deb7914193a635272607e8db2ccee9c9f84 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -156,7 +156,6 @@ endif #CONFIG_BSD_USER
 ifdef CONFIG_SOFTMMU
 obj-y += softmmu/
 obj-y += gdbstub.o
-obj-y += hw/
 LIBS := $(libs_softmmu) $(LIBS)
 
 # Temporary until emulators are linked by Meson
@@ -185,20 +184,6 @@ endif # CONFIG_SOFTMMU
 dummy := $(call unnest-vars,,obj-y)
 all-obj-y := $(obj-y)
 
-#
-# common-obj-m has some crap here, probably as side effect from
-# unnest-vars recursing into target directories to fill obj-y and not
-# properly handling the -m case.
-#
-# Clear common-obj-m as workaround.  Fixes suspious dependency errors
-# when building devices as modules.  A bit hackish, but should be ok
-# as long as we do not have any target-specific modules.
-#
-# The meson-based build system currently in development doesn't need
-# unnest-vars and will obsolete this workaround.
-#
-common-obj-m :=
-
 include $(SRC_PATH)/Makefile.objs
 dummy := $(call fix-paths,../,, \
               authz-obj-y \
diff --git a/hw/9pfs/Kconfig b/hw/9pfs/Kconfig
index 3ae574966136522470c2c5e7004f588a190c1e00..d3ebd7373014c5ce986cbe8de3e22630c52bae48 100644
--- a/hw/9pfs/Kconfig
+++ b/hw/9pfs/Kconfig
@@ -2,8 +2,12 @@ config FSDEV_9P
     bool
     depends on VIRTFS
 
+config 9PFS
+    bool
+
 config VIRTIO_9P
     bool
     default y
     depends on VIRTFS && VIRTIO
     select FSDEV_9P
+    select 9PFS
diff --git a/hw/9pfs/Makefile.objs b/hw/9pfs/Makefile.objs
deleted file mode 100644
index 70ded6fd8fbf4e490124e00321544a614674e8fb..0000000000000000000000000000000000000000
--- a/hw/9pfs/Makefile.objs
+++ /dev/null
@@ -1,9 +0,0 @@
-common-obj-y  = 9p.o 9p-util.o
-common-obj-y += 9p-local.o 9p-xattr.o
-common-obj-y += 9p-xattr-user.o 9p-posix-acl.o
-common-obj-y += coth.o cofs.o codir.o cofile.o
-common-obj-y += coxattr.o 9p-synth.o
-common-obj-y += 9p-proxy.o
-
-common-obj-$(CONFIG_XEN) += xen-9p-backend.o
-obj-$(CONFIG_VIRTIO_9P) += virtio-9p-device.o
diff --git a/hw/9pfs/meson.build b/hw/9pfs/meson.build
new file mode 100644
index 0000000000000000000000000000000000000000..cc09426212230d2ecb22cf0b7945296ce3ef475e
--- /dev/null
+++ b/hw/9pfs/meson.build
@@ -0,0 +1,20 @@
+fs_ss = ss.source_set()
+fs_ss.add(files(
+  '9p-local.c',
+  '9p-posix-acl.c',
+  '9p-proxy.c',
+  '9p-synth.c',
+  '9p-util.c',
+  '9p-xattr-user.c',
+  '9p-xattr.c',
+  '9p.c',
+  'codir.c',
+  'cofile.c',
+  'cofs.c',
+  'coth.c',
+  'coxattr.c',
+))
+fs_ss.add(when: 'CONFIG_XEN', if_true: files('xen-9p-backend.c'))
+softmmu_ss.add_all(when: 'CONFIG_9PFS', if_true: fs_ss)
+
+specific_ss.add(when: 'CONFIG_VIRTIO_9P', if_true: files('virtio-9p-device.c'))
diff --git a/hw/Makefile.objs b/hw/Makefile.objs
deleted file mode 100644
index ebae00af6e74f75d94352062b91f0aad98e687e7..0000000000000000000000000000000000000000
--- a/hw/Makefile.objs
+++ /dev/null
@@ -1,8 +0,0 @@
-ifeq ($(CONFIG_SOFTMMU), y)
-devices-dirs-$(call lor,$(CONFIG_VIRTIO_9P),$(call land,$(CONFIG_VIRTFS),$(CONFIG_XEN))) += 9pfs/
-endif
-
-common-obj-y += $(devices-dirs-y)
-common-obj-m += display/
-common-obj-m += usb/
-obj-y += $(devices-dirs-y)
diff --git a/hw/meson.build b/hw/meson.build
index 55ca2b2b61c948a59354f0aa77f0bb23f73173e0..ba8763cad898f43aee6db470a61400da655c47ad 100644
--- a/hw/meson.build
+++ b/hw/meson.build
@@ -1,3 +1,4 @@
+subdir('9pfs')
 subdir('acpi')
 subdir('adc')
 subdir('audio')