/*
 * Copyright (C) 2023 The Android Open Source Project
 *
 * 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.
 */

// Refer to https://llvm.org/doxygen/Object_2COFF_8h_source.html and Microsoft Portable Executable
// and Common Object File Format Specification for more detail.

// See https://learn.microsoft.com/en-us/windows/win32/debug/pe-format#machine-types
#define IMAGE_FILE_MACHINE_RISCV64 0x5064

// See https://learn.microsoft.com/en-us/windows/win32/debug/pe-format#characteristics
#define IMAGE_FILE_EXECUTABLE_IMAGE 0x0002

// See https://learn.microsoft.com/en-us/windows/win32/debug/pe-format#optional-header-image-only
#define PE32_PLUS_HEADER_MAGIC 0x020b

// See https://learn.microsoft.com/en-us/windows/win32/debug/pe-format#windows-subsystem
#define IMAGE_SUBSYSTEM_EFI_APPLICATION 10

.macro DATA_DIRECTORY name
\name\()_data_directory:
  .long 0  // support::ulittle32_t RelativeVirtualAddress;
  .long 0  // support::ulittle32_t Size;
.endm

.section .efi_header,"a"
.global dos_header

// MS-DOS Stub
dos_header:
  .short 0x5a4d    // Magic 'MZ'
  .short 0         // support::ulittle16_t UsedBytesInTheLastPage;
  .short 0         // support::ulittle16_t FileSizeInPages;
  .short 0         // support::ulittle16_t NumberOfRelocationItems;
  .short 0         // support::ulittle16_t HeaderSizeInParagraphs;
  .short 0         // support::ulittle16_t MinimumExtraParagraphs;
  .short 0         // support::ulittle16_t MaximumExtraParagraphs;
  .short 0         // support::ulittle16_t InitialRelativeSS;
  .short 0         // support::ulittle16_t InitialSP;
  .short 0         // support::ulittle16_t Checksum;
  .short 0         // support::ulittle16_t InitialIP;
  .short 0         // support::ulittle16_t InitialRelativeCS;
  .short 0         // support::ulittle16_t AddressOfRelocationTable;
  .short 0         // support::ulittle16_t OverlayNumber;
  .skip 2*4        // support::ulittle16_t Reserved[4];
  .short 0         // support::ulittle16_t OEMid;
  .short 0         // support::ulittle16_t OEMinfo;
  .skip 2*10       // support::ulittle16_t Reserved2[10];
  // Even though we fixed `dos_header` at 0x00, we still need to subtract it to
  // indicate this is relative offset. Otherwise linker complains about referencing
  // an absolute address when linking with `-fPIE` (position independent executable)
  .long signature - dos_header  // support::ulittle32_t AddressOfNewExeHeader;

// Signature
signature:
  .long 0x00004550  // "PE\0\0"

// COFF File Header
coff_file_header:
  .short IMAGE_FILE_MACHINE_RISCV64            // support::ulittle16_t Machine;
   // ".reloc" and ".text"
  .short 2                                     // support::ulittle16_t NumberOfSections;
  .long 0                                      // support::ulittle32_t TimeDateStamp;
  .long 0                                      // support::ulittle32_t PointerToSymbolTable;
  .long 0                                      // support::ulittle32_t NumberOfSymbols;
  .short section_table_start - pe32plus_header // support::ulittle16_t SizeOfOptionalHeader;
  .short IMAGE_FILE_EXECUTABLE_IMAGE           // support::ulittle16_t Characteristics;

