惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

博客园 - 三生石上(FineUI控件)
G
Google Developers Blog
Vercel News
Vercel News
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
N
Netflix TechBlog - Medium
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
Engineering at Meta
Engineering at Meta
B
Blog
博客园_首页
量子位
博客园 - 叶小钗
L
LangChain Blog
T
The Blog of Author Tim Ferriss
云风的 BLOG
云风的 BLOG
Blog — PlanetScale
Blog — PlanetScale
F
Fortinet All Blogs
S
SegmentFault 最新的问题
宝玉的分享
宝玉的分享
D
DataBreaches.Net
雷峰网
雷峰网
The Cloudflare Blog
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
Last Week in AI
Last Week in AI
P
Proofpoint News Feed
TaoSecurity Blog
TaoSecurity Blog
罗磊的独立博客
MongoDB | Blog
MongoDB | Blog
The GitHub Blog
The GitHub Blog
I
Intezer
H
Help Net Security
The Hacker News
The Hacker News
The Register - Security
The Register - Security
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
AWS News Blog
AWS News Blog
V
V2EX
Microsoft Security Blog
Microsoft Security Blog
T
Tenable Blog
Spread Privacy
Spread Privacy
A
Arctic Wolf
P
Proofpoint News Feed
T
Threat Research - Cisco Blogs
Schneier on Security
Schneier on Security
C
CERT Recently Published Vulnerability Notes
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
The Last Watchdog
The Last Watchdog
Latest news
Latest news
T
Troy Hunt's Blog
L
LINUX DO - 热门话题

DEV Community

