summaryrefslogtreecommitdiffstats
path: root/src/usr/isteps/istep20/call_host_load_payload.C
blob: b0b9f3e5ca9fb273b86bfc51c67536ab5ec3a86e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
/* IBM_PROLOG_BEGIN_TAG                                                   */
/* This is an automatically generated prolog.                             */
/*                                                                        */
/* $Source: src/usr/isteps/istep20/call_host_load_payload.C $             */
/*                                                                        */
/* OpenPOWER HostBoot Project                                             */
/*                                                                        */
/* Contributors Listed Below - COPYRIGHT 2016,2019                        */
/* [+] International Business Machines Corp.                              */
/*                                                                        */
/*                                                                        */
/* Licensed under the Apache License, Version 2.0 (the "License");        */
/* you may not use this file except in compliance with the License.       */
/* You may obtain a copy of the License at                                */
/*                                                                        */
/*     http://www.apache.org/licenses/LICENSE-2.0                         */
/*                                                                        */
/* Unless required by applicable law or agreed to in writing, software    */
/* distributed under the License is distributed on an "AS IS" BASIS,      */
/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or        */
/* implied. See the License for the specific language governing           */
/* permissions and limitations under the License.                         */
/*                                                                        */
/* IBM_PROLOG_END_TAG                                                     */

#include <trace/interface.H>
#include <errl/errlentry.H>
#include <initservice/isteps_trace.H>
#include <isteps/hwpisteperror.H>
#include <isteps/istep_reasoncodes.H>
#include <isteps/hwpf_reasoncodes.H>
#include <targeting/common/commontargeting.H>
#include <initservice/istepdispatcherif.H>
#include <initservice/initserviceif.H>
#include <pnor/pnorif.H>
#include <util/misc.H>
#include <sys/mm.h>
#include <arch/ppc.H>
#include <kernel/console.H>
#include <xz/xz.h>


using namespace ERRORLOG;
using namespace ISTEP;
using namespace ISTEP_ERROR;
using namespace TARGETING;

