1 /* _NVRM_COPYRIGHT_BEGIN_
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.
12 #include "os-interface.h"
14 #include "nv-freebsd.h"
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).
23 MALLOC_DEFINE(M_NVIDIA, "nvidia", "NVIDIA memory allocations");
26 #define MAX_ERROR_STRING 256
28 RM_STATUS NV_API_CALL os_alloc_contig_pages(
33 *address = contigmalloc(size, M_NVIDIA, 0, 0, ~0, PAGE_SIZE, 0);
34 return *address ? RM_OK : RM_ERROR;
37 void NV_API_CALL os_free_contig_pages(
42 contigfree(address, size, M_NVIDIA);
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.
51 RM_STATUS NV_API_CALL os_alloc_mem(
56 /* XXX Fix me? (malloc flags) */
57 *address = malloc(size, M_NVIDIA, M_NOWAIT | M_ZERO);
58 return *address ? RM_OK : RM_ERROR;
61 void NV_API_CALL os_free_mem(void *address)
63 free(address, M_NVIDIA);
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)
71 RM_STATUS NV_API_CALL os_delay(U032 MilliSeconds)
73 unsigned long MicroSeconds;
75 struct timeval tv_end, tv_aux;
77 getmicrotime(&tv_aux);
79 if (__NV_ITHREAD() && (MilliSeconds > NV_MAX_ISR_DELAY_MS))
83 DELAY(MilliSeconds * 1000);
87 MicroSeconds = MilliSeconds * 1000;
88 tv_end.tv_usec = MicroSeconds;
90 /* tv_end = tv_aux + tv_end */
91 NV_TIMERADD(&tv_aux, &tv_end, &tv_end);
93 ticks = NV_USECS_TO_TICKS(MicroSeconds);
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);
105 } while ((ticks = NV_USECS_TO_TICKS(MicroSeconds)) > 0);
108 if (MicroSeconds > 0)
114 RM_STATUS NV_API_CALL os_delay_us(U032 MicroSeconds)
116 if (__NV_ITHREAD() && (MicroSeconds > NV_MAX_ISR_DELAY_US))
122 U032 NV_API_CALL os_get_cpu_frequency(void)
124 return ((tsc_freq + 4999) / 1000000);
127 U032 NV_API_CALL os_get_current_process(void)
129 return curproc->p_pid;
132 RM_STATUS NV_API_CALL os_kill_process(
137 struct proc *p = pfind(pid);
140 * No process with the specified PID was found running. We notify
141 * the caller, but take no other action.
143 return RM_ERR_OPERATING_SYSTEM;
151 RM_STATUS NV_API_CALL os_get_current_time(
166 BOOL NV_API_CALL os_is_administrator(PHWINFO pDev)
168 return suser(CURTHREAD) ? FALSE : TRUE;
171 U008 NV_API_CALL os_io_read_byte(
176 /* XXX Fix me? (bus_space access) */
180 void NV_API_CALL os_io_write_byte(
186 /* XXX Fix me? (bus_space access) */
187 outb(address, value);
190 U016 NV_API_CALL os_io_read_word(
195 /* XXX Fix me? (bus_space access) */
199 void NV_API_CALL os_io_write_word(
205 /* XXX Fix me? (bus_space access) */
206 return outw(address, value);
209 U032 NV_API_CALL os_io_read_dword(
214 /* XXX Fix me? (bus_space access) */
218 void NV_API_CALL os_io_write_dword(
224 /* XXX Fix me? (bus_space access) */
225 outl(address, value);
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.
235 * XXX This introduces aliasing concerns, but it's better than using the
236 * cached direct mapping.
239 void* NV_API_CALL os_map_kernel_space(
246 U032 cache_bits = (mode != NV_MEMORY_CACHED) ? PG_N : 0;
249 #if defined(NVCPU_X86) && !defined(PAE)
250 if (start > 0xffffffff)
254 if (start & PAGE_MASK)
257 size = NV_ALIGN_UP(size, PAGE_SIZE);
259 va = kmem_alloc_nofault(kernel_map, size);
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);
268 pmap_invalidate_range(kernel_pmap, va, tva);
274 void NV_API_CALL os_unmap_kernel_space(
281 size = NV_ALIGN_UP(size, PAGE_SIZE);
283 va = (vm_offset_t) address;
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);
292 void* NV_API_CALL os_map_kernel_space_high(
298 if (!(pfn & ~0xfffff)) {
299 start = pfn << PAGE_SHIFT;
300 return os_map_kernel_space(start, size, NV_MEMORY_CACHED);
305 void NV_API_CALL os_unmap_kernel_space_high(
311 os_unmap_kernel_space(addr, size);
314 RM_STATUS NV_API_CALL os_set_mem_range(
321 struct mem_range_desc mrd;
325 mrd.mr_flags = MDF_WRITECOMBINE;
327 strcpy(mrd.mr_owner, "NVIDIA");
328 arg = MEMRANGE_SET_UPDATE;
330 if (mem_range_attr_set(&mrd, &arg))
336 RM_STATUS NV_API_CALL os_unset_mem_range(
342 struct mem_range_desc mrd;
346 arg = MEMRANGE_SET_REMOVE;
348 if (mem_range_attr_set(&mrd, &arg))
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.
361 U032 cur_debuglevel = 0xffffffff;
363 void NV_API_CALL os_dbg_init(void)
368 NV_UMA_ZONE_ALLOC_STACK(sp);
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;
378 NV_UMA_ZONE_FREE_STACK(sp);
381 void NV_API_CALL os_dbg_set_level(U032 new_debuglevel)
383 cur_debuglevel = new_debuglevel;
386 void NV_API_CALL NV_STACKWATCH_CALLBACK os_dbg_breakpoint(void)
389 /* FreeBSD 5.x kdb */
390 kdb_enter("os_dbg_breakpoint()");
394 void NV_API_CALL out_string(const char *message)
396 #if defined(DEBUG) || defined(QA_BUILD)
397 printf("%s", message);
401 static char nv_error_string[MAX_ERROR_STRING];
403 int NV_API_CALL nv_printf(
409 char *message = nv_error_string;
411 int chars_written = 0;
413 if (debuglevel >= ((cur_debuglevel >> 4) & 3)) {
414 va_start(arglist, format);
415 chars_written = vsprintf(message, format, arglist);
417 printf("%s", message);
420 return chars_written;
423 int NV_API_CALL nv_snprintf(
433 va_start(arglist, fmt);
434 chars_written = vsnprintf(buf, size, fmt, arglist);
437 return chars_written;
440 void NV_API_CALL nv_os_log(
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);
453 S032 NV_API_CALL os_mem_cmp(
459 return memcmp(buf0, buf1, length);
462 U008* NV_API_CALL os_mem_copy(
468 #if defined(NVCPU_X86_64)
470 for (i = 0; i < length; i++) dst[i] = src[i];
473 return memcpy(dst, src, length);
477 RM_STATUS NV_API_CALL os_memcpy_from_user(
483 if (src < (void *) VM_MAXUSER_ADDRESS)
484 return copyin(src, dst, length) ? RM_ERR_INVALID_POINTER : RM_OK;
486 return os_mem_copy(dst, src, length) ? RM_ERR_INVALID_POINTER : RM_OK;
489 RM_STATUS NV_API_CALL os_memcpy_to_user(
495 if (dst < (void *) VM_MAXUSER_ADDRESS)
496 return copyout(src, dst, length) ? RM_ERR_INVALID_POINTER : RM_OK;
498 return os_mem_copy(dst, src, length) ? RM_ERR_INVALID_POINTER : RM_OK;
501 void* NV_API_CALL os_mem_set(
507 return memset(b, c, length);
510 S032 NV_API_CALL os_string_compare(
515 return strcmp(s1, s2);
518 U008* NV_API_CALL os_string_copy(
523 return strcpy(dst, src);
526 U032 NV_API_CALL os_string_length(const U008* s)
531 RM_STATUS NV_API_CALL os_strncpy_from_user(
537 return copyinstr(src, dst, n, NULL) ? RM_ERR_INVALID_POINTER : RM_OK;
540 U032 NV_API_CALL os_get_page_size(void)
545 NvU64 NV_API_CALL os_get_page_mask(void)
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
555 NvU64 NV_API_CALL os_get_system_memory_size(void)
557 return ((NvU64)physmem * PAGE_SIZE) / RM_PAGE_SIZE;
560 U032 NV_API_CALL os_get_cpu_count(void)
565 RM_STATUS NV_API_CALL os_flush_cpu_cache(void)
568 * XXX This will do for now, but this may need to be extended
569 * to make IPI calls (flushing all caches).
571 __asm__ __volatile__("wbinvd": : :"memory");
575 void NV_API_CALL os_flush_cpu_write_combine_buffer(void)
577 __asm__ __volatile__("sfence": : :"memory");
580 RM_STATUS NV_API_CALL os_raise_smp_barrier(void)
585 RM_STATUS NV_API_CALL os_clear_smp_barrier(void)
592 struct mtx mutex_mtx;
597 RM_STATUS NV_API_CALL os_alloc_sema(void **semaphore)
600 struct os_mutex *mtx;
603 NV_UMA_ZONE_ALLOC_STACK(sp);
605 return RM_ERR_NO_FREE_MEM;
607 status = os_alloc_mem((void **)&mtx, sizeof(struct os_mutex));
608 if (status != RM_OK) {
609 NV_UMA_ZONE_FREE_STACK(sp);
613 mtx_init(&mtx->mutex_mtx, "os.mutex_mtx", NULL, MTX_DEF | MTX_RECURSE);
614 cv_init(&mtx->mutex_cv, "os.mutex_cv");
619 *semaphore = (void *) mtx;
624 RM_STATUS NV_API_CALL os_free_sema(void *semaphore)
626 struct os_mutex *mtx = semaphore;
630 NV_UMA_ZONE_FREE_STACK(sp);
632 mtx_destroy(&mtx->mutex_mtx);
633 cv_destroy(&mtx->mutex_cv);
635 os_free_mem(semaphore);
640 RM_STATUS NV_API_CALL os_acquire_sema(void *semaphore)
642 struct os_mutex *mtx = semaphore;
644 mtx_lock(&mtx->mutex_mtx);
646 rm_disable_interrupts(mtx->sp);
649 cv_wait(&mtx->mutex_cv, &mtx->mutex_mtx);
650 mtx_unlock(&mtx->mutex_mtx);
655 BOOL NV_API_CALL os_cond_acquire_sema(void *semaphore)
657 struct os_mutex *mtx = semaphore;
659 mtx_lock(&mtx->mutex_mtx);
660 if (mtx->refcnt < 1) {
661 mtx_unlock(&mtx->mutex_mtx);
664 rm_disable_interrupts(mtx->sp);
666 mtx_unlock(&mtx->mutex_mtx);
672 RM_STATUS NV_API_CALL os_release_sema(void *semaphore)
674 struct os_mutex *mtx = semaphore;
676 mtx_lock(&mtx->mutex_mtx);
678 cv_signal(&mtx->mutex_cv);
680 rm_enable_interrupts(mtx->sp);
682 mtx_unlock(&mtx->mutex_mtx);
687 BOOL NV_API_CALL os_is_acquired_sema(void *semaphore)
689 struct os_mutex *mtx = semaphore;
690 return (mtx->refcnt < 1);
693 BOOL NV_API_CALL os_pat_supported(void)
696 * FreeBSD has no native PAT support and there's no good
697 * way to implement it privately as we do on Linux.
702 void* NV_API_CALL NV_STACKWATCH_CALLBACK os_get_stack_start(void *stack_pointer)
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));
710 return (void *)cpu_getstack(td);
713 NvU64 NV_API_CALL os_get_current_pdpte(U032 address)
718 RM_STATUS NV_API_CALL os_set_mlock_capability()
723 S032 NV_API_CALL os_mlock_user_memory(
731 S032 NV_API_CALL os_munlock_user_memory(
739 RM_STATUS NV_API_CALL os_check_process_map_limit(
740 NvU64 proc_max_map_count
746 void NV_API_CALL os_register_compatible_ioctl(
753 void NV_API_CALL os_unregister_compatible_ioctl(
760 RM_STATUS NV_API_CALL os_disable_console_access(void)
765 RM_STATUS NV_API_CALL os_enable_console_access(void)