summaryrefslogtreecommitdiffhomepage
path: root/dev/boot/src/BootThread.cc
blob: e3ca92215f8a4caa5c59fed847442c99e36355af (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
/* ========================================

  Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license.

======================================== */

#include <BootKit/BootKit.h>
#include <BootKit/BootThread.h>
#include <BootKit/Support.h>
#include <FirmwareKit/EFI/API.h>

#include <CFKit/Utils.h>
#include <KernelKit/MSDOS.h>
#include <KernelKit/PE.h>
#include <KernelKit/PEF.h>
#include <modules/CoreGfx/TextGfx.h>

#define kBootThreadSz mib_cast(16)

/// @brief External boot services symbol.
EXTERN EfiBootServices* BS;

/// @note BootThread doesn't parse the symbols so doesn't nullify them, .bss is though.

namespace Boot {
EXTERN_C Int32 rt_jump_to_address(VoidPtr code, HEL::BootInfoHeader* handover, UInt8* stack);

BootThread::BootThread(VoidPtr blob) : fStartAddress(nullptr), fBlob(blob) {
  // detect the format.
  const Char* blob_bytes = reinterpret_cast<char*>(fBlob);

  BootTextWriter writer;

  if (!blob_bytes) {
    // failed to provide a valid pointer.
    return;
  }

  if (blob_bytes[0] == kMagMz0 && blob_bytes[1] == kMagMz1) {
    LDR_EXEC_HEADER_PTR     header_ptr     = CF::ldr_find_exec_header(blob_bytes);
    LDR_OPTIONAL_HEADER_PTR opt_header_ptr = CF::ldr_find_opt_exec_header(blob_bytes);

    if (!header_ptr || !opt_header_ptr) return;

#ifdef __NE_AMD64__
    if (header_ptr->Machine != kPeMachineAMD64 || header_ptr->Signature != kPeSignature) {
      writer.Write("BootZ: Not a PE32+ executable.\r");
      return;
    }
#elif defined(__NE_ARM64__)
    if (header_ptr->Machine != kPeMachineARM64 || header_ptr->Signature != kPeSignature) {
      writer.Write("BootZ: Not a PE32+ executable.\r");
      return;
    }
#endif  // __NE_AMD64__ || __NE_ARM64__

    writer.Write("BootZ: PE32+ executable detected (NeKernel Subsystem).\r");

    auto numSecs = header_ptr->NumberOfSections;

    writer.Write("BootZ: Major Linker Ver: ").Write(opt_header_ptr->MajorLinkerVersion).Write("\r");
    writer.Write("BootZ: Minor Linker Ver: ").Write(opt_header_ptr->MinorLinkerVersion).Write("\r");
    writer.Write("BootZ: Major Subsystem Ver: ")
        .Write(opt_header_ptr->MajorSubsystemVersion)
        .Write("\r");
    writer.Write("BootZ: Minor Subsystem Ver: ")
        .Write(opt_header_ptr->MinorSubsystemVersion)
        .Write("\r");
    writer.Write("BootZ: Magic: ").Write(header_ptr->Signature).Write("\r");

    EfiPhysicalAddress loadStartAddress = opt_header_ptr->ImageBase;

    writer.Write("BootZ: Image-Base: ").Write(loadStartAddress).Write("\r");

    fStack = new UInt8[kBootThreadSz];

    if (!fStack) {
      writer.Write("BootZ: Unable to allocate stack.\r");
      return;
    }

    LDR_SECTION_HEADER_PTR sectPtr =
        (LDR_SECTION_HEADER_PTR) (((Char*) opt_header_ptr) + header_ptr->SizeOfOptionalHeader);

    constexpr auto sectionForCode  = ".text";
    constexpr auto sectionForBootZ = ".ldr";

    for (SizeT sectIndex = 0; sectIndex < numSecs; ++sectIndex) {
      LDR_SECTION_HEADER_PTR sect = &sectPtr[sectIndex];

      auto sz = sect->VirtualSize;

      if (sect->SizeOfRawData > 0) sz = sect->SizeOfRawData;

      SetMem((VoidPtr) (loadStartAddress + sect->VirtualAddress), 0, sz);

      if (sect->SizeOfRawData > 0) {
        CopyMem((VoidPtr) (loadStartAddress + sect->VirtualAddress),
                (VoidPtr) ((UIntPtr) fBlob + sect->PointerToRawData), sect->SizeOfRawData);
      }

      if (StrCmp(sectionForCode, sect->Name) == 0) {
        fStartAddress =
            (VoidPtr) ((UIntPtr) loadStartAddress + opt_header_ptr->AddressOfEntryPoint);
        writer.Write("BootZ: Executable entry address: ")
            .Write((UIntPtr) fStartAddress)
            .Write("\r");

        /// @note .text region shall be marked as executable on ARM.

#ifdef __NE_ARM64__

#endif
      } else if (StrCmp(sectionForBootZ, sect->Name) == 0) {
        struct HANDOVER_INFORMATION_STUB {
          UInt64 HandoverMagic;
          UInt32 HandoverType;
          UInt32 HandoverPad;
          UInt32 HandoverArch;
        }* handover_struc =
            (struct HANDOVER_INFORMATION_STUB*) ((UIntPtr) fBlob + sect->PointerToRawData);

        if (handover_struc->HandoverMagic != kHandoverMagic) {
#ifdef __NE_AMD64__
          if (handover_struc->HandoverArch != HEL::kArchAMD64) {
            writer.Write("BootZ: Not an handover header, bad CPU...\r");
          }
#elif defined(__NE_ARM64__)
          if (handover_struc->HandoverArch != HEL::kArchARM64) {
            writer.Write("BootZ: Not an handover header, bad CPU...\r");
          }
#endif

          writer.Write("BootZ: Not an handover header...\r");
          ::Boot::Stop();
        }
      }

      writer.Write("BootZ: Raw offset: ")
          .Write(sect->PointerToRawData)
          .Write(" of ")
          .Write(sect->Name)
          .Write("\r");

      CopyMem((VoidPtr) (loadStartAddress + sect->VirtualAddress),
              (VoidPtr) ((UIntPtr) fBlob + sect->PointerToRawData), sect->SizeOfRawData);
    }
  } else if (blob_bytes[0] == kPefMagic[0] && blob_bytes[1] == kPefMagic[1] &&
             blob_bytes[2] == kPefMagic[2] && blob_bytes[3] == kPefMagic[3]) {
    //  =========================================  //
    //  PEF executable has been detected.
    //  =========================================  //

    fStartAddress = nullptr;

    writer.Write("BootZ: PEF executable detected, won't load it.\r");
    writer.Write("BootZ: note: PEF executables aren't supported for now.\r");
  } else {
    writer.Write("BootZ: Invalid Executable.\r");
  }
}

/// @note handover header has to be valid!
Int32 BootThread::Start(HEL::BootInfoHeader* handover, Bool own_stack) {
  if (!fStartAddress) {
    return kEfiFail;
  }

  if (!handover) {
    return kEfiFail;
  }

  fHandover = handover;

  BootTextWriter writer;

  writer.Write("BootZ: Starting: ").Write(fBlobName).Write("\r");
  writer.Write("BootZ: Handover address: ").Write((UIntPtr) fHandover).Write("\r");

  if (own_stack) {
    writer.Write("BootZ: Using it's own stack.\r");
    writer.Write("BootZ: Stack address: ").Write((UIntPtr) &fStack[kBootThreadSz - 1]).Write("\r");
    writer.Write("BootZ: Stack size: ").Write(kBootThreadSz).Write("\r");

    fHandover->f_StackTop = &fStack[kBootThreadSz - 1];
    fHandover->f_StackSz  = kBootThreadSz;

    auto ret = rt_jump_to_address(fStartAddress, fHandover, &fStack[kBootThreadSz - 1]);

    // we don't need the stack anymore.

    delete[] fStack;
    fStack = nullptr;

    return ret;
  } else {
    writer.Write("BootZ: Using the bootloader's stack.\r");

    return reinterpret_cast<HEL::HandoverProc>(fStartAddress)(fHandover);
  }

  return kEfiFail;
}

const Char* BootThread::GetName() {
  return fBlobName;
}

Void BootThread::SetName(const Char* name) {
  CopyMem(fBlobName, name, StrLen(name));
}

bool BootThread::IsValid() {
  return fStartAddress != nullptr;
}
}  // namespace Boot