nvidia-modprobe-utils.c 17.6 KB
Newer Older
Aaron Plattner's avatar
Aaron Plattner committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
/*
 * Copyright (c) 2013, NVIDIA CORPORATION.
 *
 * Permission is hereby granted, free of charge, to any person
 * obtaining a copy of this software and associated documentation
 * files (the "Software"), to deal in the Software without
 * restriction, including without limitation the rights to use, copy,
 * modify, merge, publish, distribute, sublicense, and/or sell copies
 * of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 *
 * This file provides utility functions on Linux for loading the
 * NVIDIA kernel module and creating NVIDIA device files.
 */

#if defined(NV_LINUX)

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/wait.h>
Aaron Plattner's avatar
Aaron Plattner committed
39
#include <fcntl.h>
Aaron Plattner's avatar
Aaron Plattner committed
40 41

#include "nvidia-modprobe-utils.h"
Aaron Plattner's avatar
Aaron Plattner committed
42
#include "pci-enum.h"
Aaron Plattner's avatar
Aaron Plattner committed
43 44 45

#define NV_PROC_MODPROBE_PATH "/proc/sys/kernel/modprobe"
#define NV_PROC_MODULES_PATH "/proc/modules"
Aaron Plattner's avatar
Aaron Plattner committed
46
#define NV_PROC_DEVICES_PATH "/proc/devices"
Aaron Plattner's avatar
Aaron Plattner committed
47

Aaron Plattner's avatar
Aaron Plattner committed
48 49 50
#define NV_PROC_MODPROBE_PATH_MAX        1024
#define NV_MAX_MODULE_NAME_SIZE          16
#define NV_MAX_PROC_REGISTRY_PATH_SIZE   NV_MAX_CHARACTER_DEVICE_FILE_STRLEN
Aaron Plattner's avatar
Aaron Plattner committed
51
#define NV_MAX_LINE_LENGTH               256
Aaron Plattner's avatar
Aaron Plattner committed
52

Aaron Plattner's avatar
Aaron Plattner committed
53
#define NV_NVIDIA_MODULE_NAME "nvidia"
Aaron Plattner's avatar
Aaron Plattner committed
54 55
#define NV_PROC_REGISTRY_PATH "/proc/driver/nvidia/params"

Aaron Plattner's avatar
Aaron Plattner committed
56 57 58
#define NV_NMODULE_NVIDIA_MODULE_NAME "nvidia%d"
#define NV_NMODULE_PROC_REGISTRY_PATH "/proc/driver/nvidia/%d/params"

Aaron Plattner's avatar
Aaron Plattner committed
59 60
#define NV_UVM_MODULE_NAME "nvidia-uvm"
#define NV_UVM_DEVICE_NAME "/dev/nvidia-uvm"
Aaron Plattner's avatar
Aaron Plattner committed
61
#define NV_UVM_TOOLS_DEVICE_NAME "/dev/nvidia-uvm-tools"
Aaron Plattner's avatar
Aaron Plattner committed
62

Aaron Plattner's avatar
Aaron Plattner committed
63 64
#define NV_MODESET_MODULE_NAME "nvidia-modeset"

Aaron Plattner's avatar
Aaron Plattner committed
65 66 67 68 69 70 71 72 73
#define NV_DEVICE_FILE_MODE_MASK (S_IRWXU|S_IRWXG|S_IRWXO)
#define NV_DEVICE_FILE_MODE (S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH)
#define NV_DEVICE_FILE_UID 0
#define NV_DEVICE_FILE_GID 0

#define NV_MAKE_DEVICE(x,y) ((dev_t)((x) << 8 | (y)))

#define NV_MAJOR_DEVICE_NUMBER 195

Aaron Plattner's avatar
Aaron Plattner committed
74 75
#define NV_PCI_VENDOR_ID    0x10DE

Aaron Plattner's avatar
Aaron Plattner committed
76 77
#define NV_MIN(a, b) (((a) < (b)) ? (a) : (b))

Aaron Plattner's avatar
Aaron Plattner committed
78
/*
Aaron Plattner's avatar
Aaron Plattner committed
79 80
 * Construct the nvidia kernel module name based on the input
 * module instance provided.  If an error occurs, the null
Aaron Plattner's avatar
Aaron Plattner committed
81 82 83 84 85 86 87 88 89 90 91 92
 * terminator will be written to nv_module_name[0].
 */