namespace ISTEP_20
{

/**
 * @brief This function loads the pnor section in sapphire mode and
 *        and also decompresseses the section if it was compressed
 *
 * @param[in] i_section  - Which section are we loading
 * @param[in] i_physAddr - The physical address of the section
 *
 * @return errlHndl_t - NULL if successful, otherwise a pointer
 *                      to the error log.
 */
static errlHndl_t load_pnor_section(PNOR::SectionId i_section,
                                    uint64_t i_physAddr);

void* call_host_load_payload (void *io_pArgs)
{
    errlHndl_t  l_err  =   NULL;

    TRACDCOMP( ISTEPS_TRACE::g_trac_isteps_trace,
               ENTER_MRK"call_host_start_payload entry" );

    do
    {
        // Get Target Service, and the system target.
        TargetService& tS = targetService();
        TARGETING::Target* sys = NULL;
        (void) tS.getTopLevelTarget( sys );
        if( NULL == sys )
        {
            // Error getting system target to get payload related values.  We
            // will create an error to be passed back.  This will cause the
            // istep to fail.
            TRACFCOMP( ISTEPS_TRACE::g_trac_isteps_trace,
                       ERR_MRK"call_load_payload: System Target was NULL!" );

            /*@
             * @errortype
             * @reasoncode       RC_TARGET_NULL
             * @severity         ERRORLOG::ERRL_SEV_CRITICAL_SYS_TERM
             * @moduleid         MOD_LOAD_PAYLOAD
             * @userdata1        <UNUSED>
             * @userdata2        <UNUSED>
             * @devdesc          System target was NULL!
             * @custdesc         A problem occurred during the IPL
             *                   of the system.
             */
            l_err = new ERRORLOG::ErrlEntry(
                            ERRORLOG::ERRL_SEV_CRITICAL_SYS_TERM,
                            MOD_LOAD_PAYLOAD,
                            RC_TARGET_NULL,
                            0x0,
                            0x0 );

            break;
        }

        if(INITSERVICE::spBaseServicesEnabled())
        {
            //this function is a NOOP on FSP system
            break;
        }

        // Get Payload base/entry from attributes
        uint64_t payloadBase = sys->getAttr<TARGETING::ATTR_PAYLOAD_BASE>();
        TRACDCOMP( ISTEPS_TRACE::g_trac_isteps_trace,INFO_MRK
                   "call_load_payload: Payload Base: 0x%08x MB, Base:0x%08x",
                   payloadBase, (payloadBase * MEGABYTE) );

        payloadBase = payloadBase * MEGABYTE;

        // Load payload data in PHYP mode or in Sapphire mode
        if(is_sapphire_load() || is_phyp_load())
        {
            l_err = load_pnor_section( PNOR::PAYLOAD, payloadBase );
            if ( l_err )
            {
                TRACFCOMP(ISTEPS_TRACE::g_trac_isteps_trace,
                          "call_load_payload: error loading pnor section");
                break;
            }
        }

    }while(0);

    return l_err;
}

static errlHndl_t load_pnor_section(PNOR::SectionId i_section,
        uint64_t i_physAddr)
{
    TRACFCOMP(ISTEPS_TRACE::g_trac_isteps_trace,ENTER_MRK"load_pnor_section()");
    errlHndl_t err = nullptr;

#ifdef CONFIG_SECUREBOOT
    // Securely load section
    TRACFCOMP(ISTEPS_TRACE::g_trac_isteps_trace,"load_pnor_section: secure section load of secId=0x%X (%s)",
              i_section, PNOR::SectionIdToString(i_section));
    err = PNOR::loadSecureSection(i_section);
    if (err)
    {
        return err;
    }
    // Do not need to unload since we have plenty of memory at this point.
#endif

    // Get the section info from PNOR.
    PNOR::SectionInfo_t pnorSectionInfo;
    err = PNOR::getSectionInfo( i_section, pnorSectionInfo );
    if( err != nullptr )
    {
        TRACFCOMP(ISTEPS_TRACE::g_trac_isteps_trace,
                 "load_pnor_section: Could not get section info from %x",
                  i_section);
        return err;
    }

    // XZ repository: http:git.tukaani.org/xz.git
    // Header specifics can be found in xz/doc/xz-file-format.txt
    const uint8_t HEADER_MAGIC[]= { 0xFD, '7', 'z', 'X', 'Z', 0x00 };
    uint8_t* l_pnor_header = reinterpret_cast<uint8_t *>(pnorSectionInfo.vaddr);

    bool l_pnor_is_XZ_compressed = (0 == memcmp(l_pnor_header,
                               HEADER_MAGIC, sizeof(HEADER_MAGIC)));

    TRACFCOMP(ISTEPS_TRACE::g_trac_isteps_trace,
             "load_pnor_section: is XZ_compressed: %d",
              l_pnor_is_XZ_compressed);

    // This assumes that the maximum decompression ratio will always be less
    // than 1:16 (compressed:uncompressed).  This works because XZ compression
    // is usually 14.1%, the decompressor does not need the exact size, and
    // we have all of mainstore memory at this point.
    uint32_t uncompressedPayloadSize = l_pnor_is_XZ_compressed ?
            (pnorSectionInfo.size * 16) : pnorSectionInfo.size;


    const uint32_t originalPayloadSize = pnorSectionInfo.size;

    printk( "Loading PNOR section %d (%s) %d bytes @0x%lx\n",
            i_section,
            pnorSectionInfo.name,
            originalPayloadSize,
            i_physAddr );

    void * loadAddr = NULL;
    // Map in the physical memory we are loading into.
    // If we are not xz compressed, the uncompressedSize
    // is equal to the original size.
    loadAddr = mm_block_map( reinterpret_cast<void*>( i_physAddr ),
                             uncompressedPayloadSize );

    // Print out inital progress bar.
#ifdef CONFIG_CONSOLE
    const int progressSteps = 80;
    int progress = 0;
    for ( int i = 0; i < progressSteps; ++i )
    {
        printk( "." );
    }
    printk( "\r" );
#endif

    if(!l_pnor_is_XZ_compressed)
    {
        // Load the data block by block and update the progress bar.
        const uint32_t BLOCK_SIZE = 4096;
        for ( uint32_t i = 0; i < originalPayloadSize; i += BLOCK_SIZE )
        {
            memcpy( reinterpret_cast<void*>(
                      reinterpret_cast<uint64_t>(loadAddr) + i ),
                    reinterpret_cast<void*>( pnorSectionInfo.vaddr + i ),
                    std::min( originalPayloadSize - i, BLOCK_SIZE ) );
#ifdef CONFIG_CONSOLE
            for ( int new_progress = (i * progressSteps) /
                  originalPayloadSize;
                  progress <= new_progress; progress++ )
            {
                printk( "=" );
            }
#endif
        }
#ifdef CONFIG_CONSOLE
        printk( "\n" );
#endif
    }

    if(l_pnor_is_XZ_compressed)
    {
        struct xz_buf b;
        struct xz_dec *s;
        enum xz_ret ret;

        xz_crc32_init();
        s = xz_dec_init(XZ_SINGLE, 0);
        if(s == NULL)
        {
            TRACFCOMP(ISTEPS_TRACE::g_trac_isteps_trace,ERR_MRK
                     "load_pnor_section: XZ Embedded Initialization failed");
            return err;
        }

        static const uint64_t compressed_SIZE = originalPayloadSize;
        static const uint64_t decompressed_SIZE = uncompressedPayloadSize;

        b.in = reinterpret_cast<uint8_t *>( pnorSectionInfo.vaddr);
        b.in_pos = 0;
        b.in_size = compressed_SIZE;
        b.out = reinterpret_cast<uint8_t *>(loadAddr);
        b.out_pos = 0;
        b.out_size = decompressed_SIZE;

        ret = xz_dec_run(s, &b);

        if(ret == XZ_STREAM_END)
        {
            TRACFCOMP(ISTEPS_TRACE::g_trac_isteps_trace,
                     "load_pnor_section: The %s section was decompressed.",
                      pnorSectionInfo.name);
        }else
        {
            TRACFCOMP(ISTEPS_TRACE::g_trac_isteps_trace,ERR_MRK
                     "load_pnor_section: xz-embedded returned an error, ",
                     "the ret is %d",ret);

            /*@
             * @errortype
             * @reasoncode       fapi::RC_INVALID_RETURN_XZ_CODE
             * @severity         ERRORLOG::ERRL_SEV_UNRECOVERABLE
             * @moduleid         fapi::MOD_START_XZ_PAYLOAD
             * @devdesc          xz-embedded has returned an error.
             *                   the return code can be found in xz.h
             * @custdesc         Error uncompressing payload image from
             *                   boot flash
             * @userdata1        Return code from xz-embedded
             * @userdata2[0:31]  Original Payload Size
             * @userdata2[32:63] Uncompressed Payload Size
             */
            err = new ERRORLOG::ErrlEntry(ERRORLOG::ERRL_SEV_UNRECOVERABLE,
                            fapi::MOD_START_XZ_PAYLOAD,
                            fapi::RC_INVALID_RETURN_XZ_CODE,
                            ret,TWO_UINT32_TO_UINT64(
                                    originalPayloadSize,
                                    uncompressedPayloadSize));
            err->addProcedureCallout(HWAS::EPUB_PRC_PHYP_CODE,
                            HWAS::SRCI_PRIORITY_HIGH);

        }
        //Clean up memory
        xz_dec_end(s);

    }

    int rc = 0;
    rc = mm_block_unmap(reinterpret_cast<void *>(loadAddr));
    if(rc)
    {
        TRACFCOMP(ISTEPS_TRACE::g_trac_isteps_trace,ERR_MRK
                 "load_pnor_section: mm_block_unmap returned 1");

        /*@
         * @errortype
         * @reasoncode      fapi::RC_MM_UNMAP_ERR
         * @severity        ERRORLOG::ERRL_SEV_UNRECOVERABLE
         * @moduleid        fapi::MOD_START_XZ_PAYLOAD
         * @devdesc         mm_block_unmap returned incorrectly with 0
         * @custdesc        Error unmapping memory section
         */
        err = new ERRORLOG::ErrlEntry(ERRORLOG::ERRL_SEV_UNRECOVERABLE,
                                      fapi::MOD_START_XZ_PAYLOAD,
                                      fapi::RC_MM_UNMAP_ERR,
                                      0,0,0);
    }
    return err;
}

};
OpenPOWER on IntegriCloud