using the user buffer has a race where the user can modify
the buffer from another process before it is copied into the cache.
this allows poisoning the cache for every file where the user
has read access.
instead, we update the cache from kernel memory.
Wnode gets two new counters: txcount and txerror
and actrate pointer that will be between minrate
and maxrate.
driver should use actrate instead of maxrate for
transmission when it can provide error feedback.
when a driver detects a transmission failed, it calls
wifitxfail() with the original packet. wifitxfail() then
reduces wn->actrate.
every 256th packet, we optimistically increase wn->actrate
before transmitting.
- reject files smaller or equal to two bytes, they are bogus
- fix out of bounds access in shargs() when n <= 2
- only copy the bytes read into line buffer
- use nil for pointers instead of 0
imagereclaim(), pagereclaim():
- move imagereclaim() and pagereclaim() declarations to portfns.h
- consistently use ulong type for page counts
- name number of pages to free "pages" instead of "min"
- check for pages == 0 on entry
freepages():
- move pagechaindone() call to wakeup newpage() consumers inside
palloc critical section.
putimage():
- use long type for refcount
- reduce delay for channel hop to 200ms
- use 1000ms timeout for auth response (dont hop channels while we wait)
- bunny hop sequence is mathematically prooven
addresses va's of 0 and -BY2PG cause trouble with some memmove()/memset()
implementations and possibly other code because of the nil pointer
and end pointers wrapping to zero.
unlock()/iunlock():
we need to place the coherence() *before* "l->key = 0", so that any
stores that where done while holding the lock become observable
*before* other processors see the lock released.
cas()/tas():
place memory barrier before successfull return to prevent reordering.
making sure to close the dot in every kproc appears repetitive,
so instead stop inheriting the dot in kproc() as this is usually
never what you wanted in the first place.
give kernel processes and local disk file servers (procs
having noswap flag set) a clear advantage for page allocation
under starved condition by giving them ther own wait queue so
they get readied as soon as pages become available.
yoann padioleaus report on 9fans:
> I think I’ve found a bug in the network stack.
> in 9/ip/ip.h there is
> struct Ipht
> {
> Lock;
> Iphash *tab[Nipht];
> };
>
> where Night is 521,
>
> but then in 9/ip/ipaux.c there is
>
> ulong
> iphash(uchar *sa, ushort sp, uchar *da, ushort dp)
> {
> return ((sa[IPaddrlen-1]<<24) ^ (sp << 16) ^ (da[IPaddrlen-1]<<8) ^ dp ) % Nhash;
> }
>
> where Nhash is just 64,
prevent double sleep():
callers to sleep() need to be serialized as there can only
be one process sleeping at a time. plrlock and plwlock do
this.
wait for dma to complete in plwrite():
we have to wait for the dma to complete before touching
plbuf again.
maintain COPEN flag in archopen()/archclose():
when open fails because it was in use, clear the COPEN
flag, so archclose() wont screw stuff up.
David du Colombier wrote:
> The slowness issue only appears on the loopback, because
> it provides a 16384 MTU.
>
> There is an old bug in the Plan 9 TCP stack, were the TCP
> MSS doesn't take account the MTU for incoming connections.
>
> I originally fixed this issue in January 2015 for the Plan 9
> port on Google Compute Engine. On GCE, there is an unusual
> 1460 MTU.
>
> The Plan 9 TCP stack defines a default 1460 MSS corresponding
> to a 1500 MTU. Then, the MSS is fixed according to the MTU
> for outgoing connections, but not incoming connections.
>
> On GCE, this issue leads to IP fragmentation, but GCE didn't
> handle IP fragmentation properly, so the connections
> were dropped.
>
> On the loopback medium, I suppose this is the opposite issue.
> Since the TCP stack didn't fix the MSS in the incoming
> connection, the programs sent multiple small 1500 bytes
> IP packets instead of large 16384 IP packets, but I don't
> know why it leads to such a slowdown.
the intend of posting a note to the faulting process is to
interrupt the syscall to give the note handler a chance
to handle it. kernel processes however, have no note handlers
and all the postnote() does is set up->notepending which will
make the next attempt to sleep raise an Eintr[] error. this
is harmless, but usually not what we want.
there's no need to waste space for a error buffer in the Segio
structure, as the segmentio kproc will be waiting for the next
command after an error and will not overwite it until we issue
another command.
devproc's procctlmemio() did not handle physical segment
types correctly, as it assumed it can just kmap() the page
in question and write to it. physical segments however
need to be mapped uncached but kmap() will always map
cached as it assumes normal memory. on some machines with
aliasing memory with different cache attributes
leads to undefined behaviour!
we borrow the code from devsegment and provide a generic
segio() function to read and write user segments which
handles all the cases without using kmap by just spawning
a kproc that attaches the segment that needs to be read
from or written to. fault() will setup the right mmu
attributes for us. it will also properly flush pages for
segments that maintain instruction cache when written.
however, tlb's have to be flushed separately.
segio() is used for devsegment and devproc now, which
also allows for simplification of fixfault() as there is no
special error handling case anymore as fixfault() is now
called from faulting process *only*.
reads from /proc/$pid/mem can now span multiple pages.
code like "return g->dlen;" is wrong as we do not hold the
qlock of the global segment. another process could come in
and override g->dlen making us return the wrong byte count.
avoid copying when we already got a kernel address (kernel memory
is the same on processes) which is the case with bread()/bwrite().
this is the same optimization that devsd does.
also avoid allocating/freeing and copying while holding the qlock.
when we copy to/from user memory, we might fault preventing
others from accessing the segment while fault handling is in
progress.
walking the freelist for every page is too slow. as we
are freeing a range, we can do a single pass unlinking all
pages in our range and at the end, check if all pages
where freed, if not put the pages that we did free back
and retry, otherwise we'r done.
fixed segments are continuous in physical memory but
allocated in user pages. unlike shared segments, they
are not allocated on demand but the pages are allocated
on creation time (devsegment). fixed segments are
never swapped out, segfreed or resized and can only be
destroyed as a whole.
the physical base address can be discovered by userspace
reading the ctl file in devsegment.
this avoids listing the upper half of 64-bit membars
in Pcidev.mem[] array avoiding potential confusion
in drivers.
we also check if the upper half is programmed to zero
by bios and otherwise zap the entry in Pcidev.mem[]
and print a warning.
qemu puts multiboot data after the end of the kernel image, so
to be able to KADDR() that memory early, we extend the initial
identity mapping by 16K. right now we just got lucky with
the pc kernel as it rounds the map to 4MB pages.
when we switch to graphics mode, we do not want graphical arcs console
to print on the screen anymore as it assumes 8bit color mode and just
messes up the screen on kernel prints.
GEVector() saves the exception return PC in Ureg.r27 which needs
to be preserved.
there should be no reason for the user to change the status
register from noted() eigther, so we now just use setregisters()
in noted() to restore previous general purpose registers. this
means that CU1 will always be off after noted() because notify()
has disabled the FPU on entry and set fpstatus to FPinactive
if it was on. once user starts using FPU again, it will trap and
restore fpu registers.
touching transmit descriptors while dma is running causes the
front to fall off. new approach keeps a counter of free
descriptors in the Ring structure that is incremented
by txintr() when transmit completed.
txintr() will clean descriptors once dma has stopped and
restart dma when there are more descrtors in the chain.
this provides basic console support using the ARC bios routines
theu uartarcs driver. and has native seeq ethernet driver which
was written by reading the 2ed devseq driver as i have no
documentation on the hardware. mmu and trap code is based on the
routerboard kernel.
bootmkfile will now looks for the following proto files in order
and pick the first one it finds to build the bootfs.paq file:
1) $CONF.boofs.proto (config specific)
2) bootfs.proto (kernel specific)
3) $BOOTDIR/bootfs.proto (default generic)
the mount cache uses Page.va to store cached range offset and
limit, but mips kernel uses cache index bits from Page.va to
maintain page coloring. Page.va was not initialized by auxpage().
this change removes auxpage() which was primarily used only
by the mount cache and use newpage() with cache file offset
page as va so we will get a page of the right color.
mount cache keeps the index bits intact by only using the top
and buttom PGSHIFT bits of Page.va for the range offset/limit.
when we are skipping a process because we could not acquire
its segment lock, dont call reclaim() again (which is pointless
as we didnt pageout any pages), instead try the next process.
the Pte.last pointer is inclusive, so don't miss the last page
in pageout().
when building bootfs in d770 mode directory, the other permissions
in bootfs paq are masked off which results in boot to fail. theres
no point in checking group/other permissions on boot, so just disable
permissin checking in paqfs with the -a flag.
mcountseg(), mfreeseg():
use Pte.first/last pointers when possible and avoid constructs
like s->map[i]->pages[j].
freepte():
do not zero entries in freepte(), the segment is going away and
here is no point in zeroing page pointers. hoist common code at
the top avoiding duplication.
segpage(), fixfault():
avoid load after store for Pte** pointer.
fixfault():
return -1 in default case to avoid the "used but not set" warning
for mmuphys and get rid of the useless initialization.
syssegflush():
due to len being unsigned, the pe = PGROUND(pe) can make "chunk"
bigger than len causing a overflow. rewrite the function and deal
with page alignment and errors at the beginning.
syssegflush(), segpage(), fixfault(), putseg(), relocateseg(),
mcountseg(), mfreeseg():
keep naming consistent.
the "to" address can overflow in syssegfree() causing wrong
number of pages to be passed to mfreeseg(). with the current
implementation of mfreeseg() however, this doesnt cause any
data corruption but was just freeing an unexpected number of
pages.
this change checks for this condition in syssegfree() and
errors out instead. also mfreeseg() was changed to take
ulong argument for number of pages instead of int to keep
it consistent with other routines that work with page counts.
sdbio() tests if it can pass the buffer pointer directly to
the driver when it is already in kernel memory. we also need
to check if the buffer is properly aligned but alignment
requirement is handled in system specific sdmalloc() and
was not known to devsd.
to solve this, we *always* page align sd buffers and get rid
of the system specific sdmalloc() macro (was only used in bcm
kernel).
ignore physical segments in mcountseg() and mfreeseg(). physical
segments are not backed by user pages, and doing putpage() on
physical segment pages in mfreeseg() is an error.
do now allow physical segemnts to be resized. the segment size
is only checked in segattach() to be within the physical segment!
ignore physical segments in portcountpagerefs() as pagenumber()
does not work on the malloced page structures of a physical segment.
get rid of Physseg.pgalloc() and Physseg.pgfree() indirection as
this was never used and if theres a need to do more efficient
allocation, it should be done in a portable way.
the following hooks have been added to the ehci Ctlr
structore to handle cache coherency (on arm):
void* (*tdalloc)(ulong,int,ulong);
void* (*dmaalloc)(ulong);
void (*dmafree)(void*);
void (*dmaflush)(int,void*,ulong);
tdalloc() is used to allocate descriptors and the periodic
frame schedule array. on arm, this needs to return uncached
memory. tdalloc()ed memory is never freed.
dmaalloc()/dmafree() is used for io buffers. this can return
cached memory when when hardware maintains cache coherency (pc)
or dmaflush() is provided to flush/invalidate the cache (zynq),
otherwise needs to return uncached memory.
dmaflush() is used to flush/invalidate the cache. the first
argument tells us if we need to flush (non zero) or
invalidate (zero).
uncached.h is gone now. this change makes the handling explicit.
map the whole ocm memory on boot so we can translate physical to
virtual addresses and back for uncached memory using KADDR() and
PADDR().
replace ualloc() with ucalloc() returning virtual address. physical
address can be acquired with PADDR() now.
as ocm is now always mapped, use KADDR() instead of tmpmap() for
mp bootstrap.
there are no kernels currently that do page coloring,
so the only use of cachectl[] is flushing the icache
(on arm and ppc).
on pc64, cachectl consumes 32 bytes in each page resulting
in over 200 megabytes of overhead for 32gb of ram with 4K
pages.
this change removes cachectl[] and adds txtflush ulong
that is set to ~0 by pio() to instruct putmmu() to flush
the icache.
we used to read beyond the boundaries of the becon because of
the end pointer was offset by the beacon header. this is
also what caused the double entries.
this bug happens when the kernel runs out of mount rpc
buffers when allocating a flush rpc. in this case, mntflushalloc()
will errorjump out of mountio() leaving the currently in
flight rpc in the mount. the caller of mountrpc()/mountio()
frees the rpc thats still queued in the mount leaving
to interesting results.
for the fix, we add a waserror() arround mntflushalloc() and
handle the error case like a mount rpc failure which will
properly dequeue the rpc's in flight.
the FPOFF macro that follows the FXSAVE/FSAVE instructions in l.s
used to execute WAIT instruction when the TS flag was not set. this
is wrong and causes pending exceptions to be raised from fpsave which
is called from provsave() which holds up->rlock making it deadlock
when matherror() tries to postnote() to itself.
so making FPOFF non-waiting (just set TS flag).
we handle pending exception when restoring the context.
initially, pio was used to access registers so i didnt need
a kernel driver for initial testing.
pio does not work under efi, so use mmio to access registers.
we have to reset hwblank when switching drivers to
prevent the generic vgablank() to be called by
blankscreen().
remove code setting hwblank from vga drivers as
devvga will always force hwblank to be 1 or 0
depending on if the driver provides a native blanking
routine.
set hwaccel to 1 when the driver provides native fill
and scroll routines independent of softscreen being
disabled. this allows hw acceleration to be used when
softscreen gets switched off.
don't hold drawlock duing vga enable and disable, but just zero
the function pointers under drawlock *before* disabling the vga
device.
holding the drawlock while calling out into enable and disable
is not a good idea. with vgavesa, this might deadlock when
userspace realemu tries to print in a rio window with vgavesa.
in 9front, screen blanking is always initiated from process context,
so there is no need for a kproc anymore.
care has been taken for the race between vesadisable() and vesablank()
by acquiering the drawlock prior calling scr->dev->enable() and
scr->dev->disable(). this also has the side effect of accelerated
fills and scrolls not being called during device disable.
At least on some NVIDIA cards the default scaling mode makes
black borders visible on all sides, even on native resolution.
This patch adds a generic "scaling MODE" command to vgactl
and adds support for it on VESA through NVIDIA VBE OEM extension.
It hasn't been tested on any other video cards, but shouldn't
break anything as the scaling mode is only set on write to vgactl.
we have to call tmpmap() with interrupts disabled as the map
is a per cpu and a interrupt can preempt us while we where
commited to use a entry but *before* we wrote it!
tmpunmap() already calls coherence() before flushpg() so it
is not needed after tmpunmap().
splhi() in l2free() isnt needed as l2free() is always called
with interrupts disabled from mmuswitch() and mmurelease().
fpsave needs to disable the fpu! otherwise we won't catch
the mathtrap() in the kernel or when context switching to
another process that will attempt to use it.
intrdisable() will always be able to unregister the interrupt
now, so there is no reason to have it return an error value.
all drivers except uart8250 already assumed it to never fail
and theres no need to maintain that complexity.
other operating systems always set the "don't fragment" bit
in ther outgoing ipv4 packets causing us to unnecesarily
call ip4reassemble() looking for a fragment reassembly queue.
the change excludes the "don't fragment" bit from the test
so we now call ip4reassemble() only when the "more fragmens"
bit is set or a fragment offset other than zero is given.
this optimization was discovered from akaros.
to allow bytewise access to /proc/#/fd, the contents of the file where
recreated on each call. if fd's had been closed or reassigned between
the reads, the offset would be inconsistent and a read could start off
in the middle of a line. this happens when you cat /proc/#/fd file of
a busy process that mutates its filedescriptor table.
to fix this, we now return one line record at a time. if the line
fits in the read size, then this means the next read will always start
at the beginning of the next line record. we remember the consumed
byte count in Chan.mrock and the current record in Chan.nrock. (these
fields are free to usefor non-directory files)
if a read comes in and the offset is the same as c->mrock, we do not
need to regenerate the file and just render the next c->nrock's record.
for reads smaller than the line count, we have to regenerate the content
up to the offset and the race is still possible, but this should not
be the common case.
the same algorithm is now used for /proc/#/ns file, allowing a simpler
reimplementation and getting rid of Mntwalk state strcture.
mpshutdown() used to call acpireset() making it impossible to build
a kernel without archacpi. now, mpshutdown() is a helper function
that only shuts down the application processors that gets used from
mpreset() and acpireset().
the generic machine reset code in exported by devarch's archreset()
function that is called by mpreset() and from acpireset() as a fallback.
so the code duplication that was in mpshutdown() is avoided.
the purpose of checkpages() is to verify consitency of the hardware mmu state,
not to notify on the console that a program faulted. a program could also
continue after handling the note. (this seems to be the case in go programs)
- shorten cpuidprnt so it doesnt have to break line
- addarchfile: complain when running out of entries
- fix range check in rmemrw() (harmless)
- use nil instead of 0 for pointers
this is a new more simple version of the mount cache
that does not require dynamic allocations for extends.
the Mntcache structure now contains a page bitmap
that is used for quick page invalidation. the size
of the bitmap is proportional to MAXCACHE.
instead of keeping track of cached range in the
Extend data structure, we keep all the information
in the Page itself. the offset from the page where
the cache range starts is in the low PGSHIT bits and
the end in the top bits of Page.va.
we choose Page.daddr to map 1:1 the Mountcache number
and page number (pn) in the Mountcache. to find a page,
we first check the bitmap if the page is there and then
do a pagelookup() with the daddr key.
change page cache ids (bid) to uintptr so we use the full
address space of Page.daddr.
make maxcache offset check consistent in cread().
use consistent types in cupdate() and simplify with goto.
make internal functions static.
use nil instead of 0 for pointers.
there is no use for "bootdisk" variable parametrization
of /boot/boot and no point for the boot section with its
boot methods in the kernel configuration anymore. so
mkboot and boot$CONF.out are gone.
move the rules for bootfs.paq creation in 9/boot/bootmkfile.
location of bootfs.proto is now in 9/boot/bootfs.proto.
our /boot/boot target is now just "boot".
expand the list of files specified in bootfs.proto and use them
as dependencies to bootfs.paq rule. this way, bootfs.paq is
regenerated when the to be included files have been modified.
factotum is started in bootrc before the network is setup
(as we need it to negotiate wpa key for wifi). once, the network
is setup, the bootstrap authservers are passed in /net/ndb,
which factotum reads when doing bootstrap authdial. it does
this only when no authserver was specified earlier! but we
want net.rc to select the proper bootstrap authserver...
the $secstore variable takes precedence over $auth. as
there is no connection server yet, we have to select the set
of servers here and pass them to secstore with -s flag.
note that this will work if multiple addresses where
specified.
bug: Rnointerrupt was used on Vqueue.used.flags instead of
Vqueue.avail.flags.
introduce vqnotify() function that notifies the device
about available ring advancement.
avoid queue notifications there that can be slow by
checking Unonotify flag in Vqueue.used.flags.
keep track of the number of notifications in the queue.
- properly negotiate Fctrlrx feature bit for promisc and multicast.
- allow setting mac address with ea= option from plan9.ini
- dont read the isr register from ifstats() as it has the side effect of reseting isr status
- embedd the Vqueue array in the Ctlr structure avoiding indirection
- add a interrupt counter Vqueue.nintr for statistical purposes
- only read network status register if the feature has been negotiated
- change name to "virtio" as "ethervirtio" is kind of redundant
add vctlcmd() function to setup and comlete control commands.
handle Vctlq and implement promiscuous and multicast mode commands.
remove Vqueue.block[] and Vqueue.header. these are not properties
of the queue (Vctlq as no block array).
the block[] array only needs to be half the queue size as we use
two descriptors per packet.
fix broken shutdown() and remove useless ctl() function.
when we hit a conflict where the pci INTL register gives us
a different irq than we get from southbridge irq router, dont
just change the router setting to the bios assigned irq (that
was previously known as the BIOS workarround), but assume the
southbridge setting to be valid and change the pci INTL register
on the device to it.
only when the router link doesnt seem to be configured or
disabled, then program the router to the irq that bios asisgned
in the INTL register.
the reason is that changing the router setting changes the
irq routing for *all* devices on the same link and changing
it breaks previously checked and valid interrupt routings.
(so happend with virtualbox where the last device on the bus
is some powermanagement device that has wrong INTL setting
and changing the routing breaks the ethernet interrupts)
this change shouldnt affect modern machines which use ioapic
and mp tables or acpi for pci interrupt routing.
we add new function convmemsize() that returns the size of
*usable* conventional memory that does some sanity checking
and reserves the last KB below the top of memory pointer.
this avoids lowraminit() overriding potential bios tables
and sigsearch() going off the rails looking for tables
at above 640K.
Based on OpenBSD driver:
- /sys/dev/pci/azalia.c rev 1.209
- /sys/dev/pci/pcidevs rev 1.1689
- only tested on amd64; machine is an Acer V5-573G
exact model: V5-573G-74518G1Takk
traditionally, the pc kernel mapped the first 8MB of physical
address space. when the kernel size grows beyond that memory mapping,
it will crash on boot and theres no checking in the build process
making sure it fits.
with the pc64 kernel, it is not hard to always map the whole
kernel memory image from KZERO to end[], so that the kernel will
always fit into the initial mapping.
we can avoid some flickering when removing the software cursor
from the shadow framebuffer by avoiding the flushscreenimage()
call.
once the cursor is redrawn, we flush the combined rect of its
old and new position in one go.
theres a race where procstopwait() is interrupted by a note,
setting p->pdbg to nil *before* acquiering the lock and
and pexit() and procctl() accessing it assuming it doesnt
change under them while they are holding the lock.
vmware in efi mode brings application processors up
with CR4 = 0 (pse disabled) which makes us page fault
when accessing the ap's pdb which might be in a 4MB
mapping when the boot processor used pse to setup
page tables.
so we unconditionally enable pse in apbootstrap
(and disable pae in case of surprises).
x230 booted in efi only (no csp) mode hangs
when traditional i8042reset() keyboard reset
is tried.
so we try acpireset() first which discoveres
and writes the acpi reset register.
the uartmini enable function used to override the baud
register so the effecive baudrate was always set to
115200 baud.
now the default baudrate of 9600 is set correctly and can
be changed in the console= boot parameter.
thanks aap and hiro for debugging, pizza and beer :)
to make it possible to mark the bootscreen framebuffer
as write combining in early initialization, mtrr() is
changed not not to error() but to return an error string.
as bootscreen() is used before multiprocessor initialization,
we have to synchronize the mtrr's for every processor as
it comes online. for this, a new mtrrsync() function is
provided that is called from cpuidentify() if mtrr support
is indicated.
the boot processor runs mtrrsync() which snarfs the
registers. later, mtrrsync() is run again from the
application processors which apply the values from the
boot processor.
checkmtrr() from mp.c was removed as its task is also
done by mtrrsync() now.
rampage() cannot be used after meminit(), so test for
conf.mem[0].npage != 0 and use xalloc()/mallocalign()
instead. this allows us to use vmap() early before
mmuinit() which is needed for bootscreeninit() and
acpi.
to get memory for page tables, pc64 needs a lowraminit().
with EFI, the RSDT pointer is passed in *acpi= parameter
from the efi loader. as the RSDT is ususally at the end of
the physical address space (and not to be found in
bios areas), we cannot KMAP() it so we need to vmap().
there was a memory corruption bug caused by us enabling the
ps2mouseputc() handler *before* initializing packetsize.
once we enabled the handler, mouse interrupts could come
in and advance the packet buffer index (nb) beyond the
buffer boundaries.
as ps2mouseputc() only checked for ++nb == packetsize, once
nb was advanced beyond the packetsize, it would continue writing
beyond the buffer and corrupt memory with each mouse packet byte.
solution is to initialize packetsize *before* enabling the
handler, and also do a >= check in ps2mouseputc() in case the
packetsize gets changed to a smaller value at runtime.
alexchandel got the kernel to crash with divide error
on qemu 2.1.2/macosx at this location. probably
caused by perfticks()/tsc being wrong or accounttime()
not having been called yet from timer interrupt yet for
some reason.
devip can only handle Maskconv+1 conversations per
protocol depending on how many bits it uses in the
qid to encode the conversation number.
we check this when the protocol gets registered.
if we do not do this, the kernel will mysteriously
panic when the conversaion numbers collide which
took some time to debug.
WHAT WHERE THEY *THINKING*??!?!
unlike seek, the (new) nsec syscall (not used in 9front libc)
returns the time value in register (from nix), so do the same
for compatibility.
from segattach(2):
Va and len specify the position of the segment in the
process's address space. Va is rounded down to the nearest
page boundary and va+len is rounded up. The system does not
permit segments to overlap. If va is zero, the system will
choose a suitable address.
just rounding up len isnt enougth. we have to round up va+len
instead of just len so that the span [va, va+len) is covered
even if va is not page aligned.
kenjis example:
print("%p\n",ap); // 206cb0
ap = segattach(0, "shared", ap, 1024);
print("%p\n",ap); // 206000
term% cat /proc/612768/segment
Stack defff000 dffff000 1
Text R 1000 6000 1
Data 6000 7000 1
Bss 7000 7000 1
Shared 206000 207000 1
term%
note that 0x206cb0 + 0x400 > 0x20700.