diff options
author | Aaron Plattner <aplattner@nvidia.com> | 2008-02-13 10:20:36 -0800 |
---|---|---|
committer | Aaron Plattner <aplattner@nvidia.com> | 2008-02-13 10:20:36 -0800 |
commit | 6d2a0069d419975ba01c2c423b18ef7cd2e76a6f (patch) | |
tree | 130e177fc9fa77d4c7d0788ba07c3b5af42dd84f /kernel.c |
1.0-61061.0-6106
Diffstat (limited to 'kernel.c')
-rw-r--r-- | kernel.c | 1568 |
1 files changed, 1568 insertions, 0 deletions
diff --git a/kernel.c b/kernel.c new file mode 100644 index 0000000..26be0ab --- /dev/null +++ b/kernel.c @@ -0,0 +1,1568 @@ +/* + * nvidia-installer: A tool for installing NVIDIA software packages on + * Unix and Linux systems. + * + * Copyright (C) 2003 NVIDIA Corporation + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the: + * + * Free Software Foundation, Inc. + * 59 Temple Place - Suite 330 + * Boston, MA 02111-1307, USA + */ + +#include <string.h> +#include <unistd.h> +#include <errno.h> +#include <sys/utsname.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <ctype.h> +#include <stdlib.h> +#include <dirent.h> +#include <fcntl.h> +#include <sys/mman.h> + +#include "nvidia-installer.h" +#include "kernel.h" +#include "user-interface.h" +#include "files.h" +#include "misc.h" +#include "precompiled.h" +#include "snarf.h" +#include "crc.h" + +/* local prototypes */ + +static char *default_kernel_module_installation_path(Options *op); +static char *default_kernel_source_path(Options *op); +static int check_for_loaded_kernel_module(Options *op, const char *); +static int rmmod_kernel_module(Options *op, const char *); +static PrecompiledInfo *download_updated_kernel_interface(Options*, Package*, + const char*); +static int cc_version_check(Options *op, Package *p); +static int rivafb_check(Options *op, Package *p); +static void rivafb_module_check(Options *op, Package *p); + +static PrecompiledInfo *scan_dir(Options *op, Package *p, + const char *directory_name, + const char *output_filename, + const char *proc_version_string); + +static char *build_distro_precompiled_kernel_interface_dir(Options *op); +static char *convert_include_path_to_source_path(const char *inc); +static char *guess_kernel_module_filename(Options *op); + +/* + * Message text that is used by several error messages. + */ + +static const char install_your_kernel_source[] = +"Please make sure you have installed the kernel source files " +"for your kernel; on Red Hat Linux systems, for example, be " +"sure you have the 'kernel-source' rpm installed. If you know the " +"correct kernel source files are installed, you may specify the " +"kernel source path with the '--kernel-source-path' " +"commandline option."; + + + + +/* + * determine_kernel_module_installation_path() - get the installation + * path for the kernel module. The order is: + * + * - if op->kernel_module_installation_path is non-NULL, then it must + * have been initialized by the commandline parser, and therefore we + * should obey that (so just return). + * + * - get the default installation path + * + * - if in expert mode, ask the user, and use what they gave, if + * non-NULL + */ + +int determine_kernel_module_installation_path(Options *op) +{ + char *result; + int count = 0; + + if (op->kernel_module_installation_path) return TRUE; + + op->kernel_module_installation_path = + default_kernel_module_installation_path(op); + + if (!op->kernel_module_installation_path) return FALSE; + + if (op->expert) { + + ask_for_kernel_install_path: + + result = ui_get_input(op, op->kernel_module_installation_path, + "Kernel module installation path"); + if (result && result[0]) { + free(op->kernel_module_installation_path); + op->kernel_module_installation_path = result; + if (!confirm_path(op, op->kernel_module_installation_path)) { + return FALSE; + } + } else { + if (result) free(result); + + if (++count < NUM_TIMES_QUESTIONS_ASKED) { + ui_warn(op, "Invalid kernel module installation path."); + goto ask_for_kernel_install_path; + } else { + ui_error(op, "Unable to determine kernel module " + "installation path."); + + return FALSE; + } + } + } + + if (!mkdir_recursive(op, op->kernel_module_installation_path, 0755)) + return FALSE; + + ui_expert(op, "Kernel module installation path: %s", + op->kernel_module_installation_path); + + return TRUE; + +} /* determine_kernel_module_installation_path() */ + + + +/* + * determine_kernel_source_path() - find the qualified path to the + * kernel source tree. This is called from install_from_cwd() if we + * need to compile the kernel interface files. Assigns + * op->kernel_source_path and returns TRUE if successful. Returns + * FALSE if no kernel source tree was found. + */ + +int determine_kernel_source_path(Options *op) +{ + char *result; + int count = 0; + + /* determine the kernel source path */ + + op->kernel_source_path = default_kernel_source_path(op); + + if (op->expert) { + + ask_for_kernel_source_path: + + result = ui_get_input(op, op->kernel_source_path, + "Kernel source path"); + if (result && result[0]) { + if (!directory_exists(op, result)) { + ui_warn(op, "Kernel source path '%s' does not exist.", + result); + free(result); + + if (++count < NUM_TIMES_QUESTIONS_ASKED) { + goto ask_for_kernel_source_path; + } else { + op->kernel_source_path = NULL; + } + } else { + op->kernel_source_path = result; + } + } else { + ui_warn(op, "Invalid kernel source path."); + if (result) free(result); + + if (++count < NUM_TIMES_QUESTIONS_ASKED) { + goto ask_for_kernel_source_path; + } else { + op->kernel_source_path = NULL; + } + } + } + + /* if we STILL don't have a kernel source path, give up */ + + if (!op->kernel_source_path) { + ui_error(op, "Unable to find the kernel source tree for the " + "currently running kernel. %s", install_your_kernel_source); + + /* + * I suppose we could ask them here for the kernel source + * path, but we've already given them multiple methods of + * specifying their kernel source tree. + */ + + return FALSE; + } + + /* check that the kernel source path exists */ + + if (!directory_exists(op, op->kernel_source_path)) { + ui_error (op, "The kernel source path '%s' does not exist. %s", + op->kernel_source_path, install_your_kernel_source); + op->kernel_source_path = NULL; + return FALSE; + } + + /* check that <path>/include/linux/kernel.h exists */ + + result = nvstrcat(op->kernel_source_path, "/include/linux/kernel.h", NULL); + if (access(result, F_OK) == -1) { + ui_error(op, "The kernel header file '%s' does not exist. " + "The most likely reason for this is that the kernel source " + "path '%s' is incorrect. %s", result, + op->kernel_source_path, install_your_kernel_source); + free(result); + return FALSE; + } + free(result); + +#if 0 + + /* + * XXX The following heurisitic is broken: distribution-provided + * 2.6 kernel sources may not contain compile.h, even though they + * are valid to compile kernel modules against. One suggestion + * has been to check for autoconf.h instead. Disabling this + * entire check for now... + */ + + /* + * try to check that the kernel headers have been configured; this + * is complicated by the fact that 2.4 and 2.6 kernels have + * different header files. Here is the heuristic: + * + * if compile.h exists: + * this is a 2.6 kernel + * if $(KERNEL_SOURCES)/Makefile does not exist + * the kernel sources have not been configured + * + * if compile.h does not exit: + * this is a 2.4 kernel + * if modversions.h does not exist + * the kernel sources have not been configured + */ + + compile_h = nvstrcat(op->kernel_source_path, + "/include/linux/compile.h", NULL); + + if (access(compile_h, F_OK) == 0) { + /* compile.h exists: this is a 2.6 kernel */ + result = nvstrcat(op->kernel_source_path, "/Makefile", NULL); + } else { + /* compile.h does not exist: this is a 2.4 kernel */ + result = nvstrcat(op->kernel_source_path, + "/include/linux/modversions.h", NULL); + } + + free(compile_h); + + if (access(result, F_OK) != 0) { + ui_error(op, "The kernel header file '%s' does not exist. " + "The most likely reason for this is that the kernel " + "source files in '%s' have not been configured.", + result, op->kernel_source_path); + free(result); + return FALSE; + } + + free(result); +#endif + + /* OK, we seem to have a path to a configured kernel source tree */ + + ui_log(op, "Kernel source path: '%s'\n", op->kernel_source_path); + + return TRUE; + +} /* determine_kernel_source_path() */ + + + +/* + * link_kernel_module() - link the prebuilt kernel interface against + * the binary-only core of the kernel module. This results in a + * complete kernel module, ready for installation. + * + * + * ld -r -o nvidia.o nv-linux.o nv-kernel.o + */ + +int link_kernel_module(Options *op, Package *p) +{ + char *cmd, *result; + int ret; + + p->kernel_module_filename = guess_kernel_module_filename(op); + + cmd = nvstrcat("cd ", p->kernel_module_build_directory, + "; ", op->utils[LD], + " ", LD_OPTIONS, + " -o ", p->kernel_module_filename, + " ", PRECOMPILED_KERNEL_INTERFACE_FILENAME, + " nv-kernel.o", NULL); + + ret = run_command(op, cmd, &result, TRUE, 0, TRUE); + + free(cmd); + + if (ret != 0) { + ui_error(op, "Unable to link kernel module."); + return FALSE; + } + + ui_log(op, "Kernel module linked successfully."); + + return TRUE; + +} /* link_kernel_module() */ + + +/* + * build_kernel_module() - determine the kernel include directory, + * copy the kernel module source files into a temporary directory, and + * compile nvidia.o. + * + * XXX depends on things like make, gcc, ld, existing. Should we + * check that the user has these before doing this? + */ + +int build_kernel_module(Options *op, Package *p) +{ + char *result, *cmd, *tmp; + int len, ret; + + if (!cc_version_check(op, p)) return FALSE; + + /* + * touch all the files in the build directory to avoid make time + * skew messages + */ + + touch_directory(op, p->kernel_module_build_directory); + + + /* + * Check if conftest.sh can determine the Makefile, there's + * no hope for the make rules if this fails. + */ + cmd = nvstrcat("sh ", p->kernel_module_build_directory, + "/conftest.sh cc ", op->kernel_source_path, "/include ", + "select_makefile just_msg", NULL); + + ret = run_command(op, cmd, &result, FALSE, 0, TRUE); + nvfree(cmd); + + if (ret != 0) { + ui_error(op, result); /* display conftest.sh's error message */ + nvfree(result); + return FALSE; + } + + + if (!rivafb_check(op, p)) return FALSE; + + rivafb_module_check(op, p); + + + cmd = nvstrcat("cd ", p->kernel_module_build_directory, + "; make print-module-filename SYSSRC=", + op->kernel_source_path, NULL); + + ret = run_command(op, cmd, &p->kernel_module_filename, FALSE, 0, FALSE); + + free(cmd); + + if (ret != 0) { + ui_error(op, "Unable to determine the NVIDIA kernel module filename."); + nvfree(result); + return FALSE; + } + + ui_log(op, "Cleaning kernel module build directory."); + + len = strlen(p->kernel_module_build_directory) + 32; + cmd = nvalloc(len); + + snprintf(cmd, len, "cd %s; make clean", p->kernel_module_build_directory); + + ret = run_command(op, cmd, &result, TRUE, 0, TRUE); + free(result); + free(cmd); + + ui_status_begin(op, "Building kernel module:", "Building"); + + cmd = nvstrcat("cd ", p->kernel_module_build_directory, + "; make module SYSSRC=", op->kernel_source_path, NULL); + + ret = run_command(op, cmd, &result, TRUE, 25, TRUE); + + free(cmd); + + if (ret != 0) { + ui_status_end(op, "Error."); + ui_error(op, "Unable to build the NVIDIA kernel module."); + /* XXX need more descriptive error message */ + return FALSE; + } + + /* check that the file actually exists */ + + tmp = nvstrcat(p->kernel_module_build_directory, "/", + p->kernel_module_filename, NULL); + if (access(tmp, F_OK) == -1) { + free(tmp); + ui_status_end(op, "Error."); + ui_error(op, "The NVIDIA kernel module was not created."); + return FALSE; + } + free(tmp); + + ui_status_end(op, "done."); + + ui_log(op, "Kernel module compilation complete."); + + return TRUE; + +} /* build_kernel_module() */ + + + +/* + * build_kernel_interface() - build the kernel interface, and place it + * here: + * + * "%s/%s", p->kernel_module_build_directory, + * PRECOMPILED_KERNEL_INTERFACE_FILENAME + * + * This is done by copying the sources to a temporary working + * directory, building, and copying the kernel interface back to the + * kernel module source directory. The tmpdir is removed when + * complete. + * + * XXX this and build_kernel_module() should be merged. + */ + +int build_kernel_interface(Options *op, Package *p) +{ + char *tmpdir = NULL; + char *cmd = NULL; + char *kernel_interface = NULL; + char *dstfile = NULL; + int ret = FALSE; + int command_ret; + + /* create a temporary directory */ + + tmpdir = make_tmpdir(op); + + if (!tmpdir) { + ui_error(op, "Unable to create a temporary build directory."); + return FALSE; + } + + /* copy the kernel module sources to it */ + + ui_log(op, "Copying kernel module sources to temporary directory."); + + if (!copy_directory_contents + (op, p->kernel_module_build_directory, tmpdir)) { + ui_error(op, "Unable to copy the kernel module sources to temporary " + "directory '%s'.", tmpdir); + goto failed; + } + + /* + * touch the contents of the build directory, to avoid make time + * skew error messages + */ + + touch_directory(op, p->kernel_module_build_directory); + + /* build the kernel interface */ + + ui_status_begin(op, "Building kernel interface:", "Building"); + + cmd = nvstrcat("cd ", tmpdir, "; make ", p->kernel_interface_filename, + " SYSSRC=", op->kernel_source_path, NULL); + + command_ret = run_command(op, cmd, NULL, TRUE, 25 /* XXX */, TRUE); + + if (command_ret != 0) { + ui_status_end(op, "Error."); + ui_error(op, "Unable to build the NVIDIA kernel module interface."); + /* XXX need more descriptive error message */ + goto failed; + } + + /* check that the file exists */ + + kernel_interface = nvstrcat(tmpdir, "/", + p->kernel_interface_filename, NULL); + + if (access(kernel_interface, F_OK) == -1) { + ui_status_end(op, "Error."); + ui_error(op, "The NVIDIA kernel module interface was not created."); + goto failed; + } + + ui_status_end(op, "done."); + + ui_log(op, "Kernel module interface compilation complete."); + + /* copy the kernel interface from the tmpdir back to the srcdir */ + + dstfile = nvstrcat(p->kernel_module_build_directory, "/", + PRECOMPILED_KERNEL_INTERFACE_FILENAME, NULL); + + if (!copy_file(op, kernel_interface, dstfile, 0644)) goto failed; + + ret = TRUE; + + failed: + + remove_directory(op, tmpdir); + + if (tmpdir) nvfree(tmpdir); + if (cmd) nvfree(cmd); + if (kernel_interface) nvfree(kernel_interface); + if (dstfile) nvfree(dstfile); + + return ret; + +} /* build_kernel_interface() */ + + + +/* + * test_kernel_module() - attempt to insmod the kernel module and then + * rmmod it. Return TRUE if the insmod succeeded, or FALSE otherwise. + * + * Pass the special silence_nvidia_output option to the kernel module + * to prevent any console output while testing. + */ + +int test_kernel_module(Options *op, Package *p) +{ + char *cmd = NULL, *data; + int ret; + + /* + * If we're building/installing for a different kernel, then we + * can't test the module now. + */ + + if (op->kernel_name) return TRUE; + + cmd = nvstrcat(op->utils[INSMOD], " ", + p->kernel_module_build_directory, "/", + p->kernel_module_filename, " silence_nvidia_output=1", + NULL); + + /* only output the result of the test if in expert mode */ + + ret = run_command(op, cmd, &data, op->expert, 0, TRUE); + + if (ret != 0) { + ui_error(op, "Unable to load the kernel module '%s'. This is " + "most likely because the kernel module was built using " + "the wrong kernel source files. %s", + p->kernel_module_filename, install_your_kernel_source); + + /* + * if in expert mode, run_command() would have caused this to + * be written to the log file; so if not in expert mode, print + * the output now. + */ + + if (!op->expert) ui_log(op, "Kernel module load error: %s", data); + ret = FALSE; + } else { + free(cmd); + cmd = nvstrcat(op->utils[RMMOD], " ", p->kernel_module_name, NULL); + run_command(op, cmd, NULL, FALSE, 0, TRUE); + /* what if we fail to rmmod? */ + ret = TRUE; + } + + if (cmd) free(cmd); + if (data) free(data); + + return ret; + +} /* test_kernel_module() */ + + + +/* + * load_kernel_module() - modprobe the kernel module + */ + +int load_kernel_module(Options *op, Package *p) +{ + char *cmd, *data; + int len, ret; + + len = strlen(op->utils[MODPROBE]) + strlen(p->kernel_module_name) + 2; + + cmd = (char *) nvalloc(len); + + snprintf(cmd, len, "%s %s", op->utils[MODPROBE], p->kernel_module_name); + + ret = run_command(op, cmd, &data, FALSE, 0, TRUE); + + if (ret != 0) { + if (op->expert) { + ui_error(op, "Unable to load the kernel module: '%s'", data); + } else { + ui_error(op, "Unable to load the kernel module."); + } + ret = FALSE; + } else { + ret = TRUE; + } + + if (cmd) free(cmd); + if (data) free(data); + + return ret; + +} /* load_kernel_module() */ + + + + + + +/* + * check_kernel_module_version() - check that the driver version + * indicated in the /proc filesystem is the same as the driver version + * specified in the package description. + */ + +int check_kernel_module_version(Options *op, Package *p) +{ + int major, minor, patch; + int proc_major, proc_minor, proc_patch; + FILE *fp = 0; + char *buf; + int eof; + + fp = fopen(NVIDIA_VERSION_PROC_FILE, "r"); + buf = fget_next_line(fp, &eof); + + if (!nvid_version(buf, &proc_major, &proc_minor, &proc_patch)) { + free(buf); + return FALSE; + } + + if (!nvid_version(p->version_string, &major, &minor, &patch)) { + return FALSE; + } + + if ((proc_major != major) || + (proc_minor != minor) || + (proc_patch != patch)) { + return FALSE; + } + + return TRUE; + +} /* check_kernel_module_version() */ + + + +/* + * check_for_unloaded_kernel_module() - test if any of the "bad" + * kernel modules are loaded; if they are, then try to unload it. If + * we can't unload it, then report an error and return FALSE; + */ + +int check_for_unloaded_kernel_module(Options *op, Package *p) +{ + int n = 0; + int loaded = FALSE; + unsigned int bits = 0; + + /* + * We can skip this check if we are installing for a non-running + * kernel and only installing a kernel module. + */ + + if (op->kernel_module_only && op->kernel_name) { + ui_log(op, "Only installing a kernel module for a non-running " + "kernel; skipping the \"is an NVIDIA kernel module loaded?\" " + "test."); + return TRUE; + } + + while (p->bad_modules[n]) { + if (check_for_loaded_kernel_module(op, p->bad_modules[n])) { + loaded = TRUE; + bits |= (1 << n); + } + n++; + } + + if (!loaded) return TRUE; + + /* one or more kernel modules is loaded... try to unload them */ + + n = 0; + while (p->bad_modules[n]) { + if (!(bits & (1 << n))) { + n++; + continue; + } + + rmmod_kernel_module(op, p->bad_modules[n]); + + /* check again */ + + if (check_for_loaded_kernel_module(op, p->bad_modules[n])) { + ui_error(op, "An NVIDIA kernel module '%s' appears to already " + "be loaded in your kernel. This may be because it is " + "in use (for example, by the X server). Please be " + "sure you have exited X before attempting to upgrade " + "your driver. If you have exited X but still receive " + "this message, then an error has occured that has " + "confused the usage count of the kernel module; the " + "simplest remedy is to reboot your computer.", + p->bad_modules[n]); + + return FALSE; + } + n++; + } + + return TRUE; + +} /* check_for_unloaded_kernel_module() */ + + + + + +/* + * find_precompiled_kernel_interface() - do assorted black magic to + * determine if the given package contains a precompiled kernel interface + * for the kernel on this system. + * + * XXX it would be nice to extend this so that a kernel module could + * be installed for a kernel other than the currently running one. + */ + +int find_precompiled_kernel_interface(Options *op, Package *p) +{ + char *proc_version_string, *output_filename, *tmp; + PrecompiledInfo *info = NULL; + + /* allow the user to completely skip this search */ + + if (op->no_precompiled_interface) { + ui_log(op, "Not probing for precompiled kernel interfaces."); + return FALSE; + } + + /* retrieve the proc version string for the running kernel */ + + proc_version_string = read_proc_version(op); + + if (!proc_version_string) goto failed; + + /* make sure the target directory exists */ + + if (!mkdir_recursive(op, p->kernel_module_build_directory, 0755)) + goto failed; + + /* build the output filename */ + + output_filename = nvstrcat(p->kernel_module_build_directory, "/", + PRECOMPILED_KERNEL_INTERFACE_FILENAME, NULL); + + /* + * if the --precompiled-kernel-interfaces-path option was + * specified, search that directory, first + */ + + if (op->precompiled_kernel_interfaces_path) { + info = scan_dir(op, p, op->precompiled_kernel_interfaces_path, + output_filename, proc_version_string); + } + + /* + * If we didn't find a match, search for distro-provided + * precompiled kernel interfaces + */ + + if (!info) { + tmp = build_distro_precompiled_kernel_interface_dir(op); + if (tmp) { + info = scan_dir(op, p, tmp, output_filename, proc_version_string); + nvfree(tmp); + } + } + + /* + * if we still haven't found a match, search in + * p->precompiled_kernel_interface_directory (the directory + * containing the precompiled kernel interfaces shipped with the + * package) + */ + + if (!info) { + info = scan_dir(op, p, p->precompiled_kernel_interface_directory, + output_filename, proc_version_string); + } + + /* + * If we didn't find a matching precompiled kernel interface, ask + * if we should try to download one. + */ + + if (!info && !op->no_network) { + if (ui_yes_no(op, TRUE, "No precompiled kernel interface was " + "found to match " + "your kernel; would you like the installer to attempt " + "to download a kernel interface for your kernel from " + "the NVIDIA ftp site (%s)?", op->ftp_site)) { + + info = download_updated_kernel_interface(op, p, + proc_version_string); + if (!info) { + ui_message(op, "No matching precompiled kernel interface was " + "found on the NVIDIA ftp site; this means that the " + "installer will need to compile a kernel interface " + "for your kernel."); + return FALSE; + } + } + } + + /* If we found one, ask expert users if they really want to use it */ + + if (info && op->expert) { + if (!ui_yes_no(op, TRUE, "A precompiled kernel interface for the " + "kernel '%s' has been found. Would you like to " + "use this? (answering 'no' will require the " + "installer to compile the interface)", + info->description)) { + /* XXX free info */ + info = NULL; + } + } + + if (info) { + /* XXX free info */ + return TRUE; + } + + failed: + + ui_message(op, "No precompiled kernel interface was found to match " + "your kernel; this means that the installer will need to " + "compile a new kernel interface."); + + return FALSE; + +} /* find_precompiled_kernel_interface() */ + + + +/* + * get_kernel_name() - get the kernel name: this is either what + * the user specified via the --kernel-name option, or `name -r`. + */ + +char __kernel_name[256]; + +char *get_kernel_name(Options *op) +{ + struct utsname uname_buf; + + if (op->kernel_name) { + return op->kernel_name; + } else { + if (uname(&uname_buf) == -1) { + ui_warn(op, "Unable to determine kernel version (%s).", + strerror(errno)); + return NULL; + } else { + strncpy(__kernel_name, uname_buf.release, 256); + return __kernel_name; + } + } +} /* get_kernel_name() */ + + + +/* + *************************************************************************** + * local static routines + *************************************************************************** + */ + + + +/* + * default_kernel_module_installation_path() - do the equivalent of: + * + * SYSSRC = /lib/modules/$(shell uname -r) + * + * ifeq ($(shell if test -d $(SYSSRC)/kernel; then echo yes; fi),yes) + * INSTALLDIR = $(SYSSRC)/kernel/drivers/video + * else + * INSTALLDIR = $(SYSSRC)/video + * endif + */ + +static char *default_kernel_module_installation_path(Options *op) +{ + char *str, *tmp; + + tmp = get_kernel_name(op); + if (!tmp) return NULL; + + str = nvstrcat("/lib/modules/", tmp, "/kernel", NULL); + + if (directory_exists(op, str)) { + free(str); + str = nvstrcat("/lib/modules/", tmp, "/kernel/drivers/video", NULL); + return str; + } + + free(str); + + str = nvstrcat("/lib/modules/", tmp, "/video", NULL); + + return str; + +} /* default_kernel_module_installation_path() */ + + + +/* + * default_kernel_source_path() - determine the default kernel + * source path, if possible. Return NULL if no default kernel path + * is found. + * + * Here is the logic: + * + * if --kernel-source-path was set, use that + * + * if --kernel-include-path was set, use that (converting it to the + * source path); also print a warning that --kernel-include-path is + * deprecated. + * + * else if SYSSRC is set, use that + * + * else if /lib/modules/`uname -r`/build exists use that + * + * else if /usr/src/linux exists use that + * + * else return NULL + * + * One thing to note is that for the first two methods + * (--kernel-source-path and $SYSSRC) we don't check for directory + * existence before returning. This is intentional: if the user set + * one of these, then they're trying to set a particular path. If + * that directory doesn't exist, then better to abort installation with + * an appropriate error message in determine_kernel_source_path(). + * Whereas, for the later two (/lib/modules/`uname -r`/build + * and /usr/src/linux), these are not explicitly requested by + * the user, so it makes sense to only use them if they exist. + */ + +static char *default_kernel_source_path(Options *op) +{ + char *str, *tmp; + + str = tmp = NULL; + + /* check --kernel-source-path */ + + if (op->kernel_source_path) { + ui_log(op, "Using the kernel source path '%s' as specified by the " + "'--kernel-source-path' commandline option.", + op->kernel_source_path); + return op->kernel_source_path; + } + + /* check --kernel-include-path */ + + if (op->kernel_include_path) { + ui_warn(op, "The \"--kernel-include-path\" option is deprecated " + "(as part of reorganization to support Linux 2.6); please use " + "\"--kernel-source-path\" instead."); + str = convert_include_path_to_source_path(op->kernel_include_path); + ui_log(op, "Using the kernel source path '%s' (inferred from the " + "'--kernel-include-path' commandline option '%s').", + str, op->kernel_include_path); + return str; + } + + /* check SYSSRC */ + + str = getenv("SYSSRC"); + if (str) { + ui_log(op, "Using the kernel source path '%s', as specified by the " + "SYSSRC environment variable.", str); + return str; + } + + /* check /lib/modules/`uname -r`/build and /usr/src/linux-`uname -r` */ + + tmp = get_kernel_name(op); + + if (tmp) { + str = nvstrcat("/lib/modules/", tmp, "/build", NULL); + + if (directory_exists(op, str)) { + return str; + } + + nvfree(str); + + /* + * check "/usr/src/linux-`uname -r`", too; patch suggested by + * Peter Berg Larsen <pebl@math.ku.dk> + */ + + str = nvstrcat("/usr/src/linux-", tmp, NULL); + if (directory_exists(op, str)) { + return str; + } + + free(str); + } + + /* finally, try /usr/src/linux */ + + if (directory_exists(op, "/usr/src/linux")) { + return "/usr/src/linux"; + } + + return NULL; + +} /* default_kernel_source_path() */ + + +/* + * check_for_loaded_kernel_module() - check if the specified kernel + * module is currently loaded using `lsmod`. Returns TRUE if the + * kernel module is loaded; FALSE if it is not. + * + * Be sure to check that the character following the kernel module + * name is a space (to avoid getting false positivies when the given + * kernel module name is contained within another kernel module name. + */ + +static int check_for_loaded_kernel_module(Options *op, const char *module_name) +{ + char *ptr, *result = NULL; + int ret; + + ret = run_command(op, op->utils[LSMOD], &result, FALSE, 0, TRUE); + + if ((ret == 0) && (result) && (result[0] != '\0')) { + ptr = strstr(result, module_name); + if (ptr) { + ptr += strlen(module_name); + if(!isspace(*ptr)) ret = 1; + } else { + ret = 1; + } + } + + if (result) free(result); + + return ret ? FALSE : TRUE; + +} /* check_for_loaded_kernel_module() */ + + +/* + * rmmod_kernel_module() - run `rmmod nvidia` + */ + +static int rmmod_kernel_module(Options *op, const char *module_name) +{ + int len, ret; + char *cmd; + + len = strlen(op->utils[RMMOD]) + strlen(module_name) + 2; + + cmd = (char *) nvalloc(len); + + snprintf(cmd, len, "%s %s", op->utils[RMMOD], module_name); + + ret = run_command(op, cmd, NULL, FALSE, 0, TRUE); + + free(cmd); + + return ret ? FALSE : TRUE; + +} /* rmmod_kernel_module() */ + + + +/* + * get_updated_kernel_interfaces() - + */ + +static PrecompiledInfo * +download_updated_kernel_interface(Options *op, Package *p, + const char *proc_version_string) +{ + int fd = -1; + int dst_fd = -1; + char *url = NULL; + char *tmpfile = NULL; + char *dstfile = NULL; + char *buf = NULL; + char *output_filename = NULL; + char *str = (void *) -1; + char *ptr, *s; + struct stat stat_buf; + PrecompiledInfo *info = NULL; + uint32 crc; + + /* initialize the tmpfile and url strings */ + + tmpfile = nvstrcat(op->tmpdir, "/nv-updates-XXXXXX", NULL); + url = nvstrcat(op->ftp_site, "/XFree86/", INSTALLER_OS, "-", + INSTALLER_ARCH, "/", p->version_string, + "/updates/updates.txt", NULL); + + /* + * create a temporary file in which to write the list of available + * updates + */ + + if ((fd = mkstemp(tmpfile)) == -1) { + ui_error(op, "Unable to create temporary file (%s)", strerror(errno)); + goto done; + } + + /* download the updates list */ + + if (!snarf(op, url, fd, SNARF_FLAGS_DOWNLOAD_SILENT)) goto done; + + /* get the length of the file */ + + if (fstat(fd, &stat_buf) == -1) goto done; + + /* map the file into memory for easier reading */ + + str = mmap(0, stat_buf.st_size, PROT_READ, MAP_FILE | MAP_SHARED, fd, 0); + if (str == (void *) -1) goto done; + + /* + * loop over each line of the updates file: each line should be of + * the format: "[filename]:::[proc version string]" + */ + + ptr = str; + + while (TRUE) { + buf = get_next_line(ptr, &ptr); + if ((!buf) || (buf[0] == '\0')) goto done; + + s = strstr(buf, ":::"); + if (!s) { + ui_error(op, "Invalid updates.txt list."); + goto done; + } + + s += 3; /* skip past the ":::" separator */ + + if (strcmp(proc_version_string, s) == 0) { + + /* proc versions strings match */ + + /* + * terminate the string at the start of the ":::" + * separator so that buf is the filename + */ + + s -= 3; + *s = '\0'; + + /* build the new url and dstfile strings */ + + nvfree(url); + url = nvstrcat(op->ftp_site, "/XFree86/", + INSTALLER_OS, "-", INSTALLER_ARCH, "/", + p->version_string, "/updates/", buf, NULL); + + dstfile = nvstrcat(p->precompiled_kernel_interface_directory, + "/", buf, NULL); + + /* create dstfile */ + + dst_fd = creat(dstfile, S_IRUSR | S_IWUSR); + if (dst_fd == -1) { + ui_error(op, "Unable to create file '%s' (%s).", + dstfile, strerror(errno)); + goto done; + } + + /* download the file */ + + if (!snarf(op, url, dst_fd, SNARF_FLAGS_STATUS_BAR)) goto done; + + close(dst_fd); + dst_fd = -1; + + /* XXX once we have gpg setup, should check the file here */ + + /* build the output filename string */ + + output_filename = nvstrcat(p->kernel_module_build_directory, "/", + PRECOMPILED_KERNEL_INTERFACE_FILENAME, + NULL); + + /* unpack the downloaded file */ + + info = precompiled_unpack(op, dstfile, output_filename, + proc_version_string, + p->major, p->minor, p->patch); + + /* compare checksums */ + + crc = compute_crc(op, output_filename); + + if (info && (info->crc != crc)) { + ui_error(op, "The embedded checksum of the downloaded file " + "'%s' (%d) does not match the computed checksum ", + "(%d); not using.", buf, info->crc, crc); + unlink(dstfile); + /* XXX free info */ + info = NULL; + } + + goto done; + } + + nvfree(buf); + } + + + done: + + if (dstfile) nvfree(dstfile); + if (buf) nvfree(buf); + if (str != (void *) -1) munmap(str, stat_buf.st_size); + if (dst_fd > 0) close(dst_fd); + if (fd > 0) close(fd); + + unlink(tmpfile); + if (tmpfile) nvfree(tmpfile); + if (url) nvfree(url); + + return info; + +} /* get_updated_kernel_interfaces() */ + + + +/* + * cc_version_check() - + */ + +static int cc_version_check(Options *op, Package *p) +{ + char *cmd, *CC, *result; + int ret; + + /* + * If we're building/installing for a different kernel, then we + * can't do the gcc version check (we don't have a /proc/version + * string from which to get the kernel's gcc version). + */ + + if (op->kernel_name) { + setenv("IGNORE_CC_MISMATCH", "1", 1); + return TRUE; + } + + + CC = getenv("CC"); + if (!CC) CC = "cc"; + + ui_log(op, "Performing cc_version_check with CC=\"%s\".", CC); + + cmd = nvstrcat("sh ", p->kernel_module_build_directory, + "/conftest.sh ", CC, " ", op->kernel_source_path, + "/include ", "cc_sanity_check just_msg", NULL); + + ret = run_command(op, cmd, &result, FALSE, 0, TRUE); + + nvfree(cmd); + + if (ret == 0) return TRUE; + + ret = ui_yes_no(op, TRUE, "gcc-version-check failed:\n\n%s\n\n" + "If you know what you are doing and want to " + "ignore the gcc version check, select \"No\" to " + "continue installation. Otherwise, select \"Yes\" to " + "abort installation, set the CC environment variable to " + "the name of the compiler used to compile your kernel, " + "and restart installation. Abort now?", result); + + nvfree(result); + + if (!ret) setenv("IGNORE_CC_MISMATCH", "1", 1); + + return !ret; + +} /* cc_version_check() */ + + + +/* + * rivafb_check() - run the rivafb_sanity_check conftest; if the test + * fails, print the error message from the test and abort driver + * installation. + */ + +static int rivafb_check(Options *op, Package *p) +{ + char *cmd, *result; + int ret; + + ui_log(op, "Performing rivafb check."); + + cmd = nvstrcat("sh ", p->kernel_module_build_directory, + "/conftest.sh cc ", op->kernel_source_path, "/include ", + "rivafb_sanity_check just_msg", NULL); + + ret = run_command(op, cmd, &result, FALSE, 0, TRUE); + + nvfree(cmd); + + if (ret == 0) return TRUE; + + ui_error(op, result); + + nvfree(result); + + return FALSE; + +} /* rivafb_check() */ + + + +/* + * rivafb_module_check() - run the rivafb_module_sanity_check + * conftest; if the test prints anything, print the warning text + * outputted by the test. + */ + +static void rivafb_module_check(Options *op, Package *p) +{ + char *cmd, *result; + int ret; + + ui_log(op, "Performing rivafb module check."); + + cmd = nvstrcat("sh ", p->kernel_module_build_directory, + "/conftest.sh cc ", op->kernel_source_path, "/include ", + "rivafb_module_sanity_check just_msg", NULL); + + ret = run_command(op, cmd, &result, FALSE, 0, TRUE); + + nvfree(cmd); + + if (result && result[0]) { + ui_warn(op, result); + } + + nvfree(result); + +} /* rivafb_module_check() */ + + + +/* + * scan_dir() - scan through the specified directory for a matching + * precompiled kernel interface. + */ + +static PrecompiledInfo *scan_dir(Options *op, Package *p, + const char *directory_name, + const char *output_filename, + const char *proc_version_string) +{ + DIR *dir; + struct dirent *ent; + PrecompiledInfo *info = NULL; + char *filename; + + if (!directory_name) return NULL; + + dir = opendir(directory_name); + if (!dir) return NULL; + + /* + * loop over all contents of the directory, looking for a + * precompiled kernel interface that matches the running kernel + */ + + while ((ent = readdir(dir)) != NULL) { + + if (((strcmp(ent->d_name, ".")) == 0) || + ((strcmp(ent->d_name, "..")) == 0)) continue; + + filename = nvstrcat(directory_name, "/", ent->d_name, NULL); + + info = precompiled_unpack(op, filename, output_filename, + proc_version_string, + p->major, p->minor, p->patch); + + if (info) break; + + free(filename); + filename = NULL; + } + + if (closedir(dir) != 0) { + ui_error(op, "Failure while closing directory '%s' (%s).", + directory_name, + strerror(errno)); + } + + return info; + +} /* scan_dir() */ + + + +/* + * build_distro_precompiled_kernel_interface_dir() - construct this + * path: + * + * /lib/modules/precompiled/`uname -r`/nvidia/gfx/ + */ + +static char *build_distro_precompiled_kernel_interface_dir(Options *op) +{ + struct utsname uname_buf; + char *str; + + if (uname(&uname_buf) == -1) { + ui_error(op, "Unable to determine kernel version (%s)", + strerror(errno)); + return NULL; + } + + str = nvstrcat("/lib/modules/precompiled/", uname_buf.release, + "/nvidia/gfx/", NULL); + + return str; + +} /* build_distro_precompiled_kernel_interface_dir() */ + + + +/* + * convert_include_path_to_source_path() - given input to + * "--kernel-include-path", convert it to "--kernel-source-path" by + * scanning from the end to the previous "/". + */ + +static char *convert_include_path_to_source_path(const char *inc) +{ + char *c, *str; + + str = nvstrdup(inc); + + /* go to the end of the string */ + + for (c = str; *c; c++); + + /* move to the last printable character */ + + c--; + + /* if the string ends in '/'; backup one more */ + + if (*c == '/') c--; + + /* now back up to the next '/' */ + + while ((c >= str) && (*c != '/')) c--; + + if (*c == '/') *c = '\0'; + + return str; + +} /* convert_include_path_to_source_path() */ + + + +/* + * guess_kernel_module_filename() - parse uname to decide if the + * kernel module filename is "nvidia.o" or "nvidia.ko". + */ + +static char *guess_kernel_module_filename(Options *op) +{ + struct utsname uname_buf; + char *tmp, *str, *dot0, *dot1; + int major, minor; + + if (op->kernel_name) { + str = op->kernel_name; + } else { + if (uname(&uname_buf) == -1) { + ui_error (op, "Unable to determine kernel version (%s)", + strerror (errno)); + return NULL; + } + str = uname_buf.release; + } + + tmp = nvstrdup(str); + + dot0 = strchr(tmp, '.'); + if (!dot0) goto fail; + + *dot0 = '\0'; + + major = atoi(tmp); + + dot0++; + dot1 = strchr(dot0, '.'); + if (!dot1) goto fail; + + *dot1 = '\0'; + + minor = atoi(dot0); + + if ((major > 2) || ((major == 2) && (minor > 4))) { + return "nvidia.ko"; + } else { + return "nvidia.o"; + } + + fail: + ui_error (op, "Unable to determine if kernel is 2.6.0 or greater from " + "uname string '%s'; assuming the kernel module filename is " + "'nvidia.o'.", str); + return "nvidia.o"; + +} /* guess_kernel_module_filename() */ |