Skip site navigation (1)Skip section navigation (2)
Date:      Sun, 21 Apr 2019 04:35:49 +0000 (UTC)
From:      Kyle Evans <kevans@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-stable@freebsd.org, svn-src-stable-11@freebsd.org
Subject:   svn commit: r346483 - in stable/11/stand: common efi/libefi efi/loader uboot/common uboot/lib userboot/userboot
Message-ID:  <201904210435.x3L4Znx1052262@repo.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: kevans
Date: Sun Apr 21 04:35:49 2019
New Revision: 346483
URL: https://svnweb.freebsd.org/changeset/base/346483

Log:
  MFC r343911, r344238-r344241, r344247, r344254-r344255, r344260, r344268,
  r344335, r344839, r345066, r345330
  
  r343911:
  Allow reading the UEFI variable size
  
  When loading bigger variables form UEFI it is necessary to know their
  size beforehand, so that an appropriate amount of memory can be
  allocated. The easiest way to do this is to try to read the variable
  with buffer size equal 0, expecting EFI_BUFFER_TOO_SMALL error to be
  returned. Allow such possible approach in efi_getenv routine.
  
  Extracted from a bigger patch as suggested by imp.
  
  r344238:
  Restore loader(8)'s ability for lsdev to show partitions within a bsd slice.
  
  I'm pretty sure this used to work at one time, perhaps long ago.  It has
  been failing recently because if you call disk_open() with dev->d_partition
  set to -1 when d_slice refers to a bsd slice, it assumes you want it to
  open the first partition within that slice.  When you then pass that open
  dev instance to ptable_open(), it tries to read the start of the 'a'
  partition and decides there is no recognizable partition type there.
  
  This restores the old functionality by resetting d_offset to the start
  of the raw slice after disk_open() returns.  For good measure, d_partition
  is also set back to -1, although that doesn't currently affect anything.
  
  I would have preferred to make disk_open() avoid such rude assumptions and
  if you ask for partition -1 you get the raw slice.  But the commit history
  shows that someone already did that once (r239058), and had to revert it
  (r239232), so I didn't even try to go down that road.
  
  r344239:
  Use a couple local variables to avoid repetitive long expressions that
  cause line-wrapping.
  
  r344240:
  Make lsdev -v output line up in neat columns by using a fixed width for
  the size field and a tab between the partition type and the size.
  
  Changes this
  
    disk devices:
          disk0 (MMC)
          disk0s1: DOS/Windows            49MB
          disk0s2: FreeBSD                14GB
          disk0s2a: FreeBSD UFS         14GB
          disk0s2b: Unknown             2048KB
          disk0s2d: FreeBSD UFS         2040KB
  
  to this
  
    disk devices:
          disk0 (MMC)
          disk0s1: DOS/Windows      49MB
          disk0s2: FreeBSD          14GB
          disk0s2a: FreeBSD UFS     14GB
          disk0s2b: Unknown       2048KB
          disk0s2d: FreeBSD UFS   2040KB
  
  r344241:
  Garbage collection no-longer-used constant.
  
  r344247:
  Make uboot_devdesc properly alias disk_devdesc, so that parsing the u-boot
  loaderdev variable works correctly.
  
  The uboot_devdesc struct is variously cast back and forth between
  uboot_devdesc and disk_devdesc as pointers are handed off through various
  opaque interfaces.  uboot_devdesc attempted to mimic the layout of
  disk_devdesc by having a devdesc struct, followed by a union of some
  device-specific stuff that included a struct that contains the same fields
  as a disk_devdesc.  However, one of those fields inside the struct is 64-bit
  which causes the entire union to be 64-bit aligned -- 32 bits of padding
  is added between the struct devdesc and the union, so the whole mess ends
  up NOT properly mimicking a disk_devdesc after all.  (In disk_devdesc there
  is also 32 bits of padding, but it shows up immediately before the d_offset
  field, rather than before the whole collection of d_* fields.)
  
  This fixes the problem by using an anonymous union to overlay the devdesc
  field uboot network devices need with the disk_devdesc that uboot storage
  devices need.  This is a different solution than the one contributed with
  the PR (so if anything goes wrong, the blame goes to me), but 95% of the
  credit for this fix goes to Pawel Worach and Manuel Stuhn who analyzed the
  problem and proposed a fix.
  
  r344254:
  Use DEV_TYP_NONE instead of -1 to indicate no device was specified.
  
  DEV_TYP_NONE has a value of zero, which makes more sense since the device
  type is a bunch of bits describing the device, crammed into an int.
  
  r344255:
  Fix more places to use DEV_TYP_NONE instead of -1 to indicate 'no device'.
  
  r344260:
  Allow the u-boot loaderdev env var to be formatted in the "usual" loader(8)
  way: device<unit>[s|p]<slice><partition>.  E.g., disk0s2a or disk3p12.
  The code first tries to parse the variable in this format using the
  standard disk_parsedev().  If that fails, it falls back to parsing the
  legacy format that has been supported by ubldr for years.
  
  In addition to 'disk', all the valid uboot device names can also be used:
  mmc, sata, usb, ide, scsi. The 'disk' device serves as an alias for all
  those types and will match the Nth storage-type device found (where N is
  the unit number).
  
  r344268:
  loader: ptable_close() should check its argument
  
  If the passed in table is NULL, just return.
  
  r344335:
  Fix the handling of legacy-format devices in the u-boot loaderdev variable.
  When I added support for the standard loader(8) disk0s2a: type formats,
  the parsing of legacy format was broken because it also contains a colon,
  but it comes before the slice and partition. That would cause disk_parsedev()
  to return success with the slice and partition set to wildcard values.
  
  This change examines the string first, and if it contains spaces, dots, or
  a colon at any position other than the end, it must be a legacy-format
  string and we don't even try to use disk_parsedev() on it.
  
  r344839:
  Add retry loop around GetMemoryMap call to fix fragmentation bug
  
  The call to BS->AllocatePages can cause the memory map to become framented,
  causing BS->GetMemoryMap to return EFI_BUFFER_TOO_SMALL more than once. For
  example this can happen on the MinnowBoard Turbot, causing the boot to stop
  with an error. Avoid this by calling GetMemoryMap in a loop.
  
  r345066:
  stand: Improve some debugging experience
  
  Some of these files using <FOO>_DEBUG defined a DEBUG() macro to serve as a
  debug-printf. -DDEBUG is useful to enable some debugging output across
  multiple ELF/common parts, so switch the DEBUG-as-printf macros over to
  something more like DPRINTF that is more commonly used for this kind of
  thing and less likely to conflict.
  
  userboot/elf64_freebsd debugging also assumed %llx for uint64; use PRIx64
  instead.
  
  r345330:
  loader: fix loading of kernels with . in path
  
  The loader indended to search the kernel file name (only) for . but
  instead searched the entire path, so paths like
  "boot/test.elfv2/kernel" would not work.
  
  PR:		233097