static __inline__ void assign_nvidia_kernel_module_name
(
    char nv_module_name[NV_MAX_MODULE_NAME_SIZE],
    int module_instance
)
{
    int ret;

    if (is_multi_module(module_instance))
    {
Aaron Plattner's avatar
Aaron Plattner committed
93
        ret = snprintf(nv_module_name, NV_MAX_MODULE_NAME_SIZE,
Aaron Plattner's avatar
Aaron Plattner committed
94 95 96 97
                       NV_NMODULE_NVIDIA_MODULE_NAME, module_instance);
    }
    else
    {
Aaron Plattner's avatar
Aaron Plattner committed
98
        ret = snprintf(nv_module_name, NV_MAX_MODULE_NAME_SIZE,
Aaron Plattner's avatar
Aaron Plattner committed
99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117
                       NV_NVIDIA_MODULE_NAME);
    }

    if (ret <= 0)
    {
        goto fail;
    }

    nv_module_name[NV_MAX_MODULE_NAME_SIZE - 1] = '\0';

    return;

fail:

    nv_module_name[0] = '\0';
}


/*
Aaron Plattner's avatar
Aaron Plattner committed
118 119
 * Construct the proc registry path name based on the input
 * module instance provided.  If an error occurs, the null
Aaron Plattner's avatar
Aaron Plattner committed
120 121 122 123 124 125 126 127 128 129 130 131
 * terminator will be written to proc_path[0].
 */
static __inline__ void assign_proc_registry_path
(
    char proc_path[NV_MAX_PROC_REGISTRY_PATH_SIZE],
    int module_instance
)
{
    int ret;

    if (is_multi_module(module_instance))
    {
Aaron Plattner's avatar
Aaron Plattner committed
132
        ret = snprintf(proc_path, NV_MAX_PROC_REGISTRY_PATH_SIZE,
Aaron Plattner's avatar
Aaron Plattner committed
133 134 135 136
                       NV_NMODULE_PROC_REGISTRY_PATH, module_instance);
    }
    else
    {
Aaron Plattner's avatar
Aaron Plattner committed
137
        ret = snprintf(proc_path, NV_MAX_PROC_REGISTRY_PATH_SIZE,
Aaron Plattner's avatar
Aaron Plattner committed
138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155
                       NV_PROC_REGISTRY_PATH);
    }

    if (ret <= 0)
    {
        goto fail;
    }

    proc_path[NV_MAX_PROC_REGISTRY_PATH_SIZE - 1] = '\0';

    return;

fail:

    proc_path[0] = '\0';
}


Aaron Plattner's avatar
Aaron Plattner committed
156
/*
Aaron Plattner's avatar
Aaron Plattner committed
157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190
 * Just like strcmp(3), except that differences between '-' and '_' are
 * ignored. This is useful for comparing module names, where '-' and '_'
 * are supposed to be treated interchangeably.
 */
static int modcmp(const char *a, const char *b)
{
    int i;

    /* Walk both strings and compare each character */
    for (i = 0; a[i] && b[i]; i++)
    {
        if (a[i] != b[i])
        {
            /* ignore differences between '-' and '_' */
            if (((a[i] == '-') || (a[i] == '_')) &&
                ((b[i] == '-') || (b[i] == '_')))
            {
                continue;
            }

            break;
        }
    }

    /*
     * If the strings are of unequal length, only one of a[i] or b[i] == '\0'.
     * If they are the same length, both will be '\0', and the strings match.
     */
    return a[i] - b[i];
}


/*
 * Check whether the specified module is loaded by reading
Aaron Plattner's avatar
Aaron Plattner committed
191 192 193
 * NV_PROC_MODULES_PATH; returns 1 if the kernel module is loaded.
 * Otherwise, it returns 0.
 */
Aaron Plattner's avatar
Aaron Plattner committed
194
static int is_kernel_module_loaded(const char *nv_module_name)
Aaron Plattner's avatar
Aaron Plattner committed
195 196
{
    FILE *fp;
Aaron Plattner's avatar
Aaron Plattner committed
197
    char module_name[NV_MAX_MODULE_NAME_SIZE];
Aaron Plattner's avatar
Aaron Plattner committed
198 199 200 201 202 203 204 205 206 207 208 209
    int module_loaded = 0;

    fp = fopen(NV_PROC_MODULES_PATH, "r");

    if (fp == NULL)
    {
        return 0;
    }

    while (fscanf(fp, "%15s%*[^\n]\n", module_name) == 1)
    {
        module_name[15] = '\0';
Aaron Plattner's avatar
Aaron Plattner committed
210
        if (modcmp(module_name, nv_module_name) == 0)
Aaron Plattner's avatar
Aaron Plattner committed
211 212 213 214 215 216 217 218 219 220 221
        {
            module_loaded = 1;
            break;
        }
    }

    fclose(fp);

    return module_loaded;
}

