Add a new key to Cargo.toml, package.prelude (optionally, lib.prelude and bin.prelude, but matching edition and only setting in one place makes sense). The default value when unset is v1, which retains today's prelude import of ::[std|core]::prelude::v1::*. It can be set to "any" other identifier string, which will make the prelude instead be ::[std|core]::prelude::{package.prelude}::*.
Alternatively, make package.prelude an array of anchored paths (the default being ["::[std|core]::prelude::v1"]), all of which are imported as prelude for the package. This would give full custom prelude powers.
Implementation: rustc grows a new flag to pass the used prelude path(s), rather than always using ::[std|core]::prelude::v1. Some magic identifier is used to select std/core.
Tying the prelude to the edition would also make sense, but if we were going to do that then it should probably have been done when the 2018 edition was published. We could still make std::prelude::e2015/e2018 aliases for v1 and add e2021 if we wanted to tie default prelude to the edition with this scheme, however.