* Re: [BUG] sched/cache: "Make LLC id continuous" causes NULL cpumask
From: Shrikanth Hegde @ 2026-05-27 18:07 UTC (permalink / raw)
To: Chen, Yu C, kprateek.nayak
Cc: srikar, venkat88, maddy, riteshh, chleroy, tim.c.chen, peterz,
linux-kernel, linuxppc-dev, linux-sched
In-Reply-To: <912676bc-230e-410f-a5fe-153b0f304aee@intel.com>
Hi Chen, Prateek.
On 5/27/26 9:35 PM, Chen, Yu C wrote:
> Hi Shrikanth,
>
> On 5/27/2026 3:01 PM, Shrikanth Hegde wrote:
>> Hi Chen, Prateek.
>>
>> I got back to work today, sorry for delay.
>> I am trying to go through the mails.
>> Apologies in case i have missed any bits.
>>
>
> Thanks for taking a look at this!
>
>> On 5/26/26 7:38 PM, Chen Yu wrote:
>>> Hi Prateek,
>>>
>>> On Tue, 26 May 2026 11:23:59 +0530, K Prateek Nayak
>>> <kprateek.nayak@amd.com> wrote:
>>>> Hello Srikar,
>>>>
>>>> On 5/26/2026 10:28 AM, Srikar Dronamraju wrote:
>>>>> L2 Cache reported here is for SMT8 Core aka CACHE domain.
>>>>
>>>> Apart for the scheduler, nothing in tree currently cares about
>>>> cpu_coregroup_mask() except for drivers/base/arch_topology.c but
>>>> Power doesn't select GENERIC_ARCH_TOPOLOGY.
>>>>
>>>> Why can't Power have an internal mask for MC domain (tl_mc_mask) and
>>>> the scheduler can use cpu_coregroup_mask() for the actual LLc? (The L2
>>>> mask in this case.)
>>
>> This seems wrong. there is no notion that coregroup_mask
>> (MC domain) has to point at LLC domain.
>>
>> For example, on Shared LPAR, there is no MC domain and LLC is at SMT
>> core level.
>> In that case coregroup_mask has point at SMT mask is wrong.
>>
>
> On Shared LPAR, highest_flag_domain(SD_SHARE_LLC) selected the
> SMT domain(L2 shared)prior to commit b5ea300a17e3.
> Prateek suggested changing cpu_coregroup_mask() to use
> cpu_l2_cache_mask(), which makes the LLC mask cover the same range.
> sd_llc, size and grouping remain unchanged. Only sd_llc_id becomes
> contiguous, which aligns with the intent of this commit.
>
> But yes, the naming is confusing. cpu_coregroup_mask suggests a
> "group of cores", but after the change, it only covers threads
> within a single SMT core.
>
Yes. Though it might achieve the same effect, keeping it explicit may
help in maintaining it better.
On PowerPC, there are these subtleties of Shared LPAR where MC domain
per se doesn't exit etc, and on power9 and earlier has different
topologies. Maybe one could figure it all out and simplifies them into
a few masks. But yes, it is slightly different as of today.
>> If we need a mask to point to the LLC mask which arch has to return,
>> then we would
>> need a new api say cpu_llc_mask ? that can point accordingly.
>>
>
> Do you mean something like this?
> https://lore.kernel.org/lkml/8d14c844-b4a8-4af6-
> acab-2cfdd42225be@intel.com/
Yes. This is something i prefer, but not to make it point to l2 mask
always. See below diff which now make it boot on Shared Processor LPAR
where i see panic without it.
Need to add proper comments where appropriate.
diff --git a/arch/powerpc/include/asm/topology.h
b/arch/powerpc/include/asm/topology.h
index 66ed5fe1b718..bd1db3b1dbb0 100644
--- a/arch/powerpc/include/asm/topology.h
+++ b/arch/powerpc/include/asm/topology.h
@@ -131,6 +131,9 @@ static inline int cpu_to_coregroup_id(int cpu)
#ifdef CONFIG_SMP
#include <asm/cputable.h>
+const struct cpumask *arch_llc_mask(int cpu);
+#define arch_llc_mask arch_llc_mask
+
struct cpumask *cpu_coregroup_mask(int cpu);
const struct cpumask *cpu_die_mask(int cpu);
int cpu_die_id(int cpu);
diff --git a/arch/powerpc/kernel/smp.c b/arch/powerpc/kernel/smp.c
index 3467f86fd78f..26c15c786c55 100644
--- a/arch/powerpc/kernel/smp.c
+++ b/arch/powerpc/kernel/smp.c
@@ -1101,6 +1101,13 @@ const struct cpumask *cpu_die_mask(int cpu)
}
EXPORT_SYMBOL_GPL(cpu_die_mask);
+const struct cpumask *arch_llc_mask(int cpu)
+{
+ if (has_coregroup_support())
+ return cpu_coregroup_mask(cpu);
+ return cpu_smallcore_mask(cpu);
+}
+
int cpu_die_id(int cpu)
{
if (has_coregroup_support())
diff --git a/kernel/sched/topology.c b/kernel/sched/topology.c
index df2ceb54c970..3b5155121276 100644
--- a/kernel/sched/topology.c
+++ b/kernel/sched/topology.c
@@ -2063,7 +2063,11 @@ const struct cpumask *tl_mc_mask(struct
sched_domain_topology_level *tl, int cpu
return cpu_coregroup_mask(cpu);
}
+#ifndef arch_llc_mask
#define llc_mask(cpu) cpu_coregroup_mask(cpu)
+#else
+#define llc_mask(cpu) arch_llc_mask(cpu)
+#endif
#else
#define llc_mask(cpu) cpumask_of(cpu)
(One more subtlety; crash would be seen only with NR_CPUS=8192 as
CPUMASK_OFFSTACK=y, but that's a different concern altogether.)
>
>> I don't like mixing MC domain and LLC into one bit.
>>
>
> [ ... ]
>
>>> struct cpumask *cpu_coregroup_mask(int cpu)
>>> {
>>> - return per_cpu(cpu_coregroup_map, cpu);
>>> + return cpu_l2_cache_mask(cpu);
>>> +}
>>
>> This looks wrong to me too. In different hardware topologies
>> there maybe distinction between coregroup and l2 mask.
>>
>> Let me go through the code and see if there is better way.
>>
>
> Sure, please go ahead - I'm on board with the direction
> you settle on.
>
> thanks,
> Chenyu
>
^ permalink raw reply related
* Re: [PATCH v2] KVM: PPC: Kconfig: Enable CONFIG_VPA_PMU with KVM
From: Sean Christopherson @ 2026-05-27 17:40 UTC (permalink / raw)
To: Gautam Menghani
Cc: maddy, npiggin, mpe, chleroy, atrajeev, linuxppc-dev, kvm,
linux-kernel, stable
In-Reply-To: <ahcY4S7shzG_kDt6@Gautams-MacBook-Pro.local>
On Wed, May 27, 2026, Gautam Menghani wrote:
> On Mon, May 18, 2026 at 06:01:04PM -0700, Sean Christopherson wrote:
> > On Mon, May 18, 2026, Gautam Menghani wrote:
> > > Enable CONFIG_VPA_PMU with KVM to enable its usage. Currently, the
> > > vpa-pmu driver cannot be used since it is not enabled in distro configs.
> >
> > That seems like a problem to take up with distros, no?
>
> Rather than enabling individually for different distros, wouldn't it be
> better if it is enabled with KVM automatically? I can rephrase the
> commit log to emphasize that this config option is only relevant for
> KVM (similar to CONFIG_KVM_BOOK3S_HV_PMU).
Not if you can't turn it off. As proposed, CONFIG_VPA_PMU gets forced to
whatever CONFIG_KVM_BOOK3S_64_HV is set to. At that point, the existence of
the VPA_PMU Kconfig is pointless.
If you want it enabled by _default_, then turn it on by default, e.g.
diff --git arch/powerpc/platforms/pseries/Kconfig arch/powerpc/platforms/pseries/Kconfig
index f7052b131a4c..74910ce3a541 100644
--- arch/powerpc/platforms/pseries/Kconfig
+++ arch/powerpc/platforms/pseries/Kconfig
@@ -154,6 +154,7 @@ config HV_PERF_CTRS
config VPA_PMU
tristate "VPA PMU events"
depends on KVM_BOOK3S_64_HV && HV_PERF_CTRS
+ default m
help
Enable access to the VPA PMU counters via perf. This enables
code that support measurement for KVM on PowerVM(KoP) feature.
^ permalink raw reply related
* Re: [PATCH] ibmvnic: fix krealloc() memory leak
From: Alexander A. Klimov @ 2026-05-27 17:22 UTC (permalink / raw)
To: Nicolai Buchwitz
Cc: Haren Myneni, Rick Lindsley, Nick Child, Madhavan Srinivasan,
Michael Ellerman, Nicholas Piggin, Christophe Leroy (CS GROUP),
Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Thomas Falcon, Desnes Augusto Nunes do Rosario,
netdev, linuxppc-dev, linux-kernel
In-Reply-To: <fc74cefb2e0167514f1c3c0c3d6133ec@tipi-net.de>
On 5/26/26 22:50, Nicolai Buchwitz wrote:
> Hi Alex
>
> You patch is missing the prefix with the target tree. Please have
> a look at [1] for more details on the workflow.
Damn. :-)
So far I've always got away with whatever I copied from the majority of
git log --oneline --follow FILE
>
> On 26.5.2026 20:41, Alexander A. Klimov wrote:
>> Don't just overwrite the original pointer passed to krealloc()
>> with its return value without checking latter:
>>
>> MEM = krealloc(MEM, SZ, GFP);
>>
>> If krealloc() returns NULL, that erases the pointer
>> to the still allocated memory, hence leaks this memory.
>> Instead, use a temporary variable, check it's not NULL
>> and only then assign it to the original pointer:
>>
>> TMP = krealloc(MEM, SZ, GFP);
>> if (!TMP) return;
>> MEM = TMP;
>>
>> Fixes: 4e6759be28e4 ("ibmvnic: Feature implementation of Vital Product Data (VPD) for the ibmvnic driver")
>> Signed-off-by: Alexander A. Klimov <grandmaster@al2klimov.de>
>> ---
>> drivers/net/ethernet/ibm/ibmvnic.c | 15 ++++++++-------
>> 1 file changed, 8 insertions(+), 7 deletions(-)
>>
>> diff --git a/drivers/net/ethernet/ibm/ibmvnic.c b/drivers/net/ethernet/ibm/ibmvnic.c
>> index 5a510eed335e..25d1d844ad19 100644
>> --- a/drivers/net/ethernet/ibm/ibmvnic.c
>> +++ b/drivers/net/ethernet/ibm/ibmvnic.c
>> @@ -1761,8 +1761,9 @@ static int ibmvnic_get_vpd(struct ibmvnic_adapter *adapter)
>> union ibmvnic_crq crq;
>> int len = 0;
>> int rc;
>> + unsigned char *buff = adapter->vpd->buff;
>
> Should be reverse x-mas tree (longest to shortest).
>
>>
>> - if (adapter->vpd->buff)
>> + if (buff)
>> len = adapter->vpd->len;
>>
>> mutex_lock(&adapter->fw_lock);
>> @@ -1788,17 +1789,17 @@ static int ibmvnic_get_vpd(struct ibmvnic_adapter *adapter)
>> if (!adapter->vpd->len)
>> return -ENODATA;
>>
>> - if (!adapter->vpd->buff)
>> - adapter->vpd->buff = kzalloc(adapter->vpd->len, GFP_KERNEL);
>> + if (!buff)
>> + buff = kzalloc(adapter->vpd->len, GFP_KERNEL);
>> else if (adapter->vpd->len != len)
>> - adapter->vpd->buff =
>> - krealloc(adapter->vpd->buff,
>> - adapter->vpd->len, GFP_KERNEL);
>> + buff = krealloc(buff,
>> + adapter->vpd->len, GFP_KERNEL);
>
> Dead branch? The only caller, init_resources(), kzalloc()s a fresh vpd
> right before, and resets run release_vpd_data() first, so vpd->buff is
> always NULL here and kzalloc() above always wins. The leak can't trigger,
Cool! No leak = no problem = nothing to do here
^ permalink raw reply
* Re: [BUG] sched/cache: "Make LLC id continuous" causes NULL cpumask
From: K Prateek Nayak @ 2026-05-27 16:30 UTC (permalink / raw)
To: Shrikanth Hegde, Chen Yu
Cc: srikar, venkat88, maddy, riteshh, chleroy, tim.c.chen, peterz,
linux-kernel, linuxppc-dev, linux-sched
In-Reply-To: <d45877d5-86f1-4581-b3e0-59ceb722de3a@linux.ibm.com>
Hello Shrikanth,
On 5/27/2026 12:31 PM, Shrikanth Hegde wrote:
> Hi Chen, Prateek.
>
> I got back to work today, sorry for delay.
> I am trying to go through the mails.
> Apologies in case i have missed any bits.
>
> On 5/26/26 7:38 PM, Chen Yu wrote:
>> Hi Prateek,
>>
>> On Tue, 26 May 2026 11:23:59 +0530, K Prateek Nayak <kprateek.nayak@amd.com> wrote:
>>> Hello Srikar,
>>>
>>> On 5/26/2026 10:28 AM, Srikar Dronamraju wrote:
>>>> L2 Cache reported here is for SMT8 Core aka CACHE domain.
>>>
>>> Apart for the scheduler, nothing in tree currently cares about
>>> cpu_coregroup_mask() except for drivers/base/arch_topology.c but
>>> Power doesn't select GENERIC_ARCH_TOPOLOGY.
>>>
>>> Why can't Power have an internal mask for MC domain (tl_mc_mask) and
>>> the scheduler can use cpu_coregroup_mask() for the actual LLc? (The L2
>>> mask in this case.)
>
> This seems wrong. there is no notion that coregroup_mask
> (MC domain) has to point at LLC domain.
It seems that only PowerPC is special at that. Only 3 architectures
override the default topology via set_sched_topology() - x86, and
s390 both still have SD_SHARE_LLC set for their MC domain, as is the
case with default_topology[] in topology.c with cpu_core_flags().
> For example, on Shared LPAR, there is no MC domain and LLC is at SMT core level.
> In that case coregroup_mask has point at SMT mask is wrong.
That is equivalent of MC degenerating onto the core domain right?
cpu_coregroup_mask() pointing to a core shouldn't be problematic
in that case.
> If we need a mask to point to the LLC mask which arch has to return, then we would
> need a new api say cpu_llc_mask ? that can point accordingly.
>
> I don't like mixing MC domain and LLC into one bit.
The SCHED_CACHE bits assume cpu_coregroup_mask() points to the sd_llc
domain and uses the sched_cpu_activate() path to assign the llc_id
independent of partitions and sched domain bits.
That assumption holds true for everything except powerpc. Is there
anything aside from the scheduler bits that use the
cpu_coregroup_mask()? We can always keep a big fat comment on top that
reads it points to the sd_llc domain and it may not be the MC domain
on power.
>> I suppose what you suggested looks like below:
Hello Chenyu! Yes, this was pretty much what I had in mind! Thank you
for the patch.
>>
>> powerpc/smp: make cpu_coregroup_mask() return the LLC
>>
>> On pSeries shared LPARs(or coregroup_enabled is false on
>> Power9 and earlier) the hemisphere map is not allocated, so
>> build_sched_domains() dereferences a NULL cpumask and crashes.
>>
>> The generic scheduler expects cpu_coregroup_mask() to span the LLC.
>> On powerpc the LLC is the L2. Return cpu_l2_cache_mask() instead of
>> the hemisphere map. Use a coregroup_map() helper for the in-file
>> hemisphere users, and a powerpc_tl_mc_mask() wrapper for the MC
>> sched-domain level.
>>
>> Fixes: b5ea300a17e3 ("sched/cache: Make LLC id continuous")
>> Reported-by: Venkat Rao Bagalkote <venkat88@linux.ibm.com>
>> Suggested-by: K Prateek Nayak <kprateek.nayak@amd.com>
>> ---
>> arch/powerpc/kernel/smp.c | 35 +++++++++++++++++++++++------------
>> 1 file changed, 23 insertions(+), 12 deletions(-)
>>
>> diff --git a/arch/powerpc/kernel/smp.c b/arch/powerpc/kernel/smp.c
>> --- a/arch/powerpc/kernel/smp.c
>> +++ b/arch/powerpc/kernel/smp.c
>> @@ -1040,11 +1040,22 @@ static const struct cpumask *tl_smallcore_smt_mask(struct sched_domain_topology_
>> }
>> #endif
>> +static inline struct cpumask *coregroup_map(int cpu)
>> +{
>> + return per_cpu(cpu_coregroup_map, cpu);
>> +}
>> +
>> struct cpumask *cpu_coregroup_mask(int cpu)
>> {
>> - return per_cpu(cpu_coregroup_map, cpu);
>> + return cpu_l2_cache_mask(cpu);
>> +}
>
> This looks wrong to me too. In different hardware topologies
> there maybe distinction between coregroup and l2 mask.
>
> Let me go through the code and see if there is better way.
The other option was to add an arch_llc_mask macro that can be
optionally defined on the arch/ side if the cpu_coregroup_mask()
doesn't point to the LLC
https://lore.kernel.org/lkml/8d14c844-b4a8-4af6-acab-2cfdd42225be@intel.com/
--
Thanks and Regards,
Prateek
^ permalink raw reply
* Re: [PATCH v2] KVM: PPC: Kconfig: Enable CONFIG_VPA_PMU with KVM
From: Gautam Menghani @ 2026-05-27 16:16 UTC (permalink / raw)
To: Sean Christopherson
Cc: maddy, npiggin, mpe, chleroy, atrajeev, linuxppc-dev, kvm,
linux-kernel, stable
In-Reply-To: <agu2UAi6lWclxFYh@google.com>
On Mon, May 18, 2026 at 06:01:04PM -0700, Sean Christopherson wrote:
> On Mon, May 18, 2026, Gautam Menghani wrote:
> > Enable CONFIG_VPA_PMU with KVM to enable its usage. Currently, the
> > vpa-pmu driver cannot be used since it is not enabled in distro configs.
>
> That seems like a problem to take up with distros, no?
Rather than enabling individually for different distros, wouldn't it be
better if it is enabled with KVM automatically? I can rephrase the
commit log to emphasize that this config option is only relevant for
KVM (similar to CONFIG_KVM_BOOK3S_HV_PMU).
Thanks,
Gautam
^ permalink raw reply
* Re: [PATCH] powerpc: Export set_memory_encrypted and set_memory_decrypted
From: Jason Gunthorpe @ 2026-05-27 16:07 UTC (permalink / raw)
To: Christoph Hellwig
Cc: T.J. Mercier, maddy, mpe, npiggin, chleroy, linuxppc-dev, mripard,
sumit.semwal, lkp, linux-kernel, iommu, linux-mm, agordeev,
gerald.schaefer, linux-s390, Dan Williams, Tom Lendacky, x86
In-Reply-To: <ahPqbfH54R3JJyaV@infradead.org>
On Sun, May 24, 2026 at 11:21:33PM -0700, Christoph Hellwig wrote:
> On Fri, May 22, 2026 at 03:58:53PM -0700, T.J. Mercier wrote:
> > After commit fd55edff8a0a ("dma-buf: heaps: system: Turn the heap into a
> > module") the system dma-buf heaps can be built as a module. The
> > system_cc_shared heap uses set_memory_encrypted and set_memory_decrypted
> > but those functions are not exported on powerpc. This can result in a
> > build error like:
>
> I'd much rather revert the above commit. Yes, x86 has exported these
> since 2017, but that's a really bad idea, and we should fix it instead
> of spreading the export.
>
> Setting memory decrypted is a dangerous operations and should only
> be available to core code. We should have various allocators for
> decrypted code, but not export the functionality to random code.
At the very least an EXPORT_SYMBOL_NS.
Looks like there are about 3 modules using it already..
Jason
^ permalink raw reply
* Re: [BUG] sched/cache: "Make LLC id continuous" causes NULL cpumask
From: Chen, Yu C @ 2026-05-27 16:05 UTC (permalink / raw)
To: Shrikanth Hegde
Cc: srikar, venkat88, maddy, riteshh, chleroy, tim.c.chen, peterz,
linux-kernel, linuxppc-dev, linux-sched, kprateek.nayak
In-Reply-To: <d45877d5-86f1-4581-b3e0-59ceb722de3a@linux.ibm.com>
Hi Shrikanth,
On 5/27/2026 3:01 PM, Shrikanth Hegde wrote:
> Hi Chen, Prateek.
>
> I got back to work today, sorry for delay.
> I am trying to go through the mails.
> Apologies in case i have missed any bits.
>
Thanks for taking a look at this!
> On 5/26/26 7:38 PM, Chen Yu wrote:
>> Hi Prateek,
>>
>> On Tue, 26 May 2026 11:23:59 +0530, K Prateek Nayak
>> <kprateek.nayak@amd.com> wrote:
>>> Hello Srikar,
>>>
>>> On 5/26/2026 10:28 AM, Srikar Dronamraju wrote:
>>>> L2 Cache reported here is for SMT8 Core aka CACHE domain.
>>>
>>> Apart for the scheduler, nothing in tree currently cares about
>>> cpu_coregroup_mask() except for drivers/base/arch_topology.c but
>>> Power doesn't select GENERIC_ARCH_TOPOLOGY.
>>>
>>> Why can't Power have an internal mask for MC domain (tl_mc_mask) and
>>> the scheduler can use cpu_coregroup_mask() for the actual LLc? (The L2
>>> mask in this case.)
>
> This seems wrong. there is no notion that coregroup_mask
> (MC domain) has to point at LLC domain.
>
> For example, on Shared LPAR, there is no MC domain and LLC is at SMT
> core level.
> In that case coregroup_mask has point at SMT mask is wrong.
>
On Shared LPAR, highest_flag_domain(SD_SHARE_LLC) selected the
SMT domain(L2 shared)prior to commit b5ea300a17e3.
Prateek suggested changing cpu_coregroup_mask() to use
cpu_l2_cache_mask(), which makes the LLC mask cover the same range.
sd_llc, size and grouping remain unchanged. Only sd_llc_id becomes
contiguous, which aligns with the intent of this commit.
But yes, the naming is confusing. cpu_coregroup_mask suggests a
"group of cores", but after the change, it only covers threads
within a single SMT core.
> If we need a mask to point to the LLC mask which arch has to return,
> then we would
> need a new api say cpu_llc_mask ? that can point accordingly.
>
Do you mean something like this?
https://lore.kernel.org/lkml/8d14c844-b4a8-4af6-acab-2cfdd42225be@intel.com/
> I don't like mixing MC domain and LLC into one bit.
>
[ ... ]
>> struct cpumask *cpu_coregroup_mask(int cpu)
>> {
>> - return per_cpu(cpu_coregroup_map, cpu);
>> + return cpu_l2_cache_mask(cpu);
>> +}
>
> This looks wrong to me too. In different hardware topologies
> there maybe distinction between coregroup and l2 mask.
>
> Let me go through the code and see if there is better way.
>
Sure, please go ahead - I'm on board with the direction
you settle on.
thanks,
Chenyu
^ permalink raw reply
* Re: (subset) [PATCH 01/23] mfd: tps6586x: fix OF node refcount
From: Lee Jones @ 2026-05-27 15:36 UTC (permalink / raw)
To: Lee Jones, Mark Brown, Thierry Reding, Sebastian Hesselbarth,
Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Srinivas Kandagatla, Greg Kroah-Hartman, Vinod Koul,
Rafael J. Wysocki, Danilo Krummrich, Rob Herring, Saravana Kannan,
Madhavan Srinivasan, Michael Ellerman, Nicholas Piggin,
Christophe Leroy (CS GROUP), Andi Shyti, Andy Shevchenko,
Joerg Roedel, Will Deacon, Robin Murphy, Doug Berger,
Florian Fainelli, Broadcom internal kernel review list,
Ulf Hansson, Frank Li, Sascha Hauer, Pengutronix Kernel Team,
Fabio Estevam, Matthew Brost, Thomas Hellström, Rodrigo Vivi,
David Airlie, Simona Vetter, Peter Chen, Paul Cercueil, Bin Liu,
Philipp Zabel, Maximilian Luz, Hans de Goede, Ilpo Järvinen,
Krzysztof Kozlowski, Benjamin Herrenschmidt, Bartosz Golaszewski
Cc: brgl, linux-kernel, netdev, linux-arm-msm, linux-sound,
driver-core, devicetree, linuxppc-dev, linux-i2c, iommu, linux-pm,
imx, linux-arm-kernel, intel-xe, dri-devel, linux-usb, linux-mips,
platform-driver-x86, stable
In-Reply-To: <20260521-pdev-fwnode-ref-v1-1-88c324a1b8d2@oss.qualcomm.com>
On Thu, 21 May 2026 10:36:24 +0200, Bartosz Golaszewski wrote:
> Platform devices created with platform_device_alloc() call
> platform_device_release() when the last reference to the device's
> kobject is dropped. This function calls of_node_put() unconditionally.
> This works fine for devices created with platform_device_register_full()
> but users of the split approach (platform_device_alloc() +
> platform_device_add()) must bump the reference of the of_node they
> assign manually. Add the missing call to of_node_get().
>
> [...]
Applied, thanks!
[01/23] mfd: tps6586x: fix OF node refcount
commit: 60a28e85ba5c0707b743857a3304107f2f9d0482
--
Lee Jones [李琼斯]
^ permalink raw reply
* Re: [PATCH 15/23] mfd: tps6586: use platform_device_set_of_node()
From: Lee Jones @ 2026-05-27 15:31 UTC (permalink / raw)
To: Bartosz Golaszewski
Cc: Mark Brown, Thierry Reding, Sebastian Hesselbarth, Andrew Lunn,
David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Srinivas Kandagatla, Greg Kroah-Hartman, Vinod Koul,
Rafael J. Wysocki, Danilo Krummrich, Rob Herring, Saravana Kannan,
Madhavan Srinivasan, Michael Ellerman, Nicholas Piggin,
Christophe Leroy (CS GROUP), Andi Shyti, Andy Shevchenko,
Joerg Roedel, Will Deacon, Robin Murphy, Doug Berger,
Florian Fainelli, Broadcom internal kernel review list,
Ulf Hansson, Frank Li, Sascha Hauer, Pengutronix Kernel Team,
Fabio Estevam, Matthew Brost, Thomas Hellström, Rodrigo Vivi,
David Airlie, Simona Vetter, Peter Chen, Paul Cercueil, Bin Liu,
Philipp Zabel, Maximilian Luz, Hans de Goede, Ilpo Järvinen,
Krzysztof Kozlowski, Benjamin Herrenschmidt, brgl, linux-kernel,
netdev, linux-arm-msm, linux-sound, driver-core, devicetree,
linuxppc-dev, linux-i2c, iommu, linux-pm, imx, linux-arm-kernel,
intel-xe, dri-devel, linux-usb, linux-mips, platform-driver-x86
In-Reply-To: <20260521-pdev-fwnode-ref-v1-15-88c324a1b8d2@oss.qualcomm.com>
On Thu, 21 May 2026, Bartosz Golaszewski wrote:
> Ahead of reworking the reference counting logic for platform devices,
> encapsulate the assignment of the OF node for dynamically allocated
> platform devices with the provided helper.
>
> Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
Acked-by: Lee Jones <lee@kernel.org>
> ---
> drivers/mfd/tps6586x.c | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/drivers/mfd/tps6586x.c b/drivers/mfd/tps6586x.c
> index f5f805446603315ba76ce1fc501c908f1cec0d16..3cfd2f02b62f3cc370e0d970ec2643b638fd0fec 100644
> --- a/drivers/mfd/tps6586x.c
> +++ b/drivers/mfd/tps6586x.c
> @@ -397,7 +397,7 @@ static int tps6586x_add_subdevs(struct tps6586x *tps6586x,
>
> pdev->dev.parent = tps6586x->dev;
> pdev->dev.platform_data = subdev->platform_data;
> - pdev->dev.of_node = of_node_get(subdev->of_node);
> + platform_device_set_of_node(pdev, subdev->of_node);
>
> ret = platform_device_add(pdev);
> if (ret) {
>
> --
> 2.47.3
>
--
Lee Jones
^ permalink raw reply
* [PATCH v3 3/4] fbdev: Wrap fbcon updates from vga-switcheroo in helper
From: Thomas Zimmermann @ 2026-05-27 15:14 UTC (permalink / raw)
To: deller, geert, simona, airlied, lukas, maddy, mpe, npiggin,
chleroy
Cc: dri-devel, linux-fbdev, linuxppc-dev, Thomas Zimmermann
In-Reply-To: <20260527151551.258659-1-tzimmermann@suse.de>
Handle console remapping in fbcon in fb_switch_output(). Vga-switcheroo
invokes this functionality before switching physical outputs to a new
graphics device. Open-coding fbcon state in vga-switcheroo exposed fbdev
implementation details.
Vga-switcheroo is used for switching physical outputs among graphics
hardware. This functionality is only supported by DRM drivers. A later
update will further move fb_switch_output() into DRM's fbdev emulation;
thus fully decoupling vga-switcheroo from fbdev.
v3:
- remove Kconfig dependency related to fbcon (Geert)
v2:
- use '#if defined' (Helge)
Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
---
drivers/gpu/vga/Kconfig | 2 +-
drivers/gpu/vga/vga_switcheroo.c | 6 +++---
drivers/video/fbdev/core/fbmem.c | 10 ++++++++++
include/linux/fb.h | 1 +
4 files changed, 15 insertions(+), 4 deletions(-)
diff --git a/drivers/gpu/vga/Kconfig b/drivers/gpu/vga/Kconfig
index eb8b14ab22c3..bad4bcee313f 100644
--- a/drivers/gpu/vga/Kconfig
+++ b/drivers/gpu/vga/Kconfig
@@ -4,7 +4,7 @@ config VGA_SWITCHEROO
depends on X86
depends on ACPI
depends on PCI
- depends on (FRAMEBUFFER_CONSOLE=n || FB=y)
+ depends on FB=y
select VGA_ARB
help
Many laptops released in 2008/9/10 have two GPUs with a multiplexer
diff --git a/drivers/gpu/vga/vga_switcheroo.c b/drivers/gpu/vga/vga_switcheroo.c
index 8fe1ae3c71bb..22cf52b78b75 100644
--- a/drivers/gpu/vga/vga_switcheroo.c
+++ b/drivers/gpu/vga/vga_switcheroo.c
@@ -31,11 +31,9 @@
#define pr_fmt(fmt) "vga_switcheroo: " fmt
#include <linux/apple-gmux.h>
-#include <linux/console.h>
#include <linux/debugfs.h>
#include <linux/fb.h>
#include <linux/fs.h>
-#include <linux/fbcon.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/pm_domain.h>
@@ -735,8 +733,10 @@ static int vga_switchto_stage2(struct vga_switcheroo_client *new_client)
if (!active->driver_power_control)
set_audio_state(active->id, VGA_SWITCHEROO_OFF);
+#if defined(CONFIG_FB)
if (new_client->fb_info)
- fbcon_remap_all(new_client->fb_info);
+ fb_switch_outputs(new_client->fb_info);
+#endif
mutex_lock(&vgasr_priv.mux_hw_lock);
ret = vgasr_priv.handler->switchto(new_client->id);
diff --git a/drivers/video/fbdev/core/fbmem.c b/drivers/video/fbdev/core/fbmem.c
index 1a6758653b64..ecadbc58abff 100644
--- a/drivers/video/fbdev/core/fbmem.c
+++ b/drivers/video/fbdev/core/fbmem.c
@@ -684,6 +684,16 @@ void fb_set_suspend(struct fb_info *info, int state)
}
EXPORT_SYMBOL(fb_set_suspend);
+/**
+ * fb_switch_outputs - framebuffer got the outputs from vga-switcheroo
+ * @info: framebuffer
+ */
+void fb_switch_outputs(struct fb_info *info)
+{
+ fbcon_remap_all(info);
+}
+EXPORT_SYMBOL(fb_switch_outputs);
+
static int __init fbmem_init(void)
{
int ret;
diff --git a/include/linux/fb.h b/include/linux/fb.h
index 88680a7cabd5..e9a26e82322a 100644
--- a/include/linux/fb.h
+++ b/include/linux/fb.h
@@ -608,6 +608,7 @@ void fb_pad_unaligned_buffer(u8 *dst, u32 d_pitch, const u8 *src, u32 idx, u32 h
u32 shift_high, u32 shift_low, u32 mod);
void fb_pad_aligned_buffer(u8 *dst, u32 d_pitch, const u8 *src, u32 s_pitch, u32 height);
extern void fb_set_suspend(struct fb_info *info, int state);
+extern void fb_switch_outputs(struct fb_info *info);
extern int fb_get_color_depth(struct fb_var_screeninfo *var,
struct fb_fix_screeninfo *fix);
extern int fb_get_options(const char *name, char **option);
--
2.54.0
^ permalink raw reply related
* [PATCH v3 4/4] fbdev: Do not export fbcon from fbdev
From: Thomas Zimmermann @ 2026-05-27 15:14 UTC (permalink / raw)
To: deller, geert, simona, airlied, lukas, maddy, mpe, npiggin,
chleroy
Cc: dri-devel, linux-fbdev, linuxppc-dev, Thomas Zimmermann
In-Reply-To: <20260527151551.258659-1-tzimmermann@suse.de>
There are no callers of fbcon outside fbdev. Move the declarations
into the internal header.
Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
---
MAINTAINERS | 1 -
drivers/video/fbdev/core/fb_chrdev.c | 2 +-
drivers/video/fbdev/core/fbcon.c | 1 -
drivers/video/fbdev/core/fbcon.h | 50 +++++++++++++++++++++++++
drivers/video/fbdev/core/fbmem.c | 2 +-
include/linux/fbcon.h | 55 ----------------------------
6 files changed, 52 insertions(+), 59 deletions(-)
delete mode 100644 include/linux/fbcon.h
diff --git a/MAINTAINERS b/MAINTAINERS
index 4ae6919454c3..a795628ff46a 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -10150,7 +10150,6 @@ F: drivers/video/fbdev/core/fbcon_rotate.h
F: drivers/video/fbdev/core/fbcon_ud.c
F: drivers/video/fbdev/core/softcursor.c
F: drivers/video/fbdev/core/tileblit.c
-F: include/linux/fbcon.h
F: include/linux/font.h
F: lib/fonts/
diff --git a/drivers/video/fbdev/core/fb_chrdev.c b/drivers/video/fbdev/core/fb_chrdev.c
index 035e67d2c28f..ba1d0bc214c5 100644
--- a/drivers/video/fbdev/core/fb_chrdev.c
+++ b/drivers/video/fbdev/core/fb_chrdev.c
@@ -3,10 +3,10 @@
#include <linux/compat.h>
#include <linux/console.h>
#include <linux/fb.h>
-#include <linux/fbcon.h>
#include <linux/major.h>
#include "fb_internal.h"
+#include "fbcon.h"
/*
* We hold a reference to the fb_info in file->private_data,
diff --git a/drivers/video/fbdev/core/fbcon.c b/drivers/video/fbdev/core/fbcon.c
index 50b84cd32938..853b52b40d01 100644
--- a/drivers/video/fbdev/core/fbcon.c
+++ b/drivers/video/fbdev/core/fbcon.c
@@ -70,7 +70,6 @@
#include <linux/printk.h>
#include <linux/slab.h>
#include <linux/fb.h>
-#include <linux/fbcon.h>
#include <linux/vt_kern.h>
#include <linux/selection.h>
#include <linux/font.h>
diff --git a/drivers/video/fbdev/core/fbcon.h b/drivers/video/fbdev/core/fbcon.h
index 321cc7f44baa..407d207b14f1 100644
--- a/drivers/video/fbdev/core/fbcon.h
+++ b/drivers/video/fbdev/core/fbcon.h
@@ -11,6 +11,7 @@
#ifndef _VIDEO_FBCON_H
#define _VIDEO_FBCON_H
+#include <linux/compiler_types.h>
#include <linux/font.h>
#include <linux/types.h>
#include <linux/vt_buffer.h>
@@ -19,6 +20,11 @@
#include <asm/io.h>
+struct fb_blit_caps;
+struct fb_info;
+struct fb_var_screeninfo;
+struct fb_videomode;
+
/*
* This is the interface between the low-level console driver and the
* low-level frame buffer device
@@ -233,4 +239,48 @@ static inline int get_attribute(struct fb_info *info, u16 c)
(void) (&_r == &_v); \
(i == FB_ROTATE_UR || i == FB_ROTATE_UD) ? _r : _v; })
+#ifdef CONFIG_FRAMEBUFFER_CONSOLE
+void __init fb_console_init(void);
+void __exit fb_console_exit(void);
+int fbcon_fb_registered(struct fb_info *info);
+void fbcon_fb_unregistered(struct fb_info *info);
+void fbcon_fb_unbind(struct fb_info *info);
+void fbcon_suspended(struct fb_info *info);
+void fbcon_resumed(struct fb_info *info);
+int fbcon_mode_deleted(struct fb_info *info,
+ struct fb_videomode *mode);
+void fbcon_delete_modelist(struct list_head *head);
+void fbcon_new_modelist(struct fb_info *info);
+void fbcon_get_requirement(struct fb_info *info,
+ struct fb_blit_caps *caps);
+void fbcon_fb_blanked(struct fb_info *info, int blank);
+int fbcon_modechange_possible(struct fb_info *info,
+ struct fb_var_screeninfo *var);
+void fbcon_update_vcs(struct fb_info *info, bool all);
+void fbcon_remap_all(struct fb_info *info);
+int fbcon_set_con2fb_map_ioctl(void __user *argp);
+int fbcon_get_con2fb_map_ioctl(void __user *argp);
+#else
+static inline void fb_console_init(void) {}
+static inline void fb_console_exit(void) {}
+static inline int fbcon_fb_registered(struct fb_info *info) { return 0; }
+static inline void fbcon_fb_unregistered(struct fb_info *info) {}
+static inline void fbcon_fb_unbind(struct fb_info *info) {}
+static inline void fbcon_suspended(struct fb_info *info) {}
+static inline void fbcon_resumed(struct fb_info *info) {}
+static inline int fbcon_mode_deleted(struct fb_info *info,
+ struct fb_videomode *mode) { return 0; }
+static inline void fbcon_delete_modelist(struct list_head *head) {}
+static inline void fbcon_new_modelist(struct fb_info *info) {}
+static inline void fbcon_get_requirement(struct fb_info *info,
+ struct fb_blit_caps *caps) {}
+static inline void fbcon_fb_blanked(struct fb_info *info, int blank) {}
+static inline int fbcon_modechange_possible(struct fb_info *info,
+ struct fb_var_screeninfo *var) { return 0; }
+static inline void fbcon_update_vcs(struct fb_info *info, bool all) {}
+static inline void fbcon_remap_all(struct fb_info *info) {}
+static inline int fbcon_set_con2fb_map_ioctl(void __user *argp) { return 0; }
+static inline int fbcon_get_con2fb_map_ioctl(void __user *argp) { return 0; }
+#endif
+
#endif /* _VIDEO_FBCON_H */
diff --git a/drivers/video/fbdev/core/fbmem.c b/drivers/video/fbdev/core/fbmem.c
index ecadbc58abff..e5221653ec2b 100644
--- a/drivers/video/fbdev/core/fbmem.c
+++ b/drivers/video/fbdev/core/fbmem.c
@@ -14,13 +14,13 @@
#include <linux/console.h>
#include <linux/export.h>
#include <linux/fb.h>
-#include <linux/fbcon.h>
#include <linux/lcd.h>
#include <linux/leds.h>
#include <video/nomodeset.h>
#include "fb_internal.h"
+#include "fbcon.h"
/*
* Frame buffer device initialization and setup routines
diff --git a/include/linux/fbcon.h b/include/linux/fbcon.h
deleted file mode 100644
index f206370060e1..000000000000
--- a/include/linux/fbcon.h
+++ /dev/null
@@ -1,55 +0,0 @@
-#ifndef _LINUX_FBCON_H
-#define _LINUX_FBCON_H
-
-#include <linux/compiler_types.h>
-
-struct fb_blit_caps;
-struct fb_info;
-struct fb_var_screeninfo;
-struct fb_videomode;
-
-#ifdef CONFIG_FRAMEBUFFER_CONSOLE
-void __init fb_console_init(void);
-void __exit fb_console_exit(void);
-int fbcon_fb_registered(struct fb_info *info);
-void fbcon_fb_unregistered(struct fb_info *info);
-void fbcon_fb_unbind(struct fb_info *info);
-void fbcon_suspended(struct fb_info *info);
-void fbcon_resumed(struct fb_info *info);
-int fbcon_mode_deleted(struct fb_info *info,
- struct fb_videomode *mode);
-void fbcon_delete_modelist(struct list_head *head);
-void fbcon_new_modelist(struct fb_info *info);
-void fbcon_get_requirement(struct fb_info *info,
- struct fb_blit_caps *caps);
-void fbcon_fb_blanked(struct fb_info *info, int blank);
-int fbcon_modechange_possible(struct fb_info *info,
- struct fb_var_screeninfo *var);
-void fbcon_update_vcs(struct fb_info *info, bool all);
-void fbcon_remap_all(struct fb_info *info);
-int fbcon_set_con2fb_map_ioctl(void __user *argp);
-int fbcon_get_con2fb_map_ioctl(void __user *argp);
-#else
-static inline void fb_console_init(void) {}
-static inline void fb_console_exit(void) {}
-static inline int fbcon_fb_registered(struct fb_info *info) { return 0; }
-static inline void fbcon_fb_unregistered(struct fb_info *info) {}
-static inline void fbcon_fb_unbind(struct fb_info *info) {}
-static inline void fbcon_suspended(struct fb_info *info) {}
-static inline void fbcon_resumed(struct fb_info *info) {}
-static inline int fbcon_mode_deleted(struct fb_info *info,
- struct fb_videomode *mode) { return 0; }
-static inline void fbcon_delete_modelist(struct list_head *head) {}
-static inline void fbcon_new_modelist(struct fb_info *info) {}
-static inline void fbcon_get_requirement(struct fb_info *info,
- struct fb_blit_caps *caps) {}
-static inline void fbcon_fb_blanked(struct fb_info *info, int blank) {}
-static inline int fbcon_modechange_possible(struct fb_info *info,
- struct fb_var_screeninfo *var) { return 0; }
-static inline void fbcon_update_vcs(struct fb_info *info, bool all) {}
-static inline void fbcon_remap_all(struct fb_info *info) {}
-static inline int fbcon_set_con2fb_map_ioctl(void __user *argp) { return 0; }
-static inline int fbcon_get_con2fb_map_ioctl(void __user *argp) { return 0; }
-#endif
-
-#endif /* _LINUX_FBCON_H */
--
2.54.0
^ permalink raw reply related
* [PATCH v3 1/4] fbdev: Wrap user-invoked calls to fb_set_var() in helper
From: Thomas Zimmermann @ 2026-05-27 15:14 UTC (permalink / raw)
To: deller, geert, simona, airlied, lukas, maddy, mpe, npiggin,
chleroy
Cc: dri-devel, linux-fbdev, linuxppc-dev, Thomas Zimmermann
In-Reply-To: <20260527151551.258659-1-tzimmermann@suse.de>
Handle fbcon during display updates in fb_set_var_from_user(). Check
with fbcon if the mode change is possible, update hardware state and
finally update fbcon. Update all callers.
Only the FBIOPUT_VSCREENINFO ioctl currently does all steps. Other
mode-changes callers in sysfs and driver code are missing fbcon-related
steps.
With the new helper, ps3fb and sh_mobile_lcdcfb no longer maintain
fbcon state themselves.
Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
---
drivers/video/fbdev/core/fb_chrdev.c | 6 +-----
drivers/video/fbdev/core/fbcon.c | 2 --
drivers/video/fbdev/core/fbmem.c | 13 +++++++++++++
drivers/video/fbdev/core/fbsysfs.c | 4 +---
drivers/video/fbdev/ps3fb.c | 5 +----
drivers/video/fbdev/sh_mobile_lcdcfb.c | 5 +----
include/linux/fb.h | 2 ++
7 files changed, 19 insertions(+), 18 deletions(-)
diff --git a/drivers/video/fbdev/core/fb_chrdev.c b/drivers/video/fbdev/core/fb_chrdev.c
index 4ebd16b7e3b8..54f926fb411b 100644
--- a/drivers/video/fbdev/core/fb_chrdev.c
+++ b/drivers/video/fbdev/core/fb_chrdev.c
@@ -85,11 +85,7 @@ static long do_fb_ioctl(struct fb_info *info, unsigned int cmd,
var.activate &= ~FB_ACTIVATE_KD_TEXT;
console_lock();
lock_fb_info(info);
- ret = fbcon_modechange_possible(info, &var);
- if (!ret)
- ret = fb_set_var(info, &var);
- if (!ret)
- fbcon_update_vcs(info, var.activate & FB_ACTIVATE_ALL);
+ ret = fb_set_var_from_user(info, &var);
unlock_fb_info(info);
console_unlock();
if (!ret && copy_to_user(argp, &var, sizeof(var)))
diff --git a/drivers/video/fbdev/core/fbcon.c b/drivers/video/fbdev/core/fbcon.c
index b0e3e765360d..50b84cd32938 100644
--- a/drivers/video/fbdev/core/fbcon.c
+++ b/drivers/video/fbdev/core/fbcon.c
@@ -2699,7 +2699,6 @@ void fbcon_update_vcs(struct fb_info *info, bool all)
else
fbcon_modechanged(info);
}
-EXPORT_SYMBOL(fbcon_update_vcs);
/* let fbcon check if it supports a new screen resolution */
int fbcon_modechange_possible(struct fb_info *info, struct fb_var_screeninfo *var)
@@ -2727,7 +2726,6 @@ int fbcon_modechange_possible(struct fb_info *info, struct fb_var_screeninfo *va
return 0;
}
-EXPORT_SYMBOL_GPL(fbcon_modechange_possible);
int fbcon_mode_deleted(struct fb_info *info,
struct fb_videomode *mode)
diff --git a/drivers/video/fbdev/core/fbmem.c b/drivers/video/fbdev/core/fbmem.c
index 30f2b59c47bf..d37a1039e221 100644
--- a/drivers/video/fbdev/core/fbmem.c
+++ b/drivers/video/fbdev/core/fbmem.c
@@ -346,6 +346,19 @@ fb_set_var(struct fb_info *info, struct fb_var_screeninfo *var)
}
EXPORT_SYMBOL(fb_set_var);
+int fb_set_var_from_user(struct fb_info *info, struct fb_var_screeninfo *var)
+{
+ int ret = fbcon_modechange_possible(info, var);
+
+ if (!ret)
+ ret = fb_set_var(info, var);
+ if (!ret)
+ fbcon_update_vcs(info, var->activate & FB_ACTIVATE_ALL);
+
+ return ret;
+}
+EXPORT_SYMBOL(fb_set_var_from_user);
+
static void fb_lcd_notify_blank(struct fb_info *info)
{
int power;
diff --git a/drivers/video/fbdev/core/fbsysfs.c b/drivers/video/fbdev/core/fbsysfs.c
index baa2bae0fb5b..5ece236e6252 100644
--- a/drivers/video/fbdev/core/fbsysfs.c
+++ b/drivers/video/fbdev/core/fbsysfs.c
@@ -19,9 +19,7 @@ static int activate(struct fb_info *fb_info, struct fb_var_screeninfo *var)
var->activate |= FB_ACTIVATE_FORCE;
console_lock();
lock_fb_info(fb_info);
- err = fb_set_var(fb_info, var);
- if (!err)
- fbcon_update_vcs(fb_info, var->activate & FB_ACTIVATE_ALL);
+ err = fb_set_var_from_user(fb_info, var);
unlock_fb_info(fb_info);
console_unlock();
if (err)
diff --git a/drivers/video/fbdev/ps3fb.c b/drivers/video/fbdev/ps3fb.c
index dbcda307f6a6..1376d19b19ae 100644
--- a/drivers/video/fbdev/ps3fb.c
+++ b/drivers/video/fbdev/ps3fb.c
@@ -29,7 +29,6 @@
#include <linux/freezer.h>
#include <linux/uaccess.h>
#include <linux/fb.h>
-#include <linux/fbcon.h>
#include <linux/init.h>
#include <asm/cell-regs.h>
@@ -830,9 +829,7 @@ static int ps3fb_ioctl(struct fb_info *info, unsigned int cmd,
/* Force, in case only special bits changed */
var.activate |= FB_ACTIVATE_FORCE;
par->new_mode_id = val;
- retval = fb_set_var(info, &var);
- if (!retval)
- fbcon_update_vcs(info, var.activate & FB_ACTIVATE_ALL);
+ retval = fb_set_var_from_user(info, &var);
console_unlock();
}
break;
diff --git a/drivers/video/fbdev/sh_mobile_lcdcfb.c b/drivers/video/fbdev/sh_mobile_lcdcfb.c
index 72969fe8e513..e8324b01700f 100644
--- a/drivers/video/fbdev/sh_mobile_lcdcfb.c
+++ b/drivers/video/fbdev/sh_mobile_lcdcfb.c
@@ -15,7 +15,6 @@
#include <linux/ctype.h>
#include <linux/dma-mapping.h>
#include <linux/delay.h>
-#include <linux/fbcon.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/ioctl.h>
@@ -1768,11 +1767,9 @@ static void sh_mobile_fb_reconfig(struct fb_info *info)
var.height = ch->display.height;
var.activate = FB_ACTIVATE_NOW;
- if (fb_set_var(info, &var) < 0)
+ if (fb_set_var_from_user(info, &var) < 0)
/* Couldn't reconfigure, hopefully, can continue as before */
return;
-
- fbcon_update_vcs(info, true);
}
/*
diff --git a/include/linux/fb.h b/include/linux/fb.h
index 5178a33c752c..88680a7cabd5 100644
--- a/include/linux/fb.h
+++ b/include/linux/fb.h
@@ -533,6 +533,8 @@ extern int fb_set_var(struct fb_info *info, struct fb_var_screeninfo *var);
extern int fb_pan_display(struct fb_info *info, struct fb_var_screeninfo *var);
extern int fb_blank(struct fb_info *info, int blank);
+int fb_set_var_from_user(struct fb_info *info, struct fb_var_screeninfo *var);
+
/*
* Helpers for framebuffers in I/O memory
*/
--
2.54.0
^ permalink raw reply related
* [PATCH v3 0/4] fbdev: Internalize fbcon
From: Thomas Zimmermann @ 2026-05-27 15:14 UTC (permalink / raw)
To: deller, geert, simona, airlied, lukas, maddy, mpe, npiggin,
chleroy
Cc: dri-devel, linux-fbdev, linuxppc-dev, Thomas Zimmermann
Turn fbcon into an internal client of fbdev. Manage all interactions
with graphics drivers within fbdev. Add helpers for these tasks and
convert drivers.
Fbdev's PS3 and SH-Mobile drivers update fbcon as part of user-invoked
mode changes. Call the new helpers, which also fix inconsistencies
among the various code paths.
Vga-switcheroo remaps the fbcon terminals when switching physical
outputs. For now, hide this in another helper. The call will later
move into DRM's fbdev emulation.
With all refactoring in place, fbdev manages fbcon interactions by
itself. Remove the public interfaces.
v3:
- resolve vga-switcheroo dependencies wrt fbcon (Geert)
v2:
- use '#if defined' (Helge)
Thomas Zimmermann (4):
fbdev: Wrap user-invoked calls to fb_set_var() in helper
fbdev: Wrap user-invoked calls to fb_blank() in helper
fbdev: Wrap fbcon updates from vga-switcheroo in helper
fbdev: Do not export fbcon from fbdev
MAINTAINERS | 1 -
drivers/gpu/vga/Kconfig | 2 +-
drivers/gpu/vga/vga_switcheroo.c | 6 +--
drivers/video/fbdev/core/fb_chrdev.c | 12 ++----
drivers/video/fbdev/core/fb_internal.h | 1 +
drivers/video/fbdev/core/fbcon.c | 3 --
drivers/video/fbdev/core/fbcon.h | 50 +++++++++++++++++++++++
drivers/video/fbdev/core/fbmem.c | 35 +++++++++++++++-
drivers/video/fbdev/core/fbsysfs.c | 9 +----
drivers/video/fbdev/ps3fb.c | 5 +--
drivers/video/fbdev/sh_mobile_lcdcfb.c | 5 +--
include/linux/fb.h | 3 ++
include/linux/fbcon.h | 55 --------------------------
13 files changed, 99 insertions(+), 88 deletions(-)
delete mode 100644 include/linux/fbcon.h
base-commit: 5fb5a9a63cf5ece68e0eeb6fa397da27712bccf0
--
2.54.0
^ permalink raw reply
* [PATCH v3 2/4] fbdev: Wrap user-invoked calls to fb_blank() in helper
From: Thomas Zimmermann @ 2026-05-27 15:14 UTC (permalink / raw)
To: deller, geert, simona, airlied, lukas, maddy, mpe, npiggin,
chleroy
Cc: dri-devel, linux-fbdev, linuxppc-dev, Thomas Zimmermann
In-Reply-To: <20260527151551.258659-1-tzimmermann@suse.de>
Handle fbcon during blanking in fb_blank_from_user(). First blank the
hardware, then blank fbcon. Same for unblanking. Update all callers and
resolve the duplicated logic.
With the new helper, fbdev's sysfb code no longer maintains fbcon state
by itself.
Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
---
drivers/video/fbdev/core/fb_chrdev.c | 4 +---
drivers/video/fbdev/core/fb_internal.h | 1 +
drivers/video/fbdev/core/fbmem.c | 10 ++++++++++
drivers/video/fbdev/core/fbsysfs.c | 5 +----
4 files changed, 13 insertions(+), 7 deletions(-)
diff --git a/drivers/video/fbdev/core/fb_chrdev.c b/drivers/video/fbdev/core/fb_chrdev.c
index 54f926fb411b..035e67d2c28f 100644
--- a/drivers/video/fbdev/core/fb_chrdev.c
+++ b/drivers/video/fbdev/core/fb_chrdev.c
@@ -138,9 +138,7 @@ static long do_fb_ioctl(struct fb_info *info, unsigned int cmd,
return -EINVAL;
console_lock();
lock_fb_info(info);
- ret = fb_blank(info, arg);
- /* might again call into fb_blank */
- fbcon_fb_blanked(info, arg);
+ ret = fb_blank_from_user(info, arg);
unlock_fb_info(info);
console_unlock();
break;
diff --git a/drivers/video/fbdev/core/fb_internal.h b/drivers/video/fbdev/core/fb_internal.h
index 613832d335fe..62e75bf15b9b 100644
--- a/drivers/video/fbdev/core/fb_internal.h
+++ b/drivers/video/fbdev/core/fb_internal.h
@@ -44,6 +44,7 @@ extern struct fb_info *registered_fb[FB_MAX];
extern int num_registered_fb;
struct fb_info *get_fb_info(unsigned int idx);
void put_fb_info(struct fb_info *fb_info);
+int fb_blank_from_user(struct fb_info *info, int blank);
/* fb_procfs.c */
#if defined(CONFIG_FB_DEVICE)
diff --git a/drivers/video/fbdev/core/fbmem.c b/drivers/video/fbdev/core/fbmem.c
index d37a1039e221..1a6758653b64 100644
--- a/drivers/video/fbdev/core/fbmem.c
+++ b/drivers/video/fbdev/core/fbmem.c
@@ -422,6 +422,16 @@ int fb_blank(struct fb_info *info, int blank)
}
EXPORT_SYMBOL(fb_blank);
+int fb_blank_from_user(struct fb_info *info, int blank)
+{
+ int ret = fb_blank(info, blank);
+
+ /* might again call into fb_blank */
+ fbcon_fb_blanked(info, blank);
+
+ return ret;
+}
+
static int fb_check_foreignness(struct fb_info *fi)
{
const bool foreign_endian = fi->flags & FBINFO_FOREIGN_ENDIAN;
diff --git a/drivers/video/fbdev/core/fbsysfs.c b/drivers/video/fbdev/core/fbsysfs.c
index 5ece236e6252..d9743ef35355 100644
--- a/drivers/video/fbdev/core/fbsysfs.c
+++ b/drivers/video/fbdev/core/fbsysfs.c
@@ -7,7 +7,6 @@
#include <linux/console.h>
#include <linux/fb.h>
-#include <linux/fbcon.h>
#include <linux/major.h>
#include "fb_internal.h"
@@ -229,9 +228,7 @@ static ssize_t store_blank(struct device *device,
arg = simple_strtoul(buf, &last, 0);
console_lock();
- err = fb_blank(fb_info, arg);
- /* might again call into fb_blank */
- fbcon_fb_blanked(fb_info, arg);
+ err = fb_blank_from_user(fb_info, arg);
console_unlock();
if (err < 0)
return err;
--
2.54.0
^ permalink raw reply related
* [PATCH] crypto: powerpc/aes - use min in ppc_{ecb,cbc,ctr,xts}_crypt
From: Thorsten Blum @ 2026-05-27 14:11 UTC (permalink / raw)
To: Herbert Xu, David S. Miller, Madhavan Srinivasan,
Michael Ellerman, Nicholas Piggin, Christophe Leroy (CS GROUP)
Cc: Thorsten Blum, linux-crypto, linuxppc-dev, linux-kernel
Replace min_t() with the simpler min() macro since the values are
unsigned and compatible.
Signed-off-by: Thorsten Blum <thorsten.blum@linux.dev>
---
arch/powerpc/crypto/aes-spe-glue.c | 9 +++++----
1 file changed, 5 insertions(+), 4 deletions(-)
diff --git a/arch/powerpc/crypto/aes-spe-glue.c b/arch/powerpc/crypto/aes-spe-glue.c
index 7d2827e65240..e038488087e6 100644
--- a/arch/powerpc/crypto/aes-spe-glue.c
+++ b/arch/powerpc/crypto/aes-spe-glue.c
@@ -9,6 +9,7 @@
*/
#include <crypto/aes.h>
+#include <linux/minmax.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/types.h>
@@ -140,7 +141,7 @@ static int ppc_ecb_crypt(struct skcipher_request *req, bool enc)
err = skcipher_walk_virt(&walk, req, false);
while ((nbytes = walk.nbytes) != 0) {
- nbytes = min_t(unsigned int, nbytes, MAX_BYTES);
+ nbytes = min(nbytes, MAX_BYTES);
nbytes = round_down(nbytes, AES_BLOCK_SIZE);
spe_begin();
@@ -179,7 +180,7 @@ static int ppc_cbc_crypt(struct skcipher_request *req, bool enc)
err = skcipher_walk_virt(&walk, req, false);
while ((nbytes = walk.nbytes) != 0) {
- nbytes = min_t(unsigned int, nbytes, MAX_BYTES);
+ nbytes = min(nbytes, MAX_BYTES);
nbytes = round_down(nbytes, AES_BLOCK_SIZE);
spe_begin();
@@ -220,7 +221,7 @@ static int ppc_ctr_crypt(struct skcipher_request *req)
err = skcipher_walk_virt(&walk, req, false);
while ((nbytes = walk.nbytes) != 0) {
- nbytes = min_t(unsigned int, nbytes, MAX_BYTES);
+ nbytes = min(nbytes, MAX_BYTES);
if (nbytes < walk.total)
nbytes = round_down(nbytes, AES_BLOCK_SIZE);
@@ -248,7 +249,7 @@ static int ppc_xts_crypt(struct skcipher_request *req, bool enc)
twk = ctx->key_twk;
while ((nbytes = walk.nbytes) != 0) {
- nbytes = min_t(unsigned int, nbytes, MAX_BYTES);
+ nbytes = min(nbytes, MAX_BYTES);
nbytes = round_down(nbytes, AES_BLOCK_SIZE);
spe_begin();
^ permalink raw reply related
* Re: [PATCH v14 6/8] lib/test: memcpy_kunit: add copy_page() and copy_mc_page() tests
From: Shuai Xue @ 2026-05-27 13:43 UTC (permalink / raw)
To: Ruidong Tian, catalin.marinas, will, rafael, tony.luck, guohanjun,
mchehab, tongtiangen, james.morse, robin.murphy, andreyknvl,
dvyukov, vincenzo.frascino, mpe, npiggin, ryabinin.a.a, glider,
christophe.leroy, aneesh.kumar, naveen.n.rao, tglx, mingo
Cc: linux-arm-kernel, linux-mm, linuxppc-dev, linux-kernel, kasan-dev
In-Reply-To: <20260518084956.2538442-7-tianruidong@linux.alibaba.com>
On 5/18/26 4:49 PM, Ruidong Tian wrote:
> Add KUnit tests for copy_page() and copy_mc_page(), modeled after
> the existing memcpy_test() style: a static page-aligned src and a
> two-page dst, filled with random bytes plus non-zero edges, then
> verify byte-for-byte equality and that the adjacent page is
> untouched. The copy_mc_page() case additionally checks the return
> value is 0 on clean memory and is gated on CONFIG_ARCH_HAS_COPY_MC.
>
> Signed-off-by: Ruidong Tian <tianruidong@linux.alibaba.com>
> ---
> lib/tests/memcpy_kunit.c | 67 +++++++++++++++++++++++++++++++++++++++-
> 1 file changed, 66 insertions(+), 1 deletion(-)
>
> diff --git a/lib/tests/memcpy_kunit.c b/lib/tests/memcpy_kunit.c
> index d36933554e46..85df53ccfb0c 100644
> --- a/lib/tests/memcpy_kunit.c
> +++ b/lib/tests/memcpy_kunit.c
> @@ -493,6 +493,67 @@ static void memmove_overlap_test(struct kunit *test)
> }
> }
>
> +/* --- Page-sized copy tests --- */
> +
> +static u8 page_src[PAGE_SIZE] __aligned(PAGE_SIZE);
> +static u8 page_dst[PAGE_SIZE * 2] __aligned(PAGE_SIZE);
> +static const u8 page_zero[PAGE_SIZE] __aligned(PAGE_SIZE);
> +
> +static void init_page(struct kunit *test)
> +{
> + /* Get many bit patterns. */
> + get_random_bytes(page_src, PAGE_SIZE);
> +
> + /* Make sure we have non-zero edges. */
> + set_random_nonzero(test, &page_src[0]);
> + set_random_nonzero(test, &page_src[PAGE_SIZE - 1]);
> +
> + /* Explicitly zero the entire destination. */
> + memset(page_dst, 0, ARRAY_SIZE(page_dst));
init_page() memsets the entire two-page page_dst to zero, and the
test then asserts the second page is still zero. A buggy copy_page()
that overwrites the second page with bytes that happen to include a
zero byte would still pass.
Please fill page_dst with a non-zero sentinel (e.g. 0xA5) instead
of zero, and check the second half against that sentinel using
memchr_inv():
memset(page_dst, 0xA5, ARRAY_SIZE(page_dst));
...
KUNIT_ASSERT_TRUE_MSG(test,
!memchr_inv(page_dst + PAGE_SIZE, 0xA5, PAGE_SIZE),
"copy_page overflow into adjacent page");
This also lets you delete page_zero[] entirely, which on
PAGE_SIZE=64K configs (arm64) saves a non-trivial amount of BSS.
> +}
> +
> +static void copy_page_test(struct kunit *test)
> +{
> + init_page(test);
> +
> + /* Copy. */
> + copy_page(page_dst, page_src);
> +
> + /* Verify byte-for-byte exact. */
> + KUNIT_ASSERT_EQ_MSG(test,
> + memcmp(page_dst, page_src, PAGE_SIZE), 0,
> + "copy_page content mismatch with random data");
> +
> + /* Verify no overflow into second page. */
> + KUNIT_ASSERT_EQ_MSG(test,
> + memcmp(page_dst + PAGE_SIZE, page_zero, PAGE_SIZE), 0,
> + "copy_page overflow into adjacent page");
> +}
> +
> +#ifdef CONFIG_ARCH_HAS_COPY_MC
> +static void copy_mc_page_test(struct kunit *test)
> +{
> + int ret;
> +
> + init_page(test);
> +
> + /* Copy and check return value. */
> + ret = copy_mc_page(page_dst, page_src);
From sashiko:
Does this introduce a build regression on architectures like x86_64 or
powerpc?
The CONFIG_ARCH_HAS_COPY_MC flag is selected by multiple architectures,
but copy_mc_page() is an arm64-specific API. Other architectures use the
generic copy_mc_to_kernel() API instead.
If the kernel is built with KUnit enabled on non-arm64 architectures, it
appears copy_mc_page() would be an undeclared identifier.
Thanks.
Shuai
^ permalink raw reply
* [RFC v3 10/10] KVM: PPC: selftests: Replace u64 gpa, u64 gva|vaddr with gpa_t and gva_t
From: Ritesh Harjani (IBM) @ 2026-05-27 12:49 UTC (permalink / raw)
To: kvm
Cc: linuxppc-dev, Madhavan Srinivasan, Harsh Prateek Bora,
Christophe Leroy, Venkat Rao Bagalkote, Nicholas Piggin,
Misbah Anjum N, Anushree Mathur, Michael Ellerman, linux-kernel,
Ritesh Harjani (IBM)
In-Reply-To: <cover.1779885589.git.ritesh.list@gmail.com>
commit df079910f9814 ("KVM: selftests: Replace "u64 gpa" with "gpa_t" throughout")
commit 014dfb7b9bf3f ("KVM: selftests: Replace "vaddr" with "gva" throughout")
Similar to above 2 commits, this patch makes the relevant changes to
powerpc kvm selftests code to make use of gpa_t and gva_t types instead
of u64 throughout the code.
No functional change intended.
Signed-off-by: Ritesh Harjani (IBM) <ritesh.list@gmail.com>
---
.../selftests/kvm/include/powerpc/processor.h | 3 ++-
.../selftests/kvm/lib/powerpc/processor.c | 20 +++++++++----------
2 files changed, 12 insertions(+), 11 deletions(-)
diff --git a/tools/testing/selftests/kvm/include/powerpc/processor.h b/tools/testing/selftests/kvm/include/powerpc/processor.h
index 95ec3debc83c..f0ee21a8e2d4 100644
--- a/tools/testing/selftests/kvm/include/powerpc/processor.h
+++ b/tools/testing/selftests/kvm/include/powerpc/processor.h
@@ -7,6 +7,7 @@
#include <linux/compiler.h>
#include "ppc_asm.h"
+#include "kvm_util_types.h"
extern unsigned char __interrupts_start[];
extern unsigned char __interrupts_end[];
@@ -24,7 +25,7 @@ struct ex_regs {
u64 xer;
u32 cr;
u32 trap;
- u64 vaddr; /* vaddr of this struct */
+ gva_t gva; /* gva of this struct */
};
void vm_install_exception_handler(struct kvm_vm *vm, int vector,
diff --git a/tools/testing/selftests/kvm/lib/powerpc/processor.c b/tools/testing/selftests/kvm/lib/powerpc/processor.c
index 2c1e95c8aa9c..009a0859ba05 100644
--- a/tools/testing/selftests/kvm/lib/powerpc/processor.c
+++ b/tools/testing/selftests/kvm/lib/powerpc/processor.c
@@ -97,17 +97,17 @@ static u64 pt_entry_coverage(struct kvm_vm *vm, int level)
return size;
}
-static int pt_idx(struct kvm_vm *vm, u64 vaddr, int level, u64 *nls)
+static int pt_idx(struct kvm_vm *vm, gva_t gva, int level, u64 *nls)
{
switch (level) {
case 1:
if (nls)
*nls = 0x9;
- return (vaddr >> 39) & 0x1fff;
+ return (gva >> 39) & 0x1fff;
case 2:
if (nls)
*nls = 0x9;
- return (vaddr >> 30) & 0x1ff;
+ return (gva >> 30) & 0x1ff;
case 3:
if (vm->mode == VM_MODE_P52V52_4K) {
if (nls)
@@ -116,12 +116,12 @@ static int pt_idx(struct kvm_vm *vm, u64 vaddr, int level, u64 *nls)
if (nls)
*nls = 0x5;
}
- return (vaddr >> 21) & 0x1ff;
+ return (gva >> 21) & 0x1ff;
case 4:
if (vm->mode == VM_MODE_P52V52_4K)
- return (vaddr >> 12) & 0x1ff;
+ return (gva >> 12) & 0x1ff;
else /* vm->mode == VM_MODE_P52V52_64K */
- return (vaddr >> 16) & 0x1f;
+ return (gva >> 16) & 0x1f;
default:
TEST_ASSERT(false, "Invalid page table level %d\n", level);
return 0;
@@ -129,9 +129,9 @@ static int pt_idx(struct kvm_vm *vm, u64 vaddr, int level, u64 *nls)
}
static u64 *virt_get_pte(struct kvm_vm *vm, gpa_t pt,
- u64 vaddr, int level, u64 *nls)
+ gva_t gva, int level, u64 *nls)
{
- int idx = pt_idx(vm, vaddr, level, nls);
+ int idx = pt_idx(vm, gva, level, nls);
u64 *ptep = addr_gpa2hva(vm, pt + idx * 8);
return ptep;
@@ -189,7 +189,7 @@ static gpa_t __vm_alloc_pt(struct kvm_vm *vm, u64 pt_shift)
return pt;
}
-void virt_arch_pg_map(struct kvm_vm *vm, u64 gva, u64 gpa)
+void virt_arch_pg_map(struct kvm_vm *vm, gva_t gva, gpa_t gpa)
{
gpa_t pt = vm->mmu.pgd;
u64 *ptep, pte;
@@ -331,7 +331,7 @@ struct kvm_vcpu *vm_arch_vcpu_add(struct kvm_vm *vm, u32 vcpu_id)
MEM_REGION_DATA);
ex_regs_paddr = addr_gva2gpa(vm, ex_regs_vaddr);
ex_regs = addr_gpa2hva(vm, ex_regs_paddr);
- ex_regs->vaddr = ex_regs_vaddr;
+ ex_regs->gva = ex_regs_vaddr;
vcpu = __vm_vcpu_add(vm, vcpu_id);
--
2.39.5
^ permalink raw reply related
* [RFC v3 09/10] KVM: PPC: selftests: Use u8 instead of uint8_t
From: Ritesh Harjani (IBM) @ 2026-05-27 12:49 UTC (permalink / raw)
To: kvm
Cc: linuxppc-dev, Madhavan Srinivasan, Harsh Prateek Bora,
Christophe Leroy, Venkat Rao Bagalkote, Nicholas Piggin,
Misbah Anjum N, Anushree Mathur, Michael Ellerman, linux-kernel,
Ritesh Harjani (IBM)
In-Reply-To: <cover.1779885589.git.ritesh.list@gmail.com>
commit 6ec982b5a2c7c ("KVM: selftests: Use u8 instead of uint8_t")
made this change from uint32_t -> u32 for all other kvm selftests.
Do the same for powerpc as well. No functional changes expected.
It uses the same method described in above commit.
git ls-files tools/testing/selftests/kvm | xargs sed -i 's/uint8_t/u8/g'
Signed-off-by: Ritesh Harjani (IBM) <ritesh.list@gmail.com>
---
tools/testing/selftests/kvm/lib/powerpc/processor.c | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/tools/testing/selftests/kvm/lib/powerpc/processor.c b/tools/testing/selftests/kvm/lib/powerpc/processor.c
index 0a65fc3a8d9a..2c1e95c8aa9c 100644
--- a/tools/testing/selftests/kvm/lib/powerpc/processor.c
+++ b/tools/testing/selftests/kvm/lib/powerpc/processor.c
@@ -256,7 +256,7 @@ gpa_t addr_arch_gva2gpa(struct kvm_vm *vm, gva_t gva)
}
static void virt_dump_pt(FILE *stream, struct kvm_vm *vm, gpa_t pt,
- gva_t va, int level, uint8_t indent)
+ gva_t va, int level, u8 indent)
{
int size, idx;
@@ -283,7 +283,7 @@ static void virt_dump_pt(FILE *stream, struct kvm_vm *vm, gpa_t pt,
}
-void virt_arch_dump(FILE *stream, struct kvm_vm *vm, uint8_t indent)
+void virt_arch_dump(FILE *stream, struct kvm_vm *vm, u8 indent)
{
gpa_t pt = vm->mmu.pgd;
@@ -380,7 +380,7 @@ void vcpu_args_set(struct kvm_vcpu *vcpu, unsigned int num, ...)
va_end(ap);
}
-void vcpu_arch_dump(FILE *stream, struct kvm_vcpu *vcpu, uint8_t indent)
+void vcpu_arch_dump(FILE *stream, struct kvm_vcpu *vcpu, u8 indent)
{
struct kvm_regs regs;
--
2.39.5
^ permalink raw reply related
* [RFC v3 08/10] KVM: PPC: selftests: Use u32 instead of uint32_t
From: Ritesh Harjani (IBM) @ 2026-05-27 12:49 UTC (permalink / raw)
To: kvm
Cc: linuxppc-dev, Madhavan Srinivasan, Harsh Prateek Bora,
Christophe Leroy, Venkat Rao Bagalkote, Nicholas Piggin,
Misbah Anjum N, Anushree Mathur, Michael Ellerman, linux-kernel,
Ritesh Harjani (IBM)
In-Reply-To: <cover.1779885589.git.ritesh.list@gmail.com>
commit 0c3a8774692aa ("KVM: selftests: Use u32 instead of uint32_t")
made this change from uint32_t -> u32 for all other kvm selftests.
Do the same for powerpc as well. No functional changes expected.
It uses the same method described in above commit.
git ls-files tools/testing/selftests/kvm | xargs sed -i 's/uint32_t/u32/g'
Signed-off-by: Ritesh Harjani (IBM) <ritesh.list@gmail.com>
---
tools/testing/selftests/kvm/include/powerpc/processor.h | 4 ++--
tools/testing/selftests/kvm/lib/powerpc/processor.c | 2 +-
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/tools/testing/selftests/kvm/include/powerpc/processor.h b/tools/testing/selftests/kvm/include/powerpc/processor.h
index cbbe390ae244..95ec3debc83c 100644
--- a/tools/testing/selftests/kvm/include/powerpc/processor.h
+++ b/tools/testing/selftests/kvm/include/powerpc/processor.h
@@ -22,8 +22,8 @@ struct ex_regs {
u64 lr;
u64 ctr;
u64 xer;
- uint32_t cr;
- uint32_t trap;
+ u32 cr;
+ u32 trap;
u64 vaddr; /* vaddr of this struct */
};
diff --git a/tools/testing/selftests/kvm/lib/powerpc/processor.c b/tools/testing/selftests/kvm/lib/powerpc/processor.c
index ac5c3421ec81..0a65fc3a8d9a 100644
--- a/tools/testing/selftests/kvm/lib/powerpc/processor.c
+++ b/tools/testing/selftests/kvm/lib/powerpc/processor.c
@@ -312,7 +312,7 @@ void vcpu_arch_set_entry_point(struct kvm_vcpu *vcpu, void *guest_code)
vcpu_regs_set(vcpu, ®s);
}
-struct kvm_vcpu *vm_arch_vcpu_add(struct kvm_vm *vm, uint32_t vcpu_id)
+struct kvm_vcpu *vm_arch_vcpu_add(struct kvm_vm *vm, u32 vcpu_id)
{
const size_t stack_size = SZ_64K;
gva_t stack_vaddr, ex_regs_vaddr;
--
2.39.5
^ permalink raw reply related
* [RFC v3 07/10] KVM: PPC: selftests: Use s64 instead of int64_t
From: Ritesh Harjani (IBM) @ 2026-05-27 12:49 UTC (permalink / raw)
To: kvm
Cc: linuxppc-dev, Madhavan Srinivasan, Harsh Prateek Bora,
Christophe Leroy, Venkat Rao Bagalkote, Nicholas Piggin,
Misbah Anjum N, Anushree Mathur, Michael Ellerman, linux-kernel,
Ritesh Harjani (IBM)
In-Reply-To: <cover.1779885589.git.ritesh.list@gmail.com>
commit 286e8903aed14 ("KVM: selftests: Use s64 instead of int64_t")
made this change from int64_t -> s64 for all other kvm selftests.
Do the same for powerpc as well. No functional changes expected.
It uses the same method describes in above commit.
git ls-files tools/testing/selftests/kvm | xargs sed -i 's/int64_t/s64/g'
Signed-off-by: Ritesh Harjani (IBM) <ritesh.list@gmail.com>
---
tools/testing/selftests/kvm/include/powerpc/hcall.h | 6 +++---
tools/testing/selftests/kvm/lib/powerpc/hcall.c | 6 +++---
2 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/tools/testing/selftests/kvm/include/powerpc/hcall.h b/tools/testing/selftests/kvm/include/powerpc/hcall.h
index f923b3f5e233..21b2bf9180f1 100644
--- a/tools/testing/selftests/kvm/include/powerpc/hcall.h
+++ b/tools/testing/selftests/kvm/include/powerpc/hcall.h
@@ -10,8 +10,8 @@
/* Ucalls use unimplemented PAPR hcall 0 which exits KVM */
#define H_UCALL 0
-int64_t hcall0(u64 token);
-int64_t hcall1(u64 token, u64 arg1);
-int64_t hcall2(u64 token, u64 arg1, u64 arg2);
+s64 hcall0(u64 token);
+s64 hcall1(u64 token, u64 arg1);
+s64 hcall2(u64 token, u64 arg1, u64 arg2);
#endif
diff --git a/tools/testing/selftests/kvm/lib/powerpc/hcall.c b/tools/testing/selftests/kvm/lib/powerpc/hcall.c
index cd10022572a5..d5f5282654ee 100644
--- a/tools/testing/selftests/kvm/lib/powerpc/hcall.c
+++ b/tools/testing/selftests/kvm/lib/powerpc/hcall.c
@@ -5,7 +5,7 @@
#include "kvm_util.h"
#include "hcall.h"
-int64_t hcall0(u64 token)
+s64 hcall0(u64 token)
{
register uintptr_t r3 asm ("r3") = token;
@@ -17,7 +17,7 @@ int64_t hcall0(u64 token)
return r3;
}
-int64_t hcall1(u64 token, u64 arg1)
+s64 hcall1(u64 token, u64 arg1)
{
register uintptr_t r3 asm ("r3") = token;
register uintptr_t r4 asm ("r4") = arg1;
@@ -30,7 +30,7 @@ int64_t hcall1(u64 token, u64 arg1)
return r3;
}
-int64_t hcall2(u64 token, u64 arg1, u64 arg2)
+s64 hcall2(u64 token, u64 arg1, u64 arg2)
{
register uintptr_t r3 asm ("r3") = token;
register uintptr_t r4 asm ("r4") = arg1;
--
2.39.5
^ permalink raw reply related
* [RFC v3 06/10] KVM: PPC: selftests: Use u64 instead of uint64_t
From: Ritesh Harjani (IBM) @ 2026-05-27 12:49 UTC (permalink / raw)
To: kvm
Cc: linuxppc-dev, Madhavan Srinivasan, Harsh Prateek Bora,
Christophe Leroy, Venkat Rao Bagalkote, Nicholas Piggin,
Misbah Anjum N, Anushree Mathur, Michael Ellerman, linux-kernel,
Ritesh Harjani (IBM)
In-Reply-To: <cover.1779885589.git.ritesh.list@gmail.com>
commit 26f8453288d4c ("KVM: selftests: Use u64 instead of uint64_t")
made this change from uint64_t -> u64 for all other kvm selftests.
Do the same for powerpc as well. No functional changes expected.
Signed-off-by: Ritesh Harjani (IBM) <ritesh.list@gmail.com>
---
.../selftests/kvm/include/powerpc/hcall.h | 6 +--
.../selftests/kvm/include/powerpc/processor.h | 16 +++----
.../testing/selftests/kvm/lib/powerpc/hcall.c | 6 +--
.../selftests/kvm/lib/powerpc/processor.c | 44 +++++++++----------
4 files changed, 36 insertions(+), 36 deletions(-)
diff --git a/tools/testing/selftests/kvm/include/powerpc/hcall.h b/tools/testing/selftests/kvm/include/powerpc/hcall.h
index 4028baa6c5d8..f923b3f5e233 100644
--- a/tools/testing/selftests/kvm/include/powerpc/hcall.h
+++ b/tools/testing/selftests/kvm/include/powerpc/hcall.h
@@ -10,8 +10,8 @@
/* Ucalls use unimplemented PAPR hcall 0 which exits KVM */
#define H_UCALL 0
-int64_t hcall0(uint64_t token);
-int64_t hcall1(uint64_t token, uint64_t arg1);
-int64_t hcall2(uint64_t token, uint64_t arg1, uint64_t arg2);
+int64_t hcall0(u64 token);
+int64_t hcall1(u64 token, u64 arg1);
+int64_t hcall2(u64 token, u64 arg1, u64 arg2);
#endif
diff --git a/tools/testing/selftests/kvm/include/powerpc/processor.h b/tools/testing/selftests/kvm/include/powerpc/processor.h
index cb75b77c33bb..cbbe390ae244 100644
--- a/tools/testing/selftests/kvm/include/powerpc/processor.h
+++ b/tools/testing/selftests/kvm/include/powerpc/processor.h
@@ -15,16 +15,16 @@ struct kvm_vm;
struct kvm_vcpu;
struct ex_regs {
- uint64_t gprs[32];
- uint64_t nia;
- uint64_t msr;
- uint64_t cfar;
- uint64_t lr;
- uint64_t ctr;
- uint64_t xer;
+ u64 gprs[32];
+ u64 nia;
+ u64 msr;
+ u64 cfar;
+ u64 lr;
+ u64 ctr;
+ u64 xer;
uint32_t cr;
uint32_t trap;
- uint64_t vaddr; /* vaddr of this struct */
+ u64 vaddr; /* vaddr of this struct */
};
void vm_install_exception_handler(struct kvm_vm *vm, int vector,
diff --git a/tools/testing/selftests/kvm/lib/powerpc/hcall.c b/tools/testing/selftests/kvm/lib/powerpc/hcall.c
index efb4318252be..cd10022572a5 100644
--- a/tools/testing/selftests/kvm/lib/powerpc/hcall.c
+++ b/tools/testing/selftests/kvm/lib/powerpc/hcall.c
@@ -5,7 +5,7 @@
#include "kvm_util.h"
#include "hcall.h"
-int64_t hcall0(uint64_t token)
+int64_t hcall0(u64 token)
{
register uintptr_t r3 asm ("r3") = token;
@@ -17,7 +17,7 @@ int64_t hcall0(uint64_t token)
return r3;
}
-int64_t hcall1(uint64_t token, uint64_t arg1)
+int64_t hcall1(u64 token, u64 arg1)
{
register uintptr_t r3 asm ("r3") = token;
register uintptr_t r4 asm ("r4") = arg1;
@@ -30,7 +30,7 @@ int64_t hcall1(uint64_t token, uint64_t arg1)
return r3;
}
-int64_t hcall2(uint64_t token, uint64_t arg1, uint64_t arg2)
+int64_t hcall2(u64 token, u64 arg1, u64 arg2)
{
register uintptr_t r3 asm ("r3") = token;
register uintptr_t r4 asm ("r4") = arg1;
diff --git a/tools/testing/selftests/kvm/lib/powerpc/processor.c b/tools/testing/selftests/kvm/lib/powerpc/processor.c
index 9846ec26a32a..ac5c3421ec81 100644
--- a/tools/testing/selftests/kvm/lib/powerpc/processor.c
+++ b/tools/testing/selftests/kvm/lib/powerpc/processor.c
@@ -14,9 +14,9 @@
#define RADIX_TREE_SIZE ((0x2UL << 61) | (0x5UL << 5)) /* 52-bits */
#define RADIX_PGD_INDEX_SIZE 13
-static void set_proc_table(struct kvm_vm *vm, int pid, uint64_t dw0, uint64_t dw1)
+static void set_proc_table(struct kvm_vm *vm, int pid, u64 dw0, u64 dw1)
{
- uint64_t *proc_table;
+ u64 *proc_table;
proc_table = addr_gpa2hva(vm, vm->arch.prtb);
proc_table[pid * 2 + 0] = cpu_to_be64(dw0);
@@ -81,9 +81,9 @@ static int pt_shift(struct kvm_vm *vm, int level)
}
}
-static uint64_t pt_entry_coverage(struct kvm_vm *vm, int level)
+static u64 pt_entry_coverage(struct kvm_vm *vm, int level)
{
- uint64_t size = vm->page_size;
+ u64 size = vm->page_size;
if (level == 4)
return size;
@@ -97,7 +97,7 @@ static uint64_t pt_entry_coverage(struct kvm_vm *vm, int level)
return size;
}
-static int pt_idx(struct kvm_vm *vm, uint64_t vaddr, int level, uint64_t *nls)
+static int pt_idx(struct kvm_vm *vm, u64 vaddr, int level, u64 *nls)
{
switch (level) {
case 1:
@@ -128,11 +128,11 @@ static int pt_idx(struct kvm_vm *vm, uint64_t vaddr, int level, uint64_t *nls)
}
}
-static uint64_t *virt_get_pte(struct kvm_vm *vm, gpa_t pt,
- uint64_t vaddr, int level, uint64_t *nls)
+static u64 *virt_get_pte(struct kvm_vm *vm, gpa_t pt,
+ u64 vaddr, int level, u64 *nls)
{
int idx = pt_idx(vm, vaddr, level, nls);
- uint64_t *ptep = addr_gpa2hva(vm, pt + idx * 8);
+ u64 *ptep = addr_gpa2hva(vm, pt + idx * 8);
return ptep;
}
@@ -151,7 +151,7 @@ static uint64_t *virt_get_pte(struct kvm_vm *vm, gpa_t pt,
#define PDE_NLS 0x0000000000000011ull
#define PDE_PT_MASK 0x0fffffffffffff00ull
-static gpa_t __vm_alloc_pt(struct kvm_vm *vm, uint64_t pt_shift)
+static gpa_t __vm_alloc_pt(struct kvm_vm *vm, u64 pt_shift)
{
gpa_t pt;
@@ -189,16 +189,16 @@ static gpa_t __vm_alloc_pt(struct kvm_vm *vm, uint64_t pt_shift)
return pt;
}
-void virt_arch_pg_map(struct kvm_vm *vm, uint64_t gva, uint64_t gpa)
+void virt_arch_pg_map(struct kvm_vm *vm, u64 gva, u64 gpa)
{
gpa_t pt = vm->mmu.pgd;
- uint64_t *ptep, pte;
+ u64 *ptep, pte;
int level;
for (level = 1; level <= 3; level++) {
- uint64_t nls;
- uint64_t *pdep = virt_get_pte(vm, pt, gva, level, &nls);
- uint64_t pde = be64_to_cpu(*pdep);
+ u64 nls;
+ u64 *pdep = virt_get_pte(vm, pt, gva, level, &nls);
+ u64 pde = be64_to_cpu(*pdep);
if (pde) {
TEST_ASSERT((pde & PDE_VALID) && !(pde & PTE_LEAF),
@@ -227,13 +227,13 @@ void virt_arch_pg_map(struct kvm_vm *vm, uint64_t gva, uint64_t gpa)
gpa_t addr_arch_gva2gpa(struct kvm_vm *vm, gva_t gva)
{
gpa_t pt = vm->mmu.pgd;
- uint64_t *ptep, pte;
+ u64 *ptep, pte;
int level;
for (level = 1; level <= 3; level++) {
- uint64_t nls;
- uint64_t *pdep = virt_get_pte(vm, pt, gva, level, &nls);
- uint64_t pde = be64_to_cpu(*pdep);
+ u64 nls;
+ u64 *pdep = virt_get_pte(vm, pt, gva, level, &nls);
+ u64 pde = be64_to_cpu(*pdep);
TEST_ASSERT((pde & PDE_VALID) && !(pde & PTE_LEAF),
"PDE not present at level: %u gva: 0x%lx pde:0x%lx\n",
@@ -263,8 +263,8 @@ static void virt_dump_pt(FILE *stream, struct kvm_vm *vm, gpa_t pt,
size = 1U << (pt_shift(vm, level) + 3);
for (idx = 0; idx < size; idx += 8, va += pt_entry_coverage(vm, level)) {
- uint64_t *page_table = addr_gpa2hva(vm, pt + idx);
- uint64_t pte = be64_to_cpu(*page_table);
+ u64 *page_table = addr_gpa2hva(vm, pt + idx);
+ u64 pte = be64_to_cpu(*page_table);
if (!(pte & PTE_VALID))
continue;
@@ -320,7 +320,7 @@ struct kvm_vcpu *vm_arch_vcpu_add(struct kvm_vm *vm, uint32_t vcpu_id)
struct ex_regs *ex_regs;
struct kvm_regs regs;
struct kvm_vcpu *vcpu;
- uint64_t lpcr;
+ u64 lpcr;
stack_vaddr = __vm_alloc(vm, stack_size,
DEFAULT_GUEST_STACK_VADDR_MIN,
@@ -374,7 +374,7 @@ void vcpu_args_set(struct kvm_vcpu *vcpu, unsigned int num, ...)
vcpu_regs_get(vcpu, ®s);
for (i = 0; i < num; i++)
- regs.gpr[i + 3] = va_arg(ap, uint64_t);
+ regs.gpr[i + 3] = va_arg(ap, u64);
vcpu_regs_set(vcpu, ®s);
va_end(ap);
--
2.39.5
^ permalink raw reply related
* [RFC v3 05/10] KVM: selftests: Print the vcpu_id when KVM_CREATE_VCPU ioctl fails
From: Ritesh Harjani (IBM) @ 2026-05-27 12:49 UTC (permalink / raw)
To: kvm
Cc: linuxppc-dev, Madhavan Srinivasan, Harsh Prateek Bora,
Christophe Leroy, Venkat Rao Bagalkote, Nicholas Piggin,
Misbah Anjum N, Anushree Mathur, Michael Ellerman, linux-kernel,
Ritesh Harjani (IBM)
In-Reply-To: <cover.1779885589.git.ritesh.list@gmail.com>
Print the vcpu_id and errno when the KVM_CREATE_VCPU ioctl fails, for
debug purposes.
This helped in debugging an issue with KVM on PowerVM, where KVM_CREATE_VCPU
only supports max 2048 vcpus, because the PAPR_HCALL H_GUEST_CREATE_VCPU
("Documentation/arch/powerpc/kvm-nested.rst") supports only up to 0-2047
vcpu_id. However KVM_CAP_MAX_VCPUS capability extension always reports max_vcpus
as NR_CPUS of the host.
Signed-off-by: Ritesh Harjani (IBM) <ritesh.list@gmail.com>
---
tools/testing/selftests/kvm/lib/kvm_util.c | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/tools/testing/selftests/kvm/lib/kvm_util.c b/tools/testing/selftests/kvm/lib/kvm_util.c
index e00a25f59416..6319e2037882 100644
--- a/tools/testing/selftests/kvm/lib/kvm_util.c
+++ b/tools/testing/selftests/kvm/lib/kvm_util.c
@@ -1354,6 +1354,13 @@ struct kvm_vcpu *__vm_vcpu_add(struct kvm_vm *vm, u32 vcpu_id)
vcpu->vm = vm;
vcpu->id = vcpu_id;
vcpu->fd = __vm_ioctl(vm, KVM_CREATE_VCPU, (void *)(unsigned long)vcpu_id);
+ if (vcpu->fd < 0) {
+ int saved_errno = errno;
+
+ pr_info("Failed KVM_CREATE_VCPU for vcpu_id %u with errno %d\n",
+ vcpu_id, saved_errno);
+ errno = saved_errno;
+ }
TEST_ASSERT_VM_VCPU_IOCTL(vcpu->fd >= 0, KVM_CREATE_VCPU, vcpu->fd, vm);
TEST_ASSERT(vcpu_mmap_sz() >= sizeof(*vcpu->run), "vcpu mmap size "
--
2.39.5
^ permalink raw reply related
* [RFC v3 04/10] KVM: PPC: selftests: powerpc enable kvm_create_max_vcpus test
From: Ritesh Harjani (IBM) @ 2026-05-27 12:49 UTC (permalink / raw)
To: kvm
Cc: linuxppc-dev, Madhavan Srinivasan, Harsh Prateek Bora,
Christophe Leroy, Venkat Rao Bagalkote, Nicholas Piggin,
Misbah Anjum N, Anushree Mathur, Michael Ellerman, linux-kernel,
Ritesh Harjani (IBM)
In-Reply-To: <cover.1779885589.git.ritesh.list@gmail.com>
From: Nicholas Piggin <npiggin@gmail.com>
powerpc's maximum permitted vCPU ID depends on the VM's SMT mode, and
the maximum reported by KVM_CAP_MAX_VCPU_ID exceeds a simple non-SMT
VM's limit.
The powerpc KVM selftest port uses non-SMT VMs, so add a workaround
to the kvm_create_max_vcpus test case to limit vCPU IDs to
KVM_CAP_MAX_VCPUS on powerpc.
Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
[Rebased to latest mainline tree]
Signed-off-by: Ritesh Harjani (IBM) <ritesh.list@gmail.com>
---
tools/testing/selftests/kvm/kvm_create_max_vcpus.c | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/tools/testing/selftests/kvm/kvm_create_max_vcpus.c b/tools/testing/selftests/kvm/kvm_create_max_vcpus.c
index c5310736ed06..a82c13d6cdf5 100644
--- a/tools/testing/selftests/kvm/kvm_create_max_vcpus.c
+++ b/tools/testing/selftests/kvm/kvm_create_max_vcpus.c
@@ -56,6 +56,15 @@ int main(int argc, char *argv[])
"KVM_MAX_VCPU_IDS (%d) must be at least as large as KVM_MAX_VCPUS (%d).",
kvm_max_vcpu_id, kvm_max_vcpus);
+#ifdef __powerpc64__
+ /*
+ * powerpc has a particular format for the vcpu ID that depends on
+ * the guest SMT mode, and the max ID cap is too large for non-SMT
+ * modes, where the maximum ID is the same as the maximum vCPUs.
+ */
+ kvm_max_vcpu_id = kvm_max_vcpus;
+#endif
+
test_vcpu_creation(0, kvm_max_vcpus);
if (kvm_max_vcpu_id > kvm_max_vcpus)
--
2.39.5
^ permalink raw reply related
* [RFC v3 03/10] KVM: PPC: selftests: add support for powerpc
From: Ritesh Harjani (IBM) @ 2026-05-27 12:49 UTC (permalink / raw)
To: kvm
Cc: linuxppc-dev, Madhavan Srinivasan, Harsh Prateek Bora,
Christophe Leroy, Venkat Rao Bagalkote, Nicholas Piggin,
Misbah Anjum N, Anushree Mathur, Michael Ellerman, linux-kernel,
Ritesh Harjani (IBM)
In-Reply-To: <cover.1779885589.git.ritesh.list@gmail.com>
From: Nicholas Piggin <npiggin@gmail.com>
Implement KVM selftests support for powerpc (Book3S-64).
ucalls are implemented with an unsupported PAPR hcall number which will
always cause KVM to exit to userspace.
Virtual memory is implemented for the radix MMU, and only a base page
size is supported (both 4K and 64K).
Guest interrupts are taken in real-mode, so require a page allocated at
gRA 0x0. Interrupt entry is complicated because gVA:gRA is not 1:1
mapped (like the kernel is), so the MMU can not just be switched on and
off.
Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
[Rebased to latest mainline tree, "cc" to clobber list, fix the
size calculation in kvm_arch_vm_post_create()]
Signed-off-by: Ritesh Harjani (IBM) <ritesh.list@gmail.com>
---
MAINTAINERS | 2 +
tools/testing/selftests/kvm/Makefile | 2 +-
tools/testing/selftests/kvm/Makefile.kvm | 10 +
.../testing/selftests/kvm/include/kvm_util.h | 9 +
.../selftests/kvm/include/powerpc/hcall.h | 17 +
.../kvm/include/powerpc/kvm_util_arch.h | 22 +
.../selftests/kvm/include/powerpc/ppc_asm.h | 32 ++
.../selftests/kvm/include/powerpc/processor.h | 38 ++
.../selftests/kvm/include/powerpc/ucall.h | 21 +
tools/testing/selftests/kvm/lib/guest_modes.c | 20 +-
tools/testing/selftests/kvm/lib/kvm_util.c | 8 +
.../selftests/kvm/lib/powerpc/handlers.S | 93 ++++
.../testing/selftests/kvm/lib/powerpc/hcall.c | 45 ++
.../selftests/kvm/lib/powerpc/processor.c | 483 ++++++++++++++++++
.../testing/selftests/kvm/lib/powerpc/ucall.c | 22 +
15 files changed, 821 insertions(+), 3 deletions(-)
create mode 100644 tools/testing/selftests/kvm/include/powerpc/hcall.h
create mode 100644 tools/testing/selftests/kvm/include/powerpc/kvm_util_arch.h
create mode 100644 tools/testing/selftests/kvm/include/powerpc/ppc_asm.h
create mode 100644 tools/testing/selftests/kvm/include/powerpc/processor.h
create mode 100644 tools/testing/selftests/kvm/include/powerpc/ucall.h
create mode 100644 tools/testing/selftests/kvm/lib/powerpc/handlers.S
create mode 100644 tools/testing/selftests/kvm/lib/powerpc/hcall.c
create mode 100644 tools/testing/selftests/kvm/lib/powerpc/processor.c
create mode 100644 tools/testing/selftests/kvm/lib/powerpc/ucall.c
diff --git a/MAINTAINERS b/MAINTAINERS
index 4087b67bbc69..ae5c933bfa55 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -14241,6 +14241,8 @@ F: arch/powerpc/include/asm/kvm*
F: arch/powerpc/include/uapi/asm/kvm*
F: arch/powerpc/kernel/kvm*
F: arch/powerpc/kvm/
+F: tools/testing/selftests/kvm/*/powerpc/
+F: tools/testing/selftests/kvm/powerpc/
KERNEL VIRTUAL MACHINE FOR RISC-V (KVM/riscv)
M: Anup Patel <anup@brainfault.org>
diff --git a/tools/testing/selftests/kvm/Makefile b/tools/testing/selftests/kvm/Makefile
index f2b223072b62..03d91f00092f 100644
--- a/tools/testing/selftests/kvm/Makefile
+++ b/tools/testing/selftests/kvm/Makefile
@@ -3,7 +3,7 @@ top_srcdir = ../../../..
include $(top_srcdir)/scripts/subarch.include
ARCH ?= $(SUBARCH)
-ifeq ($(ARCH),$(filter $(ARCH),arm64 s390 riscv x86 x86_64 loongarch))
+ifeq ($(ARCH),$(filter $(ARCH),arm64 s390 riscv x86 x86_64 loongarch powerpc))
# Top-level selftests allows ARCH=x86_64 :-(
ifeq ($(ARCH),x86_64)
override ARCH := x86
diff --git a/tools/testing/selftests/kvm/Makefile.kvm b/tools/testing/selftests/kvm/Makefile.kvm
index 3aba476def62..70b40bd9e663 100644
--- a/tools/testing/selftests/kvm/Makefile.kvm
+++ b/tools/testing/selftests/kvm/Makefile.kvm
@@ -52,6 +52,11 @@ LIBKVM_loongarch += lib/loongarch/processor.c
LIBKVM_loongarch += lib/loongarch/ucall.c
LIBKVM_loongarch += lib/loongarch/exception.S
+LIBKVM_powerpc += lib/powerpc/handlers.S
+LIBKVM_powerpc += lib/powerpc/processor.c
+LIBKVM_powerpc += lib/powerpc/ucall.c
+LIBKVM_powerpc += lib/powerpc/hcall.c
+
# Non-compiled test targets
TEST_PROGS_x86 += x86/nx_huge_pages_test.sh
@@ -241,6 +246,11 @@ TEST_GEN_PROGS_loongarch += memslot_perf_test
TEST_GEN_PROGS_loongarch += set_memory_region_test
TEST_GEN_PROGS_loongarch += steal_time
+TEST_GEN_PROGS_powerpc = $(TEST_GEN_PROGS_COMMON)
+TEST_GEN_PROGS_powerpc += access_tracking_perf_test
+TEST_GEN_PROGS_powerpc += dirty_log_perf_test
+TEST_GEN_PROGS_powerpc += hardware_disable_test
+
SPLIT_TESTS += arch_timer
SPLIT_TESTS += get-reg-list
diff --git a/tools/testing/selftests/kvm/include/kvm_util.h b/tools/testing/selftests/kvm/include/kvm_util.h
index c515c918c2c9..10f03a182c8b 100644
--- a/tools/testing/selftests/kvm/include/kvm_util.h
+++ b/tools/testing/selftests/kvm/include/kvm_util.h
@@ -209,6 +209,9 @@ enum vm_guest_mode {
VM_MODE_P41V48_4K,
VM_MODE_P41V39_4K,
+ VM_MODE_P52V52_4K, /* For powerpc64 */
+ VM_MODE_P52V52_64K,
+
NUM_VM_MODES,
};
@@ -268,6 +271,12 @@ extern enum vm_guest_mode vm_mode_default;
#define MIN_PAGE_SHIFT 12U
#define ptes_per_page(page_size) ((page_size) / 8)
+#elif defined(__powerpc64__)
+
+#define VM_MODE_DEFAULT vm_mode_default
+#define MIN_PAGE_SHIFT 12U
+#define ptes_per_page(page_size) ((page_size) / 8)
+
#endif
#define VM_SHAPE_DEFAULT VM_SHAPE(VM_MODE_DEFAULT)
diff --git a/tools/testing/selftests/kvm/include/powerpc/hcall.h b/tools/testing/selftests/kvm/include/powerpc/hcall.h
new file mode 100644
index 000000000000..4028baa6c5d8
--- /dev/null
+++ b/tools/testing/selftests/kvm/include/powerpc/hcall.h
@@ -0,0 +1,17 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * powerpc hcall defines
+ */
+#ifndef SELFTEST_KVM_HCALL_H
+#define SELFTEST_KVM_HCALL_H
+
+#include <linux/compiler.h>
+
+/* Ucalls use unimplemented PAPR hcall 0 which exits KVM */
+#define H_UCALL 0
+
+int64_t hcall0(uint64_t token);
+int64_t hcall1(uint64_t token, uint64_t arg1);
+int64_t hcall2(uint64_t token, uint64_t arg1, uint64_t arg2);
+
+#endif
diff --git a/tools/testing/selftests/kvm/include/powerpc/kvm_util_arch.h b/tools/testing/selftests/kvm/include/powerpc/kvm_util_arch.h
new file mode 100644
index 000000000000..5d45c25cd299
--- /dev/null
+++ b/tools/testing/selftests/kvm/include/powerpc/kvm_util_arch.h
@@ -0,0 +1,22 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+#ifndef SELFTEST_KVM_UTIL_ARCH_H
+#define SELFTEST_KVM_UTIL_ARCH_H
+
+#include <stdint.h>
+
+#include "kvm_util_types.h"
+
+struct kvm_mmu_arch {};
+
+/* Page table fragment cache for guest page tables < page size */
+struct vm_pt_frag_cache {
+ gpa_t page;
+ size_t page_nr_used;
+};
+
+struct kvm_vm_arch {
+ gpa_t prtb; /* process table */
+ struct vm_pt_frag_cache pt_frag_cache[2]; /* 256B and 4KB PT caches */
+};
+
+#endif /* SELFTEST_KVM_UTIL_ARCH_H */
diff --git a/tools/testing/selftests/kvm/include/powerpc/ppc_asm.h b/tools/testing/selftests/kvm/include/powerpc/ppc_asm.h
new file mode 100644
index 000000000000..b9df64659792
--- /dev/null
+++ b/tools/testing/selftests/kvm/include/powerpc/ppc_asm.h
@@ -0,0 +1,32 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * powerpc asm specific defines
+ */
+#ifndef SELFTEST_KVM_PPC_ASM_H
+#define SELFTEST_KVM_PPC_ASM_H
+
+#define STACK_FRAME_MIN_SIZE 112 /* Could be 32 on ELFv2 */
+#define STACK_REDZONE_SIZE 512
+
+#define INT_FRAME_SIZE (STACK_FRAME_MIN_SIZE + STACK_REDZONE_SIZE)
+
+#define SPR_SRR0 0x01a
+#define SPR_SRR1 0x01b
+#define SPR_CFAR 0x01c
+
+#define MSR_SF 0x8000000000000000ULL
+#define MSR_HV 0x1000000000000000ULL
+#define MSR_VEC 0x0000000002000000ULL
+#define MSR_VSX 0x0000000000800000ULL
+#define MSR_EE 0x0000000000008000ULL
+#define MSR_PR 0x0000000000004000ULL
+#define MSR_FP 0x0000000000002000ULL
+#define MSR_ME 0x0000000000001000ULL
+#define MSR_IR 0x0000000000000020ULL
+#define MSR_DR 0x0000000000000010ULL
+#define MSR_RI 0x0000000000000002ULL
+#define MSR_LE 0x0000000000000001ULL
+
+#define LPCR_ILE 0x0000000002000000ULL
+
+#endif
diff --git a/tools/testing/selftests/kvm/include/powerpc/processor.h b/tools/testing/selftests/kvm/include/powerpc/processor.h
new file mode 100644
index 000000000000..cb75b77c33bb
--- /dev/null
+++ b/tools/testing/selftests/kvm/include/powerpc/processor.h
@@ -0,0 +1,38 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * powerpc processor specific defines
+ */
+#ifndef SELFTEST_KVM_PROCESSOR_H
+#define SELFTEST_KVM_PROCESSOR_H
+
+#include <linux/compiler.h>
+#include "ppc_asm.h"
+
+extern unsigned char __interrupts_start[];
+extern unsigned char __interrupts_end[];
+
+struct kvm_vm;
+struct kvm_vcpu;
+
+struct ex_regs {
+ uint64_t gprs[32];
+ uint64_t nia;
+ uint64_t msr;
+ uint64_t cfar;
+ uint64_t lr;
+ uint64_t ctr;
+ uint64_t xer;
+ uint32_t cr;
+ uint32_t trap;
+ uint64_t vaddr; /* vaddr of this struct */
+};
+
+void vm_install_exception_handler(struct kvm_vm *vm, int vector,
+ void (*handler)(struct ex_regs *));
+
+static inline void cpu_relax(void)
+{
+ asm volatile("" ::: "memory");
+}
+
+#endif
diff --git a/tools/testing/selftests/kvm/include/powerpc/ucall.h b/tools/testing/selftests/kvm/include/powerpc/ucall.h
new file mode 100644
index 000000000000..e0dbe91e8848
--- /dev/null
+++ b/tools/testing/selftests/kvm/include/powerpc/ucall.h
@@ -0,0 +1,21 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+#ifndef SELFTEST_KVM_UCALL_H
+#define SELFTEST_KVM_UCALL_H
+
+#include "hcall.h"
+
+#define UCALL_EXIT_REASON KVM_EXIT_PAPR_HCALL
+
+#define UCALL_R4_UCALL 0x5715 /* regular ucall, r5 contains ucall pointer */
+#define UCALL_R4_SIMPLE 0x0000 /* simple exit usable by asm with no ucall data */
+
+static inline void ucall_arch_init(struct kvm_vm *vm, gpa_t mmio_gpa)
+{
+}
+
+static inline void ucall_arch_do_ucall(gva_t uc)
+{
+ hcall2(H_UCALL, UCALL_R4_UCALL, (uintptr_t)(uc));
+}
+
+#endif
diff --git a/tools/testing/selftests/kvm/lib/guest_modes.c b/tools/testing/selftests/kvm/lib/guest_modes.c
index 7a96c43b5704..439766fad693 100644
--- a/tools/testing/selftests/kvm/lib/guest_modes.c
+++ b/tools/testing/selftests/kvm/lib/guest_modes.c
@@ -4,16 +4,20 @@
*/
#include "guest_modes.h"
-#if defined(__aarch64__) || defined(__riscv)
+#if defined(__aarch64__) || defined(__riscv) || defined(__powerpc64__)
#include "processor.h"
enum vm_guest_mode vm_mode_default;
#endif
+#if defined(__powerpc64__)
+#include <unistd.h>
+#endif
+
struct guest_mode guest_modes[NUM_VM_MODES];
void guest_modes_append_default(void)
{
-#if !defined(__aarch64__) && !defined(__riscv)
+#if !defined(__aarch64__) && !defined(__riscv) && !defined(__powerpc64__)
guest_mode_append(VM_MODE_DEFAULT, true);
#endif
@@ -108,6 +112,18 @@ void guest_modes_append_default(void)
TEST_ASSERT(vm_mode_default != NUM_VM_MODES, "No supported mode!");
}
#endif
+#ifdef __powerpc64__
+ {
+ TEST_REQUIRE(kvm_has_cap(KVM_CAP_PPC_MMU_RADIX));
+ /* Radix guest EA and RA are 52-bit on POWER9 and POWER10 */
+ if (sysconf(_SC_PAGESIZE) == 4096)
+ vm_mode_default = VM_MODE_P52V52_4K;
+ else
+ vm_mode_default = VM_MODE_P52V52_64K;
+ guest_mode_append(VM_MODE_P52V52_4K, true);
+ guest_mode_append(VM_MODE_P52V52_64K, true);
+ }
+#endif
}
void for_each_guest_mode(void (*func)(enum vm_guest_mode, void *), void *arg)
diff --git a/tools/testing/selftests/kvm/lib/kvm_util.c b/tools/testing/selftests/kvm/lib/kvm_util.c
index ac7215824203..e00a25f59416 100644
--- a/tools/testing/selftests/kvm/lib/kvm_util.c
+++ b/tools/testing/selftests/kvm/lib/kvm_util.c
@@ -218,6 +218,8 @@ const char *vm_guest_mode_string(u32 i)
[VM_MODE_P41V57_4K] = "PA-bits:41, VA-bits:57, 4K pages",
[VM_MODE_P41V48_4K] = "PA-bits:41, VA-bits:48, 4K pages",
[VM_MODE_P41V39_4K] = "PA-bits:41, VA-bits:39, 4K pages",
+ [VM_MODE_P52V52_4K] = "PA-bits:52, VA-bits:52, 4K pages",
+ [VM_MODE_P52V52_64K] = "PA-bits:52, VA-bits:52, 64K pages",
};
_Static_assert(sizeof(strings)/sizeof(char *) == NUM_VM_MODES,
"Missing new mode strings?");
@@ -254,6 +256,8 @@ const struct vm_guest_mode_params vm_guest_mode_params[] = {
[VM_MODE_P41V57_4K] = { 41, 57, 0x1000, 12 },
[VM_MODE_P41V48_4K] = { 41, 48, 0x1000, 12 },
[VM_MODE_P41V39_4K] = { 41, 39, 0x1000, 12 },
+ [VM_MODE_P52V52_4K] = { 52, 52, 0x1000, 12 },
+ [VM_MODE_P52V52_64K] = { 52, 52, 0x10000, 16 },
};
_Static_assert(sizeof(vm_guest_mode_params)/sizeof(struct vm_guest_mode_params) == NUM_VM_MODES,
"Missing new mode params?");
@@ -371,6 +375,10 @@ struct kvm_vm *____vm_create(struct vm_shape shape)
case VM_MODE_P41V39_4K:
vm->mmu.pgtable_levels = 3;
break;
+ case VM_MODE_P52V52_4K:
+ case VM_MODE_P52V52_64K:
+ vm->mmu.pgtable_levels = 4;
+ break;
default:
TEST_FAIL("Unknown guest mode: 0x%x", vm->mode);
}
diff --git a/tools/testing/selftests/kvm/lib/powerpc/handlers.S b/tools/testing/selftests/kvm/lib/powerpc/handlers.S
new file mode 100644
index 000000000000..b860f6a520a1
--- /dev/null
+++ b/tools/testing/selftests/kvm/lib/powerpc/handlers.S
@@ -0,0 +1,93 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#include <ppc_asm.h>
+
+.macro INTERRUPT vec
+. = __interrupts_start + \vec
+ std %r0,(0*8)(%r13)
+ std %r3,(3*8)(%r13)
+ mfspr %r0,SPR_CFAR
+ li %r3,\vec
+ b handle_interrupt
+.endm
+
+.balign 0x1000
+.global __interrupts_start
+__interrupts_start:
+INTERRUPT 0x100
+INTERRUPT 0x200
+INTERRUPT 0x300
+INTERRUPT 0x380
+INTERRUPT 0x400
+INTERRUPT 0x480
+INTERRUPT 0x500
+INTERRUPT 0x600
+INTERRUPT 0x700
+INTERRUPT 0x800
+INTERRUPT 0x900
+INTERRUPT 0xa00
+INTERRUPT 0xc00
+INTERRUPT 0xd00
+INTERRUPT 0xf00
+INTERRUPT 0xf20
+INTERRUPT 0xf40
+INTERRUPT 0xf60
+
+virt_handle_interrupt:
+ stdu %r1,-INT_FRAME_SIZE(%r1)
+ mr %r3,%r31
+ bl route_interrupt
+ ld %r4,(32*8)(%r31) /* NIA */
+ ld %r5,(33*8)(%r31) /* MSR */
+ ld %r6,(35*8)(%r31) /* LR */
+ ld %r7,(36*8)(%r31) /* CTR */
+ ld %r8,(37*8)(%r31) /* XER */
+ lwz %r9,(38*8)(%r31) /* CR */
+ mtspr SPR_SRR0,%r4
+ mtspr SPR_SRR1,%r5
+ mtlr %r6
+ mtctr %r7
+ mtxer %r8
+ mtcr %r9
+reg=4
+ ld %r0,(0*8)(%r31)
+ ld %r3,(3*8)(%r31)
+.rept 28
+ ld reg,(reg*8)(%r31)
+ reg=reg+1
+.endr
+ addi %r1,%r1,INT_FRAME_SIZE
+ rfid
+
+virt_handle_interrupt_p:
+ .llong virt_handle_interrupt
+
+handle_interrupt:
+reg=4
+.rept 28
+ std reg,(reg*8)(%r13)
+ reg=reg+1
+.endr
+ mfspr %r4,SPR_SRR0
+ mfspr %r5,SPR_SRR1
+ mflr %r6
+ mfctr %r7
+ mfxer %r8
+ mfcr %r9
+ std %r4,(32*8)(%r13) /* NIA */
+ std %r5,(33*8)(%r13) /* MSR */
+ std %r0,(34*8)(%r13) /* CFAR */
+ std %r6,(35*8)(%r13) /* LR */
+ std %r7,(36*8)(%r13) /* CTR */
+ std %r8,(37*8)(%r13) /* XER */
+ stw %r9,(38*8 + 0)(%r13) /* CR */
+ stw %r3,(38*8 + 4)(%r13) /* TRAP */
+
+ ld %r31,(39*8)(%r13) /* vaddr */
+ ld %r4,virt_handle_interrupt_p - __interrupts_start(0)
+ mtspr SPR_SRR0,%r4
+ /* Reuse SRR1 */
+
+ rfid
+.global __interrupts_end
+__interrupts_end:
diff --git a/tools/testing/selftests/kvm/lib/powerpc/hcall.c b/tools/testing/selftests/kvm/lib/powerpc/hcall.c
new file mode 100644
index 000000000000..efb4318252be
--- /dev/null
+++ b/tools/testing/selftests/kvm/lib/powerpc/hcall.c
@@ -0,0 +1,45 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * PAPR (pseries) hcall support.
+ */
+#include "kvm_util.h"
+#include "hcall.h"
+
+int64_t hcall0(uint64_t token)
+{
+ register uintptr_t r3 asm ("r3") = token;
+
+ asm volatile("sc 1" : "+r"(r3) :
+ : "r0", "r4", "r5", "r6", "r7", "r8", "r9",
+ "r10", "r11", "r12", "ctr", "xer",
+ "cc", "memory");
+
+ return r3;
+}
+
+int64_t hcall1(uint64_t token, uint64_t arg1)
+{
+ register uintptr_t r3 asm ("r3") = token;
+ register uintptr_t r4 asm ("r4") = arg1;
+
+ asm volatile("sc 1" : "+r"(r3), "+r"(r4) :
+ : "r0", "r5", "r6", "r7", "r8", "r9",
+ "r10", "r11", "r12", "ctr", "xer",
+ "cc", "memory");
+
+ return r3;
+}
+
+int64_t hcall2(uint64_t token, uint64_t arg1, uint64_t arg2)
+{
+ register uintptr_t r3 asm ("r3") = token;
+ register uintptr_t r4 asm ("r4") = arg1;
+ register uintptr_t r5 asm ("r5") = arg2;
+
+ asm volatile("sc 1" : "+r"(r3), "+r"(r4), "+r"(r5) :
+ : "r0", "r6", "r7", "r8", "r9",
+ "r10", "r11", "r12", "ctr", "xer",
+ "cc", "memory");
+
+ return r3;
+}
diff --git a/tools/testing/selftests/kvm/lib/powerpc/processor.c b/tools/testing/selftests/kvm/lib/powerpc/processor.c
new file mode 100644
index 000000000000..9846ec26a32a
--- /dev/null
+++ b/tools/testing/selftests/kvm/lib/powerpc/processor.c
@@ -0,0 +1,483 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * KVM selftest powerpc library code - CPU-related functions (page tables...)
+ */
+
+#include <linux/sizes.h>
+
+#include "processor.h"
+#include "kvm_util.h"
+#include "ucall_common.h"
+#include "guest_modes.h"
+#include "hcall.h"
+
+#define RADIX_TREE_SIZE ((0x2UL << 61) | (0x5UL << 5)) /* 52-bits */
+#define RADIX_PGD_INDEX_SIZE 13
+
+static void set_proc_table(struct kvm_vm *vm, int pid, uint64_t dw0, uint64_t dw1)
+{
+ uint64_t *proc_table;
+
+ proc_table = addr_gpa2hva(vm, vm->arch.prtb);
+ proc_table[pid * 2 + 0] = cpu_to_be64(dw0);
+ proc_table[pid * 2 + 1] = cpu_to_be64(dw1);
+}
+
+static void set_radix_proc_table(struct kvm_vm *vm, int pid, gpa_t pgd)
+{
+ set_proc_table(vm, pid, pgd | RADIX_TREE_SIZE | RADIX_PGD_INDEX_SIZE, 0);
+}
+
+void virt_arch_pgd_alloc(struct kvm_vm *vm)
+{
+ struct kvm_ppc_mmuv3_cfg mmu_cfg;
+ gpa_t prtb, pgtb;
+ size_t pgd_pages;
+
+ TEST_ASSERT((vm->mode == VM_MODE_P52V52_4K) ||
+ (vm->mode == VM_MODE_P52V52_64K),
+ "Unsupported guest mode, mode: 0x%x", vm->mode);
+
+ prtb = vm_phy_page_alloc(vm, KVM_GUEST_PAGE_TABLE_MIN_PADDR,
+ vm->memslots[MEM_REGION_PT]);
+ vm->arch.prtb = prtb;
+
+ pgd_pages = (1UL << (RADIX_PGD_INDEX_SIZE + 3)) >> vm->page_shift;
+ if (!pgd_pages)
+ pgd_pages = 1;
+ pgtb = vm_phy_pages_alloc_align(vm, pgd_pages, pgd_pages,
+ KVM_GUEST_PAGE_TABLE_MIN_PADDR,
+ vm->memslots[MEM_REGION_PT]);
+ vm->mmu.pgd = pgtb;
+
+ /* Set the base page directory in the proc table */
+ set_radix_proc_table(vm, 0, pgtb);
+
+ if (vm->mode == VM_MODE_P52V52_4K)
+ mmu_cfg.process_table = prtb | 0x8000000000000000UL | 0x0; /* 4K size */
+ else /* vm->mode == VM_MODE_P52V52_64K */
+ mmu_cfg.process_table = prtb | 0x8000000000000000UL | 0x4; /* 64K size */
+ mmu_cfg.flags = KVM_PPC_MMUV3_RADIX | KVM_PPC_MMUV3_GTSE;
+
+ vm_ioctl(vm, KVM_PPC_CONFIGURE_V3_MMU, &mmu_cfg);
+}
+
+static int pt_shift(struct kvm_vm *vm, int level)
+{
+ switch (level) {
+ case 1:
+ return 13;
+ case 2:
+ case 3:
+ return 9;
+ case 4:
+ if (vm->mode == VM_MODE_P52V52_4K)
+ return 9;
+ else /* vm->mode == VM_MODE_P52V52_64K */
+ return 5;
+ default:
+ TEST_ASSERT(false, "Invalid page table level %d\n", level);
+ return 0;
+ }
+}
+
+static uint64_t pt_entry_coverage(struct kvm_vm *vm, int level)
+{
+ uint64_t size = vm->page_size;
+
+ if (level == 4)
+ return size;
+ size <<= pt_shift(vm, 4);
+ if (level == 3)
+ return size;
+ size <<= pt_shift(vm, 3);
+ if (level == 2)
+ return size;
+ size <<= pt_shift(vm, 2);
+ return size;
+}
+
+static int pt_idx(struct kvm_vm *vm, uint64_t vaddr, int level, uint64_t *nls)
+{
+ switch (level) {
+ case 1:
+ if (nls)
+ *nls = 0x9;
+ return (vaddr >> 39) & 0x1fff;
+ case 2:
+ if (nls)
+ *nls = 0x9;
+ return (vaddr >> 30) & 0x1ff;
+ case 3:
+ if (vm->mode == VM_MODE_P52V52_4K) {
+ if (nls)
+ *nls = 0x9;
+ } else { /* vm->mode == VM_MODE_P52V52_64K */
+ if (nls)
+ *nls = 0x5;
+ }
+ return (vaddr >> 21) & 0x1ff;
+ case 4:
+ if (vm->mode == VM_MODE_P52V52_4K)
+ return (vaddr >> 12) & 0x1ff;
+ else /* vm->mode == VM_MODE_P52V52_64K */
+ return (vaddr >> 16) & 0x1f;
+ default:
+ TEST_ASSERT(false, "Invalid page table level %d\n", level);
+ return 0;
+ }
+}
+
+static uint64_t *virt_get_pte(struct kvm_vm *vm, gpa_t pt,
+ uint64_t vaddr, int level, uint64_t *nls)
+{
+ int idx = pt_idx(vm, vaddr, level, nls);
+ uint64_t *ptep = addr_gpa2hva(vm, pt + idx * 8);
+
+ return ptep;
+}
+
+#define PTE_VALID 0x8000000000000000ull
+#define PTE_LEAF 0x4000000000000000ull
+#define PTE_REFERENCED 0x0000000000000100ull
+#define PTE_CHANGED 0x0000000000000080ull
+#define PTE_PRIV 0x0000000000000008ull
+#define PTE_READ 0x0000000000000004ull
+#define PTE_RW 0x0000000000000002ull
+#define PTE_EXEC 0x0000000000000001ull
+#define PTE_PAGE_MASK 0x01fffffffffff000ull
+
+#define PDE_VALID PTE_VALID
+#define PDE_NLS 0x0000000000000011ull
+#define PDE_PT_MASK 0x0fffffffffffff00ull
+
+static gpa_t __vm_alloc_pt(struct kvm_vm *vm, uint64_t pt_shift)
+{
+ gpa_t pt;
+
+ if (pt_shift >= vm->page_shift) {
+ size_t pt_pages = 1ULL << (pt_shift - vm->page_shift);
+
+ pt = vm_phy_pages_alloc_align(vm, pt_pages, pt_pages,
+ KVM_GUEST_PAGE_TABLE_MIN_PADDR,
+ vm->memslots[MEM_REGION_PT]);
+ } else {
+ struct vm_pt_frag_cache *pt_frag_cache;
+
+ if (pt_shift == 8) {
+ pt_frag_cache = &vm->arch.pt_frag_cache[0];
+ } else if (pt_shift == 12) {
+ pt_frag_cache = &vm->arch.pt_frag_cache[1];
+ } else {
+ TEST_ASSERT(0, "Invalid pt_shift:%lu\n", pt_shift);
+ return 0;
+ }
+
+ if (!pt_frag_cache->page) {
+ pt_frag_cache->page = vm_phy_pages_alloc_align(vm, 1, 1,
+ KVM_GUEST_PAGE_TABLE_MIN_PADDR,
+ vm->memslots[MEM_REGION_PT]);
+ }
+ pt = pt_frag_cache->page + pt_frag_cache->page_nr_used;
+ pt_frag_cache->page_nr_used += (1 << pt_shift);
+ if (pt_frag_cache->page_nr_used == vm->page_size) {
+ pt_frag_cache->page = 0;
+ pt_frag_cache->page_nr_used = 0;
+ }
+ }
+
+ return pt;
+}
+
+void virt_arch_pg_map(struct kvm_vm *vm, uint64_t gva, uint64_t gpa)
+{
+ gpa_t pt = vm->mmu.pgd;
+ uint64_t *ptep, pte;
+ int level;
+
+ for (level = 1; level <= 3; level++) {
+ uint64_t nls;
+ uint64_t *pdep = virt_get_pte(vm, pt, gva, level, &nls);
+ uint64_t pde = be64_to_cpu(*pdep);
+
+ if (pde) {
+ TEST_ASSERT((pde & PDE_VALID) && !(pde & PTE_LEAF),
+ "Invalid PDE at level: %u gva: 0x%lx pde:0x%lx\n",
+ level, gva, pde);
+ pt = pde & PDE_PT_MASK;
+ continue;
+ }
+
+ pt = __vm_alloc_pt(vm, nls + 3);
+ pde = PDE_VALID | nls | pt;
+ *pdep = cpu_to_be64(pde);
+ }
+
+ ptep = virt_get_pte(vm, pt, gva, level, NULL);
+ pte = be64_to_cpu(*ptep);
+
+ TEST_ASSERT(!pte, "PTE already present at level: %u gva: 0x%lx pte:0x%lx\n",
+ level, gva, pte);
+
+ pte = PTE_VALID | PTE_LEAF | PTE_REFERENCED | PTE_CHANGED | PTE_PRIV |
+ PTE_READ | PTE_RW | PTE_EXEC | (gpa & PTE_PAGE_MASK);
+ *ptep = cpu_to_be64(pte);
+}
+
+gpa_t addr_arch_gva2gpa(struct kvm_vm *vm, gva_t gva)
+{
+ gpa_t pt = vm->mmu.pgd;
+ uint64_t *ptep, pte;
+ int level;
+
+ for (level = 1; level <= 3; level++) {
+ uint64_t nls;
+ uint64_t *pdep = virt_get_pte(vm, pt, gva, level, &nls);
+ uint64_t pde = be64_to_cpu(*pdep);
+
+ TEST_ASSERT((pde & PDE_VALID) && !(pde & PTE_LEAF),
+ "PDE not present at level: %u gva: 0x%lx pde:0x%lx\n",
+ level, gva, pde);
+ pt = pde & PDE_PT_MASK;
+ }
+
+ ptep = virt_get_pte(vm, pt, gva, level, NULL);
+ pte = be64_to_cpu(*ptep);
+
+ TEST_ASSERT(pte,
+ "PTE not present at level: %u gva: 0x%lx pte:0x%lx\n",
+ level, gva, pte);
+
+ TEST_ASSERT((pte & PTE_VALID) && (pte & PTE_LEAF),
+ "PTE not valid at level: %u gva: 0x%lx pte:0x%lx\n",
+ level, gva, pte);
+
+ return (pte & PTE_PAGE_MASK) + (gva & (vm->page_size - 1));
+}
+
+static void virt_dump_pt(FILE *stream, struct kvm_vm *vm, gpa_t pt,
+ gva_t va, int level, uint8_t indent)
+{
+ int size, idx;
+
+ size = 1U << (pt_shift(vm, level) + 3);
+
+ for (idx = 0; idx < size; idx += 8, va += pt_entry_coverage(vm, level)) {
+ uint64_t *page_table = addr_gpa2hva(vm, pt + idx);
+ uint64_t pte = be64_to_cpu(*page_table);
+
+ if (!(pte & PTE_VALID))
+ continue;
+
+ if (pte & PTE_LEAF) {
+ fprintf(stream,
+ "%*s PTE[%d] gVA:0x%016lx -> gRA:0x%016llx\n",
+ indent, "", idx / 8, va, pte & PTE_PAGE_MASK);
+ } else {
+ fprintf(stream, "%*sPDE%d[%d] gVA:0x%016lx\n",
+ indent, "", level, idx / 8, va);
+ virt_dump_pt(stream, vm, pte & PDE_PT_MASK, va,
+ level + 1, indent + 2);
+ }
+ }
+
+}
+
+void virt_arch_dump(FILE *stream, struct kvm_vm *vm, uint8_t indent)
+{
+ gpa_t pt = vm->mmu.pgd;
+
+ if (!vm->mmu.pgd_created)
+ return;
+
+ virt_dump_pt(stream, vm, pt, 0, 1, indent);
+}
+
+static unsigned long get_r2(void)
+{
+ unsigned long r2;
+
+ asm("mr %0,%%r2" : "=r"(r2));
+
+ return r2;
+}
+
+void vcpu_arch_set_entry_point(struct kvm_vcpu *vcpu, void *guest_code)
+{
+ struct kvm_regs regs;
+
+ vcpu_regs_get(vcpu, ®s);
+ regs.pc = (uintptr_t)guest_code;
+ regs.gpr[12] = (uintptr_t)guest_code;
+ vcpu_regs_set(vcpu, ®s);
+}
+
+struct kvm_vcpu *vm_arch_vcpu_add(struct kvm_vm *vm, uint32_t vcpu_id)
+{
+ const size_t stack_size = SZ_64K;
+ gva_t stack_vaddr, ex_regs_vaddr;
+ gpa_t ex_regs_paddr;
+ struct ex_regs *ex_regs;
+ struct kvm_regs regs;
+ struct kvm_vcpu *vcpu;
+ uint64_t lpcr;
+
+ stack_vaddr = __vm_alloc(vm, stack_size,
+ DEFAULT_GUEST_STACK_VADDR_MIN,
+ MEM_REGION_DATA);
+
+ ex_regs_vaddr = __vm_alloc(vm, stack_size,
+ DEFAULT_GUEST_STACK_VADDR_MIN,
+ MEM_REGION_DATA);
+ ex_regs_paddr = addr_gva2gpa(vm, ex_regs_vaddr);
+ ex_regs = addr_gpa2hva(vm, ex_regs_paddr);
+ ex_regs->vaddr = ex_regs_vaddr;
+
+ vcpu = __vm_vcpu_add(vm, vcpu_id);
+
+ vcpu_enable_cap(vcpu, KVM_CAP_PPC_PAPR, 1);
+
+ /* Setup guest registers */
+ vcpu_regs_get(vcpu, ®s);
+ lpcr = vcpu_get_reg(vcpu, KVM_REG_PPC_LPCR_64);
+
+ regs.gpr[1] = stack_vaddr + stack_size - 256;
+ regs.gpr[2] = (uintptr_t)get_r2();
+ regs.gpr[13] = (uintptr_t)ex_regs_paddr;
+
+ regs.msr = MSR_SF | MSR_VEC | MSR_VSX | MSR_FP |
+ MSR_ME | MSR_IR | MSR_DR | MSR_RI;
+
+ if (BYTE_ORDER == LITTLE_ENDIAN) {
+ regs.msr |= MSR_LE;
+ lpcr |= LPCR_ILE;
+ } else {
+ lpcr &= ~LPCR_ILE;
+ }
+
+ vcpu_regs_set(vcpu, ®s);
+ vcpu_set_reg(vcpu, KVM_REG_PPC_LPCR_64, lpcr);
+
+ return vcpu;
+}
+
+void vcpu_args_set(struct kvm_vcpu *vcpu, unsigned int num, ...)
+{
+ va_list ap;
+ struct kvm_regs regs;
+ int i;
+
+ TEST_ASSERT(num >= 1 && num <= 5, "Unsupported number of args: %u\n",
+ num);
+
+ va_start(ap, num);
+ vcpu_regs_get(vcpu, ®s);
+
+ for (i = 0; i < num; i++)
+ regs.gpr[i + 3] = va_arg(ap, uint64_t);
+
+ vcpu_regs_set(vcpu, ®s);
+ va_end(ap);
+}
+
+void vcpu_arch_dump(FILE *stream, struct kvm_vcpu *vcpu, uint8_t indent)
+{
+ struct kvm_regs regs;
+
+ vcpu_regs_get(vcpu, ®s);
+
+ fprintf(stream, "%*sNIA: 0x%016llx MSR: 0x%016llx\n",
+ indent, "", regs.pc, regs.msr);
+ fprintf(stream, "%*sLR: 0x%016llx CTR :0x%016llx\n",
+ indent, "", regs.lr, regs.ctr);
+ fprintf(stream, "%*sCR: 0x%08llx XER :0x%016llx\n",
+ indent, "", regs.cr, regs.xer);
+}
+
+void kvm_arch_vm_post_create(struct kvm_vm *vm, unsigned int nr_vcpus)
+{
+ size_t excp_size = __interrupts_end - __interrupts_start;
+ size_t excp_pages = (excp_size + vm->page_size - 1) / vm->page_size;
+ gpa_t excp_paddr;
+ void *mem;
+
+ excp_paddr = vm_phy_pages_alloc(vm, excp_pages, 0,
+ vm->memslots[MEM_REGION_DATA]);
+
+ TEST_ASSERT(excp_paddr == 0,
+ "Interrupt vectors not allocated at gPA address 0: (0x%lx)",
+ excp_paddr);
+
+ mem = addr_gpa2hva(vm, excp_paddr);
+ memcpy(mem, __interrupts_start, excp_size);
+}
+
+void assert_on_unhandled_exception(struct kvm_vcpu *vcpu)
+{
+ struct ucall uc;
+
+ if (get_ucall(vcpu, &uc) == UCALL_UNHANDLED) {
+ gpa_t ex_regs_paddr;
+ struct ex_regs *ex_regs;
+ struct kvm_regs regs;
+
+ vcpu_regs_get(vcpu, ®s);
+ ex_regs_paddr = (gpa_t)regs.gpr[13];
+ ex_regs = addr_gpa2hva(vcpu->vm, ex_regs_paddr);
+
+ TEST_FAIL("Unexpected interrupt in guest NIA:0x%016lx MSR:0x%016lx TRAP:0x%04x",
+ ex_regs->nia, ex_regs->msr, ex_regs->trap);
+ }
+}
+
+struct handler {
+ void (*fn)(struct ex_regs *regs);
+ int trap;
+};
+
+#define NR_HANDLERS 10
+static struct handler handlers[NR_HANDLERS];
+
+void route_interrupt(struct ex_regs *regs)
+{
+ int i;
+
+ for (i = 0; i < NR_HANDLERS; i++) {
+ if (handlers[i].trap == regs->trap) {
+ handlers[i].fn(regs);
+ return;
+ }
+ }
+
+ ucall(UCALL_UNHANDLED, 0);
+}
+
+void vm_install_exception_handler(struct kvm_vm *vm, int trap,
+ void (*fn)(struct ex_regs *))
+{
+ int i;
+
+ for (i = 0; i < NR_HANDLERS; i++) {
+ if (!handlers[i].trap || handlers[i].trap == trap) {
+ if (fn == NULL)
+ trap = 0; /* Clear handler */
+ handlers[i].trap = trap;
+ handlers[i].fn = fn;
+ sync_global_to_guest(vm, handlers[i]);
+ return;
+ }
+ }
+
+ TEST_FAIL("Out of exception handlers");
+}
+
+void kvm_selftest_arch_init(void)
+{
+ TEST_REQUIRE(kvm_has_cap(KVM_CAP_PPC_MMU_RADIX));
+
+ /*
+ * powerpc default mode is set by host page size and not static,
+ * so start by computing that early.
+ */
+ guest_modes_append_default();
+}
diff --git a/tools/testing/selftests/kvm/lib/powerpc/ucall.c b/tools/testing/selftests/kvm/lib/powerpc/ucall.c
new file mode 100644
index 000000000000..3481a7a0b850
--- /dev/null
+++ b/tools/testing/selftests/kvm/lib/powerpc/ucall.c
@@ -0,0 +1,22 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * ucall support. A ucall is a "hypercall to host userspace".
+ */
+#include "kvm_util.h"
+#include "ucall_common.h"
+#include "hcall.h"
+
+void *ucall_arch_get_ucall(struct kvm_vcpu *vcpu)
+{
+ struct kvm_run *run = vcpu->run;
+
+ if (run->exit_reason == UCALL_EXIT_REASON &&
+ run->papr_hcall.nr == H_UCALL) {
+ struct kvm_regs regs;
+
+ vcpu_regs_get(vcpu, ®s);
+ if (regs.gpr[4] == UCALL_R4_UCALL)
+ return (void *)regs.gpr[5];
+ }
+ return NULL;
+}
--
2.39.5
^ permalink raw reply related
* [RFC v3 02/10] KVM: selftests: Add aligned guest physical page allocator
From: Ritesh Harjani (IBM) @ 2026-05-27 12:49 UTC (permalink / raw)
To: kvm
Cc: linuxppc-dev, Madhavan Srinivasan, Harsh Prateek Bora,
Christophe Leroy, Venkat Rao Bagalkote, Nicholas Piggin,
Misbah Anjum N, Anushree Mathur, Michael Ellerman, linux-kernel,
Ritesh Harjani (IBM)
In-Reply-To: <cover.1779885589.git.ritesh.list@gmail.com>
From: Nicholas Piggin <npiggin@gmail.com>
powerpc will require this to allocate MMU tables in guest memory that
are larger than guest base page size.
Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
[Rebased to latest mainline tree]
Signed-off-by: Ritesh Harjani (IBM) <ritesh.list@gmail.com>
---
.../testing/selftests/kvm/include/kvm_util.h | 20 +++++++++--
tools/testing/selftests/kvm/lib/kvm_util.c | 33 +++++++++----------
2 files changed, 33 insertions(+), 20 deletions(-)
diff --git a/tools/testing/selftests/kvm/include/kvm_util.h b/tools/testing/selftests/kvm/include/kvm_util.h
index 3666a8530f31..c515c918c2c9 100644
--- a/tools/testing/selftests/kvm/include/kvm_util.h
+++ b/tools/testing/selftests/kvm/include/kvm_util.h
@@ -991,8 +991,8 @@ void kvm_gsi_routing_write(struct kvm_vm *vm, struct kvm_irq_routing *routing);
const char *exit_reason_str(unsigned int exit_reason);
gpa_t vm_phy_page_alloc(struct kvm_vm *vm, gpa_t min_gpa, u32 memslot);
-gpa_t __vm_phy_pages_alloc(struct kvm_vm *vm, size_t num, gpa_t min_gpa,
- u32 memslot, bool protected);
+gpa_t __vm_phy_pages_alloc(struct kvm_vm *vm, size_t num, size_t align,
+ gpa_t min_gpa, u32 memslot, bool protected);
gpa_t vm_alloc_page_table(struct kvm_vm *vm);
static inline gpa_t vm_phy_pages_alloc(struct kvm_vm *vm, size_t num,
@@ -1003,10 +1003,24 @@ static inline gpa_t vm_phy_pages_alloc(struct kvm_vm *vm, size_t num,
* protected memory, as the majority of memory for such VMs is
* protected, i.e. using shared memory is effectively opt-in.
*/
- return __vm_phy_pages_alloc(vm, num, min_gpa, memslot,
+ return __vm_phy_pages_alloc(vm, num, 1, min_gpa, memslot,
vm_arch_has_protected_memory(vm));
}
+static inline gpa_t vm_phy_pages_alloc_align(struct kvm_vm *vm, size_t num,
+ size_t align, gpa_t min_gpa,
+ u32 memslot)
+{
+ /*
+ * By default, allocate memory as protected for VMs that support
+ * protected memory, as the majority of memory for such VMs is
+ * protected, i.e. using shared memory is effectively opt-in.
+ */
+ return __vm_phy_pages_alloc(vm, num, align, min_gpa, memslot,
+ vm_arch_has_protected_memory(vm));
+}
+
+
/*
* ____vm_create() does KVM_CREATE_VM and little else. __vm_create() also
* loads the test binary into guest memory and creates an IRQ chip (x86 only).
diff --git a/tools/testing/selftests/kvm/lib/kvm_util.c b/tools/testing/selftests/kvm/lib/kvm_util.c
index e08967ef7b7b..ac7215824203 100644
--- a/tools/testing/selftests/kvm/lib/kvm_util.c
+++ b/tools/testing/selftests/kvm/lib/kvm_util.c
@@ -1442,7 +1442,7 @@ static gva_t ____vm_alloc(struct kvm_vm *vm, size_t sz, gva_t min_gva,
u64 pages = (sz >> vm->page_shift) + ((sz % vm->page_size) != 0);
virt_pgd_alloc(vm);
- gpa_t gpa = __vm_phy_pages_alloc(vm, pages,
+ gpa_t gpa = __vm_phy_pages_alloc(vm, pages, 1,
KVM_UTIL_MIN_PFN * vm->page_size,
vm->memslots[type], protected);
@@ -2021,7 +2021,7 @@ const char *exit_reason_str(unsigned int exit_reason)
* and their base address is returned. A TEST_ASSERT failure occurs if
* not enough pages are available at or above min_gpa.
*/
-gpa_t __vm_phy_pages_alloc(struct kvm_vm *vm, size_t num,
+gpa_t __vm_phy_pages_alloc(struct kvm_vm *vm, size_t num, size_t align,
gpa_t min_gpa, u32 memslot,
bool protected)
{
@@ -2039,23 +2039,22 @@ gpa_t __vm_phy_pages_alloc(struct kvm_vm *vm, size_t num,
TEST_ASSERT(!protected || region->protected_phy_pages,
"Region doesn't support protected memory");
- base = pg = min_gpa >> vm->page_shift;
- do {
- for (; pg < base + num; ++pg) {
- if (!sparsebit_is_set(region->unused_phy_pages, pg)) {
- base = pg = sparsebit_next_set(region->unused_phy_pages, pg);
- break;
+ base = min_gpa >> vm->page_shift;
+again:
+ base = (base + align - 1) & ~(align - 1);
+ for (pg = base; pg < base + num; ++pg) {
+ if (!sparsebit_is_set(region->unused_phy_pages, pg)) {
+ base = sparsebit_next_set(region->unused_phy_pages, pg);
+ if (!base) {
+ fprintf(stderr, "No guest physical page available, "
+ "min_gpa: 0x%lx page_size: 0x%x memslot: %u\n",
+ min_gpa, vm->page_size, memslot);
+ fputs("---- vm dump ----\n", stderr);
+ vm_dump(stderr, vm, 2);
+ abort();
}
+ goto again;
}
- } while (pg && pg != base + num);
-
- if (pg == 0) {
- fprintf(stderr, "No guest physical page available, "
- "min_gpa: 0x%lx page_size: 0x%x memslot: %u\n",
- min_gpa, vm->page_size, memslot);
- fputs("---- vm dump ----\n", stderr);
- vm_dump(stderr, vm, 2);
- abort();
}
for (pg = base; pg < base + num; ++pg) {
--
2.39.5
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox