diff --git a/core/modules/migrate_drupal/lib/Drupal/migrate_drupal/Form/MigrateDrupalUpgradeForm.php b/core/modules/migrate_drupal/lib/Drupal/migrate_drupal/Form/MigrateDrupalUpgradeForm.php
new file mode 100644
index 0000000..e7570fb
--- /dev/null
+++ b/core/modules/migrate_drupal/lib/Drupal/migrate_drupal/Form/MigrateDrupalUpgradeForm.php
@@ -0,0 +1,218 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\migrate_drupal\Form\MigrateDrupalUpgrade.
+ */
+
+namespace Drupal\migrate_drupal\Form;
+
+use Drupal\Component\Utility\MapArray;
+use Drupal\Core\Database\Database;
+use Drupal\Core\Entity\EntityStorageControllerInterface;
+use Drupal\Core\Form\FormBase;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+class MigrateDrupalUpgradeForm extends FormBase {
+
+  /**
+   * @var \Drupal\Core\Entity\EntityStorageControllerInterface
+   */
+  protected $storageController;
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getFormId() {
+    return 'migrate_drupal_upgrade_form';
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function buildForm(array $form, array &$form_state) {
+    // The multistep is for testing only. The final version will run a fixed
+    // set of migrations.
+
+    if (!isset($form_state['database'])) {
+      $form['description'] = array(
+        '#markup' => $this->t('Enter the database credentials for the legacy Drupal ' .
+          'site you are upgrading into this Drupal 8 instance.'),
+      );
+      // The following is stolen from install.core.inc. If the install process
+      // would use form classes (https://drupal.org/node/2112569), we could inherit.
+      global $databases;
+
+      $database = isset($databases['default']['default']) ? $databases['default']['default'] : array();
+
+      drupal_set_title(t('Database configuration'));
+
+      require_once DRUPAL_ROOT . '/core/includes/install.inc';
+      $drivers = drupal_get_database_types();
+      $drivers_keys = array_keys($drivers);
+
+      $form['driver'] = array(
+        '#type' => 'radios',
+        '#title' => t('Database type'),
+        '#required' => TRUE,
+        '#default_value' => !empty($database['driver']) ? $database['driver'] : current($drivers_keys),
+      );
+      if (count($drivers) == 1) {
+        $form['driver']['#disabled'] = TRUE;
+      }
+
+      // Add driver specific configuration options.
+      foreach ($drivers as $key => $driver) {
+        $form['driver']['#options'][$key] = $driver->name();
+
+        $form['settings'][$key] = $driver->getFormOptions($database);
+        $form['settings'][$key]['#prefix'] = '<h2 class="js-hide">' .
+          $this->t('@driver_name settings', array('@driver_name' => $driver->name())) . '</h2>';
+        $form['settings'][$key]['#type'] = 'container';
+        $form['settings'][$key]['#tree'] = TRUE;
+        $form['settings'][$key]['advanced_options']['#parents'] = array($key);
+        $form['settings'][$key]['#states'] = array(
+          'visible' => array(
+            ':input[name=driver]' => array('value' => $key),
+          )
+        );
+      }
+
+      $form['actions'] = array('#type' => 'actions');
+      $form['actions']['save'] = array(
+        '#type' => 'submit',
+        '#value' => t('Next'),
+        '#button_type' => 'primary',
+        '#limit_validation_errors' => array(
+          array('driver'),
+          array(isset($form_state['input']['driver']) ? $form_state['input']['driver'] : current($drivers_keys)),
+        ),
+      );
+    }
+    elseif (isset($form_state['step']) && $form_state['step'] == 'confirmation') {
+      Database::addConnectionInfo('migrate', 'default', $form_state['database']);
+      $version = $form_state['drupal_version'];
+      $form['description'] = array(
+        '#markup' => $this->t('We are importing from a site running Drupal version @version.',
+          array('@version' => $version)),
+      );
+      $migrations = $this->storageController()->loadMultiple();
+      // For now, identify the relevant migrations based on prefix.
+      $prefix = 'd' . $version . '_';
+      $options = array();
+      foreach (array_keys($migrations) as $machine_name) {
+        if (!strncmp($prefix, $machine_name, strlen($prefix))) {
+          $options[$machine_name] = $machine_name;
+        }
+      }
+      $form['migrations'] = array(
+        '#type' => 'checkboxes',
+        '#options' => $options,
+      );
+      $form['submit'] = array(
+        '#type' => 'submit',
+        '#value' => $this->t('Submit'),
+      );
+    }
+
+    return $form;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function validateForm(array &$form, array &$form_state) {
+    if (isset($form_state['values']['driver'])) {
+      // Verify we have a valid connection to a Drupal database supported for
+      // upgrade.
+      $driver = $form_state['values']['driver'];
+      $form_state['database'] = $form_state['values'][$driver];
+      $form_state['database']['driver'] = $driver;
+      // @todo: There should be a DrupalSqlBase class to use to determine the
+      // version.
+      try {
+        Database::addConnectionInfo('migrate', 'default', $form_state['database']);
+        $connection = Database::getConnection('default', 'migrate');
+      }
+      catch (\Exception $e) {
+        $message = t('Unable to connect to the source database. %message',
+          array('%message' => $e->getMessage()));
+        $this->setFormError(NULL, $form_state, $message);
+        return;
+      }
+      if (!$connection->schema()->tableExists('node')) {
+        $this->setFormError(NULL, $form_state, t('Source database does not ' .
+          'contain a Drupal installation.'));
+      }
+      // Note we check D8 first, because it's reintroduced the menu_router
+      // table we have used as the signature of D6.
+      elseif ($connection->schema()->tableExists('key_value')) {
+        $this->setFormError(NULL, $form_state, t('Upgrade from this version ' .
+                  'of Drupal is not supported.'));
+      }
+      elseif ($connection->schema()->tableExists('filter_format')) {
+        $form_state['drupal_version'] = 7;
+      }
+      elseif ($connection->schema()->tableExists('menu_router')) {
+        $form_state['drupal_version'] = 6;
+      }
+      else {
+        $this->setFormError(NULL, $form_state, t('Upgrade from this version ' .
+          'of Drupal is not supported.'));
+      }
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function submitForm(array &$form, array &$form_state) {
+    if (isset($form_state['values']['driver'])) {
+      $form_state['rebuild'] = TRUE;
+      $form_state['step'] = 'confirmation';
+    }
+    elseif ($form_state['step'] == 'confirmation') {
+      $migration_ids = array_keys(array_filter($form_state['values']['migrations']));
+      $batch = array(
+        'title' => t('Running migrations'),
+        'operations' => array(
+          array(array('Drupal\migrate_drupal\MigrateDrupalRunBatch', 'run'), array($migration_ids, $form_state['database'])),
+        ),
+        'finished' => array('Drupal\migrate_drupal\MigrateDrupalRunBatch', 'finished'),
+        'progress_message' => '',
+        'init_message' => t('Processing migration @num of @max.', array('@num' => '1', '@max' => count($migration_ids))),
+      );
+      $this->batchSet($batch);
+    }
+  }
+
+  /**
+   * Set a batch.
+   *
+   * @param $batch
+   */
+  protected function batchSet($batch) {
+    batch_set($batch);
+  }
+
+  /**
+   * @return EntityStorageControllerInterface
+   */
+  protected function storageController() {
+    if (!isset($this->storageController)) {
+      $this->storageController = \Drupal::entityManager()->getStorageController('migration');
+    }
+    return $this->storageController;
+  }
+
+  /**
+   * Returns the properties to be serialized
+   *
+   * @return array
+   */
+  public function __sleep() {
+    // This apparently contains a PDOStatement somewhere.
+    unset($this->storageController);
+    return parent::__sleep();
+  }
+}
diff --git a/core/modules/migrate_drupal/migrate_drupal.routing.yml b/core/modules/migrate_drupal/migrate_drupal.routing.yml
index a7e8fef..73f5b20 100644
--- a/core/modules/migrate_drupal/migrate_drupal.routing.yml
+++ b/core/modules/migrate_drupal/migrate_drupal.routing.yml
@@ -5,3 +5,10 @@ migrate_drupal.run:
     _title: 'Run'
   requirements:
     _permission: 'administer site configuration'
+migrate_drupal.upgrade:
+  path: '/upgrade'
+  defaults:
+    _form: '\Drupal\migrate_drupal\Form\MigrateDrupalUpgradeForm'
+    _title: 'Upgrade Drupal'
+  requirements:
+    _permission: 'administer site configuration'
