import NVIDIA-FreeBSD-x86-190.42
[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_thread(NvU64 *threadId)
133 {
134     if (__NV_ITHREAD())
135         *threadId = 0;
136     else
137         *threadId = (NvU64) CURTHREAD->td_tid;
138
139     return RM_OK;
140 }
141
142 RM_STATUS NV_API_CALL os_get_current_time(
143     U032 *sec,
144     U032 *usec
145 )
146 {
147     struct timeval tv;
148
149     getmicrotime(&tv);
150
151     *sec  = tv.tv_sec;
152     *usec = tv.tv_usec;
153
154     return RM_OK;
155 }
156
157 BOOL NV_API_CALL os_is_administrator(PHWINFO pDev)
158 {
159     return suser(CURTHREAD) ? FALSE : TRUE;
160 }
161
162 U008 NV_API_CALL os_io_read_byte(
163     U032 address
164 )
165 {
166     /* XXX Fix me? (bus_space access) */
167     return inb(address);
168 }
169
170 void NV_API_CALL os_io_write_byte(
171     U032 address,
172     U008 value
173 )
174 {
175     /* XXX Fix me? (bus_space access) */
176     outb(address, value);
177 }
178
179 U016 NV_API_CALL os_io_read_word(
180     U032 address
181 )
182 {
183     /* XXX Fix me? (bus_space access) */
184     return inw(address);
185 }
186
187 void NV_API_CALL os_io_write_word(
188     U032 address,
189     U016 value
190 )
191 {
192     /* XXX Fix me? (bus_space access) */
193     return outw(address, value);
194 }
195
196 U032 NV_API_CALL os_io_read_dword(
197     U032 address
198 )
199 {
200     /* XXX Fix me? (bus_space access) */
201     return inl(address);
202 }
203
204 void NV_API_CALL os_io_write_dword(
205     U032 address,
206     U032 value
207 )
208 {
209     /* XXX Fix me? (bus_space access) */
210     outl(address, value);
211 }
212
213 void* NV_API_CALL os_map_kernel_space(
214     NvU64 start,
215     NvU64 size,
216     U032 mode
217 )
218 {
219     int map_mode;
220
221 #if defined(NVCPU_X86) && !defined(PAE)
222     if (start > 0xffffffff)
223         return NULL;
224 #endif
225
226     if (start & PAGE_MASK)
227         return NULL;
228
229     size = NV_ALIGN_UP(size, PAGE_SIZE);
230
231     switch (mode) {
232         case NV_MEMORY_CACHED:
233             map_mode = PAT_WRITE_BACK;
234             break;
235         case NV_MEMORY_WRITECOMBINED:
236             map_mode = PAT_WRITE_COMBINING;
237             break;
238         case NV_MEMORY_UNCACHED:
239         case NV_MEMORY_DEFAULT:
240             map_mode = PAT_UNCACHEABLE;
241             break;
242         default:
243             nv_printf(NV_DBG_ERRORS,
244                       "NVRM: unknown mode in os_map_kernel_space()\n");
245             return NULL;
246     }
247
248     return pmap_mapdev_attr(start, size, map_mode);
249 }
250
251 void NV_API_CALL os_unmap_kernel_space(
252     void *address,
253     NvU64 size
254 )
255 {
256     pmap_unmapdev_attr((vm_offset_t)address, size);
257 }
258
259 RM_STATUS NV_API_CALL os_set_mem_range(
260     NvU64 start,
261     NvU64 size,
262     U032 mode
263 )
264 {
265     int arg;
266     struct mem_range_desc mrd;
267
268     mrd.mr_base  = start;
269     mrd.mr_len   = size;
270     mrd.mr_flags = MDF_WRITECOMBINE;
271
272     strcpy(mrd.mr_owner, "NVIDIA");
273     arg = MEMRANGE_SET_UPDATE;
274
275     if (mem_range_attr_set(&mrd, &arg))
276         return RM_ERROR;
277
278     return RM_OK;
279 }
280
281 RM_STATUS NV_API_CALL os_unset_mem_range(
282     NvU64 start,
283     NvU64 size
284 )
285 {
286     int arg;
287     struct mem_range_desc mrd;
288
289     mrd.mr_base = start;
290     mrd.mr_len  = size;
291     arg = MEMRANGE_SET_REMOVE;
292
293     if (mem_range_attr_set(&mrd, &arg))
294         return RM_ERROR;
295
296     return RM_OK;
297 }
298
299
300 /*
301  * The current debug level is used to determine if certain debug messages
302  * are printed to the system console/log files or not. It defaults to the
303  * highest debug level, i.e. the lowest debug output.
304  */
305
306 U032 cur_debuglevel = 0xffffffff;
307
308 void NV_API_CALL os_dbg_init(void)
309 {
310     U032 new_debuglevel;
311     nv_stack_t *sp;
312
313     NV_UMA_ZONE_ALLOC_STACK(sp);
314     if (sp == NULL)
315         return;
316
317     if (rm_read_registry_dword(sp, NULL, "NVreg", "ResmanDebugLevel",
318             &new_debuglevel) == RM_OK) {
319         if (new_debuglevel != 0xffffffff)
320             cur_debuglevel = new_debuglevel;
321     }
322
323     NV_UMA_ZONE_FREE_STACK(sp);
324 }
325
326 RM_STATUS NV_API_CALL os_schedule(void)
327 {
328     return RM_ERR_NOT_SUPPORTED;
329 }
330
331 void NV_API_CALL os_dbg_set_level(U032 new_debuglevel)
332 {
333     cur_debuglevel = new_debuglevel;
334 }
335
336 void NV_API_CALL os_dbg_breakpoint(void)
337 {
338 #ifdef DEBUG
339     /* FreeBSD 5.x kdb */
340     kdb_enter("os_dbg_breakpoint()");
341 #endif
342 }
343
344 void NV_API_CALL out_string(const char *message)
345 {
346 #if defined(DEBUG) || defined(QA_BUILD)
347     printf("%s", message);
348 #endif
349 }
350
351 static char nv_error_string[MAX_ERROR_STRING];
352
353 int NV_API_CALL nv_printf(
354     U032 debuglevel,
355     const char *format,
356     ...
357 )
358 {
359     char *message = nv_error_string;
360     va_list arglist;
361     int chars_written = 0;
362
363     if (debuglevel >= ((cur_debuglevel >> 4) & 3)) {
364         va_start(arglist, format);
365         chars_written = vsprintf(message, format, arglist);
366         va_end(arglist);
367         printf("%s", message);
368     }
369
370     return chars_written;
371 }
372
373 int NV_API_CALL nv_snprintf(
374     char *buf,
375     unsigned int size,
376     const char *fmt,
377     ...
378 )
379 {
380     va_list arglist;
381     int chars_written;
382
383     va_start(arglist, fmt);
384     chars_written = vsnprintf(buf, size, fmt, arglist);
385     va_end(arglist);
386
387     return chars_written;
388 }
389
390 void NV_API_CALL nv_os_log(
391     int loglevel,
392     const char *fmt,
393     void *ap
394 )
395 {
396     int l;
397     sprintf(nv_error_string, "NVRM: ");
398     l = strlen(nv_error_string);
399     vsnprintf(nv_error_string + l, MAX_ERROR_STRING - l, fmt, ap);
400     printf("%s", nv_error_string);
401 }
402
403 S032 NV_API_CALL os_mem_cmp(
404     const U008 *buf0,
405     const U008 *buf1,
406     U032 length
407 )
408 {
409     return memcmp(buf0, buf1, length);
410 }
411
412 U008* NV_API_CALL os_mem_copy(
413     U008 *dst,
414     const U008 *src,
415     U032 length
416 )
417 {
418 #if defined(NVCPU_X86_64)
419     uint32_t i;
420     for (i = 0; i < length; i++) dst[i] = src[i];
421     return dst;
422 #else
423     return memcpy(dst, src, length);
424 #endif
425 }
426
427 RM_STATUS NV_API_CALL os_memcpy_from_user(
428     void *dst,
429     const void *src,
430     U032 length
431 )
432 {
433     if (src < (void *) VM_MAXUSER_ADDRESS)
434         return copyin(src, dst, length)  ? RM_ERR_INVALID_POINTER : RM_OK;
435
436     return os_mem_copy(dst, src, length) ? RM_ERR_INVALID_POINTER : RM_OK;
437 }
438
439 RM_STATUS NV_API_CALL os_memcpy_to_user(
440     void *dst,
441     const void *src,
442     U032 length
443 )
444 {
445     if (dst < (void *) VM_MAXUSER_ADDRESS)
446         return copyout(src, dst, length) ? RM_ERR_INVALID_POINTER : RM_OK;
447
448     return os_mem_copy(dst, src, length) ? RM_ERR_INVALID_POINTER : RM_OK;
449 }
450
451 void* NV_API_CALL os_mem_set(
452     void *b,
453     U008 c,
454     U032 length
455 )
456 {
457     return memset(b, c, length);
458 }
459
460 S032 NV_API_CALL os_string_compare(
461     const char *s1,
462     const char *s2
463 )
464 {
465     return strcmp(s1, s2);
466 }
467
468 char* NV_API_CALL os_string_copy(
469     char *dst,
470     const char *src
471 )
472 {
473     return strcpy(dst, src);
474 }
475
476 U032 NV_API_CALL os_string_length(const char* s)
477 {
478     return strlen(s);
479 }
480
481 RM_STATUS NV_API_CALL os_strncpy_from_user(
482     char *dst,
483     const char *src,
484     U032 n
485 )
486 {
487     return copyinstr(src, dst, n, NULL) ? RM_ERR_INVALID_POINTER : RM_OK;
488 }
489
490 U032 NV_API_CALL os_get_page_size(void)
491 {
492     return PAGE_SIZE;
493 }
494
495 NvU64 NV_API_CALL os_get_page_mask(void)
496 {
497     /*
498      * On FreeBSD, PAGE_MASK means (PAGE_SIZE - 1); on Linux it means the
499      * opposite, ~(PAGE_SIZE - 1); that is what this function is expected
500      * to return.
501      */
502     return ~PAGE_MASK;
503 }
504
505 NvU64 NV_API_CALL os_get_system_memory_size(void)
506 {
507     return ((NvU64)physmem * PAGE_SIZE) / RM_PAGE_SIZE;
508 }
509
510 U032 NV_API_CALL os_get_cpu_count(void)
511 {
512     return mp_ncpus;
513 }
514
515 RM_STATUS NV_API_CALL os_flush_cpu_cache(void)
516 {
517     /*
518      * XXX This will do for now, but this may need to be extended
519      * to make IPI calls (flushing all caches).
520      */
521     __asm__ __volatile__("wbinvd": : :"memory");
522     return RM_OK;
523 }
524
525 void NV_API_CALL os_flush_cpu_write_combine_buffer(void)
526 {
527     __asm__ __volatile__("sfence": : :"memory");
528 }
529
530 RM_STATUS NV_API_CALL os_raise_smp_barrier(void)
531 {
532     return RM_ERR_NOT_SUPPORTED;
533 }
534
535 RM_STATUS NV_API_CALL os_clear_smp_barrier(void)
536 {
537     return RM_ERR_NOT_SUPPORTED;
538 }
539
540 struct os_mutex {
541     nv_stack_t *sp;
542     struct mtx mutex_mtx;
543     struct cv mutex_cv;
544     int refcnt;
545 };
546
547 RM_STATUS NV_API_CALL os_alloc_sema(void **semaphore)
548 {
549     nv_stack_t *sp;
550     struct os_mutex *mtx;
551     RM_STATUS status;
552
553     NV_UMA_ZONE_ALLOC_STACK(sp);
554     if (sp == NULL)
555         return RM_ERR_NO_FREE_MEM;
556
557     status = os_alloc_mem((void **)&mtx, sizeof(struct os_mutex));
558     if (status != RM_OK) {
559         NV_UMA_ZONE_FREE_STACK(sp);
560         return status;
561     }
562
563     mtx_init(&mtx->mutex_mtx, "os.mutex_mtx", NULL, MTX_DEF | MTX_RECURSE);
564     cv_init(&mtx->mutex_cv, "os.mutex_cv");
565
566     mtx->sp = sp;
567     mtx->refcnt = 1;
568
569     *semaphore = (void *) mtx;
570
571     return RM_OK;
572 }
573
574 RM_STATUS NV_API_CALL os_free_sema(void *semaphore)
575 {
576     struct os_mutex *mtx = semaphore;
577     nv_stack_t *sp;
578
579     sp = mtx->sp;
580     NV_UMA_ZONE_FREE_STACK(sp);
581
582     mtx_destroy(&mtx->mutex_mtx);
583     cv_destroy(&mtx->mutex_cv);
584
585     os_free_mem(semaphore);
586
587     return RM_OK;
588 }
589
590 RM_STATUS NV_API_CALL os_acquire_sema(void *semaphore)
591 {
592     struct os_mutex *mtx = semaphore;
593
594     mtx_lock(&mtx->mutex_mtx);
595     if (mtx->refcnt > 0)
596         rm_disable_interrupts(mtx->sp);
597     mtx->refcnt--;
598     if (mtx->refcnt < 0)
599         cv_wait(&mtx->mutex_cv, &mtx->mutex_mtx);
600     mtx_unlock(&mtx->mutex_mtx);
601
602     return RM_OK;
603 }
604
605 RM_STATUS NV_API_CALL os_cond_acquire_sema(void *semaphore)
606 {
607     struct os_mutex *mtx = semaphore;
608
609     mtx_lock(&mtx->mutex_mtx);
610     if (mtx->refcnt < 1) {
611         mtx_unlock(&mtx->mutex_mtx);
612         return RM_ERROR;
613     } else {
614         rm_disable_interrupts(mtx->sp);
615         mtx->refcnt--;
616         mtx_unlock(&mtx->mutex_mtx);
617     }
618
619     return RM_OK;
620 }
621
622 RM_STATUS NV_API_CALL os_release_sema(void *semaphore)
623 {
624     struct os_mutex *mtx = semaphore;
625
626     mtx_lock(&mtx->mutex_mtx);
627     if (mtx->refcnt < 0)
628         cv_signal(&mtx->mutex_cv);
629     else
630         rm_enable_interrupts(mtx->sp);
631     mtx->refcnt++;
632     mtx_unlock(&mtx->mutex_mtx);
633
634     return RM_OK;
635 }
636
637 BOOL NV_API_CALL os_is_acquired_sema(void *semaphore)
638 {
639     struct os_mutex *mtx = semaphore;
640     return (mtx->refcnt < 1);
641 }
642
643 BOOL NV_API_CALL os_pat_supported(void)
644 {
645     /*
646      * FreeBSD has no native PAT support and there's no good
647      * way to implement it privately as we do on Linux.
648      */
649     return FALSE;
650 }
651
652 void NV_API_CALL os_register_compatible_ioctl(
653     U032 cmd,
654     U032 size
655 )
656 {
657 }
658
659 void NV_API_CALL os_unregister_compatible_ioctl(
660     U032 cmd,
661     U032 size
662 )
663 {
664 }
665
666 RM_STATUS NV_API_CALL os_disable_console_access(void)
667 {
668     return RM_OK;
669 }
670
671 RM_STATUS NV_API_CALL os_enable_console_access(void)
672 {
673     return RM_OK;
674 }
675 NvU64 NV_API_CALL os_acquire_spinlock(void *pSema)
676 {
677     struct os_mutex *mtx = pSema;
678
679     mtx_lock(&mtx->mutex_mtx);
680
681     return 0;
682 }
683
684 void NV_API_CALL os_release_spinlock(void *pSema, NvU64 oldIrql)
685 {
686     struct os_mutex *mtx = pSema;
687
688     mtx_unlock(&mtx->mutex_mtx);
689 }
690
691 RM_STATUS NV_API_CALL os_get_address_space_info(
692     NvU64 *userStartAddress,
693     NvU64 *userEndAddress,
694     NvU64 *kernelStartAddress,
695     NvU64 *kernelEndAddress
696 )
697 {
698     return RM_ERR_NOT_SUPPORTED;
699 }
700