Modified:
  stable/11/stand/common/bcache.c
  stable/11/stand/common/disk.c
  stable/11/stand/common/interp_forth.c
  stable/11/stand/common/load_elf.c
  stable/11/stand/common/part.c
  stable/11/stand/efi/libefi/efienv.c
  stable/11/stand/efi/loader/bootinfo.c
  stable/11/stand/efi/loader/copy.c
  stable/11/stand/uboot/common/main.c
  stable/11/stand/uboot/lib/libuboot.h
  stable/11/stand/userboot/userboot/elf64_freebsd.c
Directory Properties:
  stable/11/   (props changed)

Modified: stable/11/stand/common/bcache.c
==============================================================================
--- stable/11/stand/common/bcache.c	Sun Apr 21 04:26:02 2019	(r346482)
+++ stable/11/stand/common/bcache.c	Sun Apr 21 04:35:49 2019	(r346483)
@@ -44,9 +44,9 @@ __FBSDID("$FreeBSD$");
 /* #define BCACHE_DEBUG */
 
 #ifdef BCACHE_DEBUG
-# define DEBUG(fmt, args...)	printf("%s: " fmt "\n" , __func__ , ## args)
+# define DPRINTF(fmt, args...)	printf("%s: " fmt "\n" , __func__ , ## args)
 #else
-# define DEBUG(fmt, args...)
+# define DPRINTF(fmt, args...)
 #endif
 
 struct bcachectl
@@ -369,7 +369,7 @@ bcache_strategy(void *devdata, int rw, daddr_t blk, si
     /* bypass large requests, or when the cache is inactive */
     if (bc == NULL ||
 	((size * 2 / bcache_blksize) > bcache_nblks)) {
-	DEBUG("bypass %zu from %qu", size / bcache_blksize, blk);
+	DPRINTF("bypass %zu from %qu", size / bcache_blksize, blk);
 	bcache_bypasses++;
 	rw &= F_MASK;
 	return (dd->dv_strategy(dd->dv_devdata, rw, blk, size, buf, rsize));
@@ -444,7 +444,7 @@ bcache_insert(struct bcache *bc, daddr_t blkno)
     
     cand = BHASH(bc, blkno);
 
-    DEBUG("insert blk %llu -> %u # %d", blkno, cand, bcache_bcount);
+    DPRINTF("insert blk %llu -> %u # %d", blkno, cand, bcache_bcount);
     bc->bcache_ctl[cand].bc_blkno = blkno;
     bc->bcache_ctl[cand].bc_count = bcache_bcount++;
 }
@@ -461,7 +461,7 @@ bcache_invalidate(struct bcache *bc, daddr_t blkno)
     if (bc->bcache_ctl[i].bc_blkno == blkno) {
 	bc->bcache_ctl[i].bc_count = -1;
 	bc->bcache_ctl[i].bc_blkno = -1;
-	DEBUG("invalidate blk %llu", blkno);
+	DPRINTF("invalidate blk %llu", blkno);
     }
 }
 

Modified: stable/11/stand/common/disk.c
==============================================================================
--- stable/11/stand/common/disk.c	Sun Apr 21 04:26:02 2019	(r346482)
+++ stable/11/stand/common/disk.c	Sun Apr 21 04:35:49 2019	(r346483)
@@ -38,9 +38,9 @@ __FBSDID("$FreeBSD$");
 #include "disk.h"
 
 #ifdef DISK_DEBUG
-# define DEBUG(fmt, args...)	printf("%s: " fmt "\n" , __func__ , ## args)
+# define DPRINTF(fmt, args...)	printf("%s: " fmt "\n" , __func__ , ## args)
 #else
-# define DEBUG(fmt, args...)
+# define DPRINTF(fmt, args...)
 #endif
 
 struct open_disk {
@@ -75,7 +75,7 @@ display_size(uint64_t size, u_int sectorsize)
 		size /= 1024;
 		unit = 'M';
 	}
-	sprintf(buf, "%ld%cB", (long)size, unit);
+	sprintf(buf, "%4ld%cB", (long)size, unit);
 	return (buf);
 }
 
@@ -102,7 +102,6 @@ ptblread(void *d, void *buf, size_t blocks, uint64_t o
 	    blocks * od->sectorsize, (char *)buf, NULL));
 }
 
