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