Code from xv6's bootmain.c:
// Read 'count' bytes at 'offset' from kernel into physical address 'pa'.
// Might copy more than asked.
void
readseg(uchar* pa, uint count, uint offset)
{
uchar* epa;
epa = pa + count;
// Round down to sector boundary.
pa -= offset % SECTSIZE;
// Translate from bytes to sectors; kernel starts at sector 1.
offset = (offset / SECTSIZE) + 1;
// If this is too slow, we could read lots of sectors at a time.
// We'd write more to memory than asked, but it doesn't matter --
// we load in increasing order.
for(; pa < epa; pa += SECTSIZE, offset++)
readsect(pa, offset);
}
I dont understand the following statement:
pa -= offset % SECTSIZE;
What exactly does "Round down to sector boundary" mean ? I don't understand why we are subtracting the physical address (when offset is not zero).
For simplicity, let's say pa = 100 (decimal), count = 50, offset = 5, SECTSIZE = 100.
Then epa becomes 100 + (50*1) = 150.
new pa = 100 - (5%100) = 95. (What's the point ?)
The loop runs 3 times (1 sector more than necessary, why it doesn't matter ?)
Memory is broken down into sectors of SECTSIZE
bytes each.
The code is reducing the destination address because it delegates its work to readsect
which only reads whole sectors.
If it's passed an address that's not on a sector boundary, it reads more than needed (as warned by the comment), to an address lower than that specified by the caller (something not covered by the warning). This is perhaps inefficient, and potentially disastrous, but this implementation does at least put the byte that the caller asked for at the address that they specified.
Consider the following example, with a sector size of 4 bytes. The user calls readseg(pa, 2, 14);
and let's assume for the sake of argument that each byte in kernel memory is set to a value equal to its offset, and that pa is pointing at the second word in "Hello world".
readsect
is going to be called once, and it's going to read in a 4 byte sector. The line that's causing confusion, pa -= offset % SECTSIZE;
is how the code ensures that byte 14 ends up at the address specified by the caller (the initial value of pa
).
Before After
'H' 'H'
'e' 'e'
'l' 'l
'l' 'l'
'o' pa 12
' ' 13
pa 'w' 14
'o' 15
epa 'r' epa 'r'
'l' 'l'
'd' 'd'
0 0