summaryrefslogtreecommitdiffstats
path: root/kernel/gsp_phys_boot.c
blob: 0118e065134dac35de5376aaf5c249d3a8b9083a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
/*
 * Real-GSP boot bring-up stage 3b: boot-argument allocation and WPR layout.
 *
 * A real GSP boot needs three host-DMA pages - the WPR metadata the booter
 * consumes, the libos init arguments, and the GSP/bootloader arguments - plus
 * the video-memory layout recorded in that metadata. This file mirrors the
 * layout computation the distribution driver performs for Turing, fills the
 * 256-byte WprMeta structure and logs every field.
 *
 * Stage 3b writes nothing to the device: no BAR/register access happens here,
 * the metadata page is filled in host memory only. Disabled by default
 * (gsp_phys_layout=N); claim must be enabled too (gsp_phys_gpu=1).
 */

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/pci.h>
#include <linux/dma-mapping.h>
#include <linux/slab.h>
#include <linux/limits.h>

#include "gsp_phys_boot.h"

static bool gsp_phys_layout_param;
module_param_named(gsp_phys_layout, gsp_phys_layout_param, bool, 0644);
MODULE_PARM_DESC(gsp_phys_layout,
		 "Allocate the GSP boot-argument pages and compute/log the WPR/FB layout on the claimed GPU (default 0, no device writes)");

bool gsp_phys_layout_enabled(void)
{
	return gsp_phys_layout_param;
}

/* Boot-argument allocation sizes (mirror of the reference allocator). */
#define GSP_PHYS_PAGE_SIZE		0x1000
#define GSP_PHYS_LIBOS_ARGS_SIZE	0x1000
#define GSP_PHYS_GSP_ARGS_SIZE		0x1000

/*
 * The boot binary ("bootloader"/SK ucode) is not a section of the staged
 * .fwimage; the distribution driver carries it as compiled-in bindata. The
 * Turing boot ucode image is 4 KB, but the staged image cannot confirm the
 * exact size, so this stays a clearly-named stub used for layout only.
 */
#define GSP_PHYS_BOOTLOADER_SIZE_STUB	0x1000

/* Turing-era layout constants (HAL values for TU102..AD10x). */
#define GSP_PHYS_FRTS_SIZE		(0x100 * 0x1000)
#define GSP_PHYS_WPR_END_ALIGN		0x20000
#define GSP_PHYS_BOOTBIN_ALIGN		0x1000
#define GSP_PHYS_FW_ALIGN		0x10000
#define GSP_PHYS_MB			(1ULL << 20)
#define GSP_PHYS_PRAMIN_SIZE		(1 * GSP_PHYS_MB)
#define GSP_PHYS_NON_WPR_HEAP_SIZE	(1 * GSP_PHYS_MB)
#define GSP_PHYS_FW_HEAP_OS_CARVEOUT	0
#define GSP_PHYS_FW_HEAP_BASE_RM_SIZE	(8 * GSP_PHYS_MB)
#define GSP_PHYS_FW_HEAP_PER_GB_FB	(96 * 1024)
#define GSP_PHYS_FW_HEAP_CLIENT_ALLOC	((48 * 1024) * 2048)
#define GSP_PHYS_FW_HEAP_MIN_MB		64
#define GSP_PHYS_FW_HEAP_MAX_MB		256
#define GSP_PHYS_PRESCRUBBED_TOP_FB	(256 * GSP_PHYS_MB)

#define GSP_PHYS_WPR_META_MAGIC		0xdc3aae21371a60b3ULL
#define GSP_PHYS_WPR_META_REVISION	1
#define GSP_PHYS_WPR_META_FLAG_CLOCK_BOOST	(1U << 0)

/*
 * GspFwWprMeta, the 256-byte handoff structure the booter consumes from the
 * WPR metadata page. Field order and offsets follow the driver ABI (magic at
 * 0, revision at 8, layout fields from 88, verified at 248); the two unions
 * alias signature/free-list and partition/crash-queue members respectively.
 */