Aaron Plattner's avatar
Aaron Plattner committed
222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239
/*
 * Attempt to redirect STDOUT and STDERR to /dev/null.
 *
 * This is only for the cosmetics of silencing warnings, so do not
 * treat any errors here as fatal.
 */
static void silence_current_process(void)
{
    int dev_null_fd = open("/dev/null", O_RDWR);
    if (dev_null_fd < 0)
    {
        return;
    }

    dup2(dev_null_fd, STDOUT_FILENO);
    dup2(dev_null_fd, STDERR_FILENO);
    close(dev_null_fd);
}
Aaron Plattner's avatar
Aaron Plattner committed
240 241

/*
Aaron Plattner's avatar
Aaron Plattner committed
242
 * Attempt to load a kernel module; returns 1 if kernel module is
Aaron Plattner's avatar
Aaron Plattner committed
243 244 245 246 247 248
 * successfully loaded.  Returns 0 if the kernel module could not be
 * loaded.
 *
 * If any error is encountered and print_errors is non-0, then print the
 * error to stderr.
 */
Aaron Plattner's avatar
Aaron Plattner committed
249
static int modprobe_helper(const int print_errors, const char *module_name)
Aaron Plattner's avatar
Aaron Plattner committed
250 251 252
{
    char modprobe_path[NV_PROC_MODPROBE_PATH_MAX];
    int status = 1;
Aaron Plattner's avatar
Aaron Plattner committed
253
    struct stat file_status;
Aaron Plattner's avatar
Aaron Plattner committed
254 255 256 257
    pid_t pid;
    const char *envp[] = { "PATH=/sbin", NULL };
    FILE *fp;

Aaron Plattner's avatar
Aaron Plattner committed
258 259 260 261 262 263 264 265 266 267 268 269 270 271
    /*
     * Use PCI_BASE_CLASS_MASK to cover both types of DISPLAY controllers that
     * NVIDIA ships (VGA = 0x300 and 3D = 0x302).
     */
    struct pci_id_match id_match = {
        NV_PCI_VENDOR_ID,       /* Vendor ID    = 0x10DE                 */
        PCI_MATCH_ANY,          /* Device ID    = any                    */
        PCI_MATCH_ANY,          /* Subvendor ID = any                    */
        PCI_MATCH_ANY,          /* Subdevice ID = any                    */
        0x0300,                 /* Device Class = PCI_BASE_CLASS_DISPLAY */
        PCI_BASE_CLASS_MASK,    /* Display Mask = base class only        */
        0                       /* Initial number of matches             */
    };

Aaron Plattner's avatar
Aaron Plattner committed
272 273
    modprobe_path[0] = '\0';

Aaron Plattner's avatar
Aaron Plattner committed
274 275 276 277
    if (module_name == NULL || module_name[0] == '\0') {
        return 0;
    }

Aaron Plattner's avatar
Aaron Plattner committed
278 279
    /* If the kernel module is already loaded, nothing more to do: success. */

Aaron Plattner's avatar
Aaron Plattner committed
280
    if (is_kernel_module_loaded(module_name))
Aaron Plattner's avatar
Aaron Plattner committed
281 282 283 284
    {
        return 1;
    }

Aaron Plattner's avatar
Aaron Plattner committed
285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305
    /*
     * Before attempting to load the module, look for any NVIDIA PCI devices.
     * If none exist, exit instead of attempting the modprobe, because doing so
     * would issue error messages that are really irrelevant if there are no
     * NVIDIA PCI devices present.
     *
     * If our check fails, for whatever reason, continue with the modprobe just
     * in case.
     */
    status = pci_enum_match_id(&id_match);
    if (status == 0 && id_match.num_matches == 0)
    {
        if (print_errors)
        {
            fprintf(stderr,
                    "NVIDIA: no NVIDIA devices found\n");
        }

        return 0;
    }

Aaron Plattner's avatar
Aaron Plattner committed
306 307 308 309 310 311 312
    /* Only attempt to load the kernel module if root. */

    if (geteuid() != 0)
    {
        return 0;
    }

Aaron Plattner's avatar
Aaron Plattner committed
313 314 315 316 317 318 319 320 321
    /* Attempt to read the full path to the modprobe executable from /proc. */

    fp = fopen(NV_PROC_MODPROBE_PATH, "r");
    if (fp != NULL)
    {
        char *str;
        size_t n;

        n = fread(modprobe_path, 1, sizeof(modprobe_path), fp);
Aaron Plattner's avatar
Aaron Plattner committed
322 323 324 325 326 327 328

        /*
         * Null terminate the string, but make sure 'n' is in the range
         * [0, sizeof(modprobe_path)-1].
         */
        n = NV_MIN(n, sizeof(modprobe_path) - 1);
        modprobe_path[n] = '\0';
Aaron Plattner's avatar
Aaron Plattner committed
329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350

        /*
         * If str was longer than a line, we might still have a
         * newline in modprobe_path: if so, overwrite it with the nul
         * terminator.
         */
        str = strchr(modprobe_path, '\n');
        if (str != NULL)
        {
            *str = '\0';
        }

        fclose(fp);
    }

    /* If we couldn't read it from /proc, pick a reasonable default. */

    if (modprobe_path[0] == '\0')
    {
        sprintf(modprobe_path, "/sbin/modprobe");
    }

Aaron Plattner's avatar
Aaron Plattner committed
351 352 353 354 355 356 357 358 359
    /* Do not attempt to exec(3) modprobe if it does not exist. */

    if (stat(modprobe_path, &file_status) != 0 ||
        !S_ISREG(file_status.st_mode) ||
        (file_status.st_mode & S_IXUSR) != S_IXUSR)
    {
        return 0;
    }

Aaron Plattner's avatar
Aaron Plattner committed
360 361 362 363 364 365
    /* Fork and exec modprobe from the child process. */

    switch (pid = fork())
    {
        case 0:

Aaron Plattner's avatar
Aaron Plattner committed
366 367 368 369 370 371 372 373 374 375 376
            /*
             * modprobe might complain in expected scenarios.  E.g.,
             * `modprobe nvidia` on a Tegra system with dGPU where no nvidia.ko is
             * present will complain:
             *
             *  "modprobe: FATAL: Module nvidia not found."
             *
             * Silence the current process to avoid such unwanted messages.
             */
            silence_current_process();

Aaron Plattner's avatar
Aaron Plattner committed
377
            execle(modprobe_path, "modprobe",
Aaron Plattner's avatar
Aaron Plattner committed
378
                   module_name, NULL, envp);
Aaron Plattner's avatar
Aaron Plattner committed
379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411

            /* If execl(3) returned, then an error has occurred. */

            if (print_errors)
            {
                fprintf(stderr,
                        "NVIDIA: failed to execute `%s`: %s.\n",
                        modprobe_path, strerror(errno));
            }
            exit(1);

        case -1:
            return 0;

        default:
            if (waitpid(pid, &status, 0) < 0)
            {
                return 0;
            }
            if (WIFEXITED(status) && WEXITSTATUS(status) == 0)
            {
                return 1;
            }
            else
            {
                return 0;
            }
    }

    return 1;
}


