diff --git a/core/includes/install.inc b/core/includes/install.inc
index 8c91dc8..f3fbdf0 100644
--- a/core/includes/install.inc
+++ b/core/includes/install.inc
@@ -630,8 +630,14 @@ function drupal_install_system($install_state) {
 
   $system_path = drupal_get_path('module', 'system');
   require_once DRUPAL_ROOT . '/' . $system_path . '/system.install';
+
+  // Set the schema version to the number of the last update provided by the
+  // module, or the minimum core schema version.
+  $system_version = \Drupal::CORE_MINIMUM_SCHEMA_VERSION;
   $system_versions = drupal_get_schema_versions('system');
-  $system_version = $system_versions ? max($system_versions) : SCHEMA_INSTALLED;
+  if ($system_versions) {
+    $system_version = max(max($system_versions), $system_version);
+  }
   \Drupal::keyValue('system.schema')->set('system', $system_version);
 
   // System module needs to be enabled and the system/module lists need to be
diff --git a/core/includes/schema.inc b/core/includes/schema.inc
index e086da7..bb1c6e6 100644
--- a/core/includes/schema.inc
+++ b/core/includes/schema.inc
@@ -20,11 +20,6 @@
 const SCHEMA_UNINSTALLED = -1;
 
 /**
- * Indicates that a module has been installed.
- */
-const SCHEMA_INSTALLED = 0;
-
-/**
  * Gets the schema definition of a table, or the whole database schema.
  *
  * The returned schema will include any modifications made by any
diff --git a/core/includes/theme.inc b/core/includes/theme.inc
index 96a0466..144882b 100644
--- a/core/includes/theme.inc
+++ b/core/includes/theme.inc
@@ -1046,7 +1046,6 @@ function theme_get_setting($setting_name, $theme = NULL) {
  * Converts theme settings to configuration.
  *
  * @see system_theme_settings_submit()
- * @see system_update_8054()
  *
  * @param array $theme_settings
  *   An array of theme settings from system setting form or a Drupal 7 variable.
diff --git a/core/includes/update.inc b/core/includes/update.inc
index 9383a07..f185036 100644
--- a/core/includes/update.inc
+++ b/core/includes/update.inc
@@ -20,16 +20,6 @@
 use Symfony\Component\HttpFoundation\Request;
 
 /**
- * Minimum schema version of Drupal 7 required for upgrade to Drupal 8.
- *
- * Upgrades from Drupal 7 to Drupal 8 require that Drupal 7 be running
- * the most recent version, or the upgrade could fail. We can't easily
- * check the Drupal 7 version once the update process has begun, so instead
- * we check the schema version of system.module.
- */
-const REQUIRED_D7_SCHEMA_VERSION = '7069';
-
-/**
  * Disables any extensions that are incompatible with the current core version.
  */
 function update_fix_compatibility() {
@@ -82,653 +72,95 @@ function update_check_incompatibility($name, $type = 'module') {
 }
 
 /**
- * Performs extra steps required to bootstrap when using a Drupal 7 database.
- *
- * Users who still have a Drupal 7 database (and are in the process of
- * updating to Drupal 8) need extra help before a full bootstrap can be
- * achieved. This function does the necessary preliminary work that allows
- * the bootstrap to be successful.
+ * Returns whether the settings file requirement has been satisfied.
  *
- * No access check has been performed when this function is called, so no
- * irreversible changes to the database are made here.
+ * @return array
+ *  A requirements info array.
  */
-function update_prepare_d8_bootstrap() {
-  include_once __DIR__ . '/install.inc';
-  include_once __DIR__ . '/schema.inc';
-  // Bootstrap to configuration.
-  drupal_bootstrap(DRUPAL_BOOTSTRAP_CONFIGURATION);
-
-  // During the bootstrap to DRUPAL_BOOTSTRAP_PAGE_CACHE, code will try to read
-  // the cache but the cache tables are not compatible yet. Use the Null backend
-  // by default to avoid exceptions.
-  $settings = settings()->getAll();
-  $settings['cache']['default'] = 'cache.backend.memory';
-  new Settings($settings);
-
-  // Enable UpdateServiceProvider service overrides.
-  // @see update_flush_all_caches()
-  $GLOBALS['conf']['container_service_providers']['UpdateServiceProvider'] = 'Drupal\Core\DependencyInjection\UpdateServiceProvider';
-  $GLOBALS['conf']['update_service_provider_overrides'] = TRUE;
+function update_settings_file_requirements() {
+  $requirements = array();
 
   // Check whether settings.php needs to be rewritten.
-  $settings_exist = !empty($GLOBALS['config_directories']);
-
-  if (!$settings_exist || !is_dir(config_get_config_directory('active')) || !is_dir(config_get_config_directory('staging'))) {
-    drupal_install_config_directories();
-  }
-
-  // Bootstrap the kernel.
-  // Do not attempt to dump and write it.
-  $kernel = new DrupalKernel('update', drupal_classloader(), FALSE);
-  $kernel->boot();
-  $request = Request::createFromGlobals();
-  \Drupal::getContainer()->set('request', $request);
-
-  // If any of the required settings needs to be written, then settings.php
-  // needs to be writable.
-  if (!$settings_exist) {
-    $settings_file = conf_path() . '/settings.php';
-    $writable = drupal_verify_install_file($settings_file, FILE_EXIST | FILE_READABLE | FILE_WRITABLE);
-    $requirements['settings file']['title'] = 'Settings file';
-    if ($writable) {
-      $requirements['settings file'] += array(
-        'value' => 'settings.php is writable.',
-      );
-    }
-    else {
-      $requirements['settings file'] += array(
-        'value' => 'settings.php is not writable.',
-        'severity' => REQUIREMENT_ERROR,
-        'description' => 'Drupal requires write permissions to <em>' . $settings_file . '</em> during the update process. If you are unsure how to grant file permissions, consult the <a href="http://drupal.org/server-permissions">online handbook</a>.',
-      );
-    }
-    update_extra_requirements($requirements);
-  }
-
-  // Bootstrap the database.
-  require_once __DIR__ . '/database.inc';
-
-  // module.inc is not yet loaded but there are calls to module_config_sort()
-  // below.
-  require_once __DIR__ . '/module.inc';
-
-  // If the site has not updated to Drupal 8 yet, check to make sure that it is
-  // running an up-to-date version of Drupal 7 before proceeding. Note this has
-  // to happen AFTER the database bootstraps because of
-  // drupal_get_installed_schema_version().
-  try {
-    $system_schema = drupal_get_installed_schema_version('system');
-  }
-  catch (\Exception $e) {
-    $system_schema = db_query('SELECT schema_version FROM {system} WHERE name = :system', array(':system' => 'system'))->fetchField();
-  }
-  if ($system_schema < 8000) {
-    $has_required_schema = $system_schema >= REQUIRED_D7_SCHEMA_VERSION;
-    $requirements = array(
-      'drupal 7 version' => array(
-        'title' => 'Drupal 7 version',
-        'value' => $has_required_schema ? 'You are running a current version of Drupal 7.' : 'You are not running a current version of Drupal 7',
-        'severity' => $has_required_schema ? NULL : REQUIREMENT_ERROR,
-        'description' => $has_required_schema ? '' : 'Please update your Drupal 7 installation to the most recent version before attempting to upgrade to Drupal 8',
-      ),
+  $settings_file = conf_path() . '/settings.php';
+  $writable = drupal_verify_install_file($settings_file, FILE_EXIST | FILE_READABLE | FILE_WRITABLE);
+  $requirements['settings file']['title'] = 'Settings file';
+  if ($writable) {
+    $requirements['settings file'] += array(
+      'value' => 'settings.php is writable.',
     );
-    update_extra_requirements($requirements);
-
-    // @todo update.php stages seem to be completely screwed up; the initial
-    //   requirements check is not supposed to change the system. All of the
-    //   following code seems to have been mistakenly/unknowingly added here and
-    //   does not belong into update_prepare_d8_bootstrap().
-    if ($has_required_schema) {
-      if (!db_table_exists('key_value')) {
-        $specs = array(
-          'description' => 'Generic key-value storage table. See the state system for an example.',
-          'fields' => array(
-            'collection' => array(
-              'description' => 'A named collection of key and value pairs.',
-              'type' => 'varchar',
-              'length' => 128,
-              'not null' => TRUE,
-              'default' => '',
-            ),
-            'name' => array(
-              'description' => 'The key of the key-value pair. As KEY is a SQL reserved keyword, name was chosen instead.',
-              'type' => 'varchar',
-              'length' => 128,
-              'not null' => TRUE,
-              'default' => '',
-            ),
-            'value' => array(
-              'description' => 'The value.',
-              'type' => 'blob',
-              'not null' => TRUE,
-              'size' => 'big',
-              'translatable' => TRUE,
-            ),
-          ),
-          'primary key' => array('collection', 'name'),
-        );
-        db_create_table('key_value', $specs);
-      }
-      if (!db_table_exists('cache_tags')) {
-        $table = array(
-          'description' => 'Cache table for tracking cache tags related to the cache bin.',
-          'fields' => array(
-            'tag' => array(
-              'description' => 'Namespace-prefixed tag string.',
-              'type' => 'varchar',
-              'length' => 255,
-              'not null' => TRUE,
-              'default' => '',
-            ),
-            'invalidations' => array(
-              'description' => 'Number incremented when the tag is invalidated.',
-              'type' => 'int',
-              'not null' => TRUE,
-              'default' => 0,
-            ),
-            'deletions' => array(
-              'description' => 'Number incremented when the tag is deleted.',
-              'type' => 'int',
-              'not null' => TRUE,
-              'default' => 0,
-            ),
-          ),
-          'primary key' => array('tag'),
-        );
-        db_create_table('cache_tags', $table);
-      }
-      if (!db_table_exists('cache_config')) {
-        $spec = array(
-          'description' =>  'Cache table for configuration data.',
-          'fields' => array(
-            'cid' => array(
-              'description' => 'Primary Key: Unique cache ID.',
-              'type' => 'varchar',
-              'length' => 255,
-              'not null' => TRUE,
-              'default' => '',
-            ),
-            'data' => array(
-              'description' => 'A collection of data to cache.',
-              'type' => 'blob',
-              'not null' => FALSE,
-              'size' => 'big',
-            ),
-            'expire' => array(
-              'description' => 'A Unix timestamp indicating when the cache entry should expire, or 0 for never.',
-              'type' => 'int',
-              'not null' => TRUE,
-              'default' => 0,
-            ),
-            'created' => array(
-              'description' => 'A Unix timestamp indicating when the cache entry was created.',
-              'type' => 'int',
-              'not null' => TRUE,
-              'default' => 0,
-            ),
-            'serialized' => array(
-              'description' => 'A flag to indicate whether content is serialized (1) or not (0).',
-              'type' => 'int',
-              'size' => 'small',
-              'not null' => TRUE,
-              'default' => 0,
-            ),
-            'tags' => array(
-              'description' => 'Space-separated list of cache tags for this entry.',
-              'type' => 'text',
-              'size' => 'big',
-              'not null' => FALSE,
-            ),
-            'checksum_invalidations' => array(
-              'description' => 'The tag invalidation sum when this entry was saved.',
-              'type' => 'int',
-              'not null' => TRUE,
-              'default' => 0,
-            ),
-            'checksum_deletions' => array(
-              'description' => 'The tag deletion sum when this entry was saved.',
-              'type' => 'int',
-              'not null' => TRUE,
-              'default' => 0,
-            ),
-          ),
-          'indexes' => array(
-            'expire' => array('expire'),
-          ),
-          'primary key' => array('cid'),
-        );
-        db_create_table('cache_config', $spec);
-      }
-
-      require_once DRUPAL_ROOT . '/core/modules/system/system.install';
-      $tables = array(
-        'cache',
-        'cache_bootstrap',
-        'cache_block',
-        'cache_field',
-        'cache_filter',
-        'cache_form',
-        'cache_image',
-        'cache_menu',
-        'cache_page',
-        'cache_path',
-        'cache_update',
-      );
-
-      foreach ($tables as $table) {
-        update_add_cache_columns($table);
-      }
-
-      // Bootstrap to cache system.
-      drupal_bootstrap(DRUPAL_BOOTSTRAP_PAGE_CACHE);
-
-      // Update the 'language_default' system variable, if configured.
-      // Required to run before drupal_install_config_directories(), since that
-      // triggers a call into system_stream_wrappers(), which calls t(), which
-      // calls into language_default().
-      $language_default = update_variable_get('language_default');
-      if (!empty($language_default) && (isset($language_default->id) || isset($language_default->language))) {
-        if (!isset($language_default->id)) {
-          $language_default->id = $language_default->language;
-        }
-        unset($language_default->language);
-        // In D8, the 'language_default' is not anymore an object, but an array,
-        // so make sure that the new value that is saved into this variable is an
-        // array.
-        update_variable_set('language_default', (array) $language_default);
-      }
-
-      $module_config = \Drupal::config('system.module');
-      $theme_config = \Drupal::config('system.theme');
-      $disabled_themes = \Drupal::config('system.theme.disabled');
-      $schema_store = \Drupal::keyValue('system.schema');
-
-      // Load system.module, because update_prepare_d8_bootstrap() is called in
-      // the initial minimal update.php bootstrap that performs the core
-      // requirements check.
-      require_once DRUPAL_ROOT . '/core/modules/system/system.module';
-
-      // Make sure that the bootstrap cache is cleared as that might contain
-      // incompatible data structures.
-      cache('bootstrap')->deleteAll();
-
-      // Retrieve all installed extensions from the {system} table.
-      // Uninstalled extensions are ignored and not converted.
-      $result = db_query('SELECT name, status, weight, schema_version, type FROM {system} WHERE type = :theme OR (type = :module AND schema_version <> :schema_uninstalled)', array(
-        ':theme' => 'theme',
-        ':module' => 'module',
-        ':schema_uninstalled' => SCHEMA_UNINSTALLED,
-      ));
-
-      $module_data = _system_rebuild_module_data();
-
-      // Migrate each extension into configuration, varying by the extension's
-      // status, and record its schema version.
-      foreach ($result as $record) {
-        // Before migrating any extension into configuration, make sure the
-        // extensions name length is not higher than the limit.
-        if (drupal_strlen($record->name) > 50) {
-          $requirements['module name too long ' . $record->name] = array(
-            'title' => 'Module name too long',
-            'value' => format_string('@name is @count characters long.', array('@name' => $record->name, '@count' => drupal_strlen($record->name))),
-            'description' => 'Module names longer than 50 characters are <a href="https://drupal.org/node/2014073">no longer supported</a>.',
-            'severity' => REQUIREMENT_ERROR,
-          );
-          update_extra_requirements($requirements);
-        }
-
-        if ($record->type == 'module') {
-          if ($record->status && isset($module_data[$record->name])) {
-            $module_config->set('enabled.' . $record->name, $record->weight);
-          }
-        }
-        elseif ($record->type == 'theme') {
-          if ($record->status) {
-            $theme_config->set('enabled.' . $record->name, 0);
-          }
-          else {
-            $disabled_themes->set($record->name, 0);
-          }
-        }
-        $schema_store->set($record->name, $record->schema_version);
-      }
-      $sorted_modules = module_config_sort($module_config->get('enabled'));
-      $module_config->set('enabled', $sorted_modules)->save();
-      $sorted_with_filenames = array();
-      foreach (array_keys($sorted_modules) as $m) {
-        $sorted_with_filenames[$m] = drupal_get_filename('module', $m);
-      }
-      \Drupal::moduleHandler()->setModuleList($sorted_with_filenames);
-      $theme_config->save();
-      $disabled_themes->save();
-
-      // Migrate the private key to state. This is used to create the token for
-      // the upgrade batch so needs to be be done before the upgrade has begun.
-      update_variables_to_state(array(
-        'drupal_private_key' => 'system.private_key',
-      ));
-
-      // Update the dynamic include paths that might be used before running the
-      // proper update functions.
-      update_prepare_stored_includes();
-      // Update the environment for the language bootstrap if needed.
-      update_prepare_d8_language();
-      // Rebuild kernel after new language fields are added in the database
-      // because the translation service depends on them being there.
-      \Drupal::service('kernel')->updateModules($sorted_with_filenames, $sorted_with_filenames);
-
-      // Change language column to langcode in url_alias.
-      if (db_table_exists('url_alias') && db_field_exists('url_alias', 'language')) {
-        db_drop_index('url_alias', 'alias_language_pid');
-        db_drop_index('url_alias', 'source_language_pid');
-        $langcode_spec = array(
-          'description' => "The language code this alias is for; if 'und', the alias will be used for unknown languages. Each Drupal path can have an alias for each supported language.",
-          'type' => 'varchar',
-          'length' => 12,
-          'not null' => TRUE,
-          'default' => '',
-        );
-        $langcode_indexes = array('indexes' =>
-          array(
-            'alias_langcode_pid' => array('alias', 'langcode', 'pid'),
-            'source_langcode_pid' => array('source', 'langcode', 'pid'),
-          ),
-        );
-        db_change_field('url_alias', 'language', 'langcode', $langcode_spec, $langcode_indexes);
-      }
-    }
   }
-  // Moves install_profile from variable to settings. You can't do that in
-  // system.install because _system_rebuild_module_data() needs the profile
-  // directly. Check that it has not been set already. This is the case for
-  // Simpletest upgrade path tests.
-  if (!settings()->get('install_profile')) {
-    $old_variable = unserialize(\Drupal::database()->query('SELECT value FROM {variable} WHERE name = :name', array(':name' => 'install_profile'))->fetchField());
-    $settings = array(
-      'settings' => array(
-        'install_profile' => (object) array(
-            'value' => $old_variable,
-            'required' => TRUE,
-        ),
-      )
+  else {
+    $requirements['settings file'] += array(
+      'value' => 'settings.php is not writable.',
+      'severity' => REQUIREMENT_ERROR,
+      'description' => 'Drupal requires write permissions to <em>' . $settings_file . '</em> during the update process. If you are unsure how to grant file permissions, consult the <a href="http://drupal.org/server-permissions">online handbook</a>.',
     );
-    drupal_rewrite_settings($settings);
   }
-
-  // Now remove the cache override.
-  $settings = settings()->getAll();
-  unset($settings['cache']['default']);
-  new Settings($settings);
-  $kernel = new DrupalKernel('update', drupal_classloader(), FALSE);
-  $kernel->boot();
-  \Drupal::getContainer()->set('request', $request);
-
-  // Clear the D7 caches, to ensure that for example the theme_registry does not
-  // take part in the upgrade process.
-  Drupal::cache('cache')->deleteAll();
+  return $requirements;
 }
 
 /**
- * Fixes stored include paths to match the "/core" migration.
- */
-function update_prepare_stored_includes() {
-  // Retrieve the currently stored language types. Default to the hardcoded D7
-  // values.
-  $default_language_types = array('language' => TRUE, 'language_content' => FALSE, 'language_url' => FALSE);
-  $language_types = array_keys(update_variable_get('language_types', $default_language_types));
-
-  // Update language negotiation settings.
-  foreach ($language_types as $language_type) {
-    $negotiation = update_variable_get("language_negotiation_$language_type", array());
-    foreach ($negotiation as &$method) {
-      unset($method['file']);
-    }
-    update_variable_set("language_negotiation_$language_type", $negotiation);
-  }
-}
-
-/**
- * Prepares Drupal 8 language changes for the bootstrap if needed.
+ * Returns whether the minimum schema requirement has been satisfied.
+ *
+ * @return array
+ *  A requirements info array.
  */
-function update_prepare_d8_language() {
-  if (db_table_exists('languages')) {
-    \Drupal::moduleHandler()->install(array('language'));
-
-    $languages = db_select('languages', 'l')
-      ->fields('l')
-      ->execute();
-    $plurals = array();
-    $javascript = array();
-    $prefixes = array();
-    $domains = array();
-    foreach ($languages as $language) {
-      $plurals[$language->language] = array(
-        'plurals' => $language->plurals,
-        'formula' => $language->formula,
-      );
-      $javascript[$language->language] = $language->javascript;
-      $prefixes[$language->language] = $language->prefix;
-      $domains[$language->language] = $language->domain;
-    }
-    \Drupal::state()->set('locale.translation.plurals', $plurals);
-    \Drupal::state()->set('locale.translation.javascript', $javascript);
-    \Drupal::config('language.negotiation')
-      ->set('url.prefixes', $prefixes)
-      ->set('url.domains', $domains)
-      ->save();
+function update_system_schema_requirements() {
+  $requirements = array();
 
-    // Drop now unneeded columns.
-    db_drop_field('languages', 'plurals');
-    db_drop_field('languages', 'formula');
-    db_drop_field('languages', 'javascript');
-    db_drop_field('languages', 'prefix');
-    db_drop_field('languages', 'domain');
-    db_drop_field('languages', 'native');
-    db_drop_field('languages', 'enabled');
+  $system_schema = drupal_get_installed_schema_version('system');
 
-    // Rename the languages table to language.
-    db_rename_table('languages', 'language');
-
-    // Rename language column to langcode and set it again as the primary key.
-    if (db_field_exists('language', 'language')) {
-      db_drop_primary_key('language');
-      $langcode_spec = array(
-        'type' => 'varchar',
-        'length' => 12,
-        'not null' => TRUE,
-        'default' => '',
-        'description' => "Language code, e.g. 'de' or 'en-US'.",
-      );
-      db_change_field('language', 'language', 'langcode', $langcode_spec, array('primary key' => array('langcode')));
-    }
-
-    // Adds the locked column and saves the special languages.
-    if (!db_field_exists('language', 'locked')) {
-      $locked_spec = array(
-        'type' => 'int',
-        'size' => 'tiny',
-        'not null' => TRUE,
-        'default' => 0,
-        'description' => 'A boolean indicating whether the administrator can edit or delete the language.',
-      );
-      db_add_field('language', 'locked', $locked_spec);
-
-      $max_language_weight = db_query('SELECT MAX(weight) FROM {language}')->fetchField();
-      $languages = \Drupal::languageManager()->getDefaultLockedLanguages($max_language_weight);
-      foreach ($languages as $language) {
-        db_insert('language')
-          ->fields(array(
-            'langcode' => $language->id,
-            'name' => $language->name,
-            'weight' => $language->weight,
-            // These languages are locked, default to enabled.
-            'locked' => 1,
-          ))
-          ->execute();
-      }
-    }
-
-    // Update the 'language_default' system variable with the langcode change.
-    $language_default = update_variable_get('language_default');
-    if (!empty($language_default)) {
-      if (isset($language_default->language)) {
-        $language_default->id = $language_default->language;
-        unset($language_default->language);
-      }
-      unset($language_default->enabled);
-      // In D8, the 'language_default' is not anymore an object, but an array,
-      // so make sure that the new value that is saved into this variable is an
-      // array.
-      $language_default = (array) $language_default;
-      $language_default['langcode'] = 'en';
-      update_variable_set('language_default', $language_default);
-    }
-
-    // Convert languages to config entities.
-    $result = db_query('SELECT * FROM {language}');
-    $uuid = \Drupal::service('uuid');
-    foreach ($result as $language) {
-      \Drupal::config('language.entity.' . $language->langcode)
-        ->set('id', $language->langcode)
-        ->set('uuid', $uuid->generate())
-        ->set('label', $language->name)
-        ->set('direction', $language->direction)
-        ->set('weight', $language->weight)
-        ->set('locked', $language->locked)
-        ->set('langcode', 'en')
-        ->save();
-    }
-
-    // Add column to track customized string status to locales_target.
-    // When updating in a non-English language, the locale translation system is
-    // triggered, which attempts to query string translations already.
-    if (db_table_exists('locales_target') && !db_field_exists('locales_target', 'customized')) {
-      $spec = array(
-        'type' => 'int',
-        'not null' => TRUE,
-        'default' => 0, // LOCALE_NOT_CUSTOMIZED
-        'description' => 'Boolean indicating whether the translation is custom to this site.',
-      );
-      db_add_field('locales_target', 'customized', $spec);
-    }
-    // Add locales_location table to track string locations.
-    // When updating in a non-English language, this table is used for
-    // refreshing JavaScript translations.
-    if (db_table_exists('locales_source') && !db_table_exists('locales_location')) {
-      $table = array(
-        'description' => 'Location information for source strings.',
-        'fields' => array(
-          'lid' => array(
-            'type' => 'serial',
-            'not null' => TRUE,
-            'description' => 'Unique identifier of this location.',
-          ),
-          'sid' => array(
-            'type' => 'int',
-            'not null' => TRUE,
-            'description' => 'Unique identifier of this string.',
-          ),
-          'type' => array(
-            'type' => 'varchar',
-            'length' => 50,
-            'not null' => TRUE,
-            'default' => '',
-            'description' => 'The location type (file, config, path, etc).',
-          ),
-          'name' => array(
-            'type' => 'varchar',
-            'length' => 255,
-            'not null' => TRUE,
-            'default' => '',
-            'description' => 'Type dependent location information (file name, path, etc).',
-          ),
-          'version' => array(
-            'type' => 'varchar',
-            'length' => 20,
-            'not null' => TRUE,
-            'default' => 'none',
-            'description' => 'Version of Drupal where the location was found.',
-          ),
-        ),
-        'primary key' => array('lid'),
-        'foreign keys' => array(
-          'locales_source' => array(
-            'table' => 'locales_source',
-            'columns' => array('sid' => 'lid'),
-          ),
-        ),
-        'indexes' => array(
-          'string_id' => array('sid'),
-          'string_type' => array('sid', 'type'),
-        ),
-      );
-
-      db_create_table('locales_location', $table);
-    }
+  $requirements['minimum schema']['title'] = 'Minimum schema version';
+  if ($system_schema >= \Drupal::CORE_MINIMUM_SCHEMA_VERSION) {
+    $requirements['minimum schema'] += array(
+      'value' => 'The installed schema version meets the minimum.',
+      'description' => 'Schema version: ' . $system_schema,
+    );
   }
+  else {
+    $requirements['minimum schema'] += array(
+      'value' => 'The installed schema version does not meet the minimum.',
+      'severity' => REQUIREMENT_ERROR,
+      'description' => 'Your system schema version is ' . $system_schema . '. Updating directly from a schema version prior to 8000 is not supported. You must <a href="https://drupal.org/node/2179269">migrate your site to Drupal 8</a> first.',
+    );
+  }
+
+  return $requirements;
 }
 
 /**
- * Performs Drupal 7.x to 8.x required update.php updates.
+ * Checks update requirements and reports errors and (optionally) warnings.
  *
- * This function runs when update.php is run the first time for 8.x,
- * even before updates are selected or performed. It is important
- * that if updates are not ultimately performed that no changes are
- * made which make it impossible to continue using the prior version.
+ * @param $skip_warnings
+ *   (optional) If set to TRUE, requirement warnings will be ignored, and a
+ *   report will only be issued if there are requirement errors. Defaults to
+ *   FALSE.
  */
-function update_fix_d8_requirements() {
-  if (drupal_get_installed_schema_version('system') < 8000 && !update_variable_get('update_d8_requirements', FALSE)) {
-
-    // Make sure that file.module is enabled as it is required for the user
-    // picture upgrade path.
-    \Drupal::moduleHandler()->install(array('file'));
-
-    $schema = array(
-      'description' => 'Generic key/value storage table with an expiration.',
-      'fields' => array(
-        'collection' => array(
-          'description' => 'A named collection of key and value pairs.',
-          'type' => 'varchar',
-          'length' => 128,
-          'not null' => TRUE,
-          'default' => '',
-        ),
-        'name' => array(
-          // KEY is an SQL reserved word, so use 'name' as the key's field name.
-          'description' => 'The key of the key/value pair.',
-          'type' => 'varchar',
-          'length' => 128,
-          'not null' => TRUE,
-          'default' => '',
-        ),
-        'value' => array(
-          'description' => 'The value of the key/value pair.',
-          'type' => 'blob',
-          'not null' => TRUE,
-          'size' => 'big',
-        ),
-        'expire' => array(
-          'description' => 'The time since Unix epoch in seconds when this item expires. Defaults to the maximum possible time.',
-          'type' => 'int',
-          'not null' => TRUE,
-          'default' => 2147483647,
-        ),
-      ),
-      'primary key' => array('collection', 'name'),
-      'indexes' => array(
-        'all' => array('name', 'collection', 'expire'),
-      ),
+function update_check_requirements($skip_warnings = FALSE) {
+  // Check requirements of all loaded modules.
+  $requirements = \Drupal::moduleHandler()->invokeAll('requirements', array('update'));
+  $requirements += update_system_schema_requirements();
+  $requirements += update_settings_file_requirements();
+  $severity = drupal_requirements_severity($requirements);
+
+  // If there are errors, always display them. If there are only warnings, skip
+  // them if the caller has indicated they should be skipped.
+  if ($severity == REQUIREMENT_ERROR || ($severity == REQUIREMENT_WARNING && !$skip_warnings)) {
+    update_task_list('requirements');
+    drupal_set_title('Requirements problem');
+    $status = array(
+      '#theme' => 'status_report',
+      '#requirements' => $requirements,
     );
-    db_create_table('key_value_expire', $schema);
-
-    // Views module is required to convert all previously existing listings into
-    // views configurations.
-    // Like any other module APIs and services, Views' services are not available
-    // in update.php. Existing listings are migrated into configuration, using
-    // the limited standard tools of raw database queries and \Drupal::config().
-    \Drupal::moduleHandler()->install(array('views'));
-
-    update_variable_set('update_d8_requirements', TRUE);
+    $status_report = drupal_render($status);
+    $status_report .= 'Check the messages and <a href="' . check_url(drupal_requirements_url($severity)) . '">try again</a>.';
+    drupal_add_http_header('Content-Type', 'text/html; charset=utf-8');
+    $maintenance_page = array(
+      '#theme' => 'maintenance_page',
+      '#content' => $status_report,
+    );
+    print drupal_render($maintenance_page);
+    exit();
   }
 }
 
@@ -985,6 +417,12 @@ function update_get_update_list() {
     if ($schema_version == SCHEMA_UNINSTALLED || update_check_incompatibility($module)) {
       continue;
     }
+    // Display a requirements error if the user somehow has a schema version
+    // from the previous Drupal major version.
+    if ($schema_version < \Drupal::CORE_MINIMUM_SCHEMA_VERSION) {
+      $ret[$module]['warning'] = '<em>' . $module . '</em> module cannot be updated. Its schema version is ' . $schema_version . ', which is from an earlier major release of Drupal. You will need to <a href="https://drupal.org/node/2127611">migrate the data for this module</a> instead.';
+      continue;
+    }
     // Otherwise, get the list of updates defined by this module.
     $updates = drupal_get_schema_versions($module);
     if ($updates !== FALSE) {
@@ -992,12 +430,16 @@ function update_get_update_list() {
       // are removed, it will == 0.
       $last_removed = module_invoke($module, 'update_last_removed');
       if ($schema_version < $last_removed) {
-        $ret[$module]['warning'] = '<em>' . $module . '</em> module can not be updated. Its schema version is ' . $schema_version . '. Updates up to and including ' . $last_removed . ' have been removed in this release. In order to update <em>' . $module . '</em> module, you will first <a href="http://drupal.org/upgrade">need to upgrade</a> to the last version in which these updates were available.';
+        $ret[$module]['warning'] = '<em>' . $module . '</em> module cannot be updated. Its schema version is ' . $schema_version . '. Updates up to and including ' . $last_removed . ' have been removed in this release. In order to update <em>' . $module . '</em> module, you will first <a href="http://drupal.org/upgrade">need to upgrade</a> to the last version in which these updates were available.';
         continue;
       }
 
       $updates = drupal_map_assoc($updates);
       foreach (array_keys($updates) as $update) {
+        if ($update == \Drupal::CORE_MINIMUM_SCHEMA_VERSION) {
+          $ret[$module]['warning'] = '<em>' . $module . '</em> module cannot be updated. It contains an update numbered as ' . \Drupal::CORE_MINIMUM_SCHEMA_VERSION . ' which is reserved for the earliest installation of a module in Drupal ' .  \Drupal::CORE_COMPATIBILITY . ', before any updates. In order to update <em>' . $module . '</em> module, you will need to install a version of the module with valid updates.';
+          continue 2;
+        }
         if ($update > $schema_version) {
           // The description for an update comes from its Doxygen.
           $func = new ReflectionFunction($module . '_update_' . $update);
@@ -1146,10 +588,10 @@ function update_get_update_function_list($starting_updates) {
  * requirement that the first update function needs to run before the second.
  * For example, consider this graph:
  *
- * system_update_8000 ---> system_update_8001 ---> system_update_8002
+ * system_update_8001 ---> system_update_8002 ---> system_update_8003
  *
- * Visually, this indicates that system_update_8000() must run before
- * system_update_8001(), which in turn must run before system_update_8002().
+ * Visually, this indicates that system_update_8001() must run before
+ * system_update_8002(), which in turn must run before system_update_8003().
  *
  * The function takes into account standard dependencies within each module, as
  * shown above (i.e., the fact that each module's updates must run in numerical
@@ -1302,15 +744,15 @@ function update_retrieve_dependencies() {
               // implementation of hook_update_dependencies() required this
               // ordering:
               //
-              // system_update_8001 ---> user_update_8000
+              // system_update_8002 ---> user_update_8001
               //
               // but another module's implementation of the hook required this
               // one:
               //
-              // system_update_8002 ---> user_update_8000
+              // system_update_8003 ---> user_update_8001
               //
-              // we record the second one, since system_update_8001() is always
-              // guaranteed to run before system_update_8002() anyway (within
+              // we record the second one, since system_update_8002() is always
+              // guaranteed to run before system_update_8003() anyway (within
               // an individual module, updates are always run in numerical
               // order).
               if (!isset($return[$module][$update][$module_dependency]) || $update_dependency > $return[$module][$update][$module_dependency]) {
@@ -1327,207 +769,6 @@ function update_retrieve_dependencies() {
 }
 
 /**
- * Gets the value of a variable from the database during 7.x-8.x upgrades.
- *
- * Use this during the 7.x-8.x upgrade path instead of variable_get().
- *
- * @param string $name
- *   The name of the variable.
- * @param mixed $default
- *   The default value of the variable.
- *
- * @return mixed
- *   The value of the variable in the database unserialized, or NULL if not set.
- *
- * @see update_variable_set()
- * @see update_variable_del()
- * @ingroup config_upgrade
- */
-function update_variable_get($name, $default = NULL) {
-  $result = db_query('SELECT value FROM {variable} WHERE name = :name', array(':name' => $name))->fetchField();
-  if ($result !== FALSE) {
-    return unserialize($result);
-  }
-  return $default;
-}
-
-/**
- * Sets a persistent variable during the 7.x-8.x upgrade path.
- *
- * Use this during the 7.x-8.x upgrade path instead of variable_set().
- *
- * @param string $name
- *   The name of the variable to set.
- * @param mixed $value
- *   The value to set. This can be any PHP data type; these functions take care
- *   of serialization as necessary.
- *
- * @see update_variable_get()
- * @see update_variable_del()
- * @ingroup config_upgrade
- */
-function update_variable_set($name, $value) {
-  db_merge('variable')
-    ->key(array(
-      'name' => $name,
-    ))
-    ->fields(array(
-      'value' => serialize($value),
-    ))
-    ->execute();
-}
-
-/**
- * Delete a variable from the database during the 7.x-8.x upgrade path.
- *
- * Use this during the 7.x-8.x upgrade path instead of variable_del().
- *
- * @param string $name
- *   The name of the variable to delete.
- *
- * @see update_variable_get()
- * @see update_variable_set()
- * @ingroup config_upgrade
- */
-function update_variable_del($name) {
-  db_delete('variable')
-    ->condition('name', $name)
-    ->execute();
-}
-
-/**
- * Updates config with values set on Drupal 7.x.
- *
- * Provides a generalised method to migrate variables from Drupal 7 to
- * Drupal 8's configuration management system.
- *
- * @param string $config_name
- *   The configuration object name to retrieve.
- * @param array $variable_map
- *   An associative array that maps old variables names to new configuration
- *   object keys; e.g.:
- *   @code
- *     array('old_variable' => 'new_config.sub_key')
- *   @endcode
- *   This would migrate the value contained in variable name 'old_variable' into
- *   the data key 'new_config.sub_key' of the configuration object $config_name.
- */
-function update_variables_to_config($config_name, array $variable_map) {
-  // Build the new configuration object.
-  // This potentially loads an existing configuration object, in case another
-  // update function migrated configuration values into $config_name already.
-  $config = \Drupal::config($config_name);
-  $original_data = $config->get();
-
-  // Extract the module namespace/owner from the configuration object name.
-  $module = strtok($config_name, '.');
-
-  // Load and set default configuration values.
-  $file = new FileStorage(drupal_get_path('module', $module) . '/config');
-  if (!$file->exists($config_name)) {
-    throw new ConfigException("Default configuration file $config_name for $module extension not found but is required to exist.");
-  }
-  $default_data = $file->read($config_name);
-
-  // Apply the default values.
-  $config->setData($default_data);
-
-  // Merge any possibly existing original data into default values.
-  // Only relevant when being called repetitively on the same config object.
-  if (!empty($original_data)) {
-    $config->merge($original_data);
-  }
-
-  // Fetch existing variables.
-  $variables = db_query('SELECT name, value FROM {variable} WHERE name IN (:variables)', array(':variables' => array_keys($variable_map)))->fetchAllKeyed();
-
-  // Set configuration values according to the provided variable mapping.
-  foreach ($variable_map as $variable_name => $config_key) {
-    // This function migrates variables regardless of their value, including
-    // NULL values. Any possibly required customizations need to be performed
-    // manually, either via variable_set() before calling this function or via
-    // \Drupal::config() after calling this function.
-    if (isset($variables[$variable_name])) {
-      $value = unserialize($variables[$variable_name]);
-      $config->set($config_key, $value);
-    }
-  }
-
-  // Save the configuration object.
-  $config->save();
-
-  // Delete the migrated variables.
-  db_delete('variable')->condition('name', array_keys($variable_map), 'IN')->execute();
-}
-
-/**
- * Installs a default configuration file into the active store.
- *
- * Provide a generalised method to save a default configuration object for an
- * already enabled module or theme as part of an update from Drupal 7 to Drupal
- * 8's configuration management system.
- *
- * @param string $type
- *   The extension type; e.g., 'module' or 'theme'.
- * @param string $config_name
- *   The configuration object name to retrieve.
- * @param string $name
- *   (optional) The owner of the config. Defaults to NULL, in which case the
- *   name will be derived from the $config_name.
- *
- * @return boolean
- *   True on success, false if config file does not exist.
- */
-function update_install_default_config($type, $config_name, $name = NULL) {
-  // Build the new configuration object.
-  $config = \Drupal::config($config_name);
-
-  // Extract the extension namespace/owner from the configuration object name.
-  if (!$name) {
-    $name = strtok($config_name, '.');
-  }
-
-  // Load and set default configuration values.
-  $file = new FileStorage(drupal_get_path($type, $name) . '/config');
-  if (!$file->exists($config_name)) {
-    return FALSE;
-  }
-
-  // Apply and save the default values.
-  $config->setData($file->read($config_name))->save();
-  return TRUE;
-}
-
-/**
- * Updates 7.x variables to state records.
- *
- * Provides a generalized method to migrate variables from 7.x to 8.x's
- * \Drupal::state() system.
- *
- * @param array $variable_map
- *   An associative array that maps old variables names to new state record
- *   names; e.g.:
- *   @code
- *     array('old_variable' => 'extension.new_name')
- *   @endcode
- *   This would migrate the value contained in variable name 'old_variable' into
- *   the state item 'extension.new_name'.
- *   Non-existing variables and variables with NULL values are omitted.
- */
-function update_variables_to_state(array $variable_map) {
-  foreach ($variable_map as $variable_name => $state_name) {
-    if (NULL !== $value = update_variable_get($variable_name)) {
-      \Drupal::state()->set($state_name, $value);
-    }
-  }
-
-  // Delete the migrated variables.
-  db_delete('variable')
-    ->condition('name', array_keys($variable_map))
-    ->execute();
-}
-
-/**
  * Helper function to update entities with uuid.
  *
  * @param array $sandbox
diff --git a/core/lib/Drupal.php b/core/lib/Drupal.php
index 0ec88d7..cc68937 100644
--- a/core/lib/Drupal.php
+++ b/core/lib/Drupal.php
@@ -87,6 +87,11 @@ class Drupal {
   const CORE_COMPATIBILITY = '8.x';
 
   /**
+   * Core minimum schema version.
+   */
+  const CORE_MINIMUM_SCHEMA_VERSION = 8000;
+
+  /**
    * The currently active container object.
    *
    * @var \Symfony\Component\DependencyInjection\ContainerInterface
diff --git a/core/lib/Drupal/Core/Extension/ModuleHandler.php b/core/lib/Drupal/Core/Extension/ModuleHandler.php
index d486973..ef7b5e7 100644
--- a/core/lib/Drupal/Core/Extension/ModuleHandler.php
+++ b/core/lib/Drupal/Core/Extension/ModuleHandler.php
@@ -627,10 +627,13 @@ public function install(array $module_list, $enable_dependencies = TRUE) {
         // Now install the module's schema if necessary.
         drupal_install_schema($module);
 
-        // Set the schema version to the number of the last update provided
-        // by the module.
+        // Set the schema version to the number of the last update provided by
+        // the module, or the minimum core schema version.
+        $version = \Drupal::CORE_MINIMUM_SCHEMA_VERSION;
         $versions = drupal_get_schema_versions($module);
-        $version = $versions ? max($versions) : SCHEMA_INSTALLED;
+        if ($versions) {
+          $version = max(max($versions), $version);
+        }
 
         // Install default configuration of the module.
         \Drupal::service('config.installer')->installDefaultConfig('module', $module);
diff --git a/core/lib/Drupal/Core/Extension/UpdateModuleHandler.php b/core/lib/Drupal/Core/Extension/UpdateModuleHandler.php
index 65f26a0..a8334b8 100644
--- a/core/lib/Drupal/Core/Extension/UpdateModuleHandler.php
+++ b/core/lib/Drupal/Core/Extension/UpdateModuleHandler.php
@@ -42,7 +42,7 @@ public function getImplementations($hook) {
       // Forms do not render without the basic elements in
       // drupal_common_theme() called from system_theme().
       case 'theme':
-      // user_update_8011() uses public:// to create the image style directory.
+      // Allow access to stream wrappers.
       case 'stream_wrappers':
         return array('system');
 
@@ -71,10 +71,7 @@ public function install(array $module_list, $enable_dependencies = TRUE) {
     $schema_store = \Drupal::keyValue('system.schema');
     $old_schema = array();
     foreach ($module_list as $module) {
-      // Check for initial schema and install it. The schema version of a newly
-      // installed module is always 0. Using 8000 here would be inconsistent
-      // since $module_update_8000() may involve a schema change, and we want
-      // to install the schema as it was before any updates were added.
+      // Check for initial schema and install it.
       module_load_install($module);
       $function = $module . '_schema_0';
       if (function_exists($function)) {
diff --git a/core/modules/system/lib/Drupal/system/Tests/Update/DependencyHookInvocationTest.php b/core/modules/system/lib/Drupal/system/Tests/Update/DependencyHookInvocationTest.php
index 154643b..f9b0a86 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Update/DependencyHookInvocationTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Update/DependencyHookInvocationTest.php
@@ -19,7 +19,7 @@ class DependencyHookInvocationTest extends WebTestBase {
    *
    * @var array
    */
-  public static $modules = array('update_test_1', 'update_test_2');
+  public static $modules = array('update_test_0', 'update_test_1', 'update_test_2');
 
   public static function getInfo() {
     return array(
@@ -39,8 +39,8 @@ function setUp() {
    */
   function testHookUpdateDependencies() {
     $update_dependencies = update_retrieve_dependencies();
-    $this->assertTrue($update_dependencies['system'][8000]['update_test_1'] == 8000, 'An update function that has a dependency on two separate modules has the first dependency recorded correctly.');
-    $this->assertTrue($update_dependencies['system'][8000]['update_test_2'] == 8001, 'An update function that has a dependency on two separate modules has the second dependency recorded correctly.');
-    $this->assertTrue($update_dependencies['system'][8001]['update_test_1'] == 8002, 'An update function that depends on more than one update from the same module only has the dependency on the higher-numbered update function recorded.');
+    $this->assertTrue($update_dependencies['update_test_0'][8001]['update_test_1'] == 8001, 'An update function that has a dependency on two separate modules has the first dependency recorded correctly.');
+    $this->assertTrue($update_dependencies['update_test_0'][8001]['update_test_2'] == 8002, 'An update function that has a dependency on two separate modules has the second dependency recorded correctly.');
+    $this->assertTrue($update_dependencies['update_test_0'][8002]['update_test_1'] == 8003, 'An update function that depends on more than one update from the same module only has the dependency on the higher-numbered update function recorded.');
   }
 }
diff --git a/core/modules/system/lib/Drupal/system/Tests/Update/DependencyMissingTest.php b/core/modules/system/lib/Drupal/system/Tests/Update/DependencyMissingTest.php
index 6c71371..167beea 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Update/DependencyMissingTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Update/DependencyMissingTest.php
@@ -19,7 +19,7 @@ class DependencyMissingTest extends WebTestBase {
    *
    * @var array
    */
-  public static $modules = array('update_test_2');
+  public static $modules = array('update_test_0', 'update_test_2');
 
   public static function getInfo() {
     return array(
@@ -38,11 +38,11 @@ function setUp() {
 
   function testMissingUpdate() {
     $starting_updates = array(
-      'update_test_2' => 8000,
+      'update_test_2' => 8001,
     );
     $update_graph = update_resolve_dependencies($starting_updates);
-    $this->assertTrue($update_graph['update_test_2_update_8000']['allowed'], "The module's first update function is allowed to run, since it does not have any missing dependencies.");
-    $this->assertFalse($update_graph['update_test_2_update_8001']['allowed'], "The module's second update function is not allowed to run, since it has a direct dependency on a missing update.");
-    $this->assertFalse($update_graph['update_test_2_update_8002']['allowed'], "The module's third update function is not allowed to run, since it has an indirect dependency on a missing update.");
+    $this->assertTrue($update_graph['update_test_2_update_8001']['allowed'], "The module's first update function is allowed to run, since it does not have any missing dependencies.");
+    $this->assertFalse($update_graph['update_test_2_update_8002']['allowed'], "The module's second update function is not allowed to run, since it has a direct dependency on a missing update.");
+    $this->assertFalse($update_graph['update_test_2_update_8003']['allowed'], "The module's third update function is not allowed to run, since it has an indirect dependency on a missing update.");
   }
 }
diff --git a/core/modules/system/lib/Drupal/system/Tests/Update/DependencyOrderingTest.php b/core/modules/system/lib/Drupal/system/Tests/Update/DependencyOrderingTest.php
index b007546..5667be0 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Update/DependencyOrderingTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Update/DependencyOrderingTest.php
@@ -19,7 +19,7 @@ class DependencyOrderingTest extends WebTestBase {
    *
    * @var array
    */
-  public static $modules = array('update_test_1', 'update_test_2', 'update_test_3');
+  public static $modules = array('update_test_0', 'update_test_1', 'update_test_2', 'update_test_3');
 
   public static function getInfo() {
     return array(
@@ -39,12 +39,12 @@ function setUp() {
    */
   function testUpdateOrderingSingleModule() {
     $starting_updates = array(
-      'update_test_1' => 8000,
+      'update_test_1' => 8001,
     );
     $expected_updates = array(
-      'update_test_1_update_8000',
       'update_test_1_update_8001',
       'update_test_1_update_8002',
+      'update_test_1_update_8003',
     );
     $actual_updates = array_keys(update_resolve_dependencies($starting_updates));
     $this->assertEqual($expected_updates, $actual_updates, 'Updates within a single module run in the correct order.');
@@ -55,14 +55,14 @@ function testUpdateOrderingSingleModule() {
    */
   function testUpdateOrderingModuleInterdependency() {
     $starting_updates = array(
-      'update_test_2' => 8000,
-      'update_test_3' => 8000,
+      'update_test_2' => 8001,
+      'update_test_3' => 8001,
     );
     $update_order = array_keys(update_resolve_dependencies($starting_updates));
     // Make sure that each dependency is satisfied.
-    $first_dependency_satisfied = array_search('update_test_2_update_8000', $update_order) < array_search('update_test_3_update_8000', $update_order);
+    $first_dependency_satisfied = array_search('update_test_2_update_8001', $update_order) < array_search('update_test_3_update_8001', $update_order);
     $this->assertTrue($first_dependency_satisfied, 'The dependency of the second module on the first module is respected by the update function order.');
-    $second_dependency_satisfied = array_search('update_test_3_update_8000', $update_order) < array_search('update_test_2_update_8001', $update_order);
+    $second_dependency_satisfied = array_search('update_test_3_update_8001', $update_order) < array_search('update_test_2_update_8002', $update_order);
     $this->assertTrue($second_dependency_satisfied, 'The dependency of the first module on the second module is respected by the update function order.');
   }
 }
diff --git a/core/modules/system/lib/Drupal/system/Tests/Update/InvalidUpdateHook.php b/core/modules/system/lib/Drupal/system/Tests/Update/InvalidUpdateHook.php
new file mode 100644
index 0000000..460d03f
--- /dev/null
+++ b/core/modules/system/lib/Drupal/system/Tests/Update/InvalidUpdateHook.php
@@ -0,0 +1,63 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\system\Tests\Update\InvalidUpdateHook.
+ */
+
+namespace Drupal\system\Tests\Update;
+
+use Drupal\simpletest\WebTestBase;
+use Drupal\Core\Extension\ExtensionSchemaVersionException;
+
+/**
+ * Tests for missing update dependencies.
+ */
+class InvalidUpdateHook extends WebTestBase {
+
+  /**
+   * Modules to enable.
+   *
+   * @var array
+   */
+  public static $modules = array('update_test_invalid_hook', 'update_script_test', 'dblog');
+
+  /**
+   * URL for the upgrade script.
+   *
+   * @var string
+   */
+  private $update_url;
+
+  /**
+   * A user account with upgrade permission.
+   *
+   * @var \Drupal\user\UserInterface
+   */
+  private $update_user;
+
+  public static function getInfo() {
+    return array(
+      'name' => 'Invalid update hook',
+      'description' => 'Tests that a module implementing hook_update_8000() causes an error to be displayed on update.',
+      'group' => 'Update API',
+    );
+  }
+
+  function setUp() {
+    parent::setUp();
+    require_once DRUPAL_ROOT . '/core/includes/update.inc';
+
+    $this->update_url = $GLOBALS['base_url'] . '/core/update.php';
+    $this->update_user = $this->drupalCreateUser(array('administer software updates'));
+  }
+
+  function testInvalidUpdateHook() {
+    // Confirm that a module with hook_update_8000() cannot be updated.
+    $this->drupalLogin($this->update_user);
+    $this->drupalGet($this->update_url);
+    $this->drupalPostForm($this->update_url, array(), t('Continue'), array('external' => TRUE));
+    $this->assertText(t('Some of the pending updates cannot be applied because their dependencies were not met.'));
+  }
+
+}
diff --git a/core/modules/system/lib/Drupal/system/Tests/Update/UpdateScriptTest.php b/core/modules/system/lib/Drupal/system/Tests/Update/UpdateScriptTest.php
index d581147..463b486 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Update/UpdateScriptTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Update/UpdateScriptTest.php
@@ -41,6 +41,28 @@ function setUp() {
   }
 
   /**
+   * Tests that updates from schema versions prior to 8000 are prevented.
+   */
+  function testInvalidMigration() {
+    // Mock a D7 system table so that the schema value of the system module
+    // can be retrieved.
+    db_create_table('system', $this->getSystemSchema());
+    // Assert that the table exists.
+    $this->assertTrue(db_table_exists('system'), 'The table exists.');
+    // Insert a value for the system module.
+    db_insert('system')
+      ->fields(array(
+        'name' => 'system',
+        'schema_version' => 7000,
+      ))
+      ->execute();
+    $system_schema = db_query('SELECT schema_version FROM {system} WHERE name = :system', array(':system' => 'system'))->fetchField();
+    $this->drupalGet($this->update_url, array('external' => TRUE));
+    $text = 'Your system schema version is ' . $system_schema . '. Updating directly from a schema version prior to 8000 is not supported. You must <a href="https://drupal.org/node/2179269">migrate your site to Drupal 8</a> first.';
+    $this->assertRaw($text, 'Updates from schema versions prior to 8000 are prevented.');
+  }
+
+  /**
    * Tests access to the update script.
    */
   function testUpdateAccess() {
@@ -99,7 +121,7 @@ function testRequirements() {
     $this->assertNoText('This is a requirements warning provided by the update_script_test module.');
     $this->drupalPostForm(NULL, array(), t('Continue'));
     $this->drupalPostForm(NULL, array(), 'Apply pending updates');
-    $this->assertText(t('The update_script_test_update_8000() update was executed successfully.'), 'End of update process was reached.');
+    $this->assertText(t('The update_script_test_update_8001() update was executed successfully.'), 'End of update process was reached.');
     // Confirm that all caches were cleared.
     $this->assertText(t('hook_cache_flush() invoked for update_script_test.module.'), 'Caches were cleared after resolving a requirements warning and applying updates.');
 
@@ -165,20 +187,46 @@ function testNoUpdateFunctionality() {
    * Tests update.php after performing a successful update.
    */
   function testSuccessfulUpdateFunctionality() {
-    drupal_set_installed_schema_version('update_script_test', drupal_get_installed_schema_version('update_script_test') - 1);
+    $schema_version = drupal_get_installed_schema_version('update_script_test');
+    $this->assertEqual($schema_version, 8001, 'update_script_test is initially installed with schema version 8001.');
+
+    // Set the installed schema version to one less than the current update.
+    drupal_set_installed_schema_version('update_script_test', $schema_version - 1);
+    $schema_version = drupal_get_installed_schema_version('update_script_test', TRUE);
+    $this->assertEqual($schema_version, 8000, 'update_script_test schema version overridden to 8000.');
+
     // Click through update.php with 'administer software updates' permission.
     $this->drupalLogin($this->update_user);
     $this->drupalPostForm($this->update_url, array(), t('Continue'), array('external' => TRUE));
     $this->drupalPostForm(NULL, array(), t('Apply pending updates'));
+
+    // Verify that updates were completed successfully.
     $this->assertText('Updates were attempted.');
     $this->assertLink('site');
+    $this->assertText('The update_script_test_update_8001() update was executed successfully.');
+
+    // Verify that no 7.x updates were run.
+    $this->assertNoText('The update_script_test_update_7200() update was executed successfully.');
+    $this->assertNoText('The update_script_test_update_7201() update was executed successfully.');
+
+    // Verify that there are no links to different parts of the workflow.
     $this->assertNoLink('Administration pages');
     $this->assertNoLinkByHref('update.php', 0);
     $this->assertNoLink('logged');
+
+    // Verify the front page can be visited following the upgrade.
     $this->clickLink('Front page');
     $this->assertResponse(200);
 
-    drupal_set_installed_schema_version('update_script_test', drupal_get_installed_schema_version('update_script_test') - 1);
+    // Reset the static cache to ensure we have the most current setting.
+    $schema_version = drupal_get_installed_schema_version('update_script_test', TRUE);
+    $this->assertEqual($schema_version, 8001, 'update_script_test schema version is 8001 after updating.');
+
+    // Set the installed schema version to one less than the current update.
+    drupal_set_installed_schema_version('update_script_test', $schema_version - 1);
+    $schema_version = drupal_get_installed_schema_version('update_script_test', TRUE);
+    $this->assertEqual($schema_version, 8000, 'update_script_test schema version overridden to 8000.');
+
     // Click through update.php with 'access administration pages' and
     // 'access site reports' permissions.
     $admin_user = $this->drupalCreateUser(array('administer software updates', 'access administration pages', 'access site reports'));
@@ -192,4 +240,78 @@ function testSuccessfulUpdateFunctionality() {
     $this->clickLink('Administration pages');
     $this->assertResponse(200);
   }
+
+  /**
+   * Returns the Drupal 7 system table schema.
+   */
+  public function getSystemSchema() {
+    return array(
+      'description' => "A list of all modules, themes, and theme engines that are or have been installed in Drupal's file system.",
+      'fields' => array(
+        'filename' => array(
+          'description' => 'The path of the primary file for this item, relative to the Drupal root; e.g. modules/node/node.module.',
+          'type' => 'varchar',
+          'length' => 255,
+          'not null' => TRUE,
+          'default' => '',
+        ),
+        'name' => array(
+          'description' => 'The name of the item; e.g. node.',
+          'type' => 'varchar',
+          'length' => 255,
+          'not null' => TRUE,
+          'default' => '',
+        ),
+        'type' => array(
+          'description' => 'The type of the item, either module, theme, or theme_engine.',
+          'type' => 'varchar',
+          'length' => 12,
+          'not null' => TRUE,
+          'default' => '',
+        ),
+        'owner' => array(
+          'description' => "A theme's 'parent' . Can be either a theme or an engine.",
+          'type' => 'varchar',
+          'length' => 255,
+          'not null' => TRUE,
+          'default' => '',
+        ),
+        'status' => array(
+          'description' => 'Boolean indicating whether or not this item is enabled.',
+          'type' => 'int',
+          'not null' => TRUE,
+          'default' => 0,
+        ),
+        'bootstrap' => array(
+          'description' => "Boolean indicating whether this module is loaded during Drupal's early bootstrapping phase (e.g. even before the page cache is consulted).",
+          'type' => 'int',
+          'not null' => TRUE,
+          'default' => 0,
+        ),
+        'schema_version' => array(
+          'description' => "The module's database schema version number. -1 if the module is not installed (its tables do not exist); \Drupal::CORE_MINIMUM_SCHEMA_VERSION or the largest N of the module's hook_update_N() function that has either been run or existed when the module was first installed.",
+          'type' => 'int',
+          'not null' => TRUE,
+          'default' => -1,
+          'size' => 'small',
+        ),
+        'weight' => array(
+          'description' => "The order in which this module's hooks should be invoked relative to other modules. Equal-weighted modules are ordered by name.",
+          'type' => 'int',
+          'not null' => TRUE,
+          'default' => 0,
+        ),
+        'info' => array(
+          'description' => "A serialized array containing information from the module's .info file; keys can include name, description, package, version, core, dependencies, and php.",
+          'type' => 'blob',
+          'not null' => FALSE,
+        ),
+      ),
+      'primary key' => array('filename'),
+      'indexes' => array(
+        'system_list' => array('status', 'bootstrap', 'type', 'weight', 'name'),
+        'type_name' => array('type', 'name'),
+      ),
+    );
+  }
 }
diff --git a/core/modules/system/lib/Drupal/system/Tests/Update/UpdatesWith7x.php b/core/modules/system/lib/Drupal/system/Tests/Update/UpdatesWith7x.php
new file mode 100644
index 0000000..4d85f9d
--- /dev/null
+++ b/core/modules/system/lib/Drupal/system/Tests/Update/UpdatesWith7x.php
@@ -0,0 +1,63 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\system\Tests\Update\UpdatesWith7x.
+ */
+
+namespace Drupal\system\Tests\Update;
+
+use Drupal\simpletest\WebTestBase;
+
+/**
+ * Tests for missing update dependencies.
+ */
+class UpdatesWith7x extends WebTestBase {
+
+  /**
+   * Modules to enable.
+   *
+   * @var array
+   */
+  public static $modules = array('update_test_with_7x');
+
+  /**
+   * The URL for the update page.
+   */
+  private $update_url;
+
+  /**
+   * An administrative user.
+   */
+  private $update_user;
+
+  public static function getInfo() {
+    return array(
+      'name' => '7.x update hooks',
+      'description' => 'Tests that the minimum schema version is correct even if only 7.x update hooks are retained .',
+      'group' => 'Update API',
+    );
+  }
+
+  function setUp() {
+    parent::setUp();
+    require_once DRUPAL_ROOT . '/core/includes/update.inc';
+    $this->update_url = $GLOBALS['base_url'] . '/core/update.php';
+    $this->update_user = $this->drupalCreateUser(array('administer software updates'));
+  }
+
+  function testWith7x() {
+    // Ensure that the minimum schema version is 8000, despite 7200 update
+    // hooks and a 7XXX hook_update_last_removed().
+    $this->assertEqual(drupal_get_installed_schema_version('update_test_with_7x'), 8000);
+
+    // Try to manually set the schema version to 7110 and ensure that no
+    // updates are allowed.
+    drupal_set_installed_schema_version('update_test_with_7x', 7110);
+
+    // Click through update.php with 'administer software updates' permission.
+    $this->drupalLogin($this->update_user);
+    $this->drupalPostForm($this->update_url, array(), t('Continue'), array('external' => TRUE));
+    $this->assertText(t('Some of the pending updates cannot be applied because their dependencies were not met.'));
+  }
+}
diff --git a/core/modules/system/system.api.php b/core/modules/system/system.api.php
index 6c83611..a3b9d67 100644
--- a/core/modules/system/system.api.php
+++ b/core/modules/system/system.api.php
@@ -2258,22 +2258,24 @@ function hook_install() {
  * The numbers are composed of three parts:
  * - 1 digit for Drupal core compatibility.
  * - 1 digit for your module's major release version (e.g., is this the 8.x-1.*
- *   (1) or 8.x-2.* (2) series of your module?). This digit should be 0 for
- *   initial porting of your module to a new Drupal core API.
- * - 2 digits for sequential counting, starting with 00.
+ *   (1) or 8.x-2.* (2) series of your module).
+ * - 2 digits for sequential counting, starting with 01.
  *
  * Examples:
- * - mymodule_update_8000(): This is the required update for mymodule to run
- *   with Drupal core API 8.x when upgrading from Drupal core API 7.x.
  * - mymodule_update_8100(): This is the first update to get the database ready
  *   to run mymodule 8.x-1.*.
  * - mymodule_update_8200(): This is the first update to get the database ready
- *   to run mymodule 8.x-2.*. Users can directly update from 7.x-2.* to 8.x-2.*
- *   and they get all 80xx and 82xx updates, but not 81xx updates, because
- *   those reside in the 8.x-1.x branch only.
+ *   to run mymodule 8.x-2.*.
+ *
+ * As of Drupal 8.0, the database upgrade system no longer supports updating a
+ * database from an earlier major version of Drupal: update.php can be used to
+ * upgrade from 7.x-1.x to 7.x-2.x, or 8.x-1.x to 8.x-2.x, but not from 7.x to
+ * 8.x. Therefore, only update hooks numbered 8001 or later will run for
+ * Drupal 8. 8000 is reserved for the minimum core schema version and defining
+ * mymodule_update_8000() will result in an exception. Use the
+ * @link https://drupal.org/node/2127611 Migration API @endlink instead to
+ * migrate data from an earlier major version of Drupal.
  *
- * A good rule of thumb is to remove updates older than two major releases of
- * Drupal. See hook_update_last_removed() to notify Drupal about the removals.
  * For further information about releases and release numbers see:
  * @link http://drupal.org/node/711070 Maintaining a drupal.org project with Git @endlink
  *
@@ -2398,21 +2400,21 @@ function hook_update_N(&$sandbox) {
  * @see hook_update_N()
  */
 function hook_update_dependencies() {
-  // Indicate that the mymodule_update_8000() function provided by this module
-  // must run after the another_module_update_8002() function provided by the
+  // Indicate that the mymodule_update_8001() function provided by this module
+  // must run after the another_module_update_8003() function provided by the
   // 'another_module' module.
-  $dependencies['mymodule'][8000] = array(
-    'another_module' => 8002,
+  $dependencies['mymodule'][8001] = array(
+    'another_module' => 8003,
   );
-  // Indicate that the mymodule_update_8001() function provided by this module
-  // must run before the yet_another_module_update_8004() function provided by
+  // Indicate that the mymodule_update_8002() function provided by this module
+  // must run before the yet_another_module_update_8005() function provided by
   // the 'yet_another_module' module. (Note that declaring dependencies in this
   // direction should be done only in rare situations, since it can lead to the
   // following problem: If a site has already run the yet_another_module
   // module's database updates before it updates its codebase to pick up the
   // newest mymodule code, then the dependency declared here will be ignored.)
-  $dependencies['yet_another_module'][8004] = array(
-    'mymodule' => 8001,
+  $dependencies['yet_another_module'][8005] = array(
+    'mymodule' => 8002,
   );
   return $dependencies;
 }
@@ -2434,9 +2436,9 @@ function hook_update_dependencies() {
  * @see hook_update_N()
  */
 function hook_update_last_removed() {
-  // We've removed the 5.x-1.x version of mymodule, including database updates.
-  // The next update function is mymodule_update_5200().
-  return 5103;
+  // We've removed the 8.x-1.x version of mymodule, including database updates.
+  // The next update function is mymodule_update_8200().
+  return 8103;
 }
 
 /**
@@ -3232,10 +3234,10 @@ function hook_link_alter(&$variables) {
  * API function including the module's name.
  *
  * Examples:
- * - _update_7000_mymodule_save(): This function performs a save operation
- *   without invoking any hooks using the 7.x schema.
- * - _update_8000_mymodule_save(): This function performs the same save
- *   operation using the 8.x schema.
+ * - _update_8001_mymodule_save(): This function performs a save operation
+ *   without invoking any hooks using the original 8.x schema.
+ * - _update_8002_mymodule_save(): This function performs the same save
+ *   operation using an updated 8.x schema.
  *
  * The utility function should not invoke any hooks, and should perform database
  * operations using functions from the
@@ -3244,69 +3246,69 @@ function hook_link_alter(&$variables) {
  *
  * If a change to the schema necessitates a change to the utility function, a
  * new function should be created with a name based on the version of the schema
- * it acts on. See _update_8000_bar_get_types() and _update_8001_bar_get_types()
+ * it acts on. See _update_8002_bar_get_types() and _update_8003_bar_get_types()
  * in the code examples that follow.
  *
  * For example, foo.install could contain:
  * @code
  * function foo_update_dependencies() {
- *   // foo_update_8010() needs to run after bar_update_8000().
+ *   // foo_update_8010() needs to run after bar_update_8002().
  *   $dependencies['foo'][8010] = array(
- *     'bar' => 8000,
+ *     'bar' => 8002,
  *   );
  *
- *   // foo_update_8036() needs to run after bar_update_8001().
+ *   // foo_update_8036() needs to run after bar_update_8003().
  *   $dependencies['foo'][8036] = array(
- *     'bar' => 8001,
+ *     'bar' => 8003,
  *   );
  *
  *   return $dependencies;
  * }
  *
- * function foo_update_8000() {
+ * function foo_update_8002() {
  *   // No updates have been run on the {bar_types} table yet, so this needs
- *   // to work with the 7.x schema.
- *   foreach (_update_7000_bar_get_types() as $type) {
+ *   // to work with the original 8.x schema.
+ *   foreach (_update_8001_bar_get_types() as $type) {
  *     // Rename a variable.
  *   }
  * }
  *
  * function foo_update_8010() {
- *    // Since foo_update_8010() is going to run after bar_update_8000(), it
+ *    // Since foo_update_8010() is going to run after bar_update_8002(), it
  *    // needs to operate on the new schema, not the old one.
- *    foreach (_update_8000_bar_get_types() as $type) {
+ *    foreach (_update_8002_bar_get_types() as $type) {
  *      // Rename a different variable.
  *    }
  * }
  *
  * function foo_update_8036() {
- *   // This update will run after bar_update_8001().
- *   foreach (_update_8001_bar_get_types() as $type) {
+ *   // This update will run after bar_update_8003().
+ *   foreach (_update_8003_bar_get_types() as $type) {
  *   }
  * }
  * @endcode
  *
  * And bar.install could contain:
  * @code
- * function bar_update_8000() {
+ * function bar_update_8002() {
  *   // Type and bundle are confusing, so we renamed the table.
  *   db_rename_table('bar_types', 'bar_bundles');
  * }
  *
- * function bar_update_8001() {
+ * function bar_update_8003() {
  *   // Database table names should be singular when possible.
  *   db_rename_table('bar_bundles', 'bar_bundle');
  * }
  *
- * function _update_7000_bar_get_types() {
+ * function _update_8001_bar_get_types() {
  *   db_query('SELECT * FROM {bar_types}')->fetchAll();
  * }
  *
- * function _update_8000_bar_get_types() {
+ * function _update_8002_bar_get_types() {
  *   db_query('SELECT * FROM {bar_bundles'})->fetchAll();
  * }
  *
- * function _update_8001_bar_get_types() {
+ * function _update_8003_bar_get_types() {
  *   db_query('SELECT * FROM {bar_bundle}')->fetchAll();
  * }
  * @endcode
diff --git a/core/modules/system/tests/modules/update_script_test/update_script_test.install b/core/modules/system/tests/modules/update_script_test/update_script_test.install
index 594dfa3..0f1718d 100644
--- a/core/modules/system/tests/modules/update_script_test/update_script_test.install
+++ b/core/modules/system/tests/modules/update_script_test/update_script_test.install
@@ -38,8 +38,29 @@ function update_script_test_requirements($phase) {
 }
 
 /**
+ * Implements hook_update_last_removed().
+ */
+function update_script_test_update_last_removed() {
+  return 7110;
+}
+
+/**
+ * Dummy update_script_test update 7200.
+ */
+function update_script_test_update_7200() {
+  return 'The update_script_test_update_7200() update was executed successfully.';
+}
+
+/**
+ * Dummy update_script_test update 7201.
+ */
+function update_script_test_update_7201() {
+  return 'The update_script_test_update_7201() update was executed successfully.';
+}
+
+/**
  * Dummy update function to run during the tests.
  */
-function update_script_test_update_8000() {
-  return t('The update_script_test_update_8000() update was executed successfully.');
+function update_script_test_update_8001() {
+  return 'The update_script_test_update_8001() update was executed successfully.';
 }
diff --git a/core/modules/system/tests/modules/update_test_0/update_test_0.info.yml b/core/modules/system/tests/modules/update_test_0/update_test_0.info.yml
new file mode 100644
index 0000000..d9f07cd
--- /dev/null
+++ b/core/modules/system/tests/modules/update_test_0/update_test_0.info.yml
@@ -0,0 +1,7 @@
+name: 'Update test 0'
+type: module
+description: 'Support module for update testing.'
+package: Testing
+version: VERSION
+core: 8.x
+hidden: true
diff --git a/core/modules/system/tests/modules/update_test_0/update_test_0.install b/core/modules/system/tests/modules/update_test_0/update_test_0.install
new file mode 100644
index 0000000..16e0c38
--- /dev/null
+++ b/core/modules/system/tests/modules/update_test_0/update_test_0.install
@@ -0,0 +1,24 @@
+<?php
+
+/**
+ * @file
+ * Install, update and uninstall functions for the update_test_0 module.
+ */
+
+/**
+ * Dummy update_test_0 update 8001.
+ */
+function update_test_0_update_8001() {
+}
+
+/**
+ * Dummy update_test_0 update 8002.
+ */
+function update_test_0_update_8002() {
+}
+
+/**
+ * Dummy update_test_0 update 8003.
+ */
+function update_test_0_update_8003() {
+}
diff --git a/core/modules/system/tests/modules/update_test_0/update_test_0.module b/core/modules/system/tests/modules/update_test_0/update_test_0.module
new file mode 100644
index 0000000..b3d9bbc
--- /dev/null
+++ b/core/modules/system/tests/modules/update_test_0/update_test_0.module
@@ -0,0 +1 @@
+<?php
diff --git a/core/modules/system/tests/modules/update_test_1/update_test_1.install b/core/modules/system/tests/modules/update_test_1/update_test_1.install
index bfb7170..7ef5d98 100644
--- a/core/modules/system/tests/modules/update_test_1/update_test_1.install
+++ b/core/modules/system/tests/modules/update_test_1/update_test_1.install
@@ -15,33 +15,28 @@ function update_test_1_update_dependencies() {
   // These dependencies are used in combination with those declared in
   // update_test_2_update_dependencies() for the sole purpose of testing that
   // the results of hook_update_dependencies() are collected correctly and have
-  // the correct array structure. Therefore, we use updates from System module
-  // (which have already run), so that they will not get in the way of other
-  // tests.
-  $dependencies['system'][8000] = array(
-    // Compare to update_test_2_update_dependencies(), where the same System
-    // module update function is forced to depend on an update function from a
-    // different module. This allows us to test that both dependencies are
-    // correctly recorded.
-    'update_test_1' => 8000,
+  // the correct array structure. Therefore, we use updates from the
+  // update_test_0 module (which will be installed first) that they will not
+  // get in the way of other tests.
+  $dependencies['update_test_0'][8001] = array(
+    // Compare to update_test_2_update_dependencies(), where the same
+    // update_test_0 module update function is forced to depend on an update
+    // function from a different module. This allows us to test that both
+    // dependencies are correctly recorded.
+    'update_test_1' => 8001,
   );
-  $dependencies['system'][8001] = array(
-    // Compare to update_test_2_update_dependencies(), where the same System
-    // module update function is forced to depend on a different update
-    // function within the same module. This allows us to test that only the
-    // dependency on the higher-numbered update function is recorded.
-    'update_test_1' => 8002,
+  $dependencies['update_test_0'][8002] = array(
+    // Compare to update_test_2_update_dependencies(), where the same
+    // update_test_0 module update function is forced to depend on a
+    // different update function within the same module. This allows us to
+    // test that only the dependency on the higher-numbered update function
+    // is recorded.
+    'update_test_1' => 8003,
   );
   return $dependencies;
 }
 
 /**
- * Dummy update_test_1 update 8000.
- */
-function update_test_1_update_8000() {
-}
-
-/**
  * Dummy update_test_1 update 8001.
  */
 function update_test_1_update_8001() {
@@ -52,3 +47,9 @@ function update_test_1_update_8001() {
  */
 function update_test_1_update_8002() {
 }
+
+/**
+ * Dummy update_test_1 update 8003.
+ */
+function update_test_1_update_8003() {
+}
diff --git a/core/modules/system/tests/modules/update_test_2/update_test_2.install b/core/modules/system/tests/modules/update_test_2/update_test_2.install
index c73271a..4202720 100644
--- a/core/modules/system/tests/modules/update_test_2/update_test_2.install
+++ b/core/modules/system/tests/modules/update_test_2/update_test_2.install
@@ -14,33 +14,27 @@
 function update_test_2_update_dependencies() {
   // Combined with update_test_3_update_dependencies(), we are declaring here
   // that these two modules run updates in the following order:
-  // 1. update_test_2_update_8000()
-  // 2. update_test_3_update_8000()
-  // 3. update_test_2_update_8001()
-  // 4. update_test_2_update_8002()
-  $dependencies['update_test_2'][8001] = array(
-    'update_test_3' => 8000,
+  // 1. update_test_2_update_8001()
+  // 2. update_test_3_update_8001()
+  // 3. update_test_2_update_8002()
+  // 4. update_test_2_update_8003()
+  $dependencies['update_test_2'][8002] = array(
+    'update_test_3' => 8001,
   );
 
   // These are coordinated with the corresponding dependencies declared in
   // update_test_1_update_dependencies().
-  $dependencies['system'][8000] = array(
-    'update_test_2' => 8001,
+  $dependencies['update_test_0'][8001] = array(
+    'update_test_2' => 8002,
   );
-  $dependencies['system'][8001] = array(
-    'update_test_1' => 8001,
+  $dependencies['update_test_0'][8002] = array(
+    'update_test_1' => 8002,
   );
 
   return $dependencies;
 }
 
 /**
- * Dummy update_test_2 update 8000.
- */
-function update_test_2_update_8000() {
-}
-
-/**
  * Dummy update_test_2 update 8001.
  */
 function update_test_2_update_8001() {
@@ -51,3 +45,9 @@ function update_test_2_update_8001() {
  */
 function update_test_2_update_8002() {
 }
+
+/**
+ * Dummy update_test_2 update 8003.
+ */
+function update_test_2_update_8003() {
+}
diff --git a/core/modules/system/tests/modules/update_test_3/update_test_3.install b/core/modules/system/tests/modules/update_test_3/update_test_3.install
index 96830c8..ae2da4b 100644
--- a/core/modules/system/tests/modules/update_test_3/update_test_3.install
+++ b/core/modules/system/tests/modules/update_test_3/update_test_3.install
@@ -11,14 +11,14 @@
  * @see update_test_2_update_dependencies()
  */
 function update_test_3_update_dependencies() {
-  $dependencies['update_test_3'][8000] = array(
-    'update_test_2' => 8000,
+  $dependencies['update_test_3'][8001] = array(
+    'update_test_2' => 8001,
   );
   return $dependencies;
 }
 
 /**
- * Dummy update_test_3 update 8000.
+ * Dummy update_test_3 update 8001.
  */
-function update_test_3_update_8000() {
+function update_test_3_update_8001() {
 }
diff --git a/core/modules/system/tests/modules/update_test_invalid_hook/update_test_invalid_hook.info.yml b/core/modules/system/tests/modules/update_test_invalid_hook/update_test_invalid_hook.info.yml
new file mode 100644
index 0000000..a41ace9
--- /dev/null
+++ b/core/modules/system/tests/modules/update_test_invalid_hook/update_test_invalid_hook.info.yml
@@ -0,0 +1,7 @@
+name: 'Update test with an invalid hook_update_8000().'
+type: module
+description: 'Support module for update testing.'
+package: Testing
+version: VERSION
+core: 8.x
+hidden: true
diff --git a/core/modules/system/tests/modules/update_test_invalid_hook/update_test_invalid_hook.install b/core/modules/system/tests/modules/update_test_invalid_hook/update_test_invalid_hook.install
new file mode 100644
index 0000000..0a33af7
--- /dev/null
+++ b/core/modules/system/tests/modules/update_test_invalid_hook/update_test_invalid_hook.install
@@ -0,0 +1,12 @@
+<?php
+
+/**
+ * @file
+ * Install, update and uninstall functions for the update_test_invalid_hook module.
+ */
+
+/**
+ * Hook implementation using the reserved schema version 8000.
+ */
+function update_test_invalid_hook_update_8000() {
+}
diff --git a/core/modules/system/tests/modules/update_test_invalid_hook/update_test_invalid_hook.module b/core/modules/system/tests/modules/update_test_invalid_hook/update_test_invalid_hook.module
new file mode 100644
index 0000000..b3d9bbc
--- /dev/null
+++ b/core/modules/system/tests/modules/update_test_invalid_hook/update_test_invalid_hook.module
@@ -0,0 +1 @@
+<?php
diff --git a/core/modules/system/tests/modules/update_test_with_7x/update_test_with_7x.info.yml b/core/modules/system/tests/modules/update_test_with_7x/update_test_with_7x.info.yml
new file mode 100644
index 0000000..3ac4a35
--- /dev/null
+++ b/core/modules/system/tests/modules/update_test_with_7x/update_test_with_7x.info.yml
@@ -0,0 +1,7 @@
+name: 'Update test with 7.x updates left in the codebase.'
+type: module
+description: 'Support module for update testing.'
+package: Testing
+version: VERSION
+core: 8.x
+hidden: true
diff --git a/core/modules/system/tests/modules/update_test_with_7x/update_test_with_7x.install b/core/modules/system/tests/modules/update_test_with_7x/update_test_with_7x.install
new file mode 100644
index 0000000..fd5c65b
--- /dev/null
+++ b/core/modules/system/tests/modules/update_test_with_7x/update_test_with_7x.install
@@ -0,0 +1,25 @@
+<?php
+
+/**
+ * @file
+ * Install, update and uninstall functions for the update_test_with_7x module.
+ */
+
+/**
+ * Dummy update_test_with_7x update 7200.
+ */
+function update_test_with_7x_update_7200() {
+}
+
+/**
+ * Dummy update_test_with_7x update 7201.
+ */
+function update_test_with_7x_update_7201() {
+}
+
+/**
+ * Implements hook_update_last_removed().
+ */
+function update_test_with_7x_update_last_removed() {
+  return 7110;
+}
diff --git a/core/modules/system/tests/modules/update_test_with_7x/update_test_with_7x.module b/core/modules/system/tests/modules/update_test_with_7x/update_test_with_7x.module
new file mode 100644
index 0000000..b3d9bbc
--- /dev/null
+++ b/core/modules/system/tests/modules/update_test_with_7x/update_test_with_7x.module
@@ -0,0 +1 @@
+<?php
diff --git a/core/update.php b/core/update.php
index db6b9d2..6491afc 100644
--- a/core/update.php
+++ b/core/update.php
@@ -14,6 +14,7 @@
  * back to its original state!
  */
 
+use Drupal\Component\Utility\Settings;
 use Drupal\Core\DrupalKernel;
 use Drupal\Core\Update\Form\UpdateScriptSelectionForm;
 use Symfony\Component\HttpFoundation\Request;
@@ -285,53 +286,6 @@ function update_task_list($active = NULL) {
   drupal_add_region_content('sidebar_first', drupal_render($task_list));
 }
 
-/**
- * Returns and stores extra requirements that apply during the update process.
- */
-function update_extra_requirements($requirements = NULL) {
-  static $extra_requirements = array();
-  if (isset($requirements)) {
-    $extra_requirements += $requirements;
-  }
-  return $extra_requirements;
-}
-
-/**
- * Checks update requirements and reports errors and (optionally) warnings.
- *
- * @param $skip_warnings
- *   (optional) If set to TRUE, requirement warnings will be ignored, and a
- *   report will only be issued if there are requirement errors. Defaults to
- *   FALSE.
- */
-function update_check_requirements($skip_warnings = FALSE) {
-  // Check requirements of all loaded modules.
-  $requirements = \Drupal::moduleHandler()->invokeAll('requirements', array('update'));
-  $requirements += update_extra_requirements();
-  $severity = drupal_requirements_severity($requirements);
-
-  // If there are errors, always display them. If there are only warnings, skip
-  // them if the caller has indicated they should be skipped.
-  if ($severity == REQUIREMENT_ERROR || ($severity == REQUIREMENT_WARNING && !$skip_warnings)) {
-    update_task_list('requirements');
-    drupal_set_title('Requirements problem');
-    $status = array(
-      '#theme' => 'status_report',
-      '#requirements' => $requirements,
-    );
-    $status_report = drupal_render($status);
-    $status_report .= 'Check the messages and <a href="' . check_url(drupal_requirements_url($severity)) . '">try again</a>.';
-    drupal_add_http_header('Content-Type', 'text/html; charset=utf-8');
-    $maintenance_page = array(
-      '#theme' => 'maintenance_page',
-      '#content' => $status_report,
-    );
-    print drupal_render($maintenance_page);
-    exit();
-  }
-}
-
-
 // Some unavoidable errors happen because the database is not yet up-to-date.
 // Our custom error handler is not yet installed, so we just suppress them.
 ini_set('display_errors', FALSE);
@@ -343,12 +297,43 @@ function update_check_requirements($skip_warnings = FALSE) {
 require_once __DIR__ . '/includes/common.inc';
 require_once __DIR__ . '/includes/file.inc';
 require_once __DIR__ . '/includes/unicode.inc';
+require_once __DIR__ . '/includes/install.inc';
 require_once __DIR__ . '/includes/schema.inc';
-update_prepare_d8_bootstrap();
+// Bootstrap to configuration.
+drupal_bootstrap(DRUPAL_BOOTSTRAP_CONFIGURATION);
+
+// Bootstrap the database.
+require_once __DIR__ . '/includes/database.inc';
+
+// Updating from a site schema version prior to 8000 should block the update
+// process. Ensure that the site is not attempting to update a database
+// created in a previous version of Drupal.
+if (db_table_exists('system')) {
+  $system_schema = db_query('SELECT schema_version FROM {system} WHERE name = :system', array(':system' => 'system'))->fetchField();
+  if ($system_schema < \Drupal::CORE_MINIMUM_SCHEMA_VERSION) {
+    print 'Your system schema version is ' . $system_schema . '. Updating directly from a schema version prior to 8000 is not supported. You must <a href="https://drupal.org/node/2179269">migrate your site to Drupal 8</a> first.';
+    exit;
+  }
+}
+
+// Enable UpdateServiceProvider service overrides.
+// @see update_flush_all_caches()
+$GLOBALS['conf']['container_service_providers']['UpdateServiceProvider'] = 'Drupal\Core\DependencyInjection\UpdateServiceProvider';
+$GLOBALS['conf']['update_service_provider_overrides'] = TRUE;
+
+// module.inc is not yet loaded but there are calls to module_config_sort()
+// below.
+require_once __DIR__ . '/includes/module.inc';
+
+$settings = settings()->getAll();
+new Settings($settings);
+$kernel = new DrupalKernel('update', drupal_classloader(), FALSE);
+$kernel->boot();
+$request = Request::createFromGlobals();
+\Drupal::getContainer()->set('request', $request);
 
 // Determine if the current user has access to run update.php.
 drupal_bootstrap(DRUPAL_BOOTSTRAP_PAGE_CACHE);
-$request = \Drupal::request();
 
 require_once DRUPAL_ROOT . '/' . settings()->get('session_inc', 'core/includes/session.inc');
 drupal_session_initialize();
@@ -382,16 +367,14 @@ function update_check_requirements($skip_warnings = FALSE) {
 
   // Check the update requirements for Drupal. Only report on errors at this
   // stage, since the real requirements check happens further down.
+  // The request will exit() if any requirement violations are reported in the
+  // following function invocation.
   update_check_requirements(TRUE);
 
   // Redirect to the update information page if all requirements were met.
   install_goto('core/update.php?op=info');
 }
 
-// Allow update_fix_d8_requirements() to run before code that can break on a
-// Drupal 7 database.
-drupal_bootstrap(DRUPAL_BOOTSTRAP_CODE);
-update_fix_d8_requirements();
 drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL);
 drupal_maintenance_theme();
 