Authentication Security Deep Dive: From Brute Force to Salted Hashing (With Java Examples) Why AI Systems Don’t Fail — They Drift Spilling beans for how i learn for exam😁"Reinforcement Learning Cheat Sheet" I Replaced Chrome with Safari for AI Browser Automation. Here's What Broke (and What Finally Worked) How Python Borrows Other People's Work The $40 Architecture: Processing 1 Billion API Requests with 99.99% Uptime Vibe Coding: A Workflow Guide (From Zero to SaaS) Most webhook security guides protect the wrong side. The scary part is delivery. Headless CMS for TanStack Start: Build a Blog with Cosmic EU Age Verification App "Hacked in 2 Minutes" — What Actually Happened Comfy Cloud’s delete function does not actually remove files Running AI Models on GPU Cloud Servers: A Beginner Guide Event-driven media intelligence with AWS Step Functions and Bedrock I scored 500 AI prompts across 8 quality dimensions — here's what broke How to Call Google Gemini API from Next.js (Free Tier, No Backend Needed) The Portal Protocol: Reclaiming Human Connection in the Age of AI How to Fix Your Team's Scattered Knowledge Problem With a Self-Hosted Forum Intro to tc Cloud Functors: A Graph-First Mental Model for the Modern Cloud Designing Multi-Tenant Backends With Both Ownership and Team Access I Built a Neumorphic CSS Library with 77+ Components — Here's What I Learned PostgreSQL Performance Optimization: Why Connection Pooling Is Critical at Scale Cómo construí un SaaS multi-rubro para gestionar expensas en Argentina con FastAPI + Vue 3 🚀 I Built an Ethical Hacking Scanner Tool – Open Source Project I Replaced /usage and /context in Claude Code With a Single Statusline A Pythonic Way to Handle Emails (IMAP/SMTP) with Auto-Discovery and AI-Ready Design I Collected 8.9 Million Polymarket Price Points — Here's What I Found About How Markets Really Move EcoTrack AI — Carbon Footprint Tracker & Dashboard Everyone's Using AI. No One Agrees How. 5 self-hosted ebook managers worth trying in 2026 Building Your First AI Agent with LangChain: From Chatbot to Autonomous Assistant Common SOC 2 Failures (Real World) Stop Vibe-Checking Your AI App: A Practical Guide to Evals How to Use SonarQube and SonarScanner Locally to Level Up Your Code Quality Your Next To-Do App Is Dead — I Replaced Mine with an OpenClaw AI Sign a Nostr event in 60 lines of Python using coincurve — no nostr-sdk, no nbxplorer, no rust toolchain ITGC Audit Explained Like You’re in Big 4 Patch Tuesday abril 2026: Microsoft parcha 163 vulnerabilidades y un zero-day en SharePoint Stop scraping everything: a better way to track competitor price changes Listing on MCPize + the Official MCP Registry while routing payments OUTSIDE the marketplace — how I kept 100% of my x402 revenue Building an AI-Powered Risk Intelligence System Using Serverless Architecture Why We Ripped Function Overloading Out of Our AI Toolchain Testing AI-Generated Code: How to Actually Know If It Works SaaS Churn Is Killing Your Business. Here Is What to Do About It (Without a Support Team) The Speed of AI Is No Longer Linear - And Self-Improving Models Are Why How to Implement RBAC for MCP Tools: A Practical Guide for Engineering Teams From Standard Quote to Persuasive Proposal: AI Automation for Arborists I built a CLI that scaffolds complete multi-tenant SaaS apps Axios CVE-2025–62718: The Silent SSRF Bug That Could Be Hiding in Your Node.js App Right Now The dashboard that ended our friendship Data Pipelines Explained Simply (and How to Build Them with Python) The Hidden Cost of AI Systems Nobody Talks About. undefined vs undeclared, and how typeof behaves Switching from file-based jobs to NATS/Kafka in Rust without changing code io_uring Adventures: Rust Servers That Love Syscalls Why Agentic AI is Killing the Traditional Database The POUR principles of web accessibility for developers and designers Quantum Neural Network 3D — A Deep Dive into Interactive WebGL Visualization How To Install Caveman In Codex On macOS And Windows Automation Pipeline Reliability: Why Your Workflow Breaks When Nobody Is Watching I Built an 'Open World' AI Coding Agent — It Works From ANY Folder From Freelancing to Product: A Tech Service Company's SaaS Transformation China's AI Giants: Adding Tencent Hunyuan & ByteDance Doubao to AI University (74 Providers) On the Vibe Coders and Their Lies clerk: Auto-Summarize Your Claude Code Sessions AI Weekly — 2026/04/10–04/17 | The Model Lockdown Is Here, but the Toolchain Is the Real Battleground AI 週報 — 2026/04/10–2026/04/17 模型封鎖潮來了,但工具鏈才是真戰場 Maybe this is how Open-Source apps are born... 🚀 Fine-Tune LLMs with LoRA and QLoRA: 2026 Guide tRPC v11 + Next.js App Router: End-to-End Type Safety Without the Boilerplate ShadCN UI in 2026: Why I Stopped Installing Component Libraries and Started Owning My Components SaaS Billing in React Server Components: Stripe + Supabase Without a Single `useEffect` Join our DEV Weekend Challenge — $1,000 in Prizes Across TEN winners! Submissions Due April 20 at 6:59 AM UTC. Implementing FSRS Spaced Repetition in Flutter + Supabase — Adding Memory Science to an AI Learning App "I Texted My Localhost From the Train — Claude Code Fixed the Bug Before I Got Home" I Built a Sales Prep AI and It Went Deeper Than Expected Design to Code #2: One JSON, Eleven Outputs Solving the 100M-Row Problem: A Summary Table Pattern for High-Volume Push Notification Logs Flutter Web With Wasm: What Actually Changes For Developers I Built 50 Royalty-Free Soundtracks for My Side Project in a Weekend Using AI Music Generation The Vibe Coding Security Checklist: 7 Things to Check Before You Ship Stop Letting Googlebot Guess Fix Your React App's SEO Right Desconstruindo o Streaming do LinkedIn: Como Criar um Engine de Extração de Vídeo de Alta Performance com HLS e FFmpeg (EDA Part-1) EDA (Exploratory Data Analysis) Explained With Real Life — Why Looking at Your Data Is the Most Important Step in Machine Learning Brand Relationship Management at Scale: Our 4-Touch Outreach System for 200+ Brands Why String.fromEnvironment() Might Return an Empty String in Dart JGuardrails 1.0.0 — Hardening Java LLM Apps Against Jailbreaks, Toxicity, and Prompt Injection Plan and Schedule a Full Week of Threads Content From One Claude Conversation Coding Cat Oran Ep3, Five Tables Changed Everything Updated: BFF Pattern I'm done watching freelancers get buried by 200 proposals. So I'm building the alternative. This is my first post BFS Algorithm in Java Step by Step Tutorial with Examples Tracking LLM Pricing Monthly: An Open Dataset for 22 AI Models How We Measure Content ROI on a Comparison Site: Revenue Attribution Without Perfect Data Introducing Nova AI Ops: The AI-Native Operating System for SRE Teams I built a free desktop video downloader for Windows — Grabbit How Talkie OCR Helps Vision-Impaired & Dyslexic Users Read the World Around Them VRCFaceTracking安装和iPhone面捕配置教程,有bug Even CrowdStrike Can't See Your Agents The Automation Gold Rush: What n8n Workflows and Claude Are Opening Up for Developers Right Now
Remix Mini PC: Moving the Whole Operating System Onto the eMMC
Matt Miller · 2026-05-25 · via DEV Community

Source files: GitHub

The previous post ended with a working cold boot. Plug in power, the BROM loads our TOC0 SPL from sector 16 of the SD card, our U-Boot runs, Armbian comes up to a login prompt. No laptop, no FEL, no manual intervention.