Aaron Plattner's avatar
Aaron Plattner committed
412 413 414 415 416 417 418 419 420 421 422 423 424
/*
 * Attempt to load an NVIDIA kernel module
 */
int nvidia_modprobe(const int print_errors, int module_instance)
{
    char nv_module_name[NV_MAX_MODULE_NAME_SIZE];

    assign_nvidia_kernel_module_name(nv_module_name, module_instance);

    return modprobe_helper(print_errors, nv_module_name);
}


Aaron Plattner's avatar
Aaron Plattner committed
425 426 427 428 429 430 431
/*
 * Determine the requested device file parameters: allow users to
 * override the default UID/GID and/or mode of the NVIDIA device
 * files, or even whether device file modification should be allowed;
 * the attributes are managed globally, and can be adjusted via the
 * appropriate kernel module parameters.
 */
Aaron Plattner's avatar
Aaron Plattner committed
432
static void init_device_file_parameters(uid_t *uid, gid_t *gid, mode_t *mode,
Aaron Plattner's avatar
Aaron Plattner committed
433
                                        int *modify, const char *proc_path)
Aaron Plattner's avatar
Aaron Plattner committed
434 435 436 437 438 439 440 441 442 443
{
    FILE *fp;
    char name[32];
    unsigned int value;

    *mode = NV_DEVICE_FILE_MODE;
    *uid = NV_DEVICE_FILE_UID;
    *gid = NV_DEVICE_FILE_GID;
    *modify = 1;

Aaron Plattner's avatar
Aaron Plattner committed
444
    if (proc_path == NULL || proc_path[0] == '\0')
Aaron Plattner's avatar
Aaron Plattner committed
445 446 447 448 449
    {
        return;
    }

    fp = fopen(proc_path, "r");
Aaron Plattner's avatar
Aaron Plattner committed
450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481

    if (fp == NULL)
    {
        return;
    }

    while (fscanf(fp, "%31[^:]: %u\n", name, &value) == 2)
    {
        name[31] = '\0';
        if (strcmp(name, "DeviceFileUID") == 0)
        {
            *uid = value;
        }
        if (strcmp(name, "DeviceFileGID") == 0)
        {
            *gid = value;
        }
        if (strcmp(name, "DeviceFileMode") == 0)
        {
            *mode = value;
        }
        if (strcmp(name, "ModifyDeviceFiles") == 0)
        {
            *modify = value;
        }
    }

    fclose(fp);
}


