e262eea9f316dde989ef72a61955a60107210553
[nvidia.git] / src / nvidia_os.c
1 /* _NVRM_COPYRIGHT_BEGIN_
2  *
3  * Copyright 2001-2002 by NVIDIA Corporation.  All rights reserved.  All
4  * information contained herein is proprietary and confidential to NVIDIA
5  * Corporation.  Any use, reproduction, or disclosure without the written
6  * permission of NVIDIA Corporation is prohibited.
7  *
8  * _NVRM_COPYRIGHT_END_
9  */
10
11 #include "nv-misc.h"
12 #include "os-interface.h"
13 #include "nv.h"
14 #include "nv-freebsd.h"
15
16
17 /* DragonFly compat */
18 #include <machine/pmap_inval.h>
19
20 void
21 pmap_invalidate_range(struct pmap *pmap, vm_offset_t sva, vm_offset_t eva)
22 {
23     struct pmap_inval_info info;
24     vm_offset_t va;
25
26     pmap_inval_init(&info);
27     for (va = sva; va < eva; va += PAGE_SIZE)
28         pmap_inval_add(&info, pmap, va);
29     pmap_inval_flush(&info);
30 }
31
32
33 /*
34  * The NVIDIA kernel module's malloc identifier, needed for both tracking
35  * and actual allocation/freeing purposes. M_NVIDIA is declared elsewhere
36  * to make it known to other parts of the kernel module (nv-freebsd.h).
37  */
38
39 MALLOC_DEFINE(M_NVIDIA, "nvidia", "NVIDIA memory allocations");
40
41
42 #define MAX_ERROR_STRING    256
43
44 RM_STATUS NV_API_CALL os_alloc_contig_pages(
45     void **address,
46     U032 size
47 )
48 {
49     *address = contigmalloc(size, M_NVIDIA, 0, 0, ~0, PAGE_SIZE, 0);
50     return *address ? RM_OK : RM_ERROR;
51 }
52
53 void NV_API_CALL os_free_contig_pages(
54     void *address,
55     U032 size
56 )
57 {
58     contigfree(address, size, M_NVIDIA);
59 }
60
61 /*
62  * The core resource manager's favorite source of memory, this routine is
63  * called from different contexts, including ISRs. This means that it can
64  * not be allowed to sleep when memory is low.
65  */
66
67 RM_STATUS NV_API_CALL os_alloc_mem(
68     void **address,
69     U032 size
70 )
71 {
72     /* XXX Fix me? (malloc flags) */
73     *address = malloc(size, M_NVIDIA, M_INTWAIT | M_ZERO | M_NULLOK);
74     return *address ? RM_OK : RM_ERROR;
75 }
76
77 void NV_API_CALL os_free_mem(void *address)
78 {
79     free(address, M_NVIDIA);
80 }
81
82 #define NV_MSECS_PER_TICK       (1000 / hz)
83 #define NV_MSECS_TO_TICKS(ms)   ((ms) * hz / 1000)
84 #define NV_USECS_PER_TICK       (1000000 / hz)
85 #define NV_USECS_TO_TICKS(us)   ((us) * hz / 1000000)
86
87 RM_STATUS NV_API_CALL os_delay(U032 MilliSeconds)
88 {
89     unsigned long MicroSeconds;
90     unsigned long ticks;
91     struct timeval tv_end, tv_aux;
92
93     getmicrotime(&tv_aux);
94
95     if (__NV_ITHREAD() && (MilliSeconds > NV_MAX_ISR_DELAY_MS))
96         return RM_ERROR;
97
98     if (__NV_ITHREAD()) {
99         DELAY(MilliSeconds * 1000);
100         return RM_OK;
101     }
102
103     MicroSeconds = MilliSeconds * 1000;
104     tv_end.tv_usec = MicroSeconds;
105     tv_end.tv_sec = 0;
106     /* tv_end = tv_aux + tv_end */
107     NV_TIMERADD(&tv_aux, &tv_end, &tv_end);
108
109     ticks = NV_USECS_TO_TICKS(MicroSeconds);
110
111     if (ticks > 0) {
112         do {
113             tsleep((void *)os_delay, PCATCH, "delay", ticks);
114             getmicrotime(&tv_aux);
115             if (NV_TIMERCMP(&tv_aux, &tv_end, <)) {
116                 /* tv_aux = tv_end - tv_aux */
117                 NV_TIMERSUB(&tv_end, &tv_aux, &tv_aux);
118                 MicroSeconds = tv_aux.tv_usec + (tv_aux.tv_sec * 1000000);
119             } else
120                 MicroSeconds = 0;
121         } while ((ticks = NV_USECS_TO_TICKS(MicroSeconds)) > 0);
122     }
123
124     if (MicroSeconds > 0)
125         DELAY(MicroSeconds);
126
127     return RM_OK;
128 }
129
130 RM_STATUS NV_API_CALL os_delay_us(U032 MicroSeconds)
131 {
132     if (__NV_ITHREAD() && (MicroSeconds > NV_MAX_ISR_DELAY_US))
133         return RM_ERROR;
134     DELAY(MicroSeconds);
135     return RM_OK;
136 }
137
138 U032 NV_API_CALL os_get_cpu_frequency(void)
139 {
140     return ((tsc_frequency + 4999) / 1000000);
141 }
142
143 U032 NV_API_CALL os_get_current_process(void)
144 {
145     return curproc->p_pid;
146 }
147
148 RM_STATUS NV_API_CALL os_get_current_time(
149     U032 *sec,
150     U032 *usec
151 )
152 {
153     struct timeval tv;
154
155     getmicrotime(&tv);
156
157     *sec  = tv.tv_sec;
158     *usec = tv.tv_usec;
159
160     return RM_OK;
161 }
162
163 BOOL NV_API_CALL os_is_administrator(PHWINFO pDev)
164 {
165     return suser(CURTHREAD) ? FALSE : TRUE;
166 }
167
168 U008 NV_API_CALL os_io_read_byte(
169     PHWINFO pDev,
170     U032 address
171 )
172 {
173     /* XXX Fix me? (bus_space access) */
174     return inb(address);
175 }
176
177 void NV_API_CALL os_io_write_byte(
178     PHWINFO pDev,
179     U032 address,
180     U008 value
181 )
182 {
183     /* XXX Fix me? (bus_space access) */
184     outb(address, value);
185 }
186
187 U016 NV_API_CALL os_io_read_word(
188     PHWINFO pDev,
189     U032 address
190 )
191 {
192     /* XXX Fix me? (bus_space access) */
193     return inw(address);
194 }
195
196 void NV_API_CALL os_io_write_word(
197     PHWINFO pDev,
198     U032 address,
199     U016 value
200 )
201 {
202     /* XXX Fix me? (bus_space access) */
203     return outw(address, value);
204 }
205
206 U032 NV_API_CALL os_io_read_dword(
207     PHWINFO pDev,
208     U032 address
209 )
210 {
211     /* XXX Fix me? (bus_space access) */
212     return inl(address);
213 }
214
215 void NV_API_CALL os_io_write_dword(
216     PHWINFO pDev,
217     U032 address,
218     U032 value
219 )
220 {
221     /* XXX Fix me? (bus_space access) */
222     outl(address, value);
223 }
224
225 void* NV_API_CALL os_map_kernel_space(
226     NvU64 start,
227     NvU64 size,
228     U032 mode
229 )
230 {
231     int map_mode;
232
233 #if defined(NVCPU_X86) && !defined(PAE)
234     if (start > 0xffffffff)
235         return NULL;
236 #endif
237
238     if (start & PAGE_MASK)
239         return NULL;
240
241     size = NV_ALIGN_UP(size, PAGE_SIZE);
242
243     switch (mode) {
244         case NV_MEMORY_CACHED:
245             map_mode = PAT_WRITE_BACK;
246             break;
247         case NV_MEMORY_WRITECOMBINED:
248             map_mode = PAT_WRITE_COMBINING;
249             break;
250         case NV_MEMORY_UNCACHED:
251         case NV_MEMORY_DEFAULT:
252             map_mode = PAT_UNCACHEABLE;
253             break;
254         default:
255             nv_printf(NV_DBG_ERRORS,
256                       "NVRM: unknown mode in os_map_kernel_space()\n");
257             return NULL;
258     }
259
260     return pmap_mapdev_attr(start, size, map_mode);
261 }
262
263 void NV_API_CALL os_unmap_kernel_space(
264     void *address,
265     NvU64 size
266 )
267 {
268     pmap_unmapdev_attr((vm_offset_t)address, size);
269 }
270
271 void* NV_API_CALL os_map_kernel_space_high(
272     U032 pfn,
273     U032 size
274 )
275 {
276     U032 start;
277     if (!(pfn & ~0xfffff)) {
278         start = pfn << PAGE_SHIFT;
279         return os_map_kernel_space(start, size, NV_MEMORY_CACHED);
280     }
281     return NULL;
282 }
283
284 void NV_API_CALL os_unmap_kernel_space_high(
285     void *addr,
286     U032 pfn,
287     U032 size
288 )
289 {
290     os_unmap_kernel_space(addr, size);
291 }
292
293 RM_STATUS NV_API_CALL os_set_mem_range(
294     U032 start,
295     U032 size,
296     U032 mode
297 )
298 {
299     int arg;
300     struct mem_range_desc mrd;
301
302     mrd.mr_base  = start;
303     mrd.mr_len   = size;
304     mrd.mr_flags = MDF_WRITECOMBINE;
305
306     strcpy(mrd.mr_owner, "NVIDIA");
307     arg = MEMRANGE_SET_UPDATE;
308
309     if (mem_range_attr_set(&mrd, &arg))
310         return RM_ERROR;
311
312     return RM_OK;
313 }
314
315 RM_STATUS NV_API_CALL os_unset_mem_range(
316     U032 start,
317     U032 size
318 )
319 {
320     int arg;
321     struct mem_range_desc mrd;
322
323     mrd.mr_base = start;
324     mrd.mr_len  = size;
325     arg = MEMRANGE_SET_REMOVE;
326
327     if (mem_range_attr_set(&mrd, &arg))
328         return RM_ERROR;
329
330     return RM_OK;
331 }
332
333
334 /*
335  * The current debug level is used to determine if certain debug messages
336  * are printed to the system console/log files or not. It defaults to the
337  * highest debug level, i.e. the lowest debug output.
338  */
339
340 U032 cur_debuglevel = 0xffffffff;
341
342 void NV_API_CALL os_dbg_init(void)
343 {
344     U032 new_debuglevel;
345     nv_stack_t *sp;
346
347     NV_UMA_ZONE_ALLOC_STACK(sp);
348     if (sp == NULL)
349         return;
350
351     if (rm_read_registry_dword(sp, NULL, "NVreg", "ResmanDebugLevel",
352             &new_debuglevel) == RM_OK) {
353         if (new_debuglevel != 0xffffffff)
354             cur_debuglevel = new_debuglevel;
355     }
356
357     NV_UMA_ZONE_FREE_STACK(sp);
358 }
359
360 RM_STATUS NV_API_CALL os_schedule(void)
361 {
362     return RM_ERR_NOT_SUPPORTED;
363 }
364
365 void NV_API_CALL os_dbg_set_level(U032 new_debuglevel)
366 {
367     cur_debuglevel = new_debuglevel;
368 }
369
370 void NV_API_CALL os_dbg_breakpoint(void)
371 {
372 #ifdef DEBUG
373     Debugger("nv");
374 #endif
375 }
376
377 void NV_API_CALL out_string(const char *message)
378 {
379 #if defined(DEBUG) || defined(QA_BUILD)
380     kprintf("%s", message);
381 #endif
382 }
383
384 static char nv_error_string[MAX_ERROR_STRING];
385
386 int NV_API_CALL nv_printf(
387     U032 debuglevel,
388     const char *format,
389     ...
390 )
391 {
392     char *message = nv_error_string;
393     __va_list arglist;
394     int chars_written = 0;
395
396     if (debuglevel >= ((cur_debuglevel >> 4) & 3)) {
397         __va_start(arglist, format);
398         chars_written = kvsprintf(message, format, arglist);
399         __va_end(arglist);
400         kprintf("%s", message);
401     }
402
403     return chars_written;
404 }
405
406 int NV_API_CALL nv_snprintf(
407     char *buf,
408     unsigned int size,
409     const char *fmt,
410     ...
411 )
412 {
413     __va_list arglist;
414     int chars_written;
415
416     __va_start(arglist, fmt);
417     chars_written = kvsnprintf(buf, size, fmt, arglist);
418     __va_end(arglist);
419
420     return chars_written;
421 }
422
423 void NV_API_CALL nv_os_log(
424     int loglevel,
425     const char *fmt,
426     void *ap
427 )
428 {
429     int l;
430     ksprintf(nv_error_string, "NVRM: ");
431     l = strlen(nv_error_string);
432     kvsnprintf(nv_error_string + l, MAX_ERROR_STRING - l, fmt, ap);
433     kprintf("%s", nv_error_string);
434 }
435
436 S032 NV_API_CALL os_mem_cmp(
437     const U008 *buf0,
438     const U008 *buf1,
439     U032 length
440 )
441 {
442     return memcmp(buf0, buf1, length);
443 }
444
445 U008* NV_API_CALL os_mem_copy(
446     U008 *dst,
447     const U008 *src,
448     U032 length
449 )
450 {
451 #if defined(NVCPU_X86_64)
452     uint32_t i;
453     for (i = 0; i < length; i++) dst[i] = src[i];
454     return dst;
455 #else
456     return memcpy(dst, src, length);
457 #endif
458 }
459
460 RM_STATUS NV_API_CALL os_memcpy_from_user(
461     void *dst,
462     const void *src,
463     U032 length
464 )
465 {
466     if (src < (void *) VM_MAX_USER_ADDRESS)
467         return copyin(src, dst, length)  ? RM_ERR_INVALID_POINTER : RM_OK;
468
469     return os_mem_copy(dst, src, length) ? RM_ERR_INVALID_POINTER : RM_OK;
470 }
471
472 RM_STATUS NV_API_CALL os_memcpy_to_user(
473     void *dst,
474     const void *src,
475     U032 length
476 )
477 {
478     if (dst < (void *) VM_MAX_USER_ADDRESS)
479         return copyout(src, dst, length) ? RM_ERR_INVALID_POINTER : RM_OK;
480
481     return os_mem_copy(dst, src, length) ? RM_ERR_INVALID_POINTER : RM_OK;
482 }
483
484 void* NV_API_CALL os_mem_set(
485     void *b,
486     U008 c,
487     U032 length
488 )
489 {
490     return memset(b, c, length);
491 }
492
493 S032 NV_API_CALL os_string_compare(
494     const char *s1,
495     const char *s2
496 )
497 {
498     return strcmp(s1, s2);
499 }
500
501 char* NV_API_CALL os_string_copy(
502     char *dst,
503     const char *src
504 )
505 {
506     return strcpy(dst, src);
507 }
508
509 U032 NV_API_CALL os_string_length(const char* s)
510 {
511     return strlen(s);
512 }
513
514 RM_STATUS NV_API_CALL os_strncpy_from_user(
515     char *dst,
516     const char *src,
517     U032 n
518 )
519 {
520     return copyinstr(src, dst, n, NULL) ? RM_ERR_INVALID_POINTER : RM_OK;
521 }
522
523 U032 NV_API_CALL os_get_page_size(void)
524 {
525     return PAGE_SIZE;
526 }
527
528 NvU64 NV_API_CALL os_get_page_mask(void)
529 {
530     /*
531      * On FreeBSD, PAGE_MASK means (PAGE_SIZE - 1); on Linux it means the
532      * opposite, ~(PAGE_SIZE - 1); that is what this function is expected
533      * to return.
534      */
535     return ~PAGE_MASK;
536 }
537
538 NvU64 NV_API_CALL os_get_system_memory_size(void)
539 {
540     return ((NvU64)physmem * PAGE_SIZE) / RM_PAGE_SIZE;
541 }
542
543 U032 NV_API_CALL os_get_cpu_count(void)
544 {
545     return ncpus;
546 }
547
548 RM_STATUS NV_API_CALL os_flush_cpu_cache(void)
549 {
550     /*
551      * XXX This will do for now, but this may need to be extended
552      * to make IPI calls (flushing all caches).
553      */
554     __asm__ __volatile__("wbinvd": : :"memory");
555     return RM_OK;
556 }
557
558 void NV_API_CALL os_flush_cpu_write_combine_buffer(void)
559 {
560     __asm__ __volatile__("sfence": : :"memory");
561 }
562
563 RM_STATUS NV_API_CALL os_raise_smp_barrier(void)
564 {
565     return RM_ERR_NOT_SUPPORTED;
566 }
567
568 RM_STATUS NV_API_CALL os_clear_smp_barrier(void)
569 {
570     return RM_ERR_NOT_SUPPORTED;
571 }
572
573 struct os_mutex {
574     nv_stack_t *sp;
575     struct spinlock lock;
576     int refcnt;
577 };
578
579 RM_STATUS NV_API_CALL os_alloc_sema(void **semaphore)
580 {
581     nv_stack_t *sp;
582     struct os_mutex *mtx;
583     RM_STATUS status;
584
585     NV_UMA_ZONE_ALLOC_STACK(sp);
586     if (sp == NULL)
587         return RM_ERR_NO_FREE_MEM;
588
589     status = os_alloc_mem((void **)&mtx, sizeof(struct os_mutex));
590     if (status != RM_OK) {
591         NV_UMA_ZONE_FREE_STACK(sp);
592         return status;
593     }
594
595     spin_init(&mtx->lock);
596
597     mtx->sp = sp;
598     mtx->refcnt = 1;
599
600     *semaphore = (void *) mtx;
601
602     return RM_OK;
603 }
604
605 RM_STATUS NV_API_CALL os_free_sema(void *semaphore)
606 {
607     struct os_mutex *mtx = semaphore;
608     nv_stack_t *sp;
609
610     sp = mtx->sp;
611     NV_UMA_ZONE_FREE_STACK(sp);
612
613     spin_uninit(&mtx->lock);
614
615     os_free_mem(semaphore);
616
617     return RM_OK;
618 }
619
620 RM_STATUS NV_API_CALL os_acquire_sema(void *semaphore)
621 {
622     struct os_mutex *mtx = semaphore;
623
624     spin_lock_wr(&mtx->lock);
625     if (mtx->refcnt > 0)
626         rm_disable_interrupts(mtx->sp);
627     mtx->refcnt--;
628     if (mtx->refcnt < 0)
629         msleep(mtx, &mtx->lock, 0, "nvsemaq", 0);
630     spin_unlock_wr(&mtx->lock);
631
632     return RM_OK;
633 }
634
635 BOOL NV_API_CALL os_cond_acquire_sema(void *semaphore)
636 {
637     struct os_mutex *mtx = semaphore;
638
639     spin_lock_wr(&mtx->lock);
640     if (mtx->refcnt < 1) {
641         spin_unlock_wr(&mtx->lock);
642         return FALSE;
643     } else {
644         rm_disable_interrupts(mtx->sp);
645         mtx->refcnt--;
646         spin_unlock_wr(&mtx->lock);
647     }
648
649     return TRUE;
650 }
651
652 RM_STATUS NV_API_CALL os_release_sema(void *semaphore)
653 {
654     struct os_mutex *mtx = semaphore;
655
656     spin_lock_wr(&mtx->lock);
657     if (mtx->refcnt < 0)
658         wakeup_one(mtx);
659     else
660         rm_enable_interrupts(mtx->sp);
661     mtx->refcnt++;
662     spin_unlock_wr(&mtx->lock);
663
664     return RM_OK;
665 }
666
667 BOOL NV_API_CALL os_is_acquired_sema(void *semaphore)
668 {
669     struct os_mutex *mtx = semaphore;
670     return (mtx->refcnt < 1);
671 }
672
673 BOOL NV_API_CALL os_pat_supported(void)
674 {
675     /*
676      * FreeBSD has no native PAT support and there's no good
677      * way to implement it privately as we do on Linux.
678      */
679     return FALSE;
680 }
681
682 RM_STATUS NV_API_CALL os_set_mlock_capability()
683 {
684     return (RM_ERROR);
685 }
686
687 S032 NV_API_CALL os_mlock_user_memory(
688     void *address,
689     U032 length
690 )
691 {
692     return -1;
693 }
694
695 S032 NV_API_CALL os_munlock_user_memory(
696     void *address,
697     U032 length
698 )
699 {
700     return -1;
701 }
702
703 RM_STATUS NV_API_CALL os_check_process_map_limit(
704     NvU64 proc_max_map_count
705 )
706 {
707     return (RM_ERROR);
708 }
709
710 void NV_API_CALL os_register_compatible_ioctl(
711     U032 cmd,
712     U032 size
713 )
714 {
715 }
716
717 void NV_API_CALL os_unregister_compatible_ioctl(
718     U032 cmd,
719     U032 size
720 )
721 {
722 }
723
724 RM_STATUS NV_API_CALL os_disable_console_access(void)
725 {
726     return RM_OK;
727 }
728
729 RM_STATUS NV_API_CALL os_enable_console_access(void)
730 {
731     return RM_OK;
732 }