From fb44b255c84c44dbf5be0565c6fa4a6bdc205c21 Mon Sep 17 00:00:00 2001
From: "A. Wilcox" <AWilcox@Wilcox-Tech.com>
Date: Sun, 23 Feb 2020 00:42:16 -0600
Subject: Qt UI: Initial draft of automatic partitioning system

---
 ui/qt5/horizonwizard.cc | 184 +++++++++++++++++++++++++++++++++++++++++-------
 ui/qt5/horizonwizard.hh |   9 +++
 2 files changed, 166 insertions(+), 27 deletions(-)

(limited to 'ui')

diff --git a/ui/qt5/horizonwizard.cc b/ui/qt5/horizonwizard.cc
index 6391937..d181a80 100644
--- a/ui/qt5/horizonwizard.cc
+++ b/ui/qt5/horizonwizard.cc
@@ -23,6 +23,7 @@
 #include <string>
 
 #ifdef HAS_INSTALL_ENV
+#   include <QProcess>
 #   include <libudev.h>
 #   include <net/if.h>      /* ifreq */
 #   include "commitpage.hh"
@@ -182,6 +183,34 @@ std::map<std::string, HorizonWizard::NetworkInterface> probe_ifaces(void) {
 #endif  /* HAS_INSTALL_ENV */
 
 
+#if defined(HAS_INSTALL_ENV) && defined(__powerpc64__)
+HorizonWizard::Subarch determinePowerPCPlatform() {
+    if(!QFile::exists("/tmp/horizon-platform")) {
+        QProcess p;
+        p.execute("horizon-ppc64-detect");
+    }
+
+    QFile platform;
+    platform.setFileName("/tmp/horizon-platform");
+    if(!platform.open(QFile::ReadOnly)) {
+        qDebug() << "can't read PowerPC platform type!";
+        return HorizonWizard::ppc64_pSeries;
+    }
+
+    QByteArray result = platform.readAll();
+    platform.close();
+
+    if(result.startsWith("PowerMac")) {
+        return HorizonWizard::ppc64_PowerMac;
+    } else if(result.startsWith("PowerNV")) {
+        return HorizonWizard::ppc64_PowerNV;
+    } else {
+        return HorizonWizard::ppc64_pSeries;
+}
+}
+#endif
+
+
 HorizonWizard::HorizonWizard(QWidget *parent) : QWizard(parent) {
     setWindowTitle(tr("Adélie Linux System Installation"));
 
@@ -189,6 +218,7 @@ HorizonWizard::HorizonWizard(QWidget *parent) : QWizard(parent) {
 
     mirror_domain = "distfiles.adelielinux.org";
     version = "stable";
+    subarch = NoSubarch;
     /* TODO XXX:
      * Determine which platform kernel is being used, if any (-power8 etc)
      * Determine hardware requirements (easy or mainline)
@@ -302,6 +332,7 @@ HorizonWizard::HorizonWizard(QWidget *parent) : QWizard(parent) {
 
 #   if defined(__powerpc64__)
     arch = ppc64;
+    subarch = determinePowerPCPlatform();
 #   elif defined(__powerpc__)
     arch = ppc;
 #   elif defined(__aarch64__)
@@ -343,6 +374,99 @@ char *encrypt_pw(const char *the_pw) {
 }
 
 
+/*! Determine the node name of a given partition. */
+QString nameForPartitionOnDisk(const std::string &dev, int part) {
+    const std::string maybe_p(::isdigit(dev[dev.size() - 1]) ? "p" : "");
+    std::string raw_root = dev + maybe_p + std::to_string(part);
+    return QString::fromStdString(raw_root);
+}
+
+
+/*! Determine the correct disk label based on the target platform. */
+QStringList eraseDiskForArch(const std::string raw_disk,
+                             HorizonWizard::Arch arch,
+                             HorizonWizard::Subarch subarch) {
+    QString disk = QString::fromStdString(raw_disk);
+
+    switch(arch) {
+    case HorizonWizard::aarch64:/* 64-bit ARM mostly uses GPT */
+    case HorizonWizard::x86_64: /* 64-bit Intel uses GPT */
+        return {QString{"disklabel %1 gpt"}.arg(disk)};
+    case HorizonWizard::ppc:    /* 32-bit PowerPC: we only support Power Mac */
+        return {QString{"disklabel %1 apm"}.arg(disk)};
+    case HorizonWizard::ppc64:  /* Complicated */
+        switch(subarch) {
+        case HorizonWizard::ppc64_PowerMac:
+            return {QString{"disklabel %1 apm"}.arg(disk)};
+        case HorizonWizard::ppc64_PowerNV:
+            return {QString{"disklabel %1 gpt"}.arg(disk)};
+        case HorizonWizard::ppc64_pSeries:
+        default:
+            return {QString{"disklabel %1 mbr"}.arg(disk)};
+        }
+    case HorizonWizard::armv7:  /* your guess is as good as mine */
+    case HorizonWizard::pmmx:   /* 32-bit Intel requires MBR */
+    case HorizonWizard::UnknownCPU: /* safe enough as a fallback */
+    default:
+        return {QString{"disklabel %1 mbr"}.arg(disk)};
+    }
+}
+
+
+/*! Determine the correct boot disk layout based on the target platform. */
+QStringList bootForArch(const std::string raw_disk, HorizonWizard::Arch arch,
+                        HorizonWizard::Subarch subarch, int *start) {
+    QString disk = QString::fromStdString(raw_disk);
+
+    switch(arch) {
+    case HorizonWizard::aarch64:/* 64-bit ARM: assume UEFI */
+        return {
+            QString{"partition %1 %2 256M esp"}.arg(disk).arg(*start),
+            QString{"fs %1 fat32"}.arg(nameForPartitionOnDisk(raw_disk, *start)),
+            QString{"mount %1 /boot/efi"}.arg(nameForPartitionOnDisk(raw_disk, (*start)++))
+        };
+    case HorizonWizard::x86_64: /* 64-bit Intel: support UEFI and BIOS */
+        return {
+            QString{"partition %1 %2 1M bios"}.arg(disk).arg((*start)++),
+            QString{"partition %1 %2 256M esp"}.arg(disk).arg(*start),
+            QString{"fs %1 fat32"}.arg(nameForPartitionOnDisk(raw_disk, *start)),
+            QString{"mount %1 /boot/efi"}.arg(nameForPartitionOnDisk(raw_disk, (*start)++))
+        };
+    case HorizonWizard::ppc:    /* 32-bit PowerPC: we only support Power Mac */
+        return {
+            QString{"partition %1 %2 16M boot"}.arg(disk).arg(*start),
+            QString{"fs %1 hfs+"}.arg(nameForPartitionOnDisk(raw_disk, *start)),
+            QString{"mount %1 /boot/grub"}.arg(nameForPartitionOnDisk(raw_disk, (*start)++))
+        };
+    case HorizonWizard::ppc64:  /* Complicated */
+        switch(subarch) {
+        case HorizonWizard::ppc64_PowerMac: /* as ppc32 */
+            return {
+                QString{"partition %1 %2 16M boot"}.arg(disk).arg(*start),
+                QString{"fs %1 hfs+"}.arg(nameForPartitionOnDisk(raw_disk, *start)),
+                QString{"mount %1 /boot/grub"}.arg(nameForPartitionOnDisk(raw_disk, (*start)++))
+            };
+        case HorizonWizard::ppc64_PowerNV:  /* doesn't need a separate /boot */
+            return {};
+        case HorizonWizard::ppc64_pSeries:  /* PReP boot partition */
+        default:
+            return {
+                QString{"partition %1 %2 10M prep"}.arg(disk).arg((*start)++),
+            };
+        }
+    case HorizonWizard::armv7:
+    case HorizonWizard::pmmx:   /* 32-bit Intel, bog standard GRUB */
+    case HorizonWizard::UnknownCPU: /* safe enough as a fallback */
+    default:
+        return {
+            QString{"partition %1 %2 256M boot"}.arg(disk).arg(*start),
+            QString{"fs %1 ext2"}.arg(nameForPartitionOnDisk(raw_disk, *start)),
+            QString{"mount %1 /boot"}.arg(nameForPartitionOnDisk(raw_disk, (*start)++))
+        };
+    }
+}
+
+
 QString HorizonWizard::toHScript() {
     QStringList lines;
 
@@ -407,34 +531,42 @@ QString HorizonWizard::toHScript() {
         break;
     }
 
-    if(chosen_disk.empty() || !auto_part) {
+    if(chosen_disk.empty()) {
+        lines << part_lines;
+    } else if(!auto_part) {
         lines << part_lines;
     } else {
-        /* XXX TODO: examples for thoughts on auto-partition setups are in
-         * architecture-specific comments below */
-        switch(arch) {
-        case aarch64:
-            /* Do GPT stuff */
-            break;
-        case armv7:
-            /* Run away */
-            break;
-        case pmmx:
-            /* Do MBR stuff */
-            break;
-        case ppc:
-            /* Do APM stuff */
-            break;
-        case ppc64:
-            /* Figure stuff out */
-            break;
-        case x86_64:
-            /* Do EFI */
-            break;
-        case UnknownCPU:
-            /* Probably MBR, as it's the most common. */
-            break;
+        /* We have a chosen disk, and will be automatically partitioning it. */
+        int start = 1;
+
+        if(erase) {
+            lines << eraseDiskForArch(chosen_disk, arch, subarch);
+        } else {
+#ifdef HAS_INSTALL_ENV
+            Disk *disk = nullptr;
+            for(auto &d_iter : disks) {
+                if(d_iter.node() == chosen_disk) {
+                    disk = &d_iter;
+                    break;
+                }
+            }
+            Q_ASSERT(disk != nullptr);
+            start = disk->partitions().size() + 1;
+#else  /* !HAS_INSTALL_ENV */
+            Q_ASSERT(false);
+#endif  /* HAS_INSTALL_ENV */
+        }
+
+        if(this->grub) {
+            lines << bootForArch(chosen_disk, arch, subarch, &start);
         }
+
+        /* Create the root partition */
+        lines << QString{"partition %1 %2 fill"}
+                 .arg(QString::fromStdString(chosen_disk)).arg(start);
+        QString root_part = nameForPartitionOnDisk(chosen_disk, start);
+        lines << QString{"fs %1 ext4"}.arg(root_part);
+        lines << QString{"mount %1 /"}.arg(root_part);
     }
 
     switch(this->pkgtype) {
@@ -544,8 +676,6 @@ QString HorizonWizard::toHScript() {
 }
 
 
-#include <iostream>
-
 void HorizonWizard::accept() {
     QFile file;
 #ifdef HAS_INSTALL_ENV
diff --git a/ui/qt5/horizonwizard.hh b/ui/qt5/horizonwizard.hh
index 33b980a..bf53ae0 100644
--- a/ui/qt5/horizonwizard.hh
+++ b/ui/qt5/horizonwizard.hh
@@ -103,6 +103,13 @@ public:
         UnknownCPU
     };
 
+    enum Subarch {
+        NoSubarch,
+        ppc64_PowerMac,
+        ppc64_pSeries,
+        ppc64_PowerNV
+    };
+
     enum PackageType {
         Standard,
         Mobile,
@@ -136,6 +143,8 @@ public:
     std::string version;
     /*! The architecture being installed. */
     Arch arch;
+    /*! If relevant, the subtype of the architecture being installed. */
+    Subarch subarch;
 #ifdef HAS_INSTALL_ENV
     /*! The disks present on this computer. */
     std::vector<Horizon::DiskMan::Disk> disks;
-- 
cgit v1.2.3-70-g09d2