/*
Aaron Plattner's avatar
Aaron Plattner committed
482 483 484
 * Attempt to create the specified device file with the specified major
 * and minor number.  If proc_path is specified, scan it for custom file
 * permissions.  Returns 1 if the file is successfully created; returns 0
Aaron Plattner's avatar
Aaron Plattner committed
485 486
 * if the file could not be created.
 */
Aaron Plattner's avatar
Aaron Plattner committed
487 488
static int mknod_helper(int major, int minor, const char *path,
                        const char *proc_path)
Aaron Plattner's avatar
Aaron Plattner committed
489
{
Aaron Plattner's avatar
Aaron Plattner committed
490
    dev_t dev = NV_MAKE_DEVICE(major, minor);
Aaron Plattner's avatar
Aaron Plattner committed
491 492 493 494 495 496 497 498
    mode_t mode;
    uid_t uid;
    gid_t gid;
    int modification_allowed;
    int ret;
    struct stat stat_buf;
    int do_mknod;

Aaron Plattner's avatar
Aaron Plattner committed
499
    if (path == NULL || path[0] == '\0')
Aaron Plattner's avatar
Aaron Plattner committed
500 501 502 503
    {
        return 0;
    }

Aaron Plattner's avatar
Aaron Plattner committed
504
    init_device_file_parameters(&uid, &gid, &mode, &modification_allowed,
Aaron Plattner's avatar
Aaron Plattner committed
505
                                proc_path);
Aaron Plattner's avatar
Aaron Plattner committed
506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582

    /* If device file modification is not allowed, nothing to do: success. */

    if (modification_allowed != 1)
    {
        return 1;
    }

    /*
     * If the device file already exists with correct properties,
     * nothing to do: success.
     */
    ret = stat(path, &stat_buf);

    if ((ret == 0) &&
        (S_ISCHR(stat_buf.st_mode)) &&
        (stat_buf.st_rdev == dev) &&
        ((stat_buf.st_mode & NV_DEVICE_FILE_MODE_MASK) == mode) &&
        (stat_buf.st_uid == uid) &&
        (stat_buf.st_gid == gid))
    {
        return 1;
    }

    /* If the stat(2) above failed, we need to create the device file. */

    do_mknod = 0;

    if (ret != 0)
    {
        do_mknod = 1;
    }

    /*
     * If the stat(2) above succeeded but the file is either not a
     * character device or has the wrong major/minor character device
     * number, then we need to delete it and recreate it.
     */
    if ((ret == 0) &&
        (!S_ISCHR(stat_buf.st_mode) ||
         (stat_buf.st_rdev != dev)))
    {
        ret = remove(path);
        if (ret != 0)
        {
            return 0;
        }
        do_mknod = 1;
    }

    if (do_mknod)
    {
        ret = mknod(path, S_IFCHR | mode, dev);
        if (ret != 0)
        {
            return 0;
        }
    }

    /*
     * Make sure the permissions and ownership are set correctly; if
     * we created the device above and either of the below fails, then
     * also delete the device file.
     */
    if ((chmod(path, mode) != 0) ||
        (chown(path, uid, gid) != 0))
    {
        if (do_mknod)
        {
            remove(path);
        }
        return 0;
    }

    return 1;
}

