Thursday, February 10, 2022

Over my dead eMMc...

You find yourself in an impossible situation.  Impossible in that, it's never going to happen to you.

Until it happens.


You have a Chromebook that will not boot.  You discover the internal eMMC is defunct.  Cracking it open, you discover the inevitable truth; the eMMC is soldered on.  

After removing the WP screw, you succeed in preparing a flash drive that includes MrChromebox's BIOS for the Chromebook and a few moments later, it reboots and splashes a welcoming running rabbit.

Your favorite Linux distro now boots from a USB drive and a SD-CARD. But, you now have a new problem. 

The ex-Chromebook will not suspend because the internal eMMC can't be communicated with in full, and inhibits ACPI suspend.  

What now?

You learn by trial and error that after a warm reboot from a successful initramfs prompt, the internal  eMMC is temporarily out of the way, and suspend works.  

You now face the challenge of how to make that eMMC disappear without physical intervention.

You fiddle around with the init ramfs and arrive at a solution.  You realize you're no expert, but it suffices.

You make the business portion of this bundle communicate a shutdown to plymouth, which disrupts any cryptsetup (decrypt password prompts) and follow with a call to systemctl to start the shutdown.target.  An older iteration tried using pkill ^plymouth and a reboot command, but after realizing calling binaries directly is a tad abrupt, you fall back on using modernized system administrator techniques.

You drop this in /usr/lib/systemd/system

#  SPDX-License-Identifier: LGPL-2.1-or-later
#
#  This file is part of systemd.
#
#  systemd is free software; you can redistribute it and/or modify it
#  under the terms of the GNU Lesser General Public License as published by
#  the Free Software Foundation; either version 2.1 of the License, or
#  (at your option) any later version.

[Unit]
Description=Reboot the system if a more than one mmc is found.
Documentation=man:man
DefaultDependencies=no
After=systemd-udev-settle.service
Conflicts=cryptsetup.target
Before=lvm2-activation-early.service cryptsetup.target local-fs-pre.target dmraid-activation.service
Wants=systemd-udev-settle.service