There was just one cosmetic issue. The SD card was still required.

If you pulled it out, the device fell straight to FEL because the BROM had nothing else to boot. That's not actually a problem in most usage, but it bothered me. The whole point of internal storage is that you don't need external media. A 16 GB eMMC is sitting right there on the mainboard, untouched since the project began, except for the small TOC0 SPL I had written to its boot partition during placement experiments and which the BROM had never accepted.

So: one more push. Move everything onto the eMMC, eject the SD card forever.

The unknown that wasn't

I had spent a long evening earlier in the project writing TOC0 SPLs to the eMMC's boot partition (/dev/mmcblk2boot0) at offset 0 and offset 0x2000, both with the same result every time: BROM silent, no SPL banner, device falls to FEL. I had concluded from this that "the eMMC boot partition is not the right place" — but I had never finished the obvious next experiment. I had never written a TOC0 SPL to the user area at sector 16, which is the exact offset that works on SD.

It is embarrassing how easy this was to test. From inside Armbian (booted from the SD as usual), with the boot partition's read-only flag still in its default state (we don't even need to unlock it because the user area is fully writable):

sudo dd if=~/uboot-build/u-boot/u-boot-sunxi-with-spl.bin \
        of=/dev/mmcblk2 bs=512 seek=16 conv=fsync

Enter fullscreen mode Exit fullscreen mode

Power off. Pull the SD out. Plug power back in. Watch the UART.

U-Boot SPL 2026.07-rc2-g744cf5d4e398-dirty
DRAM: 2048 MiB
Trying to boot from MMC2
NOTICE:  BL31: ...
NOTICE:  BL31: Found U-Boot DTB at 0x20ad278, model: Remix Mini PC
...
=>

Enter fullscreen mode Exit fullscreen mode

The BROM had just loaded our TOC0 SPL from the eMMC user area at sector 16, same offset as SD. The whole "the eMMC boot partition is the special boot device" mental model was wrong. Allwinner BROM on this device treats the eMMC user area and the SD card the same way — it scans them for a TOC0 signature at sector 16 and runs whatever it finds. The boot partitions are a separate eMMC feature the BROM does not use here.

The U-Boot prompt at the end is just because there is no Linux rootfs on the eMMC yet, so U-Boot has nothing to chain to. But the hard part — getting BROM to accept eMMC — was over the moment I tried the right offset.

The rootfs migration

Once the eMMC was a usable boot device, the remaining work was straightforward Linux administration. Create a partition on the eMMC user area starting at 16 MiB (well clear of the SPL at sector 16), format it ext4, rsync the SD's contents over, fix up the bootloader config to point at the new UUID.

sudo parted -s /dev/mmcblk2 mklabel msdos
sudo parted -s /dev/mmcblk2 mkpart primary ext4 16MiB 100%
sudo partprobe /dev/mmcblk2

Enter fullscreen mode Exit fullscreen mode

Then immediately I learned something I had not known about parted. After running mklabel msdos, my TOC0 SPL at sector 16 was gone:

$ sudo dd if=/dev/mmcblk2 bs=512 skip=16 count=1 | hexdump -C | head -1
00000000  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00

Enter fullscreen mode Exit fullscreen mode

Zeros. parted does not just write the 512-byte MBR at sector 0; it zeros some configurable amount of leading sectors to scrub residual filesystem signatures and old SPL data. The exact size depends on parted's version and the device, but on my install it was at least 8 KiB — which is exactly where my SPL lived.

This was completely recoverable — the SPL file was still on disk, one dd puts it back at sector 16 — but it's a real gotcha for anyone doing this in the same order I did. The right ordering is:

  1. Create the partition table first with parted.
  2. Then write the SPL at sector 16.
  3. The partition itself starts at sector 32768, so nothing else touches sector 16 again.

After restoring the SPL and verifying, mkfs.ext4 -L armbi_emmc /dev/mmcblk2p1 formats the partition cleanly without touching the bootloader region. rsync -aHAXx / /mnt/emmc/ copies the current SD rootfs over while running on top of it. About 89,000 files and 2.1 GB. Nine minutes at SD-card-speed.

The variable substitution trap

Inside the eMMC copy, two files needed editing:

  • /etc/fstab — the kernel needs to know which device to mount as root.
  • /boot/armbianEnv.txt — the U-Boot boot script reads this to pass the root= parameter to the kernel.

Both currently referenced the SD card's UUID. They needed to reference the eMMC partition's UUID instead. Easy substitution, four shell commands:

SD_UUID=$(sudo blkid -s UUID -o value /dev/mmcblk0p1)
EMMC_UUID=$(sudo blkid -s UUID -o value /dev/mmcblk2p1)
sudo sed -i "s|$SD_UUID|$EMMC_UUID|g" /mnt/emmc/etc/fstab
sudo sed -i "s|$SD_UUID|$EMMC_UUID|g" /mnt/emmc/boot/armbianEnv.txt

Enter fullscreen mode Exit fullscreen mode

I ran the first command. I skipped the second by accident — possibly the multi-line paste cut off, possibly I just didn't notice it. The third and fourth commands ran with $EMMC_UUID set to an empty string, and sed happily did exactly what I told it to: replace the SD UUID with nothing. The result was a fstab that read:

UUID= / ext4 defaults,noatime,...

Enter fullscreen mode Exit fullscreen mode

and an armbianEnv.txt that read rootdev=UUID=. The eMMC rootfs would have failed to mount on boot in a way that would have been hard to debug at three in the morning.

The verify step — printing both files after the substitution — is what caught it. I noticed the UUID column was suddenly empty, looked at the variables, found $EMMC_UUID was unset, set it explicitly, ran a targeted sed to put the right UUID back. Twenty seconds of fix work because of a verify step that took ten seconds. Always print before and after.

The final test

After fixing fstab and armbianEnv, unmounting cleanly, and writing one last sync:

sudo poweroff

Enter fullscreen mode Exit fullscreen mode

Unplug power. Pull SD card out (critical — the BROM tries SD first, and if it finds the SD's TOC0 SPL it'll boot from there regardless of whether the eMMC also has one). Plug power back in.

U-Boot SPL 2026.07-rc2-g744cf5d4e398-dirty
DRAM: 2048 MiB
Trying to boot from MMC2                          ← eMMC, not SD
NOTICE:  BL31: ...
NOTICE:  BL31: Found U-Boot DTB at 0x20ad278, model: Remix Mini PC

U-Boot 2026.07-rc2 Allwinner Technology
CPU:   Allwinner A64 (SUN50I)
Model: Remix Mini PC
DRAM:  2 GiB
...
MMC: no card present                              ← SD physically removed
...
switch to partitions #0, OK
mmc1(part 0) is current device
Scanning mmc 1:1...                               ← eMMC partition 1
Found U-Boot script /boot/boot.scr
...
Starting kernel ...

Armbian 24.11.1 Bookworm ttyS0

pine64 login:

 v24.11.1 for Pine64 running Armbian Linux 6.6.62-current-sunxi64
 Usage of /:  17% of 15G                          ← root mounted from eMMC

Enter fullscreen mode Exit fullscreen mode

Full Armbian login. No SD card in the device. No laptop attached. The Remix Mini PC is now genuinely autonomous: plug in power, after about twenty seconds it is running Linux, ready to be SSH'd into or used directly through HDMI and a USB keyboard.

The boot trace is worth comparing to the previous post's. Same SPL build, same BL31, same U-Boot, same kernel, same rootfs — but the source has shifted from "SD card sector 16" to "eMMC user area sector 16." The SD card is no longer required for any part of the chain.

What this finishes

The original project goal — turn the device into something that boots its own operating system from its own internal storage without a host PC — is done. There is a small list of polish items left (the wired Ethernet is still off, the kernel-side device tree is still a patched BananaPi-M64 rather than a true Remix DTS, the eMMC could be using its boot partition more cleverly) but nothing on that list is blocking. The machine works.

The SD card, freshly imaged and gzip-backed-up at 893 MB, goes into a drawer as a recovery boot media. The two Remix Mini PCs on my desk — one with eMMC, one without — are both functional Linux SBCs as of this evening.

Lessons that I'd put on a sticky note

  • Test the obvious-but-untested case before getting deep into the non-obvious ones. Writing TOC0 to the eMMC user area at sector 16 — the same offset that works on SD — was the simplest possible experiment, and I never ran it until very late in the project. Hours of "the boot partition won't accept TOC0" investigation could have been compressed into one dd command.

  • parted mklabel zeros more than just the MBR. If you have anything in the first few kilobytes of a disk that you want to keep, write it after you create the partition table.

  • Empty shell variables make sed eat data. sed 's|X|$Y|g' with $Y unset does not error; it silently substitutes X with nothing. Always echo your variables before passing them to sed.

  • Verify steps are not optional. Every single bug in this project was caught by either a UART log or a "print the file after editing" command. Every single one was lost time when those verifies were skipped.

Three years between buying the device and getting it to boot on its own. About twenty seconds between power-on and login prompt now. That's the trade I would make again.


Enjoying the content? If you'd like to support my work and keep the ideas flowing, consider buying me a coffee! Your support means the world to me!

Buy Me A Coffee