Aaron Plattner's avatar
Aaron Plattner committed
583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677

/*
 * Attempt to create a device file with the specified minor number for
 * the specified NVIDIA module instance.
 */
int nvidia_mknod(int minor, int module_instance)
{
    char path[NV_MAX_CHARACTER_DEVICE_FILE_STRLEN];
    char proc_path[NV_MAX_PROC_REGISTRY_PATH_SIZE];

    assign_device_file_name(path, minor, module_instance);
    assign_proc_registry_path(proc_path, module_instance);

    return mknod_helper(NV_MAJOR_DEVICE_NUMBER, minor, path, proc_path);
}


/*
 * Scan NV_PROC_DEVICES_PATH to find the major number of the character
 * device with the specified name.  Returns the major number on success,
 * or -1 on failure.
 */
static int get_chardev_major(const char *name)
{
    int ret = -1;
    char line[NV_MAX_LINE_LENGTH];
    FILE *fp;

    line[NV_MAX_LINE_LENGTH - 1] = '\0';

    fp = fopen(NV_PROC_DEVICES_PATH, "r");
    if (!fp)
    {
        goto done;
    }

    /* Find the beginning of the 'Character devices:' section */

    while (fgets(line, NV_MAX_LINE_LENGTH - 1, fp))
    {
        if (strcmp(line, "Character devices:\n") == 0)
        {
            break;
        }
    }

    if (ferror(fp)) {
        goto done;
    }

    /* Search for the given module name */

    while (fgets(line, NV_MAX_LINE_LENGTH - 1, fp))
    {
        char *found;

        if (strcmp(line, "\n") == 0 )
        {
            /* we've reached the end of the 'Character devices:' section */
            break;
        }

        found = strstr(line, name);

        /* Check for a newline to avoid partial matches */

        if (found && found[strlen(name)] == '\n')
        {
            int major;

            /* Read the device major number */

            if (sscanf(line, " %d %*s", &major) == 1)
            {
                ret = major;
            }

            break;
        }
    }

done:

    if (fp)
    {
        fclose(fp);
    }

    return ret;
}


/*
 * Attempt to create the NVIDIA Unified Memory device file
 */
Aaron Plattner's avatar
Aaron Plattner committed
678
int nvidia_uvm_mknod(int base_minor)
Aaron Plattner's avatar
Aaron Plattner committed
679 680 681 682 683 684 685 686
{
    int major = get_chardev_major(NV_UVM_MODULE_NAME);

    if (major < 0)
    {
        return 0;
    }

Aaron Plattner's avatar
Aaron Plattner committed
687 688
    return mknod_helper(major, base_minor, NV_UVM_DEVICE_NAME, NULL) &&
           mknod_helper(major, base_minor + 1, NV_UVM_TOOLS_DEVICE_NAME, NULL);
Aaron Plattner's avatar
Aaron Plattner committed
689 690 691 692 693 694
}


/*
 * Attempt to load the NVIDIA Unified Memory kernel module
 */
Aaron Plattner's avatar
Aaron Plattner committed
695
int nvidia_uvm_modprobe(void)
Aaron Plattner's avatar
Aaron Plattner committed
696
{
Aaron Plattner's avatar
Aaron Plattner committed
697
    return modprobe_helper(0, NV_UVM_MODULE_NAME);
Aaron Plattner's avatar
Aaron Plattner committed
698 699
}

Aaron Plattner's avatar
Aaron Plattner committed
700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723

/*
 * Attempt to load the NVIDIA modeset driver.
 */
int nvidia_modeset_modprobe(void)
{
    return modprobe_helper(0, NV_MODESET_MODULE_NAME);
}


/*
 * Attempt to create the NVIDIA modeset driver device file.
 */
int nvidia_modeset_mknod(void)
{
    char proc_path[NV_MAX_PROC_REGISTRY_PATH_SIZE];

    assign_proc_registry_path(proc_path, 0);

    return mknod_helper(NV_MAJOR_DEVICE_NUMBER,
                        NV_MODESET_MINOR_DEVICE_NUM,
                        NV_MODESET_DEVICE_NAME, proc_path);
}

Aaron Plattner's avatar
Aaron Plattner committed
724
#endif /* NV_LINUX */