struct gsp_phys_wpr_meta {
	__u64 magic;
	__u64 revision;
	__u64 sysmem_addr_of_radix3_elf;
	__u64 size_of_radix3_elf;
	__u64 sysmem_addr_of_bootloader;
	__u64 size_of_bootloader;
	__u64 bootloader_code_offset;
	__u64 bootloader_data_offset;
	__u64 bootloader_manifest_offset;
	union {
		struct {
			__u64 sysmem_addr_of_signature;
			__u64 size_of_signature;
		};
		struct {
			__u32 gsp_fw_heap_free_list_wpr_offset;
			__u32 unused0;
			__u64 unused1;
		};
	};
	__u64 gsp_fw_rsvd_start;
	__u64 non_wpr_heap_offset;
	__u64 non_wpr_heap_size;
	__u64 gsp_fw_wpr_start;
	__u64 gsp_fw_heap_offset;
	__u64 gsp_fw_heap_size;
	__u64 gsp_fw_offset;
	__u64 boot_bin_offset;
	__u64 frts_offset;
	__u64 frts_size;
	__u64 gsp_fw_wpr_end;
	__u64 fb_size;
	__u64 vga_workspace_offset;
	__u64 vga_workspace_size;
	__u64 boot_count;
	union {
		struct {
			__u64 partition_rpc_addr;
			__u16 partition_rpc_request_offset;
			__u16 partition_rpc_reply_offset;
			__u32 elf_code_offset;
			__u32 elf_data_offset;
			__u32 elf_code_size;
			__u32 elf_data_size;
			__u32 ls_ucode_version;
		};
		struct {
			__u32 partition_rpc_padding[4];
			__u64 sysmem_addr_of_crash_report_queue;
			__u32 size_of_crash_report_queue;
			__u32 ls_ucode_version_padding[1];
		};
	};
	__u8 gsp_fw_heap_vf_partition_count;
	__u8 flags;
	__u8 padding[2];
	__u32 pmu_reserved_size;
	__u64 verified;
};

static u64 gsp_phys_align_down(u64 value, u64 align)
{
	return value & ~(align - 1);
}

static u64 gsp_phys_align_up(u64 value, u64 align)
{
	return (value + align - 1) & ~(align - 1);
}

/*
 * GSP FW (WPR) heap size for a bare-metal Turing part. Ported from the
 * reference: a base RM reservation, a per-GB-of-FB term, and a client-channel
 * allocation term, clamped to the architecture min/max and to the pre-scrubbed
 * top of FB (Turing has no scrubber ucode, so the heap must fit there).
 */
static u64 gsp_phys_fw_heap_size(u64 fb_size, u64 anterior, u64 posterior)
{
	const u64 fb_size_gb = gsp_phys_align_up(fb_size, 1ULL << 30) >> 30;
	u64 max_scrubbed_mb = U32_MAX;
	u64 heap;

	if (anterior + posterior < GSP_PHYS_PRESCRUBBED_TOP_FB)
		max_scrubbed_mb = (GSP_PHYS_PRESCRUBBED_TOP_FB -
				   anterior - posterior) >> 20;

	heap = GSP_PHYS_FW_HEAP_OS_CARVEOUT +
	       GSP_PHYS_FW_HEAP_BASE_RM_SIZE +
	       gsp_phys_align_up(GSP_PHYS_FW_HEAP_PER_GB_FB * fb_size_gb,
				 GSP_PHYS_MB) +
	       gsp_phys_align_up((u64)GSP_PHYS_FW_HEAP_CLIENT_ALLOC,
				 GSP_PHYS_MB);

	heap = max(heap, (u64)GSP_PHYS_FW_HEAP_MIN_MB * GSP_PHYS_MB);
	heap = min(heap, min((u64)GSP_PHYS_FW_HEAP_MAX_MB,
			     max_scrubbed_mb) * GSP_PHYS_MB);
	return heap;
}

static bool gsp_phys_wpr_layout_valid(const struct gsp_phys_wpr_meta *m)
{
	return m->frts_size <= m->gsp_fw_wpr_end &&
	       m->gsp_fw_offset < m->boot_bin_offset &&
	       m->boot_bin_offset < m->frts_offset &&
	       m->frts_offset < m->gsp_fw_wpr_end &&
	       m->gsp_fw_wpr_end <= m->vga_workspace_offset &&
	       m->gsp_fw_wpr_start < m->gsp_fw_heap_offset &&
	       m->non_wpr_heap_offset < m->gsp_fw_wpr_start &&
	       m->gsp_fw_rsvd_start == m->non_wpr_heap_offset &&
	       m->gsp_fw_offset > 0 &&
	       m->non_wpr_heap_offset > 0;
}

