1 /*
2 * SPDX-FileCopyrightText: Copyright (c) 1999-2022 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
3 * SPDX-License-Identifier: MIT
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining a
6 * copy of this software and associated documentation files (the "Software"),
7 * to deal in the Software without restriction, including without limitation
8 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9 * and/or sell copies of the Software, and to permit persons to whom the
10 * Software is furnished to do so, subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice shall be included in
13 * all copies or substantial portions of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21 * DEALINGS IN THE SOFTWARE.
22 */
23
24
25 /******************************************************************************
26 *
27 * Description:
28 * This file provides the glue layer between the RM's OS object
29 * unixCallVideoBIOS() method and the x86 real mode emulator
30 *
31 ******************************************************************************/
32
33
34 #include <core/core.h>
35 #include <nv.h>
36 #include <nv-priv.h>
37 #include <osapi.h>
38
39 #include <os/os.h>
40
41 #include <x86emu/x86emu.h>
42
43 #define IO_LOG(port, val)
44
45 #define NV_ROMLEY_VGA_PHYS_ADDR 0x9d000
46 #define NV_VGA_PHYS_ADDR 0xa0000
47 #define NV_VIDEO_ROM_PHYS_ADDR 0xc0000
48 #define NV_EXPANSION_VIDEO_ROM_PHYS_ADDR 0xc8000
49 #define NV_SYSTEM_ROM_PHYS_ADDR 0xf0000
50
51 #define X86EMU_LOWMEM 0x600 // Interrupt vectors, BIOS data size
52 #define X86EMU_STACK_ADDR 0x10000
53 #define X86EMU_STACK_SIZE 0x1000
54 #define X86EMU_BUF_ADDR 0x20000 // EDID buffer for VESA int10 0x4f15
55 #define X86EMU_BUF_SIZE 128
56
57 #define OP_HLT 0xf4 // HLT instruction - causes the emulator to return
58
59 // This is where IRET from int10 will take us
60 #define X86EMU_IRET_SEG (X86EMU_STACK_ADDR >> 4)
61 #define X86EMU_IRET_OFF 0
62
63 #define X86EMU_DFLT_FLAGS (F_IF)
64
65 static NvBool x86emuReady;
66 static void *x86emuStack;
67
68 static struct x86emu_mem_seg {
69 NvU32 start;
70 NvU32 end;
71 void *vaddr;
72 } x86emu_mem_map[] = {
73 {NV_ROMLEY_VGA_PHYS_ADDR, NV_VGA_PHYS_ADDR - 1},
74 {NV_VGA_PHYS_ADDR, NV_VIDEO_ROM_PHYS_ADDR - 1}, // vga
75 {NV_VIDEO_ROM_PHYS_ADDR, NV_EXPANSION_VIDEO_ROM_PHYS_ADDR -1}, // vbios, sbios
76 {NV_EXPANSION_VIDEO_ROM_PHYS_ADDR, NV_SYSTEM_ROM_PHYS_ADDR -1}, // vbios, sbios
77 {NV_SYSTEM_ROM_PHYS_ADDR, 0xFFFFF}, // Motherboard BIOS
78 {X86EMU_STACK_ADDR, X86EMU_STACK_ADDR + X86EMU_STACK_SIZE - 1}, // emulator stack
79 {0, X86EMU_LOWMEM - 1}, // Interrupt vectors, BIOS data
80 {X86EMU_BUF_ADDR, X86EMU_BUF_ADDR + X86EMU_BUF_SIZE - 1},
81 };
82
83 enum {
84 X86EMU_SEG_ROMLEY_VGA = 0,
85 X86EMU_SEG_VGA,
86 X86EMU_SEG_ROM,
87 X86EMU_SEG_ROM_EXPANSION,
88 X86EMU_SEG_ROM_MOTHERBOARD_BIOS,
89 X86EMU_SEG_EMULATOR_STACK,
90 X86EMU_SEG_BIOS_DATA,
91 X86EMU_SEG_EDID_BUFFER,
92 X86EMU_NUM_SEGS
93 };
94
95 ct_assert(X86EMU_NUM_SEGS == NV_ARRAY_ELEMENTS(x86emu_mem_map));
96
97 static NvU8
x_inb(NvU16 port)98 x_inb(NvU16 port)
99 {
100 NvU8 val;
101
102 val = os_io_read_byte(port);
103 IO_LOG(port, val);
104 return val;
105 }
106
107 static void
x_outb(NvU16 port,NvU8 val)108 x_outb(NvU16 port, NvU8 val)
109 {
110 IO_LOG(port, val);
111 os_io_write_byte(port, val);
112 }
113
114 static NvU16
x_inw(NvU16 port)115 x_inw(NvU16 port)
116 {
117 NvU16 val;
118
119 val = os_io_read_word(port);
120 IO_LOG(port, val);
121 return val;
122 }
123
124 static void
x_outw(NvU16 port,NvU16 val)125 x_outw(NvU16 port, NvU16 val)
126 {
127 IO_LOG(port, val);
128 os_io_write_word(port, val);
129 }
130
131 static u32
x_inl(X86EMU_pioAddr port)132 x_inl(X86EMU_pioAddr port)
133 {
134 NvU32 val;
135
136 val = os_io_read_dword(port);
137 IO_LOG(port, val);
138 return val;
139 }
140
141 static void
x_outl(X86EMU_pioAddr port,u32 val)142 x_outl(X86EMU_pioAddr port, u32 val)
143 {
144 IO_LOG(port, val);
145 os_io_write_dword(port, val);
146 }
147
148 static void *
Mem_addr_xlat(NvU32 addr,NvU32 sz)149 Mem_addr_xlat(NvU32 addr, NvU32 sz)
150 {
151 int i;
152 struct x86emu_mem_seg *pseg;
153
154 for (i = 0; i < X86EMU_NUM_SEGS; ++i)
155 {
156 pseg = x86emu_mem_map + i;
157 if (pseg->vaddr != 0 && addr >= pseg->start && addr + sz - 1 <= pseg->end)
158 return (void *)((char *)pseg->vaddr + addr - pseg->start);
159 }
160
161 X86EMU_halt_sys();
162
163 return 0;
164 }
165
166 static NvU8
Mem_rb(u32 addr)167 Mem_rb(u32 addr)
168 {
169 NvU8 *va = Mem_addr_xlat(addr, 1);
170
171 return va != 0 ? *va : 0;
172 }
173
174 static NvU16
Mem_rw(u32 addr)175 Mem_rw(u32 addr)
176 {
177 NvU16 *va = Mem_addr_xlat(addr, 2);
178
179 return va != 0 ? *va : 0;
180 }
181
182 static u32
Mem_rl(u32 addr)183 Mem_rl(u32 addr)
184 {
185 NvU32 *va = Mem_addr_xlat(addr, 4);
186
187 return va != 0 ? *va : 0;
188 }
189
190 static void
Mem_wb(u32 addr,NvU8 val)191 Mem_wb(u32 addr, NvU8 val)
192 {
193 NvU8 *va = Mem_addr_xlat(addr, 1);
194
195 if (va != 0)
196 *va = val;
197 }
198
199 static void
Mem_ww(u32 addr,NvU16 val)200 Mem_ww(u32 addr, NvU16 val)
201 {
202 NvU16 *va = Mem_addr_xlat(addr, 2);
203
204 if (va != 0)
205 *va = val;
206 }
207
208 static void
Mem_wl(u32 addr,u32 val)209 Mem_wl(u32 addr, u32 val)
210 {
211 NvU32 *va = Mem_addr_xlat(addr, 4);
212
213 if (va != 0)
214 *va = val;
215 }
216
217 static NvU16
get_int_seg(int i)218 get_int_seg(int i)
219 {
220 return Mem_rw(i * 4 + 2);
221 }
222
223
224 static NvU16
get_int_off(int i)225 get_int_off(int i)
226 {
227 return Mem_rw(i * 4);
228 }
229
230
231 static void
pushw(NvU16 i)232 pushw(NvU16 i)
233 {
234 M.x86.R_ESP -= 2;
235 Mem_ww((M.x86.R_SS << 4) + M.x86.R_ESP, i);
236 }
237
238 static void
x86emu_do_int(int num)239 x86emu_do_int(int num)
240 {
241 #if 0
242 Int10Current->num = num;
243
244 if (!int_handler(Int10Current)) {
245 X86EMU_halt_sys();
246 }
247 #else
248 if ((num == 0x15) && (M.x86.R_AX == 0x5f80))
249 {
250 //
251 // Handle the MXM_SYS_INFO_CALLBACK_NUM int 15h SBIOS
252 // callback: disclaim support by returning a value
253 // other than 005fh (MXM_SYS_INFO_CALLBACK_FUNC_SUPPORTED)
254 // to the caller.
255 //
256 M.x86.R_AX = 0;
257 }
258 else
259 {
260 NV_PRINTF(LEVEL_ERROR, "x86emu: int $%d (eax = %08x)\n",
261 num, M.x86.R_EAX);
262 DBG_BREAKPOINT();
263 X86EMU_halt_sys();
264 }
265 #endif
266 }
267
268 NV_STATUS
RmInitX86EmuState(OBJGPU * pGpu)269 RmInitX86EmuState(OBJGPU *pGpu) {
270 int i;
271 struct x86emu_mem_seg *pseg;
272 X86EMU_intrFuncs *intFuncs;
273 X86EMU_pioFuncs pioFuncs = {
274 (&x_inb),
275 (&x_inw),
276 (&x_inl),
277 (&x_outb),
278 (&x_outw),
279 (&x_outl)
280 };
281
282 X86EMU_memFuncs memFuncs = {
283 (&Mem_rb),
284 (&Mem_rw),
285 (&Mem_rl),
286 (&Mem_wb),
287 (&Mem_ww),
288 (&Mem_wl)
289 };
290
291 if (!NV_PRIMARY_VGA(NV_GET_NV_STATE(pGpu))) // not the primary GPU
292 return NV_OK;
293
294 NV_ASSERT(!x86emuReady);
295
296 x86emuStack = portMemAllocNonPaged(
297 X86EMU_STACK_SIZE + sizeof(X86EMU_intrFuncs*) * 256);
298 if (x86emuStack == NULL)
299 return NV_ERR_NO_MEMORY;
300
301 // Interrupt dispatch table
302 intFuncs = (void *)((NvU8 *)x86emuStack + X86EMU_STACK_SIZE);
303
304 // Fill virtual addresses in the memory map
305 for (i = 0; i < X86EMU_NUM_SEGS; ++i)
306 {
307 pseg = x86emu_mem_map + i;
308 switch (i)
309 {
310 case X86EMU_SEG_ROM:
311 case X86EMU_SEG_ROM_EXPANSION:
312 case X86EMU_SEG_ROM_MOTHERBOARD_BIOS:
313 nv_get_updated_emu_seg(&pseg->start, &pseg->end);
314 /* fallthrough */
315 case X86EMU_SEG_VGA:
316 case X86EMU_SEG_BIOS_DATA:
317 pseg->vaddr = os_map_kernel_space(pseg->start,
318 pseg->end - pseg->start + 1,
319 NV_MEMORY_CACHED);
320 if (pseg->vaddr == 0)
321 {
322 NV_PRINTF(LEVEL_ERROR, "x86emu can't map phys addr 0x%05x\n",
323 pseg->start);
324 return NV_ERR_GENERIC;
325 }
326 break;
327 case X86EMU_SEG_EMULATOR_STACK:
328 pseg->vaddr = x86emuStack;
329 break;
330 default:
331 pseg->vaddr = 0;
332 break;
333 }
334 }
335 X86EMU_setupMemFuncs(&memFuncs);
336
337 M.mem_base = 0;
338 M.mem_size = 1024*1024;
339 X86EMU_setupPioFuncs(&pioFuncs);
340
341 for (i=0;i<256;i++)
342 intFuncs[i] = x86emu_do_int;
343 X86EMU_setupIntrFuncs(intFuncs);
344
345 x86emuReady = NV_TRUE;
346
347 return NV_OK;
348 }
349
350 void
RmFreeX86EmuState(OBJGPU * pGpu)351 RmFreeX86EmuState(OBJGPU *pGpu)
352 {
353 int i;
354 struct x86emu_mem_seg *pseg;
355
356 if (! x86emuReady || !NV_PRIMARY_VGA(NV_GET_NV_STATE(pGpu))) // not the primary GPU
357 return;
358
359 portMemFree(x86emuStack);
360 x86emuStack = 0;
361
362 for (i = 0; i < X86EMU_NUM_SEGS; ++i)
363 {
364 pseg = x86emu_mem_map + i;
365 switch (i)
366 {
367 case X86EMU_SEG_ROMLEY_VGA:
368 case X86EMU_SEG_VGA:
369 case X86EMU_SEG_ROM:
370 case X86EMU_SEG_ROM_EXPANSION:
371 case X86EMU_SEG_ROM_MOTHERBOARD_BIOS:
372 case X86EMU_SEG_BIOS_DATA:
373 if (pseg->vaddr != 0)
374 os_unmap_kernel_space(pseg->vaddr, pseg->end - pseg->start + 1);
375 }
376 pseg->vaddr = 0;
377 }
378
379 x86emuReady = NV_FALSE;
380 }
381
382 NV_STATUS
nv_vbios_call(OBJGPU * pGpu,NvU32 * eax,NvU32 * ebx)383 nv_vbios_call(
384 OBJGPU *pGpu,
385 NvU32 *eax,
386 NvU32 *ebx)
387 {
388 NvU16 seg;
389 NvU16 off;
390 struct x86emu_mem_seg *pseg;
391
392 if (!NV_PRIMARY_VGA(NV_GET_NV_STATE(pGpu)))
393 return NV_ERR_GENERIC;
394
395 seg = get_int_seg(0x10);
396 off = get_int_off(0x10);
397
398 pseg = &x86emu_mem_map[X86EMU_SEG_VGA];
399 if (seg < (pseg->start >> 4))
400 {
401 pseg = &x86emu_mem_map[X86EMU_SEG_ROMLEY_VGA];
402 if (seg < (pseg->start >> 4))
403 {
404 NV_PRINTF(LEVEL_ERROR,
405 "cannot call the VBIOS. INT10 vector not in ROM: %04x:%04x\n",
406 seg, off);
407 return NV_ERR_GENERIC;
408 }
409 else if (pseg->vaddr == NULL)
410 {
411 pseg->vaddr = os_map_kernel_space(pseg->start,
412 (pseg->end - pseg->start + 1),
413 NV_MEMORY_CACHED);
414 if (pseg->vaddr == NULL)
415 {
416 NV_PRINTF(LEVEL_ERROR, "x86emu can't map phys addr 0x%05x\n",
417 pseg->start);
418 return NV_ERR_GENERIC;
419 }
420 }
421 }
422
423 // Reset the CPU
424 portMemSet(&M, 0, sizeof(M));
425
426 M.x86.R_SS = X86EMU_STACK_ADDR >> 4;
427 M.x86.R_ESP = X86EMU_STACK_SIZE;
428 M.x86.R_CS = seg;
429 M.x86.R_EIP = off;
430 M.x86.R_EFLG = X86EMU_DFLT_FLAGS;
431 M.x86.R_EAX = *eax;
432 M.x86.R_EBX = *ebx;
433 M.x86.R_ECX = 0;
434 M.x86.R_EDX = 0;
435 M.x86.R_ES = X86EMU_BUF_ADDR >> 4;
436
437 X86EMU_trace_on();
438
439 // Map ES:EDI to buffer. Used by vesa intr 4f15 - read EDID
440 pseg = &x86emu_mem_map[X86EMU_SEG_EDID_BUFFER];
441 pseg->vaddr = NULL;
442
443 // Prepare the code for IRET to jump to
444 Mem_wb((X86EMU_IRET_SEG << 4) + X86EMU_IRET_OFF, OP_HLT);
445
446 pushw(X86EMU_DFLT_FLAGS);
447 pushw(X86EMU_IRET_SEG);
448 pushw(X86EMU_IRET_OFF);
449
450 // Run the emulator
451 X86EMU_exec();
452
453 *eax = M.x86.R_EAX;
454 *ebx = M.x86.R_EBX;
455
456 return NV_OK;
457 }
458