// PE32+ Optional Header
pe32plus_header:
  .short PE32_PLUS_HEADER_MAGIC           // support::ulittle16_t Magic;
  .byte 0                                 // uint8_t MajorLinkerVersion;
  .byte 0                                 // uint8_t MinorLinkerVersion;
  .long _end - dos_header                 // support::ulittle32_t SizeOfCode;
  .long 0                                 // support::ulittle32_t SizeOfInitializedData;
  .long 0                                 // support::ulittle32_t SizeOfUninitializedData;
  .long _start - dos_header               // support::ulittle32_t AddressOfEntryPoint;
  .long dos_header - dos_header           // support::ulittle32_t BaseOfCode;
  .quad 0                                 // support::ulittle64_t ImageBase;
  .long 512                               // support::ulittle32_t SectionAlignment;
  .long 512                               // support::ulittle32_t FileAlignment;
  .short 0                                // support::ulittle16_t MajorOperatingSystemVersion;
  .short 0                                // support::ulittle16_t MinorOperatingSystemVersion;
  .short 0                                // support::ulittle16_t MajorImageVersion;
  .short 0                                // support::ulittle16_t MinorImageVersion;
  .short 0                                // support::ulittle16_t MajorSubsystemVersion;
  .short 0                                // support::ulittle16_t MinorSubsystemVersion;
  .long 0                                 // support::ulittle32_t Win32VersionValue;
  .long _end - dos_header                 // support::ulittle32_t SizeOfImage;
  .long end_of_header - dos_header        // support::ulittle32_t SizeOfHeaders;
  .long 0                                 // support::ulittle32_t CheckSum;
  .short IMAGE_SUBSYSTEM_EFI_APPLICATION  // support::ulittle16_t Subsystem;
  .short 0                                // support::ulittle16_t DLLCharacteristics;
  .quad 0                                 // support::ulittle64_t SizeOfStackReserve;
  .quad 0                                 // support::ulittle64_t SizeOfStackCommit;
  .quad 0                                 // support::ulittle64_t SizeOfHeapReserve;
  .quad 0                                 // support::ulittle64_t SizeOfHeapCommit;
  .long 0                                 // support::ulittle32_t LoaderFlags;
  // 16 data directories in total
  .long 16                                // support::ulittle32_t NumberOfRvaAndSize;

  // The following are data directories mostly for place holder purpose for now. Entries
  // such as certification_table might be needed for secure boot in the future.
  DATA_DIRECTORY export_table
  DATA_DIRECTORY import_table
  DATA_DIRECTORY resource_table
  DATA_DIRECTORY exception_table
  DATA_DIRECTORY certification_table
  DATA_DIRECTORY base_relocation_table
  DATA_DIRECTORY debug
  DATA_DIRECTORY architecture_data
  DATA_DIRECTORY global_ptr
  DATA_DIRECTORY tls_table
  DATA_DIRECTORY load_config_table
  DATA_DIRECTORY bound_inport
  DATA_DIRECTORY import_address_table
  DATA_DIRECTORY delay_import_descriptor
  DATA_DIRECTORY CLR_runtime_header
  .quad 0

// Section Table
section_table_start:
// .reloc. May be expected by some PE32+ loader
reloc_section:
  .ascii  ".reloc"
  .byte 0
  .byte 0           // 8 bytes of `char Name[COFF::NameSize];`
  .long 0           // support::ulittle32_t VirtualSize;
  .long 0           // support::ulittle32_t VirtualAddress;
  .long 0           // support::ulittle32_t SizeOfRawData;
  .long 0           // support::ulittle32_t PointerToRawData;
  .long 0           // support::ulittle32_t PointerToRelocations;
  .long 0           // support::ulittle32_t PointerToLinenumbers;
  .short 0          // support::ulittle16_t NumberOfRelocations;
  .short 0          // support::ulittle16_t NumberOfLinenumbers;
  // https://learn.microsoft.com/en-us/windows/win32/debug/pe-format#section-flags
  // We use the same value as Clang would generate with "-Wl,/subsystem:efi_application"
  // IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_DISCARDABLE |
  // IMAGE_SCN_MEM_READ
  .long 0x42000040  // support::ulittle32_t Characteristics;

// .text
text_section:
  .ascii  ".text"
  .byte  0
  .byte  0
  .byte  0                          // 8 bytes of `char Name[COFF::NameSize];`
  .long _end - end_of_header        // support::ulittle32_t VirtualSize;
  .long end_of_header - dos_header  // support::ulittle32_t VirtualAddress;
  .long _end - end_of_header        // support::ulittle32_t SizeOfRawData;
  .long end_of_header - dos_header  // support::ulittle32_t PointerToRawData;
  .long 0                           // support::ulittle32_t PointerToRelocations;
  .long 0                           // support::ulittle32_t PointerToLinenumbers;
  .short 0                          // support::ulittle16_t NumberOfRelocations;
  .short 0                          // support::ulittle16_t NumberOfLinenumbers;
  // IMAGE_SCN_CNT_CODE | IMAGE_SCN_CNT_INITIALIZED_DATA |
  // IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_READ
  // We use the same value as Clang would generate with "-Wl,/subsystem:efi_application"
  .long 0x60000020                  // support::ulittle32_t Characteristics;

end_of_header:

// Traditionally, PE/COFF header occupies the entire first page. We keep this pattern.
.align 9

_start:
  // Behave like a function call.
  addi sp, sp, -8*3
  sd ra, 0(sp)
  sd t0, 8(sp)
  sd t1, 16(sp)

  // Save the EFI image handle from a0
  mv t0, a0
  // Save the EFI system table from a1
  mv t1, a1

  // Get the program load address (same as dos_header), and .dynamic section address.
  lla a0, dos_header
  lla a1, _DYNAMIC
  // Apply relocation fixup
  call ApplyRelocationHangIfFail

  // Now call efi_main.
  mv a0, t0
  mv a1, t1
  call efi_main

  // Prepare to return.
  ld ra, 0(sp)
  ld t0, 8(sp)
  ld t1, 16(sp)
  add sp, sp, 8*3
  ret