fb.rs 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217
  1. // SPDX-License-Identifier: GPL-2.0
  2. use core::ops::Range;
  3. use kernel::{
  4. device,
  5. prelude::*,
  6. ptr::{
  7. Alignable,
  8. Alignment, //
  9. },
  10. sizes::*,
  11. sync::aref::ARef, //
  12. };
  13. use crate::{
  14. dma::DmaObject,
  15. driver::Bar0,
  16. firmware::gsp::GspFirmware,
  17. gpu::Chipset,
  18. gsp,
  19. num::{
  20. usize_as_u64,
  21. FromSafeCast, //
  22. },
  23. regs,
  24. };
  25. mod hal;
  26. /// Type holding the sysmem flush memory page, a page of memory to be written into the
  27. /// `NV_PFB_NISO_FLUSH_SYSMEM_ADDR*` registers and used to maintain memory coherency.
  28. ///
  29. /// A system memory page is required for `sysmembar`, which is a GPU-initiated hardware
  30. /// memory-barrier operation that flushes all pending GPU-side memory writes that were done through
  31. /// PCIE to system memory. It is required for falcons to be reset as the reset operation involves a
  32. /// reset handshake. When the falcon acknowledges a reset, it writes into system memory. To ensure
  33. /// this write is visible to the host and prevent driver timeouts, the falcon must perform a
  34. /// sysmembar operation to flush its writes.
  35. ///
  36. /// Because of this, the sysmem flush memory page must be registered as early as possible during
  37. /// driver initialization, and before any falcon is reset.
  38. ///
  39. /// Users are responsible for manually calling [`Self::unregister`] before dropping this object,
  40. /// otherwise the GPU might still use it even after it has been freed.
  41. pub(crate) struct SysmemFlush {
  42. /// Chipset we are operating on.
  43. chipset: Chipset,
  44. device: ARef<device::Device>,
  45. /// Keep the page alive as long as we need it.
  46. page: DmaObject,
  47. }
  48. impl SysmemFlush {
  49. /// Allocate a memory page and register it as the sysmem flush page.
  50. pub(crate) fn register(
  51. dev: &device::Device<device::Bound>,
  52. bar: &Bar0,
  53. chipset: Chipset,
  54. ) -> Result<Self> {
  55. let page = DmaObject::new(dev, kernel::page::PAGE_SIZE)?;
  56. hal::fb_hal(chipset).write_sysmem_flush_page(bar, page.dma_handle())?;
  57. Ok(Self {
  58. chipset,
  59. device: dev.into(),
  60. page,
  61. })
  62. }
  63. /// Unregister the managed sysmem flush page.
  64. ///
  65. /// In order to gracefully tear down the GPU, users must make sure to call this method before
  66. /// dropping the object.
  67. pub(crate) fn unregister(&self, bar: &Bar0) {
  68. let hal = hal::fb_hal(self.chipset);
  69. if hal.read_sysmem_flush_page(bar) == self.page.dma_handle() {
  70. let _ = hal.write_sysmem_flush_page(bar, 0).inspect_err(|e| {
  71. dev_warn!(
  72. &self.device,
  73. "failed to unregister sysmem flush page: {:?}\n",
  74. e
  75. )
  76. });
  77. } else {
  78. // Another page has been registered after us for some reason - warn as this is a bug.
  79. dev_warn!(
  80. &self.device,
  81. "attempt to unregister a sysmem flush page that is not active\n"
  82. );
  83. }
  84. }
  85. }
  86. /// Layout of the GPU framebuffer memory.
  87. ///
  88. /// Contains ranges of GPU memory reserved for a given purpose during the GSP boot process.
  89. #[derive(Debug)]
  90. pub(crate) struct FbLayout {
  91. /// Range of the framebuffer. Starts at `0`.
  92. pub(crate) fb: Range<u64>,
  93. /// VGA workspace, small area of reserved memory at the end of the framebuffer.
  94. pub(crate) vga_workspace: Range<u64>,
  95. /// FRTS range.
  96. pub(crate) frts: Range<u64>,
  97. /// Memory area containing the GSP bootloader image.
  98. pub(crate) boot: Range<u64>,
  99. /// Memory area containing the GSP firmware image.
  100. pub(crate) elf: Range<u64>,
  101. /// WPR2 heap.
  102. pub(crate) wpr2_heap: Range<u64>,
  103. /// WPR2 region range, starting with an instance of `GspFwWprMeta`.
  104. pub(crate) wpr2: Range<u64>,
  105. pub(crate) heap: Range<u64>,
  106. pub(crate) vf_partition_count: u8,
  107. }
  108. impl FbLayout {
  109. /// Computes the FB layout for `chipset` required to run the `gsp_fw` GSP firmware.
  110. pub(crate) fn new(chipset: Chipset, bar: &Bar0, gsp_fw: &GspFirmware) -> Result<Self> {
  111. let hal = hal::fb_hal(chipset);
  112. let fb = {
  113. let fb_size = hal.vidmem_size(bar);
  114. 0..fb_size
  115. };
  116. let vga_workspace = {
  117. let vga_base = {
  118. const NV_PRAMIN_SIZE: u64 = usize_as_u64(SZ_1M);
  119. let base = fb.end - NV_PRAMIN_SIZE;
  120. if hal.supports_display(bar) {
  121. match regs::NV_PDISP_VGA_WORKSPACE_BASE::read(bar).vga_workspace_addr() {
  122. Some(addr) => {
  123. if addr < base {
  124. const VBIOS_WORKSPACE_SIZE: u64 = usize_as_u64(SZ_128K);
  125. // Point workspace address to end of framebuffer.
  126. fb.end - VBIOS_WORKSPACE_SIZE
  127. } else {
  128. addr
  129. }
  130. }
  131. None => base,
  132. }
  133. } else {
  134. base
  135. }
  136. };
  137. vga_base..fb.end
  138. };
  139. let frts = {
  140. const FRTS_DOWN_ALIGN: Alignment = Alignment::new::<SZ_128K>();
  141. const FRTS_SIZE: u64 = usize_as_u64(SZ_1M);
  142. let frts_base = vga_workspace.start.align_down(FRTS_DOWN_ALIGN) - FRTS_SIZE;
  143. frts_base..frts_base + FRTS_SIZE
  144. };
  145. let boot = {
  146. const BOOTLOADER_DOWN_ALIGN: Alignment = Alignment::new::<SZ_4K>();
  147. let bootloader_size = u64::from_safe_cast(gsp_fw.bootloader.ucode.size());
  148. let bootloader_base = (frts.start - bootloader_size).align_down(BOOTLOADER_DOWN_ALIGN);
  149. bootloader_base..bootloader_base + bootloader_size
  150. };
  151. let elf = {
  152. const ELF_DOWN_ALIGN: Alignment = Alignment::new::<SZ_64K>();
  153. let elf_size = u64::from_safe_cast(gsp_fw.size);
  154. let elf_addr = (boot.start - elf_size).align_down(ELF_DOWN_ALIGN);
  155. elf_addr..elf_addr + elf_size
  156. };
  157. let wpr2_heap = {
  158. const WPR2_HEAP_DOWN_ALIGN: Alignment = Alignment::new::<SZ_1M>();
  159. let wpr2_heap_size =
  160. gsp::LibosParams::from_chipset(chipset).wpr_heap_size(chipset, fb.end);
  161. let wpr2_heap_addr = (elf.start - wpr2_heap_size).align_down(WPR2_HEAP_DOWN_ALIGN);
  162. wpr2_heap_addr..(elf.start).align_down(WPR2_HEAP_DOWN_ALIGN)
  163. };
  164. let wpr2 = {
  165. const WPR2_DOWN_ALIGN: Alignment = Alignment::new::<SZ_1M>();
  166. let wpr2_addr = (wpr2_heap.start - u64::from_safe_cast(size_of::<gsp::GspFwWprMeta>()))
  167. .align_down(WPR2_DOWN_ALIGN);
  168. wpr2_addr..frts.end
  169. };
  170. let heap = {
  171. const HEAP_SIZE: u64 = usize_as_u64(SZ_1M);
  172. wpr2.start - HEAP_SIZE..wpr2.start
  173. };
  174. Ok(Self {
  175. fb,
  176. vga_workspace,
  177. frts,
  178. boot,
  179. elf,
  180. wpr2_heap,
  181. wpr2,
  182. heap,
  183. vf_partition_count: 0,
  184. })
  185. }
  186. }