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