-#define	PWIDTH	35
 static int
 ptable_print(void *arg, const char *pname, const struct ptable_entry *part)
 {
@@ -112,16 +111,16 @@ ptable_print(void *arg, const char *pname, const struc
 	struct ptable *table;
 	char line[80];
 	int res;
+	u_int sectsize;
+	uint64_t partsize;
 
 	pa = (struct print_args *)arg;
 	od = (struct open_disk *)pa->dev->dd.d_opendata;
-	sprintf(line, "  %s%s: %s", pa->prefix, pname,
-	    parttype2str(part->type));
-	if (pa->verbose)
-		sprintf(line, "%-*s%s", PWIDTH, line,
-		    display_size(part->end - part->start + 1,
-		    od->sectorsize));
-	strcat(line, "\n");
+	sectsize = od->sectorsize;
+	partsize = part->end - part->start + 1;
+	sprintf(line, "  %s%s: %s\t%s\n", pa->prefix, pname,
+	    parttype2str(part->type),
+	    pa->verbose ? display_size(partsize, sectsize) : "");
 	if (pager_output(line))
 		return 1;
 	res = 0;
@@ -131,10 +130,15 @@ ptable_print(void *arg, const char *pname, const struc
 		dev.dd.d_unit = pa->dev->dd.d_unit;
 		dev.d_slice = part->index;
 		dev.d_partition = -1;
-		if (disk_open(&dev, part->end - part->start + 1,
-		    od->sectorsize) == 0) {
-			table = ptable_open(&dev, part->end - part->start + 1,
-			    od->sectorsize, ptblread);
+		if (disk_open(&dev, partsize, sectsize) == 0) {
+			/*
+			 * disk_open() for partition -1 on a bsd slice assumes
+			 * you want the first bsd partition.  Reset things so
+			 * that we're looking at the start of the raw slice.
+			 */
+			dev.d_partition = -1;
+			dev.d_offset = part->start;
+			table = ptable_open(&dev, partsize, sectsize, ptblread);
 			if (table != NULL) {
 				sprintf(line, "  %s%s", pa->prefix, pname);
 				bsd.dev = pa->dev;
@@ -149,7 +153,6 @@ ptable_print(void *arg, const char *pname, const struc
 
 	return (res);
 }
-#undef PWIDTH
 
 int
 disk_print(struct disk_devdesc *dev, char *prefix, int verbose)
@@ -228,7 +231,7 @@ disk_open(struct disk_devdesc *dev, uint64_t mediasize
 	rc = 0;
 	od = (struct open_disk *)malloc(sizeof(struct open_disk));
 	if (od == NULL) {
-		DEBUG("no memory");
+		DPRINTF("no memory");
 		return (ENOMEM);
 	}
 	dev->dd.d_opendata = od;
@@ -249,14 +252,14 @@ disk_open(struct disk_devdesc *dev, uint64_t mediasize
 	slice = dev->d_slice;
 	partition = dev->d_partition;
 
-	DEBUG("%s unit %d, slice %d, partition %d => %p",
+	DPRINTF("%s unit %d, slice %d, partition %d => %p",
 	    disk_fmtdev(dev), dev->dd.d_unit, dev->d_slice, dev->d_partition, od);
 
 	/* Determine disk layout. */
 	od->table = ptable_open(&partdev, mediasize / sectorsize, sectorsize,
 	    ptblread);
 	if (od->table == NULL) {
-		DEBUG("Can't read partition table");
+		DPRINTF("Can't read partition table");
 		rc = ENXIO;
 		goto out;
 	}
@@ -312,7 +315,7 @@ disk_open(struct disk_devdesc *dev, uint64_t mediasize
 		table = ptable_open(dev, part.end - part.start + 1,
 		    od->sectorsize, ptblread);
 		if (table == NULL) {
-			DEBUG("Can't read BSD label");
+			DPRINTF("Can't read BSD label");
 			rc = ENXIO;
 			goto out;
 		}
@@ -340,12 +343,12 @@ out:
 		if (od->table != NULL)
 			ptable_close(od->table);
 		free(od);
-		DEBUG("%s could not open", disk_fmtdev(dev));
+		DPRINTF("%s could not open", disk_fmtdev(dev));
 	} else {
 		/* Save the slice and partition number to the dev */
 		dev->d_slice = slice;
 		dev->d_partition = partition;
-		DEBUG("%s offset %lld => %p", disk_fmtdev(dev),
+		DPRINTF("%s offset %lld => %p", disk_fmtdev(dev),
 		    (long long)dev->d_offset, od);
 	}
 	return (rc);
@@ -357,7 +360,7 @@ disk_close(struct disk_devdesc *dev)
 	struct open_disk *od;
 
 	od = (struct open_disk *)dev->dd.d_opendata;
-	DEBUG("%s closed => %p", disk_fmtdev(dev), od);
+	DPRINTF("%s closed => %p", disk_fmtdev(dev), od);
 	ptable_close(od->table);
 	free(od);
 	return (0);

Modified: stable/11/stand/common/interp_forth.c
==============================================================================
--- stable/11/stand/common/interp_forth.c	Sun Apr 21 04:26:02 2019	(r346482)
+++ stable/11/stand/common/interp_forth.c	Sun Apr 21 04:35:49 2019	(r346483)
@@ -39,9 +39,9 @@ INTERP_DEFINE("4th");
 /* #define BFORTH_DEBUG */
 
 #ifdef BFORTH_DEBUG
-#define	DEBUG(fmt, args...)	printf("%s: " fmt "\n" , __func__ , ## args)
+#define	DPRINTF(fmt, args...)	printf("%s: " fmt "\n" , __func__ , ## args)
 #else
-#define	DEBUG(fmt, args...)
+#define	DPRINTF(fmt, args...)
 #endif
 
 /*
@@ -128,7 +128,7 @@ bf_command(FICL_VM *vm)
 			vmUpdateTib(vm, tail + len);
 		}
 	}
-	DEBUG("cmd '%s'", line);
+	DPRINTF("cmd '%s'", line);
 
 	command_errmsg = command_errbuf;
 	command_errbuf[0] = 0;
@@ -305,7 +305,7 @@ bf_run(const char *line)
 	 */
 	result = ficlExec(bf_vm, __DECONST(char *, line));
 
-	DEBUG("ficlExec '%s' = %d", line, result);
+	DPRINTF("ficlExec '%s' = %d", line, result);
 	switch (result) {
 	case VM_OUTOFTEXT:
 	case VM_ABORTQ:

Modified: stable/11/stand/common/load_elf.c
==============================================================================
--- stable/11/stand/common/load_elf.c	Sun Apr 21 04:26:02 2019	(r346482)
+++ stable/11/stand/common/load_elf.c	Sun Apr 21 04:35:49 2019	(r346483)
@@ -862,14 +862,16 @@ fake_modname(const char *name)
 		sp++;
 	else
 		sp = name;
-	ep = strrchr(name, '.');
-	if (ep) {
-		if (ep == name) {
-			sp = invalid_name;
-			ep = invalid_name + sizeof(invalid_name) - 1;
-		}
-	} else
-		ep = name + strlen(name);
+
+	ep = strrchr(sp, '.');
+	if (ep == NULL) {
+		ep = sp + strlen(sp);
+	}
+	if (ep == sp) {
+		sp = invalid_name;
+		ep = invalid_name + sizeof(invalid_name) - 1;
+	}
+
 	len = ep - sp;
 	fp = malloc(len + 1);
 	if (fp == NULL)

Modified: stable/11/stand/common/part.c
==============================================================================
--- stable/11/stand/common/part.c	Sun Apr 21 04:26:02 2019	(r346482)
+++ stable/11/stand/common/part.c	Sun Apr 21 04:35:49 2019	(r346483)
@@ -44,9 +44,9 @@ __FBSDID("$FreeBSD$");
 #include <uuid.h>
 
 #ifdef PART_DEBUG
-#define	DEBUG(fmt, args...) printf("%s: " fmt "\n", __func__, ## args)
+#define	DPRINTF(fmt, args...) printf("%s: " fmt "\n", __func__, ## args)
 #else
-#define	DEBUG(fmt, args...)
+#define	DPRINTF(fmt, args...)
 #endif
 
 #ifdef LOADER_GPT_SUPPORT
@@ -155,34 +155,34 @@ gpt_checkhdr(struct gpt_hdr *hdr, uint64_t lba_self, u
 	uint32_t sz, crc;
 
 	if (memcmp(hdr->hdr_sig, GPT_HDR_SIG, sizeof(hdr->hdr_sig)) != 0) {
-		DEBUG("no GPT signature");
+		DPRINTF("no GPT signature");
 		return (NULL);
 	}
 	sz = le32toh(hdr->hdr_size);
 	if (sz < 92 || sz > sectorsize) {
-		DEBUG("invalid GPT header size: %d", sz);
+		DPRINTF("invalid GPT header size: %d", sz);
 		return (NULL);
 	}
 	crc = le32toh(hdr->hdr_crc_self);
 	hdr->hdr_crc_self = 0;
 	if (crc32(hdr, sz) != crc) {
-		DEBUG("GPT header's CRC doesn't match");
+		DPRINTF("GPT header's CRC doesn't match");
 		return (NULL);
 	}
 	hdr->hdr_crc_self = crc;
 	hdr->hdr_revision = le32toh(hdr->hdr_revision);
 	if (hdr->hdr_revision < GPT_HDR_REVISION) {
-		DEBUG("unsupported GPT revision %d", hdr->hdr_revision);
+		DPRINTF("unsupported GPT revision %d", hdr->hdr_revision);
 		return (NULL);
 	}
 	hdr->hdr_lba_self = le64toh(hdr->hdr_lba_self);
 	if (hdr->hdr_lba_self != lba_self) {
-		DEBUG("self LBA doesn't match");
+		DPRINTF("self LBA doesn't match");
 		return (NULL);
 	}
 	hdr->hdr_lba_alt = le64toh(hdr->hdr_lba_alt);
 	if (hdr->hdr_lba_alt == hdr->hdr_lba_self) {
-		DEBUG("invalid alternate LBA");
+		DPRINTF("invalid alternate LBA");
 		return (NULL);
 	}
 	hdr->hdr_entries = le32toh(hdr->hdr_entries);
@@ -190,7 +190,7 @@ gpt_checkhdr(struct gpt_hdr *hdr, uint64_t lba_self, u
 	if (hdr->hdr_entries == 0 ||
 	    hdr->hdr_entsz < sizeof(struct gpt_ent) ||
 	    sectorsize % hdr->hdr_entsz != 0) {
-		DEBUG("invalid entry size or number of entries");
+		DPRINTF("invalid entry size or number of entries");
 		return (NULL);
 	}
 	hdr->hdr_lba_start = le64toh(hdr->hdr_lba_start);
@@ -214,7 +214,7 @@ gpt_checktbl(const struct gpt_hdr *hdr, uint8_t *tbl, 
 		/* Check CRC only when buffer size is enough for table. */
 		if (hdr->hdr_crc_table !=
 		    crc32(tbl, hdr->hdr_entries * hdr->hdr_entsz)) {
-			DEBUG("GPT table's CRC doesn't match");
+			DPRINTF("GPT table's CRC doesn't match");
 			return (-1);
 		}
 	}
@@ -310,7 +310,7 @@ ptable_gptread(struct ptable *table, void *dev, diskre
 		table->type = PTABLE_NONE;
 		goto out;
 	}
-	DEBUG("GPT detected");
+	DPRINTF("GPT detected");
 	size = MIN(hdr.hdr_entries * hdr.hdr_entsz,
 	    MAXTBLSZ * table->sectorsize);
 
@@ -346,7 +346,7 @@ ptable_gptread(struct ptable *table, void *dev, diskre
 		entry->flags = le64toh(ent->ent_attr);
 		memcpy(&entry->type.gpt, &ent->ent_type, sizeof(uuid_t));
 		STAILQ_INSERT_TAIL(&table->entries, entry, entry);
-		DEBUG("new GPT partition added");
+		DPRINTF("new GPT partition added");
 	}
 out:
 	free(buf);
@@ -402,7 +402,7 @@ ptable_ebrread(struct ptable *table, void *dev, diskre
 	buf = malloc(table->sectorsize);
 	if (buf == NULL)
 		return (table);
-	DEBUG("EBR detected");
+	DPRINTF("EBR detected");
 	for (i = 0; i < MAXEBRENTRIES; i++) {
 #if 0	/* Some BIOSes return an incorrect number of sectors */
 		if (offset >= table->sectors)
@@ -430,7 +430,7 @@ ptable_ebrread(struct ptable *table, void *dev, diskre
 		entry->flags = dp[0].dp_flag;
 		entry->type.mbr = dp[0].dp_typ;
 		STAILQ_INSERT_TAIL(&table->entries, entry, entry);
-		DEBUG("new EBR partition added");
+		DPRINTF("new EBR partition added");
 		if (dp[1].dp_typ == 0)
 			break;
 		offset = e1->part.start + le32toh(dp[1].dp_start);
@@ -470,14 +470,14 @@ ptable_bsdread(struct ptable *table, void *dev, diskre
 	int i;
 
 	if (table->sectorsize < sizeof(struct disklabel)) {
-		DEBUG("Too small sectorsize");
+		DPRINTF("Too small sectorsize");
 		return (table);
 	}
 	buf = malloc(table->sectorsize);
 	if (buf == NULL)
 		return (table);
 	if (dread(dev, buf, 1, 1) != 0) {
-		DEBUG("read failed");
+		DPRINTF("read failed");
 		ptable_close(table);
 		table = NULL;
 		goto out;
@@ -487,15 +487,15 @@ ptable_bsdread(struct ptable *table, void *dev, diskre
 	    le32toh(dl->d_magic2) != DISKMAGIC)
 		goto out;
 	if (le32toh(dl->d_secsize) != table->sectorsize) {
-		DEBUG("unsupported sector size");
+		DPRINTF("unsupported sector size");
 		goto out;
 	}
 	dl->d_npartitions = le16toh(dl->d_npartitions);
 	if (dl->d_npartitions > 20 || dl->d_npartitions < 8) {
-		DEBUG("invalid number of partitions");
+		DPRINTF("invalid number of partitions");
 		goto out;
 	}
-	DEBUG("BSD detected");
+	DPRINTF("BSD detected");
 	part = &dl->d_partitions[0];
 	raw_offset = le32toh(part[RAW_PART].p_offset);
 	for (i = 0; i < dl->d_npartitions; i++, part++) {
@@ -513,7 +513,7 @@ ptable_bsdread(struct ptable *table, void *dev, diskre
 		entry->part.index = i; /* starts from zero */
 		entry->type.bsd = part->p_fstype;
 		STAILQ_INSERT_TAIL(&table->entries, entry, entry);
-		DEBUG("new BSD partition added");
+		DPRINTF("new BSD partition added");
 	}
 	table->type = PTABLE_BSD;
 out:
@@ -556,7 +556,7 @@ ptable_vtoc8read(struct ptable *table, void *dev, disk
 	if (buf == NULL)
 		return (table);
 	if (dread(dev, buf, 1, 0) != 0) {
-		DEBUG("read failed");
+		DPRINTF("read failed");
 		ptable_close(table);
 		table = NULL;
 		goto out;
@@ -566,20 +566,20 @@ ptable_vtoc8read(struct ptable *table, void *dev, disk
 	for (i = sum = 0; i < sizeof(struct vtoc8); i += sizeof(sum))
 		sum ^= be16dec(buf + i);
 	if (sum != 0) {
-		DEBUG("incorrect checksum");
+		DPRINTF("incorrect checksum");
 		goto out;
 	}
 	if (be16toh(dl->nparts) != VTOC8_NPARTS) {
-		DEBUG("invalid number of entries");
+		DPRINTF("invalid number of entries");
 		goto out;
 	}
 	sectors = be16toh(dl->nsecs);
 	heads = be16toh(dl->nheads);
 	if (sectors * heads == 0) {
-		DEBUG("invalid geometry");
+		DPRINTF("invalid geometry");
 		goto out;
 	}
-	DEBUG("VTOC8 detected");
+	DPRINTF("VTOC8 detected");
 	for (i = 0; i < VTOC8_NPARTS; i++) {
 		dl->part[i].tag = be16toh(dl->part[i].tag);
 		if (i == VTOC_RAW_PART ||
@@ -595,7 +595,7 @@ ptable_vtoc8read(struct ptable *table, void *dev, disk
 		entry->part.index = i; /* starts from zero */
 		entry->type.vtoc8 = dl->part[i].tag;
 		STAILQ_INSERT_TAIL(&table->entries, entry, entry);
-		DEBUG("new VTOC8 partition added");
+		DPRINTF("new VTOC8 partition added");
 	}
 	table->type = PTABLE_VTOC8;
 out:
@@ -619,7 +619,7 @@ ptable_iso9660read(struct ptable *table, void *dev, di
 		return (table);
 		
 	if (dread(dev, buf, 1, cdb2devb(16)) != 0) {
-		DEBUG("read failed");
+		DPRINTF("read failed");
 		ptable_close(table);
 		table = NULL;
 		goto out;
@@ -663,7 +663,7 @@ ptable_open(void *dev, uint64_t sectors, uint16_t sect
 		return (NULL);
 	/* First, read the MBR. */
 	if (dread(dev, buf, 1, DOSBBSECTOR) != 0) {
-		DEBUG("read failed");
+		DPRINTF("read failed");
 		goto out;
 	}
 
@@ -703,7 +703,7 @@ ptable_open(void *dev, uint64_t sectors, uint16_t sect
 	/* Check the MBR magic. */
 	if (buf[DOSMAGICOFFSET] != 0x55 ||
 	    buf[DOSMAGICOFFSET + 1] != 0xaa) {
-		DEBUG("magic sequence not found");
+		DPRINTF("magic sequence not found");
 #if defined(LOADER_GPT_SUPPORT)
 		/* There is no PMBR, check that we have backup GPT */
 		table->type = PTABLE_GPT;
@@ -715,13 +715,13 @@ ptable_open(void *dev, uint64_t sectors, uint16_t sect
 	dp = (struct dos_partition *)(buf + DOSPARTOFF);
 	for (i = 0, count = 0; i < NDOSPART; i++) {
 		if (dp[i].dp_flag != 0 && dp[i].dp_flag != 0x80) {
-			DEBUG("invalid partition flag %x", dp[i].dp_flag);
+			DPRINTF("invalid partition flag %x", dp[i].dp_flag);
 			goto out;
 		}
 #ifdef LOADER_GPT_SUPPORT
 		if (dp[i].dp_typ == DOSPTYP_PMBR) {
 			table->type = PTABLE_GPT;
-			DEBUG("PMBR detected");
+			DPRINTF("PMBR detected");
 		}
 #endif
 		if (dp[i].dp_typ != 0)
@@ -731,9 +731,9 @@ ptable_open(void *dev, uint64_t sectors, uint16_t sect
 	if (table->type == PTABLE_GPT && count > 1) {
 		if (dp[1].dp_typ != DOSPTYP_HFS) {
 			table->type = PTABLE_NONE;
-			DEBUG("Incorrect PMBR, ignore it");
+			DPRINTF("Incorrect PMBR, ignore it");
 		} else {
-			DEBUG("Bootcamp detected");
+			DPRINTF("Bootcamp detected");
 		}
 	}
 #ifdef LOADER_GPT_SUPPORT
@@ -744,7 +744,7 @@ ptable_open(void *dev, uint64_t sectors, uint16_t sect
 #endif
 #ifdef LOADER_MBR_SUPPORT
 	/* Read MBR. */
-	DEBUG("MBR detected");
+	DPRINTF("MBR detected");
 	table->type = PTABLE_MBR;
 	for (i = has_ext = 0; i < NDOSPART; i++) {
 		if (dp[i].dp_typ == 0)
@@ -770,7 +770,7 @@ ptable_open(void *dev, uint64_t sectors, uint16_t sect
 		entry->flags = dp[i].dp_flag;
 		entry->type.mbr = dp[i].dp_typ;
 		STAILQ_INSERT_TAIL(&table->entries, entry, entry);
-		DEBUG("new MBR partition added");
+		DPRINTF("new MBR partition added");
 	}
 	if (has_ext) {
 		table = ptable_ebrread(table, dev, dread);
@@ -787,6 +787,9 @@ void
 ptable_close(struct ptable *table)
 {
 	struct pentry *entry;
+
+	if (table == NULL)
+		return;
 
 	while (!STAILQ_EMPTY(&table->entries)) {
 		entry = STAILQ_FIRST(&table->entries);

Modified: stable/11/stand/efi/libefi/efienv.c
==============================================================================
--- stable/11/stand/efi/libefi/efienv.c	Sun Apr 21 04:26:02 2019	(r346482)
+++ stable/11/stand/efi/libefi/efienv.c	Sun Apr 21 04:35:49 2019	(r346483)
@@ -49,7 +49,7 @@ efi_getenv(EFI_GUID *g, const char *v, void *data, siz
 		return (EFI_OUT_OF_RESOURCES);
 	dl = *len;
 	rv = RS->GetVariable(uv, g, &attr, &dl, data);
-	if (rv == EFI_SUCCESS)
+	if (rv == EFI_SUCCESS || rv == EFI_BUFFER_TOO_SMALL)
 		*len = dl;
 	free(uv);
 	return (rv);

Modified: stable/11/stand/efi/loader/bootinfo.c
==============================================================================
--- stable/11/stand/efi/loader/bootinfo.c	Sun Apr 21 04:26:02 2019	(r346482)
+++ stable/11/stand/efi/loader/bootinfo.c	Sun Apr 21 04:35:49 2019	(r346483)
@@ -287,12 +287,12 @@ static int
 bi_load_efi_data(struct preloaded_file *kfp)
 {
 	EFI_MEMORY_DESCRIPTOR *mm;
-	EFI_PHYSICAL_ADDRESS addr;
+	EFI_PHYSICAL_ADDRESS addr = 0;
 	EFI_STATUS status;
 	const char *efi_novmap;
 	size_t efisz;
 	UINTN efi_mapkey;
-	UINTN mmsz, pages, retry, sz;
+	UINTN dsz, pages, retry, sz;
 	UINT32 mmver;
 	struct efi_map_header *efihdr;
 	bool do_vmap;
@@ -323,76 +323,94 @@ bi_load_efi_data(struct preloaded_file *kfp)
 	efisz = (sizeof(struct efi_map_header) + 0xf) & ~0xf;
 
 	/*
-	 * Assgin size of EFI_MEMORY_DESCRIPTOR to keep compatible with
+	 * Assign size of EFI_MEMORY_DESCRIPTOR to keep compatible with
 	 * u-boot which doesn't fill this value when buffer for memory
 	 * descriptors is too small (eg. 0 to obtain memory map size)
 	 */
-	mmsz = sizeof(EFI_MEMORY_DESCRIPTOR);
+	dsz = sizeof(EFI_MEMORY_DESCRIPTOR);
 
 	/*
-	 * It is possible that the first call to ExitBootServices may change
-	 * the map key. Fetch a new map key and retry ExitBootServices in that
-	 * case.
+	 * Allocate enough pages to hold the bootinfo block and the
+	 * memory map EFI will return to us. The memory map has an
+	 * unknown size, so we have to determine that first. Note that
+	 * the AllocatePages call can itself modify the memory map, so
+	 * we have to take that into account as well. The changes to
+	 * the memory map are caused by splitting a range of free
+	 * memory into two, so that one is marked as being loader
+	 * data.
 	 */
+
+	sz = 0;
+
+	/*
+	 * Matthew Garrett has observed at least one system changing the
+	 * memory map when calling ExitBootServices, causing it to return an
+	 * error, probably because callbacks are allocating memory.
+	 * So we need to retry calling it at least once.
+	 */
 	for (retry = 2; retry > 0; retry--) {
-		/*
-		 * Allocate enough pages to hold the bootinfo block and the
-		 * memory map EFI will return to us. The memory map has an
-		 * unknown size, so we have to determine that first. Note that
-		 * the AllocatePages call can itself modify the memory map, so
-		 * we have to take that into account as well. The changes to
-		 * the memory map are caused by splitting a range of free
-		 * memory into two (AFAICT), so that one is marked as being
-		 * loader data.
-		 */
-		sz = 0;
-		BS->GetMemoryMap(&sz, NULL, &efi_mapkey, &mmsz, &mmver);
-		sz += mmsz;
-		sz = (sz + 0xf) & ~0xf;
-		pages = EFI_SIZE_TO_PAGES(sz + efisz);
-		status = BS->AllocatePages(AllocateAnyPages, EfiLoaderData,
-		     pages, &addr);
-		if (EFI_ERROR(status)) {
-			printf("%s: AllocatePages error %lu\n", __func__,
-			    EFI_ERROR_CODE(status));
-			return (ENOMEM);
-		}
+		for (;;) {
+			status = BS->GetMemoryMap(&sz, mm, &efi_mapkey, &dsz, &mmver);
+			if (!EFI_ERROR(status))
+				break;
 
-		/*
-		 * Read the memory map and stash it after bootinfo. Align the
-		 * memory map on a 16-byte boundary (the bootinfo block is page
-		 * aligned).
-		 */
-		efihdr = (struct efi_map_header *)(uintptr_t)addr;
-		mm = (void *)((uint8_t *)efihdr + efisz);
-		sz = (EFI_PAGE_SIZE * pages) - efisz;
+			if (status != EFI_BUFFER_TOO_SMALL) {
+				printf("%s: GetMemoryMap error %lu\n", __func__,
+	                           EFI_ERROR_CODE(status));
+				return (EINVAL);
+			}
 
-		status = BS->GetMemoryMap(&sz, mm, &efi_mapkey, &mmsz, &mmver);
-		if (EFI_ERROR(status)) {
-			printf("%s: GetMemoryMap error %lu\n", __func__,
-			    EFI_ERROR_CODE(status));
-			return (EINVAL);
-		}
-		status = BS->ExitBootServices(IH, efi_mapkey);
-		if (EFI_ERROR(status) == 0) {
+			if (addr != 0)
+				BS->FreePages(addr, pages);
+
+			/* Add 10 descriptors to the size to allow for
+			 * fragmentation caused by calling AllocatePages */
+			sz += (10 * dsz);
+			pages = EFI_SIZE_TO_PAGES(sz + efisz);
+			status = BS->AllocatePages(AllocateAnyPages, EfiLoaderData,
+					pages, &addr);
+			if (EFI_ERROR(status)) {
+				printf("%s: AllocatePages error %lu\n", __func__,
+				    EFI_ERROR_CODE(status));
+				return (ENOMEM);
+			}
+
 			/*
-			 * This may be disabled by setting efi_disable_vmap in
-			 * loader.conf(5). By default we will setup the virtual
-			 * map entries.
+			 * Read the memory map and stash it after bootinfo. Align the
+			 * memory map on a 16-byte boundary (the bootinfo block is page
+			 * aligned).
 			 */
-			if (do_vmap)
-				efi_do_vmap(mm, sz, mmsz, mmver);
-			efihdr->memory_size = sz;
-			efihdr->descriptor_size = mmsz;
-			efihdr->descriptor_version = mmver;
-			file_addmetadata(kfp, MODINFOMD_EFI_MAP, efisz + sz,
-			    efihdr);
-			return (0);
+			efihdr = (struct efi_map_header *)(uintptr_t)addr;
+			mm = (void *)((uint8_t *)efihdr + efisz);
+			sz = (EFI_PAGE_SIZE * pages) - efisz;
 		}
+
+		status = BS->ExitBootServices(IH, efi_mapkey);
+		if (!EFI_ERROR(status))
+			break;
+	}
+
+	if (retry == 0) {
 		BS->FreePages(addr, pages);
+		printf("ExitBootServices error %lu\n", EFI_ERROR_CODE(status));
+		return (EINVAL);
 	}
-	printf("ExitBootServices error %lu\n", EFI_ERROR_CODE(status));
-	return (EINVAL);
+
+	/*
+	 * This may be disabled by setting efi_disable_vmap in
+	 * loader.conf(5). By default we will setup the virtual
+	 * map entries.
+	 */
+
+	if (do_vmap)
+		efi_do_vmap(mm, sz, dsz, mmver);
+	efihdr->memory_size = sz;
+	efihdr->descriptor_size = dsz;
+	efihdr->descriptor_version = mmver;
+	file_addmetadata(kfp, MODINFOMD_EFI_MAP, efisz + sz,
+	    efihdr);
+
+	return (0);
 }
 
 /*

Modified: stable/11/stand/efi/loader/copy.c
==============================================================================
--- stable/11/stand/efi/loader/copy.c	Sun Apr 21 04:26:02 2019	(r346482)
+++ stable/11/stand/efi/loader/copy.c	Sun Apr 21 04:35:49 2019	(r346483)
@@ -95,7 +95,7 @@ static void
 efi_verify_staging_size(unsigned long *nr_pages)
 {
 	UINTN sz;
-	EFI_MEMORY_DESCRIPTOR *map, *p;
+	EFI_MEMORY_DESCRIPTOR *map = NULL, *p;
 	EFI_PHYSICAL_ADDRESS start, end;
 	UINTN key, dsz;
 	UINT32 dver;
@@ -104,17 +104,28 @@ efi_verify_staging_size(unsigned long *nr_pages)
 	unsigned long available_pages = 0;
 
 	sz = 0;
-	status = BS->GetMemoryMap(&sz, 0, &key, &dsz, &dver);
-	if (status != EFI_BUFFER_TOO_SMALL) {
-		printf("Can't determine memory map size\n");
-		return;
-	}
 
-	map = malloc(sz);
-	status = BS->GetMemoryMap(&sz, map, &key, &dsz, &dver);
-	if (EFI_ERROR(status)) {
-		printf("Can't read memory map\n");
-		goto out;
+	for (;;) {
+		status = BS->GetMemoryMap(&sz, map, &key, &dsz, &dver);
+		if (!EFI_ERROR(status))
+			break;
+
+		if (status != EFI_BUFFER_TOO_SMALL) {
+			printf("Can't read memory map: %lu\n",
+			    EFI_ERROR_CODE(status));
+			goto out;
+		}
+
+		free(map);
+
+		/* Allocate 10 descriptors more than the size reported,
+		 * to allow for any fragmentation caused by calling
+		 * malloc */
+		map = malloc(sz + (10 * dsz));
+		if (map == NULL) {
+			printf("Unable to allocate memory\n");
+			goto out;
+		}
 	}
 
 	ndesc = sz / dsz;

Modified: stable/11/stand/uboot/common/main.c
==============================================================================
--- stable/11/stand/uboot/common/main.c	Sun Apr 21 04:26:02 2019	(r346482)
+++ stable/11/stand/uboot/common/main.c	Sun Apr 21 04:35:49 2019	(r346483)
@@ -156,7 +156,7 @@ get_device_type(const char *devstr, int *devtype)
 		printf("Unknown device type '%s'\n", devstr);
 	}
 
-	*devtype = -1;
+	*devtype = DEV_TYP_NONE;
 	return (NULL);
 }
 
@@ -182,6 +182,14 @@ device_typename(int type)
  * The returned values for slice and partition are interpreted by
  * disk_open().
  *
+ * The device string can be a standard loader(8) disk specifier:
+ *
+ * disk<unit>s<slice>              disk0s1
+ * disk<unit>s<slice><partition>   disk1s2a
+ * disk<unit>p<partition>          disk0p4
+ *
+ * or one of the following formats:
+ *
  * Valid device strings:                     For device types:
  *
  * <type_name>                               DEV_TYP_STOR, DEV_TYP_NET
@@ -198,11 +206,12 @@ device_typename(int type)
 static void
 get_load_device(int *type, int *unit, int *slice, int *partition)
 {
+	struct disk_devdesc dev;
 	char *devstr;
 	const char *p;
 	char *endp;
 
-	*type = -1;
+	*type = DEV_TYP_NONE;
 	*unit = -1;
 	*slice = 0;
 	*partition = -1;
@@ -216,18 +225,38 @@ get_load_device(int *type, int *unit, int *slice, int 
 
 	p = get_device_type(devstr, type);
 
+	/*
+	 * If type is DEV_TYP_STOR we have a disk-like device.  If the remainder
+	 * of the string contains spaces, dots, or a colon in any location other
+	 * than the last char, it's legacy format.  Otherwise it might be
+	 * standard loader(8) format (e.g., disk0s2a or mmc1p12), so try to
+	 * parse the remainder of the string as such, and if it works, return
+	 * those results. Otherwise we'll fall through to the code that parses
+	 * the legacy format.
+	 */
+	if (*type & DEV_TYP_STOR) {
+		size_t len = strlen(p);
+		if (strcspn(p, " .") == len && strcspn(p, ":") >= len - 1 &&
+		    disk_parsedev(&dev, p, NULL) == 0) {
+			*unit = dev.dd.d_unit;
+			*slice = dev.d_slice;
+			*partition = dev.d_partition;
+			return;
+		}
+	}
+
 	/* Ignore optional spaces after the device name. */
 	while (*p == ' ')
 		p++;
 
 	/* Unknown device name, or a known name without unit number.  */
-	if ((*type == -1) || (*p == '\0')) {
+	if ((*type == DEV_TYP_NONE) || (*p == '\0')) {
 		return;
 	}
 
 	/* Malformed unit number. */
 	if (!isdigit(*p)) {
-		*type = -1;
+		*type = DEV_TYP_NONE;
 		return;
 	}
 
@@ -242,7 +271,7 @@ get_load_device(int *type, int *unit, int *slice, int 
 
 	/* Device string is malformed beyond unit number. */
 	if (*p != ':') {
-		*type = -1;
+		*type = DEV_TYP_NONE;
 		*unit = -1;
 		return;
 	}
@@ -255,7 +284,7 @@ get_load_device(int *type, int *unit, int *slice, int 
 
 	/* Only DEV_TYP_STOR devices can have a slice specification. */
 	if (!(*type & DEV_TYP_STOR)) {
-		*type = -1;
+		*type = DEV_TYP_NONE;
 		*unit = -1;
 		return;
 	}
@@ -264,7 +293,7 @@ get_load_device(int *type, int *unit, int *slice, int 
 
 	/* Malformed slice number. */
 	if (p == endp) {
-		*type = -1;
+		*type = DEV_TYP_NONE;
 		*unit = -1;
 		*slice = 0;
 		return;
@@ -278,7 +307,7 @@ get_load_device(int *type, int *unit, int *slice, int 
 
 	/* Device string is malformed beyond slice number. */
 	if (*p != '.') {
-		*type = -1;
+		*type = DEV_TYP_NONE;
 		*unit = -1;
 		*slice = 0;
 		return;
@@ -298,7 +327,7 @@ get_load_device(int *type, int *unit, int *slice, int 
 		return;
 
 	/* Junk beyond partition number. */
-	*type = -1;
+	*type = DEV_TYP_NONE;
 	*unit = -1;
 	*slice = 0;
 	*partition = -1;
@@ -310,13 +339,13 @@ print_disk_probe_info()
 	char slice[32];
 	char partition[32];
 
-	if (currdev.d_disk.slice > 0)
-		sprintf(slice, "%d", currdev.d_disk.slice);
+	if (currdev.d_disk.d_slice > 0)
+		sprintf(slice, "%d", currdev.d_disk.d_slice);
 	else
 		strcpy(slice, "<auto>");
 
-	if (currdev.d_disk.partition >= 0)
-		sprintf(partition, "%d", currdev.d_disk.partition);
+	if (currdev.d_disk.d_partition >= 0)
+		sprintf(partition, "%d", currdev.d_disk.d_partition);
 	else
 		strcpy(partition, "<auto>");
 
@@ -332,8 +361,8 @@ probe_disks(int devidx, int load_type, int load_unit, 
 	int open_result, unit;
 	struct open_file f;
 
-	currdev.d_disk.slice = load_slice;
-	currdev.d_disk.partition = load_partition;
+	currdev.d_disk.d_slice = load_slice;
+	currdev.d_disk.d_partition = load_partition;
 
 	f.f_devdata = &currdev;
 	open_result = -1;
@@ -467,14 +496,14 @@ main(int argc, char **argv)
 		currdev.dd.d_dev = devsw[i];
 		currdev.dd.d_unit = 0;
 
-		if ((load_type == -1 || (load_type & DEV_TYP_STOR)) &&
+		if ((load_type == DEV_TYP_NONE || (load_type & DEV_TYP_STOR)) &&
 		    strcmp(devsw[i]->dv_name, "disk") == 0) {
 			if (probe_disks(i, load_type, load_unit, load_slice, 
 			    load_partition) == 0)
 				break;
 		}
 
-		if ((load_type == -1 || (load_type & DEV_TYP_NET)) &&
+		if ((load_type == DEV_TYP_NONE || (load_type & DEV_TYP_NET)) &&
 		    strcmp(devsw[i]->dv_name, "net") == 0)
 			break;
 	}

Modified: stable/11/stand/uboot/lib/libuboot.h
==============================================================================
--- stable/11/stand/uboot/lib/libuboot.h	Sun Apr 21 04:26:02 2019	(r346482)
+++ stable/11/stand/uboot/lib/libuboot.h	Sun Apr 21 04:35:49 2019	(r346483)
@@ -27,18 +27,14 @@
  * $FreeBSD$
  */
 
+#include <disk.h>
+
 struct uboot_devdesc {
-	struct devdesc		dd;	/* Must be first. */
 	union {
-		struct {
-			int	slice;
-			int	partition;
-			off_t	offset;
-		} disk;
-	} d_kind;
+		struct devdesc      dd;
+		struct disk_devdesc d_disk;
+	};
 };
-
-#define d_disk d_kind.disk
 
 /*
  * Default network packet alignment in memory.  On arm arches packets must be

Modified: stable/11/stand/userboot/userboot/elf64_freebsd.c
==============================================================================
--- stable/11/stand/userboot/userboot/elf64_freebsd.c	Sun Apr 21 04:26:02 2019	(r346482)
+++ stable/11/stand/userboot/userboot/elf64_freebsd.c	Sun Apr 21 04:35:49 2019	(r346483)
@@ -31,6 +31,9 @@ __FBSDID("$FreeBSD$");
 #include <sys/param.h>
 #include <sys/exec.h>
 #include <sys/linker.h>
+#ifdef DEBUG
+#include <machine/_inttypes.h>
+#endif
 #include <string.h>
 #include <i386/include/bootinfo.h>
 #include <machine/elf.h>
@@ -136,7 +139,7 @@ elf64_exec(struct preloaded_file *fp)
 	}
 
 #ifdef DEBUG
-	printf("Start @ %#llx ...\n", ehdr->e_entry);
+	printf("Start @ %#"PRIx64" ...\n", ehdr->e_entry);
 #endif
 
 	dev_cleanup();



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201904210435.x3L4Znx1052262>