Conversation
| ) | ||
|
|
||
| const version = "0.0.6" | ||
| const version = "0.0.7" |
There was a problem hiding this comment.
Can probably leave this out, i usually increase this just before a new release
format/mbr/testdata/mbr.fqtest
Outdated
| 0x1b0| 00 00| ..| partition_table: raw bits | ||
| 0x1c0|00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00|................| | ||
| * |until 0x1fd.7 (64) | | | ||
| 0x1f0| 55 aa| U.| boot_record_sig: raw bits |
There was a problem hiding this comment.
This is always 0x55aa for a mbr? if so could seek and assert i guess
| } | ||
|
|
||
| func mbrDecode(d *decode.D, in interface{}) interface{} { | ||
| d.FieldRawLen("code_area", 446*8) |
There was a problem hiding this comment.
Plan is to use some kind of x86_16 decoder here?
There was a problem hiding this comment.
For the first iteration I'm going to focus on the partition table. Your work on the x86_16 decoder can be reused here.
format/mbr/mbr.go
Outdated
|
|
||
| func mbrDecode(d *decode.D, in interface{}) interface{} { | ||
| d.FieldRawLen("code_area", 446*8) | ||
| d.FieldRawLen("partition_table", 64*8) |
There was a problem hiding this comment.
Know if this partition table format is used outside mbr? if not i guess maybe not worth having it as separate format
There was a problem hiding this comment.
I don't think so, I just wanted to format it nicely enough that you can tell how big the partitions are
|
Nice start! will be interesting to get started with ISA decoding in fq, have some ideas but nothing clear yet so we will probably have to try things out :) |
|
Just comment here, IM or send me an email if you have any questions or want to discuss something |
|
Tried the x86_16 decoder, seems to work ok. But i'm not that pleased with the current output, it would be nice to split out more operands and such.. hmm $ go run fq.go -o line_bytes=4 -d raw 'tobytes[0:446] | x86_16 | dd' mbr.bin
│00 01 02 03│0123│.[0:193]: (x86_16)
│ │ │ [0]{}:
0x000│fa │. │ opcode: "cli" (raw bits)
│ │ │ op: "cli" (0xfa000000)
│ │ │ [1]{}:
0x000│ bc 00 7c│ ..|│ opcode: "mov sp, 0x7c00" (raw bits)
│ │ │ op: "mov" (0xbc000000)
│ │ │ [2]{}:
0x004│31 c0 │1. │ opcode: "xor ax, ax" (raw bits)
│ │ │ op: "xor" (0x31c00000)
│ │ │ [3]{}:
0x004│ 8e d0│ ..│ opcode: "mov ss, ax" (raw bits)
│ │ │ op: "mov" (0x8ed00000)
│ │ │ [4]{}:
0x008│8e c0 │.. │ opcode: "mov es, ax" (raw bits)
│ │ │ op: "mov" (0x8ec00000)
│ │ │ [5]{}:
0x008│ 8e d8│ ..│ opcode: "mov ds, ax" (raw bits)
│ │ │ op: "mov" (0x8ed80000)
│ │ │ [6]{}:
0x00c│52 │R │ opcode: "push dx" (raw bits)
│ │ │ op: "push" (0x52000000)
│ │ │ [7]{}:
0x00c│ be 00 7c│ ..|│ opcode: "mov si, 0x7c00" (raw bits)
│ │ │ op: "mov" (0xbe000000)
│ │ │ [8]{}:
0x010│bf 00 06 │... │ opcode: "mov di, 0x600" (raw bits)
│ │ │ op: "mov" (0xbf000000)
.... |
|
Probably going to pick up work on this next week, it's Hack Week at SUSE |
|
🥳 let me know if you have questions or want to discuss something |
| sectorHighBits, err := d.Bits(2) | ||
| if err != nil { | ||
| d.IOPanic(err, "chs") | ||
| } |
There was a problem hiding this comment.
You can simplify this to just d.U2() or d.U(2) and it will do the error check and panic. I should probably rename Bits to TryBits, the convention usually is Try* functions returns error other just panic and there is usually pairs of functions.
| d.FieldU8("partition_type", partitionTypes) | ||
| d.FieldStrScalarFn("ending_chs_vals", decodeCHSBytes) | ||
| d.FieldStrScalarFn("starting_sector", decodeCHSBytes) | ||
| d.U8() // extra byte |
There was a problem hiding this comment.
Maybe field for this byte also? otherwise it will just be skipped and end up as "unknown" fields. fq figures out where there is gaps and fill them in with unknown fields, so decoders should only skip things that are truely unknown. The idea is to always make all bits "addressable", is quite useful when digging into broken or strange files
| func decodePartitionTableEntry(d *decode.D) { | ||
| d.FieldU8("boot_indicator", scalar.UToDescription{ | ||
| 0x80: "active", | ||
| 0x00: "inactive", |
There was a problem hiding this comment.
You can do a bit field struct here (struct with d.FieldBool("...")) if it's useful to be able to address individual bits. Sometimes i also use 0b1000_0000 etc literals for binary literals
There was a problem hiding this comment.
Maybe this could use scalar.UToSym that way you can write queries as .boot_indicator == "active" and if you really want to compare the non-mapped value do (.boot_indicator | toactual) == 0x80
| } | ||
| cylinder := (sectorHighBits << 2) | cylinderLowerBits | ||
| return scalar.S{Actual: fmt.Sprintf("CHS(%x, %x, %x)", cylinder, head, sector)} | ||
| } |
There was a problem hiding this comment.
Could it make sense to read CHS as a struct with cylinder, head , sector fields?
There was a problem hiding this comment.
Yes, and that suggestion is in line with this general tip
There was a problem hiding this comment.
Ah yes, forgot i wrote that :) maybe should add something more about that it also makes it easier to write queries if things are split up into more individual fields
| d.FieldStruct("entry_2", decodePartitionTableEntry) | ||
| d.FieldStruct("entry_3", decodePartitionTableEntry) | ||
| d.FieldStruct("entry_4", decodePartitionTableEntry) | ||
| } |
There was a problem hiding this comment.
Sometimes when i have problem figure how to model i think about how it will feel and look to user doing queries etc, ex for this a query to get partition size will be .partition_table.entry_1.partition_size for struct vs .partition_table[0].partition_size for array.
format/mbr/testdata/mbr.fqtest
Outdated
| @@ -0,0 +1,41 @@ | |||
| # head -c 512 /dev/sda > mbr.bin | |||
There was a problem hiding this comment.
You can add $ fq -d mbr dv mbr.bin line here and the test framework should be able to regenerate expected output using WRITE_ACTUAL=1 go test .... so you don't have to copy/paste or do it manually. My workflow is usually to run tests, check diffs on failure, if looks ok run with WRITE_ACTUAL=1
|
Cleaned up some of the Try* things #302 if you switch to |
|
Also fixed some of the doc typos #303 |
| if err != nil { | ||
| d.IOPanic(err, "chs") | ||
| } | ||
| cylinder := (sectorHighBits << 2) | cylinderLowerBits |
There was a problem hiding this comment.
Oh the cylinder bits are "continuous" as 16LE, this is a bit messy to represent :( i've run into this issue with some other formats like https://github.com/wader/fq/blob/master/format/vpx/vp8_frame.go#L36 haven't figured any nice way to handle this yet. Also would be nice if there was some decoding helpers for doing this
| @@ -0,0 +1,7 @@ | |||
| def _mbr__help: | |||
| { notes: "Supports decoding Master Boot Record data", | |||
There was a problem hiding this comment.
Looking at the generated help text maybe this is redundant? I've used this to note missing/extra features etc
| var mbrFS embed.FS | ||
|
|
||
| func init() { | ||
| registry.MustRegister(decode.Format{ |
There was a problem hiding this comment.
This was refactored a bit in master, is now interp.RegisterFormat(...)
|
Just a note if you start working on this again: in master format documentation is now .md files again, so you can convert format/mbr/mbr.jq into a normal markdown file, see macho.md etc, and then embed it, see macho.go etc. Then it will be used both for generating documentation and cli help. |
|
Format registration on master have changed a bit but should be straight forward to copy/paste from msgpack.go etc and rename some things and also do the same for formats.go |
Related to issue #23
This is a first pass, it breaks down the MBR into the code_area, the partition_table, and the signature (magic number denoting the end of the boot record)
Future work
for example
Background
MBR is short for Master Boot Record. It is the legacy method of booting,
the code area is limited to 446 bytes, and it's all 16-bit x86 opcodes.
Old BIOS code knows how to call into it and execute the instructions there.
MBR also has a 64-byte partition table that can store up to 4 partitions.
The GPT partitioning scheme obsoletes this, and allows for more partitions, but
that requires UEFI to work.
For more information on MBR, see: https://thestarman.pcministry.com/asm/mbr/PartTables.htm#mbr