static void gsp_phys_wpr_meta_dump(const struct gsp_phys_wpr_meta *m)
{
	pr_info("gsp-phys-boot: GspFwWprMeta sizeof=%zu (must be 256) magic=0x%llx revision=%llu flags=0x%x\n",
		sizeof(*m), (u64)m->magic, (u64)m->revision, m->flags);
	pr_info("gsp-phys-boot:   fbSize=0x%llx vgaWorkspace=0x%llx - 0x%llx (0x%llx)\n",
		m->fb_size, m->vga_workspace_offset,
		m->vga_workspace_offset + m->vga_workspace_size - 1,
		m->vga_workspace_size);
	pr_info("gsp-phys-boot:   gspFwRsvdStart=0x%llx\n", m->gsp_fw_rsvd_start);
	pr_info("gsp-phys-boot:   nonWprHeap=0x%llx - 0x%llx (0x%llx)\n",
		m->non_wpr_heap_offset,
		m->non_wpr_heap_offset + m->non_wpr_heap_size - 1,
		m->non_wpr_heap_size);
	pr_info("gsp-phys-boot:   gspFwWprStart=0x%llx\n", m->gsp_fw_wpr_start);
	pr_info("gsp-phys-boot:   gspFwHeap=0x%llx - 0x%llx (0x%llx)\n",
		m->gsp_fw_heap_offset,
		m->gsp_fw_heap_offset + m->gsp_fw_heap_size - 1,
		m->gsp_fw_heap_size);
	pr_info("gsp-phys-boot:   gspFwOffset=0x%llx - 0x%llx (0x%llx)\n",
		m->gsp_fw_offset, m->gsp_fw_offset + m->size_of_radix3_elf - 1,
		m->size_of_radix3_elf);
	pr_info("gsp-phys-boot:   bootBinOffset=0x%llx - 0x%llx (0x%llx)\n",
		m->boot_bin_offset,
		m->boot_bin_offset + m->size_of_bootloader - 1,
		m->size_of_bootloader);
	pr_info("gsp-phys-boot:   frtsOffset=0x%llx - 0x%llx (0x%llx)\n",
		m->frts_offset, m->frts_offset + m->frts_size - 1, m->frts_size);
	pr_info("gsp-phys-boot:   gspFwWprEnd=0x%llx bootCount=%llu verified=%llu\n",
		m->gsp_fw_wpr_end, (u64)m->boot_count, (u64)m->verified);
	pr_info("gsp-phys-boot:   stubs: sysmemAddrOfRadix3Elf/Bootloader/Signature=0 crashReportQueue=0 (image not staged in 3b), bootloader offsets=0\n");
}

/*
 * Stage 3b entry: allocate the boot-argument pages on the claimed device and
 * compute/record the WPR layout in the metadata page. No device writes.
 */
