diff --git a/core/includes/module.inc b/core/includes/module.inc
index 49a38db..941adcf 100644
--- a/core/includes/module.inc
+++ b/core/includes/module.inc
@@ -6,7 +6,7 @@
  */
 
 /**
- * Load all the modules that have been enabled in the system table.
+ * Load all enabled or hibernated modules.
  *
  * @param $bootstrap
  *   Whether to load only the reduced set of modules loaded in "bootstrap mode"
@@ -20,7 +20,7 @@ function module_load_all($bootstrap = FALSE) {
   static $has_run = FALSE;
 
   if (isset($bootstrap)) {
-    $type = $bootstrap ? 'bootstrap' : 'module_enabled';
+    $type = $bootstrap ? 'bootstrap' : 'module_load';
     foreach (module_list($type) as $module) {
       drupal_load('module', $module);
     }
@@ -35,13 +35,14 @@ function module_load_all($bootstrap = FALSE) {
  * Returns a list of currently active modules.
  *
  * Acts as a wrapper around system_list(), returning either a list of all
- * enabled modules, or just modules needed for bootstrap.
+ * loadable modules, all enabled modules, or just modules needed for bootstrap.
  * An optional parameter allows an alternative module list to be provided,
  * which is then stored and used in subsequent calls instead of the one
  * provided by system_list().
  *
  * @param $type
  *   The type of list to return:
+ *   - module_load: All loadable modules (enabled or hibernated).
  *   - module_enabled: All enabled modules.
  *   - bootstrap: All enabled modules required for bootstrap.
  * @param $fixed_list
@@ -84,19 +85,20 @@ function module_list($type = 'module_enabled', $fixed_list = NULL) {
 }
 
 /**
- * Build a list of bootstrap modules and enabled modules and themes.
+ * Build a list of modules and themes.
  *
  * @param $type
  *   The type of list to return:
- *   - module_enabled: All enabled modules.
  *   - bootstrap: All enabled modules required for bootstrap.
+ *   - module_enabled: All enabled modules.
+ *   - module_load: All loadable modules (enabled or hibernated).
  *   - theme: All themes.
  *
  * @return
  *   An associative array of modules or themes, keyed by name. For $type
- *   'bootstrap', the array values equal the keys. For $type 'module_enabled'
- *   or 'theme', the array values are objects representing the respective
- *   database row, with the 'info' property already unserialized.
+ *   'bootstrap', the array values equal the keys. For $type 'module_enabled',
+ *   'module_load', or 'theme', the array values are objects representing the
+ *   respective database row, with the 'info' property already unserialized.
  *
  * @see module_list()
  * @see list_themes()
@@ -137,20 +139,26 @@ function system_list($type) {
     else {
       $lists = array(
         'module_enabled' => array(),
+        'module_load' => array(),
         'theme' => array(),
         'filepaths' => array(),
       );
       // The module name (rather than the filename) is used as the fallback
       // weighting in order to guarantee consistent behavior across different
       // Drupal installations, which might have modules installed in different
-      // locations in the file system. The ordering here must also be
-      // consistent with the one used in module_implements().
-      $result = db_query("SELECT * FROM {system} WHERE type = 'theme' OR (type = 'module' AND status = 1) ORDER BY weight ASC, name ASC");
+      // locations in the file system.
+      $result = db_query("SELECT * FROM {system} WHERE type = 'theme' OR type = 'module' ORDER BY weight ASC, name ASC");
       foreach ($result as $record) {
         $record->info = unserialize($record->info);
-        // Build a list of all enabled modules.
         if ($record->type == 'module') {
-          $lists['module_enabled'][$record->name] = $record;
+          // Build a list of all enabled modules.
+          if ($record->status == 1) {
+            $lists['module_enabled'][$record->name] = $record;
+          }
+          // Build a list of loadable modules.
+          if ($record->status == 1 || $record->status == 2) {
+            $lists['module_load'][$record->name] = $record;
+          }
         }
         // Build a list of themes.
         if ($record->type == 'theme') {
@@ -314,11 +322,11 @@ function module_load_include($type, $module, $name = NULL) {
 }
 
 /**
- * Load an include file for each of the modules that have been enabled in
- * the system table.
+ * Load an include file for each of the loadable (enabled or hibernated)
+ * modules in the system table.
  */
 function module_load_all_includes($type, $name = NULL) {
-  $modules = module_list();
+  $modules = module_list('module_load');
   foreach ($modules as $module) {
     module_load_include($type, $module, $name);
   }
@@ -409,7 +417,7 @@ function module_enable($module_list, $enable_dependencies = TRUE) {
       ':type' => 'module',
       ':name' => $module))
       ->fetchObject();
