Hmmm. So either my diagnosis is incorrect, or my assumption that the UEFI boot loader chooses the boot partition based on the GPT type is incorrect.
I didn't have any ideas for a different cause, so I had a look at the
source code for the UEFI boot loader. I had expected that it would examine the GPT partition type for each partition and attempt to load the next stage from the first partition of type
freebsd-ufs. However, I believe it ignores the partition type and instead tests each disk partition to see whether it has a UFS filesystem, attempting to load the next stage from the first it finds. I've posted my code investigation* at the bottom, since it is quite long.
If I am correct, this means that changing the GPT partition type as I suggested would not work.
Thierry Leloup: Can you test my theory by trying the following and reporting back what happens:
- Deleting the partition on the external disk (
sudo gpart delete -i 1 /dev/da1) and trying to boot your server with the disk plugged in.
- Creating a partition of type UFS (
sudo gpart add -t freebsd-ufs /dev/da1) but writing an invalid file system (Warning: destroys data on the disk! sudo dd if=/dev/urandom of=/dev/da1p1 bs=1m count=64 to write 64MB of pseudo-random data to the start of the partition) and trying to boot your server with the disk plugged in.
I expect that in both situations your server will boot correctly. If so, a horrible work-around might be to create the partition on your external drive so that FreeBSD can recognise it but the UEFI bootloader does not. My first thought was to use old-style FreeBSD partitioning, with a MBR partitioning scheme for the external disk, a slice for the BSD label and then a UFS partition within that. Since the UEFI bootloader will find the BSD label and not the UFS filesystem that might work. Before we try that, let's see how you get on with the tests I proposed.
* Ad:
I had a look at the
source code for the UEFI boot loader. In the
efi_main() function I can see the lines (128, 129) that print the last messages
Thierry Leloup sees:
Code:
128 printf(" \n>> FreeBSD EFI boot block\n");
129 printf(" Loader path: %s\n", path);
I believe the next lines (131-154) loop through the list of block devices attached to the system. Devices that are not disk partitions are skipped (lines 149-150 and see the
UEFI Specification version 2.5 page 601). For each partition, the
domount() function is called. When a UFS filesystem is successfully mounted, the loop ends.
Code:
131 status = systab->BootServices->LocateHandle(ByProtocol,
132 &BlockIoProtocolGUID, NULL, &nparts, handles);
133 nparts /= sizeof(handles[0]);
134
135 for (i = 0; i < nparts; i++) {
136 status = systab->BootServices->HandleProtocol(handles[i],
137 &DevicePathGUID, (void **)&devpath);
138 if (EFI_ERROR(status))
139 continue;
140
141 while (!IsDevicePathEnd(NextDevicePathNode(devpath)))
142 devpath = NextDevicePathNode(devpath);
143
144 status = systab->BootServices->HandleProtocol(handles[i],
145 &BlockIoProtocolGUID, (void **)&blkio);
146 if (EFI_ERROR(status))
147 continue;
148
149 if (!blkio->Media->LogicalPartition)
150 continue;
151
152 if (domount(devpath, blkio, 1) >= 0)
153 break;
154 }
[/i][/i]
The
domount() function calls
fsread() from
ufsread.c:
Code:
267 static int
268 domount(EFI_DEVICE_PATH *device, EFI_BLOCK_IO *blkio, int quiet)
269 {
270
271 dmadat = &__dmadat;
272 bootdev = blkio;
273 bootdevpath = device;
274 if (fsread(0, NULL, 0)) {
275 if (!quiet)
276 printf("domount: can't read superblock\n");
277 return (-1);
278 }
279 if (!quiet)
280 printf("Succesfully mounted UFS filesystem\n");
281 return (0);
282 }
The
fsread() function (I've cut it down to the crucial parts), I believe reads the blocks at the start of the partition to determine whether or not it is a UFS file system. At no point is the GPT partition type considered.
Code:
static ssize_t
168 fsread(ufs_ino_t inode, void *buf, size_t nbyte)
169 {
[...]
192 for (n = 0; sblock_try[n] != -1; n++) {
193 if (dskread(dmadat->sbbuf, sblock_try[n] / DEV_BSIZE,
194 SBLOCKSIZE / DEV_BSIZE))
195 return -1;
196 memcpy(&fs, dmadat->sbbuf, sizeof(struct fs));
197 if ((
198 #if defined(UFS1_ONLY)
199 fs.fs_magic == FS_UFS1_MAGIC
200 #elif defined(UFS2_ONLY)
201 (fs.fs_magic == FS_UFS2_MAGIC &&
202 fs.fs_sblockloc == sblock_try[n])
203 #else
204 fs.fs_magic == FS_UFS1_MAGIC ||
205 (fs.fs_magic == FS_UFS2_MAGIC &&
206 fs.fs_sblockloc == sblock_try[n])
207 #endif
208 ) &&
209 fs.fs_bsize <= MAXBSIZE &&
210 fs.fs_bsize >= sizeof(struct fs))
211 break;
212 }
213 if (sblock_try[n] == -1) {
214 printf("Not ufs\n");
215 return -1;
216 }[...]
304 }