📜 ⬆️ ⬇️

FreeBSD versus GRUB

Good day. This article is devoted to my private struggle against GRUB, well, or for it, this is how to look.
It took to put a grub-loader on freebsd, of course, many people know about chainloader + 1, but this method was not suitable for me.


Prelude


So, we put a hornbeam (the first, the second seemed to me too monstrous):
# cd /usr/ports/sysutils/grub && make install
Now you need to actually register it as a loader.
# grub-install /dev/ad8
This is where the problems went. He tells us the unfortunate news - "The file / boot / grub / stage1 is not read correctly." .
How so? I myself saw this file when I checked whether the hornbeam was delivered.
Okay, I'll look at the logs:
# cat /tmp/grub-install.log.XXXXX
grub> dump (hd2,0,a)/boot/grub/stage1 /tmp/grub-install.img.XXXXX

Error 22: No such partition
grub> quit


Error does not tell me anything, because there is a section by itself. Google did not help, except that it was a question of a bug with a large inode size in ext3, but I have ufs, and there the size is fixed and equal to 128 bytes. Therefore, I made the decision to dig the sorts myself, despite the fact that the code was written badly in terms of design and structure.
')
Error 22


We start the study with the definition of a constant corresponding to this error, it is simple - ERR_NO_PART , then we can see who can return this error (in fact, grub'e has a certain errno analogue, that is, a global variable that contains the last error number).
There are only 2 such places, and they are located in 2 neighboring lambda functions (hello gcc!) Next_bsd_partition () and next_pc_slice () by name everything is clear - the second lists the bsd slices, the first one lists the sections in these slices.

I immediately dismissed the idea of ​​static analysis, because of the structure of grub'a code - very, very simple, a lot of global variables and almost no arguments for functions. I also didn’t really want to debug with gdb, because there is not much experience in this and everything happened on a remote server, so it’s impossible to use any xxgdb. Therefore, I chose the simple approach of newbies - we insert printfs in obscure places. Here I also had difficulties:



I wanted a quicker check, in fact - not to enter the hornbeam console every time and not to type 2 lines. This is done by a simple command (which I spied in the grub-install shell script):
./grub/grub --batch < cmd
Where cmd contains a list of commands, I have in this case:
dump (hd2,0,a)/boot/grub/stage1 test_img
quit


The workplace is ready, we start "debugging". We insert printf () into the beginning of those two functions and watch at what level of hierarchy we stumble.
We stumbled over sections, so here we have a cycle:
  /* Search next valid BSD partition. */ for (i = bsd_part_no + 1; i < BSD_LABEL_NPARTS (buf); i++) { if (BSD_PART_TYPE (buf, i)) { /* Note that *TYPE and *PARTITION were set for current PC slice. */ *type = (BSD_PART_TYPE (buf, i) << 8) | (*type & 0xFF); *start = BSD_PART_START (buf, i); *len = BSD_PART_LENGTH (buf, i); *partition = (*partition & 0xFF00FF) | (i << 8); #ifndef STAGE1_5 /* XXX */ if ((drive & 0x80) && BSD_LABEL_DTYPE (buf) == DTYPE_SCSI) bsd_evil_hack = 4; #endif /* ! STAGE1_5 */ return 1; } } 

By the way, you can pay attention to the interesting position of developers - indents are implemented using a combination of tabs and spaces, that is, 2 tabs, 4 spaces, for example. Which is very "like" the editor, in which I ruled the code.
If the BSD_PART_TYPE (buf, i) condition never worked, then we ’ve concluded that the patricians had run out.

 #define BSD_PART_TYPE(l_ptr, part) \ ( *( (unsigned char *) (((int) l_ptr) + BSD_PART_OFFSET + 12 \ + (part << 4)) ) ) 

This define is “very” understandable. For clarification, I had to go to Dr. CPJ Koymans ’s “Booting, Partitioning and File Systems” .
There is a slice structure, in which we see that at offset 12 from the block describing one section lies the Partition type field, and here it is apparently equal to 0, since the condition does not work.
Checking - bsdlabel / dev / ad8s1 , really - it is written that unused . Well, to fix the matter is not long - bsdlavel -e / dev / ad8s1 . But he does not want - “class not found”.
Of course, before installing the hornbeam, I checked the box ( sysctl kern.geom.debugflags = 16 ), googled, advised to put 17 in the latest versions of the fryah, set it - no result.

There was no desire to delve into the bsdlabel'a sorcerers, so ktrace bsdlabel -e / dev / ad8s1 was easier , then kdump | more .
74340 bsdlabel CALL open(0x28201030,O_RDWR,[unused]0xbfbfe9e8)
74340 bsdlabel NAMI "/dev/ad8s1"
74340 bsdlabel RET open -1 errno 1 Operation not permitted
74340 bsdlabel CALL open(0x28097653,O_RDONLY,[unused]0x28050629)
74340 bsdlabel NAMI "/dev/geom.ctl"
74340 bsdlabel RET open 3
74340 bsdlabel CALL ioctl(0x3,GEOM_CTL,0x28203040)
74340 bsdlabel RET ioctl 0

“Operation not permitted”, apparently not destiny to edit the system disk on the fly using bsdlabel.
But do not worry - there is gpart:
gpart modify -i 1 -t freebsd-ufs /dev/ad8s1
Index 1, for the rule of the section 'a', such logic. / dev / ad8s1, not / dev / ad8, because we are editing not the slice table (MBR in my case), but the slice itself.
Check bsdlabel'om - success. Cross your fingers, check how the hornbeam will react to this. He was positive, but not to the end. Error 22 replaced the new - Error 17: Cannot mount selected partition .

Error 17


From the title, everything is clear - an error with mounting ufs. However, do not be lazy and put the signal printf in ufs2_mount () .
And there is. Function faylitsya. The reason is also clear - can not find the superblock. This is a structure that contains basic information about the file system (node ​​size, cluster size, checkboxes, and so on).
A fairly common structure in the architecture of the FS, there is it in ufs2, but grub does not find it. Let's see where it sits - dumpfs / | head -2 gives this line "superblock location 65536" .
We look in grub:
 #define SBLOCK_FLOPPY 0 #define SBLOCK_UFS1 8192 #define SBLOCK_UFS2 65536 #define SBLOCK_PIGGY 262144 #define SBLOCKSIZE 8192 #define SBLOCKSEARCH \ { SBLOCK_UFS2, SBLOCK_UFS1, SBLOCK_FLOPPY, SBLOCK_PIGGY, -1 } 

We see the treasured number, why not find?
65536 is the offset in bytes, as to what it is considered to be, I was definitely not sure, so I took it with a margin:
dd if=/dev/ad8 bs=512 count=200 | hexdump -C | grep 54
We read the first 200 sectors (the section itself began with sector 16, 128 (superblock offset) + 16 (maximum reference point) + 16 (size of superblock) <200, backlash for reliability).
grep 54 - this is what we get the lines that contain byte 0x54, which is in the signature of the superblock, the full size of the signature is 4 bytes.
#define FS_UFS2_MAGIC 0x19540119 /* UFS2 fast filesystem magic number */
But how to look for dword'am did not immediately occur, so I looked at a couple of dozen lines. Fully the signature was found in the 69 sector, it is important 2 times that I was embarrassed. I took a closer look at dump, there is further mention of FS_UFS1_MAGIC , so this is most likely the loader code, and not the superblock itself.

Then I thought that it would be better to search inside the section:
dd if=/dev/ad8s1a bs=512 count=200 | hexdump -C | grep 54
And here I was waiting for success - at offset 0x10550 (66896) we see our signature.
The offset is not exactly equal to 65536, because the signature lies at the end of the superblock and not at its beginning, so a “delta” ( FIELD_OFFSET () ) is added, which is less than 8192.
As you can see, the superblock is present, and precisely by the offset from the beginning of the section, where it is expected. Determine the offset with respect, Stupidly go over the first n sectors with a known search string.
We get - 0001a350 (107344), which is equal to 209 sector. It is amusing that our estimated score did not reach quite a bit. This was due to the fact that I forgot to add the beginning of the slice itself (63), 63 + 16 + 128 == 207,
And 2 sectors occupy the remaining fields of the superblock.
The target sector is calculated in devread (sector, byte_offset) simply:
sector + (byte_offset >> SECTOR_BITS) + part_start
The variables sector and byte_offset are arguments and in our case are 0 and 65536 respectively, which of course is correct. But part_start gives 16. That is also true. But the hornbeam perceives it as an absolute value, relative to the beginning of the disk, and not the beginning of the slice.
The mane reads " The first partition should start at offset 16 ".
But since the system works, and everything is mounted successfully, I think that the bug in the hornbeam. Therefore, fix it by adding just one character:
*start += BSD_PART_START (buf, i);
And now everything goes well - part_start = 0x4F , and the dump is executed.
We will use reassembled hornbeam, so that my fix will enter stage1_5 / stage2 .
grub_install went right, everything is fine.

The limit on the size of the read file


A little off topic, but for ufs, grub'a and fryahi.
The maximum file size grub can read is:
(12 + superblock.nindir) * superblock.bsize

Superblock parameters are taken from the dumpfs log.
I got about 32 meters on the system, which doesn’t make me very happy, so I cut out the check from the code.
To do this, comment on this line in fsys_ufs2.c :
fsmax = (NDADDR + NINDIR (SUPERBLOCK)) * SUPERBLOCK->fs_bsize;


Finita


Here is such a small story about how I ran the grub on the fryahe.
What is useful here:

Source: https://habr.com/ru/post/118894/


All Articles