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