* Re: [PATCH] staging: sm750fb: constify static char pointer arrays
From: Greg Kroah-Hartman @ 2026-03-31 8:45 UTC (permalink / raw)
To: Hungyu Lin
Cc: Sudip Mukherjee, Teddy Wang, linux-fbdev, linux-staging,
linux-kernel
In-Reply-To: <20260331050738.1547-1-dennylin0707@gmail.com>
On Tue, Mar 31, 2026 at 05:07:38AM +0000, Hungyu Lin wrote:
> The static const char * arrays 'g_fbmode' and 'fix_id' should be
> defined as 'static const char * const' to make the pointer arrays
> themselves constant. This allows the compiler to place them in the
> read-only data section.
>
> Signed-off-by: Hungyu Lin <dennylin0707@gmail.com>
> ---
> drivers/staging/sm750fb/sm750.c | 4 ++--
> 1 file changed, 2 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/staging/sm750fb/sm750.c b/drivers/staging/sm750fb/sm750.c
> index 9a42a08c8..b0bdfaeca 100644
> --- a/drivers/staging/sm750fb/sm750.c
> +++ b/drivers/staging/sm750fb/sm750.c
> @@ -33,7 +33,7 @@
> static int g_hwcursor = 1;
> static int g_noaccel;
> static int g_nomtrr;
> -static const char *g_fbmode[] = {NULL, NULL};
> +static const char * const g_fbmode[] = {NULL, NULL};
> static const char *g_def_fbmode = "1024x768-32@60";
> static char *g_settings;
> static int g_dualview;
> @@ -728,7 +728,7 @@ static int lynxfb_set_fbinfo(struct fb_info *info, int index)
> lynx750_ext, NULL, vesa_modes,
> };
> int cdb[] = {ARRAY_SIZE(lynx750_ext), 0, VESA_MODEDB_SIZE};
> - static const char *fix_id[2] = {
> + static const char * const fix_id[2] = {
> "sm750_fb1", "sm750_fb2",
> };
>
> --
> 2.34.1
>
>
Please always test-build your changes so you do not get grumpy kernel
maintainers asking you why you did not test-build your changes :(
thanks,
greg k-h
^ permalink raw reply
* Re: (subset) [PATCH v1] backlight: apple_bl: Convert to a platform driver
From: Lee Jones @ 2026-03-31 10:48 UTC (permalink / raw)
To: Lee Jones, Rafael J. Wysocki
Cc: LKML, Linux ACPI, Daniel Thompson, Jingoo Han, Helge Deller,
dri-devel, linux-fbdev
In-Reply-To: <5084777.GXAFRqVoOG@rafael.j.wysocki>
On Sat, 14 Mar 2026 12:50:11 +0100, Rafael J. Wysocki wrote:
> In all cases in which a struct acpi_driver is used for binding a driver
> to an ACPI device object, a corresponding platform device is created by
> the ACPI core and that device is regarded as a proper representation of
> underlying hardware. Accordingly, a struct platform_driver should be
> used by driver code to bind to that device. There are multiple reasons
> why drivers should not bind directly to ACPI device objects [1].
>
> [...]
Applied, thanks!
[1/1] backlight: apple_bl: Convert to a platform driver
commit: 04d8f3fd0b52ead84eb722989afa094b8fca9129
--
Lee Jones [李琼斯]
^ permalink raw reply
* Re: [PATCH] staging: sm750fb: constify static char pointer arrays
From: Denny Lin @ 2026-03-31 13:30 UTC (permalink / raw)
To: Greg Kroah-Hartman
Cc: Sudip Mukherjee, Teddy Wang, linux-fbdev, linux-staging,
linux-kernel
In-Reply-To: <2026033116-possibly-reference-7ee1@gregkh>
Hi Greg,
Thanks for the reminder. I will make sure to test-build my changes
before submitting patches next time.
Best regards,
Hungyu Lin
On Tue, Mar 31, 2026 at 1:45 AM Greg Kroah-Hartman
<gregkh@linuxfoundation.org> wrote:
>
> On Tue, Mar 31, 2026 at 05:07:38AM +0000, Hungyu Lin wrote:
> > The static const char * arrays 'g_fbmode' and 'fix_id' should be
> > defined as 'static const char * const' to make the pointer arrays
> > themselves constant. This allows the compiler to place them in the
> > read-only data section.
> >
> > Signed-off-by: Hungyu Lin <dennylin0707@gmail.com>
> > ---
> > drivers/staging/sm750fb/sm750.c | 4 ++--
> > 1 file changed, 2 insertions(+), 2 deletions(-)
> >
> > diff --git a/drivers/staging/sm750fb/sm750.c b/drivers/staging/sm750fb/sm750.c
> > index 9a42a08c8..b0bdfaeca 100644
> > --- a/drivers/staging/sm750fb/sm750.c
> > +++ b/drivers/staging/sm750fb/sm750.c
> > @@ -33,7 +33,7 @@
> > static int g_hwcursor = 1;
> > static int g_noaccel;
> > static int g_nomtrr;
> > -static const char *g_fbmode[] = {NULL, NULL};
> > +static const char * const g_fbmode[] = {NULL, NULL};
> > static const char *g_def_fbmode = "1024x768-32@60";
> > static char *g_settings;
> > static int g_dualview;
> > @@ -728,7 +728,7 @@ static int lynxfb_set_fbinfo(struct fb_info *info, int index)
> > lynx750_ext, NULL, vesa_modes,
> > };
> > int cdb[] = {ARRAY_SIZE(lynx750_ext), 0, VESA_MODEDB_SIZE};
> > - static const char *fix_id[2] = {
> > + static const char * const fix_id[2] = {
> > "sm750_fb1", "sm750_fb2",
> > };
> >
> > --
> > 2.34.1
> >
> >
>
> Please always test-build your changes so you do not get grumpy kernel
> maintainers asking you why you did not test-build your changes :(
>
> thanks,
>
> greg k-h
^ permalink raw reply
* [PATCH] staging: sm750fb: constify fix_id array
From: Hungyu Lin @ 2026-03-31 13:37 UTC (permalink / raw)
To: Sudip Mukherjee, Teddy Wang, Greg Kroah-Hartman
Cc: linux-fbdev, linux-staging, linux-kernel, Hungyu Lin
In-Reply-To: <20260331050738.1547-1-dennylin0707@gmail.com>
Make the static fix_id array const-qualified so it can be placed
in read-only memory.
Signed-off-by: Hungyu Lin <dennylin0707@gmail.com>
---
drivers/staging/sm750fb/sm750.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/staging/sm750fb/sm750.c b/drivers/staging/sm750fb/sm750.c
index 62f6e0cdf..795e9164b 100644
--- a/drivers/staging/sm750fb/sm750.c
+++ b/drivers/staging/sm750fb/sm750.c
@@ -740,7 +740,7 @@ static int lynxfb_set_fbinfo(struct fb_info *info, int index)
"kernel HELPERS prepared vesa_modes",
};
- static const char *fix_id[2] = {
+ static const char * const fix_id[2] = {
"sm750_fb1", "sm750_fb2",
};
--
2.34.1
^ permalink raw reply related
* [PATCH v2] staging: sm750fb: constify fix_id array
From: Hungyu Lin @ 2026-03-31 13:43 UTC (permalink / raw)
To: Sudip Mukherjee, Teddy Wang, Greg Kroah-Hartman
Cc: linux-fbdev, linux-staging, linux-kernel, Hungyu Lin
In-Reply-To: <20260331050738.1547-1-dennylin0707@gmail.com>
Make the static fix_id array const-qualified so it can be placed
in read-only memory.
Signed-off-by: Hungyu Lin <dennylin0707@gmail.com>
---
drivers/staging/sm750fb/sm750.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/staging/sm750fb/sm750.c b/drivers/staging/sm750fb/sm750.c
index 62f6e0cdf..795e9164b 100644
--- a/drivers/staging/sm750fb/sm750.c
+++ b/drivers/staging/sm750fb/sm750.c
@@ -740,7 +740,7 @@ static int lynxfb_set_fbinfo(struct fb_info *info, int index)
"kernel HELPERS prepared vesa_modes",
};
- static const char *fix_id[2] = {
+ static const char * const fix_id[2] = {
"sm750_fb1", "sm750_fb2",
};
--
2.34.1
^ permalink raw reply related
* Re: [PATCH v2] staging: sm750fb: constify fix_id array
From: Greg Kroah-Hartman @ 2026-03-31 13:50 UTC (permalink / raw)
To: Hungyu Lin
Cc: Sudip Mukherjee, Teddy Wang, linux-fbdev, linux-staging,
linux-kernel
In-Reply-To: <20260331134349.18998-1-dennylin0707@gmail.com>
On Tue, Mar 31, 2026 at 01:43:49PM +0000, Hungyu Lin wrote:
> Make the static fix_id array const-qualified so it can be placed
> in read-only memory.
>
> Signed-off-by: Hungyu Lin <dennylin0707@gmail.com>
> ---
> drivers/staging/sm750fb/sm750.c | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/drivers/staging/sm750fb/sm750.c b/drivers/staging/sm750fb/sm750.c
> index 62f6e0cdf..795e9164b 100644
> --- a/drivers/staging/sm750fb/sm750.c
> +++ b/drivers/staging/sm750fb/sm750.c
> @@ -740,7 +740,7 @@ static int lynxfb_set_fbinfo(struct fb_info *info, int index)
> "kernel HELPERS prepared vesa_modes",
> };
>
> - static const char *fix_id[2] = {
> + static const char * const fix_id[2] = {
> "sm750_fb1", "sm750_fb2",
> };
>
> --
> 2.34.1
>
>
Hi,
This is the friendly patch-bot of Greg Kroah-Hartman. You have sent him
a patch that has triggered this response. He used to manually respond
to these common problems, but in order to save his sanity (he kept
writing the same thing over and over, yet to different people), I was
created. Hopefully you will not take offence and will fix the problem
in your patch and resubmit it so that it can be accepted into the Linux
kernel tree.
You are receiving this message because of the following common error(s)
as indicated below:
- This looks like a new version of a previously submitted patch, but you
did not list below the --- line any changes from the previous version.
Please read the section entitled "The canonical patch format" in the
kernel file, Documentation/process/submitting-patches.rst for what
needs to be done here to properly describe this.
If you wish to discuss this problem further, or you have questions about
how to resolve this issue, please feel free to respond to this email and
Greg will reply once he has dug out from the pending patches received
from other developers.
thanks,
greg k-h's patch email bot
^ permalink raw reply
* [PATCH v2] staging: sm750fb: constify fix_id array
From: Hungyu Lin @ 2026-03-31 13:57 UTC (permalink / raw)
To: Sudip Mukherjee, Teddy Wang, Greg Kroah-Hartman
Cc: linux-fbdev, linux-staging, linux-kernel, Hungyu Lin
In-Reply-To: <20260331050738.1547-1-dennylin0707@gmail.com>
Constify the static fix_id array so it can be placed in read-only memory.
Signed-off-by: Hungyu Lin <dennylin0707@gmail.com>
---
Changes in v2:
- Drop g_fbmode change as it is modified at runtime.
---
drivers/staging/sm750fb/sm750.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/staging/sm750fb/sm750.c b/drivers/staging/sm750fb/sm750.c
index 62f6e0cdf..795e9164b 100644
--- a/drivers/staging/sm750fb/sm750.c
+++ b/drivers/staging/sm750fb/sm750.c
@@ -740,7 +740,7 @@ static int lynxfb_set_fbinfo(struct fb_info *info, int index)
"kernel HELPERS prepared vesa_modes",
};
- static const char *fix_id[2] = {
+ static const char * const fix_id[2] = {
"sm750_fb1", "sm750_fb2",
};
--
2.34.1
^ permalink raw reply related
* Re: [PATCH] staging: sm750fb: constify static char pointer arrays
From: kernel test robot @ 2026-03-31 14:33 UTC (permalink / raw)
To: Hungyu Lin, Sudip Mukherjee, Teddy Wang, Greg Kroah-Hartman
Cc: llvm, oe-kbuild-all, linux-fbdev, linux-staging, linux-kernel,
Hungyu Lin
In-Reply-To: <20260331050738.1547-1-dennylin0707@gmail.com>
Hi Hungyu,
kernel test robot noticed the following build errors:
[auto build test ERROR on staging/staging-testing]
url: https://github.com/intel-lab-lkp/linux/commits/Hungyu-Lin/staging-sm750fb-constify-static-char-pointer-arrays/20260331-152633
base: staging/staging-testing
patch link: https://lore.kernel.org/r/20260331050738.1547-1-dennylin0707%40gmail.com
patch subject: [PATCH] staging: sm750fb: constify static char pointer arrays
config: i386-buildonly-randconfig-004-20260331 (https://download.01.org/0day-ci/archive/20260331/202603312237.JBEuEw74-lkp@intel.com/config)
compiler: clang version 20.1.8 (https://github.com/llvm/llvm-project 87f0227cb60147a26a1eeb4fb06e3b505e9c7261)
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20260331/202603312237.JBEuEw74-lkp@intel.com/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202603312237.JBEuEw74-lkp@intel.com/
All errors (new ones prefixed by >>):
>> drivers/staging/sm750fb/sm750.c:782:19: error: cannot assign to variable 'g_fbmode' with const-qualified type 'const char *const[2]'
782 | g_fbmode[index] = g_def_fbmode;
| ~~~~~~~~~~~~~~~ ^
drivers/staging/sm750fb/sm750.c:36:27: note: variable 'g_fbmode' declared const here
36 | static const char * const g_fbmode[] = {NULL, NULL};
| ~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~
drivers/staging/sm750fb/sm750.c:784:20: error: cannot assign to variable 'g_fbmode' with const-qualified type 'const char *const[2]'
784 | g_fbmode[index] = g_fbmode[0];
| ~~~~~~~~~~~~~~~ ^
drivers/staging/sm750fb/sm750.c:36:27: note: variable 'g_fbmode' declared const here
36 | static const char * const g_fbmode[] = {NULL, NULL};
| ~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~
drivers/staging/sm750fb/sm750.c:893:17: error: cannot assign to variable 'g_fbmode' with const-qualified type 'const char *const[2]'
893 | g_fbmode[0] = opt;
| ~~~~~~~~~~~ ^
drivers/staging/sm750fb/sm750.c:36:27: note: variable 'g_fbmode' declared const here
36 | static const char * const g_fbmode[] = {NULL, NULL};
| ~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~
drivers/staging/sm750fb/sm750.c:897:17: error: cannot assign to variable 'g_fbmode' with const-qualified type 'const char *const[2]'
897 | g_fbmode[1] = opt;
| ~~~~~~~~~~~ ^
drivers/staging/sm750fb/sm750.c:36:27: note: variable 'g_fbmode' declared const here
36 | static const char * const g_fbmode[] = {NULL, NULL};
| ~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~
4 errors generated.
vim +782 drivers/staging/sm750fb/sm750.c
81dee67e215b23 Sudip Mukherjee 2015-03-03 716
81dee67e215b23 Sudip Mukherjee 2015-03-03 717 static int lynxfb_set_fbinfo(struct fb_info *info, int index)
81dee67e215b23 Sudip Mukherjee 2015-03-03 718 {
81dee67e215b23 Sudip Mukherjee 2015-03-03 719 int i;
81dee67e215b23 Sudip Mukherjee 2015-03-03 720 struct lynxfb_par *par;
e359b6a863e19f Mike Rapoport 2015-10-26 721 struct sm750_dev *sm750_dev;
81dee67e215b23 Sudip Mukherjee 2015-03-03 722 struct lynxfb_crtc *crtc;
81dee67e215b23 Sudip Mukherjee 2015-03-03 723 struct lynxfb_output *output;
81dee67e215b23 Sudip Mukherjee 2015-03-03 724 struct fb_var_screeninfo *var;
81dee67e215b23 Sudip Mukherjee 2015-03-03 725 struct fb_fix_screeninfo *fix;
81dee67e215b23 Sudip Mukherjee 2015-03-03 726
81dee67e215b23 Sudip Mukherjee 2015-03-03 727 const struct fb_videomode *pdb[] = {
81dee67e215b23 Sudip Mukherjee 2015-03-03 728 lynx750_ext, NULL, vesa_modes,
81dee67e215b23 Sudip Mukherjee 2015-03-03 729 };
81dee67e215b23 Sudip Mukherjee 2015-03-03 730 int cdb[] = {ARRAY_SIZE(lynx750_ext), 0, VESA_MODEDB_SIZE};
218d4cc450cea4 Hungyu Lin 2026-03-31 731 static const char * const fix_id[2] = {
81dee67e215b23 Sudip Mukherjee 2015-03-03 732 "sm750_fb1", "sm750_fb2",
81dee67e215b23 Sudip Mukherjee 2015-03-03 733 };
81dee67e215b23 Sudip Mukherjee 2015-03-03 734
81dee67e215b23 Sudip Mukherjee 2015-03-03 735 int ret, line_length;
81dee67e215b23 Sudip Mukherjee 2015-03-03 736
81dee67e215b23 Sudip Mukherjee 2015-03-03 737 ret = 0;
81dee67e215b23 Sudip Mukherjee 2015-03-03 738 par = (struct lynxfb_par *)info->par;
e359b6a863e19f Mike Rapoport 2015-10-26 739 sm750_dev = par->dev;
81dee67e215b23 Sudip Mukherjee 2015-03-03 740 crtc = &par->crtc;
81dee67e215b23 Sudip Mukherjee 2015-03-03 741 output = &par->output;
81dee67e215b23 Sudip Mukherjee 2015-03-03 742 var = &info->var;
81dee67e215b23 Sudip Mukherjee 2015-03-03 743 fix = &info->fix;
81dee67e215b23 Sudip Mukherjee 2015-03-03 744
81dee67e215b23 Sudip Mukherjee 2015-03-03 745 /* set index */
81dee67e215b23 Sudip Mukherjee 2015-03-03 746 par->index = index;
81dee67e215b23 Sudip Mukherjee 2015-03-03 747 output->channel = &crtc->channel;
81dee67e215b23 Sudip Mukherjee 2015-03-03 748 sm750fb_set_drv(par);
81dee67e215b23 Sudip Mukherjee 2015-03-03 749
d11ac7cbcc266c Sudip Mukherjee 2015-08-07 750 /*
d11ac7cbcc266c Sudip Mukherjee 2015-08-07 751 * set current cursor variable and proc pointer,
d11ac7cbcc266c Sudip Mukherjee 2015-08-07 752 * must be set after crtc member initialized
d11ac7cbcc266c Sudip Mukherjee 2015-08-07 753 */
fdc234d85210d9 Benjamin Philip 2021-07-28 754 crtc->cursor.offset = crtc->o_screen + crtc->vidmem_size - 1024;
e359b6a863e19f Mike Rapoport 2015-10-26 755 crtc->cursor.mmio = sm750_dev->pvReg +
e359b6a863e19f Mike Rapoport 2015-10-26 756 0x800f0 + (int)crtc->channel * 0x140;
81dee67e215b23 Sudip Mukherjee 2015-03-03 757
cd33da26036ea5 Christopher Carbone 2022-08-23 758 crtc->cursor.max_h = 64;
cd33da26036ea5 Christopher Carbone 2022-08-23 759 crtc->cursor.max_w = 64;
39f9137268ee3d Benjamin Philip 2021-07-26 760 crtc->cursor.size = crtc->cursor.max_h * crtc->cursor.max_w * 2 / 8;
e359b6a863e19f Mike Rapoport 2015-10-26 761 crtc->cursor.vstart = sm750_dev->pvMem + crtc->cursor.offset;
81dee67e215b23 Sudip Mukherjee 2015-03-03 762
3de08a2d14ff8c Lorenzo Stoakes 2015-03-20 763 memset_io(crtc->cursor.vstart, 0, crtc->cursor.size);
f7c8a046577e09 Thomas Zimmermann 2023-11-27 764 if (!g_hwcursor)
52d0744d751d8f Arnd Bergmann 2016-11-09 765 sm750_hw_cursor_disable(&crtc->cursor);
81dee67e215b23 Sudip Mukherjee 2015-03-03 766
81dee67e215b23 Sudip Mukherjee 2015-03-03 767 /* set info->fbops, must be set before fb_find_mode */
e359b6a863e19f Mike Rapoport 2015-10-26 768 if (!sm750_dev->accel_off) {
81dee67e215b23 Sudip Mukherjee 2015-03-03 769 /* use 2d acceleration */
f7c8a046577e09 Thomas Zimmermann 2023-11-27 770 if (!g_hwcursor)
f7c8a046577e09 Thomas Zimmermann 2023-11-27 771 info->fbops = &lynxfb_ops_accel;
f7c8a046577e09 Thomas Zimmermann 2023-11-27 772 else
f7c8a046577e09 Thomas Zimmermann 2023-11-27 773 info->fbops = &lynxfb_ops_accel_with_cursor;
f7c8a046577e09 Thomas Zimmermann 2023-11-27 774 } else {
f7c8a046577e09 Thomas Zimmermann 2023-11-27 775 if (!g_hwcursor)
81dee67e215b23 Sudip Mukherjee 2015-03-03 776 info->fbops = &lynxfb_ops;
f7c8a046577e09 Thomas Zimmermann 2023-11-27 777 else
f7c8a046577e09 Thomas Zimmermann 2023-11-27 778 info->fbops = &lynxfb_ops_with_cursor;
f7c8a046577e09 Thomas Zimmermann 2023-11-27 779 }
81dee67e215b23 Sudip Mukherjee 2015-03-03 780
81dee67e215b23 Sudip Mukherjee 2015-03-03 781 if (!g_fbmode[index]) {
81dee67e215b23 Sudip Mukherjee 2015-03-03 @782 g_fbmode[index] = g_def_fbmode;
81dee67e215b23 Sudip Mukherjee 2015-03-03 783 if (index)
81dee67e215b23 Sudip Mukherjee 2015-03-03 784 g_fbmode[index] = g_fbmode[0];
81dee67e215b23 Sudip Mukherjee 2015-03-03 785 }
81dee67e215b23 Sudip Mukherjee 2015-03-03 786
81dee67e215b23 Sudip Mukherjee 2015-03-03 787 for (i = 0; i < 3; i++) {
81dee67e215b23 Sudip Mukherjee 2015-03-03 788 ret = fb_find_mode(var, info, g_fbmode[index],
81dee67e215b23 Sudip Mukherjee 2015-03-03 789 pdb[i], cdb[i], NULL, 8);
81dee67e215b23 Sudip Mukherjee 2015-03-03 790
db7fb3588ab492 Artem Lytkin 2026-02-23 791 if (ret == 1 || ret == 2)
81dee67e215b23 Sudip Mukherjee 2015-03-03 792 break;
81dee67e215b23 Sudip Mukherjee 2015-03-03 793 }
81dee67e215b23 Sudip Mukherjee 2015-03-03 794
81dee67e215b23 Sudip Mukherjee 2015-03-03 795 /* set par */
81dee67e215b23 Sudip Mukherjee 2015-03-03 796 par->info = info;
81dee67e215b23 Sudip Mukherjee 2015-03-03 797
81dee67e215b23 Sudip Mukherjee 2015-03-03 798 /* set info */
e3a3f9f5123683 Mike Rapoport 2015-10-26 799 line_length = ALIGN((var->xres_virtual * var->bits_per_pixel / 8),
e3a3f9f5123683 Mike Rapoport 2015-10-26 800 crtc->line_pad);
81dee67e215b23 Sudip Mukherjee 2015-03-03 801
81dee67e215b23 Sudip Mukherjee 2015-03-03 802 info->pseudo_palette = &par->pseudo_palette[0];
cc59bde1c920ab Benjamin Philip 2021-07-28 803 info->screen_base = crtc->v_screen;
81dee67e215b23 Sudip Mukherjee 2015-03-03 804 info->screen_size = line_length * var->yres_virtual;
81dee67e215b23 Sudip Mukherjee 2015-03-03 805
81dee67e215b23 Sudip Mukherjee 2015-03-03 806 /* set info->fix */
81dee67e215b23 Sudip Mukherjee 2015-03-03 807 fix->type = FB_TYPE_PACKED_PIXELS;
81dee67e215b23 Sudip Mukherjee 2015-03-03 808 fix->type_aux = 0;
81dee67e215b23 Sudip Mukherjee 2015-03-03 809 fix->xpanstep = crtc->xpanstep;
81dee67e215b23 Sudip Mukherjee 2015-03-03 810 fix->ypanstep = crtc->ypanstep;
81dee67e215b23 Sudip Mukherjee 2015-03-03 811 fix->ywrapstep = crtc->ywrapstep;
81dee67e215b23 Sudip Mukherjee 2015-03-03 812 fix->accel = FB_ACCEL_SMI;
81dee67e215b23 Sudip Mukherjee 2015-03-03 813
8c475735085a7d Tim Wassink 2025-12-21 814 strscpy(fix->id, fix_id[index], sizeof(fix->id));
81dee67e215b23 Sudip Mukherjee 2015-03-03 815
fdc234d85210d9 Benjamin Philip 2021-07-28 816 fix->smem_start = crtc->o_screen + sm750_dev->vidmem_start;
d11ac7cbcc266c Sudip Mukherjee 2015-08-07 817 /*
d11ac7cbcc266c Sudip Mukherjee 2015-08-07 818 * according to mmap experiment from user space application,
81dee67e215b23 Sudip Mukherjee 2015-03-03 819 * fix->mmio_len should not larger than virtual size
81dee67e215b23 Sudip Mukherjee 2015-03-03 820 * (xres_virtual x yres_virtual x ByPP)
81dee67e215b23 Sudip Mukherjee 2015-03-03 821 * Below line maybe buggy when user mmap fb dev node and write
81dee67e215b23 Sudip Mukherjee 2015-03-03 822 * data into the bound over virtual size
d11ac7cbcc266c Sudip Mukherjee 2015-08-07 823 */
81dee67e215b23 Sudip Mukherjee 2015-03-03 824 fix->smem_len = crtc->vidmem_size;
81dee67e215b23 Sudip Mukherjee 2015-03-03 825 info->screen_size = fix->smem_len;
81dee67e215b23 Sudip Mukherjee 2015-03-03 826 fix->line_length = line_length;
e359b6a863e19f Mike Rapoport 2015-10-26 827 fix->mmio_start = sm750_dev->vidreg_start;
e359b6a863e19f Mike Rapoport 2015-10-26 828 fix->mmio_len = sm750_dev->vidreg_size;
b610e1193a917f Matej Dujava 2020-04-30 829
b610e1193a917f Matej Dujava 2020-04-30 830 lynxfb_set_visual_mode(info);
81dee67e215b23 Sudip Mukherjee 2015-03-03 831
81dee67e215b23 Sudip Mukherjee 2015-03-03 832 /* set var */
81dee67e215b23 Sudip Mukherjee 2015-03-03 833 var->activate = FB_ACTIVATE_NOW;
81dee67e215b23 Sudip Mukherjee 2015-03-03 834 var->accel_flags = 0;
81dee67e215b23 Sudip Mukherjee 2015-03-03 835 var->vmode = FB_VMODE_NONINTERLACED;
81dee67e215b23 Sudip Mukherjee 2015-03-03 836
61c507cf652da1 Michel von Czettritz 2015-03-26 837 ret = fb_alloc_cmap(&info->cmap, 256, 0);
61c507cf652da1 Michel von Czettritz 2015-03-26 838 if (ret < 0) {
fbab250eb51d6d Artem Lytkin 2026-02-07 839 dev_err(info->device, "Could not allocate memory for cmap.\n");
81dee67e215b23 Sudip Mukherjee 2015-03-03 840 goto exit;
81dee67e215b23 Sudip Mukherjee 2015-03-03 841 }
81dee67e215b23 Sudip Mukherjee 2015-03-03 842
81dee67e215b23 Sudip Mukherjee 2015-03-03 843 exit:
81dee67e215b23 Sudip Mukherjee 2015-03-03 844 lynxfb_ops_check_var(var, info);
81dee67e215b23 Sudip Mukherjee 2015-03-03 845 return ret;
81dee67e215b23 Sudip Mukherjee 2015-03-03 846 }
81dee67e215b23 Sudip Mukherjee 2015-03-03 847
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
^ permalink raw reply
* Re: [PATCH] fbdev: atyfb: Remove unused fb_list
From: Helge Deller @ 2026-03-31 14:40 UTC (permalink / raw)
To: Geert Uytterhoeven
Cc: linux-fbdev, dri-devel, linux-kernel, kernel test robot
In-Reply-To: <571a3e072a2eef5a587d768d74559fc549b03ab6.1774863796.git.geert@linux-m68k.org>
On 3/30/26 11:44, Geert Uytterhoeven wrote:
> With clang and W=1:
>
> drivers/video/fbdev/aty/atyfb_base.c:2327:24: warning: variable 'fb_list' set but not used [-Wunused-but-set-global]
> 2327 | static struct fb_info *fb_list = NULL;
>
> Indeed, the last user of fb_list was removed in 2004, while the actual
> linked list was removed in 2002.
>
> Reported-by: kernel test robot <lkp@intel.com>
> Closes: https://lore.kernel.org/oe-kbuild-all/202603300931.osMYxYZ7-lkp@intel.com/
> Signed-off-by: Geert Uytterhoeven <geert@linux-m68k.org>
> ---
> drivers/video/fbdev/aty/atyfb_base.c | 4 ----
> 1 file changed, 4 deletions(-)
applied.
Thanks!
Helge
^ permalink raw reply
* Re: [PATCH 00/10] fbcon,fonts: Refactor framebuffer console rotation
From: Helge Deller @ 2026-03-31 15:08 UTC (permalink / raw)
To: Thomas Zimmermann, gregkh, jirislaby, simona, sam
Cc: linux-fbdev, dri-devel, linux-kernel, linux-serial
In-Reply-To: <20260327130431.59481-1-tzimmermann@suse.de>
Hi Thomas,
On 3/27/26 13:49, Thomas Zimmermann wrote:
> Refactor the framebuffer console rotation into individual components
> for glyphs, fonts and the overall fbcon state. Right now this is mixed
> up in fbcon_rotate.{c,h}. Also build cursor rotation on top of the new
> interfaces.
>
> Start with an OOB fix in patch 1. If buffer allocation fails, fbcon
> currently uses a too-small glyph buffer for output. Avoid that.
>
> Patches 2 to 4 make a number of small improvements to the font library
> and its callers.
>
> Patches 5 to 8 refactor the font rotation. Fbcon rotation rotates each
> individual glphy in a font buffer and uses the rotated buffer's glyphs
> for output. The result looks like the console buffer has been rotated
> as a whole. Split this into helpers that rotate individual glyphs and
> a helper that rotates the font buffer of these. Then reimplement fbcon
> rotation on top. Document the public font helpers.
>
> Patch 9 rebuilds cursor rotation on top of the new glyph helpers. The
> fbcon cursor is itself a glyph that has to be rotated in sync with the
> font.
>
> Patch 10 moves the state of fbcon rotation into a single place and makes
> is a build-time conditional.
>
> Tested with fbcon under bochs on Qemu.
>
> Built upon the fbcon changes at [1].
>
> [1] https://lore.kernel.org/linux-fbdev/20260309141723.137364-1-tzimmermann@suse.de/
Thanks a lot for this cleanup work!
I've applied this series to the fbdev git tree.
Helge
> Thomas Zimmermann (10):
> fbcon: Avoid OOB font access if console rotation fails
> vt: Implement helpers for struct vc_font in source file
> lib/fonts: Provide helpers for calculating glyph pitch and size
> lib/fonts: Clean up Makefile
> lib/fonts: Implement glyph rotation
> lib/fonts: Refactor glyph-pattern helpers
> lib/fonts: Refactor glyph-rotation helpers
> lib/fonts: Implement font rotation
> fbcon: Fill cursor mask in helper function
> fbcon: Put font-rotation state into separate struct
>
> drivers/tty/vt/vt.c | 34 +++
> drivers/video/fbdev/core/bitblit.c | 35 +--
> drivers/video/fbdev/core/fbcon.c | 48 ++++-
> drivers/video/fbdev/core/fbcon.h | 14 +-
> drivers/video/fbdev/core/fbcon_ccw.c | 70 ++----
> drivers/video/fbdev/core/fbcon_cw.c | 70 ++----
> drivers/video/fbdev/core/fbcon_rotate.c | 88 ++------
> drivers/video/fbdev/core/fbcon_rotate.h | 71 ------
> drivers/video/fbdev/core/fbcon_ud.c | 67 ++----
> include/linux/console_struct.h | 30 +--
> include/linux/font.h | 51 +++++
> lib/fonts/Makefile | 36 ++--
> lib/fonts/font_rotate.c | 275 ++++++++++++++++++++++++
> lib/fonts/fonts.c | 2 +-
> 14 files changed, 525 insertions(+), 366 deletions(-)
> create mode 100644 lib/fonts/font_rotate.c
>
^ permalink raw reply
* Re: [PATCH 00/10] fbcon,fonts: Refactor framebuffer console rotation
From: Thomas Zimmermann @ 2026-03-31 15:29 UTC (permalink / raw)
To: Helge Deller, gregkh, jirislaby, simona, sam
Cc: linux-fbdev, dri-devel, linux-kernel, linux-serial
In-Reply-To: <7c963dce-7b39-4047-b0bb-548957852d65@gmx.de>
Hi
Am 31.03.26 um 17:08 schrieb Helge Deller:
> Hi Thomas,
>
> On 3/27/26 13:49, Thomas Zimmermann wrote:
>> Refactor the framebuffer console rotation into individual components
>> for glyphs, fonts and the overall fbcon state. Right now this is mixed
>> up in fbcon_rotate.{c,h}. Also build cursor rotation on top of the new
>> interfaces.
>>
>> Start with an OOB fix in patch 1. If buffer allocation fails, fbcon
>> currently uses a too-small glyph buffer for output. Avoid that.
>>
>> Patches 2 to 4 make a number of small improvements to the font library
>> and its callers.
>>
>> Patches 5 to 8 refactor the font rotation. Fbcon rotation rotates each
>> individual glphy in a font buffer and uses the rotated buffer's glyphs
>> for output. The result looks like the console buffer has been rotated
>> as a whole. Split this into helpers that rotate individual glyphs and
>> a helper that rotates the font buffer of these. Then reimplement fbcon
>> rotation on top. Document the public font helpers.
>>
>> Patch 9 rebuilds cursor rotation on top of the new glyph helpers. The
>> fbcon cursor is itself a glyph that has to be rotated in sync with the
>> font.
>>
>> Patch 10 moves the state of fbcon rotation into a single place and makes
>> is a build-time conditional.
>>
>> Tested with fbcon under bochs on Qemu.
>>
>> Built upon the fbcon changes at [1].
>>
>> [1]
>> https://lore.kernel.org/linux-fbdev/20260309141723.137364-1-tzimmermann@suse.de/
>
>
> Thanks a lot for this cleanup work!
>
> I've applied this series to the fbdev git tree.
Thanks a lot.
There's a small typo in patch 2 that I noticed when Greg gave his ack.
The commit description say <liux/math.h> instead of <linux/math.h>. Let
me know whether you can fix it or if I should send an update.
Best regards
Thomas
>
> Helge
>
>
>> Thomas Zimmermann (10):
>> fbcon: Avoid OOB font access if console rotation fails
>> vt: Implement helpers for struct vc_font in source file
>> lib/fonts: Provide helpers for calculating glyph pitch and size
>> lib/fonts: Clean up Makefile
>> lib/fonts: Implement glyph rotation
>> lib/fonts: Refactor glyph-pattern helpers
>> lib/fonts: Refactor glyph-rotation helpers
>> lib/fonts: Implement font rotation
>> fbcon: Fill cursor mask in helper function
>> fbcon: Put font-rotation state into separate struct
>>
>> drivers/tty/vt/vt.c | 34 +++
>> drivers/video/fbdev/core/bitblit.c | 35 +--
>> drivers/video/fbdev/core/fbcon.c | 48 ++++-
>> drivers/video/fbdev/core/fbcon.h | 14 +-
>> drivers/video/fbdev/core/fbcon_ccw.c | 70 ++----
>> drivers/video/fbdev/core/fbcon_cw.c | 70 ++----
>> drivers/video/fbdev/core/fbcon_rotate.c | 88 ++------
>> drivers/video/fbdev/core/fbcon_rotate.h | 71 ------
>> drivers/video/fbdev/core/fbcon_ud.c | 67 ++----
>> include/linux/console_struct.h | 30 +--
>> include/linux/font.h | 51 +++++
>> lib/fonts/Makefile | 36 ++--
>> lib/fonts/font_rotate.c | 275 ++++++++++++++++++++++++
>> lib/fonts/fonts.c | 2 +-
>> 14 files changed, 525 insertions(+), 366 deletions(-)
>> create mode 100644 lib/fonts/font_rotate.c
>>
>
--
--
Thomas Zimmermann
Graphics Driver Developer
SUSE Software Solutions Germany GmbH
Frankenstr. 146, 90461 Nürnberg, Germany, www.suse.com
GF: Jochen Jaser, Andrew McDonald, Werner Knoblich, (HRB 36809, AG Nürnberg)
^ permalink raw reply
* [PATCH v10 00/21] gpu: nova-core: Add memory management support
From: Joel Fernandes @ 2026-03-31 21:20 UTC (permalink / raw)
To: linux-kernel
Cc: Miguel Ojeda, Boqun Feng, Gary Guo, Bjorn Roy Baron, Benno Lossin,
Andreas Hindborg, Alice Ryhl, Trevor Gross, Danilo Krummrich,
Dave Airlie, Daniel Almeida, Koen Koning, dri-devel,
rust-for-linux, Nikola Djukic, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Jonathan Corbet,
Alex Deucher, Christian Koenig, Jani Nikula, Joonas Lahtinen,
Rodrigo Vivi, Tvrtko Ursulin, Huang Rui, Matthew Auld,
Matthew Brost, Lucas De Marchi, Thomas Hellstrom, Helge Deller,
Alex Gaynor, Boqun Feng, John Hubbard, Alistair Popple,
Timur Tabi, Edwin Peer, Alexandre Courbot, Andrea Righi,
Andy Ritger, Zhi Wang, Balbir Singh, Philipp Stanner,
Elle Rhumsaa, alexeyi, Eliot Courtney, joel, linux-doc, amd-gfx,
intel-gfx, intel-xe, linux-fbdev, Joel Fernandes
In-Reply-To: <20260311004008.2208806-1-joelagnelf@nvidia.com>
This series adds support for nova-core memory management, including VRAM
allocation, PRAMIN, VMM, page table walking, and BAR 1 read/writes.
These are critical for channel management, vGPU, and all other memory
management uses of nova-core.
The patches are based on drm-rust-next and work on Ampere, and should "just
work on Blackwell" once John's Blackwell patches are merged, however it does
not depend on those patches and can independently go in.
Change log:
Changes from v9 to v10:
- Rebased and dropped patches already merged in to drm-rust-next.
- GPU_BUDDY select folded into GpuMm patch.
- updated code with new register macro API.
- Refactored fb_regions() to use iterator (Alex Courbot).
- Renamed Pramin::window() to get_window() to make it more clear it is 'acquiring a resource'.
- Converted Bar0WindowTarget to bounded_enum! macro, replacing TryFrom. Allows to use `with_*`
instead of `try_with_*`.
Changes from v8 to v9:
- Added fixes from Zhi Wang for bitfield position changes in virtual address
and larger BAR1 size on some platforms. Tested and working for vGPU usecase!
- Refactored gsp: boot() to return only GspStaticInfo, removing FbLayout (Alex).
- bar1_pde_base and bar2_pde_base are now accessed via GspStaticInfo directly (Alex).
- Added new patch "gsp: Expose total physical VRAM end from FB region info"
introducing total_fb_end() to expose VRAM extent (Alex).
- Consolidated usable VRAM and BarUser setup; removed dedicated
"fb: Add usable_vram field to FbLayout", "mm: Use usable VRAM region for
buddy allocator", and "mm: Add BarUser to struct Gpu and create at boot".
Changes from v7 to v8:
- Incorporated "Select GPU_BUDDY for VRAM allocation" patch from the
dependency series (Alex).
- Significant patch reordering for better logical flow (GSP/FB patches
moved earlier, page table patches, Vmm, Bar1, tests) (Alex).
- Replaced several 'as' usages with into_safe_cast() (Danilo, Alex).
- Updated BAR 1 test cases to include exercising the block size API (Eliot, Danilo).
Changes from v6 to v7:
- Addressed DMA fence signalling usecase per Danilo's feedback.
Pre v6:
- Simplified PRAMIN code (John Hubbard, Alex Courbot).
- Handled different MMU versions: ver2 versus ver3 (John Hubbard).
- Added BAR1 usecase so we have user of DRM Buddy / VMM (John Hubbard).
- Iterating over clist/buddy bindings.
Link to v9: https://lore.kernel.org/all/20260311004008.2208806-1-joelagnelf@nvidia.com/
Link to v8: https://lore.kernel.org/all/20260224225323.3312204-1-joelagnelf@nvidia.com/
Link to v7: https://lore.kernel.org/all/20260218212020.800836-1-joelagnelf@nvidia.com/
Joel Fernandes (20):
gpu: nova-core: gsp: Return GspStaticInfo from boot()
gpu: nova-core: gsp: Extract usable FB region from GSP
gpu: nova-core: gsp: Expose total physical VRAM end from FB region
info
gpu: nova-core: mm: Add support to use PRAMIN windows to write to VRAM
docs: gpu: nova-core: Document the PRAMIN aperture mechanism
gpu: nova-core: mm: Add common memory management types
gpu: nova-core: mm: Add TLB flush support
gpu: nova-core: mm: Add GpuMm centralized memory manager
gpu: nova-core: mm: Add common types for all page table formats
gpu: nova-core: mm: Add MMU v2 page table types
gpu: nova-core: mm: Add MMU v3 page table types
gpu: nova-core: mm: Add unified page table entry wrapper enums
gpu: nova-core: mm: Add page table walker for MMU v2/v3
gpu: nova-core: mm: Add Virtual Memory Manager
gpu: nova-core: mm: Add virtual address range tracking to VMM
gpu: nova-core: mm: Add multi-page mapping API to VMM
gpu: nova-core: Add BAR1 aperture type and size constant
gpu: nova-core: mm: Add BAR1 user interface
gpu: nova-core: mm: Add BAR1 memory management self-tests
gpu: nova-core: mm: Add PRAMIN aperture self-tests
Zhi Wang (1):
gpu: nova-core: Use runtime BAR1 size instead of hardcoded 256MB
Documentation/gpu/nova/core/pramin.rst | 123 +++++
Documentation/gpu/nova/index.rst | 1 +
drivers/gpu/nova-core/Kconfig | 11 +
drivers/gpu/nova-core/driver.rs | 3 +
drivers/gpu/nova-core/gpu.rs | 94 +++-
drivers/gpu/nova-core/gsp/boot.rs | 9 +-
drivers/gpu/nova-core/gsp/commands.rs | 18 +-
drivers/gpu/nova-core/gsp/fw/commands.rs | 59 ++-
drivers/gpu/nova-core/mm.rs | 234 ++++++++++
drivers/gpu/nova-core/mm/bar_user.rs | 388 ++++++++++++++++
drivers/gpu/nova-core/mm/pagetable.rs | 489 ++++++++++++++++++++
drivers/gpu/nova-core/mm/pagetable/ver2.rs | 232 ++++++++++
drivers/gpu/nova-core/mm/pagetable/ver3.rs | 337 ++++++++++++++
drivers/gpu/nova-core/mm/pagetable/walk.rs | 218 +++++++++
drivers/gpu/nova-core/mm/pramin.rs | 489 ++++++++++++++++++++
drivers/gpu/nova-core/mm/tlb.rs | 95 ++++
drivers/gpu/nova-core/mm/vmm.rs | 499 +++++++++++++++++++++
drivers/gpu/nova-core/nova_core.rs | 1 +
drivers/gpu/nova-core/regs.rs | 52 +++
19 files changed, 3344 insertions(+), 8 deletions(-)
create mode 100644 Documentation/gpu/nova/core/pramin.rst
create mode 100644 drivers/gpu/nova-core/mm.rs
create mode 100644 drivers/gpu/nova-core/mm/bar_user.rs
create mode 100644 drivers/gpu/nova-core/mm/pagetable.rs
create mode 100644 drivers/gpu/nova-core/mm/pagetable/ver2.rs
create mode 100644 drivers/gpu/nova-core/mm/pagetable/ver3.rs
create mode 100644 drivers/gpu/nova-core/mm/pagetable/walk.rs
create mode 100644 drivers/gpu/nova-core/mm/pramin.rs
create mode 100644 drivers/gpu/nova-core/mm/tlb.rs
create mode 100644 drivers/gpu/nova-core/mm/vmm.rs
base-commit: 76bce7ac51673640a4a46236ea723cf5543268d7
--
2.34.1
^ permalink raw reply
* [PATCH v10 01/21] gpu: nova-core: gsp: Return GspStaticInfo from boot()
From: Joel Fernandes @ 2026-03-31 21:20 UTC (permalink / raw)
To: linux-kernel
Cc: Miguel Ojeda, Boqun Feng, Gary Guo, Bjorn Roy Baron, Benno Lossin,
Andreas Hindborg, Alice Ryhl, Trevor Gross, Danilo Krummrich,
Dave Airlie, Daniel Almeida, Koen Koning, dri-devel,
rust-for-linux, Nikola Djukic, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Jonathan Corbet,
Alex Deucher, Christian Koenig, Jani Nikula, Joonas Lahtinen,
Rodrigo Vivi, Tvrtko Ursulin, Huang Rui, Matthew Auld,
Matthew Brost, Lucas De Marchi, Thomas Hellstrom, Helge Deller,
Alex Gaynor, Boqun Feng, John Hubbard, Alistair Popple,
Timur Tabi, Edwin Peer, Alexandre Courbot, Andrea Righi,
Andy Ritger, Zhi Wang, Balbir Singh, Philipp Stanner,
Elle Rhumsaa, alexeyi, Eliot Courtney, joel, linux-doc, amd-gfx,
intel-gfx, intel-xe, linux-fbdev, Joel Fernandes
In-Reply-To: <20260331212048.2229260-1-joelagnelf@nvidia.com>
Refactor the GSP boot function to return only the GspStaticInfo,
removing the FbLayout from the return tuple.
This enables access required for memory management initialization to:
- bar1_pde_base: BAR1 page directory base.
- bar2_pde_base: BAR2 page directory base.
- usable memory regions in vidmem.
Cc: Nikola Djukic <ndjukic@nvidia.com>
Signed-off-by: Joel Fernandes <joelagnelf@nvidia.com>
---
drivers/gpu/nova-core/gpu.rs | 9 +++++++--
drivers/gpu/nova-core/gsp/boot.rs | 9 ++++++---
2 files changed, 13 insertions(+), 5 deletions(-)
diff --git a/drivers/gpu/nova-core/gpu.rs b/drivers/gpu/nova-core/gpu.rs
index 0f6fe9a1b955..b4da4a1ae156 100644
--- a/drivers/gpu/nova-core/gpu.rs
+++ b/drivers/gpu/nova-core/gpu.rs
@@ -21,7 +21,10 @@
},
fb::SysmemFlush,
gfw,
- gsp::Gsp,
+ gsp::{
+ commands::GetGspStaticInfoReply,
+ Gsp, //
+ },
regs,
};
@@ -238,6 +241,8 @@ pub(crate) struct Gpu {
/// GSP runtime data. Temporarily an empty placeholder.
#[pin]
gsp: Gsp,
+ /// Static GPU information from GSP.
+ gsp_static_info: GetGspStaticInfoReply,
}
impl Gpu {
@@ -269,7 +274,7 @@ pub(crate) fn new<'a>(
gsp <- Gsp::new(pdev),
- _: { gsp.boot(pdev, bar, spec.chipset, gsp_falcon, sec2_falcon)? },
+ gsp_static_info: { gsp.boot(pdev, bar, spec.chipset, gsp_falcon, sec2_falcon)? },
bar: devres_bar,
})
diff --git a/drivers/gpu/nova-core/gsp/boot.rs b/drivers/gpu/nova-core/gsp/boot.rs
index 6f707b3d1a54..d42637db06dd 100644
--- a/drivers/gpu/nova-core/gsp/boot.rs
+++ b/drivers/gpu/nova-core/gsp/boot.rs
@@ -33,7 +33,10 @@
},
gpu::Chipset,
gsp::{
- commands,
+ commands::{
+ self,
+ GetGspStaticInfoReply, //
+ },
sequencer::{
GspSequencer,
GspSequencerParams, //
@@ -145,7 +148,7 @@ pub(crate) fn boot(
chipset: Chipset,
gsp_falcon: &Falcon<Gsp>,
sec2_falcon: &Falcon<Sec2>,
- ) -> Result {
+ ) -> Result<GetGspStaticInfoReply> {
let dev = pdev.as_ref();
let bios = Vbios::new(dev, bar)?;
@@ -235,6 +238,6 @@ pub(crate) fn boot(
Err(e) => dev_warn!(pdev, "GPU name unavailable: {:?}\n", e),
}
- Ok(())
+ Ok(info)
}
}
--
2.34.1
^ permalink raw reply related
* [PATCH v10 02/21] gpu: nova-core: gsp: Extract usable FB region from GSP
From: Joel Fernandes @ 2026-03-31 21:20 UTC (permalink / raw)
To: linux-kernel
Cc: Miguel Ojeda, Boqun Feng, Gary Guo, Bjorn Roy Baron, Benno Lossin,
Andreas Hindborg, Alice Ryhl, Trevor Gross, Danilo Krummrich,
Dave Airlie, Daniel Almeida, Koen Koning, dri-devel,
rust-for-linux, Nikola Djukic, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Jonathan Corbet,
Alex Deucher, Christian Koenig, Jani Nikula, Joonas Lahtinen,
Rodrigo Vivi, Tvrtko Ursulin, Huang Rui, Matthew Auld,
Matthew Brost, Lucas De Marchi, Thomas Hellstrom, Helge Deller,
Alex Gaynor, Boqun Feng, John Hubbard, Alistair Popple,
Timur Tabi, Edwin Peer, Alexandre Courbot, Andrea Righi,
Andy Ritger, Zhi Wang, Balbir Singh, Philipp Stanner,
Elle Rhumsaa, alexeyi, Eliot Courtney, joel, linux-doc, amd-gfx,
intel-gfx, intel-xe, linux-fbdev, Joel Fernandes
In-Reply-To: <20260331212048.2229260-1-joelagnelf@nvidia.com>
Add first_usable_fb_region() to GspStaticConfigInfo to extract the first
usable FB region from GSP's fbRegionInfoParams. Usable regions are those
that are not reserved or protected.
The extracted region is stored in GetGspStaticInfoReply and exposed as
usable_fb_region field for use by the memory subsystem.
Cc: Nikola Djukic <ndjukic@nvidia.com>
Signed-off-by: Joel Fernandes <joelagnelf@nvidia.com>
---
drivers/gpu/nova-core/gsp/commands.rs | 11 ++++--
drivers/gpu/nova-core/gsp/fw/commands.rs | 44 +++++++++++++++++++++++-
2 files changed, 52 insertions(+), 3 deletions(-)
diff --git a/drivers/gpu/nova-core/gsp/commands.rs b/drivers/gpu/nova-core/gsp/commands.rs
index c89c7b57a751..41742c1633c8 100644
--- a/drivers/gpu/nova-core/gsp/commands.rs
+++ b/drivers/gpu/nova-core/gsp/commands.rs
@@ -4,6 +4,7 @@
array,
convert::Infallible,
ffi::FromBytesUntilNulError,
+ ops::Range,
str::Utf8Error, //
};
@@ -189,22 +190,28 @@ fn init(&self) -> impl Init<Self::Command, Self::InitError> {
}
}
-/// The reply from the GSP to the [`GetGspInfo`] command.
+/// The reply from the GSP to the [`GetGspStaticInfo`] command.
pub(crate) struct GetGspStaticInfoReply {
gpu_name: [u8; 64],
+ /// Usable FB (VRAM) region for driver memory allocation.
+ #[expect(dead_code)]
+ pub(crate) usable_fb_region: Range<u64>,
}
impl MessageFromGsp for GetGspStaticInfoReply {
const FUNCTION: MsgFunction = MsgFunction::GetGspStaticInfo;
type Message = GspStaticConfigInfo;
- type InitError = Infallible;
+ type InitError = Error;
fn read(
msg: &Self::Message,
_sbuffer: &mut SBufferIter<array::IntoIter<&[u8], 2>>,
) -> Result<Self, Self::InitError> {
+ let (base, size) = msg.first_usable_fb_region().ok_or(ENODEV)?;
+
Ok(GetGspStaticInfoReply {
gpu_name: msg.gpu_name_str(),
+ usable_fb_region: base..base.saturating_add(size),
})
}
}
diff --git a/drivers/gpu/nova-core/gsp/fw/commands.rs b/drivers/gpu/nova-core/gsp/fw/commands.rs
index db46276430be..9fffa74d03f9 100644
--- a/drivers/gpu/nova-core/gsp/fw/commands.rs
+++ b/drivers/gpu/nova-core/gsp/fw/commands.rs
@@ -10,7 +10,10 @@
}, //
};
-use crate::gsp::GSP_PAGE_SIZE;
+use crate::{
+ gsp::GSP_PAGE_SIZE,
+ num::IntoSafeCast, //
+};
use super::bindings;
@@ -121,6 +124,45 @@ impl GspStaticConfigInfo {
pub(crate) fn gpu_name_str(&self) -> [u8; 64] {
self.0.gpuNameString
}
+
+ /// Returns an iterator over valid FB regions from GSP firmware data.
+ fn fb_regions(
+ &self,
+ ) -> impl Iterator<Item = &bindings::NV2080_CTRL_CMD_FB_GET_FB_REGION_FB_REGION_INFO> {
+ let fb_info = &self.0.fbRegionInfoParams;
+ fb_info
+ .fbRegion
+ .iter()
+ .take(fb_info.numFBRegions.into_safe_cast())
+ .filter(|reg| reg.limit >= reg.base)
+ }
+
+ /// Extracts the first usable FB region from GSP firmware data.
+ ///
+ /// Returns the first region suitable for driver memory allocation as a `(base, size)` tuple.
+ /// Usable regions are those that:
+ /// - Are not reserved for firmware internal use.
+ /// - Are not protected (hardware-enforced access restrictions).
+ /// - Support compression (can use GPU memory compression for bandwidth).
+ /// - Support ISO (isochronous memory for display requiring guaranteed bandwidth).
+ ///
+ /// TODO: Multiple discontinuous usable regions of RAM are possible in
+ /// special cases. We need to support it (to also match Nouveau's behavior).
+ pub(crate) fn first_usable_fb_region(&self) -> Option<(u64, u64)> {
+ self.fb_regions().find_map(|reg| {
+ // Filter: not reserved, not protected, supports compression and ISO.
+ if reg.reserved == 0
+ && reg.bProtected == 0
+ && reg.supportCompressed != 0
+ && reg.supportISO != 0
+ {
+ let size = reg.limit - reg.base + 1;
+ Some((reg.base, size))
+ } else {
+ None
+ }
+ })
+ }
}
// SAFETY: Padding is explicit and will not contain uninitialized data.
--
2.34.1
^ permalink raw reply related
* [PATCH v10 03/21] gpu: nova-core: gsp: Expose total physical VRAM end from FB region info
From: Joel Fernandes @ 2026-03-31 21:20 UTC (permalink / raw)
To: linux-kernel
Cc: Miguel Ojeda, Boqun Feng, Gary Guo, Bjorn Roy Baron, Benno Lossin,
Andreas Hindborg, Alice Ryhl, Trevor Gross, Danilo Krummrich,
Dave Airlie, Daniel Almeida, Koen Koning, dri-devel,
rust-for-linux, Nikola Djukic, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Jonathan Corbet,
Alex Deucher, Christian Koenig, Jani Nikula, Joonas Lahtinen,
Rodrigo Vivi, Tvrtko Ursulin, Huang Rui, Matthew Auld,
Matthew Brost, Lucas De Marchi, Thomas Hellstrom, Helge Deller,
Alex Gaynor, Boqun Feng, John Hubbard, Alistair Popple,
Timur Tabi, Edwin Peer, Alexandre Courbot, Andrea Righi,
Andy Ritger, Zhi Wang, Balbir Singh, Philipp Stanner,
Elle Rhumsaa, alexeyi, Eliot Courtney, joel, linux-doc, amd-gfx,
intel-gfx, intel-xe, linux-fbdev, Joel Fernandes
In-Reply-To: <20260331212048.2229260-1-joelagnelf@nvidia.com>
Add `total_fb_end()` to `GspStaticConfigInfo` that computes the
exclusive end address of the highest valid FB region covering both
usable and GSP-reserved areas.
This allows callers to know the full physical VRAM extent, not just
the allocatable portion.
Signed-off-by: Joel Fernandes <joelagnelf@nvidia.com>
---
drivers/gpu/nova-core/gsp/commands.rs | 6 ++++++
drivers/gpu/nova-core/gsp/fw/commands.rs | 7 +++++++
2 files changed, 13 insertions(+)
diff --git a/drivers/gpu/nova-core/gsp/commands.rs b/drivers/gpu/nova-core/gsp/commands.rs
index 41742c1633c8..5e0649024637 100644
--- a/drivers/gpu/nova-core/gsp/commands.rs
+++ b/drivers/gpu/nova-core/gsp/commands.rs
@@ -196,6 +196,9 @@ pub(crate) struct GetGspStaticInfoReply {
/// Usable FB (VRAM) region for driver memory allocation.
#[expect(dead_code)]
pub(crate) usable_fb_region: Range<u64>,
+ /// End of VRAM.
+ #[expect(dead_code)]
+ pub(crate) total_fb_end: u64,
}
impl MessageFromGsp for GetGspStaticInfoReply {
@@ -209,9 +212,12 @@ fn read(
) -> Result<Self, Self::InitError> {
let (base, size) = msg.first_usable_fb_region().ok_or(ENODEV)?;
+ let total_fb_end = msg.total_fb_end().ok_or(ENODEV)?;
+
Ok(GetGspStaticInfoReply {
gpu_name: msg.gpu_name_str(),
usable_fb_region: base..base.saturating_add(size),
+ total_fb_end,
})
}
}
diff --git a/drivers/gpu/nova-core/gsp/fw/commands.rs b/drivers/gpu/nova-core/gsp/fw/commands.rs
index 9fffa74d03f9..46932d5c8c1d 100644
--- a/drivers/gpu/nova-core/gsp/fw/commands.rs
+++ b/drivers/gpu/nova-core/gsp/fw/commands.rs
@@ -163,6 +163,13 @@ pub(crate) fn first_usable_fb_region(&self) -> Option<(u64, u64)> {
}
})
}
+
+ /// Compute the end of physical VRAM from all FB regions.
+ pub(crate) fn total_fb_end(&self) -> Option<u64> {
+ self.fb_regions()
+ .map(|reg| reg.limit.saturating_add(1))
+ .max()
+ }
}
// SAFETY: Padding is explicit and will not contain uninitialized data.
--
2.34.1
^ permalink raw reply related
* [PATCH v10 04/21] gpu: nova-core: mm: Add support to use PRAMIN windows to write to VRAM
From: Joel Fernandes @ 2026-03-31 21:20 UTC (permalink / raw)
To: linux-kernel
Cc: Miguel Ojeda, Boqun Feng, Gary Guo, Bjorn Roy Baron, Benno Lossin,
Andreas Hindborg, Alice Ryhl, Trevor Gross, Danilo Krummrich,
Dave Airlie, Daniel Almeida, Koen Koning, dri-devel,
rust-for-linux, Nikola Djukic, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Jonathan Corbet,
Alex Deucher, Christian Koenig, Jani Nikula, Joonas Lahtinen,
Rodrigo Vivi, Tvrtko Ursulin, Huang Rui, Matthew Auld,
Matthew Brost, Lucas De Marchi, Thomas Hellstrom, Helge Deller,
Alex Gaynor, Boqun Feng, John Hubbard, Alistair Popple,
Timur Tabi, Edwin Peer, Alexandre Courbot, Andrea Righi,
Andy Ritger, Zhi Wang, Balbir Singh, Philipp Stanner,
Elle Rhumsaa, alexeyi, Eliot Courtney, joel, linux-doc, amd-gfx,
intel-gfx, intel-xe, linux-fbdev, Joel Fernandes
In-Reply-To: <20260331212048.2229260-1-joelagnelf@nvidia.com>
PRAMIN apertures are a crucial mechanism to direct read/write to VRAM.
Add support for the same.
Cc: Nikola Djukic <ndjukic@nvidia.com>
Signed-off-by: Joel Fernandes <joelagnelf@nvidia.com>
---
drivers/gpu/nova-core/mm.rs | 5 +
drivers/gpu/nova-core/mm/pramin.rs | 280 +++++++++++++++++++++++++++++
drivers/gpu/nova-core/nova_core.rs | 1 +
drivers/gpu/nova-core/regs.rs | 10 ++
4 files changed, 296 insertions(+)
create mode 100644 drivers/gpu/nova-core/mm.rs
create mode 100644 drivers/gpu/nova-core/mm/pramin.rs
diff --git a/drivers/gpu/nova-core/mm.rs b/drivers/gpu/nova-core/mm.rs
new file mode 100644
index 000000000000..7a5dd4220c67
--- /dev/null
+++ b/drivers/gpu/nova-core/mm.rs
@@ -0,0 +1,5 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! Memory management subsystems for nova-core.
+
+pub(crate) mod pramin;
diff --git a/drivers/gpu/nova-core/mm/pramin.rs b/drivers/gpu/nova-core/mm/pramin.rs
new file mode 100644
index 000000000000..fde0eb30eaeb
--- /dev/null
+++ b/drivers/gpu/nova-core/mm/pramin.rs
@@ -0,0 +1,280 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! Direct VRAM access through the PRAMIN aperture.
+//!
+//! PRAMIN provides a 1MB sliding window into VRAM through BAR0, allowing the CPU to access
+//! video memory directly. Access is managed through a two-level API:
+//!
+//! - [`Pramin`]: The parent object that owns the BAR0 reference and synchronization lock.
+//! - [`PraminWindow`]: A guard object that holds exclusive PRAMIN access for its lifetime.
+//!
+//! The PRAMIN aperture is a 1MB region at BAR0 + 0x700000 for all GPUs. The window base is
+//! controlled by the `NV_PBUS_BAR0_WINDOW` register and is 64KB aligned.
+//!
+//! # Examples
+//!
+//! ## Basic read/write
+//!
+//! ```no_run
+//! use crate::driver::Bar0;
+//! use crate::mm::pramin;
+//! use kernel::devres::Devres;
+//! use kernel::prelude::*;
+//! use kernel::sync::Arc;
+//!
+//! fn example(devres_bar: Arc<Devres<Bar0>>, vram_region: core::ops::Range<u64>) -> Result<()> {
+//! let pramin = Arc::pin_init(pramin::Pramin::new(devres_bar, vram_region)?, GFP_KERNEL)?;
+//! let mut window = pramin.get_window()?;
+//!
+//! // Write and read back.
+//! window.try_write32(0x100, 0xDEADBEEF)?;
+//! let val = window.try_read32(0x100)?;
+//! assert_eq!(val, 0xDEADBEEF);
+//!
+//! Ok(())
+//! }
+//! ```
+//!
+//! ## Auto-repositioning across VRAM regions
+//!
+//! ```no_run
+//! use crate::driver::Bar0;
+//! use crate::mm::pramin;
+//! use kernel::devres::Devres;
+//! use kernel::prelude::*;
+//! use kernel::sync::Arc;
+//!
+//! fn example(devres_bar: Arc<Devres<Bar0>>, vram_region: core::ops::Range<u64>) -> Result<()> {
+//! let pramin = Arc::pin_init(pramin::Pramin::new(devres_bar, vram_region)?, GFP_KERNEL)?;
+//! let mut window = pramin.get_window()?;
+//!
+//! // Access first 1MB region.
+//! window.try_write32(0x100, 0x11111111)?;
+//!
+//! // Access at 2MB - window auto-repositions.
+//! window.try_write32(0x200000, 0x22222222)?;
+//!
+//! // Back to first region - window repositions again.
+//! let val = window.try_read32(0x100)?;
+//! assert_eq!(val, 0x11111111);
+//!
+//! Ok(())
+//! }
+//! ```
+
+#![expect(unused)]
+
+use core::ops::Range;
+
+use crate::{
+ bounded_enum,
+ driver::Bar0,
+ num::IntoSafeCast,
+ regs, //
+};
+
+use kernel::{
+ devres::Devres,
+ io::Io,
+ new_mutex,
+ num::Bounded,
+ prelude::*,
+ revocable::RevocableGuard,
+ sizes::{
+ SZ_1M,
+ SZ_64K, //
+ },
+ sync::{
+ lock::mutex::MutexGuard,
+ Arc,
+ Mutex, //
+ },
+};
+
+bounded_enum! {
+ /// Target memory type for the BAR0 window register.
+ ///
+ /// Only VRAM is supported; Hopper+ GPUs do not support other targets.
+ #[derive(Debug)]
+ pub(crate) enum Bar0WindowTarget with TryFrom<Bounded<u32, 2>> {
+ /// Video RAM (GPU framebuffer memory).
+ Vram = 0,
+ }
+}
+
+/// PRAMIN aperture base offset in BAR0.
+const PRAMIN_BASE: usize = 0x700000;
+
+/// PRAMIN aperture size (1MB).
+const PRAMIN_SIZE: usize = SZ_1M;
+
+/// Generate a PRAMIN read accessor.
+macro_rules! define_pramin_read {
+ ($name:ident, $ty:ty) => {
+ #[doc = concat!("Read a `", stringify!($ty), "` from VRAM at the given offset.")]
+ pub(crate) fn $name(&mut self, vram_offset: usize) -> Result<$ty> {
+ let (bar_offset, new_base) =
+ self.compute_window(vram_offset, ::core::mem::size_of::<$ty>())?;
+
+ if let Some(base) = new_base {
+ Self::write_window_base(&self.bar, base)?;
+ *self.state = base;
+ }
+ self.bar.$name(bar_offset)
+ }
+ };
+}
+
+/// Generate a PRAMIN write accessor.
+macro_rules! define_pramin_write {
+ ($name:ident, $ty:ty) => {
+ #[doc = concat!("Write a `", stringify!($ty), "` to VRAM at the given offset.")]
+ pub(crate) fn $name(&mut self, vram_offset: usize, value: $ty) -> Result {
+ let (bar_offset, new_base) =
+ self.compute_window(vram_offset, ::core::mem::size_of::<$ty>())?;
+
+ if let Some(base) = new_base {
+ Self::write_window_base(&self.bar, base)?;
+ *self.state = base;
+ }
+ self.bar.$name(value, bar_offset)
+ }
+ };
+}
+
+/// PRAMIN aperture manager.
+///
+/// Call [`Pramin::get_window()`] to acquire exclusive PRAMIN access.
+#[pin_data]
+pub(crate) struct Pramin {
+ bar: Arc<Devres<Bar0>>,
+ /// Valid VRAM region. Accesses outside this range are rejected.
+ vram_region: Range<u64>,
+ /// PRAMIN aperture state, protected by a mutex.
+ ///
+ /// # Invariants
+ ///
+ /// This lock is acquired during the DMA fence signaling critical path.
+ /// It must NEVER be held across any reclaimable CPU memory / allocations
+ /// (`GFP_KERNEL`), because the memory reclaim path can call
+ /// `dma_fence_wait()`, which would deadlock with this lock held.
+ #[pin]
+ state: Mutex<u64>,
+}
+
+impl Pramin {
+ /// Create a pin-initializer for PRAMIN.
+ ///
+ /// `vram_region` specifies the valid VRAM address range.
+ pub(crate) fn new(
+ bar: Arc<Devres<Bar0>>,
+ vram_region: Range<u64>,
+ ) -> Result<impl PinInit<Self>> {
+ let bar_access = bar.try_access().ok_or(ENODEV)?;
+ let current_base = Self::read_window_base(&bar_access);
+
+ Ok(pin_init!(Self {
+ bar,
+ vram_region,
+ state <- new_mutex!(current_base, "pramin_state"),
+ }))
+ }
+
+ /// Acquire exclusive PRAMIN access.
+ ///
+ /// Returns a [`PraminWindow`] guard that provides VRAM read/write accessors.
+ /// The [`PraminWindow`] is exclusive and only one can exist at a time.
+ pub(crate) fn get_window(&self) -> Result<PraminWindow<'_>> {
+ let bar = self.bar.try_access().ok_or(ENODEV)?;
+ let state = self.state.lock();
+ Ok(PraminWindow {
+ bar,
+ vram_region: self.vram_region.clone(),
+ state,
+ })
+ }
+
+ /// Read the current window base from the BAR0_WINDOW register.
+ fn read_window_base(bar: &Bar0) -> u64 {
+ let reg = bar.read(regs::NV_PBUS_BAR0_WINDOW);
+
+ // TODO: Convert to Bounded<u64, 40> when available.
+ u64::from(reg.window_base()) << 16
+ }
+}
+
+/// PRAMIN window guard for direct VRAM access.
+///
+/// This guard holds exclusive access to the PRAMIN aperture. The window auto-repositions
+/// when accessing VRAM offsets outside the current 1MB range.
+///
+/// Only one [`PraminWindow`] can exist at a time per [`Pramin`] instance (enforced by the
+/// internal `MutexGuard`).
+pub(crate) struct PraminWindow<'a> {
+ bar: RevocableGuard<'a, Bar0>,
+ vram_region: Range<u64>,
+ state: MutexGuard<'a, u64>,
+}
+
+impl PraminWindow<'_> {
+ /// Write a new window base to the BAR0_WINDOW register.
+ fn write_window_base(bar: &Bar0, base: u64) -> Result {
+ // CAST: After >> 16, a VRAM address fits in u32.
+ let window_base = (base >> 16) as u32;
+ bar.write_reg(
+ regs::NV_PBUS_BAR0_WINDOW::zeroed()
+ .with_target(Bar0WindowTarget::Vram)
+ .try_with_window_base(window_base)?,
+ );
+ Ok(())
+ }
+
+ /// Compute window parameters for a VRAM access.
+ ///
+ /// Returns (`bar_offset`, `new_base`) where:
+ /// - `bar_offset`: The BAR0 offset to use for the access.
+ /// - `new_base`: `Some(base)` if window needs repositioning, `None` otherwise.
+ fn compute_window(
+ &self,
+ vram_offset: usize,
+ access_size: usize,
+ ) -> Result<(usize, Option<u64>)> {
+ // Validate VRAM offset is within the valid VRAM region.
+ let vram_addr = vram_offset as u64;
+ let end_addr = vram_addr.checked_add(access_size as u64).ok_or(EINVAL)?;
+ if vram_addr < self.vram_region.start || end_addr > self.vram_region.end {
+ return Err(EINVAL);
+ }
+
+ // Check if access fits within the current 1MB window.
+ let current_base = *self.state;
+ if vram_addr >= current_base {
+ let offset_in_window: usize = (vram_addr - current_base).into_safe_cast();
+ if offset_in_window + access_size <= PRAMIN_SIZE {
+ return Ok((PRAMIN_BASE + offset_in_window, None));
+ }
+ }
+
+ // Access doesn't fit in current window - reposition.
+ // Hardware requires 64KB alignment for the window base register.
+ let needed_base = vram_addr & !(SZ_64K as u64 - 1);
+ let offset_in_window: usize = (vram_addr - needed_base).into_safe_cast();
+
+ // Verify access fits in the 1MB window from the new base.
+ if offset_in_window + access_size > PRAMIN_SIZE {
+ return Err(EINVAL);
+ }
+
+ Ok((PRAMIN_BASE + offset_in_window, Some(needed_base)))
+ }
+
+ define_pramin_read!(try_read8, u8);
+ define_pramin_read!(try_read16, u16);
+ define_pramin_read!(try_read32, u32);
+ define_pramin_read!(try_read64, u64);
+
+ define_pramin_write!(try_write8, u8);
+ define_pramin_write!(try_write16, u16);
+ define_pramin_write!(try_write32, u32);
+ define_pramin_write!(try_write64, u64);
+}
diff --git a/drivers/gpu/nova-core/nova_core.rs b/drivers/gpu/nova-core/nova_core.rs
index 04a1fa6b25f8..5f716d1b8c1c 100644
--- a/drivers/gpu/nova-core/nova_core.rs
+++ b/drivers/gpu/nova-core/nova_core.rs
@@ -20,6 +20,7 @@
mod gfw;
mod gpu;
mod gsp;
+mod mm;
#[macro_use]
mod num;
mod regs;
diff --git a/drivers/gpu/nova-core/regs.rs b/drivers/gpu/nova-core/regs.rs
index 2f171a4ff9ba..a3ca02345e20 100644
--- a/drivers/gpu/nova-core/regs.rs
+++ b/drivers/gpu/nova-core/regs.rs
@@ -30,6 +30,7 @@
Architecture,
Chipset, //
},
+ mm::pramin::Bar0WindowTarget,
num::FromSafeCast,
};
@@ -115,6 +116,15 @@ fn fmt(&self, f: &mut kernel::fmt::Formatter<'_>) -> kernel::fmt::Result {
}
}
+register! {
+ /// BAR0 window control for PRAMIN access.
+ pub(crate) NV_PBUS_BAR0_WINDOW(u32) @ 0x00001700 {
+ 25:24 target ?=> Bar0WindowTarget;
+ /// Window base address (bits 39:16 of FB addr).
+ 23:0 window_base;
+ }
+}
+
// PFB
register! {
--
2.34.1
^ permalink raw reply related
* [PATCH v10 05/21] docs: gpu: nova-core: Document the PRAMIN aperture mechanism
From: Joel Fernandes @ 2026-03-31 21:20 UTC (permalink / raw)
To: linux-kernel
Cc: Miguel Ojeda, Boqun Feng, Gary Guo, Bjorn Roy Baron, Benno Lossin,
Andreas Hindborg, Alice Ryhl, Trevor Gross, Danilo Krummrich,
Dave Airlie, Daniel Almeida, Koen Koning, dri-devel,
rust-for-linux, Nikola Djukic, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Jonathan Corbet,
Alex Deucher, Christian Koenig, Jani Nikula, Joonas Lahtinen,
Rodrigo Vivi, Tvrtko Ursulin, Huang Rui, Matthew Auld,
Matthew Brost, Lucas De Marchi, Thomas Hellstrom, Helge Deller,
Alex Gaynor, Boqun Feng, John Hubbard, Alistair Popple,
Timur Tabi, Edwin Peer, Alexandre Courbot, Andrea Righi,
Andy Ritger, Zhi Wang, Balbir Singh, Philipp Stanner,
Elle Rhumsaa, alexeyi, Eliot Courtney, joel, linux-doc, amd-gfx,
intel-gfx, intel-xe, linux-fbdev, Joel Fernandes
In-Reply-To: <20260331212048.2229260-1-joelagnelf@nvidia.com>
Add documentation for the PRAMIN aperture mechanism used by nova-core
for direct VRAM access.
Nova only uses TARGET=VRAM for VRAM access. The SYS_MEM target values
are documented for completeness but not used by the driver.
Cc: Nikola Djukic <ndjukic@nvidia.com>
Signed-off-by: Joel Fernandes <joelagnelf@nvidia.com>
---
Documentation/gpu/nova/core/pramin.rst | 123 +++++++++++++++++++++++++
Documentation/gpu/nova/index.rst | 1 +
2 files changed, 124 insertions(+)
create mode 100644 Documentation/gpu/nova/core/pramin.rst
diff --git a/Documentation/gpu/nova/core/pramin.rst b/Documentation/gpu/nova/core/pramin.rst
new file mode 100644
index 000000000000..bcedb6e06d33
--- /dev/null
+++ b/Documentation/gpu/nova/core/pramin.rst
@@ -0,0 +1,123 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+=========================
+PRAMIN aperture mechanism
+=========================
+
+.. note::
+ The following description is approximate and current as of the Ampere family.
+ It may change for future generations and is intended to assist in understanding
+ the driver code.
+
+Introduction
+============
+
+PRAMIN is a hardware aperture mechanism that provides CPU access to GPU Video RAM (VRAM) before
+the GPU's Memory Management Unit (MMU) and page tables are initialized. This 1MB sliding window,
+located at a fixed offset within BAR0, is essential for setting up page tables and other critical
+GPU data structures without relying on the GPU's MMU.
+
+Architecture Overview
+=====================
+
+The PRAMIN aperture mechanism is logically implemented by the GPU's PBUS (PCIe Bus Controller Unit)
+and provides a CPU-accessible window into VRAM through the PCIe interface::
+
+ +-----------------+ PCIe +------------------------------+
+ | CPU |<----------->| GPU |
+ +-----------------+ | |
+ | +----------------------+ |
+ | | PBUS | |
+ | | (Bus Controller) | |
+ | | | |
+ | | +--------------+<------------ (window starts at
+ | | | PRAMIN | | | BAR0 + 0x700000)
+ | | | Window | | |
+ | | | (1MB) | | |
+ | | +--------------+ | |
+ | | | | |
+ | +---------|------------+ |
+ | | |
+ | v |
+ | +----------------------+<------------ (Program PRAMIN to any
+ | | VRAM | | 64KB-aligned VRAM boundary)
+ | | (Several GBs) | |
+ | | | |
+ | | FB[0x000000000000] | |
+ | | ... | |
+ | | FB[0x7FFFFFFFFFF] | |
+ | +----------------------+ |
+ +------------------------------+
+
+PBUS (PCIe Bus Controller) is responsible for, among other things, handling MMIO
+accesses to the BAR registers.
+
+PRAMIN Window Operation
+=======================
+
+The PRAMIN window provides a 1MB sliding aperture that can be repositioned over
+the entire VRAM address space using the ``NV_PBUS_BAR0_WINDOW`` register.
+
+Window Control Mechanism
+-------------------------
+
+::
+
+ NV_PBUS_BAR0_WINDOW Register (0x1700):
+ +-------+--------+--------------------------------------+
+ | 31:26 | 25:24 | 23:0 |
+ | RSVD | TARGET | BASE_ADDR |
+ | | | (bits 39:16 of VRAM address) |
+ +-------+--------+--------------------------------------+
+
+ The 24-bit BASE_ADDR field encodes bits [39:16] of the target VRAM address,
+ providing 40-bit (1TB) address space coverage with 64KB alignment.
+
+ TARGET field (bits 25:24):
+ - 0x0: VRAM (Video Memory)
+ - 0x1: SYS_MEM_COH (Coherent System Memory)
+ - 0x2: SYS_MEM_NONCOH (Non-coherent System Memory)
+ - 0x3: Reserved
+
+ .. note::
+ Nova only uses TARGET=VRAM (0x0) for video memory access. The SYS_MEM
+ target values are documented here for hardware completeness but are
+ not used by the driver.
+
+64KB Alignment Requirement
+---------------------------
+
+The PRAMIN window must be aligned to 64KB boundaries in VRAM. This is enforced
+by the ``BASE_ADDR`` field representing bits [39:16] of the target address::
+
+ VRAM Address Calculation:
+ actual_vram_addr = (BASE_ADDR << 16) + pramin_offset
+ Where:
+ - BASE_ADDR: 24-bit value from NV_PBUS_BAR0_WINDOW[23:0]
+ - pramin_offset: 20-bit offset within the PRAMIN window [0x00000-0xFFFFF]
+
+ Example Window Positioning:
+ +---------------------------------------------------------+
+ | VRAM Space |
+ | |
+ | 0x000000000 +-----------------+ <-- 64KB aligned |
+ | | PRAMIN Window | |
+ | | (1MB) | |
+ | 0x0000FFFFF +-----------------+ |
+ | |
+ | | ^ |
+ | | | Window can slide |
+ | v | to any 64KB-aligned boundary |
+ | |
+ | 0x123400000 +-----------------+ <-- 64KB aligned |
+ | | PRAMIN Window | |
+ | | (1MB) | |
+ | 0x1234FFFFF +-----------------+ |
+ | |
+ | ... |
+ | |
+ | 0x7FFFF0000 +-----------------+ <-- 64KB aligned |
+ | | PRAMIN Window | |
+ | | (1MB) | |
+ | 0x7FFFFFFFF +-----------------+ |
+ +---------------------------------------------------------+
diff --git a/Documentation/gpu/nova/index.rst b/Documentation/gpu/nova/index.rst
index e39cb3163581..b8254b1ffe2a 100644
--- a/Documentation/gpu/nova/index.rst
+++ b/Documentation/gpu/nova/index.rst
@@ -32,3 +32,4 @@ vGPU manager VFIO driver and the nova-drm driver.
core/devinit
core/fwsec
core/falcon
+ core/pramin
--
2.34.1
^ permalink raw reply related
* [PATCH v10 06/21] gpu: nova-core: mm: Add common memory management types
From: Joel Fernandes @ 2026-03-31 21:20 UTC (permalink / raw)
To: linux-kernel
Cc: Miguel Ojeda, Boqun Feng, Gary Guo, Bjorn Roy Baron, Benno Lossin,
Andreas Hindborg, Alice Ryhl, Trevor Gross, Danilo Krummrich,
Dave Airlie, Daniel Almeida, Koen Koning, dri-devel,
rust-for-linux, Nikola Djukic, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Jonathan Corbet,
Alex Deucher, Christian Koenig, Jani Nikula, Joonas Lahtinen,
Rodrigo Vivi, Tvrtko Ursulin, Huang Rui, Matthew Auld,
Matthew Brost, Lucas De Marchi, Thomas Hellstrom, Helge Deller,
Alex Gaynor, Boqun Feng, John Hubbard, Alistair Popple,
Timur Tabi, Edwin Peer, Alexandre Courbot, Andrea Righi,
Andy Ritger, Zhi Wang, Balbir Singh, Philipp Stanner,
Elle Rhumsaa, alexeyi, Eliot Courtney, joel, linux-doc, amd-gfx,
intel-gfx, intel-xe, linux-fbdev, Joel Fernandes
In-Reply-To: <20260331212048.2229260-1-joelagnelf@nvidia.com>
Add foundational types for GPU memory management. These types are used
throughout the nova memory management subsystem for page table
operations, address translation, and memory allocation.
Cc: Nikola Djukic <ndjukic@nvidia.com>
Signed-off-by: Joel Fernandes <joelagnelf@nvidia.com>
---
drivers/gpu/nova-core/mm.rs | 159 ++++++++++++++++++++++++++++++++++++
1 file changed, 159 insertions(+)
diff --git a/drivers/gpu/nova-core/mm.rs b/drivers/gpu/nova-core/mm.rs
index 7a5dd4220c67..8f3089a5fa88 100644
--- a/drivers/gpu/nova-core/mm.rs
+++ b/drivers/gpu/nova-core/mm.rs
@@ -2,4 +2,163 @@
//! Memory management subsystems for nova-core.
+#![expect(dead_code)]
+
pub(crate) mod pramin;
+
+use kernel::sizes::SZ_4K;
+
+use crate::num::u64_as_usize;
+
+/// Page size in bytes (4 KiB).
+pub(crate) const PAGE_SIZE: usize = SZ_4K;
+
+bitfield! {
+ pub(crate) struct VramAddress(u64), "Physical VRAM address in GPU video memory" {
+ 11:0 offset as u64, "Offset within 4KB page";
+ 63:12 frame_number as u64 => Pfn, "Physical frame number";
+ }
+}
+
+impl VramAddress {
+ /// Create a new VRAM address from a raw value.
+ pub(crate) const fn new(addr: u64) -> Self {
+ Self(addr)
+ }
+
+ /// Get the raw address value as `usize` (useful for MMIO offsets).
+ pub(crate) const fn raw(&self) -> usize {
+ u64_as_usize(self.0)
+ }
+
+ /// Get the raw address value as `u64`.
+ pub(crate) const fn raw_u64(&self) -> u64 {
+ self.0
+ }
+}
+
+impl PartialEq for VramAddress {
+ fn eq(&self, other: &Self) -> bool {
+ self.0 == other.0
+ }
+}
+
+impl Eq for VramAddress {}
+
+impl PartialOrd for VramAddress {
+ fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
+ Some(self.cmp(other))
+ }
+}
+
+impl Ord for VramAddress {
+ fn cmp(&self, other: &Self) -> core::cmp::Ordering {
+ self.0.cmp(&other.0)
+ }
+}
+
+impl From<Pfn> for VramAddress {
+ fn from(pfn: Pfn) -> Self {
+ Self::default().set_frame_number(pfn)
+ }
+}
+
+bitfield! {
+ pub(crate) struct VirtualAddress(u64), "Virtual address in GPU address space" {
+ 11:0 offset as u64, "Offset within 4KB page";
+ 63:12 frame_number as u64 => Vfn, "Virtual frame number";
+ }
+}
+
+impl VirtualAddress {
+ /// Create a new virtual address from a raw value.
+ #[expect(dead_code)]
+ pub(crate) const fn new(addr: u64) -> Self {
+ Self(addr)
+ }
+
+ /// Get the raw address value as `u64`.
+ pub(crate) const fn raw_u64(&self) -> u64 {
+ self.0
+ }
+}
+
+impl From<Vfn> for VirtualAddress {
+ fn from(vfn: Vfn) -> Self {
+ Self::default().set_frame_number(vfn)
+ }
+}
+
+/// Physical Frame Number.
+///
+/// Represents a physical page in VRAM.
+#[repr(transparent)]
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
+pub(crate) struct Pfn(u64);
+
+impl Pfn {
+ /// Create a new PFN from a frame number.
+ pub(crate) const fn new(frame_number: u64) -> Self {
+ Self(frame_number)
+ }
+
+ /// Get the raw frame number.
+ pub(crate) const fn raw(self) -> u64 {
+ self.0
+ }
+}
+
+impl From<VramAddress> for Pfn {
+ fn from(addr: VramAddress) -> Self {
+ addr.frame_number()
+ }
+}
+
+impl From<u64> for Pfn {
+ fn from(val: u64) -> Self {
+ Self(val)
+ }
+}
+
+impl From<Pfn> for u64 {
+ fn from(pfn: Pfn) -> Self {
+ pfn.0
+ }
+}
+
+/// Virtual Frame Number.
+///
+/// Represents a virtual page in GPU address space.
+#[repr(transparent)]
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
+pub(crate) struct Vfn(u64);
+
+impl Vfn {
+ /// Create a new VFN from a frame number.
+ pub(crate) const fn new(frame_number: u64) -> Self {
+ Self(frame_number)
+ }
+
+ /// Get the raw frame number.
+ pub(crate) const fn raw(self) -> u64 {
+ self.0
+ }
+}
+
+impl From<VirtualAddress> for Vfn {
+ fn from(addr: VirtualAddress) -> Self {
+ addr.frame_number()
+ }
+}
+
+impl From<u64> for Vfn {
+ fn from(val: u64) -> Self {
+ Self(val)
+ }
+}
+
+impl From<Vfn> for u64 {
+ fn from(vfn: Vfn) -> Self {
+ vfn.0
+ }
+}
--
2.34.1
^ permalink raw reply related
* [PATCH v10 07/21] gpu: nova-core: mm: Add TLB flush support
From: Joel Fernandes @ 2026-03-31 21:20 UTC (permalink / raw)
To: linux-kernel
Cc: Miguel Ojeda, Boqun Feng, Gary Guo, Bjorn Roy Baron, Benno Lossin,
Andreas Hindborg, Alice Ryhl, Trevor Gross, Danilo Krummrich,
Dave Airlie, Daniel Almeida, Koen Koning, dri-devel,
rust-for-linux, Nikola Djukic, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Jonathan Corbet,
Alex Deucher, Christian Koenig, Jani Nikula, Joonas Lahtinen,
Rodrigo Vivi, Tvrtko Ursulin, Huang Rui, Matthew Auld,
Matthew Brost, Lucas De Marchi, Thomas Hellstrom, Helge Deller,
Alex Gaynor, Boqun Feng, John Hubbard, Alistair Popple,
Timur Tabi, Edwin Peer, Alexandre Courbot, Andrea Righi,
Andy Ritger, Zhi Wang, Balbir Singh, Philipp Stanner,
Elle Rhumsaa, alexeyi, Eliot Courtney, joel, linux-doc, amd-gfx,
intel-gfx, intel-xe, linux-fbdev, Joel Fernandes
In-Reply-To: <20260331212048.2229260-1-joelagnelf@nvidia.com>
Add TLB (Translation Lookaside Buffer) flush support for GPU MMU.
After modifying page table entries, the GPU's TLB must be invalidated
to ensure the new mappings take effect. The Tlb struct provides flush
functionality through BAR0 registers.
The flush operation writes the page directory base address and triggers
an invalidation, polling for completion with a 2 second timeout matching
the Nouveau driver.
Cc: Nikola Djukic <ndjukic@nvidia.com>
Signed-off-by: Joel Fernandes <joelagnelf@nvidia.com>
---
drivers/gpu/nova-core/mm.rs | 1 +
drivers/gpu/nova-core/mm/tlb.rs | 95 +++++++++++++++++++++++++++++++++
drivers/gpu/nova-core/regs.rs | 42 +++++++++++++++
3 files changed, 138 insertions(+)
create mode 100644 drivers/gpu/nova-core/mm/tlb.rs
diff --git a/drivers/gpu/nova-core/mm.rs b/drivers/gpu/nova-core/mm.rs
index 8f3089a5fa88..cfe9cbe11d57 100644
--- a/drivers/gpu/nova-core/mm.rs
+++ b/drivers/gpu/nova-core/mm.rs
@@ -5,6 +5,7 @@
#![expect(dead_code)]
pub(crate) mod pramin;
+pub(crate) mod tlb;
use kernel::sizes::SZ_4K;
diff --git a/drivers/gpu/nova-core/mm/tlb.rs b/drivers/gpu/nova-core/mm/tlb.rs
new file mode 100644
index 000000000000..cd3cbcf4c739
--- /dev/null
+++ b/drivers/gpu/nova-core/mm/tlb.rs
@@ -0,0 +1,95 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! TLB (Translation Lookaside Buffer) flush support for GPU MMU.
+//!
+//! After modifying page table entries, the GPU's TLB must be flushed to
+//! ensure the new mappings take effect. This module provides TLB flush
+//! functionality for virtual memory managers.
+//!
+//! # Example
+//!
+//! ```ignore
+//! use crate::mm::tlb::Tlb;
+//!
+//! fn page_table_update(tlb: &Tlb, pdb_addr: VramAddress) -> Result<()> {
+//! // ... modify page tables ...
+//!
+//! // Flush TLB to make changes visible (polls for completion).
+//! tlb.flush(pdb_addr)?;
+//!
+//! Ok(())
+//! }
+//! ```
+
+use kernel::{
+ devres::Devres,
+ io::poll::read_poll_timeout,
+ io::Io,
+ new_mutex,
+ prelude::*,
+ sync::{
+ Arc,
+ Mutex, //
+ },
+ time::Delta, //
+};
+
+use crate::{
+ driver::Bar0,
+ mm::VramAddress,
+ regs, //
+};
+
+/// TLB manager for GPU translation buffer operations.
+#[pin_data]
+pub(crate) struct Tlb {
+ bar: Arc<Devres<Bar0>>,
+ /// TLB flush serialization lock: This lock is acquired during the
+ /// DMA fence signalling critical path. It must NEVER be held across any
+ /// reclaimable CPU memory allocations because the memory reclaim path can
+ /// call `dma_fence_wait()`, which would deadlock with this lock held.
+ #[pin]
+ lock: Mutex<()>,
+}
+
+impl Tlb {
+ /// Create a new TLB manager.
+ pub(super) fn new(bar: Arc<Devres<Bar0>>) -> impl PinInit<Self> {
+ pin_init!(Self {
+ bar,
+ lock <- new_mutex!((), "tlb_flush"),
+ })
+ }
+
+ /// Flush the GPU TLB for a specific page directory base.
+ ///
+ /// This invalidates all TLB entries associated with the given PDB address.
+ /// Must be called after modifying page table entries to ensure the GPU sees
+ /// the updated mappings.
+ pub(crate) fn flush(&self, pdb_addr: VramAddress) -> Result {
+ let _guard = self.lock.lock();
+
+ let bar = self.bar.try_access().ok_or(ENODEV)?;
+
+ // Write PDB address.
+ bar.write_reg(regs::NV_TLB_FLUSH_PDB_LO::from_pdb_addr(pdb_addr.raw_u64()));
+ bar.write_reg(regs::NV_TLB_FLUSH_PDB_HI::from_pdb_addr(pdb_addr.raw_u64()));
+
+ // Trigger flush: invalidate all pages and enable.
+ bar.write_reg(
+ regs::NV_TLB_FLUSH_CTRL::zeroed()
+ .with_page_all(true)
+ .with_enable(true),
+ );
+
+ // Poll for completion - enable bit clears when flush is done.
+ read_poll_timeout(
+ || Ok(bar.read(regs::NV_TLB_FLUSH_CTRL)),
+ |ctrl: ®s::NV_TLB_FLUSH_CTRL| !ctrl.enable(),
+ Delta::ZERO,
+ Delta::from_secs(2),
+ )?;
+
+ Ok(())
+ }
+}
diff --git a/drivers/gpu/nova-core/regs.rs b/drivers/gpu/nova-core/regs.rs
index a3ca02345e20..5e3f5933a55c 100644
--- a/drivers/gpu/nova-core/regs.rs
+++ b/drivers/gpu/nova-core/regs.rs
@@ -548,3 +548,45 @@ pub(crate) mod ga100 {
}
}
}
+
+// MMU TLB
+
+register! {
+ /// TLB flush register: PDB address bits [39:8].
+ pub(crate) NV_TLB_FLUSH_PDB_LO(u32) @ 0x00b830a0 {
+ /// PDB address bits [39:8].
+ 31:0 pdb_lo => u32;
+ }
+
+ /// TLB flush register: PDB address bits [47:40].
+ pub(crate) NV_TLB_FLUSH_PDB_HI(u32) @ 0x00b830a4 {
+ /// PDB address bits [47:40].
+ 7:0 pdb_hi => u8;
+ }
+
+ /// TLB flush control register.
+ pub(crate) NV_TLB_FLUSH_CTRL(u32) @ 0x00b830b0 {
+ /// Invalidate all pages.
+ 0:0 page_all => bool;
+ /// Enable/trigger flush (clears when flush completes).
+ 31:31 enable => bool;
+ }
+}
+
+impl NV_TLB_FLUSH_PDB_LO {
+ /// Create a register value from a PDB address.
+ ///
+ /// Extracts bits [39:8] of the address and shifts it right by 8 bits.
+ pub(crate) fn from_pdb_addr(addr: u64) -> Self {
+ Self::zeroed().with_pdb_lo(((addr >> 8) & 0xFFFF_FFFF) as u32)
+ }
+}
+
+impl NV_TLB_FLUSH_PDB_HI {
+ /// Create a register value from a PDB address.
+ ///
+ /// Extracts bits [47:40] of the address and shifts it right by 40 bits.
+ pub(crate) fn from_pdb_addr(addr: u64) -> Self {
+ Self::zeroed().with_pdb_hi(((addr >> 40) & 0xFF) as u8)
+ }
+}
--
2.34.1
^ permalink raw reply related
* [PATCH v10 08/21] gpu: nova-core: mm: Add GpuMm centralized memory manager
From: Joel Fernandes @ 2026-03-31 21:20 UTC (permalink / raw)
To: linux-kernel
Cc: Miguel Ojeda, Boqun Feng, Gary Guo, Bjorn Roy Baron, Benno Lossin,
Andreas Hindborg, Alice Ryhl, Trevor Gross, Danilo Krummrich,
Dave Airlie, Daniel Almeida, Koen Koning, dri-devel,
rust-for-linux, Nikola Djukic, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Jonathan Corbet,
Alex Deucher, Christian Koenig, Jani Nikula, Joonas Lahtinen,
Rodrigo Vivi, Tvrtko Ursulin, Huang Rui, Matthew Auld,
Matthew Brost, Lucas De Marchi, Thomas Hellstrom, Helge Deller,
Alex Gaynor, Boqun Feng, John Hubbard, Alistair Popple,
Timur Tabi, Edwin Peer, Alexandre Courbot, Andrea Righi,
Andy Ritger, Zhi Wang, Balbir Singh, Philipp Stanner,
Elle Rhumsaa, alexeyi, Eliot Courtney, joel, linux-doc, amd-gfx,
intel-gfx, intel-xe, linux-fbdev, Joel Fernandes
In-Reply-To: <20260331212048.2229260-1-joelagnelf@nvidia.com>
Introduce GpuMm as the centralized GPU memory manager that owns:
- Buddy allocator for VRAM allocation.
- PRAMIN window for direct VRAM access.
- TLB manager for translation buffer operations.
This provides clean ownership model where GpuMm provides accessor
methods for its components that can be used for memory management
operations.
Cc: Nikola Djukic <ndjukic@nvidia.com>
Signed-off-by: Joel Fernandes <joelagnelf@nvidia.com>
---
drivers/gpu/nova-core/Kconfig | 1 +
drivers/gpu/nova-core/gpu.rs | 34 ++++++++++++-
drivers/gpu/nova-core/gsp/commands.rs | 2 -
drivers/gpu/nova-core/mm.rs | 70 ++++++++++++++++++++++++++-
4 files changed, 102 insertions(+), 5 deletions(-)
diff --git a/drivers/gpu/nova-core/Kconfig b/drivers/gpu/nova-core/Kconfig
index a4f2380654e2..6513007bf66f 100644
--- a/drivers/gpu/nova-core/Kconfig
+++ b/drivers/gpu/nova-core/Kconfig
@@ -4,6 +4,7 @@ config NOVA_CORE
depends on PCI
depends on RUST
select AUXILIARY_BUS
+ select GPU_BUDDY
select RUST_FW_LOADER_ABSTRACTIONS
default n
help
diff --git a/drivers/gpu/nova-core/gpu.rs b/drivers/gpu/nova-core/gpu.rs
index b4da4a1ae156..c49fa9c380b8 100644
--- a/drivers/gpu/nova-core/gpu.rs
+++ b/drivers/gpu/nova-core/gpu.rs
@@ -4,10 +4,13 @@
device,
devres::Devres,
fmt,
+ gpu::buddy::GpuBuddyParams,
io::Io,
num::Bounded,
pci,
prelude::*,
+ ptr::Alignment,
+ sizes::SZ_4K,
sync::Arc, //
};
@@ -25,6 +28,7 @@
commands::GetGspStaticInfoReply,
Gsp, //
},
+ mm::GpuMm,
regs,
};
@@ -238,6 +242,9 @@ pub(crate) struct Gpu {
gsp_falcon: Falcon<GspFalcon>,
/// SEC2 falcon instance, used for GSP boot up and cleanup.
sec2_falcon: Falcon<Sec2Falcon>,
+ /// GPU memory manager owning memory management resources.
+ #[pin]
+ mm: GpuMm,
/// GSP runtime data. Temporarily an empty placeholder.
#[pin]
gsp: Gsp,
@@ -274,7 +281,32 @@ pub(crate) fn new<'a>(
gsp <- Gsp::new(pdev),
- gsp_static_info: { gsp.boot(pdev, bar, spec.chipset, gsp_falcon, sec2_falcon)? },
+ gsp_static_info: {
+ let info = gsp.boot(pdev, bar, spec.chipset, gsp_falcon, sec2_falcon)?;
+
+ dev_info!(
+ pdev.as_ref(),
+ "Using FB region: {:#x}..{:#x}\n",
+ info.usable_fb_region.start,
+ info.usable_fb_region.end
+ );
+
+ info
+ },
+
+ // Create GPU memory manager owning memory management resources.
+ mm <- {
+ let usable_vram = &gsp_static_info.usable_fb_region;
+
+ // PRAMIN covers all physical VRAM (including GSP-reserved areas
+ // above the usable region, e.g. the BAR1 page directory).
+ let pramin_vram_region = 0..gsp_static_info.total_fb_end;
+ GpuMm::new(devres_bar.clone(), GpuBuddyParams {
+ base_offset: usable_vram.start,
+ size: usable_vram.end - usable_vram.start,
+ chunk_size: Alignment::new::<SZ_4K>(),
+ }, pramin_vram_region)?
+ },
bar: devres_bar,
})
diff --git a/drivers/gpu/nova-core/gsp/commands.rs b/drivers/gpu/nova-core/gsp/commands.rs
index 5e0649024637..ec03bf94b34e 100644
--- a/drivers/gpu/nova-core/gsp/commands.rs
+++ b/drivers/gpu/nova-core/gsp/commands.rs
@@ -194,10 +194,8 @@ fn init(&self) -> impl Init<Self::Command, Self::InitError> {
pub(crate) struct GetGspStaticInfoReply {
gpu_name: [u8; 64],
/// Usable FB (VRAM) region for driver memory allocation.
- #[expect(dead_code)]
pub(crate) usable_fb_region: Range<u64>,
/// End of VRAM.
- #[expect(dead_code)]
pub(crate) total_fb_end: u64,
}
diff --git a/drivers/gpu/nova-core/mm.rs b/drivers/gpu/nova-core/mm.rs
index cfe9cbe11d57..3c34d7f05555 100644
--- a/drivers/gpu/nova-core/mm.rs
+++ b/drivers/gpu/nova-core/mm.rs
@@ -7,9 +7,75 @@
pub(crate) mod pramin;
pub(crate) mod tlb;
-use kernel::sizes::SZ_4K;
+use kernel::{
+ devres::Devres,
+ gpu::buddy::{
+ GpuBuddy,
+ GpuBuddyParams, //
+ },
+ prelude::*,
+ sizes::SZ_4K,
+ sync::Arc, //
+};
-use crate::num::u64_as_usize;
+use crate::{
+ driver::Bar0,
+ num::u64_as_usize, //
+};
+
+pub(crate) use tlb::Tlb;
+
+/// GPU Memory Manager - owns all core MM components.
+///
+/// Provides centralized ownership of memory management resources:
+/// - [`GpuBuddy`] allocator for VRAM page table allocation.
+/// - [`pramin::Pramin`] for direct VRAM access.
+/// - [`Tlb`] manager for translation buffer flush operations.
+#[pin_data]
+pub(crate) struct GpuMm {
+ buddy: GpuBuddy,
+ #[pin]
+ pramin: pramin::Pramin,
+ #[pin]
+ tlb: Tlb,
+}
+
+impl GpuMm {
+ /// Create a pin-initializer for `GpuMm`.
+ ///
+ /// `pramin_vram_region` is the full physical VRAM range (including GSP-reserved
+ /// areas). PRAMIN window accesses are validated against this range.
+ pub(crate) fn new(
+ bar: Arc<Devres<Bar0>>,
+ buddy_params: GpuBuddyParams,
+ pramin_vram_region: core::ops::Range<u64>,
+ ) -> Result<impl PinInit<Self>> {
+ let buddy = GpuBuddy::new(buddy_params)?;
+ let tlb_init = Tlb::new(bar.clone());
+ let pramin_init = pramin::Pramin::new(bar, pramin_vram_region)?;
+
+ Ok(pin_init!(Self {
+ buddy,
+ pramin <- pramin_init,
+ tlb <- tlb_init,
+ }))
+ }
+
+ /// Access the [`GpuBuddy`] allocator.
+ pub(crate) fn buddy(&self) -> &GpuBuddy {
+ &self.buddy
+ }
+
+ /// Access the [`pramin::Pramin`].
+ pub(crate) fn pramin(&self) -> &pramin::Pramin {
+ &self.pramin
+ }
+
+ /// Access the [`Tlb`] manager.
+ pub(crate) fn tlb(&self) -> &Tlb {
+ &self.tlb
+ }
+}
/// Page size in bytes (4 KiB).
pub(crate) const PAGE_SIZE: usize = SZ_4K;
--
2.34.1
^ permalink raw reply related
* [PATCH v10 09/21] gpu: nova-core: mm: Add common types for all page table formats
From: Joel Fernandes @ 2026-03-31 21:20 UTC (permalink / raw)
To: linux-kernel
Cc: Miguel Ojeda, Boqun Feng, Gary Guo, Bjorn Roy Baron, Benno Lossin,
Andreas Hindborg, Alice Ryhl, Trevor Gross, Danilo Krummrich,
Dave Airlie, Daniel Almeida, Koen Koning, dri-devel,
rust-for-linux, Nikola Djukic, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Jonathan Corbet,
Alex Deucher, Christian Koenig, Jani Nikula, Joonas Lahtinen,
Rodrigo Vivi, Tvrtko Ursulin, Huang Rui, Matthew Auld,
Matthew Brost, Lucas De Marchi, Thomas Hellstrom, Helge Deller,
Alex Gaynor, Boqun Feng, John Hubbard, Alistair Popple,
Timur Tabi, Edwin Peer, Alexandre Courbot, Andrea Righi,
Andy Ritger, Zhi Wang, Balbir Singh, Philipp Stanner,
Elle Rhumsaa, alexeyi, Eliot Courtney, joel, linux-doc, amd-gfx,
intel-gfx, intel-xe, linux-fbdev, Joel Fernandes
In-Reply-To: <20260331212048.2229260-1-joelagnelf@nvidia.com>
Add common page table types shared between MMU v2 and v3. These types
are hardware-agnostic and used by both MMU versions.
Cc: Nikola Djukic <ndjukic@nvidia.com>
Signed-off-by: Joel Fernandes <joelagnelf@nvidia.com>
---
drivers/gpu/nova-core/mm.rs | 1 +
drivers/gpu/nova-core/mm/pagetable.rs | 155 ++++++++++++++++++++++++++
2 files changed, 156 insertions(+)
create mode 100644 drivers/gpu/nova-core/mm/pagetable.rs
diff --git a/drivers/gpu/nova-core/mm.rs b/drivers/gpu/nova-core/mm.rs
index 3c34d7f05555..151b9add67d8 100644
--- a/drivers/gpu/nova-core/mm.rs
+++ b/drivers/gpu/nova-core/mm.rs
@@ -4,6 +4,7 @@
#![expect(dead_code)]
+pub(crate) mod pagetable;
pub(crate) mod pramin;
pub(crate) mod tlb;
diff --git a/drivers/gpu/nova-core/mm/pagetable.rs b/drivers/gpu/nova-core/mm/pagetable.rs
new file mode 100644
index 000000000000..50b76d5e5aaf
--- /dev/null
+++ b/drivers/gpu/nova-core/mm/pagetable.rs
@@ -0,0 +1,155 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! Common page table types shared between MMU v2 and v3.
+//!
+//! This module provides foundational types used by both MMU versions:
+//! - Page table level hierarchy
+//! - Memory aperture types for PDEs and PTEs
+
+#![expect(dead_code)]
+
+use crate::gpu::Architecture;
+
+/// Extracts the page table index at a given level from a virtual address.
+pub(crate) trait VaLevelIndex {
+ /// Return the page table index at `level` for this virtual address.
+ fn level_index(&self, level: u64) -> u64;
+}
+
+/// MMU version enumeration.
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub(crate) enum MmuVersion {
+ /// MMU v2 for Turing/Ampere/Ada.
+ V2,
+ /// MMU v3 for Hopper and later.
+ V3,
+}
+
+impl From<Architecture> for MmuVersion {
+ fn from(arch: Architecture) -> Self {
+ match arch {
+ Architecture::Turing | Architecture::Ampere | Architecture::Ada => Self::V2,
+ // In the future, uncomment the following to support V3.
+ // _ => Self::V3,
+ }
+ }
+}
+
+/// Page Table Level hierarchy for MMU v2/v3.
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub(crate) enum PageTableLevel {
+ /// Level 0 - Page Directory Base (root).
+ Pdb,
+ /// Level 1 - Intermediate page directory.
+ L1,
+ /// Level 2 - Intermediate page directory.
+ L2,
+ /// Level 3 - Intermediate page directory or dual PDE (version-dependent).
+ L3,
+ /// Level 4 - PTE level for v2, intermediate page directory for v3.
+ L4,
+ /// Level 5 - PTE level used for MMU v3 only.
+ L5,
+}
+
+impl PageTableLevel {
+ /// Number of entries per page table (512 for 4KB pages).
+ pub(crate) const ENTRIES_PER_TABLE: usize = 512;
+
+ /// Get the next level in the hierarchy.
+ pub(crate) const fn next(&self) -> Option<PageTableLevel> {
+ match self {
+ Self::Pdb => Some(Self::L1),
+ Self::L1 => Some(Self::L2),
+ Self::L2 => Some(Self::L3),
+ Self::L3 => Some(Self::L4),
+ Self::L4 => Some(Self::L5),
+ Self::L5 => None,
+ }
+ }
+
+ /// Convert level to index.
+ pub(crate) const fn as_index(&self) -> u64 {
+ match self {
+ Self::Pdb => 0,
+ Self::L1 => 1,
+ Self::L2 => 2,
+ Self::L3 => 3,
+ Self::L4 => 4,
+ Self::L5 => 5,
+ }
+ }
+}
+
+/// Memory aperture for Page Table Entries (`PTE`s).
+///
+/// Determines which memory region the `PTE` points to.
+#[repr(u8)]
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
+pub(crate) enum AperturePte {
+ /// Local video memory (VRAM).
+ #[default]
+ VideoMemory = 0,
+ /// Peer GPU's video memory.
+ PeerMemory = 1,
+ /// System memory with cache coherence.
+ SystemCoherent = 2,
+ /// System memory without cache coherence.
+ SystemNonCoherent = 3,
+}
+
+// TODO[FPRI]: Replace with `#[derive(FromPrimitive)]` when available.
+impl From<u8> for AperturePte {
+ fn from(val: u8) -> Self {
+ match val {
+ 0 => Self::VideoMemory,
+ 1 => Self::PeerMemory,
+ 2 => Self::SystemCoherent,
+ 3 => Self::SystemNonCoherent,
+ _ => Self::VideoMemory,
+ }
+ }
+}
+
+// TODO[FPRI]: Replace with `#[derive(ToPrimitive)]` when available.
+impl From<AperturePte> for u8 {
+ fn from(val: AperturePte) -> Self {
+ val as u8
+ }
+}
+
+/// Memory aperture for Page Directory Entries (`PDE`s).
+///
+/// Note: For `PDE`s, `Invalid` (0) means the entry is not valid.
+#[repr(u8)]
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
+pub(crate) enum AperturePde {
+ /// Invalid/unused entry.
+ #[default]
+ Invalid = 0,
+ /// Page table is in video memory.
+ VideoMemory = 1,
+ /// Page table is in system memory with coherence.
+ SystemCoherent = 2,
+ /// Page table is in system memory without coherence.
+ SystemNonCoherent = 3,
+}
+
+// TODO[FPRI]: Replace with `#[derive(FromPrimitive)]` when available.
+impl From<u8> for AperturePde {
+ fn from(val: u8) -> Self {
+ match val {
+ 1 => Self::VideoMemory,
+ 2 => Self::SystemCoherent,
+ 3 => Self::SystemNonCoherent,
+ _ => Self::Invalid,
+ }
+ }
+}
+
+// TODO[FPRI]: Replace with `#[derive(ToPrimitive)]` when available.
+impl From<AperturePde> for u8 {
+ fn from(val: AperturePde) -> Self {
+ val as u8
+ }
+}
--
2.34.1
^ permalink raw reply related
* [PATCH v10 10/21] gpu: nova-core: mm: Add MMU v2 page table types
From: Joel Fernandes @ 2026-03-31 21:20 UTC (permalink / raw)
To: linux-kernel
Cc: Miguel Ojeda, Boqun Feng, Gary Guo, Bjorn Roy Baron, Benno Lossin,
Andreas Hindborg, Alice Ryhl, Trevor Gross, Danilo Krummrich,
Dave Airlie, Daniel Almeida, Koen Koning, dri-devel,
rust-for-linux, Nikola Djukic, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Jonathan Corbet,
Alex Deucher, Christian Koenig, Jani Nikula, Joonas Lahtinen,
Rodrigo Vivi, Tvrtko Ursulin, Huang Rui, Matthew Auld,
Matthew Brost, Lucas De Marchi, Thomas Hellstrom, Helge Deller,
Alex Gaynor, Boqun Feng, John Hubbard, Alistair Popple,
Timur Tabi, Edwin Peer, Alexandre Courbot, Andrea Righi,
Andy Ritger, Zhi Wang, Balbir Singh, Philipp Stanner,
Elle Rhumsaa, alexeyi, Eliot Courtney, joel, linux-doc, amd-gfx,
intel-gfx, intel-xe, linux-fbdev, Joel Fernandes
In-Reply-To: <20260331212048.2229260-1-joelagnelf@nvidia.com>
Add page table entry and directory structures for MMU version 2
used by Turing/Ampere/Ada GPUs.
Cc: Nikola Djukic <ndjukic@nvidia.com>
Signed-off-by: Joel Fernandes <joelagnelf@nvidia.com>
---
drivers/gpu/nova-core/mm/pagetable.rs | 2 +
drivers/gpu/nova-core/mm/pagetable/ver2.rs | 232 +++++++++++++++++++++
2 files changed, 234 insertions(+)
create mode 100644 drivers/gpu/nova-core/mm/pagetable/ver2.rs
diff --git a/drivers/gpu/nova-core/mm/pagetable.rs b/drivers/gpu/nova-core/mm/pagetable.rs
index 50b76d5e5aaf..38d88f8f09a9 100644
--- a/drivers/gpu/nova-core/mm/pagetable.rs
+++ b/drivers/gpu/nova-core/mm/pagetable.rs
@@ -8,6 +8,8 @@
#![expect(dead_code)]
+pub(crate) mod ver2;
+
use crate::gpu::Architecture;
/// Extracts the page table index at a given level from a virtual address.
diff --git a/drivers/gpu/nova-core/mm/pagetable/ver2.rs b/drivers/gpu/nova-core/mm/pagetable/ver2.rs
new file mode 100644
index 000000000000..f418632764d1
--- /dev/null
+++ b/drivers/gpu/nova-core/mm/pagetable/ver2.rs
@@ -0,0 +1,232 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! MMU v2 page table types for Turing and Ampere GPUs.
+//!
+//! This module defines MMU version 2 specific types (Turing, Ampere and Ada GPUs).
+//!
+//! Bit field layouts derived from the NVIDIA OpenRM documentation:
+//! `open-gpu-kernel-modules/src/common/inc/swref/published/turing/tu102/dev_mmu.h`
+
+#![expect(dead_code)]
+
+use super::{
+ AperturePde,
+ AperturePte,
+ PageTableLevel,
+ VaLevelIndex, //
+};
+use crate::mm::{
+ Pfn,
+ VirtualAddress,
+ VramAddress, //
+};
+
+bitfield! {
+ pub(crate) struct VirtualAddressV2(u64), "MMU v2 49-bit virtual address layout" {
+ 11:0 offset as u64, "Page offset [11:0]";
+ 20:12 pt_idx as u64, "PT index [20:12]";
+ 28:21 pde0_idx as u64, "PDE0 index [28:21]";
+ 37:29 pde1_idx as u64, "PDE1 index [37:29]";
+ 46:38 pde2_idx as u64, "PDE2 index [46:38]";
+ 48:47 pde3_idx as u64, "PDE3 index [48:47]";
+ }
+}
+
+impl VirtualAddressV2 {
+ /// Create a [`VirtualAddressV2`] from a [`VirtualAddress`].
+ pub(crate) fn new(va: VirtualAddress) -> Self {
+ Self(va.raw_u64())
+ }
+}
+
+impl VaLevelIndex for VirtualAddressV2 {
+ fn level_index(&self, level: u64) -> u64 {
+ match level {
+ 0 => self.pde3_idx(),
+ 1 => self.pde2_idx(),
+ 2 => self.pde1_idx(),
+ 3 => self.pde0_idx(),
+ 4 => self.pt_idx(),
+ _ => 0,
+ }
+ }
+}
+
+/// `PDE` levels for MMU v2 (5-level hierarchy: `PDB` -> `L1` -> `L2` -> `L3` -> `L4`).
+pub(crate) const PDE_LEVELS: &[PageTableLevel] = &[
+ PageTableLevel::Pdb,
+ PageTableLevel::L1,
+ PageTableLevel::L2,
+ PageTableLevel::L3,
+];
+
+/// `PTE` level for MMU v2.
+pub(crate) const PTE_LEVEL: PageTableLevel = PageTableLevel::L4;
+
+/// Dual `PDE` level for MMU v2 (128-bit entries).
+pub(crate) const DUAL_PDE_LEVEL: PageTableLevel = PageTableLevel::L3;
+
+// Page Table Entry (PTE) for MMU v2 - 64-bit entry at level 4.
+bitfield! {
+ pub(crate) struct Pte(u64), "Page Table Entry for MMU v2" {
+ 0:0 valid as bool, "Entry is valid";
+ 2:1 aperture as u8 => AperturePte, "Memory aperture type";
+ 3:3 volatile as bool, "Volatile (bypass L2 cache)";
+ 4:4 encrypted as bool, "Encryption enabled (Confidential Computing)";
+ 5:5 privilege as bool, "Privileged access only";
+ 6:6 read_only as bool, "Write protection";
+ 7:7 atomic_disable as bool, "Atomic operations disabled";
+ 53:8 frame_number_sys as u64 => Pfn, "Frame number for system memory";
+ 32:8 frame_number_vid as u64 => Pfn, "Frame number for video memory";
+ 35:33 peer_id as u8, "Peer GPU ID for peer memory (0-7)";
+ 53:36 comptagline as u32, "Compression tag line bits";
+ 63:56 kind as u8, "Surface kind/format";
+ }
+}
+
+impl Pte {
+ /// Create a `PTE` from a `u64` value.
+ pub(crate) fn new(val: u64) -> Self {
+ Self(val)
+ }
+
+ /// Create a valid `PTE` for video memory.
+ pub(crate) fn new_vram(pfn: Pfn, writable: bool) -> Self {
+ Self::default()
+ .set_valid(true)
+ .set_aperture(AperturePte::VideoMemory)
+ .set_frame_number_vid(pfn)
+ .set_read_only(!writable)
+ }
+
+ /// Create an invalid `PTE`.
+ pub(crate) fn invalid() -> Self {
+ Self::default()
+ }
+
+ /// Get the frame number based on aperture type.
+ pub(crate) fn frame_number(&self) -> Pfn {
+ match self.aperture() {
+ AperturePte::VideoMemory => self.frame_number_vid(),
+ _ => self.frame_number_sys(),
+ }
+ }
+
+ /// Get the raw `u64` value.
+ pub(crate) fn raw_u64(&self) -> u64 {
+ self.0
+ }
+}
+
+// Page Directory Entry (PDE) for MMU v2 - 64-bit entry at levels 0-2.
+bitfield! {
+ pub(crate) struct Pde(u64), "Page Directory Entry for MMU v2" {
+ 0:0 valid_inverted as bool, "Valid bit (inverted logic)";
+ 2:1 aperture as u8 => AperturePde, "Memory aperture type";
+ 3:3 volatile as bool, "Volatile (bypass L2 cache)";
+ 5:5 no_ats as bool, "Disable Address Translation Services";
+ 53:8 table_frame_sys as u64 => Pfn, "Table frame number for system memory";
+ 32:8 table_frame_vid as u64 => Pfn, "Table frame number for video memory";
+ 35:33 peer_id as u8, "Peer GPU ID (0-7)";
+ }
+}
+
+impl Pde {
+ /// Create a `PDE` from a `u64` value.
+ pub(crate) fn new(val: u64) -> Self {
+ Self(val)
+ }
+
+ /// Create a valid `PDE` pointing to a page table in video memory.
+ pub(crate) fn new_vram(table_pfn: Pfn) -> Self {
+ Self::default()
+ .set_valid_inverted(false) // 0 = valid
+ .set_aperture(AperturePde::VideoMemory)
+ .set_table_frame_vid(table_pfn)
+ }
+
+ /// Create an invalid `PDE`.
+ pub(crate) fn invalid() -> Self {
+ Self::default()
+ .set_valid_inverted(true)
+ .set_aperture(AperturePde::Invalid)
+ }
+
+ /// Check if this `PDE` is valid.
+ pub(crate) fn is_valid(&self) -> bool {
+ !self.valid_inverted() && self.aperture() != AperturePde::Invalid
+ }
+
+ /// Get the table frame number based on aperture type.
+ pub(crate) fn table_frame(&self) -> Pfn {
+ match self.aperture() {
+ AperturePde::VideoMemory => self.table_frame_vid(),
+ _ => self.table_frame_sys(),
+ }
+ }
+
+ /// Get the `VRAM` address of the page table.
+ pub(crate) fn table_vram_address(&self) -> VramAddress {
+ debug_assert!(
+ self.aperture() == AperturePde::VideoMemory,
+ "table_vram_address called on non-VRAM PDE (aperture: {:?})",
+ self.aperture()
+ );
+ VramAddress::from(self.table_frame_vid())
+ }
+
+ /// Get the raw `u64` value of the `PDE`.
+ pub(crate) fn raw_u64(&self) -> u64 {
+ self.0
+ }
+}
+
+/// Dual `PDE` at Level 3 - 128-bit entry of Large/Small Page Table pointers.
+///
+/// The dual `PDE` supports both large (64KB) and small (4KB) page tables.
+#[repr(C)]
+#[derive(Debug, Clone, Copy, Default)]
+pub(crate) struct DualPde {
+ /// Large/Big Page Table pointer (lower 64 bits).
+ pub(crate) big: Pde,
+ /// Small Page Table pointer (upper 64 bits).
+ pub(crate) small: Pde,
+}
+
+impl DualPde {
+ /// Create a dual `PDE` from raw 128-bit value (two `u64`s).
+ pub(crate) fn new(big: u64, small: u64) -> Self {
+ Self {
+ big: Pde::new(big),
+ small: Pde::new(small),
+ }
+ }
+
+ /// Create a dual `PDE` with only the small page table pointer set.
+ ///
+ /// Note: The big (LPT) portion is set to 0, not `Pde::invalid()`.
+ /// According to hardware documentation, clearing bit 0 of the 128-bit
+ /// entry makes the PDE behave as a "normal" PDE. Using `Pde::invalid()`
+ /// would set bit 0 (valid_inverted), which breaks page table walking.
+ pub(crate) fn new_small(table_pfn: Pfn) -> Self {
+ Self {
+ big: Pde::new(0),
+ small: Pde::new_vram(table_pfn),
+ }
+ }
+
+ /// Check if the small page table pointer is valid.
+ pub(crate) fn has_small(&self) -> bool {
+ self.small.is_valid()
+ }
+
+ /// Check if the big page table pointer is valid.
+ pub(crate) fn has_big(&self) -> bool {
+ self.big.is_valid()
+ }
+
+ /// Get the small page table `Pfn`.
+ pub(crate) fn small_pfn(&self) -> Pfn {
+ self.small.table_frame()
+ }
+}
--
2.34.1
^ permalink raw reply related
* [PATCH v10 11/21] gpu: nova-core: mm: Add MMU v3 page table types
From: Joel Fernandes @ 2026-03-31 21:20 UTC (permalink / raw)
To: linux-kernel
Cc: Miguel Ojeda, Boqun Feng, Gary Guo, Bjorn Roy Baron, Benno Lossin,
Andreas Hindborg, Alice Ryhl, Trevor Gross, Danilo Krummrich,
Dave Airlie, Daniel Almeida, Koen Koning, dri-devel,
rust-for-linux, Nikola Djukic, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Jonathan Corbet,
Alex Deucher, Christian Koenig, Jani Nikula, Joonas Lahtinen,
Rodrigo Vivi, Tvrtko Ursulin, Huang Rui, Matthew Auld,
Matthew Brost, Lucas De Marchi, Thomas Hellstrom, Helge Deller,
Alex Gaynor, Boqun Feng, John Hubbard, Alistair Popple,
Timur Tabi, Edwin Peer, Alexandre Courbot, Andrea Righi,
Andy Ritger, Zhi Wang, Balbir Singh, Philipp Stanner,
Elle Rhumsaa, alexeyi, Eliot Courtney, joel, linux-doc, amd-gfx,
intel-gfx, intel-xe, linux-fbdev, Joel Fernandes
In-Reply-To: <20260331212048.2229260-1-joelagnelf@nvidia.com>
Add page table entry and directory structures for MMU version 3
used by Hopper and later GPUs.
Cc: Nikola Djukic <ndjukic@nvidia.com>
Signed-off-by: Joel Fernandes <joelagnelf@nvidia.com>
---
drivers/gpu/nova-core/mm/pagetable.rs | 1 +
drivers/gpu/nova-core/mm/pagetable/ver3.rs | 337 +++++++++++++++++++++
2 files changed, 338 insertions(+)
create mode 100644 drivers/gpu/nova-core/mm/pagetable/ver3.rs
diff --git a/drivers/gpu/nova-core/mm/pagetable.rs b/drivers/gpu/nova-core/mm/pagetable.rs
index 38d88f8f09a9..6e01a1af5222 100644
--- a/drivers/gpu/nova-core/mm/pagetable.rs
+++ b/drivers/gpu/nova-core/mm/pagetable.rs
@@ -9,6 +9,7 @@
#![expect(dead_code)]
pub(crate) mod ver2;
+pub(crate) mod ver3;
use crate::gpu::Architecture;
diff --git a/drivers/gpu/nova-core/mm/pagetable/ver3.rs b/drivers/gpu/nova-core/mm/pagetable/ver3.rs
new file mode 100644
index 000000000000..ef517673944e
--- /dev/null
+++ b/drivers/gpu/nova-core/mm/pagetable/ver3.rs
@@ -0,0 +1,337 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! MMU v3 page table types for Hopper and later GPUs.
+//!
+//! This module defines MMU version 3 specific types (Hopper and later GPUs).
+//!
+//! Key differences from MMU v2:
+//! - Unified 40-bit address field for all apertures (v2 had separate sys/vid fields).
+//! - PCF (Page Classification Field) replaces separate privilege/RO/atomic/cache bits.
+//! - KIND field is 4 bits (not 8).
+//! - IS_PTE bit in PDE to support large pages directly.
+//! - No COMPTAGLINE field (compression handled differently in v3).
+//! - No separate ENCRYPTED bit.
+//!
+//! Bit field layouts derived from the NVIDIA OpenRM documentation:
+//! `open-gpu-kernel-modules/src/common/inc/swref/published/hopper/gh100/dev_mmu.h`
+
+#![expect(dead_code)]
+
+use super::{
+ AperturePde,
+ AperturePte,
+ PageTableLevel,
+ VaLevelIndex, //
+};
+use crate::mm::{
+ Pfn,
+ VirtualAddress,
+ VramAddress, //
+};
+use kernel::prelude::*;
+
+bitfield! {
+ pub(crate) struct VirtualAddressV3(u64), "MMU v3 57-bit virtual address layout" {
+ 11:0 offset as u64, "Page offset [11:0]";
+ 20:12 pt_idx as u64, "PT index [20:12]";
+ 28:21 pde0_idx as u64, "PDE0 index [28:21]";
+ 37:29 pde1_idx as u64, "PDE1 index [37:29]";
+ 46:38 pde2_idx as u64, "PDE2 index [46:38]";
+ 55:47 pde3_idx as u64, "PDE3 index [55:47]";
+ 56:56 pde4_idx as u64, "PDE4 index [56]";
+ }
+}
+
+impl VirtualAddressV3 {
+ /// Create a [`VirtualAddressV3`] from a [`VirtualAddress`].
+ pub(crate) fn new(va: VirtualAddress) -> Self {
+ Self(va.raw_u64())
+ }
+}
+
+impl VaLevelIndex for VirtualAddressV3 {
+ fn level_index(&self, level: u64) -> u64 {
+ match level {
+ 0 => self.pde4_idx(),
+ 1 => self.pde3_idx(),
+ 2 => self.pde2_idx(),
+ 3 => self.pde1_idx(),
+ 4 => self.pde0_idx(),
+ 5 => self.pt_idx(),
+ _ => 0,
+ }
+ }
+}
+
+/// PDE levels for MMU v3 (6-level hierarchy).
+pub(crate) const PDE_LEVELS: &[PageTableLevel] = &[
+ PageTableLevel::Pdb,
+ PageTableLevel::L1,
+ PageTableLevel::L2,
+ PageTableLevel::L3,
+ PageTableLevel::L4,
+];
+
+/// PTE level for MMU v3.
+pub(crate) const PTE_LEVEL: PageTableLevel = PageTableLevel::L5;
+
+/// Dual PDE level for MMU v3 (128-bit entries).
+pub(crate) const DUAL_PDE_LEVEL: PageTableLevel = PageTableLevel::L4;
+
+// Page Classification Field (PCF) - 5 bits for PTEs in MMU v3.
+bitfield! {
+ pub(crate) struct PtePcf(u8), "Page Classification Field for PTEs" {
+ 0:0 uncached as bool, "Bypass L2 cache (0=cached, 1=bypass)";
+ 1:1 acd as bool, "Access counting disabled (0=enabled, 1=disabled)";
+ 2:2 read_only as bool, "Read-only access (0=read-write, 1=read-only)";
+ 3:3 no_atomic as bool, "Atomics disabled (0=enabled, 1=disabled)";
+ 4:4 privileged as bool, "Privileged access only (0=regular, 1=privileged)";
+ }
+}
+
+impl PtePcf {
+ /// Create PCF for read-write mapping (cached, no atomics, regular mode).
+ pub(crate) fn rw() -> Self {
+ Self::default().set_no_atomic(true)
+ }
+
+ /// Create PCF for read-only mapping (cached, no atomics, regular mode).
+ pub(crate) fn ro() -> Self {
+ Self::default().set_read_only(true).set_no_atomic(true)
+ }
+
+ /// Get the raw `u8` value.
+ pub(crate) fn raw_u8(&self) -> u8 {
+ self.0
+ }
+}
+
+impl From<u8> for PtePcf {
+ fn from(val: u8) -> Self {
+ Self(val)
+ }
+}
+
+// Page Classification Field (PCF) - 3 bits for PDEs in MMU v3.
+// Controls Address Translation Services (ATS) and caching.
+bitfield! {
+ pub(crate) struct PdePcf(u8), "Page Classification Field for PDEs" {
+ 0:0 uncached as bool, "Bypass L2 cache (0=cached, 1=bypass)";
+ 1:1 no_ats as bool, "ATS disabled (0=enabled, 1=disabled)";
+ }
+}
+
+impl PdePcf {
+ /// Create PCF for cached mapping with ATS enabled (default).
+ pub(crate) fn cached() -> Self {
+ Self::default()
+ }
+
+ /// Get the raw `u8` value.
+ pub(crate) fn raw_u8(&self) -> u8 {
+ self.0
+ }
+}
+
+impl From<u8> for PdePcf {
+ fn from(val: u8) -> Self {
+ Self(val)
+ }
+}
+
+// Page Table Entry (PTE) for MMU v3.
+bitfield! {
+ pub(crate) struct Pte(u64), "Page Table Entry for MMU v3" {
+ 0:0 valid as bool, "Entry is valid";
+ 2:1 aperture as u8 => AperturePte, "Memory aperture type";
+ 7:3 pcf as u8 => PtePcf, "Page Classification Field";
+ 11:8 kind as u8, "Surface kind (4 bits, 0x0=pitch, 0xF=invalid)";
+ 51:12 frame_number as u64 => Pfn, "Physical frame number (for all apertures)";
+ 63:61 peer_id as u8, "Peer GPU ID for peer memory (0-7)";
+ }
+}
+
+impl Pte {
+ /// Create a PTE from a `u64` value.
+ pub(crate) fn new(val: u64) -> Self {
+ Self(val)
+ }
+
+ /// Create a valid PTE for video memory.
+ pub(crate) fn new_vram(frame: Pfn, writable: bool) -> Self {
+ let pcf = if writable { PtePcf::rw() } else { PtePcf::ro() };
+ Self::default()
+ .set_valid(true)
+ .set_aperture(AperturePte::VideoMemory)
+ .set_pcf(pcf)
+ .set_frame_number(frame)
+ }
+
+ /// Create an invalid PTE.
+ pub(crate) fn invalid() -> Self {
+ Self::default()
+ }
+
+ /// Get the raw `u64` value.
+ pub(crate) fn raw_u64(&self) -> u64 {
+ self.0
+ }
+}
+
+// Page Directory Entry (PDE) for MMU v3.
+//
+// Note: v3 uses a unified 40-bit address field (v2 had separate sys/vid address fields).
+bitfield! {
+ pub(crate) struct Pde(u64), "Page Directory Entry for MMU v3 (Hopper+)" {
+ 0:0 is_pte as bool, "Entry is a PTE (0=PDE, 1=large page PTE)";
+ 2:1 aperture as u8 => AperturePde, "Memory aperture type";
+ 5:3 pcf as u8 => PdePcf, "Page Classification Field (3 bits for PDE)";
+ 51:12 table_frame as u64 => Pfn, "Table frame number (40-bit unified address)";
+ }
+}
+
+impl Pde {
+ /// Create a PDE from a `u64` value.
+ pub(crate) fn new(val: u64) -> Self {
+ Self(val)
+ }
+
+ /// Create a valid PDE pointing to a page table in video memory.
+ pub(crate) fn new_vram(table_pfn: Pfn) -> Self {
+ Self::default()
+ .set_is_pte(false)
+ .set_aperture(AperturePde::VideoMemory)
+ .set_table_frame(table_pfn)
+ }
+
+ /// Create an invalid PDE.
+ pub(crate) fn invalid() -> Self {
+ Self::default().set_aperture(AperturePde::Invalid)
+ }
+
+ /// Check if this PDE is valid.
+ pub(crate) fn is_valid(&self) -> bool {
+ self.aperture() != AperturePde::Invalid
+ }
+
+ /// Get the VRAM address of the page table.
+ pub(crate) fn table_vram_address(&self) -> VramAddress {
+ debug_assert!(
+ self.aperture() == AperturePde::VideoMemory,
+ "table_vram_address called on non-VRAM PDE (aperture: {:?})",
+ self.aperture()
+ );
+ VramAddress::from(self.table_frame())
+ }
+
+ /// Get the raw `u64` value.
+ pub(crate) fn raw_u64(&self) -> u64 {
+ self.0
+ }
+}
+
+// Big Page Table pointer for Dual PDE - 64-bit lower word of the 128-bit Dual PDE.
+bitfield! {
+ pub(crate) struct DualPdeBig(u64), "Big Page Table pointer in Dual PDE (MMU v3)" {
+ 0:0 is_pte as bool, "Entry is a PTE (for large pages)";
+ 2:1 aperture as u8 => AperturePde, "Memory aperture type";
+ 5:3 pcf as u8 => PdePcf, "Page Classification Field";
+ 51:8 table_frame as u64, "Table frame (table address 256-byte aligned)";
+ }
+}
+
+impl DualPdeBig {
+ /// Create a big page table pointer from a `u64` value.
+ pub(crate) fn new(val: u64) -> Self {
+ Self(val)
+ }
+
+ /// Create an invalid big page table pointer.
+ pub(crate) fn invalid() -> Self {
+ Self::default().set_aperture(AperturePde::Invalid)
+ }
+
+ /// Create a valid big PDE pointing to a page table in video memory.
+ pub(crate) fn new_vram(table_addr: VramAddress) -> Result<Self> {
+ // Big page table addresses must be 256-byte aligned (shift 8).
+ if table_addr.raw_u64() & 0xFF != 0 {
+ return Err(EINVAL);
+ }
+
+ let table_frame = table_addr.raw_u64() >> 8;
+ Ok(Self::default()
+ .set_is_pte(false)
+ .set_aperture(AperturePde::VideoMemory)
+ .set_table_frame(table_frame))
+ }
+
+ /// Check if this big PDE is valid.
+ pub(crate) fn is_valid(&self) -> bool {
+ self.aperture() != AperturePde::Invalid
+ }
+
+ /// Get the VRAM address of the big page table.
+ pub(crate) fn table_vram_address(&self) -> VramAddress {
+ debug_assert!(
+ self.aperture() == AperturePde::VideoMemory,
+ "table_vram_address called on non-VRAM DualPdeBig (aperture: {:?})",
+ self.aperture()
+ );
+ VramAddress::new(self.table_frame() << 8)
+ }
+
+ /// Get the raw `u64` value.
+ pub(crate) fn raw_u64(&self) -> u64 {
+ self.0
+ }
+}
+
+/// Dual PDE at Level 4 for MMU v3 - 128-bit entry.
+///
+/// Contains both big (64KB) and small (4KB) page table pointers:
+/// - Lower 64 bits: Big Page Table pointer.
+/// - Upper 64 bits: Small Page Table pointer.
+///
+/// ## Note
+///
+/// The big and small page table pointers have different address layouts:
+/// - Big address = field value << 8 (256-byte alignment).
+/// - Small address = field value << 12 (4KB alignment).
+///
+/// This is why `DualPdeBig` is a separate type from `Pde`.
+#[repr(C)]
+#[derive(Debug, Clone, Copy, Default)]
+pub(crate) struct DualPde {
+ /// Big Page Table pointer.
+ pub(crate) big: DualPdeBig,
+ /// Small Page Table pointer.
+ pub(crate) small: Pde,
+}
+
+impl DualPde {
+ /// Create a dual PDE from raw 128-bit value (two `u64`s).
+ pub(crate) fn new(big: u64, small: u64) -> Self {
+ Self {
+ big: DualPdeBig::new(big),
+ small: Pde::new(small),
+ }
+ }
+
+ /// Create a dual PDE with only the small page table pointer set.
+ pub(crate) fn new_small(table_pfn: Pfn) -> Self {
+ Self {
+ big: DualPdeBig::invalid(),
+ small: Pde::new_vram(table_pfn),
+ }
+ }
+
+ /// Check if the small page table pointer is valid.
+ pub(crate) fn has_small(&self) -> bool {
+ self.small.is_valid()
+ }
+
+ /// Check if the big page table pointer is valid.
+ pub(crate) fn has_big(&self) -> bool {
+ self.big.is_valid()
+ }
+}
--
2.34.1
^ permalink raw reply related
* [PATCH v10 12/21] gpu: nova-core: mm: Add unified page table entry wrapper enums
From: Joel Fernandes @ 2026-03-31 21:20 UTC (permalink / raw)
To: linux-kernel
Cc: Miguel Ojeda, Boqun Feng, Gary Guo, Bjorn Roy Baron, Benno Lossin,
Andreas Hindborg, Alice Ryhl, Trevor Gross, Danilo Krummrich,
Dave Airlie, Daniel Almeida, Koen Koning, dri-devel,
rust-for-linux, Nikola Djukic, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Jonathan Corbet,
Alex Deucher, Christian Koenig, Jani Nikula, Joonas Lahtinen,
Rodrigo Vivi, Tvrtko Ursulin, Huang Rui, Matthew Auld,
Matthew Brost, Lucas De Marchi, Thomas Hellstrom, Helge Deller,
Alex Gaynor, Boqun Feng, John Hubbard, Alistair Popple,
Timur Tabi, Edwin Peer, Alexandre Courbot, Andrea Righi,
Andy Ritger, Zhi Wang, Balbir Singh, Philipp Stanner,
Elle Rhumsaa, alexeyi, Eliot Courtney, joel, linux-doc, amd-gfx,
intel-gfx, intel-xe, linux-fbdev, Joel Fernandes
In-Reply-To: <20260331212048.2229260-1-joelagnelf@nvidia.com>
Add unified Pte, Pde, and DualPde wrapper enums that abstract over
MMU v2 and v3 page table entry formats. These enums allow the page
table walker and VMM to work with both MMU versions.
Each unified type:
- Takes MmuVersion parameter in constructors
- Wraps both ver2 and ver3 variants
- Delegates method calls to the appropriate variant
This enables version-agnostic page table operations while keeping
version-specific implementation details encapsulated in the ver2
and ver3 modules.
Cc: Nikola Djukic <ndjukic@nvidia.com>
Signed-off-by: Joel Fernandes <joelagnelf@nvidia.com>
---
drivers/gpu/nova-core/mm/pagetable.rs | 330 ++++++++++++++++++++++++++
1 file changed, 330 insertions(+)
diff --git a/drivers/gpu/nova-core/mm/pagetable.rs b/drivers/gpu/nova-core/mm/pagetable.rs
index 6e01a1af5222..909df37c3ee8 100644
--- a/drivers/gpu/nova-core/mm/pagetable.rs
+++ b/drivers/gpu/nova-core/mm/pagetable.rs
@@ -12,6 +12,13 @@
pub(crate) mod ver3;
use crate::gpu::Architecture;
+use crate::mm::{
+ pramin,
+ Pfn,
+ VirtualAddress,
+ VramAddress, //
+};
+use kernel::prelude::*;
/// Extracts the page table index at a given level from a virtual address.
pub(crate) trait VaLevelIndex {
@@ -84,6 +91,96 @@ pub(crate) const fn as_index(&self) -> u64 {
}
}
+impl MmuVersion {
+ /// Get the `PDE` levels (excluding PTE level) for page table walking.
+ pub(crate) fn pde_levels(&self) -> &'static [PageTableLevel] {
+ match self {
+ Self::V2 => ver2::PDE_LEVELS,
+ Self::V3 => ver3::PDE_LEVELS,
+ }
+ }
+
+ /// Get the PTE level for this MMU version.
+ pub(crate) fn pte_level(&self) -> PageTableLevel {
+ match self {
+ Self::V2 => ver2::PTE_LEVEL,
+ Self::V3 => ver3::PTE_LEVEL,
+ }
+ }
+
+ /// Get the dual PDE level (128-bit entries) for this MMU version.
+ pub(crate) fn dual_pde_level(&self) -> PageTableLevel {
+ match self {
+ Self::V2 => ver2::DUAL_PDE_LEVEL,
+ Self::V3 => ver3::DUAL_PDE_LEVEL,
+ }
+ }
+
+ /// Get the number of PDE levels for this MMU version.
+ pub(crate) fn pde_level_count(&self) -> usize {
+ self.pde_levels().len()
+ }
+
+ /// Get the entry size in bytes for a given level.
+ pub(crate) fn entry_size(&self, level: PageTableLevel) -> usize {
+ if level == self.dual_pde_level() {
+ 16 // 128-bit dual PDE
+ } else {
+ 8 // 64-bit PDE/PTE
+ }
+ }
+
+ /// Get the number of entries per page table page for a given level.
+ pub(crate) fn entries_per_page(&self, level: PageTableLevel) -> usize {
+ match self {
+ Self::V2 => match level {
+ // TODO: Calculate these values from the bitfield dynamically
+ // instead of hardcoding them.
+ PageTableLevel::Pdb => 4, // PD3 root: bits [48:47] = 2 bits
+ PageTableLevel::L3 => 256, // PD0 dual: bits [28:21] = 8 bits
+ _ => 512, // PD2, PD1, PT: 9 bits each
+ },
+ Self::V3 => match level {
+ PageTableLevel::Pdb => 2, // PDE4 root: bit [56] = 1 bit, 2 entries
+ PageTableLevel::L4 => 256, // PDE0 dual: bits [28:21] = 8 bits
+ _ => 512, // PDE3, PDE2, PDE1, PT: 9 bits each
+ },
+ }
+ }
+
+ /// Extract the page table index at `level` from `va` for this MMU version.
+ pub(crate) fn level_index(&self, va: VirtualAddress, level: u64) -> u64 {
+ match self {
+ Self::V2 => ver2::VirtualAddressV2::new(va).level_index(level),
+ Self::V3 => ver3::VirtualAddressV3::new(va).level_index(level),
+ }
+ }
+
+ /// Compute upper bound on page table pages needed for `num_virt_pages`.
+ ///
+ /// Walks from PTE level up through PDE levels, accumulating the tree.
+ pub(crate) fn pt_pages_upper_bound(&self, num_virt_pages: usize) -> usize {
+ let mut total = 0;
+
+ // PTE pages at the leaf level.
+ let pte_epp = self.entries_per_page(self.pte_level());
+ let mut pages_at_level = num_virt_pages.div_ceil(pte_epp);
+ total += pages_at_level;
+
+ // Walk PDE levels bottom-up (reverse of pde_levels()).
+ for &level in self.pde_levels().iter().rev() {
+ let epp = self.entries_per_page(level);
+
+ // How many pages at this level do we need to point to
+ // the previous pages_at_level?
+ pages_at_level = pages_at_level.div_ceil(epp);
+ total += pages_at_level;
+ }
+
+ total
+ }
+}
+
/// Memory aperture for Page Table Entries (`PTE`s).
///
/// Determines which memory region the `PTE` points to.
@@ -156,3 +253,236 @@ fn from(val: AperturePde) -> Self {
val as u8
}
}
+
+/// Unified Page Table Entry wrapper for both MMU v2 and v3 `PTE`
+/// types, allowing the walker to work with either format.
+#[derive(Debug, Clone, Copy)]
+pub(crate) enum Pte {
+ /// MMU v2 `PTE` (Turing/Ampere/Ada).
+ V2(ver2::Pte),
+ /// MMU v3 `PTE` (Hopper+).
+ V3(ver3::Pte),
+}
+
+impl Pte {
+ /// Create a `PTE` from a raw `u64` value for the given MMU version.
+ pub(crate) fn new(version: MmuVersion, val: u64) -> Self {
+ match version {
+ MmuVersion::V2 => Self::V2(ver2::Pte::new(val)),
+ MmuVersion::V3 => Self::V3(ver3::Pte::new(val)),
+ }
+ }
+
+ /// Create an invalid `PTE` for the given MMU version.
+ pub(crate) fn invalid(version: MmuVersion) -> Self {
+ match version {
+ MmuVersion::V2 => Self::V2(ver2::Pte::invalid()),
+ MmuVersion::V3 => Self::V3(ver3::Pte::invalid()),
+ }
+ }
+
+ /// Create a valid `PTE` for video memory.
+ pub(crate) fn new_vram(version: MmuVersion, pfn: Pfn, writable: bool) -> Self {
+ match version {
+ MmuVersion::V2 => Self::V2(ver2::Pte::new_vram(pfn, writable)),
+ MmuVersion::V3 => Self::V3(ver3::Pte::new_vram(pfn, writable)),
+ }
+ }
+
+ /// Check if this `PTE` is valid.
+ pub(crate) fn is_valid(&self) -> bool {
+ match self {
+ Self::V2(p) => p.valid(),
+ Self::V3(p) => p.valid(),
+ }
+ }
+
+ /// Get the physical frame number.
+ pub(crate) fn frame_number(&self) -> Pfn {
+ match self {
+ Self::V2(p) => p.frame_number(),
+ Self::V3(p) => p.frame_number(),
+ }
+ }
+
+ /// Get the raw `u64` value.
+ pub(crate) fn raw_u64(&self) -> u64 {
+ match self {
+ Self::V2(p) => p.raw_u64(),
+ Self::V3(p) => p.raw_u64(),
+ }
+ }
+
+ /// Read a `PTE` from VRAM.
+ pub(crate) fn read(
+ window: &mut pramin::PraminWindow<'_>,
+ addr: VramAddress,
+ mmu_version: MmuVersion,
+ ) -> Result<Self> {
+ let val = window.try_read64(addr.raw())?;
+ Ok(Self::new(mmu_version, val))
+ }
+
+ /// Write this `PTE` to VRAM.
+ pub(crate) fn write(&self, window: &mut pramin::PraminWindow<'_>, addr: VramAddress) -> Result {
+ window.try_write64(addr.raw(), self.raw_u64())
+ }
+}
+
+/// Unified Page Directory Entry wrapper for both MMU v2 and v3 `PDE`.
+#[derive(Debug, Clone, Copy)]
+pub(crate) enum Pde {
+ /// MMU v2 `PDE` (Turing/Ampere/Ada).
+ V2(ver2::Pde),
+ /// MMU v3 `PDE` (Hopper+).
+ V3(ver3::Pde),
+}
+
+impl Pde {
+ /// Create a `PDE` from a raw `u64` value for the given MMU version.
+ pub(crate) fn new(version: MmuVersion, val: u64) -> Self {
+ match version {
+ MmuVersion::V2 => Self::V2(ver2::Pde::new(val)),
+ MmuVersion::V3 => Self::V3(ver3::Pde::new(val)),
+ }
+ }
+
+ /// Create a valid `PDE` pointing to a page table in video memory.
+ pub(crate) fn new_vram(version: MmuVersion, table_pfn: Pfn) -> Self {
+ match version {
+ MmuVersion::V2 => Self::V2(ver2::Pde::new_vram(table_pfn)),
+ MmuVersion::V3 => Self::V3(ver3::Pde::new_vram(table_pfn)),
+ }
+ }
+
+ /// Create an invalid `PDE` for the given MMU version.
+ pub(crate) fn invalid(version: MmuVersion) -> Self {
+ match version {
+ MmuVersion::V2 => Self::V2(ver2::Pde::invalid()),
+ MmuVersion::V3 => Self::V3(ver3::Pde::invalid()),
+ }
+ }
+
+ /// Check if this `PDE` is valid.
+ pub(crate) fn is_valid(&self) -> bool {
+ match self {
+ Self::V2(p) => p.is_valid(),
+ Self::V3(p) => p.is_valid(),
+ }
+ }
+
+ /// Get the memory aperture of this `PDE`.
+ pub(crate) fn aperture(&self) -> AperturePde {
+ match self {
+ Self::V2(p) => p.aperture(),
+ Self::V3(p) => p.aperture(),
+ }
+ }
+
+ /// Get the VRAM address of the page table.
+ pub(crate) fn table_vram_address(&self) -> VramAddress {
+ match self {
+ Self::V2(p) => p.table_vram_address(),
+ Self::V3(p) => p.table_vram_address(),
+ }
+ }
+
+ /// Get the raw `u64` value.
+ pub(crate) fn raw_u64(&self) -> u64 {
+ match self {
+ Self::V2(p) => p.raw_u64(),
+ Self::V3(p) => p.raw_u64(),
+ }
+ }
+
+ /// Read a `PDE` from VRAM.
+ pub(crate) fn read(
+ window: &mut pramin::PraminWindow<'_>,
+ addr: VramAddress,
+ mmu_version: MmuVersion,
+ ) -> Result<Self> {
+ let val = window.try_read64(addr.raw())?;
+ Ok(Self::new(mmu_version, val))
+ }
+
+ /// Write this `PDE` to VRAM.
+ pub(crate) fn write(&self, window: &mut pramin::PraminWindow<'_>, addr: VramAddress) -> Result {
+ window.try_write64(addr.raw(), self.raw_u64())
+ }
+}
+
+/// Unified Dual Page Directory Entry wrapper for both MMU v2 and v3 [`DualPde`].
+#[derive(Debug, Clone, Copy)]
+pub(crate) enum DualPde {
+ /// MMU v2 [`DualPde`] (Turing/Ampere/Ada).
+ V2(ver2::DualPde),
+ /// MMU v3 [`DualPde`] (Hopper+).
+ V3(ver3::DualPde),
+}
+
+impl DualPde {
+ /// Create a [`DualPde`] from raw 128-bit value (two `u64`s) for the given MMU version.
+ pub(crate) fn new(version: MmuVersion, big: u64, small: u64) -> Self {
+ match version {
+ MmuVersion::V2 => Self::V2(ver2::DualPde::new(big, small)),
+ MmuVersion::V3 => Self::V3(ver3::DualPde::new(big, small)),
+ }
+ }
+
+ /// Create a [`DualPde`] with only the small page table pointer set.
+ pub(crate) fn new_small(version: MmuVersion, table_pfn: Pfn) -> Self {
+ match version {
+ MmuVersion::V2 => Self::V2(ver2::DualPde::new_small(table_pfn)),
+ MmuVersion::V3 => Self::V3(ver3::DualPde::new_small(table_pfn)),
+ }
+ }
+
+ /// Check if the small page table pointer is valid.
+ pub(crate) fn has_small(&self) -> bool {
+ match self {
+ Self::V2(d) => d.has_small(),
+ Self::V3(d) => d.has_small(),
+ }
+ }
+
+ /// Get the small page table VRAM address.
+ pub(crate) fn small_vram_address(&self) -> VramAddress {
+ match self {
+ Self::V2(d) => d.small.table_vram_address(),
+ Self::V3(d) => d.small.table_vram_address(),
+ }
+ }
+
+ /// Get the raw `u64` value of the big PDE.
+ pub(crate) fn big_raw_u64(&self) -> u64 {
+ match self {
+ Self::V2(d) => d.big.raw_u64(),
+ Self::V3(d) => d.big.raw_u64(),
+ }
+ }
+
+ /// Get the raw `u64` value of the small PDE.
+ pub(crate) fn small_raw_u64(&self) -> u64 {
+ match self {
+ Self::V2(d) => d.small.raw_u64(),
+ Self::V3(d) => d.small.raw_u64(),
+ }
+ }
+
+ /// Read a dual PDE (128-bit) from VRAM.
+ pub(crate) fn read(
+ window: &mut pramin::PraminWindow<'_>,
+ addr: VramAddress,
+ mmu_version: MmuVersion,
+ ) -> Result<Self> {
+ let lo = window.try_read64(addr.raw())?;
+ let hi = window.try_read64(addr.raw() + 8)?;
+ Ok(Self::new(mmu_version, lo, hi))
+ }
+
+ /// Write this dual PDE (128-bit) to VRAM.
+ pub(crate) fn write(&self, window: &mut pramin::PraminWindow<'_>, addr: VramAddress) -> Result {
+ window.try_write64(addr.raw(), self.big_raw_u64())?;
+ window.try_write64(addr.raw() + 8, self.small_raw_u64())
+ }
+}
--
2.34.1
^ permalink raw reply related
* [PATCH v10 13/21] gpu: nova-core: mm: Add page table walker for MMU v2/v3
From: Joel Fernandes @ 2026-03-31 21:20 UTC (permalink / raw)
To: linux-kernel
Cc: Miguel Ojeda, Boqun Feng, Gary Guo, Bjorn Roy Baron, Benno Lossin,
Andreas Hindborg, Alice Ryhl, Trevor Gross, Danilo Krummrich,
Dave Airlie, Daniel Almeida, Koen Koning, dri-devel,
rust-for-linux, Nikola Djukic, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Jonathan Corbet,
Alex Deucher, Christian Koenig, Jani Nikula, Joonas Lahtinen,
Rodrigo Vivi, Tvrtko Ursulin, Huang Rui, Matthew Auld,
Matthew Brost, Lucas De Marchi, Thomas Hellstrom, Helge Deller,
Alex Gaynor, Boqun Feng, John Hubbard, Alistair Popple,
Timur Tabi, Edwin Peer, Alexandre Courbot, Andrea Righi,
Andy Ritger, Zhi Wang, Balbir Singh, Philipp Stanner,
Elle Rhumsaa, alexeyi, Eliot Courtney, joel, linux-doc, amd-gfx,
intel-gfx, intel-xe, linux-fbdev, Joel Fernandes
In-Reply-To: <20260331212048.2229260-1-joelagnelf@nvidia.com>
Add the page table walker implementation that traverses the page table
hierarchy for both MMU v2 (5-level) and MMU v3 (6-level) to resolve
virtual addresses to physical addresses or find PTE locations.
Currently only v2 has been tested (nova-core currently boots pre-hopper)
with some initial preparatory work done for v3.
Cc: Nikola Djukic <ndjukic@nvidia.com>
Signed-off-by: Joel Fernandes <joelagnelf@nvidia.com>
---
drivers/gpu/nova-core/mm/pagetable.rs | 1 +
drivers/gpu/nova-core/mm/pagetable/walk.rs | 218 +++++++++++++++++++++
2 files changed, 219 insertions(+)
create mode 100644 drivers/gpu/nova-core/mm/pagetable/walk.rs
diff --git a/drivers/gpu/nova-core/mm/pagetable.rs b/drivers/gpu/nova-core/mm/pagetable.rs
index 909df37c3ee8..9acf100798d3 100644
--- a/drivers/gpu/nova-core/mm/pagetable.rs
+++ b/drivers/gpu/nova-core/mm/pagetable.rs
@@ -10,6 +10,7 @@
pub(crate) mod ver2;
pub(crate) mod ver3;
+pub(crate) mod walk;
use crate::gpu::Architecture;
use crate::mm::{
diff --git a/drivers/gpu/nova-core/mm/pagetable/walk.rs b/drivers/gpu/nova-core/mm/pagetable/walk.rs
new file mode 100644
index 000000000000..8bdaba229d34
--- /dev/null
+++ b/drivers/gpu/nova-core/mm/pagetable/walk.rs
@@ -0,0 +1,218 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! Page table walker implementation for NVIDIA GPUs.
+//!
+//! This module provides page table walking functionality for MMU v2 and v3.
+//! The walker traverses the page table hierarchy to resolve virtual addresses
+//! to physical addresses or to find PTE locations.
+//!
+//! # Page Table Hierarchy
+//!
+//! ## MMU v2 (Turing/Ampere/Ada) - 5 levels
+//!
+//! ```text
+//! +-------+ +-------+ +-------+ +---------+ +-------+
+//! | PDB |---->| L1 |---->| L2 |---->| L3 Dual |---->| L4 |
+//! | (L0) | | | | | | PDE | | (PTE) |
+//! +-------+ +-------+ +-------+ +---------+ +-------+
+//! 64-bit 64-bit 64-bit 128-bit 64-bit
+//! PDE PDE PDE (big+small) PTE
+//! ```
+//!
+//! ## MMU v3 (Hopper+) - 6 levels
+//!
+//! ```text
+//! +-------+ +-------+ +-------+ +-------+ +---------+ +-------+
+//! | PDB |---->| L1 |---->| L2 |---->| L3 |---->| L4 Dual |---->| L5 |
+//! | (L0) | | | | | | | | PDE | | (PTE) |
+//! +-------+ +-------+ +-------+ +-------+ +---------+ +-------+
+//! 64-bit 64-bit 64-bit 64-bit 128-bit 64-bit
+//! PDE PDE PDE PDE (big+small) PTE
+//! ```
+//!
+//! # Result of a page table walk
+//!
+//! The walker returns a [`WalkResult`] indicating the outcome.
+
+use kernel::prelude::*;
+
+use super::{
+ DualPde,
+ MmuVersion,
+ PageTableLevel,
+ Pde,
+ Pte, //
+};
+use crate::{
+ mm::{
+ pramin,
+ GpuMm,
+ Pfn,
+ Vfn,
+ VirtualAddress,
+ VramAddress, //
+ },
+ num::{
+ IntoSafeCast, //
+ },
+};
+
+/// Result of walking to a PTE.
+#[derive(Debug, Clone, Copy)]
+pub(crate) enum WalkResult {
+ /// Intermediate page tables are missing (only returned in lookup mode).
+ PageTableMissing,
+ /// PTE exists but is invalid (page not mapped).
+ Unmapped { pte_addr: VramAddress },
+ /// PTE exists and is valid (page is mapped).
+ Mapped { pte_addr: VramAddress, pfn: Pfn },
+}
+
+/// Result of walking PDE levels only.
+///
+/// Returned by [`PtWalk::walk_pde_levels()`] to indicate whether all PDE levels
+/// resolved or a PDE is missing.
+#[derive(Debug, Clone, Copy)]
+pub(crate) enum WalkPdeResult {
+ /// All PDE levels resolved -- returns PTE page table address.
+ Complete {
+ /// VRAM address of the PTE-level page table.
+ pte_table: VramAddress,
+ },
+ /// A PDE is missing and no prepared page was provided by the closure.
+ Missing {
+ /// PDE slot address in the parent page table (where to install).
+ install_addr: VramAddress,
+ /// The page table level that is missing.
+ level: PageTableLevel,
+ },
+}
+
+/// Page table walker for NVIDIA GPUs.
+///
+/// Walks the page table hierarchy (5 levels for v2, 6 for v3) to find PTE
+/// locations or resolve virtual addresses.
+pub(crate) struct PtWalk {
+ pdb_addr: VramAddress,
+ mmu_version: MmuVersion,
+}
+
+impl PtWalk {
+ /// Calculate the VRAM address of an entry within a page table.
+ fn entry_addr(
+ table: VramAddress,
+ mmu_version: MmuVersion,
+ level: PageTableLevel,
+ index: u64,
+ ) -> VramAddress {
+ let entry_size: u64 = mmu_version.entry_size(level).into_safe_cast();
+ VramAddress::new(table.raw_u64() + index * entry_size)
+ }
+
+ /// Create a new page table walker.
+ pub(crate) fn new(pdb_addr: VramAddress, mmu_version: MmuVersion) -> Self {
+ Self {
+ pdb_addr,
+ mmu_version,
+ }
+ }
+
+ /// Walk PDE levels with closure-based resolution for missing PDEs.
+ ///
+ /// Traverses all PDE levels for the MMU version. At each level, reads the PDE.
+ /// If valid, extracts the child table address and continues. If missing, calls
+ /// `resolve_prepared(install_addr)` to resolve the missing PDE.
+ pub(crate) fn walk_pde_levels(
+ &self,
+ window: &mut pramin::PraminWindow<'_>,
+ vfn: Vfn,
+ resolve_prepared: impl Fn(VramAddress) -> Option<VramAddress>,
+ ) -> Result<WalkPdeResult> {
+ let va = VirtualAddress::from(vfn);
+ let mut cur_table = self.pdb_addr;
+
+ for &level in self.mmu_version.pde_levels() {
+ let idx = self.mmu_version.level_index(va, level.as_index());
+ let install_addr = Self::entry_addr(cur_table, self.mmu_version, level, idx);
+
+ if level == self.mmu_version.dual_pde_level() {
+ // 128-bit dual PDE with big+small page table pointers.
+ let dpde = DualPde::read(window, install_addr, self.mmu_version)?;
+ if dpde.has_small() {
+ cur_table = dpde.small_vram_address();
+ continue;
+ }
+ } else {
+ // Regular 64-bit PDE.
+ let pde = Pde::read(window, install_addr, self.mmu_version)?;
+ if pde.is_valid() {
+ cur_table = pde.table_vram_address();
+ continue;
+ }
+ }
+
+ // PDE missing in HW. Ask caller for resolution.
+ if let Some(prepared_addr) = resolve_prepared(install_addr) {
+ cur_table = prepared_addr;
+ continue;
+ }
+
+ return Ok(WalkPdeResult::Missing {
+ install_addr,
+ level,
+ });
+ }
+
+ Ok(WalkPdeResult::Complete {
+ pte_table: cur_table,
+ })
+ }
+
+ /// Walk to PTE for lookup only (no allocation).
+ ///
+ /// Returns [`WalkResult::PageTableMissing`] if intermediate tables don't exist.
+ pub(crate) fn walk_to_pte_lookup(&self, mm: &GpuMm, vfn: Vfn) -> Result<WalkResult> {
+ let mut window = mm.pramin().get_window()?;
+ self.walk_to_pte_lookup_with_window(&mut window, vfn)
+ }
+
+ /// Walk to PTE using a caller-provided PRAMIN window (lookup only).
+ ///
+ /// Uses [`PtWalk::walk_pde_levels()`] for the PDE traversal, then reads the PTE at
+ /// the leaf level. Useful when called for multiple VFNs with single PRAMIN window
+ /// acquisition. Used by [`Vmm::execute_map()`] and [`Vmm::unmap_pages()`].
+ pub(crate) fn walk_to_pte_lookup_with_window(
+ &self,
+ window: &mut pramin::PraminWindow<'_>,
+ vfn: Vfn,
+ ) -> Result<WalkResult> {
+ match self.walk_pde_levels(window, vfn, |_| None)? {
+ WalkPdeResult::Complete { pte_table } => {
+ Self::read_pte_at_level(window, vfn, pte_table, self.mmu_version)
+ }
+ WalkPdeResult::Missing { .. } => Ok(WalkResult::PageTableMissing),
+ }
+ }
+
+ /// Read the PTE at the PTE level given the PTE table address.
+ fn read_pte_at_level(
+ window: &mut pramin::PraminWindow<'_>,
+ vfn: Vfn,
+ pte_table: VramAddress,
+ mmu_version: MmuVersion,
+ ) -> Result<WalkResult> {
+ let va = VirtualAddress::from(vfn);
+ let pte_level = mmu_version.pte_level();
+ let pte_idx = mmu_version.level_index(va, pte_level.as_index());
+ let pte_addr = Self::entry_addr(pte_table, mmu_version, pte_level, pte_idx);
+ let pte = Pte::read(window, pte_addr, mmu_version)?;
+
+ if pte.is_valid() {
+ return Ok(WalkResult::Mapped {
+ pte_addr,
+ pfn: pte.frame_number(),
+ });
+ }
+ Ok(WalkResult::Unmapped { pte_addr })
+ }
+}
--
2.34.1
^ 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