[zedboot][zero-state] Insert a BootZedbootShim().

This is the first step in getting to a place where hardware is delivered
to the platform in a consistent known state (Zedboot @ build revision).

Eventually we will pass the device off with the correct version of
Zedboot to the platform from infra, but this serves as an emulation
until we reach that state.

Bug: 10241
Change-Id: Iaaab821e5142075473e64fcffbd287ecb93d3023
diff --git a/botanist/boot.go b/botanist/boot.go
index ad5a898..a7b2a3e 100644
--- a/botanist/boot.go
+++ b/botanist/boot.go
@@ -159,6 +159,47 @@
 	return n.Reboot(addr)
 }
 
+// BootZedbootShim extracts the Zircon-R image that is intended to be paved to the device
+// and mexec()'s it, it is intended to be executed before calling Boot().
+// This function serves to emulate zero-state, and will eventually be superseded by an
+// infra implementation.
+func BootZedbootShim(ctx context.Context, addr *net.UDPAddr, imgs []build.Image) error {
+	zirconRImg := build.Image{}
+	for _, img := range imgs {
+		for _, arg := range img.PaveArgs {
+			// Find name by bootserver arg to ensure we are extracting the correct zircon-r.
+			// There may be more than one in images.json but only one should be passed to
+			// the bootserver for paving.
+			name, ok := bootserverArgToName[arg]
+			if !ok {
+				return fmt.Errorf("unrecognized bootserver argument found: %q", arg)
+			}
+			if name == zirconRNetsvcName {
+				zirconRImg = img
+				break
+			}
+		}
+		if zirconRImg.Name != "" {
+			break
+		}
+	}
+
+	if zirconRImg.Name != "" {
+		imgFile, err := openNetsvcFile(zirconRImg.Name, zirconRImg.Path)
+		if err != nil {
+			return err
+		}
+		defer imgFile.close()
+		if err := transfer(ctx, addr, []*netsvcFile{imgFile}); err != nil {
+			return err
+		}
+		n := netboot.NewClient(time.Second)
+		return n.Boot(addr)
+	}
+
+	return fmt.Errorf("no zircon-r image found in: %v", imgs)
+}
+
 // A file to send to netsvc.
 type netsvcFile struct {
 	name   string