[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/usr/lib/systemd/deadmmcreboot

[Install]
WantedBy=initrd.target

And you drop this in /usr/lib/systemd

#!/usr/bin/bash
# 2022-02-06 This ASUS C300 chromebook landing in my possession
# has a defunct internal eMMC. On cold boot, the eMMC can be
# read from the BIOS and even reaches a GRUB menu, and after
# the eMMC is enumerated by the Linux driver, a subsequent
# reboot appears to take the eMMC offline for future warm
# state reboots.  This helper attempts to detect if the eMMC
# is present and reboot at least once.
printMatch () {
    echo "Looks like this list qualifies for a reboot:"
    echo "  $(dmesg | grep mmcblk.:)"
  }
plymouthShutdown () {
    plymouth change-mode --shutdown
   }

for W in $(cat /proc/cmdline) 
  do
  IFS="="
  for X in ${W[@]}
    do 
    if [ ! -z $Y ]; then 
      if [ -e "/dev/${X}" ]; then
        printMatch
        plyouthShutdown
        systemctl start reboot.target
        fi
      unset Y
      fi
    if [ $X = "deadmmc" ]; then
      Y=1
      fi
    done
  done
# A crude way to determine if more than one mmc is detected.
# A fall-through in case the system is not configured with
# a kernel parameter.
# In the case of an ASUS C300 Chromebook with a defunct
# mmc, the kernel enumerates the internal eMMC as 
# mmcblk1
if [ ! -z "$(ls /dev/mmcblk0)" ] && [ ! -z "$(ls /dev/mmcblk1)" ]; then
  echo "Example: vmlinuz ... deadmmc=mmcblk1"
  printMatch
  plymouthShutdown
  systemctl start reboot.target
  fi

You create a directory 02deadmmcreboot under /usr/lib/dracut/modules.d.  Nearly done, you drop this in /usr/lib/dracut/modules.d/02deadmmcreboot

#!/usr/bin/bash

# called by dracut
check() {
    return 0
}

# called by dracut
depends() {
    return 0
}

# called by dracut
installkernel() {
    return 0
    local _arch=${DRACUT_ARCH:-$(uname -m)}

    instmods mmc_block sdhci_acpi sdhci_pci sdhci cqhci mmc_core
    hostonly="" instmods mmc_block mmc_core

    dracut_instmods -o -s ${_funcs} =drivers/mmc 
    return 0
}

# called by dracut
cmdline() {
    return 0
}

# called by dracut
install() {
    inst_simple "$systemdsystemunitdir"/deadmmcreboot.service
    inst_simple "$systemdutildir"/deadmmcreboot
    $SYSTEMCTL -q --root "$initdir" enable "deadmmcreboot.service"
}

Finally you run dracut.  
Because you weren't sure if this was going to work for all dead eMMCs, you remember that you could also try a boot parameter of deadmmc=mmcblk1 just in case, and add that to /etc/default/grub's default options.


Float this idea...

Since the early 1980's, it was known the floating bus in the Apple II would hold the value of a recently sampled value of memory displayed on screen.  When it comes to those who have taken advantage of the floating bus' availability, French Touch, deater (VMW), and John Brooks (VidHD) come to mind.  

One of the issues in using the floating bus is its availability across the various family of Apple II's.  On a //e, register $C022 will return a floating bus value, but on a IIGS it will not.  The IIGS and the //c use a number of the $C0xx IO addresses for newer purposes, and are otherwise unused in a II, II+ and //e.  

Mixed video modes are one of the neat things that can be made using the floating bus. I tested to see if I could reliably sync up the signal to provide any combination of mixed modes as seen in demos by French Touch and deater. I succeeded in making a program that reads the keyboard and based on user input, toggles the desired soft switch for the preferred mode while swapping every other scanline from page 1 and page 2.  Another program uses pages of 256-bytes to indicate any one of four IO registers to read, again, unique for page 1 and page 2.  Both work well enough for my exploration, but more ideas came to mind with regard to the floating bus.

I read that only if an expandable II series slots are not fully populated, it will reflect a recent video signal byte on the floating bus. If a slot is not populated, the address space for that card will be filled with floating bus values; but for the IIgs and some emulators, the floating bus is not exposed, rather just zeroes.  I have not tested if this also holds true for when the slot's expansion ROM space ($C800 - $CFFF).  For the sake of this exercise, slot 1 is vacant and on a //e, if the firmware is not enabled and the slot is vacant, $C100-$C1FF will take on the values of the floating bus.  This can be seen at a monitor prompt (CALL-151 from BASIC), and issuing C100L.

Slot ROM, either in the specific range, or in the expansion ROM address range, can it be used to invoke code? That question came to mind.

  • Can the displayed video contents of the floating bus be executed as code?

The answer is yes. There is a one caveat.  This caveat could be used to confound a person debugging code.  Not all instructions have a linear cycle count ratio to the number of bytes the instruction and data consume.  For example, a LDA $C030 (AD 30 C0) takes four cycles; however, the video refresh will pass over four bytes even though the instruction only consumes three.  A new question comes to mind.

  • What happens to the byte after the 2nd data byte of a LDA $absolute instruction?

At this time, I have not sought an answer to this question. I suspect the PC is now at the fifth byte and will direct the processor to that byte as the next opcode.

There is one another conditional caveat, and that is the hardware on which the scenario is applied.  On a physical enhanced Apple //e, the PC correctly fetches data for the instruction in at least some situations, such as a JMP (opcode $4C). In AppleWin 1.30, only zeroes are fetched for a JMP command. If at address $0000 a JMP $0300 (0: 4c 00 03), and if text page 2 is used for this example, and the text page contains all zeroes (800:0 m 801<800.bfem) except for address $800, and $800 has the JMP opcode (800: 4c), and the break vector is set to an address with a floating bus ($c030 on a //e), and text page 2 is actively displayed (can be lo-res but not hires), then if a BRK is executed (801g), the break vector redirects the PC to the value of $c030, which fetches the opcode (and data) from the floating bus.  If a BRK, the cycle continues. If not a BRK, the only other expected value is a JMP, which in AppleWin forms a JMP $0000, and in turn the JMP $0300 results.

On a physical //e, with slot 1 vacant, I filled text page 2 with a repeating pattern of AD 30 C0 EA EA EA EA EA except at $BF0 where I put in 4C 00 03.  I displayed text page 2 and invoked C100G.  In this exercise the BRK vector was set to an address that floats ($C050), which would not be used unless the invocation to C100G happened to land on the 00 in 4C 00 03 or some other value from the floating bus came up as 00 - say from the screen holes or during the blanking interval. The Apple //e ended up at the code at $300 every time.  This likely needs more in depth exploration as all the technical resources (Bishop's) describe that the blanking interval and RTZ can turn up some unexpected values.

That's all for now.