system.cc revision 6220
1/*
2 * Copyright (c) 2007 The Hewlett-Packard Development Company
3 * All rights reserved.
4 *
5 * Redistribution and use of this software in source and binary forms,
6 * with or without modification, are permitted provided that the
7 * following conditions are met:
8 *
9 * The software must be used only for Non-Commercial Use which means any
10 * use which is NOT directed to receiving any direct monetary
11 * compensation for, or commercial advantage from such use.  Illustrative
12 * examples of non-commercial use are academic research, personal study,
13 * teaching, education and corporate research & development.
14 * Illustrative examples of commercial use are distributing products for
15 * commercial advantage and providing services using the software for
16 * commercial advantage.
17 *
18 * If you wish to use this software or functionality therein that may be
19 * covered by patents for commercial use, please contact:
20 *     Director of Intellectual Property Licensing
21 *     Office of Strategy and Technology
22 *     Hewlett-Packard Company
23 *     1501 Page Mill Road
24 *     Palo Alto, California  94304
25 *
26 * Redistributions of source code must retain the above copyright notice,
27 * this list of conditions and the following disclaimer.  Redistributions
28 * in binary form must reproduce the above copyright notice, this list of
29 * conditions and the following disclaimer in the documentation and/or
30 * other materials provided with the distribution.  Neither the name of
31 * the COPYRIGHT HOLDER(s), HEWLETT-PACKARD COMPANY, nor the names of its
32 * contributors may be used to endorse or promote products derived from
33 * this software without specific prior written permission.  No right of
34 * sublicense is granted herewith.  Derivatives of the software and
35 * output created using the software may be prepared, but only for
36 * Non-Commercial Uses.  Derivatives of the software may be shared with
37 * others provided: (i) the others agree to abide by the list of
38 * conditions herein which includes the Non-Commercial Use restrictions;
39 * and (ii) such Derivatives of the software include the above copyright
40 * notice to acknowledge the contribution from this software where
41 * applicable, this list of conditions and the disclaimer below.
42 *
43 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
44 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
45 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
46 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
47 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
48 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
49 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
50 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
51 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
52 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
53 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
54 *
55 * Authors: Gabe Black
56 */
57
58#include "arch/x86/bios/smbios.hh"
59#include "arch/x86/bios/intelmp.hh"
60#include "arch/x86/miscregs.hh"
61#include "arch/x86/system.hh"
62#include "arch/vtophys.hh"
63#include "base/intmath.hh"
64#include "base/loader/object_file.hh"
65#include "base/loader/symtab.hh"
66#include "base/remote_gdb.hh"
67#include "base/trace.hh"
68#include "cpu/thread_context.hh"
69#include "mem/physical.hh"
70#include "params/X86System.hh"
71#include "sim/byteswap.hh"
72
73
74using namespace LittleEndianGuest;
75using namespace X86ISA;
76
77X86System::X86System(Params *p) :
78    System(p), smbiosTable(p->smbios_table),
79    mpFloatingPointer(p->intel_mp_pointer),
80    mpConfigTable(p->intel_mp_table),
81    rsdp(p->acpi_description_table_pointer)
82{}
83
84static void
85installSegDesc(ThreadContext *tc, SegmentRegIndex seg,
86        SegDescriptor desc, bool longmode)
87{
88    uint64_t base = desc.baseLow + (desc.baseHigh << 24);
89    bool honorBase = !longmode || seg == SEGMENT_REG_FS ||
90                                  seg == SEGMENT_REG_GS ||
91                                  seg == SEGMENT_REG_TSL ||
92                                  seg == SYS_SEGMENT_REG_TR;
93    uint64_t limit = desc.limitLow | (desc.limitHigh << 16);
94
95    SegAttr attr = 0;
96    if (desc.s) {
97        if (desc.type.codeOrData) {
98            // Code segment
99            attr.readable = desc.type.r;
100        } else {
101            // Data segment
102            attr.readable = 1;
103            attr.writable = desc.type.w;
104            attr.expandDown = desc.type.e;
105        }
106    } else {
107        attr.writable = 1;
108        attr.readable = 1;
109        attr.expandDown = 0;
110    }
111    attr.longMode = desc.l;
112    attr.dpl = desc.dpl;
113    attr.defaultSize = desc.d;
114
115    tc->setMiscReg(MISCREG_SEG_BASE(seg), base);
116    tc->setMiscReg(MISCREG_SEG_EFF_BASE(seg), honorBase ? base : 0);
117    tc->setMiscReg(MISCREG_SEG_LIMIT(seg), limit);
118    tc->setMiscReg(MISCREG_SEG_ATTR(seg), (MiscReg)attr);
119}
120
121void
122X86System::startup()
123{
124    System::startup();
125    ThreadContext *tc = threadContexts[0];
126    // This is the boot strap processor (BSP). Initialize it to look like
127    // the boot loader has just turned control over to the 64 bit OS. We
128    // won't actually set up real mode or legacy protected mode descriptor
129    // tables because we aren't executing any code that would require
130    // them. We do, however toggle the control bits in the correct order
131    // while allowing consistency checks and the underlying mechansims
132    // just to be safe.
133
134    const int NumPDTs = 4;
135
136    const Addr PageMapLevel4 = 0x70000;
137    const Addr PageDirPtrTable = 0x71000;
138    const Addr PageDirTable[NumPDTs] =
139        {0x72000, 0x73000, 0x74000, 0x75000};
140    const Addr GDTBase = 0x76000;
141
142    const int PML4Bits = 9;
143    const int PDPTBits = 9;
144    const int PDTBits = 9;
145
146    // Get a port to write the page tables and descriptor tables.
147    FunctionalPort * physPort = tc->getPhysPort();
148
149    /*
150     * Set up the gdt.
151     */
152    uint8_t numGDTEntries = 0;
153    // Place holder at selector 0
154    uint64_t nullDescriptor = 0;
155    physPort->writeBlob(GDTBase + numGDTEntries * 8,
156            (uint8_t *)(&nullDescriptor), 8);
157    numGDTEntries++;
158
159    //64 bit code segment
160    SegDescriptor csDesc = 0;
161    csDesc.type.codeOrData = 1;
162    csDesc.type.c = 0; // Not conforming
163    csDesc.type.r = 1; // Readable
164    csDesc.dpl = 0; // Privelege level 0
165    csDesc.p = 1; // Present
166    csDesc.l = 1; // 64 bit
167    csDesc.d = 0; // default operand size
168    csDesc.g = 1; // Page granularity
169    csDesc.s = 1; // Not a system segment
170    csDesc.limitHigh = 0xF;
171    csDesc.limitLow = 0xFF;
172    //Because we're dealing with a pointer and I don't think it's
173    //guaranteed that there isn't anything in a nonvirtual class between
174    //it's beginning in memory and it's actual data, we'll use an
175    //intermediary.
176    uint64_t csDescVal = csDesc;
177    physPort->writeBlob(GDTBase + numGDTEntries * 8,
178            (uint8_t *)(&csDescVal), 8);
179
180    numGDTEntries++;
181
182    SegSelector cs = 0;
183    cs.si = numGDTEntries - 1;
184
185    tc->setMiscReg(MISCREG_CS, (MiscReg)cs);
186
187    //32 bit data segment
188    SegDescriptor dsDesc = 0;
189    dsDesc.type.codeOrData = 0;
190    dsDesc.type.e = 0; // Not expand down
191    dsDesc.type.w = 1; // Writable
192    dsDesc.dpl = 0; // Privelege level 0
193    dsDesc.p = 1; // Present
194    dsDesc.d = 1; // default operand size
195    dsDesc.g = 1; // Page granularity
196    dsDesc.s = 1; // Not a system segment
197    dsDesc.limitHigh = 0xF;
198    dsDesc.limitLow = 0xFF;
199    uint64_t dsDescVal = dsDesc;
200    physPort->writeBlob(GDTBase + numGDTEntries * 8,
201            (uint8_t *)(&dsDescVal), 8);
202
203    numGDTEntries++;
204
205    SegSelector ds;
206    ds.si = numGDTEntries - 1;
207
208    tc->setMiscReg(MISCREG_DS, (MiscReg)ds);
209    tc->setMiscReg(MISCREG_ES, (MiscReg)ds);
210    tc->setMiscReg(MISCREG_FS, (MiscReg)ds);
211    tc->setMiscReg(MISCREG_GS, (MiscReg)ds);
212    tc->setMiscReg(MISCREG_SS, (MiscReg)ds);
213
214    tc->setMiscReg(MISCREG_TSL, 0);
215    tc->setMiscReg(MISCREG_TSG_BASE, GDTBase);
216    tc->setMiscReg(MISCREG_TSG_LIMIT, 8 * numGDTEntries - 1);
217
218    SegDescriptor tssDesc = 0;
219    tssDesc.type = 0xB;
220    tssDesc.dpl = 0; // Privelege level 0
221    tssDesc.p = 1; // Present
222    tssDesc.d = 1; // default operand size
223    tssDesc.g = 1; // Page granularity
224    tssDesc.s = 1; // Not a system segment
225    tssDesc.limitHigh = 0xF;
226    tssDesc.limitLow = 0xFF;
227    uint64_t tssDescVal = tssDesc;
228    physPort->writeBlob(GDTBase + numGDTEntries * 8,
229            (uint8_t *)(&tssDescVal), 8);
230
231    numGDTEntries++;
232
233    SegSelector tss = 0;
234    tss.si = numGDTEntries - 1;
235
236    tc->setMiscReg(MISCREG_TR, (MiscReg)tss);
237    installSegDesc(tc, SYS_SEGMENT_REG_TR, tssDesc, true);
238
239    /*
240     * Identity map the first 4GB of memory. In order to map this region
241     * of memory in long mode, there needs to be one actual page map level
242     * 4 entry which points to one page directory pointer table which
243     * points to 4 different page directory tables which are full of two
244     * megabyte pages. All of the other entries in valid tables are set
245     * to indicate that they don't pertain to anything valid and will
246     * cause a fault if used.
247     */
248
249    // Put valid values in all of the various table entries which indicate
250    // that those entries don't point to further tables or pages. Then
251    // set the values of those entries which are needed.
252
253    // Page Map Level 4
254
255    // read/write, user, not present
256    uint64_t pml4e = X86ISA::htog(0x6);
257    for (int offset = 0; offset < (1 << PML4Bits) * 8; offset += 8) {
258        physPort->writeBlob(PageMapLevel4 + offset, (uint8_t *)(&pml4e), 8);
259    }
260    // Point to the only PDPT
261    pml4e = X86ISA::htog(0x7 | PageDirPtrTable);
262    physPort->writeBlob(PageMapLevel4, (uint8_t *)(&pml4e), 8);
263
264    // Page Directory Pointer Table
265
266    // read/write, user, not present
267    uint64_t pdpe = X86ISA::htog(0x6);
268    for (int offset = 0; offset < (1 << PDPTBits) * 8; offset += 8) {
269        physPort->writeBlob(PageDirPtrTable + offset,
270                (uint8_t *)(&pdpe), 8);
271    }
272    // Point to the PDTs
273    for (int table = 0; table < NumPDTs; table++) {
274        pdpe = X86ISA::htog(0x7 | PageDirTable[table]);
275        physPort->writeBlob(PageDirPtrTable + table * 8,
276                (uint8_t *)(&pdpe), 8);
277    }
278
279    // Page Directory Tables
280
281    Addr base = 0;
282    const Addr pageSize = 2 << 20;
283    for (int table = 0; table < NumPDTs; table++) {
284        for (int offset = 0; offset < (1 << PDTBits) * 8; offset += 8) {
285            // read/write, user, present, 4MB
286            uint64_t pdte = X86ISA::htog(0x87 | base);
287            physPort->writeBlob(PageDirTable[table] + offset,
288                    (uint8_t *)(&pdte), 8);
289            base += pageSize;
290        }
291    }
292
293    /*
294     * Transition from real mode all the way up to Long mode
295     */
296    CR0 cr0 = tc->readMiscRegNoEffect(MISCREG_CR0);
297    //Turn off paging.
298    cr0.pg = 0;
299    tc->setMiscReg(MISCREG_CR0, cr0);
300    //Turn on protected mode.
301    cr0.pe = 1;
302    tc->setMiscReg(MISCREG_CR0, cr0);
303
304    CR4 cr4 = tc->readMiscRegNoEffect(MISCREG_CR4);
305    //Turn on pae.
306    cr4.pae = 1;
307    tc->setMiscReg(MISCREG_CR4, cr4);
308
309    //Point to the page tables.
310    tc->setMiscReg(MISCREG_CR3, PageMapLevel4);
311
312    Efer efer = tc->readMiscRegNoEffect(MISCREG_EFER);
313    //Enable long mode.
314    efer.lme = 1;
315    tc->setMiscReg(MISCREG_EFER, efer);
316
317    //Start using longmode segments.
318    installSegDesc(tc, SEGMENT_REG_CS, csDesc, true);
319    installSegDesc(tc, SEGMENT_REG_DS, dsDesc, true);
320    installSegDesc(tc, SEGMENT_REG_ES, dsDesc, true);
321    installSegDesc(tc, SEGMENT_REG_FS, dsDesc, true);
322    installSegDesc(tc, SEGMENT_REG_GS, dsDesc, true);
323    installSegDesc(tc, SEGMENT_REG_SS, dsDesc, true);
324
325    //Activate long mode.
326    cr0.pg = 1;
327    tc->setMiscReg(MISCREG_CR0, cr0);
328
329    tc->setPC(tc->getSystemPtr()->kernelEntry);
330    tc->setNextPC(tc->readPC());
331
332    // We should now be in long mode. Yay!
333
334    Addr ebdaPos = 0xF0000;
335    Addr fixed, table;
336
337    //Write out the SMBios/DMI table
338    writeOutSMBiosTable(ebdaPos, fixed, table);
339    ebdaPos += (fixed + table);
340    ebdaPos = roundUp(ebdaPos, 16);
341
342    //Write out the Intel MP Specification configuration table
343    writeOutMPTable(ebdaPos, fixed, table);
344    ebdaPos += (fixed + table);
345}
346
347void
348X86System::writeOutSMBiosTable(Addr header,
349        Addr &headerSize, Addr &structSize, Addr table)
350{
351    // Get a port to write the table and header to memory.
352    FunctionalPort * physPort = threadContexts[0]->getPhysPort();
353
354    // If the table location isn't specified, just put it after the header.
355    // The header size as of the 2.5 SMBios specification is 0x1F bytes
356    if (!table)
357        table = header + 0x1F;
358    smbiosTable->setTableAddr(table);
359
360    smbiosTable->writeOut(physPort, header, headerSize, structSize);
361
362    // Do some bounds checking to make sure we at least didn't step on
363    // ourselves.
364    assert(header > table || header + headerSize <= table);
365    assert(table > header || table + structSize <= header);
366}
367
368void
369X86System::writeOutMPTable(Addr fp,
370        Addr &fpSize, Addr &tableSize, Addr table)
371{
372    // Get a port to write the table and header to memory.
373    FunctionalPort * physPort = threadContexts[0]->getPhysPort();
374
375    // If the table location isn't specified and it exists, just put
376    // it after the floating pointer. The fp size as of the 1.4 Intel MP
377    // specification is 0x10 bytes.
378    if (mpConfigTable) {
379        if (!table)
380            table = fp + 0x10;
381        mpFloatingPointer->setTableAddr(table);
382    }
383
384    fpSize = mpFloatingPointer->writeOut(physPort, fp);
385    if (mpConfigTable)
386        tableSize = mpConfigTable->writeOut(physPort, table);
387    else
388        tableSize = 0;
389
390    // Do some bounds checking to make sure we at least didn't step on
391    // ourselves and the fp structure was the size we thought it was.
392    assert(fp > table || fp + fpSize <= table);
393    assert(table > fp || table + tableSize <= fp);
394    assert(fpSize == 0x10);
395}
396
397
398X86System::~X86System()
399{
400    delete smbiosTable;
401}
402
403void
404X86System::serialize(std::ostream &os)
405{
406    System::serialize(os);
407}
408
409
410void
411X86System::unserialize(Checkpoint *cp, const std::string &section)
412{
413    System::unserialize(cp,section);
414}
415
416X86System *
417X86SystemParams::create()
418{
419    return new X86System(this);
420}
421