-    if ($existing->status == 0) {
+    if ($existing->status == 0 || $existing->status == 2) {
       // Load the module's code.
       drupal_load('module', $module);
       module_load_install($module);
@@ -432,11 +440,11 @@ function module_enable($module_list, $enable_dependencies = TRUE) {
       // Refresh the schema to include it.
       drupal_get_schema(NULL, TRUE);
 
-      // Allow modules to react prior to the installation of a module.
-      module_invoke_all('modules_preinstall', array($module));
-
       // Now install the module if necessary.
       if (drupal_get_installed_schema_version($module, TRUE) == SCHEMA_UNINSTALLED) {
+        // Allow modules to react prior to the installation of a module.
+        module_invoke_all('modules_preinstall', array($module));
+
         drupal_install_schema($module);
 
         // Set the schema version to the number of the last update provided
@@ -532,8 +540,9 @@ function module_disable($module_list, $disable_dependents = TRUE) {
     if (module_exists($module)) {
       module_load_install($module);
       module_invoke($module, 'disable');
+      $new_status = _module_hibernate($module) ? 2 : 0;
       db_update('system')
-        ->fields(array('status' => 0))
+        ->fields(array('status' => $new_status))
         ->condition('type', 'module')
         ->condition('name', $module)
         ->execute();
@@ -556,6 +565,36 @@ function module_disable($module_list, $disable_dependents = TRUE) {
 }
 
 /**
+ * Checks whether the given module should be hibernated, instead of disabled.
+ *
+ * If a module implements at least one essential hook, then it can't be
+ * disabled, and needs to be hibernated instead, to ensure that the essential
+ * hook can still be invoked.
+ *
+ * @param $module
+ *   The name of the module (without the .module extension).
+ *
+ * @return
+ *  TRUE if the module should be hibernated, FALSE otherwise.
+ */
+function _module_hibernate($module) {
+  // Certain hooks should be invoked in hibernated modules, but shouldn't
+  // trigger hibernation themselves.
+  // @todo Make this generic? Another key in hook_hook_info()?
+  $ignore = array(
+    'schema',
+  );
+
+  $hook_info = module_hook_info();
+  foreach ($hook_info as $hook => $info) {
+    if (!empty($info['essential']) && !in_array($hook, $ignore) && module_hook($module, $hook)) {
+      return TRUE;
+    }
+  }
+  return FALSE;
+}
+
+/**
  * @defgroup hooks Hooks
  * @{
  * Allow modules to interact with the Drupal core.
@@ -591,17 +630,32 @@ function module_disable($module_list, $disable_dependents = TRUE) {
  *   The name of the hook (e.g. "help" or "menu").
  *
  * @return
- *   TRUE if the module is both installed and enabled, and the hook is
+ *   TRUE if the module is loadable (enabled or hibernated), and the hook is
  *   implemented in that module.
  */
 function module_hook($module, $hook) {
   $function = $module . '_' . $hook;
+  $hook_info = module_hook_info();
+  // system_list() can't be used because this function might be invoked
+  // during installation.
+  $loadable_modules = module_list('module_load');
+  $enabled_modules = module_list('module_enabled');
+  $hibernated_modules = array_diff($loadable_modules, $enabled_modules);
+  // The module is not loadable, abort.
+  if (empty($loadable_modules[$module])) {
+    return FALSE;
+  }
+  // The module is hibernated, but the hook is not in the list of essential
+  // hooks that must always run, abort.
+  if (in_array($module, $hibernated_modules) && empty($hook_info[$hook]['essential'])) {
+    return FALSE;
+  }
+
   if (function_exists($function)) {
     return TRUE;
   }
   // If the hook implementation does not exist, check whether it may live in an
   // optional include file registered via hook_hook_info().
-  $hook_info = module_hook_info();
   if (isset($hook_info[$hook]['group'])) {
     module_load_include('inc', $module, $module . '.' . $hook_info[$hook]['group']);
     if (function_exists($function)) {
@@ -647,13 +701,19 @@ function module_implements($hook) {
     $implementations['#write_cache'] = TRUE;
     $hook_info = module_hook_info();
     $implementations[$hook] = array();
-    $list = module_list();
-    foreach ($list as $module) {
+    $loadable_modules = module_list('module_load');
+    $enabled_modules = module_list('module_enabled');
+    $hibernated_modules = array_diff($loadable_modules, $enabled_modules);
+    foreach ($loadable_modules as $module) {
       $include_file = isset($hook_info[$hook]['group']) && module_load_include('inc', $module, $module . '.' . $hook_info[$hook]['group']);
       // Since module_hook() may needlessly try to load the include file again,
       // function_exists() is used directly here.
       if (function_exists($module . '_' . $hook)) {
-        $implementations[$hook][$module] = $include_file ? $hook_info[$hook]['group'] : FALSE;
+        // The module must be enabled, or the hook must be in the list of
+        // essential hooks.
+        if (!in_array($module, $hibernated_modules) || !empty($hook_info[$hook]['essential'])) {
+          $implementations[$hook][$module] = $include_file ? $hook_info[$hook]['group'] : FALSE;
+        }
       }
     }
     // Allow modules to change the weight of specific implementations but avoid
@@ -792,7 +852,11 @@ function module_invoke($module, $hook) {
 }
 
 /**
- * Invoke a hook in all enabled modules that implement it.
+ * Invoke a hook in all loadable modules that implement it.
+ *
+ * In addition to being invoked in enabled modules, the hook is also invoked
+ * in hibernated modules, if it's declared as "essential" in a hook_hook_info()
+ * implementation.
  *
  * @param $hook
  *   The name of the hook to invoke.
diff --git a/core/includes/registry.inc b/core/includes/registry.inc
index 7ac2960..e59540a 100644
--- a/core/includes/registry.inc
+++ b/core/includes/registry.inc
@@ -40,8 +40,8 @@ function _registry_update() {
     // Store the module directory for use in hook_registry_files_alter().
     $module->dir = $dir;
 
-    if ($module->status) {
-      // Add files for enabled modules to the registry.
+    if ($module->status == 1 || $module->status == 2) {
+      // Add files for loadable modules to the registry.
       foreach ($module->info['files'] as $file) {
         $files["$dir/$file"] = array('module' => $module->name, 'weight' => $module->weight);
       }
diff --git a/core/modules/entity/entity.module b/core/modules/entity/entity.module
index fe9a6e5..ae93f5d 100644
--- a/core/modules/entity/entity.module
+++ b/core/modules/entity/entity.module
@@ -19,16 +19,39 @@ function entity_help($path, $arg) {
 }
 
 /**
- * Implements hook_modules_preenable().
+ * Implements hook_hook_info().
  */
-function entity_modules_preenable() {
-  entity_info_cache_clear();
+function entity_hook_info() {
+  $essential_hooks = array(
+    'entity_info',
+    'entity_info_alter',
+    'entity_load',
+    'entity_presave',
+    'entity_insert',
+    'entity_update',
+    'entity_predelete',
+    'entity_delete',
+  );
+  // Add entity-type specific hooks.
+  $info = entity_get_info();
+  foreach ($info as $entity_type => $entity_info) {
+    $essential_hooks[] = $entity_type . '_load';
+    $essential_hooks[] = $entity_type . '_presave';
+    $essential_hooks[] = $entity_type . '_insert';
+    $essential_hooks[] = $entity_type . '_update';
+    $essential_hooks[] = $entity_type . '_predelete';
+    $essential_hooks[] = $entity_type . '_delete';
+  }
+  $hooks = array_fill_keys($essential_hooks, array(
+    'essential' => TRUE,
+  ));
+  return $hooks;
 }
 
 /**
- * Implements hook_modules_disabled().
+ * Implements hook_modules_preenable().
  */
-function entity_modules_disabled() {
+function entity_modules_preenable() {
   entity_info_cache_clear();
 }
 
diff --git a/core/modules/field/field.module b/core/modules/field/field.module
index 1dbdc61..5422fdc 100644
--- a/core/modules/field/field.module
+++ b/core/modules/field/field.module
@@ -313,6 +313,55 @@ function field_help($path, $arg) {
 }
 
 /**
+ * Implements hook_hook_info().
+ */
+function field_hook_info() {
+  $essential_hooks = array(
+    'field_attach_create_bundle',
+    'field_attach_delete',
+    'field_attach_delete_bundle',
+    'field_attach_delete_revision',
+    'field_attach_form',
+    'field_attach_insert',
+    'field_attach_load',
+    'field_attach_presave',
+    'field_attach_purge',
+    'field_attach_rename_bundle',
+    'field_attach_submit',
+    'field_attach_update',
+    'field_attach_validate',
+    'field_info',
+    'field_info_alter',
+    'field_info_max_weight',
+    'field_schema',
+    'field_storage_create_field',
+    'field_storage_delete',
+    'field_storage_delete_field',
+    'field_storage_delete_instance',
+    'field_storage_delete_revision',
+    'field_storage_details',
+    'field_storage_details_alter',
+    'field_storage_info',
+    'field_storage_info_alter',
+    'field_storage_load',
+    'field_storage_pre_insert',
+    'field_storage_pre_load',
+    'field_storage_pre_update',
+    'field_storage_purge',
+    'field_storage_purge_field',
+    'field_storage_purge_field_instance',
+    'field_storage_query',
+    'field_storage_update_field',
+    'field_storage_write',
+    'field_update_forbid',
+  );
+  $hooks = array_fill_keys($essential_hooks, array(
+    'essential' => TRUE,
+  ));
+  return $hooks;
+}
+
+/**
  * Implements hook_theme().
  */
 function field_theme() {
diff --git a/core/modules/system/system.admin.inc b/core/modules/system/system.admin.inc
index a6594da..63e9aed 100644
--- a/core/modules/system/system.admin.inc
+++ b/core/modules/system/system.admin.inc
@@ -799,7 +799,7 @@ function system_modules($form, $form_state = array()) {
   // Iterate through each of the modules.
   foreach ($visible_files as $filename => $module) {
     $extra = array();
-    $extra['enabled'] = (bool) $module->status;
+    $extra['enabled'] = ($module->status == 1);
     if (!empty($module->info['required'] )) {
       $extra['disabled'] = TRUE;
       $extra['required_by'][] = $distribution_name . (!empty($module->info['explanation']) ? ' ('. $module->info['explanation'] .')' : '');
@@ -839,7 +839,7 @@ function system_modules($form, $form_state = array()) {
       }
     }
     // Generate link for module's help page, if there is one.
-    if ($help_arg && $module->status && in_array($filename, module_implements('help'))) {
+    if ($help_arg && $module->status == 1 && in_array($filename, module_implements('help'))) {
       if (module_invoke($filename, 'help', "admin/help#$filename", $help_arg)) {
         $extra['links']['help'] = array(
           '#type' => 'link',
@@ -850,7 +850,7 @@ function system_modules($form, $form_state = array()) {
       }
     }
     // Generate link for module's permission, if the user has access to it.
-    if ($module->status && user_access('administer permissions') && in_array($filename, module_implements('permission'))) {
+    if ($module->status == 1 && user_access('administer permissions') && in_array($filename, module_implements('permission'))) {
       $extra['links']['permissions'] = array(
         '#type' => 'link',
         '#title' => t('Permissions'),
@@ -860,7 +860,7 @@ function system_modules($form, $form_state = array()) {
     }
     // Generate link for module's configuration page, if the module provides
     // one.
-    if ($module->status && isset($module->info['configure'])) {
+    if ($module->status == 1 && isset($module->info['configure'])) {
       $configure_link = menu_get_item($module->info['configure']);
       if ($configure_link['access']) {
         $extra['links']['configure'] = array(
@@ -1232,7 +1232,7 @@ function system_modules_uninstall($form, $form_state = NULL) {
   $all_modules = system_rebuild_module_data();
   $disabled_modules = array();
   foreach ($all_modules as $name => $module) {
-    if (empty($module->status) && $module->schema_version > SCHEMA_UNINSTALLED) {
+    if (in_array($module->status, array(0, 2))  && $module->schema_version > SCHEMA_UNINSTALLED) {
       $disabled_modules[$name] = $module;
     }
   }
diff --git a/core/modules/system/system.api.php b/core/modules/system/system.api.php
index 00c75c7..956714a 100644
--- a/core/modules/system/system.api.php
+++ b/core/modules/system/system.api.php
@@ -14,18 +14,26 @@
  * Defines one or more hooks that are exposed by a module.
  *
  * Normally hooks do not need to be explicitly defined. However, by declaring a
- * hook explicitly, a module may define a "group" for it. Modules that implement
- * a hook may then place their implementation in either $module.module or in
- * $module.$group.inc. If the hook is located in $module.$group.inc, then that
- * file will be automatically loaded when needed.
- * In general, hooks that are rarely invoked and/or are very large should be
- * placed in a separate include file, while hooks that are very short or very
- * frequently called should be left in the main module file so that they are
- * always available.
+ * hook explicitly, a module may define additional information for it.
+ * Supported keys:
+ * - essential: Marks a hook as needed for maintaining data integrity.
+ *   Modules implementing at least one essential hook are hibernated instead of
+ *   disabled, which means that they still get loaded in order for those hooks
+ *   to get invoked.
+ * - group: Modules that implement a hook may then place their implementation
+ *   in either $module.module or in $module.$group.inc.
+ *   If the hook is located in $module.$group.inc, then that
+ *   file will be automatically loaded when needed.
+ *   In general, hooks that are rarely invoked and/or are very large should be
+ *   placed in a separate include file, while hooks that are very short or very
+ *   frequently called should be left in the main module file so that they are
+ *   always available.
  *
  * @return
  *   An associative array whose keys are hook names and whose values are an
  *   associative array containing:
+ *   - essential: A boolean defining whether the hook is needed for maintaining
+ *     data integrity.
  *   - group: A string defining the group to which the hook belongs. The module
  *     system will determine whether a file with the name $module.$group.inc
  *     exists, and automatically load it when required.
@@ -35,6 +43,9 @@
  * @see hook_hook_info_alter().
  */
 function hook_hook_info() {
+  $hooks['entity_info'] = array(
+    'essential' => TRUE,
+  );
   $hooks['token_info'] = array(
     'group' => 'tokens',
   );
@@ -3131,8 +3142,8 @@ function hook_disable() {
  */
 function hook_registry_files_alter(&$files, $modules) {
   foreach ($modules as $module) {
-    // Only add test files for disabled modules, as enabled modules should
-    // already include any test files they provide.
+    // Only add test files for disabled modules, as enabled and hibernated
+    // modules should already include any test files they provide.
     if (!$module->status) {
       $dir = $module->dir;
       foreach ($module->info['files'] as $file) {
diff --git a/core/modules/system/system.install b/core/modules/system/system.install
index ad5c403..9870bf6 100644
--- a/core/modules/system/system.install
+++ b/core/modules/system/system.install
@@ -1562,7 +1562,7 @@ function system_schema() {
         'default' => '',
       ),
       'status' => array(
-        'description' => 'Boolean indicating whether or not this item is enabled.',
+        'description' => 'A field indicating the status of the item. Three statuses are defined in core: disabled (0), enabled(1), and hibernated (2).',
         'type' => 'int',
         'not null' => TRUE,
         'default' => 0,
diff --git a/core/modules/system/system.module b/core/modules/system/system.module
index fd571ce..55f2056 100644
--- a/core/modules/system/system.module
+++ b/core/modules/system/system.module
@@ -250,6 +250,15 @@ function system_permission() {
  * Implements hook_hook_info().
  */
 function system_hook_info() {
+  $essential_hooks = array(
+    'schema',
+    'modules_enabled',
+    'modules_disabled',
+  );
+  $hooks = array_fill_keys($essential_hooks, array(
+    'essential' => TRUE,
+  ));
+
   $hooks['token_info'] = array(
     'group' => 'tokens',
   );
