This patch series enables SeaBIOS to relocate itself out of the e-segment, which enables that space to be used by optionroms.
Before this patch series, SeaBIOS would typically use about half of the e-segment. A full debugging build could use nearly all of the e-segment. With this series, SeaBIOS shouldn't need anything beyond the standard f-segment. Future features can likely be added without fear of limiting optionroms.
To support relocation, the build determines which parts of the 32bit code is one-time initialization code. It separates that out and stores relocation information for it. The run-time code can then copy the initialization code to high memory and run it at that location. The one-time init code could be called again on a soft-reboot, but SeaBIOS now detects this case and attempts a hard-reboot.
Unfortunately, both qemu and kvm don't appear to have a reliable way to hard-reboot - normal reboots don't reset the 0xc0000-0xfffff memory. I've worked around this on qemu by manually resetting that memory. However, kvm doesn't keep a pristine copy of the bios at 0xffff0000. Until this is fixed, this patch series will cause a soft-reboot on kvm to result in a shutdown instead of a reboot.
Some details on the patches: Patches 1-4 enhance the build scripts. Patches 5-7 handle soft-reboots. Patches 8-11 enhance the runtime to support relocation.
-Kevin
Kevin O'Connor (11): Use str.startswith() in python scripts. Modify tools/layoutrom.py to use classes instead of tuples. Separate out init code from the rest of the 32bit flat code. Enhance build to emit relocation information. Try to hard-reboot processor on rerun of post under coreboot. Don't do shadow copying of optionroms when CONFIG_OPTIONROMS_DEPLOYED. Try to hard-reboot on rerun of post even on emulators. Add memalign_tmp() helper function. Move init code from _start() to post(). Move the 32bit init code to high memory at runtime. Enable optionroms to use freed space due to CONFIG_RELOCATE_INIT.
Makefile | 6 +- src/apm.c | 14 +- src/config.h | 2 + src/ioport.h | 1 + src/optionroms.c | 18 ++- src/pci.c | 10 + src/pci.h | 1 + src/pmm.c | 8 - src/post.c | 164 +++++++++++++++--- src/ps2port.c | 16 ++- src/ps2port.h | 3 +- src/shadow.c | 40 +++-- src/stacks.c | 4 +- src/types.h | 10 +- src/util.h | 13 ++- tools/checkrom.py | 9 +- tools/checkstack.py | 14 +- tools/layoutrom.py | 465 ++++++++++++++++++++++++++++++++------------------- 18 files changed, 535 insertions(+), 263 deletions(-)
Use the builtin startswith() string method instead of implementing it manually. --- tools/checkstack.py | 14 +++++++------- tools/layoutrom.py | 11 +++++------ 2 files changed, 12 insertions(+), 13 deletions(-)
diff --git a/tools/checkstack.py b/tools/checkstack.py index 2fbc160..428c296 100755 --- a/tools/checkstack.py +++ b/tools/checkstack.py @@ -140,10 +140,10 @@ def calc():
im = re_usestack.match(insn) if im is not None: - if insn[:5] == 'pushl' or insn[:6] == 'pushfl': + if insn.startswith('pushl') or insn.startswith('pushfl'): stackusage += 4 continue - elif insn[:5] == 'pushw' or insn[:6] == 'pushfw': + elif insn.startswith('pushw') or insn.startswith('pushfw'): stackusage += 2 continue stackusage += int(im.group('num'), 16) @@ -158,13 +158,13 @@ def calc(): insnaddr = m.group('insnaddr') calladdr = m.group('calladdr') if calladdr is None: - if insn[:6] == 'lcallw': + if insn.startswith('lcallw'): noteCall(cur, subfuncs, insnaddr, -1, stackusage + 4) noteYield(cur, stackusage + 4) - elif insn[:3] == 'int': + elif insn.startswith('int'): noteCall(cur, subfuncs, insnaddr, -1, stackusage + 6) noteYield(cur, stackusage + 6) - elif insn[:3] == 'sti': + elif insn.startswith('sti'): noteYield(cur, stackusage) else: # misc instruction @@ -176,10 +176,10 @@ def calc(): if '+' in ref: # Inter-function jump. pass - elif insn[:1] == 'j': + elif insn.startswith('j'): # Tail call noteCall(cur, subfuncs, insnaddr, calladdr, 0) - elif insn[:5] == 'calll': + elif insn.startswith('calll'): noteCall(cur, subfuncs, insnaddr, calladdr, stackusage + 4) else: print "unknown call", ref diff --git a/tools/layoutrom.py b/tools/layoutrom.py index 5f8c368..7603f38 100755 --- a/tools/layoutrom.py +++ b/tools/layoutrom.py @@ -59,10 +59,9 @@ def getSectionsStart(sections, endaddr, minalign=1):
# Return the subset of sections with a given name prefix def getSectionsPrefix(sections, prefix): - lp = len(prefix) out = [] for size, align, name in sections: - if name[:lp] == prefix: + if name.startswith(prefix): out.append((size, align, name)) return out
@@ -79,7 +78,7 @@ def fitSections(sections, fillsections): fixedsections = [] for sectioninfo in sections: size, align, name = sectioninfo - if name[:11] == '.fixedaddr.': + if name.startswith('.fixedaddr.'): addr = int(name[11:], 16) fixedsections.append((addr, sectioninfo)) if align != 1: @@ -295,7 +294,7 @@ PHDRS # Find and keep the section associated with a symbol (if available). def keepsymbol(symbol, infos, pos, callerpos=None): addr, section = infos[pos][1].get(symbol, (None, None)) - if section is None or '*' in section or section[:9] == '.discard.': + if section is None or '*' in section or section.startswith('.discard.'): return -1 if callerpos is not None and symbol not in infos[callerpos][4]: # This symbol reference is a cross section reference (an xref). @@ -340,7 +339,7 @@ def gc(info16, info32seg, info32flat): (info32flat[0], info32flat[1], info32flat[2], [], {})) # Start by keeping sections that are globally visible. for size, align, section in info16[0]: - if section[:11] == '.fixedaddr.' or '.export.' in section: + if section.startswith('.fixedaddr.') or '.export.' in section: keepsection(section, infos) keepsymbol('post32', infos, 0, 2) # Return sections found. @@ -372,7 +371,7 @@ def parseObjDump(file): if line == 'SYMBOL TABLE:': state = 'symbol' continue - if line[:24] == 'RELOCATION RECORDS FOR [': + if line.startswith('RELOCATION RECORDS FOR ['): state = 'reloc' relocsection = line[24:-2] continue
Using classes and updating the values in the classes makes the code easier to understand. --- tools/checkrom.py | 9 +- tools/layoutrom.py | 358 +++++++++++++++++++++++++++------------------------- 2 files changed, 189 insertions(+), 178 deletions(-)
diff --git a/tools/checkrom.py b/tools/checkrom.py index 039010d..42cf2df 100755 --- a/tools/checkrom.py +++ b/tools/checkrom.py @@ -14,10 +14,7 @@ def main():
# Read in symbols objinfofile = open(objinfo, 'rb') - symbols = layoutrom.parseObjDump(objinfofile)[1] - syms = {} - for name, (addr, section) in symbols.items(): - syms[name] = addr + symbols = layoutrom.parseObjDump(objinfofile, 'in')[1]
# Read in raw file f = open(rawfile, 'rb') @@ -29,8 +26,8 @@ def main(): finalsize = 128*1024
# Sanity checks - start = syms['code32flat_start'] - end = syms['code32flat_end'] + start = symbols['code32flat_start'].offset + end = symbols['code32flat_end'].offset expend = layoutrom.BUILD_BIOS_ADDR + layoutrom.BUILD_BIOS_SIZE if end != expend: print "Error! Code does not end at 0x%x (got 0x%x)" % ( diff --git a/tools/layoutrom.py b/tools/layoutrom.py index 7603f38..ac6fd14 100755 --- a/tools/layoutrom.py +++ b/tools/layoutrom.py @@ -1,7 +1,7 @@ #!/usr/bin/env python # Script to analyze code and arrange ld sections. # -# Copyright (C) 2008 Kevin O'Connor kevin@koconnor.net +# Copyright (C) 2008-2010 Kevin O'Connor kevin@koconnor.net # # This file may be distributed under the terms of the GNU GPLv3 license.
@@ -40,30 +40,21 @@ def alignpos(pos, alignbytes):
# Determine the final addresses for a list of sections that end at an # address. -def getSectionsStart(sections, endaddr, minalign=1): +def setSectionsStart(sections, endaddr, minalign=1): totspace = 0 - for size, align, name in sections: - if align > minalign: - minalign = align - totspace = alignpos(totspace, align) + size + for section in sections: + if section.align > minalign: + minalign = section.align + totspace = alignpos(totspace, section.align) + section.size startaddr = (endaddr - totspace) / minalign * minalign curaddr = startaddr # out = [(addr, sectioninfo), ...] out = [] - for sectioninfo in sections: - size, align, name = sectioninfo - curaddr = alignpos(curaddr, align) - out.append((curaddr, sectioninfo)) - curaddr += size - return out, startaddr - -# Return the subset of sections with a given name prefix -def getSectionsPrefix(sections, prefix): - out = [] - for size, align, name in sections: - if name.startswith(prefix): - out.append((size, align, name)) - return out + for section in sections: + curaddr = alignpos(curaddr, section.align) + section.finalloc = curaddr + curaddr += section.size + return startaddr
# The 16bit code can't exceed 64K of space. BUILD_BIOS_ADDR = 0xf0000 @@ -73,22 +64,22 @@ BUILD_BIOS_SIZE = 0x10000 # requirements are placed in the correct location. It also places the # 16bit code as high as possible in the f-segment. def fitSections(sections, fillsections): - canrelocate = list(fillsections) - # fixedsections = [(addr, sectioninfo), ...] + # fixedsections = [(addr, section), ...] fixedsections = [] - for sectioninfo in sections: - size, align, name = sectioninfo - if name.startswith('.fixedaddr.'): - addr = int(name[11:], 16) - fixedsections.append((addr, sectioninfo)) - if align != 1: + for section in sections: + if section.name.startswith('.fixedaddr.'): + addr = int(section.name[11:], 16) + section.finalloc = addr + fixedsections.append((addr, section)) + if section.align != 1: print "Error: Fixed section %s has non-zero alignment (%d)" % ( - name, align) + section.name, section.align) sys.exit(1) + fixedsections.sort() + firstfixed = fixedsections[0][0]
# Find freespace in fixed address area - fixedsections.sort() - # fixedAddr = [(freespace, sectioninfo), ...] + # fixedAddr = [(freespace, section), ...] fixedAddr = [] for i in range(len(fixedsections)): fixedsectioninfo = fixedsections[i] @@ -97,31 +88,31 @@ def fitSections(sections, fillsections): nextaddr = BUILD_BIOS_SIZE else: nextaddr = fixedsections[i+1][0] - avail = nextaddr - addr - section[0] - fixedAddr.append((avail, fixedsectioninfo)) + avail = nextaddr - addr - section.size + fixedAddr.append((avail, section)) + fixedAddr.sort()
# Attempt to fit other sections into fixed area - extrasections = [] - fixedAddr.sort() + canrelocate = [(section.size, section.align, section.name, section) + for section in fillsections] canrelocate.sort() + canrelocate = [section for size, align, name, section in canrelocate] totalused = 0 - for freespace, fixedsectioninfo in fixedAddr: - fixedaddr, fixedsection = fixedsectioninfo - addpos = fixedaddr + fixedsection[0] - totalused += fixedsection[0] + for freespace, fixedsection in fixedAddr: + addpos = fixedsection.finalloc + fixedsection.size + totalused += fixedsection.size nextfixedaddr = addpos + freespace # print "Filling section %x uses %d, next=%x, available=%d" % ( -# fixedaddr, fixedsection[0], nextfixedaddr, freespace) +# fixedsection.finalloc, fixedsection.size, nextfixedaddr, freespace) while 1: canfit = None for fitsection in canrelocate: - fitsize, fitalign, fitname = fitsection - if addpos + fitsize > nextfixedaddr: + if addpos + fitsection.size > nextfixedaddr: # Can't fit and nothing else will fit. break - fitnextaddr = alignpos(addpos, fitalign) + fitsize + fitnextaddr = alignpos(addpos, fitsection.align) + fitsection.size # print "Test %s - %x vs %x" % ( -# fitname, fitnextaddr, nextfixedaddr) +# fitsection.name, fitnextaddr, nextfixedaddr) if fitnextaddr > nextfixedaddr: # This item can't fit. continue @@ -131,13 +122,12 @@ def fitSections(sections, fillsections): # Found a section that can fit. fitnextaddr, fitsection = canfit canrelocate.remove(fitsection) - extrasections.append((addpos, fitsection)) + fitsection.finalloc = addpos addpos = fitnextaddr - totalused += fitsection[0] + totalused += fitsection.size # print " Adding %s (size %d align %d) pos=%x avail=%d" % ( # fitsection[2], fitsection[0], fitsection[1] # , fitnextaddr, nextfixedaddr - fitnextaddr) - firstfixed = fixedsections[0][0]
# Report stats total = BUILD_BIOS_SIZE-firstfixed @@ -147,40 +137,42 @@ def fitSections(sections, fillsections): firstfixed, BUILD_BIOS_SIZE, total, slack, (float(slack) / total) * 100.0))
- return fixedsections + extrasections, firstfixed + return firstfixed
-def doLayout(sections16, sections32seg, sections32flat): +# Return the subset of sections with a given name prefix +def getSectionsPrefix(sections, fileid, prefix): + return [section for section in sections + if section.fileid == fileid and section.name.startswith(prefix)] + +def doLayout(sections): # Determine 16bit positions - textsections = getSectionsPrefix(sections16, '.text.') - rodatasections = (getSectionsPrefix(sections16, '.rodata.str1.1') - + getSectionsPrefix(sections16, '.rodata.__func__.')) - datasections = getSectionsPrefix(sections16, '.data16.') - fixedsections = getSectionsPrefix(sections16, '.fixedaddr.') - - locs16fixed, firstfixed = fitSections(fixedsections, textsections) - prunesections = [i[1] for i in locs16fixed] - remsections = [i for i in textsections+rodatasections+datasections - if i not in prunesections] - locs16, code16_start = getSectionsStart(remsections, firstfixed) - locs16 = locs16 + locs16fixed - locs16.sort() + textsections = getSectionsPrefix(sections, '16', '.text.') + rodatasections = (getSectionsPrefix(sections, '16', '.rodata.str1.1') + + getSectionsPrefix(sections, '16', '.rodata.__func__.')) + datasections = getSectionsPrefix(sections, '16', '.data16.') + fixedsections = getSectionsPrefix(sections, '16', '.fixedaddr.') + + firstfixed = fitSections(fixedsections, textsections) + remsections = [s for s in textsections+rodatasections+datasections + if s.finalloc is None] + code16_start = setSectionsStart(remsections, firstfixed)
# Determine 32seg positions - textsections = getSectionsPrefix(sections32seg, '.text.') - rodatasections = (getSectionsPrefix(sections32seg, '.rodata.str1.1') - + getSectionsPrefix(sections32seg, '.rodata.__func__.')) - datasections = getSectionsPrefix(sections32seg, '.data32seg.') + textsections = getSectionsPrefix(sections, '32seg', '.text.') + rodatasections = (getSectionsPrefix(sections, '32seg', '.rodata.str1.1') + +getSectionsPrefix(sections, '32seg', '.rodata.__func__.')) + datasections = getSectionsPrefix(sections, '32seg', '.data32seg.')
- locs32seg, code32seg_start = getSectionsStart( + code32seg_start = setSectionsStart( textsections + rodatasections + datasections, code16_start)
- # Determine 32flat positions - textsections = getSectionsPrefix(sections32flat, '.text.') - rodatasections = getSectionsPrefix(sections32flat, '.rodata') - datasections = getSectionsPrefix(sections32flat, '.data.') - bsssections = getSectionsPrefix(sections32flat, '.bss.') + # Determine 32flat runtime positions + textsections = getSectionsPrefix(sections, '32flat', '.text.') + rodatasections = getSectionsPrefix(sections, '32flat', '.rodata') + datasections = getSectionsPrefix(sections, '32flat', '.data.') + bsssections = getSectionsPrefix(sections, '32flat', '.bss.')
- locs32flat, code32flat_start = getSectionsStart( + code32flat_start = setSectionsStart( textsections + rodatasections + datasections + bsssections , code32seg_start + BUILD_BIOS_ADDR, 16)
@@ -192,52 +184,60 @@ def doLayout(sections16, sections32seg, sections32flat): print "32bit segmented size: %d" % size32seg print "32bit flat size: %d" % size32flat
- return locs16, locs32seg, locs32flat -
###################################################################### # Linker script output ######################################################################
# Write LD script includes for the given cross references -def outXRefs(xrefs, finallocs, delta=0): +def outXRefs(sections): + xrefs = {} out = "" - for symbol, (fileid, section, addr) in xrefs.items(): - if fileid < 2: - addr += delta - out += "%s = 0x%x ;\n" % (symbol, finallocs[(fileid, section)] + addr) + for section in sections: + for reloc in section.relocs: + symbol = reloc.symbol + if (symbol.section is None + or symbol.section.fileid == section.fileid + or symbol.name in xrefs): + continue + xrefs[symbol.name] = 1 + addr = symbol.section.finalloc + symbol.offset + if (section.fileid == '32flat' + and symbol.section.fileid in ('16', '32seg')): + addr += BUILD_BIOS_ADDR + out += "%s = 0x%x ;\n" % (symbol.name, addr) return out
# Write LD script includes for the given sections using relative offsets -def outRelSections(locs, startsym): +def outRelSections(sections, startsym): out = "" - for addr, sectioninfo in locs: - size, align, name = sectioninfo - out += ". = ( 0x%x - %s ) ;\n" % (addr, startsym) - if name == '.rodata.str1.1': + for section in sections: + out += ". = ( 0x%x - %s ) ;\n" % (section.finalloc, startsym) + if section.name == '.rodata.str1.1': out += "_rodata = . ;\n" - out += "*(%s)\n" % (name,) + out += "*(%s)\n" % (section.name,) return out
-# Layout the 32bit segmented code. This places the code as high as possible. -def writeLinkerScripts(locs16, locs32seg, locs32flat - , xref16, xref32seg, xref32flat - , out16, out32seg, out32flat): - # Index to final location for each section - # finallocs[(fileid, section)] = addr - finallocs = {} - for fileid, locs in ((0, locs16), (1, locs32seg), (2, locs32flat)): - for addr, sectioninfo in locs: - finallocs[(fileid, sectioninfo[2])] = addr +def getSectionsFile(sections, fileid, defaddr=0): + sections = [(section.finalloc, section) + for section in sections if section.fileid == fileid] + sections.sort() + sections = [section for addr, section in sections] + pos = defaddr + if sections: + pos = sections[0].finalloc + return sections, pos
+# Layout the 32bit segmented code. This places the code as high as possible. +def writeLinkerScripts(sections, entrysym, out16, out32seg, out32flat): # Write 16bit linker script - code16_start = locs16[0][0] + sections16, code16_start = getSectionsFile(sections, '16') output = open(out16, 'wb') - output.write(COMMONHEADER + outXRefs(xref16, finallocs) + """ + output.write(COMMONHEADER + outXRefs(sections16) + """ code16_start = 0x%x ; .text16 code16_start : { """ % (code16_start) - + outRelSections(locs16, 'code16_start') + + outRelSections(sections16, 'code16_start') + """ } """ @@ -245,15 +245,14 @@ def writeLinkerScripts(locs16, locs32seg, locs32flat output.close()
# Write 32seg linker script - code32seg_start = code16_start - if locs32seg: - code32seg_start = locs32seg[0][0] + sections32seg, code32seg_start = getSectionsFile( + sections, '32seg', code16_start) output = open(out32seg, 'wb') - output.write(COMMONHEADER + outXRefs(xref32seg, finallocs) + """ + output.write(COMMONHEADER + outXRefs(sections32seg) + """ code32seg_start = 0x%x ; .text32seg code32seg_start : { """ % (code32seg_start) - + outRelSections(locs32seg, 'code32seg_start') + + outRelSections(sections32seg, 'code32seg_start') + """ } """ @@ -261,13 +260,19 @@ def writeLinkerScripts(locs16, locs32seg, locs32flat output.close()
# Write 32flat linker script + sections32flat, code32flat_start = getSectionsFile( + sections, '32flat', code32seg_start) output = open(out32flat, 'wb') output.write(COMMONHEADER - + outXRefs(xref32flat, finallocs, BUILD_BIOS_ADDR) + """ + + outXRefs(sections32flat) + """ + %s = 0x%x ; code32flat_start = 0x%x ; .text code32flat_start : { -""" % (locs32flat[0][0]) - + outRelSections(locs32flat, 'code32flat_start') +""" % (entrysym.name, + entrysym.section.finalloc + entrysym.offset + BUILD_BIOS_ADDR, + code32flat_start) + + outRelSections(getSectionsPrefix(sections32flat, '32flat', '') + , 'code32flat_start') + """ . = ( 0x%x - code32flat_start ) ; *(.text32seg) @@ -278,12 +283,12 @@ def writeLinkerScripts(locs16, locs32seg, locs32flat """ % (code32seg_start + BUILD_BIOS_ADDR, code16_start + BUILD_BIOS_ADDR) + COMMONTRAILER + """ -ENTRY(post32) +ENTRY(%s) PHDRS { text PT_LOAD AT ( code32flat_start ) ; } -""") +""" % (entrysym.name,)) output.close()
@@ -292,75 +297,69 @@ PHDRS ######################################################################
# Find and keep the section associated with a symbol (if available). -def keepsymbol(symbol, infos, pos, callerpos=None): - addr, section = infos[pos][1].get(symbol, (None, None)) - if section is None or '*' in section or section.startswith('.discard.'): +def keepsymbol(reloc, infos, pos): + symbolname = reloc.symbol.name + symbol = infos[pos][1].get(symbolname) + if (symbol is None or symbol.section is None + or symbol.section.name.startswith('.discard.')): return -1 - if callerpos is not None and symbol not in infos[callerpos][4]: - # This symbol reference is a cross section reference (an xref). - # xref[symbol] = (fileid, section, addr) - infos[callerpos][4][symbol] = (pos, section, addr) - keepsection(section, infos, pos) + reloc.symbol = symbol + keepsection(symbol.section, infos, pos) return 0
# Note required section, and recursively set all referenced sections # as required. -def keepsection(name, infos, pos=0): - if name in infos[pos][3]: +def keepsection(section, infos, pos=0): + if section.keep: # Already kept - nothing to do. return - infos[pos][3].append(name) - relocs = infos[pos][2].get(name) - if relocs is None: - return + section.keep = 1 # Keep all sections that this section points to - for symbol in relocs: - ret = keepsymbol(symbol, infos, pos) + for reloc in section.relocs: + ret = keepsymbol(reloc, infos, pos) if not ret: continue # Not in primary sections - it may be a cross 16/32 reference - ret = keepsymbol(symbol, infos, (pos+1)%3, pos) + ret = keepsymbol(reloc, infos, (pos+1)%3) if not ret: continue - ret = keepsymbol(symbol, infos, (pos+2)%3, pos) + ret = keepsymbol(reloc, infos, (pos+2)%3) if not ret: continue
-# Return a list of kept sections. -def getSectionsList(sections, names): - return [i for i in sections if i[2] in names] - # Determine which sections are actually referenced and need to be # placed into the output file. def gc(info16, info32seg, info32flat): - # infos = ((sections, symbols, relocs, keep sections, xrefs), ...) - infos = ((info16[0], info16[1], info16[2], [], {}), - (info32seg[0], info32seg[1], info32seg[2], [], {}), - (info32flat[0], info32flat[1], info32flat[2], [], {})) + # infos = ((sections16, symbols16), (sect32seg, sym32seg) + # , (sect32flat, sym32flat)) + infos = (info16, info32seg, info32flat) # Start by keeping sections that are globally visible. - for size, align, section in info16[0]: - if section.startswith('.fixedaddr.') or '.export.' in section: + for section in info16[0]: + if section.name.startswith('.fixedaddr.') or '.export.' in section.name: keepsection(section, infos) - keepsymbol('post32', infos, 0, 2) - # Return sections found. - keep16 = getSectionsList(info16[0], infos[0][3]), infos[0][4] - keep32seg = getSectionsList(info32seg[0], infos[1][3]), infos[1][4] - keep32flat = getSectionsList(info32flat[0], infos[2][3]), infos[2][4] - return keep16, keep32seg, keep32flat + return [section for section in info16[0]+info32seg[0]+info32flat[0] + if section.keep]
###################################################################### # Startup and input parsing ######################################################################
+class Section: + name = size = alignment = fileid = relocs = None + finalloc = keep = None +class Reloc: + offset = type = symbol = None +class Symbol: + name = offset = section = None + # Read in output from objdump -def parseObjDump(file): - # sections = [(size, align, section), ...] +def parseObjDump(file, fileid): + # sections = [section, ...] sections = [] - # symbols[symbol] = (addr, section) + sectionmap = {} + # symbols[symbolname] = symbol symbols = {} - # relocs[section] = [symbol, ...] - relocs = {}
state = None for line in file.readlines(): @@ -372,8 +371,13 @@ def parseObjDump(file): state = 'symbol' continue if line.startswith('RELOCATION RECORDS FOR ['): + sectionname = line[24:-2] + if sectionname.startswith('.debug_'): + # Skip debugging sections (to reduce parsing time) + state = None + continue state = 'reloc' - relocsection = line[24:-2] + relocsection = sectionmap[sectionname] continue
if state == 'section': @@ -381,27 +385,40 @@ def parseObjDump(file): idx, name, size, vma, lma, fileoff, align = line.split() if align[:3] != '2**': continue - sections.append((int(size, 16), 2**int(align[3:]), name)) - except: + section = Section() + section.name = name + section.size = int(size, 16) + section.align = 2**int(align[3:]) + section.fileid = fileid + section.relocs = [] + sections.append(section) + sectionmap[name] = section + except ValueError: pass continue if state == 'symbol': try: - section, size, symbol = line[17:].split() - size = int(size, 16) - addr = int(line[:8], 16) - symbols[symbol] = addr, section - except: + sectionname, size, name = line[17:].split() + symbol = Symbol() + symbol.size = int(size, 16) + symbol.offset = int(line[:8], 16) + symbol.name = name + symbol.section = sectionmap.get(sectionname) + symbols[name] = symbol + except ValueError: pass continue if state == 'reloc': try: - off, type, symbol = line.split() - off = int(off, 16) - relocs.setdefault(relocsection, []).append(symbol) - except: + off, type, symbolname = line.split() + reloc = Reloc() + reloc.offset = int(off, 16) + reloc.type = type + reloc.symbol = symbols[symbolname] + relocsection.relocs.append(reloc) + except ValueError: pass - return sections, symbols, relocs + return sections, symbols
def main(): # Get output name @@ -412,24 +429,21 @@ def main(): infile32seg = open(in32seg, 'rb') infile32flat = open(in32flat, 'rb')
- # infoX = (sections, symbols, relocs) - info16 = parseObjDump(infile16) - info32seg = parseObjDump(infile32seg) - info32flat = parseObjDump(infile32flat) + # infoX = (sections, symbols) + info16 = parseObjDump(infile16, '16') + info32seg = parseObjDump(infile32seg, '32seg') + info32flat = parseObjDump(infile32flat, '32flat')
# Figure out which sections to keep. - # keepX = (sections, xrefs) - keep16, keep32seg, keep32flat = gc(info16, info32seg, info32flat) + sections = gc(info16, info32seg, info32flat)
# Determine the final memory locations of each kept section. # locsX = [(addr, sectioninfo), ...] - locs16, locs32seg, locs32flat = doLayout( - keep16[0], keep32seg[0], keep32flat[0]) + doLayout(sections)
# Write out linker script files. - writeLinkerScripts(locs16, locs32seg, locs32flat - , keep16[1], keep32seg[1], keep32flat[1] - , out16, out32seg, out32flat) + entrysym = info16[1]['post32'] + writeLinkerScripts(sections, entrysym, out16, out32seg, out32flat)
if __name__ == '__main__': main()
Enhance tools/layoutrom.py code so that it can detect which sections are used from the "32bit flat" runtime code. All other "32bit flat" sections can then be assured to be initialization code only.
This is in preparation for relocating the 32bit initialization code. --- src/post.c | 2 +- src/stacks.c | 4 +- src/types.h | 10 ++++++-- tools/layoutrom.py | 53 +++++++++++++++++++++++++++++++++++++++++++++++++-- 4 files changed, 60 insertions(+), 9 deletions(-)
diff --git a/src/post.c b/src/post.c index 5d0e2cb..6cd94ce 100644 --- a/src/post.c +++ b/src/post.c @@ -178,7 +178,7 @@ init_hw(void) }
// Main setup code. -static void +void VISIBLE32INIT post(void) { // Detect and init ram. diff --git a/src/stacks.c b/src/stacks.c index f5feeeb..dade3af 100644 --- a/src/stacks.c +++ b/src/stacks.c @@ -1,6 +1,6 @@ // Code for manipulating stack locations. // -// Copyright (C) 2009 Kevin O'Connor kevin@koconnor.net +// Copyright (C) 2009-2010 Kevin O'Connor kevin@koconnor.net // // This file may be distributed under the terms of the GNU LGPLv3 license.
@@ -383,7 +383,7 @@ wait_preempt(void) extern void yield_preempt(void); #if MODESEGMENT == 0 // Try to execute 32bit threads. -void VISIBLE32FLAT +void VISIBLE32INIT yield_preempt(void) { PreemptCount++; diff --git a/src/types.h b/src/types.h index 5083941..7d610ec 100644 --- a/src/types.h +++ b/src/types.h @@ -1,6 +1,6 @@ // Basic type definitions for X86 cpus. // -// Copyright (C) 2008,2009 Kevin O'Connor kevin@koconnor.net +// Copyright (C) 2008-2010 Kevin O'Connor kevin@koconnor.net // // This file may be distributed under the terms of the GNU LGPLv3 license. #ifndef __TYPES_H @@ -45,6 +45,8 @@ extern void __force_link_error__only_in_16bit(void) __noreturn; # define VISIBLE16 __VISIBLE // Notes a function as externally visible in the 32bit flat code chunk. # define VISIBLE32FLAT __section(".discard.func32flat." UNIQSEC) noinline __weak +// Notes a 32bit flat function that will only be called during init. +# define VISIBLE32INIT VISIBLE32FLAT // Notes a function as externally visible in the 32bit segmented code chunk. # define VISIBLE32SEG __section(".discard.func32seg." UNIQSEC) noinline __weak // Designate a variable as (only) visible to 16bit code. @@ -70,6 +72,7 @@ extern void __force_link_error__only_in_16bit(void) __noreturn; #elif MODESEGMENT == 1 # define VISIBLE16 __section(".discard.func16." UNIQSEC) noinline __weak # define VISIBLE32FLAT __section(".discard.func32flat." UNIQSEC) noinline __weak +# define VISIBLE32INIT VISIBLE32FLAT # define VISIBLE32SEG __VISIBLE # define VAR16 __section(".discard.var16." UNIQSEC) # define VAR16VISIBLE VAR16 __VISIBLE __weak @@ -84,14 +87,15 @@ extern void __force_link_error__only_in_16bit(void) __noreturn; # define ASSERT32FLAT() __force_link_error__only_in_32bit_flat() #else # define VISIBLE16 __section(".discard.func16." UNIQSEC) noinline __weak -# define VISIBLE32FLAT __VISIBLE +# define VISIBLE32FLAT __section(".text.runtime." UNIQSEC) __VISIBLE +# define VISIBLE32INIT __section(".text.init." UNIQSEC) __VISIBLE # define VISIBLE32SEG __section(".discard.func32seg." UNIQSEC) noinline __weak # define VAR16 __section(".discard.var16." UNIQSEC) # define VAR16VISIBLE VAR16 __VISIBLE __weak # define VAR16EXPORT VAR16VISIBLE # define VAR16FIXED(addr) VAR16VISIBLE # define VAR32SEG __section(".discard.var32seg." UNIQSEC) -# define VAR32FLATVISIBLE __VISIBLE +# define VAR32FLATVISIBLE __section(".data.runtime." UNIQSEC) __VISIBLE # define ASM16(code) # define ASM32FLAT(code) __ASM(code) # define ASSERT16() __force_link_error__only_in_16bit() diff --git a/tools/layoutrom.py b/tools/layoutrom.py index ac6fd14..ca8cb3c 100755 --- a/tools/layoutrom.py +++ b/tools/layoutrom.py @@ -140,9 +140,9 @@ def fitSections(sections, fillsections): return firstfixed
# Return the subset of sections with a given name prefix -def getSectionsPrefix(sections, fileid, prefix): +def getSectionsPrefix(sections, category, prefix): return [section for section in sections - if section.fileid == fileid and section.name.startswith(prefix)] + if section.category == category and section.name.startswith(prefix)]
def doLayout(sections): # Determine 16bit positions @@ -176,13 +176,25 @@ def doLayout(sections): textsections + rodatasections + datasections + bsssections , code32seg_start + BUILD_BIOS_ADDR, 16)
+ # Determine 32flat init positions + textsections = getSectionsPrefix(sections, '32init', '.text.') + rodatasections = getSectionsPrefix(sections, '32init', '.rodata') + datasections = getSectionsPrefix(sections, '32init', '.data.') + bsssections = getSectionsPrefix(sections, '32init', '.bss.') + + code32init_start = setSectionsStart( + textsections + rodatasections + datasections + bsssections + , code32flat_start, 16) + # Print statistics size16 = BUILD_BIOS_SIZE - code16_start size32seg = code16_start - code32seg_start size32flat = code32seg_start + BUILD_BIOS_ADDR - code32flat_start + size32init = code32flat_start - code32init_start print "16bit size: %d" % size16 print "32bit segmented size: %d" % size32seg print "32bit flat size: %d" % size32flat + print "32bit flat init size: %d" % size32init
###################################################################### @@ -271,6 +283,11 @@ def writeLinkerScripts(sections, entrysym, out16, out32seg, out32flat): """ % (entrysym.name, entrysym.section.finalloc + entrysym.offset + BUILD_BIOS_ADDR, code32flat_start) + + outRelSections(getSectionsPrefix(sections32flat, '32init', '') + , 'code32flat_start') + + """ + code32init_end = ABSOLUTE(.) ; +""" + outRelSections(getSectionsPrefix(sections32flat, '32flat', '') , 'code32flat_start') + """ @@ -293,6 +310,33 @@ PHDRS
###################################################################### +# Detection of init code +###################################################################### + +def markRuntime(section, sections): + if (section is None or not section.keep or section.category is not None + or '.init.' in section.name or section.fileid != '32flat'): + return + section.category = '32flat' + # Recursively mark all sections this section points to + for reloc in section.relocs: + markRuntime(reloc.symbol.section, sections) + +def findInit(sections): + # Recursively find and mark all "runtime" sections. + for section in sections: + if '.runtime.' in section.name or '.export.' in section.name: + markRuntime(section, sections) + for section in sections: + if section.category is not None: + continue + if section.fileid == '32flat': + section.category = '32init' + else: + section.category = section.fileid + + +###################################################################### # Section garbage collection ######################################################################
@@ -347,7 +391,7 @@ def gc(info16, info32seg, info32flat):
class Section: name = size = alignment = fileid = relocs = None - finalloc = keep = None + finalloc = category = keep = None class Reloc: offset = type = symbol = None class Symbol: @@ -437,6 +481,9 @@ def main(): # Figure out which sections to keep. sections = gc(info16, info32seg, info32flat)
+ # Separate 32bit flat into runtime and init parts + findInit(sections) + # Determine the final memory locations of each kept section. # locsX = [(addr, sectioninfo), ...] doLayout(sections)
Add support for determining which relocations need to be adjusted to relocate the "32bit flat init" code. Place those relocations in the output object. --- tools/layoutrom.py | 63 +++++++++++++++++++++++++++++++++++++++++++++++++-- 1 files changed, 60 insertions(+), 3 deletions(-)
diff --git a/tools/layoutrom.py b/tools/layoutrom.py index ca8cb3c..7eca502 100755 --- a/tools/layoutrom.py +++ b/tools/layoutrom.py @@ -241,7 +241,7 @@ def getSectionsFile(sections, fileid, defaddr=0): return sections, pos
# Layout the 32bit segmented code. This places the code as high as possible. -def writeLinkerScripts(sections, entrysym, out16, out32seg, out32flat): +def writeLinkerScripts(sections, entrysym, genreloc, out16, out32seg, out32flat): # Write 16bit linker script sections16, code16_start = getSectionsFile(sections, '16') output = open(out16, 'wb') @@ -274,15 +274,26 @@ def writeLinkerScripts(sections, entrysym, out16, out32seg, out32flat): # Write 32flat linker script sections32flat, code32flat_start = getSectionsFile( sections, '32flat', code32seg_start) + relocstr = "" + relocminalign = 0 + if genreloc: + # Generate relocations + relocstr, size, relocminalign = genRelocs(sections) + code32flat_start -= size output = open(out32flat, 'wb') output.write(COMMONHEADER + outXRefs(sections32flat) + """ %s = 0x%x ; + _reloc_min_align = 0x%x ; code32flat_start = 0x%x ; .text code32flat_start : { """ % (entrysym.name, entrysym.section.finalloc + entrysym.offset + BUILD_BIOS_ADDR, - code32flat_start) + relocminalign, code32flat_start) + + relocstr + + """ + code32init_start = ABSOLUTE(.) ; +""" + outRelSections(getSectionsPrefix(sections32flat, '32init', '') , 'code32flat_start') + """ @@ -313,6 +324,51 @@ PHDRS # Detection of init code ######################################################################
+# Determine init section relocations +def genRelocs(sections): + absrelocs = [] + relrelocs = [] + initrelocs = [] + minalign = 16 + for section in sections: + if section.category == '32init' and section.align > minalign: + minalign = section.align + for reloc in section.relocs: + symbol = reloc.symbol + if symbol.section is None: + continue + relocpos = section.finalloc + reloc.offset + if (reloc.type == 'R_386_32' and section.category == '32init' + and symbol.section.category == '32init'): + # Absolute relocation + absrelocs.append(relocpos) + elif (reloc.type == 'R_386_PC32' and section.category == '32init' + and symbol.section.category != '32init'): + # Relative relocation + relrelocs.append(relocpos) + elif (section.category != '32init' + and symbol.section.category == '32init'): + # Relocation to the init section + if section.fileid in ('16', '32seg'): + relocpos += BUILD_BIOS_ADDR + initrelocs.append(relocpos) + absrelocs.sort() + relrelocs.sort() + initrelocs.sort() + out = (" _reloc_abs_start = ABSOLUTE(.) ;\n" + + "".join(["LONG(0x%x - code32init_start)\n" % (pos,) + for pos in absrelocs]) + + " _reloc_abs_end = ABSOLUTE(.) ;\n" + + " _reloc_rel_start = ABSOLUTE(.) ;\n" + + "".join(["LONG(0x%x - code32init_start)\n" % (pos,) + for pos in relrelocs]) + + " _reloc_rel_end = ABSOLUTE(.) ;\n" + + " _reloc_init_start = ABSOLUTE(.) ;\n" + + "".join(["LONG(0x%x - code32flat_start)\n" % (pos,) + for pos in initrelocs]) + + " _reloc_init_end = ABSOLUTE(.) ;\n") + return out, len(absrelocs + relrelocs + initrelocs) * 4, minalign + def markRuntime(section, sections): if (section is None or not section.keep or section.category is not None or '.init.' in section.name or section.fileid != '32flat'): @@ -490,7 +546,8 @@ def main():
# Write out linker script files. entrysym = info16[1]['post32'] - writeLinkerScripts(sections, entrysym, out16, out32seg, out32flat) + genreloc = '_reloc_abs_start' in info32flat[1] + writeLinkerScripts(sections, entrysym, genreloc, out16, out32seg, out32flat)
if __name__ == '__main__': main()
Add several methods for rebooting a processor.
Detect a rerun of POST under coreboot and attempt to reboot the processor. --- src/ioport.h | 1 + src/pci.c | 10 ++++++++++ src/pci.h | 1 + src/post.c | 27 +++++++++++++++++++++++++++ src/ps2port.c | 16 ++++++++++++++-- src/ps2port.h | 3 +-- 6 files changed, 54 insertions(+), 4 deletions(-)
diff --git a/src/ioport.h b/src/ioport.h index ba099a7..5bd7f28 100644 --- a/src/ioport.h +++ b/src/ioport.h @@ -50,6 +50,7 @@ #define PORT_FD_DIR 0x03f7 #define PORT_SERIAL1 0x03f8 #define PORT_PCI_CMD 0x0cf8 +#define PORT_PCI_REBOOT 0x0cf9 #define PORT_PCI_DATA 0x0cfc #define PORT_BIOS_DEBUG 0x0402 #define PORT_QEMU_CFG_CTL 0x0510 diff --git a/src/pci.c b/src/pci.c index 611d0e2..115689d 100644 --- a/src/pci.c +++ b/src/pci.c @@ -215,3 +215,13 @@ int pci_find_init_device(const struct pci_device_id *ids, void *arg) } return -1; } + +void +pci_reboot(void) +{ + u8 v = inb(PORT_PCI_REBOOT) & ~6; + outb(v|2, PORT_PCI_REBOOT); /* Request hard reset */ + udelay(50); + outb(v|6, PORT_PCI_REBOOT); /* Actually do the reset */ + udelay(50); +} diff --git a/src/pci.h b/src/pci.h index 9c3108c..64bd43b 100644 --- a/src/pci.h +++ b/src/pci.h @@ -94,6 +94,7 @@ struct pci_device_id {
int pci_init_device(const struct pci_device_id *table, u16 bdf, void *arg); int pci_find_init_device(const struct pci_device_id *ids, void *arg); +void pci_reboot(void);
// pirtable.c void create_pirtable(void); diff --git a/src/post.c b/src/post.c index 6cd94ce..f50312e 100644 --- a/src/post.c +++ b/src/post.c @@ -244,6 +244,26 @@ post(void) memmap_finalize(); }
+// Attempt to invoke a hard-reboot. +static void +tryReboot(void) +{ + dprintf(1, "Attempting a hard reboot\n"); + + // Try keyboard controller reboot. + i8042_reboot(); + + // Try PCI 0xcf9 reboot + pci_reboot(); + + // Try triple fault + asm volatile("int3"); + + panic("Could not reboot"); +} + +static int HaveRunPost; + // 32-bit entry point. void VISIBLE32FLAT _start(void) @@ -253,9 +273,16 @@ _start(void) debug_serial_setup(); dprintf(1, "Start bios (version %s)\n", VERSION);
+ if (CONFIG_COREBOOT && HaveRunPost) + // This is a soft reboot - invoke a hard reboot. + tryReboot(); + // Allow writes to modify bios area (0xf0000) make_bios_writable();
+ if (CONFIG_COREBOOT) + HaveRunPost = 1; + // Perform main setup code. post();
diff --git a/src/ps2port.c b/src/ps2port.c index ccbd2f6..d1e6d48 100644 --- a/src/ps2port.c +++ b/src/ps2port.c @@ -51,7 +51,7 @@ i8042_wait_write(void) return -1; }
-int +static int i8042_flush(void) { dprintf(7, "i8042_flush\n"); @@ -102,7 +102,7 @@ __i8042_command(int command, u8 *param) return 0; }
-int +static int i8042_command(int command, u8 *param) { dprintf(7, "i8042_command cmd=%x\n", command); @@ -128,6 +128,18 @@ i8042_aux_write(u8 c) return i8042_command(I8042_CMD_AUX_SEND, &c); }
+void +i8042_reboot(void) +{ + int i; + for (i=0; i<10; i++) { + i8042_wait_write(); + udelay(50); + outb(0xfe, PORT_PS2_STATUS); /* pulse reset low */ + udelay(50); + } +} +
/**************************************************************** * Device commands. diff --git a/src/ps2port.h b/src/ps2port.h index afb0e78..dcae391 100644 --- a/src/ps2port.h +++ b/src/ps2port.h @@ -55,8 +55,7 @@ #define I8042_CTR_XLATE 0x40
// functions -int i8042_flush(void); -int i8042_command(int command, u8 *param); +void i8042_reboot(void); int ps2_kbd_command(int command, u8 *param); int ps2_mouse_command(int command, u8 *param); void ps2port_setup(void);
When CONFIG_OPTIONROMS_DEPLOYED is set, there is no need to copy the 0xc0000-0xf0000 space around - SeaBIOS ignores what's there when it that mode. --- src/shadow.c | 27 ++++++++++----------------- 1 files changed, 10 insertions(+), 17 deletions(-)
diff --git a/src/shadow.c b/src/shadow.c index e91e54e..391257b 100644 --- a/src/shadow.c +++ b/src/shadow.c @@ -1,6 +1,6 @@ // Support for enabling/disabling BIOS ram shadowing. // -// Copyright (C) 2008,2009 Kevin O'Connor kevin@koconnor.net +// Copyright (C) 2008-2010 Kevin O'Connor kevin@koconnor.net // Copyright (C) 2006 Fabrice Bellard // // This file may be distributed under the terms of the GNU LGPLv3 license. @@ -11,16 +11,8 @@ #include "pci_ids.h" // PCI_VENDOR_ID_INTEL #include "dev-i440fx.h"
-// Test if 'addr' is in the range from 'start'..'start+size' -#define IN_RANGE(addr, start, size) ({ \ - u32 __addr = (addr); \ - u32 __start = (start); \ - u32 __size = (size); \ - (__addr - __start < __size); \ - }) - // On the emulators, the bios at 0xf0000 is also at 0xffff0000 -#define BIOS_SRC_ADDR 0xffff0000 +#define BIOS_SRC_OFFSET 0xfff00000
// Enable shadowing and copy bios. static void @@ -30,9 +22,9 @@ __make_bios_writable_intel(u16 bdf, u32 pam0) int clear = 0; int i; for (i=0; i<6; i++) { - u32 pam = pam0 + 1 + i; - int reg = pci_config_readb(bdf, pam); - if ((reg & 0x11) != 0x11) { + u32 pam = pam0 + 1 + i; + int reg = pci_config_readb(bdf, pam); + if (CONFIG_OPTIONROMS_DEPLOYED && (reg & 0x11) != 0x11) { // Need to copy optionroms to work around qemu implementation void *mem = (void*)(BUILD_ROM_START + i * 32*1024); memcpy((void*)BUILD_BIOS_TMP_ADDR, mem, 32*1024); @@ -54,7 +46,9 @@ __make_bios_writable_intel(u16 bdf, u32 pam0) return;
// Copy bios. - memcpy((void*)BUILD_BIOS_ADDR, (void*)BIOS_SRC_ADDR, BUILD_BIOS_SIZE); + extern u8 code32flat_start[], code32flat_end[]; + memcpy(code32flat_start, code32flat_start + BIOS_SRC_OFFSET + , code32flat_end - code32flat_start); }
void @@ -66,8 +60,7 @@ make_bios_writable_intel(u16 bdf, u32 pam0) // if ram isn't backing the bios segment when shadowing is // disabled, the code itself wont be in memory. So, run the // code from the high-memory flash location. - u32 pos = (u32)__make_bios_writable_intel - BUILD_BIOS_ADDR + - BIOS_SRC_ADDR; + u32 pos = (u32)__make_bios_writable_intel + BIOS_SRC_OFFSET; void (*func)(u16 bdf, u32 pam0) = (void*)pos; func(bdf, pam0); return; @@ -114,7 +107,7 @@ make_bios_writable(void)
dprintf(3, "enabling shadow ram\n");
- // at this point, staticlly alloacted variable can't written. + // at this point, statically allocated variables can't be written. // so stack should be used.
// Locate chip controlling ram shadowing.
Extend the hard-reboot logic to qemu and kvm. On qemu, a reboot will not reset the memory settings for 0xc0000-0xfffff, so copy that memory area manually before rebooting. Unfortunately, kvm does not keep a pristine copy of the BIOS at 0xffff0000, so detect that case and shutdown the machine. --- Makefile | 6 +++--- src/apm.c | 14 ++++++++++---- src/post.c | 16 +++++++++++----- src/shadow.c | 13 +++++++++++++ src/util.h | 4 +++- 5 files changed, 40 insertions(+), 13 deletions(-)
diff --git a/Makefile b/Makefile index f62c1cc..9d412f1 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ # SeaBIOS build system # -# Copyright (C) 2008,2009 Kevin O'Connor kevin@koconnor.net +# Copyright (C) 2008-2010 Kevin O'Connor kevin@koconnor.net # # This file may be distributed under the terms of the GNU LGPLv3 license.
@@ -15,8 +15,8 @@ SRCBOTH=misc.c pmm.c stacks.c output.c util.c block.c floppy.c ata.c mouse.c \ kbd.c pci.c serial.c clock.c pic.c cdrom.c ps2port.c smp.c resume.c \ pnpbios.c pirtable.c vgahooks.c ramdisk.c pcibios.c blockcmd.c \ usb.c usb-uhci.c usb-ohci.c usb-ehci.c usb-hid.c usb-msc.c \ - virtio-ring.c virtio-pci.c virtio-blk.c -SRC16=$(SRCBOTH) system.c disk.c apm.c font.c + virtio-ring.c virtio-pci.c virtio-blk.c apm.c +SRC16=$(SRCBOTH) system.c disk.c font.c SRC32FLAT=$(SRCBOTH) post.c shadow.c memmap.c coreboot.c boot.c \ acpi.c smm.c mptable.c smbios.c pciinit.c optionroms.c mtrr.c \ lzmadecode.c bootsplash.c jpeg.c usb-hub.c paravirt.c dev-i440fx.c diff --git a/src/apm.c b/src/apm.c index 1b151e9..2029ae2 100644 --- a/src/apm.c +++ b/src/apm.c @@ -105,6 +105,15 @@ handle_155306(struct bregs *regs) set_success(regs); }
+void +apm_shutdown(void) +{ + irq_disable(); + out_str("Shutdown"); + for (;;) + hlt(); +} + // APM Set Power State static void handle_155307(struct bregs *regs) @@ -121,10 +130,7 @@ handle_155307(struct bregs *regs) out_str("Suspend"); break; case 3: - irq_disable(); - out_str("Shutdown"); - for (;;) - hlt(); + apm_shutdown(); break; } set_success(regs); diff --git a/src/post.c b/src/post.c index f50312e..f1ab6be 100644 --- a/src/post.c +++ b/src/post.c @@ -244,12 +244,21 @@ post(void) memmap_finalize(); }
+static int HaveRunPost; + // Attempt to invoke a hard-reboot. static void tryReboot(void) { dprintf(1, "Attempting a hard reboot\n");
+ // Setup for reset on qemu. + if (! CONFIG_COREBOOT) { + qemu_prep_reset(); + if (HaveRunPost) + apm_shutdown(); + } + // Try keyboard controller reboot. i8042_reboot();
@@ -262,8 +271,6 @@ tryReboot(void) panic("Could not reboot"); }
-static int HaveRunPost; - // 32-bit entry point. void VISIBLE32FLAT _start(void) @@ -273,15 +280,14 @@ _start(void) debug_serial_setup(); dprintf(1, "Start bios (version %s)\n", VERSION);
- if (CONFIG_COREBOOT && HaveRunPost) + if (HaveRunPost) // This is a soft reboot - invoke a hard reboot. tryReboot();
// Allow writes to modify bios area (0xf0000) make_bios_writable();
- if (CONFIG_COREBOOT) - HaveRunPost = 1; + HaveRunPost = 1;
// Perform main setup code. post(); diff --git a/src/shadow.c b/src/shadow.c index 391257b..ed530e0 100644 --- a/src/shadow.c +++ b/src/shadow.c @@ -136,3 +136,16 @@ make_bios_readonly(void) dprintf(1, "Unable to lock ram - bridge not found\n"); } } + +void +qemu_prep_reset(void) +{ + if (CONFIG_COREBOOT) + return; + // QEMU doesn't map 0xc0000-0xfffff back to the original rom on a + // reset, so do that manually before invoking a hard reset. + make_bios_writable(); + extern u8 code32flat_start[], code32flat_end[]; + memcpy(code32flat_start, code32flat_start + BIOS_SRC_OFFSET + , code32flat_end - code32flat_start); +} diff --git a/src/util.h b/src/util.h index 3d68d45..d2003c1 100644 --- a/src/util.h +++ b/src/util.h @@ -1,6 +1,6 @@ // Basic x86 asm functions and function defs. // -// Copyright (C) 2008,2009 Kevin O'Connor kevin@koconnor.net +// Copyright (C) 2008-2010 Kevin O'Connor kevin@koconnor.net // // This file may be distributed under the terms of the GNU LGPLv3 license. #ifndef __UTIL_H @@ -327,6 +327,7 @@ void useRTC(void); void releaseRTC(void);
// apm.c +void apm_shutdown(void); void handle_1553(struct bregs *regs);
// pcibios.c @@ -338,6 +339,7 @@ void make_bios_writable(void); void make_bios_readonly(void); void make_bios_writable_intel(u16 bdf, u32 pam0); void make_bios_readonly_intel(u16 bdf, u32 pam0); +void qemu_prep_reset(void);
// smm.c void smm_save_and_copy(void);
--- src/util.h | 9 +++++++++ 1 files changed, 9 insertions(+), 0 deletions(-)
diff --git a/src/util.h b/src/util.h index d2003c1..c27037a 100644 --- a/src/util.h +++ b/src/util.h @@ -435,9 +435,18 @@ static inline void *memalign_low(u32 align, u32 size) { static inline void *memalign_high(u32 align, u32 size) { return pmm_malloc(&ZoneHigh, PMM_DEFAULT_HANDLE, size, align); } +static inline void *memalign_tmplow(u32 align, u32 size) { + return pmm_malloc(&ZoneTmpLow, PMM_DEFAULT_HANDLE, size, align); +} static inline void *memalign_tmphigh(u32 align, u32 size) { return pmm_malloc(&ZoneTmpHigh, PMM_DEFAULT_HANDLE, size, align); } +static inline void *memalign_tmp(u32 align, u32 size) { + void *ret = memalign_tmphigh(align, size); + if (ret) + return ret; + return memalign_tmplow(align, size); +} static inline void free(void *data) { pmm_free(data); }
Move the shadow calls from _start() to post() - this ensures all the one time init code is in post().
Also, reorg post so that malloc setup is done before ivt/bda/ebda setup.
This is in preparation for relocating the 32bit flat init code. --- src/pmm.c | 3 -- src/post.c | 91 +++++++++++++++++++++++++++++++++++++++-------------------- 2 files changed, 60 insertions(+), 34 deletions(-)
diff --git a/src/pmm.c b/src/pmm.c index 682be39..f5e58ad 100644 --- a/src/pmm.c +++ b/src/pmm.c @@ -252,9 +252,6 @@ malloc_finalize(void) add_e820((u32)info->dataend, giveback, E820_RAM); dprintf(1, "Returned %d bytes of ZoneHigh\n", giveback); } - - // Clear low-memory allocations. - memset((void*)BUILD_STACK_ADDR, 0, BUILD_EBDA_MINIMUM - BUILD_STACK_ADDR); }
diff --git a/src/post.c b/src/post.c index f1ab6be..4d99935 100644 --- a/src/post.c +++ b/src/post.c @@ -25,6 +25,11 @@ #include "ps2port.h" // ps2port_setup #include "virtio-blk.h" // virtio_blk_setup
+ +/**************************************************************** + * BIOS init + ****************************************************************/ + static void init_ivt(void) { @@ -77,13 +82,16 @@ init_bda(void)
int esize = EBDA_SIZE_START; SET_BDA(mem_size_kb, BUILD_LOWRAM_END/1024 - esize); - u16 eseg = EBDA_SEGMENT_START; - SET_BDA(ebda_seg, eseg); + u16 ebda_seg = EBDA_SEGMENT_START; + SET_BDA(ebda_seg, ebda_seg);
// Init ebda struct extended_bios_data_area_s *ebda = get_ebda_ptr(); memset(ebda, 0, sizeof(*ebda)); ebda->size = esize; + + add_e820((u32)MAKE_FLATPTR(ebda_seg, 0), GET_EBDA2(ebda_seg, size) * 1024 + , E820_RESERVED); }
static void @@ -120,9 +128,6 @@ ram_probe(void) add_e820(BUILD_LOWRAM_END, BUILD_BIOS_ADDR-BUILD_LOWRAM_END, E820_HOLE);
// Mark known areas as reserved. - u16 ebda_seg = get_ebda_seg(); - add_e820((u32)MAKE_FLATPTR(ebda_seg, 0), GET_EBDA2(ebda_seg, size) * 1024 - , E820_RESERVED); add_e820(BUILD_BIOS_ADDR, BUILD_BIOS_SIZE, E820_RESERVED);
u32 count = qemu_cfg_e820_entries(); @@ -177,20 +182,30 @@ init_hw(void) virtio_blk_setup(); }
+// Begin the boot process by invoking an int0x19 in 16bit mode. +static void +startBoot(void) +{ + // Clear low-memory allocations (required by PMM spec). + memset((void*)BUILD_STACK_ADDR, 0, BUILD_EBDA_MINIMUM - BUILD_STACK_ADDR); + + dprintf(3, "Jump to int19\n"); + struct bregs br; + memset(&br, 0, sizeof(br)); + br.flags = F_IF; + call16_int(0x19, &br); +} + // Main setup code. -void VISIBLE32INIT -post(void) +static void +maininit(void) { - // Detect and init ram. + // Setup ivt/bda/ebda init_ivt(); init_bda(); - memmap_setup(); - qemu_cfg_port_probe(); - ram_probe(); - malloc_setup(); - thread_setup();
// Init base pc hardware. + thread_setup(); pic_setup(); timer_setup(); mathcp_setup(); @@ -242,10 +257,42 @@ post(void) pmm_finalize(); malloc_finalize(); memmap_finalize(); + + // Setup bios checksum. + BiosChecksum -= checksum((u8*)BUILD_BIOS_ADDR, BUILD_BIOS_SIZE); + + // Write protect bios memory. + make_bios_readonly(); + + // Invoke int 19 to start boot process. + startBoot(); }
static int HaveRunPost;
+// Start of Power On Self Test (POST) - the BIOS initilization phase. +void VISIBLE32INIT +post(void) +{ + // Allow writes to modify bios area (0xf0000) + make_bios_writable(); + + HaveRunPost = 1; + + // Detect ram and setup internal malloc. + memmap_setup(); + qemu_cfg_port_probe(); + ram_probe(); + malloc_setup(); + + maininit(); +} + + +/**************************************************************** + * POST entry point + ****************************************************************/ + // Attempt to invoke a hard-reboot. static void tryReboot(void) @@ -284,24 +331,6 @@ _start(void) // This is a soft reboot - invoke a hard reboot. tryReboot();
- // Allow writes to modify bios area (0xf0000) - make_bios_writable(); - - HaveRunPost = 1; - // Perform main setup code. post(); - - // Setup bios checksum. - BiosChecksum -= checksum((u8*)BUILD_BIOS_ADDR, BUILD_BIOS_SIZE); - - // Write protect bios memory. - make_bios_readonly(); - - // Invoke int 19 to start boot process. - dprintf(3, "Jump to int19\n"); - struct bregs br; - memset(&br, 0, sizeof(br)); - br.flags = F_IF; - call16_int(0x19, &br); }
Add support for moving the 32bit init code out of the e/f-segments and into temporary ram. Update the relocations in the code so that it can live at its new address.
This frees up memory for other uses in the e/f segments. --- src/config.h | 2 ++ src/post.c | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 52 insertions(+), 2 deletions(-)
diff --git a/src/config.h b/src/config.h index 4572ee5..f9bf3b8 100644 --- a/src/config.h +++ b/src/config.h @@ -26,6 +26,8 @@ #define CONFIG_THREADS 1 // Allow hardware init to run in parallel with optionrom execution #define CONFIG_THREAD_OPTIONROMS 0 +// Support relocating the one time initialization code to high memory +#define CONFIG_RELOCATE_INIT 1 // Support int13 disk/floppy drive functions #define CONFIG_DRIVES 1 // Support floppy drive access diff --git a/src/post.c b/src/post.c index 4d99935..a93affc 100644 --- a/src/post.c +++ b/src/post.c @@ -183,7 +183,7 @@ init_hw(void) }
// Begin the boot process by invoking an int0x19 in 16bit mode. -static void +void VISIBLE32FLAT startBoot(void) { // Clear low-memory allocations (required by PMM spec). @@ -268,9 +268,57 @@ maininit(void) startBoot(); }
+ +/**************************************************************** + * Code relocation + ****************************************************************/ + +static void +updateRelocs(void *dest, u32 *rstart, u32 *rend, u32 delta) +{ + u32 *reloc; + for (reloc = rstart; reloc < rend; reloc++) + *((u32*)(dest + *reloc)) += delta; +} + +// Start of Power On Self Test - the BIOS initilization. This +// function sets up for and attempts relocation of the init code. +static void +reloc_init(void) +{ + if (!CONFIG_RELOCATE_INIT) { + maininit(); + return; + } + extern u8 code32flat_start[]; + extern u8 _reloc_min_align[]; + extern u32 _reloc_abs_start[], _reloc_abs_end[]; + extern u32 _reloc_rel_start[], _reloc_rel_end[]; + extern u32 _reloc_init_start[], _reloc_init_end[]; + extern u8 code32init_start[], code32init_end[]; + + u32 initsize = code32init_end - code32init_start; + u32 align = (u32)&_reloc_min_align; + void *dest = memalign_tmp(align, initsize); + if (!dest) + panic("No space for init relocation.\n"); + + dprintf(1, "Relocating init from %p to %p (size %d)\n" + , code32init_start, dest, initsize); + s32 delta = dest - (void*)code32init_start; + memcpy(dest, code32init_start, initsize); + updateRelocs(dest, _reloc_abs_start, _reloc_abs_end, delta); + updateRelocs(dest, _reloc_rel_start, _reloc_rel_end, -delta); + updateRelocs(code32flat_start, _reloc_init_start, _reloc_init_end, delta); + void (*func)(void) = (void*)maininit + delta; + barrier(); + func(); +} + static int HaveRunPost;
// Start of Power On Self Test (POST) - the BIOS initilization phase. +// This function sets up for and attempts relocation of the init code. void VISIBLE32INIT post(void) { @@ -285,7 +333,7 @@ post(void) ram_probe(); malloc_setup();
- maininit(); + reloc_init(); }
The space in the e-segment is available for option roms after relocating the init code.
Also, zero all of the option rom space when !CONFIG_OPTIONROMS_DEPLOYED. --- src/optionroms.c | 18 +++++++++++------- src/pmm.c | 5 ----- 2 files changed, 11 insertions(+), 12 deletions(-)
diff --git a/src/optionroms.c b/src/optionroms.c index cde5b4d..f26a3aa 100644 --- a/src/optionroms.c +++ b/src/optionroms.c @@ -168,14 +168,15 @@ get_pci_rom(struct rom_header *rom) return pci; }
+// Return start of code in 0xc0000-0xf0000 space. +static inline u32 _max_rom(void) { + extern u8 code32flat_start[], code32init_end[]; + return CONFIG_RELOCATE_INIT ? (u32)code32flat_start : (u32)code32init_end; +} // Return the memory position up to which roms may be located. -static inline u32 -max_rom(void) -{ - extern u8 code32flat_start[]; - if ((u32)code32flat_start > BUILD_BIOS_ADDR) - return BUILD_BIOS_ADDR; - return (u32)code32flat_start; +static inline u32 max_rom(void) { + u32 end = _max_rom(); + return end > BUILD_BIOS_ADDR ? BUILD_BIOS_ADDR : end; }
// Copy a rom to its permanent location below 1MiB @@ -434,6 +435,9 @@ vga_setup(void) // Option roms are already deployed on the system. init_optionrom((void*)BUILD_ROM_START, 0, 1); } else { + // Clear option rom memory + memset((void*)RomEnd, 0, _max_rom() - RomEnd); + // Find and deploy PCI VGA rom. int bdf = VGAbdf = pci_find_vga(); if (bdf >= 0) diff --git a/src/pmm.c b/src/pmm.c index f5e58ad..bb90ff0 100644 --- a/src/pmm.c +++ b/src/pmm.c @@ -197,11 +197,6 @@ malloc_setup(void) ZoneTmpLow.info = ZoneTmpHigh.info = NULL;
// Clear memory in 0xf0000 area. - extern u8 code32flat_start[]; - if ((u32)code32flat_start > BUILD_BIOS_ADDR) - // Clear unused parts of f-segment - memset((void*)BUILD_BIOS_ADDR, 0 - , (u32)code32flat_start - BUILD_BIOS_ADDR); memset(BiosTableSpace, 0, CONFIG_MAX_BIOSTABLE);
// Populate temp high ram
Kevin O'Connor wrote:
The one-time init code could be called again on a soft-reboot, but SeaBIOS now detects this case and attempts a hard-reboot.
Could it just skip relocation on warm boots? Or is there a problem with knowing where things were relocated to?
qemu and kvm don't appear to have a reliable way to hard-reboot
- normal reboots don't reset the 0xc0000-0xfffff memory.
That should probably be fixed.
kvm doesn't keep a pristine copy of the bios
Ditto.
//Peter
On Thu, Sep 16, 2010 at 05:18:33AM +0200, Peter Stuge wrote:
Kevin O'Connor wrote:
The one-time init code could be called again on a soft-reboot, but SeaBIOS now detects this case and attempts a hard-reboot.
Could it just skip relocation on warm boots?
It's possible, but it requires reserving the ram used to store the init code. It seems like a waste to reserve memory from the OS for something that is already in flash and used so infrequently.
Or is there a problem with knowing where things were relocated to?
It's tricky - harder than doing a hard-reboot - but possible. (The difficulty is in resetting the malloc zones while the relocated code is in one of the malloc zones.)
qemu and kvm don't appear to have a reliable way to hard-reboot
- normal reboots don't reset the 0xc0000-0xfffff memory.
That should probably be fixed.
kvm doesn't keep a pristine copy of the bios
Ditto.
Yeah. It looks like kvm is updating the ram at 0xffff0000 when writes are done to 0xf0000. It's not ideal.
-Kevin
On Wed, Sep 15, 2010 at 11:34:31PM -0400, Kevin O'Connor wrote:
On Thu, Sep 16, 2010 at 05:18:33AM +0200, Peter Stuge wrote:
Kevin O'Connor wrote:
The one-time init code could be called again on a soft-reboot, but SeaBIOS now detects this case and attempts a hard-reboot.
Could it just skip relocation on warm boots?
It's possible, but it requires reserving the ram used to store the init code. It seems like a waste to reserve memory from the OS for something that is already in flash and used so infrequently.
Or is there a problem with knowing where things were relocated to?
It's tricky - harder than doing a hard-reboot - but possible. (The difficulty is in resetting the malloc zones while the relocated code is in one of the malloc zones.)
qemu and kvm don't appear to have a reliable way to hard-reboot
- normal reboots don't reset the 0xc0000-0xfffff memory.
That should probably be fixed.
kvm doesn't keep a pristine copy of the bios
Ditto.
Yeah. It looks like kvm is updating the ram at 0xffff0000 when writes are done to 0xf0000. It's not ideal.
AFAIK immediately after reset memory accesses to 0xffff0000 and 0xf0000 are directed to exactly same ROM chip. Are you saying that after shadowing BIOS at location 0xf0000 and modifying it in memory BIOS copy as seeing at 0xffff0000 changes too?
-- Gleb.
Gleb Natapov wrote:
It looks like kvm is updating the ram at 0xffff0000 when writes are done to 0xf0000. It's not ideal.
AFAIK immediately after reset memory accesses to 0xffff0000 and 0xf0000 are directed to exactly same ROM chip.
No. I'll try to explain. It is messy, because of all the legacy..
After reset and until CS is reloaded, it looks a lot like the CPU is running in real mode from CS:IP f000:fff0. In practise, CS is set up (since 386) so that this actually fetches from fffffff0. Again, it goes away as soon as CS is reloaded, e.g. after a far jmp or call. (The details are similar to the flat real AKA unreal mode trick IIRC.)
The *only* place that the ROM chip is ever accessible is at top of 4GB. How much of the ROM that is actually set up to be decoded on reset is quite chipset specific.
Physical address 0xf00000 is RAM. It's true that most firmware copies at least parts of itself to top 64kb of 1MB (after RAM init of course) but this has absolutely nothing to do with the ROM chip.
It's only done to provide the 1980 BIOS interface that the entire PC industry insists on depending on for eternity.
Are you saying that after shadowing BIOS at location 0xf0000 and modifying it in memory BIOS copy as seeing at 0xffff0000 changes too?
I hope Kevin can say more about the circumstances to pin this down.
The shadowing that you mention is precisely the copying of ROM contents into RAM, and possibly modifying it along the way or after. Shadowing may not really be an accurate name for the base BIOS since they tend to be self modifying, in order to work around all legacy limitations. The term is maybe mostly suitable for option ROMs, but I understand you. :)
In any case, a write to physical f0000 should never affect physical ffff0000.
//Peter
On 09/16/2010 06:24 PM, Peter Stuge wrote:
After reset and until CS is reloaded, it looks a lot like the CPU is running in real mode from CS:IP f000:fff0. In practise, CS is set up (since 386) so that this actually fetches from fffffff0. Again, it goes away as soon as CS is reloaded, e.g. after a far jmp or call. (The details are similar to the flat real AKA unreal mode trick IIRC.)
The *only* place that the ROM chip is ever accessible is at top of 4GB. How much of the ROM that is actually set up to be decoded on reset is quite chipset specific.
Physical address 0xf00000 is RAM. It's true that most firmware copies at least parts of itself to top 64kb of 1MB (after RAM init of course) but this has absolutely nothing to do with the ROM chip.
So, the copy has to happen before the far jump, but after the DRAM controller has been initialized?
This contradicts http://download.intel.com/design/intarch/datashts/29055002.pdf:
3.1.3. BIOS MEMORY
The PIIX/PIIX3 supports 512 Kbytes of BIOS space. This includes the normal 128-Kbyte space plus an additional 384-Kbyte BIOS space (known as the extended BIOS area). The XBCS Register provides BIOS space access control. Access to the lower 64-Kbyte block of the 128-Kbyte space and the extended BIOS space can be individually enabled/disabled. In addition, write protection can be programmed for the entire BIOS space.
PCI Access to BIOS Memory
The 128-Kbyte BIOS memory space is located at 000E0000–000FFFFFh (top of 1 Mbyte) and is aliased at FFFE0000h (top of 4 Gbytes). This 128-Kbyte byte block is split into two 64-Kbyte blocks. Accesses to the top 64 Kbytes (000F0000–000FFFFFh) are forwarded to the ISA Bus and BIOSCS# is always generated. Accesses to the bottom 64 Kbytes (000E0000–000EFFFFh) are forwarded to the ISA Bus and BIOSCS# is only generated when this BIOS region is enabled. 1.If this BIOS region is enabled (bit 6=1 in the XBCS Register), accesses to the aliased region at the top of 4 Gbytes (FFFE0000h - FFFEFFFFh) are forwarded to ISA and BIOSCS# generated. If disabaled, these accesses are not forwarded to ISA and BIOSCS# is not generated. The additional 384-Kbyte region resides at FFF80000–FFFDFFFFh. If this BIOS region is enabled (bit 7=1 in the XBCS Register), these accesses (FFF80000h–FFFDFFFFh) are forwarded to ISA and BIOSCS# generated. If disabled, these accesses are not forwarded to ISA and BIOSCS# not generated. ISA Access to BIOS Memory The PIIX/PIIX3 confines all ISA-initiated BIOS accesses to the top 64 Kbytes of the 128-Kbyte region (F0000–FFFFFh) to the ISA Bus, even if BIOS is shadowed in main memory. Accesses to the bottom 64 Kbytes of the 128-Kbyte BIOS region (E0000–EFFFFh) are confined to the ISA Bus, when this region is enabled. When the BIOS region is disabled, accesses are forwarded to main memory. Accesses to the top 64-Kbyte BIOS region always generates BIOSCS#. Accesses to the bottom 64-Kbyte BIOS region generate BIOSCS#, when this region is enabled.
See also the documentation of the XBCS register.
On Thu, Sep 16, 2010 at 06:24:56PM +0200, Peter Stuge wrote:
Gleb Natapov wrote:
It looks like kvm is updating the ram at 0xffff0000 when writes are done to 0xf0000. It's not ideal.
AFAIK immediately after reset memory accesses to 0xffff0000 and 0xf0000 are directed to exactly same ROM chip.
No. I'll try to explain. It is messy, because of all the legacy..
After reset and until CS is reloaded, it looks a lot like the CPU is running in real mode from CS:IP f000:fff0. In practise, CS is set up (since 386) so that this actually fetches from fffffff0. Again, it goes away as soon as CS is reloaded, e.g. after a far jmp or call. (The details are similar to the flat real AKA unreal mode trick IIRC.)
OK until now.
The *only* place that the ROM chip is ever accessible is at top of 4GB. How much of the ROM that is actually set up to be decoded on reset is quite chipset specific.
First thing that BIOS does is long jump into 0xf000, so who puts BIOS there if ROM is only accessible at the top of 4GB?
Physical address 0xf00000 is RAM. It's true that most firmware copies at least parts of itself to top 64kb of 1MB (after RAM init of course) but this has absolutely nothing to do with the ROM chip.
AFAIK the process is called shadowing. Has chipset support and done long after BIOS runs from 0xf000 segment.
It's only done to provide the 1980 BIOS interface that the entire PC industry insists on depending on for eternity.
Are you saying that after shadowing BIOS at location 0xf0000 and modifying it in memory BIOS copy as seeing at 0xffff0000 changes too?
I hope Kevin can say more about the circumstances to pin this down.
The shadowing that you mention is precisely the copying of ROM contents into RAM, and possibly modifying it along the way or after. Shadowing may not really be an accurate name for the base BIOS since they tend to be self modifying, in order to work around all legacy limitations. The term is maybe mostly suitable for option ROMs, but I understand you. :)
My PIIX4 spec call this BIOS shadowing. Actually same spec has this:
"The 128-Kbyte BIOS memory space is located at 000E0000–000FFFFFh (top of 1 Mbyte) and is aliased at FFFE0000h (top of 4 Gbytes)"
In any case, a write to physical f0000 should never affect physical ffff0000.
//Peter
SeaBIOS mailing list SeaBIOS@seabios.org http://www.seabios.org/mailman/listinfo/seabios
-- Gleb.
On Thu, Sep 16, 2010 at 08:34:40AM +0200, Gleb Natapov wrote:
On Wed, Sep 15, 2010 at 11:34:31PM -0400, Kevin O'Connor wrote:
Yeah. It looks like kvm is updating the ram at 0xffff0000 when writes are done to 0xf0000. It's not ideal.
AFAIK immediately after reset memory accesses to 0xffff0000 and 0xf0000 are directed to exactly same ROM chip. Are you saying that after shadowing BIOS at location 0xf0000 and modifying it in memory BIOS copy as seeing at 0xffff0000 changes too?
It appears that way. I added the following code to seabios' ram_probe (you could add it just about anywhere):
{ static u32 myvar; u32 *myvardelta = (void*)&myvar + 0xfff00000; dprintf(1, "1: myvar=%d/%d\n", myvar, *myvardelta); barrier(); myvar = 99; dprintf(1, "2: myvar=%d/%d\n", myvar, *myvardelta); barrier(); myvar = 13; dprintf(1, "3: myvar=%d/%d\n", myvar, *myvardelta); }
On qemu I get:
1: myvar=0/0 2: myvar=99/0 3: myvar=13/0
On kvm I get:
1: myvar=0/0 2: myvar=99/99 3: myvar=13/13
Static variables are placed in the f-segment, so it looks like writes are going to both places on kvm. The 0xfff00000 should basically never change (on a real machine you'd need to send erase/write sequences to the flash chip to change the contents). This is qemu-kvm that ships with FC13: qemu-kvm-0.12.5
-Kevin
On Wed, Sep 15, 2010 at 10:31:36PM -0400, Kevin O'Connor wrote:
This patch series enables SeaBIOS to relocate itself out of the e-segment, which enables that space to be used by optionroms.
How resume from S3 is affected by this change?
-- Gleb.
On Thu, Sep 16, 2010 at 08:35:16AM +0200, Gleb Natapov wrote:
On Wed, Sep 15, 2010 at 10:31:36PM -0400, Kevin O'Connor wrote:
This patch series enables SeaBIOS to relocate itself out of the e-segment, which enables that space to be used by optionroms.
How resume from S3 is affected by this change?
It shouldn't be. The resume detection and handling code (src/resume.c) is not relocated. It will detect the resume and handle it as it did before.
-Kevin
On Thu, Sep 16, 2010 at 08:38:31AM -0400, Kevin O'Connor wrote:
On Thu, Sep 16, 2010 at 08:35:16AM +0200, Gleb Natapov wrote:
On Wed, Sep 15, 2010 at 10:31:36PM -0400, Kevin O'Connor wrote:
This patch series enables SeaBIOS to relocate itself out of the e-segment, which enables that space to be used by optionroms.
How resume from S3 is affected by this change?
It shouldn't be. The resume detection and handling code (src/resume.c) is not relocated. It will detect the resume and handle it as it did before.
Before code relocation? Otherwise relocation can clobber OS memory.
-- Gleb.
On Thu, Sep 16, 2010 at 02:40:05PM +0200, Gleb Natapov wrote:
On Thu, Sep 16, 2010 at 08:38:31AM -0400, Kevin O'Connor wrote:
On Thu, Sep 16, 2010 at 08:35:16AM +0200, Gleb Natapov wrote:
On Wed, Sep 15, 2010 at 10:31:36PM -0400, Kevin O'Connor wrote:
This patch series enables SeaBIOS to relocate itself out of the e-segment, which enables that space to be used by optionroms.
How resume from S3 is affected by this change?
It shouldn't be. The resume detection and handling code (src/resume.c) is not relocated. It will detect the resume and handle it as it did before.
Before code relocation? Otherwise relocation can clobber OS memory.
I don't understand. The resume code stays in the f-segment. When it is called it doesn't relocate itself, and doesn't reference the relocated memory.
-Kevin
On Thu, Sep 16, 2010 at 08:54:21AM -0400, Kevin O'Connor wrote:
On Thu, Sep 16, 2010 at 02:40:05PM +0200, Gleb Natapov wrote:
On Thu, Sep 16, 2010 at 08:38:31AM -0400, Kevin O'Connor wrote:
On Thu, Sep 16, 2010 at 08:35:16AM +0200, Gleb Natapov wrote:
On Wed, Sep 15, 2010 at 10:31:36PM -0400, Kevin O'Connor wrote:
This patch series enables SeaBIOS to relocate itself out of the e-segment, which enables that space to be used by optionroms.
How resume from S3 is affected by this change?
It shouldn't be. The resume detection and handling code (src/resume.c) is not relocated. It will detect the resume and handle it as it did before.
Before code relocation? Otherwise relocation can clobber OS memory.
I don't understand. The resume code stays in the f-segment. When it is called it doesn't relocate itself, and doesn't reference the relocated memory.
If relocation is done before checking for S3 resume the following may happen:
1. BIOS starts and relocates POST code to address A 2. BIOS runs POST and boots an OS 3. OS puts most important info into address A 4. OS does S3 suspend 5. S3 resume happens 6. BIOS start and relocates POST code to address A 7. BIOS checks for S3 and jumps to resume vector 8. OS can't resume since address A is corrupted
So the question is what happens first check for S3 resume or POST code relocation?
-- Gleb.
On Thu, Sep 16, 2010 at 03:00:20PM +0200, Gleb Natapov wrote:
On Thu, Sep 16, 2010 at 08:54:21AM -0400, Kevin O'Connor wrote:
I don't understand. The resume code stays in the f-segment. When it is called it doesn't relocate itself, and doesn't reference the relocated memory.
If relocation is done before checking for S3 resume the following may happen:
Relocation is only done after checking for resume.
On a normal boot the code flow is:
reset_vector -> entry_post_official -> entry_post -> _start -> post
(The first two are just stubs for old DOS compatibility.)
On an s3 resume entry_post detects that it is a resume and the code flow becomes:
reset_vector -> entry_post_official -> entry_post -> handle_resume -> s3_resume
Of the above, the build ensures only post() (and functions/data that can _only_ be reached via post) are relocated. So, a resume event shouldn't be a problem because all of that code/data is left in the f-segment.
-Kevin
On 09/16/2010 04:31 AM, Kevin O'Connor wrote:
Unfortunately, both qemu and kvm don't appear to have a reliable way to hard-reboot - normal reboots don't reset the 0xc0000-0xfffff memory. I've worked around this on qemu by manually resetting that memory. However, kvm doesn't keep a pristine copy of the bios at 0xffff0000. Until this is fixed, this patch series will cause a soft-reboot on kvm to result in a shutdown instead of a reboot.
Why is there a difference between qemu and kvm here? What patch did you use for qemu and why doesn't it work for kvm?
On Thu, Sep 16, 2010 at 06:34:04PM +0200, Avi Kivity wrote:
On 09/16/2010 04:31 AM, Kevin O'Connor wrote:
Unfortunately, both qemu and kvm don't appear to have a reliable way to hard-reboot - normal reboots don't reset the 0xc0000-0xfffff memory. I've worked around this on qemu by manually resetting that memory. However, kvm doesn't keep a pristine copy of the bios at 0xffff0000. Until this is fixed, this patch series will cause a soft-reboot on kvm to result in a shutdown instead of a reboot.
Why is there a difference between qemu and kvm here? What patch did you use for qemu and why doesn't it work for kvm?
I did not patch qemu or kvm. The issue (and work around) can be seen in seabios patch 7 of the series: http://www.seabios.org/pipermail/seabios/2010-September/000917.html
Basically, SeaBIOS detects a rerun of post (soft reset) when a variable in the f-segment (HaveRunPost) is set. When that occurs, seabios triggers a reset (i8042_reboot). Unfortunately, neither qemu nor kvm reset the f-segment, so on the next boot seabios still sees HaveRunPost as set, and things get stuck in a loop.
To work around this, patch 7 does a copy from 0xffff0000 to 0xf0000 to manually clear the f-segment (qemu_prep_reset). This hack works okay for qemu. Unfortunately, it doesn't work for kvm - even after the copy HaveRunPost is still set. Normally, 0xffff0000 would have a pristine copy of the bios - changes to 0xf0000 should not also change 0xffff0000, but it looks like kvm does something different.
-Kevin
On Thu, Sep 16, 2010 at 07:31:46PM -0400, Kevin O'Connor wrote:
On Thu, Sep 16, 2010 at 06:34:04PM +0200, Avi Kivity wrote:
On 09/16/2010 04:31 AM, Kevin O'Connor wrote:
Unfortunately, both qemu and kvm don't appear to have a reliable way to hard-reboot - normal reboots don't reset the 0xc0000-0xfffff memory. I've worked around this on qemu by manually resetting that memory. However, kvm doesn't keep a pristine copy of the bios at 0xffff0000. Until this is fixed, this patch series will cause a soft-reboot on kvm to result in a shutdown instead of a reboot.
[...]
To work around this, patch 7 does a copy from 0xffff0000 to 0xf0000 to manually clear the f-segment (qemu_prep_reset). This hack works okay for qemu. Unfortunately, it doesn't work for kvm - even after the copy HaveRunPost is still set. Normally, 0xffff0000 would have a pristine copy of the bios - changes to 0xf0000 should not also change 0xffff0000, but it looks like kvm does something different.
Hi Avi,
Are you okay with me applying this patch series to seabios? It will cause kvm guest reboots to turn into shutdowns until kvm can be changed.
-Kevin
On 09/19/2010 11:54 PM, Kevin O'Connor wrote:
On Thu, Sep 16, 2010 at 07:31:46PM -0400, Kevin O'Connor wrote:
On Thu, Sep 16, 2010 at 06:34:04PM +0200, Avi Kivity wrote:
On 09/16/2010 04:31 AM, Kevin O'Connor wrote:
Unfortunately, both qemu and kvm don't appear to have a reliable way to hard-reboot - normal reboots don't reset the 0xc0000-0xfffff memory. I've worked around this on qemu by manually resetting that memory. However, kvm doesn't keep a pristine copy of the bios at 0xffff0000. Until this is fixed, this patch series will cause a soft-reboot on kvm to result in a shutdown instead of a reboot.
[...]
To work around this, patch 7 does a copy from 0xffff0000 to 0xf0000 to manually clear the f-segment (qemu_prep_reset). This hack works okay for qemu. Unfortunately, it doesn't work for kvm - even after the copy HaveRunPost is still set. Normally, 0xffff0000 would have a pristine copy of the bios - changes to 0xf0000 should not also change 0xffff0000, but it looks like kvm does something different.
Hi Avi,
Are you okay with me applying this patch series to seabios? It will cause kvm guest reboots to turn into shutdowns until kvm can be changed.
Well, we can change development versions of kvm, but not deployed ones. If we apply this then we break many kvm installations.
However, if the problem is in qemu-kvm (not unlikely) then we can update qemu simultaneously with seabios. Since seabios is deployed together with qemu, that shouldn't break installations.
Can you post a git tree for me to test? I'd like to understand the issue better.
On Mon, Sep 20, 2010 at 11:47:49AM +0200, Avi Kivity wrote:
On 09/19/2010 11:54 PM, Kevin O'Connor wrote:
Are you okay with me applying this patch series to seabios? It will cause kvm guest reboots to turn into shutdowns until kvm can be changed.
Well, we can change development versions of kvm, but not deployed ones. If we apply this then we break many kvm installations.
However, if the problem is in qemu-kvm (not unlikely) then we can update qemu simultaneously with seabios. Since seabios is deployed together with qemu, that shouldn't break installations.
Can you post a git tree for me to test? I'd like to understand the issue better.
Here's a simple test case - grab the latest from git://git.linuxtogo.org/home/kevin/seabios.git and apply the patch below. Then run:
kvm-qemu -chardev stdio,id=seabios -device isa-debugcon,iobase=0x402,chardev=seabios
At the top of the output, you'll see:
1: myvar@0x000f60bc=0 myvardelta@0xffff60bc=0 2: myvar@0x000f60bc=99 myvardelta@0xffff60bc=99 3: myvar@0x000f60bc=13 myvardelta@0xffff60bc=13
Basically, writes to 0xf0000 are also being seen at 0xffff0000 which is incorrect. Gleb's patch to qemu-kvm fixes the problem.
-Kevin
diff --git a/src/post.c b/src/post.c index 5d0e2cb..e4b5f0b 100644 --- a/src/post.c +++ b/src/post.c @@ -89,6 +89,21 @@ init_bda(void) static void ram_probe(void) { + { + static u32 myvar; + u32 *myvardelta = (void*)&myvar + 0xfff00000; + dprintf(1, "1: myvar@%p=%d myvardelta@%p=%d\n" + , &myvar, myvar, myvardelta, *myvardelta); + barrier(); + myvar = 99; + dprintf(1, "2: myvar@%p=%d myvardelta@%p=%d\n" + , &myvar, myvar, myvardelta, *myvardelta); + barrier(); + myvar = 13; + dprintf(1, "3: myvar@%p=%d myvardelta@%p=%d\n" + , &myvar, myvar, myvardelta, *myvardelta); + } + dprintf(3, "Find memory size\n"); if (CONFIG_COREBOOT) { coreboot_setup();
On Wed, Sep 15, 2010 at 10:31:36PM -0400, Kevin O'Connor wrote:
Unfortunately, both qemu and kvm don't appear to have a reliable way to hard-reboot - normal reboots don't reset the 0xc0000-0xfffff memory. I've worked around this on qemu by manually resetting that memory. However, kvm doesn't keep a pristine copy of the bios at 0xffff0000. Until this is fixed, this patch series will cause a soft-reboot on kvm to result in a shutdown instead of a reboot.
Can you check if with this patch kvm keeps pristine copy of the bios at 0xffff0000?
diff --git a/hw/piix_pci.c b/hw/piix_pci.c index 933ad86..f224ce7 100644 --- a/hw/piix_pci.c +++ b/hw/piix_pci.c @@ -99,10 +99,6 @@ static void i440fx_update_memory_mappings(PCII440FXState *d) int i, r; uint32_t smram, addr;
- if (kvm_enabled()) { - /* FIXME: Support remappings and protection changes. */ - return; - } update_pam(d, 0xf0000, 0x100000, (d->dev.config[I440FX_PAM] >> 4) & 3); for(i = 0; i < 12; i++) { r = (d->dev.config[(i >> 1) + (I440FX_PAM + 1)] >> ((i & 1) * 4)) & 3; -- Gleb.
On Mon, Sep 20, 2010 at 02:30:10PM +0200, Gleb Natapov wrote:
On Wed, Sep 15, 2010 at 10:31:36PM -0400, Kevin O'Connor wrote:
Unfortunately, both qemu and kvm don't appear to have a reliable way to hard-reboot - normal reboots don't reset the 0xc0000-0xfffff memory. I've worked around this on qemu by manually resetting that memory. However, kvm doesn't keep a pristine copy of the bios at 0xffff0000. Until this is fixed, this patch series will cause a soft-reboot on kvm to result in a shutdown instead of a reboot.
Can you check if with this patch kvm keeps pristine copy of the bios at 0xffff0000?
diff --git a/hw/piix_pci.c b/hw/piix_pci.c index 933ad86..f224ce7 100644 --- a/hw/piix_pci.c +++ b/hw/piix_pci.c @@ -99,10 +99,6 @@ static void i440fx_update_memory_mappings(PCII440FXState *d) int i, r; uint32_t smram, addr;
- if (kvm_enabled()) {
/* FIXME: Support remappings and protection changes. */
return;
- } update_pam(d, 0xf0000, 0x100000, (d->dev.config[I440FX_PAM] >> 4) & 3); for(i = 0; i < 12; i++) { r = (d->dev.config[(i >> 1) + (I440FX_PAM + 1)] >> ((i & 1) * 4)) & 3;
And check with patch below if reset works correctly without your hack of copying bios from high mem (works only with qemu for now):
diff --git a/hw/piix_pci.c b/hw/piix_pci.c index 933ad86..0bf435d 100644 --- a/hw/piix_pci.c +++ b/hw/piix_pci.c @@ -205,6 +201,20 @@ static int i440fx_pcihost_initfn(SysBusDevice *dev) return 0; }
+static void i440fx_reset(void *opaque) +{ + PCII440FXState *d = opaque; + int i; + + printf("i440fx_reset called\n"); + /* restor memory mappings */ + for (i = 0; i < I440FX_PAM_SIZE; i++) + d->dev.config[I440FX_PAM] = 0x00; + d->dev.config[I440FX_SMRAM] = 0x02; + d->smm_enabled = 0; + i440fx_update_memory_mappings(d); +} + static int i440fx_initfn(PCIDevice *dev) { PCII440FXState *d = DO_UPCAST(PCII440FXState, dev, dev); @@ -217,6 +227,7 @@ static int i440fx_initfn(PCIDevice *dev) d->dev.config[I440FX_SMRAM] = 0x02;
cpu_smm_register(&i440fx_set_smm, d); + qemu_register_reset(i440fx_reset, d); return 0; }
-- Gleb.
On Mon, Sep 20, 2010 at 04:07:12PM +0200, Gleb Natapov wrote:
And check with patch below if reset works correctly without your hack of copying bios from high mem (works only with qemu for now):
How will this work on resume? On a soft-reset intended for an S3-resume or an old style 286 resume, the f-segment must not be cleared.
Ideally, there would be two types of resets - a soft-reset (doesn't clear memory) and a hard-reset (does reset memory).
-Kevin
On Mon, Sep 20, 2010 at 08:44:46PM -0400, Kevin O'Connor wrote:
On Mon, Sep 20, 2010 at 04:07:12PM +0200, Gleb Natapov wrote:
And check with patch below if reset works correctly without your hack of copying bios from high mem (works only with qemu for now):
How will this work on resume? On a soft-reset intended for an
Did the patch work for you on reset? :)
S3-resume or an old style 286 resume, the f-segment must not be cleared.
Are you sure real HW does not reset chipset's BIOS mapping on S3? S3 resume can configure pam0 to map memory again (without copying BIOS) and it will see old memory content.
Ideally, there would be two types of resets - a soft-reset (doesn't clear memory) and a hard-reset (does reset memory).
Clearing memory is not QEMU's busyness.
-- Gleb.
On Tue, Sep 21, 2010 at 08:29:20AM +0200, Gleb Natapov wrote:
On Mon, Sep 20, 2010 at 08:44:46PM -0400, Kevin O'Connor wrote:
On Mon, Sep 20, 2010 at 04:07:12PM +0200, Gleb Natapov wrote:
And check with patch below if reset works correctly without your hack of copying bios from high mem (works only with qemu for now):
How will this work on resume? On a soft-reset intended for an
Did the patch work for you on reset? :)
I ran a couple of reset tests and they look okay.
S3-resume or an old style 286 resume, the f-segment must not be cleared.
Are you sure real HW does not reset chipset's BIOS mapping on S3? S3 resume can configure pam0 to map memory again (without copying BIOS) and it will see old memory content.
That is a good point. I don't know if real hardware does that. I'll see if I can run a test on my via chipset machine this weekend.
Should we go this route, it will require seabios changes to handle resume.
-Kevin
On Mon, Sep 20, 2010 at 02:30:10PM +0200, Gleb Natapov wrote:
On Wed, Sep 15, 2010 at 10:31:36PM -0400, Kevin O'Connor wrote:
Unfortunately, both qemu and kvm don't appear to have a reliable way to hard-reboot - normal reboots don't reset the 0xc0000-0xfffff memory. I've worked around this on qemu by manually resetting that memory. However, kvm doesn't keep a pristine copy of the bios at 0xffff0000. Until this is fixed, this patch series will cause a soft-reboot on kvm to result in a shutdown instead of a reboot.
Can you check if with this patch kvm keeps pristine copy of the bios at 0xffff0000?
diff --git a/hw/piix_pci.c b/hw/piix_pci.c index 933ad86..f224ce7 100644 --- a/hw/piix_pci.c +++ b/hw/piix_pci.c @@ -99,10 +99,6 @@ static void i440fx_update_memory_mappings(PCII440FXState *d) int i, r; uint32_t smram, addr;
- if (kvm_enabled()) {
/* FIXME: Support remappings and protection changes. */
return;
- } update_pam(d, 0xf0000, 0x100000, (d->dev.config[I440FX_PAM] >> 4) & 3); for(i = 0; i < 12; i++) { r = (d->dev.config[(i >> 1) + (I440FX_PAM + 1)] >> ((i & 1) * 4)) & 3;
Yes - that seems to fix the problem.
-Kevin
On 09/21/2010 02:32 AM, Kevin O'Connor wrote:
On Mon, Sep 20, 2010 at 02:30:10PM +0200, Gleb Natapov wrote:
On Wed, Sep 15, 2010 at 10:31:36PM -0400, Kevin O'Connor wrote:
Unfortunately, both qemu and kvm don't appear to have a reliable way to hard-reboot - normal reboots don't reset the 0xc0000-0xfffff memory. I've worked around this on qemu by manually resetting that memory. However, kvm doesn't keep a pristine copy of the bios at 0xffff0000. Until this is fixed, this patch series will cause a soft-reboot on kvm to result in a shutdown instead of a reboot.
Can you check if with this patch kvm keeps pristine copy of the bios at 0xffff0000?
diff --git a/hw/piix_pci.c b/hw/piix_pci.c index 933ad86..f224ce7 100644 --- a/hw/piix_pci.c +++ b/hw/piix_pci.c @@ -99,10 +99,6 @@ static void i440fx_update_memory_mappings(PCII440FXState *d) int i, r; uint32_t smram, addr;
- if (kvm_enabled()) {
/* FIXME: Support remappings and protection changes. */
return;
- } update_pam(d, 0xf0000, 0x100000, (d->dev.config[I440FX_PAM]>> 4)& 3); for(i = 0; i< 12; i++) { r = (d->dev.config[(i>> 1) + (I440FX_PAM + 1)]>> ((i& 1) * 4))& 3;
Yes - that seems to fix the problem.
Great. Gleb, please repost to kvm@ so Marcelo can see it.