int gsp_phys_boot_layout_init(struct gsp_phys_boot *boot, struct pci_dev *pdev,
			      u64 fw_image_size, resource_size_t usable_fb_size)
{
	struct gsp_phys_wpr_meta *meta;
	u64 vbios_reserved_offset;
	u64 non_wpr_heap_size, wpr_meta_size;
	u64 pre_wpr_heap, post_wpr_heap, wpr_heap_size;
	int ret;

	if (!gsp_phys_layout_enabled() || boot->initialized)
		return 0;
	if (!pdev || !fw_image_size)
		return -EINVAL;

	BUILD_BUG_ON(sizeof(struct gsp_phys_wpr_meta) != 256);

	pr_info("gsp-phys-boot: stage 3b layout+allocation on %s (no device writes)\n",
		pci_name(pdev));
	pr_info("gsp-phys-boot: usable FB = 0x%llx (approximation: BAR1 aperture length, NOT the physical FB size)\n",
		(u64)usable_fb_size);
	pr_info("gsp-phys-boot: staged GSP-RM image (.fwimage) size = 0x%llx\n",
		fw_image_size);
	pr_info("gsp-phys-boot: stub: boot binary size assumed 0x%x (boot ucode is compiled-in bindata, not a .fwimage section)\n",
		GSP_PHYS_BOOTLOADER_SIZE_STUB);
	pr_info("gsp-phys-boot: wprEndMargin=0 (reference normal first-boot path; no override/retry margin)\n");

	boot->wpr_meta_size = GSP_PHYS_PAGE_SIZE;
	boot->wpr_meta_va = dma_alloc_coherent(&pdev->dev, boot->wpr_meta_size,
					       &boot->wpr_meta_dma, GFP_KERNEL);
	if (!boot->wpr_meta_va) {
		pr_err("gsp-phys-boot: WPR meta page allocation failed\n");
		ret = -ENOMEM;
		goto err;
	}
	boot->libos_args_size = GSP_PHYS_LIBOS_ARGS_SIZE;
	boot->libos_args_va = dma_alloc_coherent(&pdev->dev, boot->libos_args_size,
						 &boot->libos_args_dma, GFP_KERNEL);
	if (!boot->libos_args_va) {
		pr_err("gsp-phys-boot: libos init arguments allocation failed\n");
		ret = -ENOMEM;
		goto err;
	}
	boot->gsp_args_size = GSP_PHYS_GSP_ARGS_SIZE;
	boot->gsp_args_va = dma_alloc_coherent(&pdev->dev, boot->gsp_args_size,
					       &boot->gsp_args_dma, GFP_KERNEL);
	if (!boot->gsp_args_va) {
		pr_err("gsp-phys-boot: GSP/bootloader arguments allocation failed\n");
		ret = -ENOMEM;
		goto err;
	}

	pr_info("gsp-phys-boot: WPR meta page 0x%zx bytes dma=0x%llx va=%p (coherent/uncached, zeroed)\n",
		boot->wpr_meta_size, (u64)boot->wpr_meta_dma, boot->wpr_meta_va);
	pr_info("gsp-phys-boot: libos args 0x%zx bytes dma=0x%llx va=%p (coherent, zeroed; filled in a later stage)\n",
		boot->libos_args_size, (u64)boot->libos_args_dma,
		boot->libos_args_va);
	pr_info("gsp-phys-boot: gsp args 0x%zx bytes dma=0x%llx va=%p (coherent, zeroed; filled in a later stage)\n",
		boot->gsp_args_size, (u64)boot->gsp_args_dma, boot->gsp_args_va);

	meta = boot->wpr_meta_va;

	/*
	 * Layout from the top of FB downward. Turing has no VBIOS MMU-lock
	 * region (the lock read is invalid on this generation), so the VGA
	 * workspace bottom is the reserved-area boundary.
	 */
	meta->fb_size = usable_fb_size;
	meta->vga_workspace_offset = usable_fb_size - GSP_PHYS_PRAMIN_SIZE;
	meta->vga_workspace_size = usable_fb_size - meta->vga_workspace_offset;
	vbios_reserved_offset = meta->vga_workspace_offset;

	meta->size_of_radix3_elf = fw_image_size;
	meta->gsp_fw_wpr_end = gsp_phys_align_down(vbios_reserved_offset,
						   GSP_PHYS_WPR_END_ALIGN);
	meta->frts_size = GSP_PHYS_FRTS_SIZE;
	meta->frts_offset = meta->gsp_fw_wpr_end - meta->frts_size;
	meta->size_of_bootloader = GSP_PHYS_BOOTLOADER_SIZE_STUB;
	meta->boot_bin_offset = gsp_phys_align_down(meta->frts_offset -
						    meta->size_of_bootloader,
						    GSP_PHYS_BOOTBIN_ALIGN);
	meta->gsp_fw_offset = gsp_phys_align_down(meta->boot_bin_offset -
						  meta->size_of_radix3_elf,
						  GSP_PHYS_FW_ALIGN);

	non_wpr_heap_size = gsp_phys_align_up((u64)GSP_PHYS_NON_WPR_HEAP_SIZE,
					      GSP_PHYS_MB);
	wpr_meta_size = gsp_phys_align_up((u64)sizeof(*meta), GSP_PHYS_MB);
	pre_wpr_heap = wpr_meta_size + non_wpr_heap_size;
	post_wpr_heap = gsp_phys_align_up(usable_fb_size - meta->gsp_fw_offset,
					  GSP_PHYS_MB);
	wpr_heap_size = gsp_phys_fw_heap_size(usable_fb_size, pre_wpr_heap,
					      post_wpr_heap);

	meta->gsp_fw_heap_offset = gsp_phys_align_down(meta->gsp_fw_offset -
						       wpr_heap_size, GSP_PHYS_MB);
	meta->gsp_fw_heap_size = gsp_phys_align_down(meta->gsp_fw_offset -
						     meta->gsp_fw_heap_offset,
						     GSP_PHYS_MB);
	meta->gsp_fw_heap_vf_partition_count = 0;
	meta->gsp_fw_wpr_start = meta->gsp_fw_heap_offset - wpr_meta_size;
	meta->non_wpr_heap_size = non_wpr_heap_size;
	meta->non_wpr_heap_offset = meta->gsp_fw_wpr_start - non_wpr_heap_size;
	meta->gsp_fw_rsvd_start = meta->non_wpr_heap_offset;

	/* Sysmem image addresses are not staged yet (stage 3d fills them). */
	meta->sysmem_addr_of_radix3_elf = 0;
	meta->sysmem_addr_of_bootloader = 0;
	meta->bootloader_code_offset = 0;
	meta->bootloader_data_offset = 0;
	meta->bootloader_manifest_offset = 0;
	meta->sysmem_addr_of_signature = 0;
	meta->size_of_signature = 0;
	meta->sysmem_addr_of_crash_report_queue = 0;
	meta->size_of_crash_report_queue = 0;

	meta->boot_count = 0;
	meta->verified = 0;
	meta->revision = GSP_PHYS_WPR_META_REVISION;
	meta->magic = GSP_PHYS_WPR_META_MAGIC;
	meta->flags = GSP_PHYS_WPR_META_FLAG_CLOCK_BOOST;

	pr_info("gsp-phys-boot: FW heap size = 0x%llx (pre=0x%llx post=0x%llx, min %uMB max %uMB, pre-scrubbed top 0x%llx)\n",
		wpr_heap_size, pre_wpr_heap, post_wpr_heap,
		GSP_PHYS_FW_HEAP_MIN_MB, GSP_PHYS_FW_HEAP_MAX_MB,
		(u64)GSP_PHYS_PRESCRUBBED_TOP_FB);

	if (!gsp_phys_wpr_layout_valid(meta)) {
		pr_err("gsp-phys-boot: computed layout is inconsistent (frame too small for image/heap?)\n");
		ret = -EINVAL;
		goto err;
	}

	gsp_phys_wpr_meta_dump(meta);

	boot->fb_size = meta->fb_size;
	boot->vga_workspace_offset = meta->vga_workspace_offset;
	boot->vga_workspace_size = meta->vga_workspace_size;
	boot->gsp_fw_wpr_end = meta->gsp_fw_wpr_end;
	boot->frts_offset = meta->frts_offset;
	boot->frts_size = meta->frts_size;
	boot->boot_bin_offset = meta->boot_bin_offset;
	boot->size_of_bootloader = meta->size_of_bootloader;
	boot->gsp_fw_offset = meta->gsp_fw_offset;
	boot->size_of_radix3_elf = meta->size_of_radix3_elf;
	boot->gsp_fw_heap_offset = meta->gsp_fw_heap_offset;
	boot->gsp_fw_heap_size = meta->gsp_fw_heap_size;
	boot->gsp_fw_wpr_start = meta->gsp_fw_wpr_start;
	boot->non_wpr_heap_offset = meta->non_wpr_heap_offset;
	boot->non_wpr_heap_size = meta->non_wpr_heap_size;
	boot->gsp_fw_rsvd_start = meta->gsp_fw_rsvd_start;

	boot->initialized = true;
	pr_info("gsp-phys-boot: stage 3b complete: boot args allocated, WPR meta filled (no device writes)\n");
	return 0;

err:
	gsp_phys_boot_layout_fini(boot, pdev);
	return ret;
}

void gsp_phys_boot_layout_fini(struct gsp_phys_boot *boot, struct pci_dev *pdev)
{
	if (!boot->initialized && !boot->wpr_meta_va && !boot->libos_args_va &&
	    !boot->gsp_args_va)
		return;
	if (!pdev)
		return;

	if (boot->wpr_meta_va)
		dma_free_coherent(&pdev->dev, boot->wpr_meta_size,
				  boot->wpr_meta_va, boot->wpr_meta_dma);
	if (boot->libos_args_va)
		dma_free_coherent(&pdev->dev, boot->libos_args_size,
				  boot->libos_args_va, boot->libos_args_dma);
	if (boot->gsp_args_va)
		dma_free_coherent(&pdev->dev, boot->gsp_args_size,
				  boot->gsp_args_va, boot->gsp_args_dma);

	boot->wpr_meta_va = NULL;
	boot->libos_args_va = NULL;
	boot->gsp_args_va = NULL;
	boot->initialized = false;
	pr_info("gsp-phys-boot: boot-argument pages freed\n");
}